Files
Alarm/database/migrations/007_alarm_analytics.sql
T
Admin 3ccea3e9f6 [feat](alarm): keep one anonymised lifecycle event log
Record created/joined/triggered/delivered/destroyed with SHA-256 hashes of the alert and user UUIDs only, and a reason on destruction. The earlier analytics and stdout-mirror tables were superseded by the user ruling, so 009 creates alarm.event_log, migrates what still applies and drops both, leaving exactly one durable table with no network identifiers, token, body or query string. Retention is age-based via the sweeper.
2026-10-19 02:26:46 +01:00

49 lines
2.0 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
\c dcntrlized;
-- 007_alarm_analytics.sql — anonymized, append-only lifecycle counters.
--
-- User request: "anonymized 'Alarm X created by Y', 'Alarm X subscribed by Y',
-- 'Alarm X delivered/triggered to/by Y', along with timestamps". Nothing raw, no
-- foreign keys, and it must survive the alert it describes — hence no REFERENCES
-- and no cascade. A destroyed alert still has its history here.
--
-- Identity columns are SHA-256 hex of the UUID *string* form. That is genuinely
-- one-way (a UUID carries ~122 random bits, so there is no address space to
-- enumerate), which is why this file uses a plain digest while the request-log
-- IP/UA hashing uses HMAC with a secret key: an IPv4 address is only 2^32 and a
-- plain digest would be reversible by lookup. Do not "unify" the two schemes.
--
-- `reason` is set only for `destroyed` rows: `delivered` (the ledger drained) or
-- `expired` (the persistence cap fired).
--
-- Idempotent (same guard pattern as 002–006).
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'alarm') THEN
RETURN;
END IF;
CREATE TABLE IF NOT EXISTS alarm.alarm_analytics (
id BIGSERIAL PRIMARY KEY,
alert_hash TEXT NOT NULL,
user_hash TEXT,
action TEXT NOT NULL,
reason TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
CONSTRAINT alarm_analytics_action_check
CHECK (action IN ('created', 'subscribed', 'triggered', 'delivered', 'destroyed'))
);
CREATE INDEX IF NOT EXISTS alarm_analytics_alert_idx
ON alarm.alarm_analytics (alert_hash, created_at);
CREATE INDEX IF NOT EXISTS alarm_analytics_created_idx
ON alarm.alarm_analytics (created_at);
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'alarm_user') THEN
GRANT SELECT, INSERT ON alarm.alarm_analytics TO alarm_user;
GRANT USAGE, SELECT ON SEQUENCE alarm.alarm_analytics_id_seq TO alarm_user;
END IF;
END
$$;