{"id":51401964,"url":"https://github.com/containeroo/overdue","last_synced_at":"2026-07-04T07:33:40.900Z","repository":{"id":367084245,"uuid":"1278249330","full_name":"containeroo/overdue","owner":"containeroo","description":"Small HTTP check-in monitor with webhook/email notifications.","archived":false,"fork":false,"pushed_at":"2026-06-24T13:15:10.000Z","size":171,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-24T14:22:17.265Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/containeroo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-23T15:56:57.000Z","updated_at":"2026-06-24T13:16:28.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/containeroo/overdue","commit_stats":null,"previous_names":["containeroo/overdue"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/containeroo/overdue","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/containeroo%2Foverdue","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/containeroo%2Foverdue/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/containeroo%2Foverdue/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/containeroo%2Foverdue/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/containeroo","download_url":"https://codeload.github.com/containeroo/overdue/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/containeroo%2Foverdue/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35114172,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-04T02:00:05.987Z","response_time":113,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-07-04T07:33:40.209Z","updated_at":"2026-07-04T07:33:40.892Z","avatar_url":"https://github.com/containeroo.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Overdue\n\nOverdue is a small HTTP check-in monitor.\n\nIt waits for periodic check-ins, moves through a simple lifecycle when a check-in is late, and sends notifications through configured webhook or email targets when the alerting deadline is reached.\n\n## Lifecycle\n\n```text\nscheduled -\u003e awaiting -\u003e overdue -\u003e alerting -\u003e awaiting\n```\n\n- `scheduled`: no check-in has been received yet.\n- `awaiting`: a check-in was received and the next deadline is active.\n- `overdue`: the expected check-in deadline passed and the alerting delay is running.\n- `alerting`: the alerting delay elapsed and an alert notification was queued.\n- `resolved`: a resolved notification is queued when check-ins resume after alerting and the receiver has `send-resolved=true`.\n\n## Quick start\n\n```sh\ndocker run --rm -p 8080:8080 \\\n  -e OVERDUE__EXPECTED_EVERY=1m \\\n  -e OVERDUE__ALERTING_DELAY=10s \\\n  ghcr.io/containeroo/overdue:latest\n```\n\nSend a check-in:\n\n```sh\ncurl -X POST http://localhost:8080/checkin\n```\n\nCheck status:\n\n```sh\ncurl http://localhost:8080/status\n```\n\n## Configuration\n\nOverdue is configured through CLI flags or environment variables.\n\nEnvironment variables use the `OVERDUE__` prefix. Flag names are uppercased and dashes become underscores.\n\nExamples:\n\n```text\n--expected-every                  -\u003e OVERDUE__EXPECTED_EVERY\n--alerting-delay                  -\u003e OVERDUE__ALERTING_DELAY\n--public-url                      -\u003e OVERDUE__PUBLIC_URL\n--webhook.ops.url                 -\u003e OVERDUE__WEBHOOK_OPS_URL\n--webhook.ops.custom-data         -\u003e OVERDUE__WEBHOOK_OPS_CUSTOM_DATA\n--email.primary.smtp-host         -\u003e OVERDUE__EMAIL_PRIMARY_SMTP_HOST\n--email.primary.from              -\u003e OVERDUE__EMAIL_PRIMARY_FROM\n```\n\n### Core flags\n\n| Flag                  | Environment variable         | Default    | Description                                                                |\n| --------------------- | ---------------------------- | ---------- | -------------------------------------------------------------------------- |\n| `--listen-address`    | `OVERDUE__LISTEN_ADDRESS`    | `:8080`    | HTTP server listen address.                                                |\n| `--route-prefix`      | `OVERDUE__ROUTE_PREFIX`      | empty      | Optional path prefix when Overdue is served below a sub-path.              |\n| `--public-url`        | `OVERDUE__PUBLIC_URL`        | empty      | Public base URL used in notification template links.                       |\n| `--name`              | `OVERDUE__NAME`              | `default`  | Name of the check-in monitor used in responses and notifications.          |\n| `--path`              | `OVERDUE__PATH`              | `/checkin` | Route used to receive check-ins.                                           |\n| `--expected-every`    | `OVERDUE__EXPECTED_EVERY`    | required   | Maximum time between check-ins.                                            |\n| `--alerting-delay`    | `OVERDUE__ALERTING_DELAY`    | required   | Extra time after the expected deadline before alerting.                    |\n| `--start-active`      | `OVERDUE__START_ACTIVE`      | `false`    | Activate the monitor at startup instead of waiting for the first check-in. |\n| `--allow-get-checkin` | `OVERDUE__ALLOW_GET_CHECKIN` | `false`    | Also accept `GET` requests on the check-in route.                          |\n| `--response-details`  | `OVERDUE__RESPONSE_DETAILS`  | `false`    | Return detailed timing fields from check-in responses by default.          |\n| `--auth-token`        | `OVERDUE__AUTH_TOKEN`        | empty      | Optional bearer token required for check-in and status requests.           |\n| `--debug`             | `OVERDUE__DEBUG`             | `false`    | Enable debug logging.                                                      |\n| `--log-format`        | `OVERDUE__LOG_FORMAT`        | `json`     | Log format: `json` or `text`.                                              |\n\n## Notifications\n\nOverdue supports multiple named webhook and email targets.\n\nEach target is configured as a dynamic group instance:\n\n```text\nwebhook.\u003cname\u003e.\u003cfield\u003e\nemail.\u003cname\u003e.\u003cfield\u003e\n```\n\nExamples:\n\n```text\n--webhook.ops.url=https://hooks.slack.com/services/...\n--email.primary.smtp-host=smtp.example.com\n```\n\nIf no notification targets are configured, Overdue still runs and records status, but no notifications are sent.\n\n### Webhook flags\n\n| Flag pattern                           | Environment variable pattern                  | Default      | Description                                                     |\n| -------------------------------------- | --------------------------------------------- | ------------ | --------------------------------------------------------------- |\n| `--webhook.\u003cname\u003e.url`                 | `OVERDUE__WEBHOOK_\u003cNAME\u003e_URL`                 | required     | Webhook URL.                                                    |\n| `--webhook.\u003cname\u003e.method`              | `OVERDUE__WEBHOOK_\u003cNAME\u003e_METHOD`              | `POST`       | HTTP method: `POST`, `PUT`, `PATCH`, or `DELETE`.               |\n| `--webhook.\u003cname\u003e.timeout`             | `OVERDUE__WEBHOOK_\u003cNAME\u003e_TIMEOUT`             | `10s`        | HTTP request timeout.                                           |\n| `--webhook.\u003cname\u003e.tls-skip-verify`     | `OVERDUE__WEBHOOK_\u003cNAME\u003e_TLS_SKIP_VERIFY`     | `false`      | Skip TLS certificate verification.                              |\n| `--webhook.\u003cname\u003e.send-resolved`       | `OVERDUE__WEBHOOK_\u003cNAME\u003e_SEND_RESOLVED`       | `false`      | Send resolved notifications to this receiver.                   |\n| `--webhook.\u003cname\u003e.title-template`      | `OVERDUE__WEBHOOK_\u003cNAME\u003e_TITLE_TEMPLATE`      | status title | Optional notification title template.                           |\n| `--webhook.\u003cname\u003e.headers`             | `OVERDUE__WEBHOOK_\u003cNAME\u003e_HEADERS`             | empty        | HTTP headers in `KEY=VALUE` format.                             |\n| `--webhook.\u003cname\u003e.custom-data`         | `OVERDUE__WEBHOOK_\u003cNAME\u003e_CUSTOM_DATA`         | empty        | Custom template data in `KEY=VALUE` format.                     |\n| `--webhook.\u003cname\u003e.template`            | `OVERDUE__WEBHOOK_\u003cNAME\u003e_TEMPLATE`            | required     | Body template path or `builtin:\u003cname\u003e`.                         |\n| `--webhook.\u003cname\u003e.log-response`        | `OVERDUE__WEBHOOK_\u003cNAME\u003e_LOG_RESPONSE`        | `summary`    | Webhook response logging: `summary`, `body`, `full`, or `none`. |\n| `--webhook.\u003cname\u003e.response-body-limit` | `OVERDUE__WEBHOOK_\u003cNAME\u003e_RESPONSE_BODY_LIMIT` | `4096`       | Maximum response body bytes to read for logs and errors.        |\n\n### Email flags\n\n| Flag pattern                          | Environment variable pattern                 | Default      | Description                                   |\n| ------------------------------------- | -------------------------------------------- | ------------ | --------------------------------------------- |\n| `--email.\u003cname\u003e.smtp-host`            | `OVERDUE__EMAIL_\u003cNAME\u003e_SMTP_HOST`            | required     | SMTP host.                                    |\n| `--email.\u003cname\u003e.smtp-port`            | `OVERDUE__EMAIL_\u003cNAME\u003e_SMTP_PORT`            | `587`        | SMTP port.                                    |\n| `--email.\u003cname\u003e.smtp-user`            | `OVERDUE__EMAIL_\u003cNAME\u003e_SMTP_USER`            | empty        | SMTP username.                                |\n| `--email.\u003cname\u003e.smtp-pass`            | `OVERDUE__EMAIL_\u003cNAME\u003e_SMTP_PASS`            | empty        | SMTP password.                                |\n| `--email.\u003cname\u003e.smtp-tls-skip-verify` | `OVERDUE__EMAIL_\u003cNAME\u003e_SMTP_TLS_SKIP_VERIFY` | `false`      | Skip SMTP TLS certificate verification.       |\n| `--email.\u003cname\u003e.send-resolved`        | `OVERDUE__EMAIL_\u003cNAME\u003e_SEND_RESOLVED`        | `false`      | Send resolved notifications to this receiver. |\n| `--email.\u003cname\u003e.title-template`       | `OVERDUE__EMAIL_\u003cNAME\u003e_TITLE_TEMPLATE`       | status title | Optional notification title template.         |\n| `--email.\u003cname\u003e.from`                 | `OVERDUE__EMAIL_\u003cNAME\u003e_FROM`                 | required     | Sender address.                               |\n| `--email.\u003cname\u003e.to`                   | `OVERDUE__EMAIL_\u003cNAME\u003e_TO`                   | required     | Recipient address. May be repeated.           |\n| `--email.\u003cname\u003e.headers`              | `OVERDUE__EMAIL_\u003cNAME\u003e_HEADERS`              | empty        | Email headers in `KEY=VALUE` format.          |\n| `--email.\u003cname\u003e.custom-data`          | `OVERDUE__EMAIL_\u003cNAME\u003e_CUSTOM_DATA`          | empty        | Custom template data in `KEY=VALUE` format.   |\n| `--email.\u003cname\u003e.template`             | `OVERDUE__EMAIL_\u003cNAME\u003e_TEMPLATE`             | required     | Body template path or `builtin:\u003cname\u003e`.       |\n\n## Webhook examples\n\n### Slack incoming webhook\n\n```sh\ndocker run --rm -p 8080:8080 \\\n  -e OVERDUE__EXPECTED_EVERY=1m \\\n  -e OVERDUE__ALERTING_DELAY=10s \\\n  -e OVERDUE__PUBLIC_URL=https://overdue.example.com \\\n  -e OVERDUE__WEBHOOK_OPS_URL=\"$SLACK_WEBHOOK_URL\" \\\n  -e OVERDUE__WEBHOOK_OPS_TEMPLATE=builtin:slack-incoming-webhook \\\n  -e OVERDUE__WEBHOOK_OPS_CUSTOM_DATA=channel=#alertmanager \\\n  -e OVERDUE__WEBHOOK_OPS_SEND_RESOLVED=true \\\n  ghcr.io/containeroo/overdue:latest\n```\n\n### Generic JSON webhook\n\n```sh\ndocker run --rm -p 8080:8080 \\\n  -e OVERDUE__EXPECTED_EVERY=1m \\\n  -e OVERDUE__ALERTING_DELAY=10s \\\n  -e OVERDUE__WEBHOOK_OPS_URL=https://example.com/webhook \\\n  -e OVERDUE__WEBHOOK_OPS_TEMPLATE=/etc/overdue/webhook.tmpl \\\n  -v \"$PWD/webhook.tmpl:/etc/overdue/webhook.tmpl:ro\" \\\n  ghcr.io/containeroo/overdue:latest\n```\n\nExample `webhook.tmpl`:\n\n```json\n{\n  \"title\": {{ .Title | json }},\n  \"text\": {{ .Text | json }},\n  \"status\": {{ .Status | json }},\n  \"checkInName\": {{ .CheckInName | json }},\n  \"resolved\": {{ .Resolved | json }}\n}\n```\n\n## Email example\n\n```sh\ndocker run --rm -p 8080:8080 \\\n  -e OVERDUE__EXPECTED_EVERY=1m \\\n  -e OVERDUE__ALERTING_DELAY=10s \\\n  -e OVERDUE__PUBLIC_URL=https://overdue.example.com \\\n  -e OVERDUE__EMAIL_OPS_SMTP_HOST=smtp.example.com \\\n  -e OVERDUE__EMAIL_OPS_SMTP_PORT=587 \\\n  -e OVERDUE__EMAIL_OPS_SMTP_USER=overdue@example.com \\\n  -e OVERDUE__EMAIL_OPS_SMTP_PASS=\"$SMTP_PASSWORD\" \\\n  -e OVERDUE__EMAIL_OPS_FROM=overdue@example.com \\\n  -e OVERDUE__EMAIL_OPS_TO=ops@example.com \\\n  -e OVERDUE__EMAIL_OPS_TEMPLATE=builtin:email-html \\\n  -e OVERDUE__EMAIL_OPS_SEND_RESOLVED=true \\\n  ghcr.io/containeroo/overdue:latest\n```\n\n`OVERDUE__EMAIL_OPS_FROM` is required. If it is missing, notification setup or delivery fails because the email target cannot build a valid message.\n\n## Templates\n\nNotification payloads are rendered with Go templates.\n\nOverdue includes built-in templates:\n\n```text\nbuiltin:email-html\nbuiltin:slack-incoming-webhook\nbuiltin:slack-chat-post-message\n```\n\nCustom templates can be mounted into the container and referenced by path:\n\n```sh\n-e OVERDUE__WEBHOOK_OPS_TEMPLATE=/etc/overdue/slack.tmpl\n```\n\nWebhook templates must render valid JSON. Email templates may render text or HTML.\n\nTemplates use strict missing-key behavior. Missing fields fail during startup validation or delivery instead of rendering silently.\n\n### Template data\n\nTemplates receive the following data:\n\n| Field             | Type                | Description                                            |\n| ----------------- | ------------------- | ------------------------------------------------------ |\n| `.IncidentID`     | `string`            | Stable ID for one overdue incident.                    |\n| `.NotificationID` | `string`            | Stable ID for this concrete notification.              |\n| `.CheckInName`    | `string`            | Configured check-in name.                              |\n| `.LastCheckIn`    | `time.Time`         | Last received check-in time.                           |\n| `.ExpectedBy`     | `time.Time`         | Time when the next check-in was expected.              |\n| `.OverdueSince`   | `time.Time`         | Time when the check-in became overdue.                 |\n| `.AlertingAt`     | `time.Time`         | Time when alerting starts.                             |\n| `.Now`            | `time.Time`         | Time the notification event was created.               |\n| `.Phase`          | `string`            | Monitor phase.                                         |\n| `.Status`         | `string`            | Notification status: `alerting` or `resolved`.         |\n| `.Resolved`       | `bool`              | Whether this is a resolved notification.               |\n| `.Title`          | `string`            | Rendered notification title.                           |\n| `.Text`           | `string`            | Default plain text summary.                            |\n| `.Receiver`       | `string`            | Receiver name.                                         |\n| `.CustomData`     | `map[string]string` | Custom data configured with `custom-data`.             |\n| `.App.Version`    | `string`            | Overdue version.                                       |\n| `.App.SiteRoot`   | `string`            | Public base URL from `--public-url`.                   |\n| `.App.CheckInURL` | `string`            | Public check-in URL when `--public-url` is configured. |\n| `.App.StatusURL`  | `string`            | Public status URL when `--public-url` is configured.   |\n\n### Custom data\n\nCustom data is configured with `KEY=VALUE` values:\n\n```sh\n-e OVERDUE__WEBHOOK_OPS_CUSTOM_DATA=channel=#alertmanager\n```\n\nIt is available in templates as `.CustomData`.\n\nRecommended access:\n\n```gotemplate\n{{ index .CustomData \"channel\" }}\n```\n\nExample with a default:\n\n```gotemplate\n{{ index .CustomData \"channel\" | default \"alertmanager\" | withPrefix \"#\" }}\n```\n\n## Title templates\n\nTitle templates are optional. When no title template is configured, the flag default renders `[OVERDUE] Event Notification` or `[RESOLVED] Event Notification`. Built-in body templates render their title from the event data.\n\nOverride the title per receiver when you need a custom Slack title or email subject:\n\n```sh\n-e OVERDUE__WEBHOOK_OPS_TITLE_TEMPLATE='{{ if .Resolved }}[OK]{{ else }}[ALERT]{{ end }} {{ .CheckInName }}'\n-e OVERDUE__EMAIL_OPS_TITLE_TEMPLATE='{{ if .Resolved }}[OK]{{ else }}[ALERT]{{ end }} {{ .CheckInName }}'\n```\n\n## HTTP API\n\n### `POST /checkin`\n\nRecords a check-in.\n\n```sh\ncurl -X POST http://localhost:8080/checkin\n```\n\nCompact response:\n\n```json\n{ \"status\": \"ok\" }\n```\n\nDetailed response:\n\n```sh\ncurl -X POST 'http://localhost:8080/checkin?details=true'\n```\n\n### `GET /checkin`\n\nDisabled by default. Enable `--allow-get-checkin` only for simple uptime systems that cannot send `POST` requests. When enabled, `GET /checkin` also records a check-in.\n\n### `GET /status`\n\nReturns the current monitor state.\n\n```sh\ncurl http://localhost:8080/status\n```\n\nDetailed status:\n\n```sh\ncurl 'http://localhost:8080/status?details=true'\n```\n\n### `GET /healthz` and `POST /healthz`\n\nLiveness probe.\n\n```sh\ncurl http://localhost:8080/healthz\n```\n\n### `GET /readyz` and `POST /readyz`\n\nReadiness probe. If this endpoint is mounted, startup configuration and notification setup completed successfully.\n\n```sh\ncurl http://localhost:8080/readyz\n```\n\n### `GET /version`\n\nReturns build version and commit.\n\n```sh\ncurl http://localhost:8080/version\n```\n\n### `GET /metrics`\n\nPrometheus metrics endpoint. It includes monitor phase/timestamp gauges, received check-in counters, and notification queue counters for queued, skipped, and queue-failed alerting/resolved notifications.\n\nThis endpoint is intentionally not protected by `--auth-token`, because Prometheus commonly scrapes without application bearer tokens. Do not expose it directly to the public internet unless a reverse proxy, firewall, or network policy restricts access.\n\n```sh\ncurl http://localhost:8080/metrics\n```\n\n## Authentication\n\nSet `--auth-token` or `OVERDUE__AUTH_TOKEN` to require a bearer token for check-in and status requests. The metrics, health, readiness, and version endpoints remain unauthenticated; protect them at the reverse proxy or network layer when exposed outside a trusted network.\n\n```sh\ncurl -H \"Authorization: Bearer $OVERDUE_TOKEN\" \\\n  -X POST http://localhost:8080/checkin\n```\n\n## Route prefix\n\nUse `--route-prefix` when Overdue is served below a path prefix.\n\n```sh\n-e OVERDUE__ROUTE_PREFIX=/watchdog\n```\n\nWith this prefix, endpoints are served below `/watchdog`:\n\n```text\n/watchdog/checkin\n/watchdog/status\n/watchdog/metrics\n/watchdog/healthz\n/watchdog/readyz\n```\n\nSet `--public-url` to the externally reachable base URL if you want templates to include correct links:\n\n```sh\n-e OVERDUE__PUBLIC_URL=https://overdue.example.com/watchdog\n```\n\n## Docker Compose\n\n```yaml\n---\nname: overdue\n\nservices:\n  overdue:\n    image: ghcr.io/containeroo/overdue:latest\n    restart: unless-stopped\n    ports:\n      - \"8080:8080\"\n    environment:\n      OVERDUE__EXPECTED_EVERY: 1m\n      OVERDUE__ALERTING_DELAY: 10s\n      OVERDUE__PUBLIC_URL: https://overdue.example.com\n      OVERDUE__WEBHOOK_OPS_URL: \"${SLACK_WEBHOOK_URL}\"\n      OVERDUE__WEBHOOK_OPS_TEMPLATE: builtin:slack-incoming-webhook\n      OVERDUE__WEBHOOK_OPS_CUSTOM_DATA: channel=#alertmanager\n      OVERDUE__WEBHOOK_OPS_SEND_RESOLVED: \"true\"\n```\n\n## Development\n\nRun tests:\n\n```sh\nmake test\n```\n\nRun locally:\n\n```sh\ngo run . \\\n  --expected-every=1m \\\n  --alerting-delay=10s\n```\n\nRun locally with an email receiver:\n\n```sh\ngo run . \\\n  --expected-every=1m \\\n  --alerting-delay=10s \\\n  --email.ops.smtp-host=smtp.example.com \\\n  --email.ops.from=overdue@example.com \\\n  --email.ops.to=ops@example.com \\\n  --email.ops.template=builtin:email-html\n```\n\n## License\n\nThis project is licensed under the Apache 2.0 License. See the [LICENSE](LICENSE) file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcontaineroo%2Foverdue","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcontaineroo%2Foverdue","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcontaineroo%2Foverdue/lists"}