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/.
130 lines
6 KiB
Markdown
130 lines
6 KiB
Markdown
# Deploy — occupymedia.cc
|
|
|
|
**Storage phase-one addendum:** the current deployment uses separate app/chat/
|
|
PeerTube CTs. New files/docs work is an independent, undeployed scaffold;
|
|
see [files runbook](files/README.md) and [storage contracts](../../docs/occupymedia-storage.md).
|
|
The older one-box instructions below do not deploy that stack. Live conduwuit
|
|
v0.5.0 still has local media; its volumes require backup. R2 durability alone
|
|
does not back up media or the metadata needed to resolve it.
|
|
|
|
One box, everything behind the Cloudflare tunnel (already provisioned: zone
|
|
`occupymedia.cc`, tunnel `occupymedia` with ingress app/api/tube/chat, R2
|
|
buckets `occupymedia-media-encrypted`, `-peertube-videos`, `-peertube-playlists`,
|
|
`-matrix-media`). No inbound ports. All local ports bind to 127.0.0.1.
|
|
|
|
## WP3 release on the app CT (150)
|
|
|
|
Use `docker compose -f docker-compose.app.yml` in this directory. Before
|
|
syncing source, create and verify an owner `pg_dump` in
|
|
`/opt/tactical-media-broadcast-hub/backups/`. Preserve the previous source for rollback.
|
|
|
|
Both `DATABASE_URL` and `SCHEDULER_DATABASE_URL` in the CT `.env` must connect
|
|
as `sp_app`, without superuser or BYPASSRLS privileges. Provision its password
|
|
with owner access and keep real values only in the CT `.env`. Keep the
|
|
existing `SUPERPOSTER_KEK`; scheduler inherits it and `TEMPORAL_ADDRESS`.
|
|
|
|
Stop the old scheduler, then run the new API migration CLI offline with
|
|
`SUPER_DATABASE_URL` set to the owner URL:
|
|
`pnpm --filter @tactical-media-broadcast-hub/api exec tsx src/db/migrate.cli.ts --dir migrations`.
|
|
The CLI skips recorded migrations; 019 tracks resumable transactional, batch,
|
|
and concurrent-index phases. Confirm 018 and 019 in `schema_migrations` and
|
|
check `assertPublishingSchema` using the runtime URL before starting new code.
|
|
|
|
Build `api scheduler web`, then run `up -d --no-deps api`, followed by
|
|
`scheduler` and `web`. Verify API health/readiness, invite regressions, an
|
|
existing username/password login, scheduler readiness and Temporal connectivity,
|
|
and the public web app. Defer production publish tests until explicitly requested.
|
|
For rollback, restore source from the previous commit, re-sync, rebuild and
|
|
restart the three services; do not undo migrations automatically.
|
|
|
|
## 0. Prerequisites on the box
|
|
|
|
- Debian 12 / Ubuntu 24.04, 4+ vCPU, 8 GB RAM, 100 GB disk
|
|
- Docker Engine + compose plugin (`curl -fsSL https://get.docker.com | sh`)
|
|
- git
|
|
|
|
## 1. Get the code + secrets
|
|
|
|
```bash
|
|
git clone git@gitlab.example.com:mediatools/tacticalmediahub.git
|
|
cd tactical-media-broadcast-hub/infra/occupymedia
|
|
|
|
cp .env.example .env
|
|
# Fill: CLOUDFLARE_TUNNEL_TOKEN, R2_ACCESS_KEY, R2_SECRET_KEY (dashboard → R2 →
|
|
# Manage API Tokens → Object Read & Write, scoped to the occupymedia-* buckets),
|
|
# POSTGRES_PASSWORD, PEERTUBE_DB_PASSWORD, S3_KEK (openssl rand -hex 32 — ONE TIME,
|
|
# it wraps every stored blob; lose it = archive unreadable)
|
|
|
|
# peertube.env: set both PEERTUBE_DB_PASSWORD / POSTGRES_PASSWORD lines to the
|
|
# value from .env, and set a registration/SMTP sender if you have them.
|
|
```
|
|
|
|
## 2. Build + start the core
|
|
|
|
```bash
|
|
docker compose build api scheduler web media-worker
|
|
docker compose up -d postgres redis temporal peertube-postgres
|
|
```
|
|
|
|
## 3. Apply API migrations (ordered, once)
|
|
|
|
```bash
|
|
cd ../..
|
|
for f in apps/api/migrations/*.sql; do
|
|
docker compose -f infra/occupymedia/docker-compose.yml exec -T postgres \
|
|
psql -U "$(grep -oP '(?<=POSTGRES_USER:)\S+' infra/occupymedia/docker-compose.yml | head -1 | tr -d '${:-occupymedia}')" \
|
|
-d occupymedia -v ON_ERROR_STOP=1 < "$f" || { echo "FAILED at $f"; break; }
|
|
done
|
|
```
|
|
|
|
(If psql errors on an already-applied migration — re-runs are idempotent-safe
|
|
for most files; check the failing file before continuing.)
|
|
|
|
## 4. Bring everything up
|
|
|
|
```bash
|
|
docker compose -f infra/occupymedia/docker-compose.yml up -d
|
|
docker compose ps # everything Up/healthy
|
|
```
|
|
|
|
## 5. First-boot steps
|
|
|
|
1. **Tunnel**: `docker compose logs cloudflared` — should show 4 connections
|
|
registered. Then `https://app.occupymedia.cc` (control room), `/healthz` on
|
|
api., tube., chat. should all answer.
|
|
2. **PeerTube admin**: `docker compose logs peertube 2>&1 | grep -i -A3 password`
|
|
— random root password printed ONCE. Log in at tube.occupymedia.cc, change
|
|
it, then run the brand sync (`docs/peertube-setup.md`) and create the live
|
|
channel.
|
|
3. **Matrix admin**: register the first account at chat.occupymedia.cc with the
|
|
token from conduwuit.toml, then set `allow_registration = false` and
|
|
`docker compose restart conduwuit`. Configure Element as client
|
|
(`docs/matrix-setup.md`).
|
|
4. **Telegram bridge** (optional now): `docker compose --profile bridges run --rm
|
|
mautrix-telegram` → interactive login with the bot/user token; then
|
|
`docker compose --profile bridges up -d mautrix-telegram`.
|
|
|
|
## 6. Live ingest (deferred)
|
|
|
|
RTMP/SRT cannot traverse the tunnel. Options documented in the session notes:
|
|
tiny relay VPS + WireGuard to this box (recommended; keeps the home IP out of
|
|
DNS), or direct port-forward. Until then the 24/7 channel runs playlists/VOD
|
|
(media-worker pushes to PeerTube internally over the docker network).
|
|
|
|
## 7. Operations
|
|
|
|
- **Backup**: `docker compose exec postgres pg_dump -U occupymedia occupymedia |
|
|
gzip > backup-$(date +%F).sql.gz` + `tar` the named volumes (pgdata,
|
|
peertube_pgdata, conduwuit_data). Media itself is in R2 (durable) — KEK
|
|
backup matters more than volume backup.
|
|
- **Update**: `git pull && docker compose build api scheduler web media-worker &&
|
|
docker compose up -d` (migrations: repeat step 3 for NEW files only).
|
|
- **Logs**: `docker compose logs -f --tail=100 <service>`.
|
|
|
|
## Known simplifications (first-production-pass)
|
|
|
|
- observability (otel/prometheus/grafana) not included — add from
|
|
infra/docker-compose.yml services when needed
|
|
- single app instance per service (no horizontal scaling — intended at this size)
|
|
- file-sharing/collab layer: oCIS S3NG + optional Collabora scaffold in
|
|
`files/compose.yml`; deployment and Matrix migration remain separate prerequisites
|