Self-hosted Docker stack (embedded Supabase + storage + email) and local Ollama models (supersedes #196)
From the PR description
Supersedes #196 by @thijsdezoete, whose work this is - all six of his commits are carried over verbatim with authorship preserved (cherry-picked from his branch, MERGEABLE against current main). The original PR has been stalled on a CI failure; the fix for it was submitted to his fork as thijsdezoete/mike#1 but hasn't been merged there, so this PR re-submits his stack from a branch we can maintain, with that one fix added on top.
What this adds (from #196, unchanged)
- Self-hosted Docker stack: docker-compose with embedded Supabase (db, auth, REST, storage, gateway), MinIO object storage, and email - a one-command local/self-hosted deployment of the full Mike platform.
- Local Ollama models: optional local LLM support surfaced in the model picker (
useOllamaModels, availability wiring), plus optional bearer auth for the local/OpenAI-compatible provider. - Hardening from his follow-up commits: optional env files (hosted deploys), configurable published ports with infra bound to loopback, configurable auth URLs and signup policy.
- A design spec for legal workflows (C&D, contract triage, MSA review).
The one new commit - the e2e fix (from thijsdezoete/mike#1)
Why e2e failed on #196: the Supabase CLI auto-seeds supabase/roles.sql as globals during supabase start. On current supabase/postgres 17.x images that seed connection is not a superuser and supabase_auth_admin is a reserved role, so the ALTER ROLE fails with SQLSTATE 42501 and the CI job dies before Playwright ever runs.
Fix: the file only exists for the docker-compose stack, where it runs as the init superuser via /docker-entrypoint-initdb.d. Move it to docker/db-init/ and update the compose mount, so CI's supabase start no longer picks it up. Compose behavior is unchanged.
Verification
- Cherry-picks applied conflict-free onto current
main(20c0ed2). docker compose configvalidates (env-var warnings are expected - that's what the optional-env commit enables).backend/:npx tsc --noEmitclean;npm testgreen - 426 tests.- The compose mount now reads
./docker/db-init/roles.sql:/docker-entrypoint-initdb.d/zz-roles.sql:ro;supabase/roles.sqlis gone, so the CLI has nothing to mis-seed.
If @thijsdezoete returns, happy to close this in favour of #196 with the fix applied there instead - this exists so the work isn't blocked on an unresponsive branch.
🤖 Generated with Claude Code
Our analysis
Land a self-hosted Mike stack — read the full analysis →
Think the analysis missed something the PR description covers?
Commits in this PR (7)
| SHA | Subject | Author | Date | |
|---|---|---|---|---|
fe942475 | Add self-hosted Docker stack with local Supabase, storage, email, and Ollama | Thijs de Zoete | 2026-06-29 | ↗ GitHub |
commit bodyBring the whole app up with a single `docker compose up` and no external accounts. Self-contained stack: - Embedded Supabase (Postgres + GoTrue auth + PostgREST + an nginx gateway), with the schema auto-loaded on first boot and auth emails auto-confirmable. - RustFS for S3-compatible object storage, replacing Cloudflare R2. - Mailpit catches auth emails (signup/confirmation) in a local inbox. - Production-style image builds for the frontend and backend. Local models via Ollama: - New provider speaking Ollama's OpenAI-compatible chat API, including the agentic tool-call loop and a graceful fallback for models that don't support tools. - Models are detected dynamically (GET /models/ollama) and appear in every model picker (chat, title, tabular review) under a "Local" group, with no API key required. Any `ollama/<tag>` id is valid by prefix. - Manual "Refresh" button on the API Keys page re-detects installed models and re-checks key status, pushing updates live to the pickers. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> | ||||
b33b47af | docs: add design spec for legal workflows (C&D, contract triage, MSA review) | Thijs de Zoete | 2026-06-29 | ↗ GitHub |
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> | ||||
11bd520c | fix(compose): make env files optional so hosted deploys work | Thijs de Zoete | 2026-07-20 | ↗ GitHub |
commit bodyenv_file: ./backend/.env was a hard failure when the file is absent, which is always the case on Dokploy and similar platforms that inject env vars rather than committing a .env. Both backend/.env and the compose-root .env are now optional sources, root last so hosted values win. Secrets deliberately stay out of environment:, which overrides env_file - a bare name unset on the host silently blanks out the value the file supplied. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> | ||||
969233d8 | fix(compose): make published ports configurable, bind infra to loopback | Thijs de Zoete | 2026-07-20 | ↗ GitHub |
commit bodyFixed host ports assume a dev machine. On Dokploy, 3000 is the dashboard's, so the frontend fails with 'port is already allocated'. Each published port now takes an env override with the current value as default, so local dev is unchanged. Infra ports (postgres, mailpit, storage) now bind 127.0.0.1 instead of 0.0.0.0. They were reachable from the internet on a public host while still carrying default credentials, and the mailpit inbox exposes password-reset links. Compose service-to-service networking is unaffected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> | ||||
d26fe041 | fix(compose): make auth URLs and signup policy configurable | Thijs de Zoete | 2026-07-20 | ↗ GitHub |
commit bodyGoTrue's API_EXTERNAL_URL and SITE_URL were hardcoded to localhost, so confirmation links pointed at the user's own machine and signup could never be completed on a hosted deploy. Both now derive from the public URLs. MAILER_AUTOCONFIRM and DISABLE_SIGNUP are configurable too: a host with no real SMTP needs autoconfirm on to register at all, and open registration should be closable once the operator has an account. Defaults unchanged, so local dev still exercises the real email flow via Mailpit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> | ||||
168b6977 | feat(llm): optional bearer auth for the local/OpenAI-compatible provider | Thijs de Zoete | 2026-07-21 | ↗ GitHub |
commit bodyBare Ollama needs no auth, but OpenAI-compatible servers behind a gateway (vLLM --api-key, Open WebUI, LiteLLM) require a bearer token. OLLAMA_API_KEY, when set, is sent on both model discovery and chat completions; absent, the provider behaves exactly as before. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> | ||||
ea7cb9ab | fix(e2e): move roles.sql out of supabase/ so the CLI stops seeding it | Amalanand Muthukumaran | 2026-07-31 | ↗ GitHub |
commit bodyThe Supabase CLI auto-seeds supabase/roles.sql as globals during 'supabase start'. On the current supabase/postgres 17.x images that seed connection is not a superuser, and supabase_auth_admin is a reserved role, so ALTER ROLE fails with SQLSTATE 42501 and kills the e2e job before Playwright runs. The file only exists for the docker-compose stack, where it runs as the init superuser via /docker-entrypoint-initdb.d. Move it to docker/db-init/ and update the compose mount; CI's 'supabase start' no longer sees it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> | ||||
Capture this PR into my fork
Download a Markdown prompt that tells Claude how to port every
commit in this PR into your working tree. Run it via
claude -p < capture-pull-282.md from
inside the repo you want the changes in.