-- Device diagnostics / debug-reporting subsystem. -- -- When an account has users.debug_mode_enabled = true, that account's -- client(s) stream a timeseries of device state here: connectivity + -- server-health transitions (roaming / poor-data recovery), UPnP/Sonos -- sync events (locked-phone desync), and power/lifecycle state (Doze / -- battery-optimization, the prime suspect for the desync). The admin -- /admin/diagnostics view filters by account + device + time window and -- exports the slice as JSON for offline analysis. -- -- `kind` is a COARSE category gated by a CHECK whitelist; the finer event -- discriminator + all event-specific fields live in the `payload` jsonb. This -- keeps the enum small and stable so new event variants don't require a -- migration (only a new *category* would: a new CHECK value means swapping the -- constraint in the same migration). If you add a category here, mirror it in -- the handler whitelist in internal/api/diagnostics.go. -- -- Two timestamps on purpose: occurred_at is the client device clock -- (when the event happened on the phone — may be skewed/wrong, e.g. in -- a dead zone), received_at is the server clock at ingest. Retention -- pruning is keyed on received_at so a bad client clock can't keep rows -- alive forever. CREATE TABLE diagnostic_events ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, client_id text NOT NULL, app_version text, os_version text, kind text NOT NULL, payload jsonb NOT NULL DEFAULT '{}'::jsonb, occurred_at timestamptz NOT NULL, received_at timestamptz NOT NULL DEFAULT now(), CONSTRAINT diagnostic_events_kind_check CHECK (kind IN ( 'connectivity', 'upnp_sync', 'power', 'lifecycle', 'heartbeat', 'http' )) ); -- Admin reads filter by account then device, ordered newest-first. CREATE INDEX idx_diagnostic_events_user ON diagnostic_events (user_id, occurred_at DESC); CREATE INDEX idx_diagnostic_events_client ON diagnostic_events (client_id, occurred_at DESC); -- Retention pruner deletes by server-clock age. CREATE INDEX idx_diagnostic_events_received ON diagnostic_events (received_at); -- Per-account opt-in for diagnostics reporting. Admin-set primarily -- (flip remotely while a bug is live); the client also exposes a local -- OFF switch. Default false: no account reports until explicitly enabled. ALTER TABLE users ADD COLUMN IF NOT EXISTS debug_mode_enabled boolean NOT NULL DEFAULT false;