disclosure-bureau/infra
Luiz Gustavo 189a771cbe
Some checks failed
CI / Web — typecheck + lint + build (push) Failing after 38s
CI / Scripts — Python smoke (push) Failing after 3s
CI / Web — npm audit (push) Failing after 33s
CI / Retrieval — golden set (Recall@5 + MRR) (push) Failing after 4s
W3.1-W3.4: Investigation Bureau foundation — migrations, runtime, Locard
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>
2026-05-23 19:49:33 -03:00
..
coolify baseline: Disclosure Bureau pipeline + Next.js UI + Supabase stack 2026-05-17 22:44:36 -03:00
disclosure-stack W3.1-W3.4: Investigation Bureau foundation — migrations, runtime, Locard 2026-05-23 19:49:33 -03:00
embed-service baseline: Disclosure Bureau pipeline + Next.js UI + Supabase stack 2026-05-17 22:44:36 -03:00
supabase W3.1-W3.4: Investigation Bureau foundation — migrations, runtime, Locard 2026-05-23 19:49:33 -03:00
DEPLOY-CHECKLIST.md baseline: Disclosure Bureau pipeline + Next.js UI + Supabase stack 2026-05-17 22:44:36 -03:00
README.md baseline: Disclosure Bureau pipeline + Next.js UI + Supabase stack 2026-05-17 22:44:36 -03:00
RETRIEVAL.md baseline: Disclosure Bureau pipeline + Next.js UI + Supabase stack 2026-05-17 22:44:36 -03:00

Infrastructure — Disclosure Bureau

Self-hosted stack on a single VPS (16 GB / 4 CPU / 200 GB NVMe) managed via Coolify.

                   Internet (443/80)
                          │
                ┌─────────▼─────────┐
                │  Caddy (Coolify)  │ ← auto-TLS Let's Encrypt
                └────┬──────────────┘
                     │
       ┌─────────────┼──────────────────────┬──────────────────┐
       ▼             ▼                      ▼                  ▼
  ┌─────────┐   ┌──────────┐         ┌──────────┐       ┌──────────┐
  │ Next.js │   │ Supabase │         │ Supabase │       │  shared  │
  │   web   │   │ disclosure│        │ project-B │       │ services │
  │ :3000   │   │ stack    │         │  stack    │       │ Meili··· │
  └─────────┘   │  ┌─────┐ │         │  ┌─────┐ │       │ Imgproxy │
                │  │PG/GT│ │         │  │PG/GT│ │       │ Dragonfly│
                │  └─────┘ │         │  └─────┘ │       └──────────┘
                └──────────┘         └──────────┘
                disclosure.top       projeto-b.com

Components

Layer Service Notes
Orchestration Coolify v4 Self-hosted PaaS — manages all containers, TLS, backups
Database + Auth + Storage Supabase self-hosted (one per project) Each project gets own Postgres + GoTrue + Storage
Frontend Next.js 15 (this repo's /web) Deployed via Coolify Git integration
Search Meilisearch (shared) Full-text search across pages + entities
Cache + Queue Dragonfly (shared) Redis-compatible, multi-threaded
Images Imgproxy (shared) On-the-fly resize / WebP conversion
Backups restic + Backblaze B2 Nightly Postgres + Storage dumps

Quick path

  1. coolify/INSTALL.md — install Coolify on the fresh VPS (~10 min)
  2. coolify/SUPABASE.md — create the disclosure Supabase project (~5 min)
  3. Run supabase/migrations/0001_chat_schema.sql via Supabase Studio SQL editor
  4. coolify/NEXTJS.md — deploy the /web app pointing at the Supabase URL
  5. coolify/SHARED.md — bring up Meilisearch, Dragonfly, Imgproxy

Adding more projects later

For each new project, repeat step 2 (new Supabase project in Coolify UI) and step 4 (new Next.js app). They get their own subdomain, own auth, own data. Total isolation.

Local development

For dev on macOS/Linux without the VPS, see ../web/README.md — uses the Supabase CLI to spin up a local stack on localhost:54321.