disclosure-bureau/web
guto d5f6e6030a fix png-numbering: re-convert 34 zero-based docs + crop fallback
34 of 116 docs were generated with 0-based PNG numbering (p-000.png …
p-008.png) but the Sonnet chunks reference 1-based page numbers in their
YAML frontmatter (page: 9 means the 9th sheet of paper). The /api/crop
handler built p-009.png and got a 500, the browser's Next/Image surfaced
400, and the chunk rendered as a black box on screen.

Fixes:
- web/app/api/crop/route.ts: try p-NNN.png first, fall back to
  p-(NNN-1).png if the 1-based file is missing. Cheap insurance for any
  doc that comes in with the old convention.
- scripts/01-convert-pdfs.sh: previously printf '%03d' "$num" with $num
  starting at 0 (e.g. "008") raised "invalid number" because Bash
  parsed it as octal. Wrap with $((10#$num)) to force decimal — this
  was silently corrupting page sequences and producing gaps like p-001
  ... p-008, p-011 (missing p-009/p-010).
- All 34 affected docs re-converted from PDFs with the patched script;
  every directory now has continuous 1-based PNGs.
- /processing/png/ rsync'd to VPS, web redeployed.

Smoke: /api/crop?doc=doc-341-…&page=9&… now returns 200 image/webp
instead of 500. Tested in browser: chunk c0026 (diagram, p9) renders
the real engineering diagram.
2026-05-18 11:45:40 -03:00
..
app fix png-numbering: re-convert 34 zero-based docs + crop fallback 2026-05-18 11:45:40 -03:00
components ship: synthesize 158 entities, AG-UI artifacts, chat persistence, auth flow 2026-05-18 03:52:59 -03:00
lib ship: synthesize 158 entities, AG-UI artifacts, chat persistence, auth flow 2026-05-18 03:52:59 -03:00
.dockerignore baseline: Disclosure Bureau pipeline + Next.js UI + Supabase stack 2026-05-17 22:44:36 -03:00
.env.local.example baseline: Disclosure Bureau pipeline + Next.js UI + Supabase stack 2026-05-17 22:44:36 -03:00
Dockerfile baseline: Disclosure Bureau pipeline + Next.js UI + Supabase stack 2026-05-17 22:44:36 -03:00
middleware.ts baseline: Disclosure Bureau pipeline + Next.js UI + Supabase stack 2026-05-17 22:44:36 -03:00
next-env.d.ts baseline: Disclosure Bureau pipeline + Next.js UI + Supabase stack 2026-05-17 22:44:36 -03:00
next.config.ts baseline: Disclosure Bureau pipeline + Next.js UI + Supabase stack 2026-05-17 22:44:36 -03:00
package-lock.json baseline: Disclosure Bureau pipeline + Next.js UI + Supabase stack 2026-05-17 22:44:36 -03:00
package.json baseline: Disclosure Bureau pipeline + Next.js UI + Supabase stack 2026-05-17 22:44:36 -03:00
postcss.config.mjs 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
tailwind.config.ts baseline: Disclosure Bureau pipeline + Next.js UI + Supabase stack 2026-05-17 22:44:36 -03:00
tsconfig.json baseline: Disclosure Bureau pipeline + Next.js UI + Supabase stack 2026-05-17 22:44:36 -03:00

web — Disclosure Bureau Next.js app

Next.js 15 + React 19 + Tailwind + Supabase + assistant-ui.

Quick start (local dev)

# 1. Install deps
npm install

# 2. (Optional) Start local Supabase
#    Requires Docker. Skip if pointing at remote Supabase.
npx supabase init        # first time only — creates supabase/ folder
npx supabase start       # spins up Postgres/GoTrue/Storage on :54321

# 3. Configure env
cp .env.local.example .env.local
# Edit .env.local — paste local Supabase keys (printed by `supabase start`)

# 4. Apply migrations
psql postgresql://postgres:postgres@localhost:54322/postgres \
  -f ../infra/supabase/migrations/0001_chat_schema.sql

# 5. Start dev
npm run dev
# http://localhost:3030

Without Supabase

The app degrades gracefully if Supabase env vars are unset:

  • Wiki browsing works (read-only from filesystem)
  • Auth bar shows "auth: disabled (dev)"
  • Chat bubble shows "Auth not configured"

Useful for quick UI work without spinning up Docker.

Production (Coolify on VPS)

See ../infra/coolify/. Stack:

  • Coolify orchestrates everything
  • Supabase self-hosted: db.disclosure.top, studio.disclosure.top
  • Next.js: disclosure.top
  • Meilisearch (shared): search.disclosure.top
  • Imgproxy (shared): img.disclosure.top
  • Caddy: TLS + reverse proxy (built into Coolify)

Architecture

app/
├── page.tsx                          # home — 116 docs grouped by collection
├── auth/
│   ├── signin/page.tsx               # magic-link form
│   ├── callback/route.ts             # exchanges code for session
│   └── signout/route.ts
├── d/[docId]/
│   ├── page.tsx                      # doc detail
│   └── [page]/page.tsx               # page reader (OCR + entity highlights + crops + sidebar PNG)
├── api/
│   ├── me/route.ts                   # GET current profile
│   ├── sessions/route.ts             # GET list, POST new
│   ├── sessions/[id]/route.ts        # GET detail, PATCH, DELETE
│   ├── sessions/[id]/messages/route.ts  # POST send → assistant reply
│   ├── documents/, pages/, entities/, tables/  # read-only data
│   └── static/[...path]/route.ts     # sandboxed file serve
components/
├── chat-bubble.tsx                   # floating Sherlock — auth-aware, session list
├── entity-modal.tsx                  # opens on entity click
├── reader-content.tsx                # OCR + highlights + crops
└── auth-bar.tsx                      # sign in / out + budget tracker
lib/
├── wiki.ts                           # markdown reader (gray-matter)
├── entity-index.ts                   # match loader + text segmentation
└── supabase/{server,client}.ts       # SSR helpers
middleware.ts                         # session refresh on every request

Tech notes

  • No RAG: chat agent reads markdown directly. Wiki-link traversal substitutes for vector search.
  • RLS-first: Supabase Row Level Security enforces "user sees only own sessions" at the DB layer.
  • Magic-link auth: no passwords. GoTrue handles email delivery.
  • Anti-abuse: per-user budget cap (default $5) + daily message quota (default 100) enforced via check_budget RPC before each Claude call.

Cost

Each chat turn costs ~$0.005-0.05 depending on context size (mostly Haiku $1/M input, $5/M output).