rglauco cuts Mike loose from Supabase

A self-hosted deployment path for teams that can't or won't run their legal AI on a third-party backend.

infrastructuresecurity

rglauco's fork adds a 'local' mode that swaps Supabase - the hosted Postgres-and-auth service Mike normally leans on - for a self-hosted Postgres database, standard password-and-token login, and a Docker setup you can run on your own server. A compatibility layer means every existing feature keeps working without rewrites, regardless of which backend you point it at.

The initial drop shipped with a string of bugs caught in live use - login bounces, chats failing to load, broken database writes - and rglauco patched them in rapid succession over the same session. There's no sign of automated tests covering any of it, and the bugs touched authentication, storage, and query generation. Anyone borrowing this work should take the follow-up fixes too, not just the headline commit, and pressure-test the compatibility layer before trusting it in production.

So what Relevant to any legal-tech team that needs Mike running inside their own infrastructure for data-residency, procurement, or sovereignty reasons.

View this fork on GitHub →

Spotted something wrong? Or know the PR text has fresher detail than the writeup above?

Commits in this thread

8 commits from rglauco/mike, oldest first. Source extracted verbatim from the harvested git log.

SHA Subject Author Date
b8efde9f feat: add local PostgreSQL mode and Docker Compose setup Glauco 2026-05-10 ↗ GitHub
commit body
Adds an optional AUTH_MODE=local that replaces Supabase with:
- Local PostgreSQL (via pg) for all data storage
- Custom JWT auth (bcrypt + jsonwebtoken) via /auth/login and /auth/register
- PgAdapter: a thin Supabase-compatible query builder over pg.Pool
- DbClient abstraction so all route files work in both modes unchanged

Infrastructure:
- backend/Dockerfile and frontend/Dockerfile for containerised builds
- compose.yml: frontend + backend + postgres with named volumes for persistence
- docker/postgres/init.sql: full schema without Supabase-specific auth tables
- .env.example at repo root with compose variables

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
9768aef9 fix: frontend Dockerfile uses --legacy-peer-deps for npm ci Glauco 2026-05-10 ↗ GitHub
@opennextjs/cloudflare peer dep constraint doesn't exactly match the
pinned next@16.0.3, causing npm ci to fail without the flag.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1b395c20 fix: refresh AuthContext after local sign-in/sign-up Glauco 2026-05-10 ↗ GitHub
commit body
AuthContext reads localStorage once on mount. After localSignIn() stores
the JWT, the context still had user=null, so the layout redirected back
to /login.

Fix: expose refreshAuth() in the context and call it immediately after
localSignIn/localSignUp so the state is updated before router.push().

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
139ce362 fix: prevent Supabase client crash in local auth mode Glauco 2026-05-10 ↗ GitHub
commit body
createClient("", "") throws when NEXT_PUBLIC_SUPABASE_URL is not set,
crashing any page that imports supabase. Two fixes:

1. supabase.ts: use dummy localhost URL when Supabase is not configured
   so the module loads safely; add getSessionToken() that returns the
   local JWT or Supabase session token based on NEXT_PUBLIC_AUTH_MODE.

2. Replace all supabase.auth.getSession() calls in hooks and components
   with getSessionToken() so they work in both auth modes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
88fd998d fix: pgAdapter maybeSingle, jsonb serialization for chat messages Glauco 2026-05-10 ↗ GitHub
commit body
Three bugs in local/Docker mode:

1. PgAdapter.maybeSingle() returned the full rows array instead of a
   single row because _run() only checked _singleMode, not
   _maybeSingleMode. All routes using maybeSingle() got an array back
   and treated it as "not found" → "Chat not found" on every POST /chat.

2. JavaScript arrays passed to pg as query parameters are formatted as
   PostgreSQL array literals {a,b} which is invalid for jsonb columns.
   Fixed in PgAdapter insert/update to JSON.stringify arrays before
   binding.

3. Plain strings inserted into jsonb columns (chat_messages.content for
   user messages) are not valid JSON, causing the INSERT to fail
   silently. Fixed by JSON.stringify-ing string content in chat.ts,
   projectChat.ts and tabular.ts before the INSERT.

Also added missing `workflow jsonb` column to chat_messages in both
init.sql and schema.sql - the backend was already inserting this field.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
b92cbf52 fix: expose postgres port 5432 in compose for local debugging Glauco 2026-05-10 ↗ GitHub
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
27fa98f6 fix: upsert DO NOTHING when updateCols empty, title model uses tabular model for Ollama Glauco 2026-05-10 ↗ GitHub
commit body
Two bugs:

1. PgAdapter upsert with only the conflict column (e.g. upsert({ user_id })
   on conflict user_id) produced "DO UPDATE SET " with an empty SET
   clause - invalid SQL. Now falls through to DO NOTHING when updateCols
   is empty, and supports the Supabase ignoreDuplicates:true option.
   This fixed "syntax error at end of input" on ensureProfileRow calls.

2. resolveTitleModel checked "apiKeys.ollama !== undefined" to detect
   Ollama, but the apiKeys object always initialises ollama: null (not
   undefined), so the condition was always true - every user with no cloud
   keys got title_model = local-gemma-26b which doesn't exist on Ollama.
   Fixed to check hasEnvOllama() || key.trim(), and when Ollama is the
   provider, reuse the user's configured tabular model (which they know is
   installed) instead of a hardcoded default.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
d9d8c574 Merge pull request #1 from rglauco/feat/docker-local-postgres Glauco 2026-05-10 ↗ GitHub
Feat/docker local postgres

Capture this thread into my fork

Download a single Markdown prompt that tells Claude how to port every commit above into your working tree — adapting paths and structure to match your repo. Run it via claude -p < capture-thread-220.md from inside the repo you want the changes in.

⬇ Download capture-thread-220.md