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,30 @@
-- 007: first-party auth (email + password) and membership invitations.
-- Passwords live in their own table (never in users, never in audit diffs);
-- hashes are scrypt with a per-user salt, produced by apps/api/src/auth/password.ts.
CREATE TABLE user_passwords (
user_id uuid PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
password_hash text NOT NULL,
updated_at timestamptz NOT NULL DEFAULT now()
);
-- Invited members exist as memberships before they first sign in.
ALTER TABLE memberships
ADD COLUMN status text NOT NULL DEFAULT 'active'
CHECK (status IN ('active', 'invited'));
GRANT SELECT, INSERT, UPDATE, DELETE ON user_passwords TO sp_app;
-- Login happens before any tenant context exists, and memberships is FORCE
-- RLS. This narrow SECURITY DEFINER lookup returns only the caller-supplied
-- user's own memberships so login can pick a workspace without weakening RLS.
CREATE FUNCTION auth_user_memberships(p_user_id uuid)
RETURNS TABLE(workspace_id uuid, role text, status text, created_at timestamptz)
LANGUAGE sql STABLE SECURITY DEFINER SET search_path = public AS $$
SELECT m.workspace_id, m.role, m.status, m.created_at
FROM memberships m
WHERE m.user_id = p_user_id
ORDER BY m.created_at ASC
$$;
REVOKE ALL ON FUNCTION auth_user_memberships(uuid) FROM PUBLIC;
GRANT EXECUTE ON FUNCTION auth_user_memberships(uuid) TO sp_app;