8 hand-picked UFO incidents that any enthusiast recognises:
- Kenneth Arnold 1947 (the genesis — "flying saucers" coined)
- Roswell 1947 (the original Army press release)
- Maury Island 1947 (Puget Sound, slag drop, FBI plane crash)
- Mantell 1948 (first known UFO casualty)
- Rendlesham Forest 1980 (USAF security police, deputy commander tape)
- Phoenix Lights 1997 (V-formation across Arizona, governor's reversal)
- Nimitz Tic-Tac 2004 (USS Nimitz F/A-18, gun-cam released 2017)
- Green Fireballs Sandia 1948 (copper salts, nuclear-site overflights)
Each case has:
- Bilingual hand-written editorial blurb (PT-BR + EN, no LLM)
- Painterly editorial hero illustration at 2K
- Year + tag chips (military / civilian / modern / early / naval /
aviation / mass-sighting)
- Link to /e/events/<id> when indexed in corpus, /search?q=... when not,
/c/<slug> for the green-fireballs narrative case
Components:
lib/iconic-cases.ts — IconicCase type + ICONIC_CASES editorial list
components/iconic-cases.tsx — magazine grid: first two as wide hero pair,
rest as 3-up tiles below. Hover scale on images, gradient overlays,
aspect-16/11 hero + 16/10 compact, lazy-loaded.
app/page.tsx — inserted between <FeaturedCase /> and <PortalGrid />
4 new hero illustrations generated this session:
- iconic-nimitz-tic-tac-2004.png (Nano Banana Pro)
- iconic-phoenix-lights-1997.png (Nano Banana Pro)
- iconic-rendlesham-forest-1980.png (gpt-image-1.5 via Codex)
- iconic-mantell-1948.png (Nano Banana Pro)
Per user direction: mixed generators (Nano Banana primary, Codex
co-pilot) so the homepage has stylistic variety while keeping the
painterly editorial register.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|---|---|---|
| .. | ||
| app | ||
| components | ||
| lib | ||
| .dockerignore | ||
| .env.local.example | ||
| Dockerfile | ||
| instrumentation.ts | ||
| middleware.ts | ||
| next-env.d.ts | ||
| next.config.ts | ||
| package-lock.json | ||
| package.json | ||
| postcss.config.mjs | ||
| README.md | ||
| sentry.client.config.ts | ||
| sentry.server.config.ts | ||
| tailwind.config.ts | ||
| tsconfig.json | ||
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_budgetRPC 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).