Fake LLM, local Supabase runtime, and tunnel auth for shareable local demos
pwliwanow makes Mike runnable end-to-end with no cloud accounts and adds a "shareable demo" mode for putting the local stack behind a Cloudflare Tunnel. The local-runtime pieces are clean env-flag opt-ins; the tunnel auth has a deployment gotcha worth reading before you pull it.
Three pieces ship in one commit (+3536/-445, 67 files).
The local runtime puts a vendored Supabase Docker Compose setup under docker/supabase/ covering Auth, Postgres, Storage, and supporting services. Supabase Storage's built-in S3 endpoint handles blobs, so no MinIO container is needed. backend/.env.local.example configures R2_ENDPOINT_URL to point at http://localhost:8000/storage/v1/s3 with R2_FORCE_PATH_STYLE=true. There's a filesystem fallback in storage.ts (MIKE_LOCAL_STORAGE_DIR) that kicks in when R2_ENDPOINT_URL points at localhost - this exists because Supabase Storage's file backend can fail on macOS/Docker filesystems that lack extended attributes.
The fake LLM (backend/src/lib/llm/fake.ts, MIKE_FAKE_LLM=1) plugs into both streamChatWithTools and completeText. It recognizes the tabular-review and prompt-generation system prompts and returns deterministic JSON in the right shapes so the UI exercises fully without an Anthropic or Gemini key. Seven sample documents are added under samples/ so the empty-state demo has actual content to show.
The tunnel auth adds requireExposureAuth middleware that checks a BACKEND_TUNNEL_AUTH_TOKEN header or HTTP Basic credentials. It's mounted globally - before all routes including /health. The frontend injects X-Tunnel-Auth from an env var into every API call. CORS is widened to a FRONTEND_URLS comma list.
One thing to verify before deploying the tunnel auth: the frontend gate lives in frontend/src/proxy.ts, but Next.js only auto-loads middleware.{ts,js}. A file named proxy.ts is dead code unless something imports it. If the Basic Auth gate on the frontend side isn't wired up, the Cloudflare Tunnel protection is backend-only.
Also: serverApiKeyAvailability() now reports whether server-side Gemini/Claude keys are configured, and the frontend uses those flags to offer those models to signed-in users. On a single-user demo this is fine. On any multi-tenant fork it means authenticated users can run calls on the operator's keys.
The follow-up commit is a pure README restructure with no code changes.
Spotted something wrong? Or know the PR text has fresher detail than the writeup above?