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/.
79 lines
4.1 KiB
PL/PgSQL
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;
|