Notifications

This page documents which notifications Dockrev sends, the payload format (v2 schemas), and how to configure the Instance Public Base URL so Telegram / Email / Web Push / Webhook messages can include clickable links to your Dockrev instance.

Purpose: turn internal paths (e.g. queue/{jobId}) into absolute URLs (e.g. https://dockrev.example.com/queue/job_...).

  • Web UI: Settings -> System settings -> Instance Public Base URL
  • API:
    • GET /api/settings -> instance.publicBaseUrl
    • PUT /api/settings -> instance.publicBaseUrl
  • Validation / normalization:
    • null or empty string clears the setting
    • when non-empty, it must be an absolute http:// or https:// URL
    • persisted value is trimmed and forced to end with /
  • Fallback when not configured:
    • links.*Url will fall back to internal paths (starting with /)
    • Telegram / Email will show paths as code (not clickable) and include a hint to configure the base URL
    • Web Push will still open the path under the current origin

Notification Types

kind schema Trigger (high level)
job_finished dockrev.notification.job.v2 An update job finishes (success / failed / rolled back) and is not filtered out
new_version_discovered dockrev.notification.new_version_discovered.v2 Scheduled checks or GHCR-webhook-triggered checks discover new versions (aggregated per check job)
ghcr_webhook_anomaly dockrev.notification.ghcr_webhook_anomaly.v2 Scheduled GHCR webhook audit (audit_all) detects missing/conflict/error repos
notification_test dockrev.notification.test.v2 POST /api/notifications/test

Webhook receivers should always route by schema (the switch to dockrev.notification.job.v2 is a breaking change).

Event switches in Settings

Path: Settings -> Notifications

Per-event switches:

  • Update finished notification -> controls job_finished
  • New version discovered (scheduled / webhook check) -> controls new_version_discovered
  • GitHub webhook anomaly (audit) -> controls ghcr_webhook_anomaly

Behavior:

  • If an event switch is off, that event is not sent to any channel.
  • Delivery requires both switches to be on: event switch and channel switch.

Job finished (dockrev.notification.job.v2)

Fields (v2)

Top-level:

  • schema: dockrev.notification.job.v2
  • kind: job_finished
  • sentAt: RFC3339 timestamp (string)
  • channel: telegram / email / webhook / webPush
  • job: job metadata
  • links: URLs inside your Dockrev instance (the important part)
  • human: Chinese title/summary/detail used for rendering
  • debug: build/version metadata

links example:

{
  "primaryUrl": "https://dockrev.example.com/services/stk_.../svc_...",
  "jobUrl": "https://dockrev.example.com/queue/job_...",
  "serviceUrls": [
    {
      "stackId": "stk_...",
      "stackName": "blog",
      "serviceId": "svc_...",
      "serviceName": "api",
      "url": "https://dockrev.example.com/services/stk_.../svc_..."
    }
  ],
  "truncated": { "serviceUrlsOmitted": 0 }
}

URL / click rules

Internal routes:

  • Job detail: /queue/{jobId}
  • Service detail: /services/{stackId}/{serviceId}

When instance.publicBaseUrl is configured, Dockrev produces absolute URLs; otherwise it falls back to internal paths.

primaryUrl selection:

  1. If the update can be uniquely mapped to one service, primaryUrl = serviceUrl
  2. Otherwise, primaryUrl = jobUrl

Uniquely mapped means:

  • the job scope is service and the job record includes serviceId, OR
  • the update summary indicates exactly one changed service

Truncation

  • links.serviceUrls is capped at 10 entries
  • the omitted count is stored in links.truncated.serviceUrlsOmitted
  • error excerpts (when present) are truncated to avoid overly long Telegram/Email messages

New version discovered (dockrev.notification.new_version_discovered.v2)

Trigger: scheduled update checks and GHCR-webhook-triggered service checks. A notification is sent when a check run discovers new versions. UI-triggered manual checks stay silent.

Dispatch timing, dedupe, and lifecycle:

  • the check job is finalized as success before any notification wait starts; only dispatch is delayed
  • services that still depend on floating-tag inference primarily wait for an explicit snapshot_worker task_finished event before one final display-tag recompute; the fixed 10 second cap is now only a safety fallback when the event/result does not arrive cleanly
  • when a digest snapshot has already finished but tags[] still cannot produce a readable version, notifications also try OCI org.opencontainers.image.version as a display-tag fallback while leaving raw tag fields untouched for compatibility
  • active dedupe is keyed by serviceId + candidateDigest
  • only pending / sent records block repeats
  • when the candidate disappears, the candidate digest changes, or the service baseline imageRef:imageTag changes, the previous active record is marked superseded
  • when every enabled delivery channel fails, the record is finalized as failed and later jobs may retry

Key fields:

  • check.jobId: check job id
  • check.servicesChecked: total services checked
  • check.newVersions: number of services with newly discovered versions
  • links.jobUrl: /queue/{jobId}
  • links.serviceUrls[]: /services/{stackId}/{serviceId}
  • links.serviceUrls[].currentTag / candidateTag: raw tags kept for backward compatibility
  • links.serviceUrls[].currentDisplayTag / candidateDisplayTag: priority is snapshot inference > frozen/live resolved tag > OCI explicit version > raw tag; raw fields remain unchanged for compatibility
  • links.primaryUrl: service URL when exactly one service is affected, otherwise job URL
  • human.title: a compact headline for the Email subject / Web Push title; single-service payloads use the service name, while multi-service payloads use an aggregate count
  • human.summary: the human-facing copy is tightened without changing the schema version

Human-readable summary rules:

  • single service, both sides readable: blog / api has a new version available (1.0.0 -> 1.1.0).
  • single service, only one side readable: blog / api has a new version available (1.0.0 -> latest).
  • single service, both sides still unreadable after settling: blog / api has a new version available. (never latest -> latest)
  • multi-service aggregate:
    3 services have new versions available:
    - blog / api
    - blog / worker (1.0.0 -> 1.1.0)
    - shop / gateway (2.4.0 -> 2.5.0)

Channel rendering rules:

  • Telegram / Email (single service): start directly with the summary sentence and keep only one Service details action; no generic banner title, no trailing Details suffix, no Service list block, and no duplicate link list
  • Telegram / Email (multi service): keep the aggregate notification, but render one service per line; unreadable entries show only the service name without a transition suffix
  • Web Push: title uses the compact human.title headline, body is exactly human.summary, and the click target still uses links.primaryUrl

Truncation:

  • links.serviceUrls keeps at most 10 entries
  • overflow is reported via links.truncated.serviceUrlsOmitted

GHCR webhook anomaly (dockrev.notification.ghcr_webhook_anomaly.v2)

Trigger: scheduled GHCR webhook audit (audit_all) only. Sent when missing/conflict/error repos are found.

Key fields:

  • job.id: audit job id
  • job.missing/conflict/error: anomaly counts
  • links.settingsUrl: settings page URL for remediation
  • links.jobUrl: job detail URL for audit logs
  • links.primaryUrl: defaults to links.jobUrl (Web Push click target)
  • links.repos[]: failing repo list with state and error excerpt
  • human.summary: directly lists visible failing repos (example: Found 2 anomalous repos: acme/api [missing], acme/worker [error].)
  • Telegram first line includes a clickable task link: Dockrev: GitHub Webhook audit anomaly 任务.

Truncation:

  • links.repos keeps at most 10 entries
  • overflow is reported via links.truncated.reposOmitted

Notification test (dockrev.notification.test.v2)

  • url points to the settings page (/settings) to demonstrate a clickable link
  • Web Push payload additionally contains top-level title / body / url

Channel rendering

Telegram (HTML)

Chinese title with an inline clickable 详情 link + summary + per-service list + optional error excerpt (<pre>...).

Email (multipart)

Subject includes status (no internal job id). Body is sent as both HTML and plain text.

Webhook (JSON)

Raw JSON payload is POSTed. Route by schema:

  • dockrev.notification.job.v2
  • dockrev.notification.new_version_discovered.v2
  • dockrev.notification.ghcr_webhook_anomaly.v2
  • dockrev.notification.test.v2

Web Push

The payload includes title / body / url. Clicking opens url (service page, job page, or settings page depending on event type).