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>
|
||
|---|---|---|
| .. | ||
| scripts | ||
| .env.example | ||
| .gitignore | ||
| docker-compose.yml | ||
| init-db.sql | ||
| kong.yml | ||
| README.md | ||
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
- Edit
.env— changeVPS_HOST,VPS_PASSWORD(or switch toVPS_AUTH=key),VPS_DEPLOY_ROOTif needed. - Rotate secrets:
This regenerates./scripts/gen-secrets.shPOSTGRES_PASSWORD,JWT_SECRET,ANON_KEY,SERVICE_ROLE_KEY,DASHBOARD_PASSWORD, etc., and writes them back to.env. The old.envis backed up. - Sync data:
./scripts/sync-data.sh - 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
/webdir) - 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/HTTPSand 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.