The Instagram connector existed but was never registered, so Channels showed it as "provider unavailable" and publishing could not resolve it. - Register createInstagramConnector in the connections registry and the publishing registry; add the connector-runtime publishing policy (idempotency "none": media_publish has no key, never replay). - The Facebook app credentials identify the Hub, not a collective: read them only from INSTAGRAM_CLIENT_ID / INSTAGRAM_CLIENT_SECRET. Drop the clientId/clientSecret connect fields and stop copying the app secret into each connection's vault entry. The provider is connectable only when both are set (connectEnabled + connectNote). - Fail the connect when several Instagram accounts are granted instead of silently publishing as the first Page's account. - Fix the connector package's pre-existing lint errors (it now ships in api). - Tests: contract cases for env-only credentials and the multi-account refusal; connections integration case for provider gating and the Facebook dialog redirect. - docs/oauth-connect.md: Instagram setup, callback URI and limits. Not in this change: publishing images needs a public plaintext JPEG URL that Meta downloads, and storage is client-side encrypted, so presigned S3 URLs cannot serve it. Until a short-lived media endpoint exists, publishing to Instagram fails with an explicit error. Token refresh via ig_refresh_token on graph.facebook.com also needs checking against Facebook Login tokens. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|---|---|---|
| .github/workflows | ||
| apps | ||
| docs | ||
| infra | ||
| packages | ||
| scripts | ||
| .dockerignore | ||
| .editorconfig | ||
| .gitignore | ||
| eslint.config.mjs | ||
| LICENSE | ||
| package.json | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| README.md | ||
| tsconfig.base.json | ||
| turbo.json | ||
Tactical Media Broadcast Hub
Self-hosted multi-tenant social / video / livestream management platform with a generic connector system and a Virtual Channel / Auto Director subsystem.
Built for collectives that need to coordinate media across platforms without concentrating access in one person's hands. Every credential lives in an encrypted vault. Every editorial decision lands in a tamper-evident audit log. Every feature is multi-tenant with row-level security.
Features
- Publishing pipeline — drafts → group approval → schedule → fenced dispatch → receipts. Per-connection publish governance:
auto, one-admin, majority or consensus sign-off per account — boosts included. Multi-platform fan-out (Mastodon, Telegram, Bluesky, PeerTube, Facebook, Instagram, Threads, LinkedIn, Reddit, Twitch, Discord, Slack). DST-proof scheduling. - Files SSO — the Hub is the OIDC issuer for oCIS Drive: one sign-in, auto-provisioned personal drives, rotating refresh tokens that die with your Hub session (logout propagates).
- Column wall — live multi-platform feed columns, TweetDeck-style. Safe rendering (hostile-HTML tested). Per-source cooldown + adaptive backoff.
- Live Control Room — browser multiview, TAKE/CUT, preview/program, lower-thirds, scrolling chyrons, audio mixing, tally.
- Auto-Director — priority rules switch sources automatically so the stream never dies mid-action.
- Chat governance — bridged chats (Telegram/Twitch/Discord) into one overlay. Polls with configurable quorum. Approval gates on every post. Machine-readable reason codes.
- Distributed transcode workers — laptops and GPUs join as render workers over Tailscale. Per-job sandboxed FFmpeg (UID/seccomp/no-network).
- Archive — content-addressed encrypted S3, dedup, full-text search, CSV/JSON export. Deleted platform content stays citable.
- Analytics — month-partitioned snapshots, current + previous month with deltas, per-provider breakdown.
Quickstart
pnpm install
pnpm lint && pnpm typecheck && pnpm build
# infrastructure
docker compose -f infra/docker-compose.yml up -d postgres redis temporal minio otel-collector prometheus grafana
# everything incl. media stub + mock live source + mock RTMP sink
docker compose -f infra/docker-compose.yml --profile dev up -d
# apps
pnpm dev # api :3000, web :3001(adjust if colliding), scheduler, media-worker :3100
curl localhost:3000/healthz
curl localhost:3000/readyz
Documentation
| Section | Path |
|---|---|
| Engineering guide | docs/engineering-guide.md |
| Admin guide | docs/admin-guide.md |
| User manual | docs/user-manual-v2.md |
| Design docs | docs/design/ |
| Streaming architecture | docs/design/streaming-architecture.md |
| Deployment (occupymedia.cc) | infra/occupymedia/DEPLOY.md |
Deterministic live-source testing
pnpm --filter @tactical-media-broadcast-hub/media-worker soak -- --hours 8 --out
Security & trust model
Self-hosted, end to end — no corporate cloud, no accounts needed to run the system itself. Multi-tenant with row-level security; platform credentials in an encrypted vault the database never sees in plaintext; every editorial action in a hash-chained audit log; anonymous-infrastructure friendly (the host carries boxes, not content — storage is ciphertext).