Encode Buffer values as \x-hex before they cross the supabase-js boun...

✅ merged · #7 · easterbrooka/mike ← easterbrooka/mike · opened 16d ago by easterbrooka · merged 16d ago by easterbrooka · self · +125-20 across 6 files · ↗ on GitHub

From the PR description

...dary

supabase-js builds request bodies with JSON.stringify (postgrest-js PostgrestBuilder.ts:251), which means a raw Node Buffer ends up on the wire as {"type":"Buffer","data":[...]} - PostgREST cannot coerce that to a bytea column, so the first sealForUser call in prod would have 500'd. Same failure mode for .eq() filters on shared_with_email_hmac: the Buffer is toString'd to UTF-8 garbage in the URL parameter.

PostgREST already returns bytea as \x-prefixed lowercase hex on the read side, which byteaToBuffer was decoding correctly; the fix is the inverse helper bufferToBytea and a discipline of wrapping every Buffer that reaches an insert/update/upsert/eq against a bytea column.

Touched call sites:

  • crypto/migrate.ts - exports bufferToBytea; wraps wrapped_dek in both tenant_deks insert paths (tenantCrypto.ensureActiveDek and ensureUserHasDek).
  • routes/user.ts - wraps the sealed _ct value in the dual-write update.
  • routes/workflows.ts - wraps shared_with_email_ct, _hmac on the upsert, plus the two .eq("shared_with_email_hmac", ...) filters.
  • lib/chatTools.ts - wraps the .eq filter in buildWorkflowStore.
  • scripts/backfill_envelope.ts - wraps every _ct and _hmac value before the update.

Test coverage that locks this in:

  • crypto/tests/migrate.test.ts FakeSupabase now rejects raw Buffer on insert with the same error real PostgREST would surface - mirrors the wire contract so any regression breaks the test.
  • Round-trip test for bufferToBytea ↔ byteaToBuffer.
  • Explicit "why is bufferToBytea needed" test that demonstrates a raw Buffer JSON-stringifies to a bytea-incompatible shape; doubles as living documentation of the failure mode.
  • Explicit "FakeSupabase rejects raw Buffer" guardrail test.

88->90 backend tests passing. No behaviour change for the read path - byteaToBuffer was already correct.

Our analysis

Wrap Buffers as bytea literals before they hit PostgREST — read the full analysis →

Think the analysis missed something the PR description covers?

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-7.md from inside the repo you want the changes in.

⬇ Download capture-pull-7.md