Open source / Rust / MIT Field guide / indexed
Field guide / NotificationsReviewable source / main

Notifications

Ryuki has in-app notifications with per-user read receipts. They are emitted by the server on lifecycle and operational events, listed in the portal bell, and readable over the API. There is no email or webhook delivery today: a dispatch outbox records what a future sender *would* do, and nothing more.

What gets notified, and to whom

Notifications are created only by server-side domain logic; there is no API to emit one.

EventRecipientSeverity
request.planRole: DatacenterApproverInfo
request.approveRequest ownerSuccess
request.rejectRequest ownerWarning
request.verifyRequest ownerSuccess
request.cancelRequest ownerWarning
slo.breach / slo.recoveredRole: MonitoringOperatorCritical / Success
budget.breach / budget.recoveredRole: MonitoringOperatorWarning / Success
agent.offline / agent.onlineRole: MonitoringOperatorWarning / Success

Lifecycle notifications are emitted after the transaction commits, detached from the request path: a notification failure can never fail the transition. Monitoring alerts (SLO, budget, agent liveness) are written atomically with their domain event by the background scan loops. A partial approval deliberately does not tell the owner "approved."

Notification bodies are synthesized from structured fields (request id, aggregate id); free-text never flows into a notification.

Recipients and read state

A notification targets either one user or one role. Role notifications are a single row that every holder of the role sees; read state is a per-user receipt, so one operator marking an alert read does not clear it for the rest of the team.

Notifications are not site- or environment-scoped: they follow the recipient, not the resource's scope.

API

MethodPathNotes
GET/api/notificationsThe caller's notifications, newest first (capped at 200).
GET/api/notifications/unread-countBadge count.
POST/api/notifications/{id}/readMark one read. Non-recipients get a 404; existence is never leaked.
POST/api/notifications/read-allMark all read.
GET/api/admin/notifications/dispatch-outboxAdmin: dry-run dispatch telemetry (?status=, ?limit=). Returns metadata only, never bodies.

Reading requires an ordinary session (audit or request tier); marking read is self-service for the recipient. Without a database the read endpoints degrade to empty no-db responses and the write endpoints return 503.

The portal bell

The bell lives in the portal header. The badge shows the unread count and the panel lists recent notifications; clicking an item marks it read and deep-links to the request when one is referenced. Counts refresh when the panel opens or after your own read actions; a background poll is a possible follow-up, so a notification arriving mid-session appears the next time you open the panel.

Delivery channels

Every emitted notification also plans its hypothetical delivery in the dispatch outbox: Critical events plan email and webhook, Warnings plan webhook, Info and Success plan nothing. The outbox is telemetry for operators to inspect (dry_run_logged status); no send path exists yet, and outbox bookkeeping can never roll back a notification.

Known limits

There is no retention or pruning job for notifications yet; the list endpoint caps at the newest 200 per recipient. If you need long-term archival of operational events, use the audit trail and domain events, which are the durable record.

Last updated 2026-07-09 · Edit this page on GitHub