disclosure-bureau/infra/disclosure-stack
Luiz Gustavo 55cac8a395
Some checks failed
CI / Web — typecheck + lint + build (push) Failing after 1m30s
CI / Scripts — Python smoke (push) Failing after 32s
CI / Web — npm audit (push) Failing after 37s
W0+W1+W1.2: security hardening, observability, autocomplete, glitchtip, forgejo CI
W0 — security hardening (5 fixes verified live on disclosure.top)
- middleware: gate /api/admin/* same as /admin/* (F1)
- imgproxy: tighten LOCAL_FILESYSTEM_ROOT from / to /var/lib/storage (F2)
- studio: real basic-auth label (bcrypt hash, middleware reference) (F3)
- relations: ENABLE ROW LEVEL SECURITY + public SELECT policy (F4)
- migration 0003: fold is_searchable + hybrid_search update into canonical (TD#2)

W1 — observability + resilience + autocomplete
- studio: HOSTNAME=0.0.0.0 so Next.js binds on loopback for healthcheck
- compose: PG_POOL_MAX=20, CLAUDE_CODE_OAUTH_TOKEN gated by separate env
- claude-code.ts: subprocess timeout configurable (CLAUDE_CODE_TIMEOUT_MS)
- openrouter.ts: retry with exponential backoff + Retry-After + in-memory
  circuit breaker (promotes FALLBACK after CB_THRESHOLD failures)
- lib/logger.ts: pino logger (NDJSON prod / pretty dev) + withRequest helper
- middleware: mints correlation_id, stamps x-correlation-id response header,
  emits structured http_request log per /api/* call
- messages/route.ts: switch to structured logger
- 60_meili_index.py: push documents + chunks into Meilisearch
- /api/search/autocomplete: parallel meili search (docs + chunks), 5-8ms p50
- search-autocomplete.tsx: debounced dropdown wired into search-panel

W1.2 — Glitchtip + Forgejo self-hosted
- compose: glitchtip-redis + glitchtip-web + glitchtip-worker (v4.2)
- compose: forgejo + forgejo-runner (server v9, runner v6) with group_add=988
- @sentry/nextjs SDK wired (instrumentation.ts + sentry.{client,server}.config.ts)
- /api/admin/throw smoke endpoint (gated by W0-F1 middleware)
- Synthetic event ingestion verified at glitchtip.disclosure.top
- forgejo.disclosure.top up, repo discadmin/disclosure-bureau created,
  runner registered (labels: ubuntu-latest, docker)
- .forgejo/workflows/ci.yml: typecheck + lint + build + npm audit + python
  syntax + compose validation

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 18:18:42 -03:00
..
scripts baseline: Disclosure Bureau pipeline + Next.js UI + Supabase stack 2026-05-17 22:44:36 -03:00
.env.example baseline: Disclosure Bureau pipeline + Next.js UI + Supabase stack 2026-05-17 22:44:36 -03:00
.gitignore baseline: Disclosure Bureau pipeline + Next.js UI + Supabase stack 2026-05-17 22:44:36 -03:00
docker-compose.yml W0+W1+W1.2: security hardening, observability, autocomplete, glitchtip, forgejo CI 2026-05-23 18:18:42 -03:00
init-db.sql baseline: Disclosure Bureau pipeline + Next.js UI + Supabase stack 2026-05-17 22:44:36 -03:00
kong.yml ship: synthesize 158 entities, AG-UI artifacts, chat persistence, auth flow 2026-05-18 03:52:59 -03:00
README.md baseline: Disclosure Bureau pipeline + Next.js UI + Supabase stack 2026-05-17 22:44:36 -03:00

disclosure-stack — portable deployment

Single-folder deployment unit. Edit .env, run scripts, app deploys to the VPS.

When migrating to another VPS: change ONLY the VPS_ block in .env*, run ./scripts/gen-secrets.sh (regenerates per-VPS secrets), then ./scripts/deploy.sh. Done.

Layout

infra/disclosure-stack/
├── .env                    ← active config (gitignored, secrets in here)
├── .env.example            ← template, safe to commit
├── docker-compose.yml      ← TODO — supabase + next + meili + imgproxy
└── scripts/
    ├── _lib.sh             ← shared SSH/rsync helpers
    ├── ssh.sh              ← interactive SSH or one-shot remote command
    ├── status.sh           ← VPS + stack health report
    ├── gen-secrets.sh      ← rotate per-VPS secrets (JWT, Postgres, etc.)
    ├── sync-data.sh        ← rsync wiki/processing/raw to VPS
    ├── deploy.sh           ← upload + docker compose up
    └── logs.sh             ← tail logs of a service

Pre-reqs on your laptop

brew install hudochenkov/sshpass/sshpass  # for password SSH (testing VPS)

For real production, generate an SSH key, copy it to the VPS, and switch VPS_AUTH=key in .env.

Daily ops

# Open shell on VPS
./scripts/ssh.sh

# One-shot command
./scripts/ssh.sh "docker ps"

# Full health report
./scripts/status.sh

# Tail Postgres logs
./scripts/logs.sh postgres

# Push fresh wiki data (after running the local pipeline)
./scripts/sync-data.sh

# Deploy stack changes
./scripts/deploy.sh

Migrating to a different VPS

  1. Edit .env — change VPS_HOST, VPS_PASSWORD (or switch to VPS_AUTH=key), VPS_DEPLOY_ROOT if needed.
  2. Rotate secrets:
    ./scripts/gen-secrets.sh
    
    This regenerates POSTGRES_PASSWORD, JWT_SECRET, ANON_KEY, SERVICE_ROLE_KEY, DASHBOARD_PASSWORD, etc., and writes them back to .env. The old .env is backed up.
  3. Sync data:
    ./scripts/sync-data.sh
    
  4. Deploy:
    ./scripts/deploy.sh
    

That's it. The new VPS now hosts the full stack with fresh secrets, isolated from the old one.

What still needs to be built

The docker-compose.yml itself. Will include:

  • Supabase Postgres + GoTrue + PostgREST + Storage + Kong + Studio + Realtime
  • Next.js (built from this repo's /web dir)
  • Meilisearch
  • Imgproxy
  • Caddy (TLS + reverse proxy on subdomains from .env)
  • restic-cron for backups (if BACKUP_ENABLED=true)

I'll generate that next.

Coexistence with existing VPS projects

On the testing VPS, 8 other Supabase-based stacks are already running (unimed-, irmed-, v2irmed-, top10-, cf-, nirvana-, plegal-*). This stack:

  • Uses unique container names (disclosure-* prefix)
  • Uses unique host ports (PORT_* block in .env, all 18xxx)
  • Mounts its own data volumes under /data/disclosure/
  • Caddy on this stack only binds to PORT_KONG_HTTP/HTTPS and friends — does NOT take 80/443

When you move to the dedicated 4cpu/16GB VPS, you can:

  • Keep ports as-is (works)
  • OR remap PORT_KONG_HTTP=80, PORT_KONG_HTTPS=443 since nothing else uses them

The stack is portable in both directions.