bmersereau/mike

A staging ground for upstream fixes: bmersereau is quietly running a security audit on Mike, one pull request at a time.

A dense burst: 35 threads of work between mid-May and mid-June 2026, then nothing since June 13, so this reads as either a completed audit or a pause.

View on GitHub →

There's no separate product to try here. This fork carries no changes of its own; it exists so bmersereau can file proposals against the main Mike project. Clone it and run it, and you'd get stock Mike.

The work itself reads like a security audit in slow motion. Over about four weeks, bmersereau filed proposals covering how Mike encrypts the API keys users hand it, how long shared download links stay valid, whether the database can prove which account owns each record, and what happens when a deployment boots with credentials missing. One theme keeps recurring: nothing should fail quietly. A misconfigured server should refuse to start instead of limping along trusting whatever login token shows up.

Not everything landed. Two of the sharper security proposals were closed without merging, including one that would have shut a fail-open login path. Plenty remain open, alongside quieter reliability work (paginated chat history, capped bulk exports, a timeout on hung chat streams), a fix for a sharing bug that locked invited collaborators out, and a draft contributing guide, which is the kind of thing you write when you plan to stick around. We know nothing about bmersereau beyond the handle, but the pattern is consistent: an outside contributor doing unglamorous hardening work on legal software that handles sensitive documents.

What's in it

Direction

securityinfrastructurecompliance

Activity

Themed changes and pull requests touching this fork, newest first. Themed changes that haven't been turned into a public post yet still appear — they're real work even without a published writeup.

Show 50 more
📝 Extract a reusable admin client singleton in auth middleware 0 commits minor change
📝 Add a contributing guide codifying contribution norms 0 commits minor change
📝 Make per-purpose secrets mandatory and enforce isolation at boot 0 commits securityinfrastructure draft
bmersereau removes the silent fallbacks that let the download token signing secret and the user API key encryption secret quietly reuse the Supabase service key. Both secrets are now required, must be set explicitly, and…
📝 Add expiry to HMAC-signed download tokens, with 30-day default TTL 0 commits securityinfrastructure draft
bmersereau proposed adding an `exp` Unix timestamp to every HMAC-signed download token and rejecting tokens past their expiry on verification. Legacy tokens without `exp` remain valid, so nothing breaks on deploy. The PR…
📝 Harden document uploads against XSS and tighten CSP 0 commits securitycompliance draft
bmersereau's PR closes three separate security issues at once: stored XSS via malicious uploads, missing Content-Security-Policy headers, and unpatched `@xmldom/xmldom` CVEs. The core fix is magic-byte validation on both…
📝 Strip user and document IDs from edit-resolution INFO logs 0 commits compliancesecurity draft
bmersereau opens a PR against upstream to remove INFO-level log lines in the edit-resolution handler that were emitting user identifiers, document identifiers, and edit identifiers alongside internal handler state - all …
📝 Fix timingSafeEqStr to run the comparison before checking length 0 commits securityinfrastructure draft
The `timingSafeEqStr` helper guarding download tokens was short-circuiting on length mismatch before invoking the constant-time comparison underneath. That early return undermines the whole contract: an attacker with tim…
📝 Guard Supabase env vars in the backend 0 commits minor change
📝 Hoist R2 S3Client to a module-level singleton 0 commits infrastructureknowledge-management draft
The Mike backend was allocating a fresh `S3Client` on every R2 operation. bmersereau's PR hoists it to a module-level singleton with lazy initialization, so the HTTP layer can reuse keep-alive connections instead of reop…
📝 Singleton admin client in requireAuth middleware 0 commits minor change
📝 Paginate GET /chat with limit and before-cursor parameters 0 commits chat-uiinfrastructure draft
The `GET /chat` endpoint returns an unbounded list of a user's chats. bmersereau adds a `limit` parameter (default 50, cap 200) and a `before` ISO timestamp cursor, letting callers walk back through chat history one page…
📝 Add 180-second timeout to SSE chat streams via Promise.race 0 commits chat-uiinfrastructure draft
bmersereau proposed wrapping the streaming LLM call in both chat routes with a `Promise.race` against a 180-second deadline. Stalled connections now emit a structured SSE error and close cleanly instead of hanging. The P…
📝 Convert user_id columns from text to uuid with FK constraints 0 commits infrastructuremulti-tenant draft
Nine tables in the Mike backend carry `user_id` as plain text, with no referential link to the auth layer. bmersereau's PR converts all of them to the `uuid` type and adds proper foreign keys against `auth.users(id)`, wr…

Threads of work (detailed view)

31 threads have been distilled into posts.

Convert user_id columns from text to uuid with FK constraints

Nine tables in the Mike backend carry `user_id` as plain text, with no referential link to the auth layer. bmersereau's PR converts all of them to the `uuid` type and adds proper foreign keys against `auth.users(id)`, wrapped in a single transaction so the change is all-or-nothing.

Add 180-second timeout to SSE chat streams via Promise.race

bmersereau proposed wrapping the streaming LLM call in both chat routes with a `Promise.race` against a 180-second deadline. Stalled connections now emit a structured SSE error and close cleanly instead of hanging. The PR is open.

Paginate GET /chat with limit and before-cursor parameters

The `GET /chat` endpoint returns an unbounded list of a user's chats. bmersereau adds a `limit` parameter (default 50, cap 200) and a `before` ISO timestamp cursor, letting callers walk back through chat history one page at a time.

Hoist R2 S3Client to a module-level singleton

The Mike backend was allocating a fresh `S3Client` on every R2 operation. bmersereau's PR hoists it to a module-level singleton with lazy initialization, so the HTTP layer can reuse keep-alive connections instead of reopening sockets per request.

Fix timingSafeEqStr to run the comparison before checking length

The `timingSafeEqStr` helper guarding download tokens was short-circuiting on length mismatch before invoking the constant-time comparison underneath. That early return undermines the whole contract: an attacker with timing access could distinguish a length mismatch from a content mismatch.

Strip user and document IDs from edit-resolution INFO logs

bmersereau opens a PR against upstream to remove INFO-level log lines in the edit-resolution handler that were emitting user identifiers, document identifiers, and edit identifiers alongside internal handler state - all going to whatever log aggregator the deployment uses.

Normalize shared_with email casing across all project access paths

Project sharing in Mike was silently failing for users whose stored email casing didn't match the lookup. bmersereau traces the root cause to three different normalization rules scattered across four endpoints, extracts them into a single access module, and routes every project route through it.

Harden document uploads against XSS and tighten CSP

bmersereau's PR closes three separate security issues at once: stored XSS via malicious uploads, missing Content-Security-Policy headers, and unpatched `@xmldom/xmldom` CVEs. The core fix is magic-byte validation on both upload paths before a file is accepted.

Add expiry to HMAC-signed download tokens, with 30-day default TTL

bmersereau proposed adding an `exp` Unix timestamp to every HMAC-signed download token and rejecting tokens past their expiry on verification. Legacy tokens without `exp` remain valid, so nothing breaks on deploy. The PR is open against upstream.

Make per-purpose secrets mandatory and enforce isolation at boot

bmersereau removes the silent fallbacks that let the download token signing secret and the user API key encryption secret quietly reuse the Supabase service key. Both secrets are now required, must be set explicitly, and must be distinct from each other and from the Supabase key - enforced at startup before the server accepts any traffic.

Pull requests (detailed view)

35 PRs touch this fork — inbound (filed against it) or outbound (filed from it). State icons match the editorial dashboard.

🟢 Open (28)

Show 18 more open

⛔ Closed without merge (7)