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/.
This commit is contained in:
commit
aa5e262f3a
880 changed files with 111042 additions and 0 deletions
79
apps/api/migrations/028_instance_settings.sql
Normal file
79
apps/api/migrations/028_instance_settings.sql
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
-- 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;
|
||||
Loading…
Add table
Add a link
Reference in a new issue