T-HUB/apps/api/migrations/028_instance_settings.sql
glass beadian aa5e262f3a Tactical Media Broadcast Hub — initial release
Self-hosted multi-platform broadcast newsroom for activist collectives:
publishing pipeline with group approval and DST-proof scheduling, TweetDeck-style
column wall with hostile-content-safe rendering, live control room with Auto
Director, bridged chat governance with polls and quorum, distributed sandboxed
transcode workers, encrypted content-addressed archive, monthly analytics.

MIT licensed. See README.md and docs/.
2026-10-10 11:56:42 +00:00

79 lines
4.1 KiB
PL/PgSQL

-- 028: instance-level admin settings — content-policy posture for intake.
--
-- The intake-posture decision record
-- (docs/decisions/2026-10-09-intake-posture.md) grades the intake tiers by
-- legal/ToS risk:
--
-- YouTube channel RSS + metadata = safe (keyless, no quota, no ToS risk)
-- YouTube full harvest (yt-dlp) = ToS risk — per-source opt-in ONLY
-- (027's DEFAULT_MEDIA_POLICY already
-- parks youtube_channel at 'metadata');
-- this switch is the instance-wide gate
-- the per-source opt-in is subordinate to
-- YouTube broadcast playlists = CC-only (Creative Commons licensed
-- content only) — safe to rebroadcast
-- Locked platforms (X/IG/TikTok) = metadata-only via sanctioned
-- connectors; a bounded read-only pool is
-- the decision record's Tier-2 "small
-- bounded pool, later" option — opt-in
--
-- Those posture choices are INSTANCE-level (one deployment = one collective,
-- one egress IP, one legal exposure), not per-workspace: no workspace admin
-- may widen them for everyone. Hence this table is a system table:
--
-- * no workspace_id — deliberately. It is not a tenant row.
-- * no RLS — there is nothing to isolate BY. Access control is at the API
-- seam (SettingsService runs as sp_app; only Owner/Admin roles reach the
-- setters through the controller's role check). NOT a grant-surface for
-- tenant sessions: the app pool is the only grantee.
--
-- key-value shape (key TEXT PK, value TEXT NOT NULL) per task spec: settings
-- are few, admin-facing and read as a whole — typed decoding lives in
-- apps/api/src/settings/settings.service.ts (the ONE place that interprets
-- values; nothing else parses these strings), values are CHECK-constrained
-- to the exact literal the service layer understands so a stray UPDATE can
-- never park an undecodable row in the table.
BEGIN;
CREATE TABLE instance_settings (
key text PRIMARY KEY,
value text NOT NULL,
updated_at timestamptz NOT NULL DEFAULT now(),
updated_by uuid REFERENCES users(id) ON DELETE SET NULL,
-- Legal-exposure switches must never hold anything but the literals the
-- service layer decodes; unknown keys are equally rejected so a typo can't
-- fork the settings surface.
CONSTRAINT instance_settings_key_allowed CHECK (key IN (
'youtube_full_harvest', 'youtube_cc_only_broadcast',
'scrape_metadata_only', 'scrape_bounded_pilot', 'intake_posture'
)),
CONSTRAINT instance_settings_bool_value CHECK (
key NOT IN ('youtube_full_harvest', 'youtube_cc_only_broadcast',
'scrape_metadata_only', 'scrape_bounded_pilot')
OR value IN ('true', 'false')
),
CONSTRAINT instance_settings_posture_value CHECK (
key <> 'intake_posture'
OR value IN ('metadata_only', 'community_submissions', 'bounded_pilot')
)
);
-- ---------------------------------------------------------------------------
-- Seed defaults (the safe posture — matches the 2026-10-09 decision record:
-- "metadata-only + community submissions + Tier-1 sanctioned sources").
-- ON CONFLICT DO NOTHING keeps re-runs and later re-seeds idempotent.
-- ---------------------------------------------------------------------------
INSERT INTO instance_settings (key, value) VALUES
('youtube_full_harvest', 'false'), -- yt-dlp OFF at the gate
('youtube_cc_only_broadcast', 'true'), -- CC-only broadcast filter
('scrape_metadata_only', 'true'), -- locked platforms: metadata
('scrape_bounded_pilot', 'false'), -- no bounded X pool
('intake_posture', 'metadata_only') -- overall posture
ON CONFLICT (key) DO NOTHING;
-- System table: sp_app is the ONLY grantee (003's default privileges already
-- cover it, but the explicit grant documents intent the way 006/008/009 do).
GRANT SELECT, INSERT, UPDATE, DELETE ON instance_settings TO sp_app;
COMMIT;