-- Adds push notification delivery, so an incident reaches the person on call -- instead of waiting to be discovered. -- -- Delivery is an outbox rather than an inline HTTP call: the pool is limited to -- a single connection, so a POST made while holding the webhook's transaction -- would stall every other request behind it. The webhook inserts a row; the -- notifier goroutine delivers it. CREATE TABLE notifications ( id INTEGER PRIMARY KEY AUTOINCREMENT, incident_id INTEGER NOT NULL REFERENCES incidents(id) ON DELETE CASCADE, -- Nullable: a notification sent to the fallback topic belongs to nobody, -- because nobody was on call when the incident opened. user_id INTEGER REFERENCES users(id) ON DELETE SET NULL, topic TEXT NOT NULL, -- resolved at enqueue: who was on call then kind TEXT NOT NULL CHECK(kind IN ('triggered', 'reminder', 'resolved')), created_at INTEGER NOT NULL, send_after INTEGER NOT NULL, -- retry backoff watermark attempts INTEGER NOT NULL DEFAULT 0, sent_at INTEGER, last_error TEXT -- kept after the last attempt, for debugging ); -- The delivery loop's only query: what is due and still unsent. CREATE INDEX notifications_pending_idx ON notifications(send_after) WHERE sent_at IS NULL; -- Reminders and resolved notices both look up an incident's newest row. CREATE INDEX notifications_incident_idx ON notifications(incident_id, id DESC); -- A notification body is stored on the ntfy server and cached on the device, so -- a real API key must never appear in one. Each delivery mints its own token -- instead: one incident, one action, one day. CREATE TABLE incident_ack_tokens ( token_hash TEXT PRIMARY KEY, -- SHA-256 of the raw token, as with api_keys incident_id INTEGER NOT NULL REFERENCES incidents(id) ON DELETE CASCADE, user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, created_at INTEGER NOT NULL, expires_at INTEGER NOT NULL ); CREATE INDEX incident_ack_tokens_expires_idx ON incident_ack_tokens(expires_at); -- Where this user's notifications go. NULL means they get none; incidents -- assigned to them fall back to the configured fallback topic. ALTER TABLE users ADD COLUMN ntfy_topic TEXT;