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:
glass beadian 2026-10-10 11:56:42 +00:00
commit aa5e262f3a
880 changed files with 111042 additions and 0 deletions

View file

@ -0,0 +1,88 @@
-- 015: WP15 Telegram live channel archive. All identities are tenant pinned.
CREATE TABLE feed_subscriptions (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
workspace_id uuid NOT NULL,
type text NOT NULL CHECK (type = 'telegram_channel'),
connection_id uuid NOT NULL,
channel_id text NOT NULL CHECK (btrim(channel_id) <> ''),
health text NOT NULL DEFAULT 'active' CHECK (health IN ('active', 'bot_removed', 'admin_required')),
health_checked_at timestamptz,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
UNIQUE (workspace_id, connection_id, channel_id),
UNIQUE (id, workspace_id),
FOREIGN KEY (connection_id, workspace_id) REFERENCES connections (id, workspace_id) ON DELETE CASCADE ON UPDATE RESTRICT
);
CREATE INDEX feed_subscriptions_channel ON feed_subscriptions (workspace_id, channel_id);
CREATE TABLE archived_feed_items (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
workspace_id uuid NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
remote_ref text NOT NULL CHECK (remote_ref ~ '^(-?[0-9]+|@[A-Za-z0-9_]+):[0-9]+$'),
channel_id text NOT NULL,
message_id bigint NOT NULL CHECK (message_id > 0),
media_group_id text,
text_content text NOT NULL DEFAULT '',
content_hash text NOT NULL CHECK (content_hash ~ '^[0-9a-f]{64}$'),
revision integer NOT NULL DEFAULT 1 CHECK (revision > 0),
media_refs jsonb NOT NULL DEFAULT '[]'::jsonb CHECK (jsonb_typeof(media_refs) = 'array'),
attribution jsonb NOT NULL DEFAULT '{}'::jsonb CHECK (jsonb_typeof(attribution) = 'object'),
protected_content boolean NOT NULL DEFAULT false,
posted_at timestamptz NOT NULL,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
UNIQUE (workspace_id, remote_ref),
UNIQUE (id, workspace_id),
CHECK (remote_ref = channel_id || ':' || message_id::text)
);
CREATE INDEX archived_feed_items_cursor ON archived_feed_items (workspace_id, posted_at DESC, id DESC);
CREATE TABLE archived_feed_revisions (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
workspace_id uuid NOT NULL,
item_id uuid NOT NULL,
revision integer NOT NULL CHECK (revision > 0),
content_hash text NOT NULL CHECK (content_hash ~ '^[0-9a-f]{64}$'),
text_content text NOT NULL,
media_refs jsonb NOT NULL CHECK (jsonb_typeof(media_refs) = 'array'),
attribution jsonb NOT NULL CHECK (jsonb_typeof(attribution) = 'object'),
created_at timestamptz NOT NULL DEFAULT now(),
UNIQUE (workspace_id, item_id, revision),
FOREIGN KEY (item_id, workspace_id) REFERENCES archived_feed_items (id, workspace_id) ON DELETE CASCADE ON UPDATE RESTRICT
);
-- drafts predates tenant-pinned references.
ALTER TABLE drafts ADD CONSTRAINT drafts_id_workspace_key UNIQUE (id, workspace_id);
CREATE TABLE archived_feed_drafts (
workspace_id uuid NOT NULL,
item_id uuid NOT NULL,
draft_id uuid NOT NULL UNIQUE,
created_at timestamptz NOT NULL DEFAULT now(),
PRIMARY KEY (workspace_id, item_id, draft_id),
FOREIGN KEY (item_id, workspace_id) REFERENCES archived_feed_items (id, workspace_id) ON DELETE CASCADE ON UPDATE RESTRICT,
FOREIGN KEY (draft_id, workspace_id) REFERENCES drafts (id, workspace_id) ON DELETE CASCADE ON UPDATE RESTRICT
);
CREATE TRIGGER trg_feed_subscriptions_ws_immutable BEFORE UPDATE ON feed_subscriptions
FOR EACH ROW EXECUTE FUNCTION reject_workspace_change();
CREATE TRIGGER trg_archived_feed_items_ws_immutable BEFORE UPDATE ON archived_feed_items
FOR EACH ROW EXECUTE FUNCTION reject_workspace_change();
CREATE TRIGGER trg_archived_feed_revisions_ws_immutable BEFORE UPDATE ON archived_feed_revisions
FOR EACH ROW EXECUTE FUNCTION reject_workspace_change();
CREATE TRIGGER trg_archived_feed_drafts_ws_immutable BEFORE UPDATE ON archived_feed_drafts
FOR EACH ROW EXECUTE FUNCTION reject_workspace_change();
ALTER TABLE feed_subscriptions ENABLE ROW LEVEL SECURITY;
ALTER TABLE archived_feed_items ENABLE ROW LEVEL SECURITY;
ALTER TABLE archived_feed_revisions ENABLE ROW LEVEL SECURITY;
ALTER TABLE archived_feed_drafts ENABLE ROW LEVEL SECURITY;
ALTER TABLE feed_subscriptions FORCE ROW LEVEL SECURITY;
ALTER TABLE archived_feed_items FORCE ROW LEVEL SECURITY;
ALTER TABLE archived_feed_revisions FORCE ROW LEVEL SECURITY;
ALTER TABLE archived_feed_drafts FORCE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON feed_subscriptions USING (workspace_id = NULLIF(current_setting('app.workspace_id', true), '')::uuid) WITH CHECK (workspace_id = NULLIF(current_setting('app.workspace_id', true), '')::uuid);
CREATE POLICY tenant_isolation ON archived_feed_items USING (workspace_id = NULLIF(current_setting('app.workspace_id', true), '')::uuid) WITH CHECK (workspace_id = NULLIF(current_setting('app.workspace_id', true), '')::uuid);
CREATE POLICY tenant_isolation ON archived_feed_revisions USING (workspace_id = NULLIF(current_setting('app.workspace_id', true), '')::uuid) WITH CHECK (workspace_id = NULLIF(current_setting('app.workspace_id', true), '')::uuid);
CREATE POLICY tenant_isolation ON archived_feed_drafts USING (workspace_id = NULLIF(current_setting('app.workspace_id', true), '')::uuid) WITH CHECK (workspace_id = NULLIF(current_setting('app.workspace_id', true), '')::uuid);
GRANT SELECT, INSERT, UPDATE, DELETE ON feed_subscriptions, archived_feed_items, archived_feed_revisions, archived_feed_drafts TO sp_app;