Encode Buffer values as \x-hex before they cross the supabase-js boun...
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.