Migrations:
- 0004_investigation_bureau.sql: 7 new tables (investigation_jobs + evidence,
hypotheses, contradictions, witnesses, gaps, residual_uncertainties), id
sequences, pg_notify trigger on investigation_jobs, RLS read-only public,
investigator role with least-privilege grants (no service_role).
- 0005_investigator_write_policies.sql: fixup adding RLS INSERT/UPDATE
policies bound to investigator + service_role + postgres (RLS with only a
SELECT policy was silently blocking the worker's claim UPDATE).
investigator-runtime/ (new Bun + TS container):
- src/main.ts: LISTEN/NOTIFY poller, claim-with-SKIP-LOCKED, drain pool,
healthcheck file, graceful SIGTERM shutdown.
- src/orchestrator.ts: chief-detective dispatch (evidence_chain → Locard).
Marks job failed when all per-item outputs error; surfaces first errors.
- src/lib/{env,pg,audit,ids,claude}.ts: typed config (gate #8), pool +
dedicated LISTEN client, NDJSON audit, sequence allocator (E-NNNN etc),
claude -p subprocess with quota detection (api_error_status=429).
- src/tools/write_evidence.ts: schema-validate (grade A/B/C custody steps),
resolve chunk_pk via FK, verify verbatim_excerpt actually appears in
chunk content, INSERT + render case/evidence/E-NNNN.md + audit.
- src/detectives/locard.ts: load chunk → call Claude with locard.md system
prompt → parse strict JSON → call writeEvidence locally.
- Dockerfile installs `claude` CLI (OAuth) at build time.
Compose:
- new `investigator` service builds from investigator-runtime/, connects
with low-privilege role, mounts case/ RW and wiki/+raw/ RO, 512m mem cap.
Web:
- /api/admin/investigate/test (POST+GET) gated by middleware (W0-F1).
POST creates a job, GET polls status. For W3.6 it becomes the chat tool.
End-to-end smoke: INSERT job → pg_notify → claim → Locard dispatch →
claude subprocess invoked. Auth works (CLI v2.1.150). Currently quota
exhausted (weekly limit · resets 3pm UTC) — pipeline catches the typed
isQuota error, marks job failed with surfaced reason. Architecture proven;
quota reset enables real evidence creation.
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.