Repository navigation
wasm-inline has no lock context — and explains its absence incorrectly #793
Description
The wasm-inline entry cannot produce or read identity-bound ciphertext. lockContext appears in packages/stack/src/wasm-inline.ts exactly once — in a comment stating it is absent (:665) — and nowhere in the implementation.
This looks like a port gap rather than a deliberate limitation, and the way it is currently explained compounds it.
Authentication and lock context are orthogonal — the current framing conflates them
wasm-inline.ts:665 says there is no .withLockContext() because "identity-bound encryption on the edge is configured at client construction via config.authStrategy instead". skills/stash-edge repeats this as "Identity-bound encryption is configured, not chained."
That is not what an auth strategy does. There are two separate mechanisms on the native entry:
- Authentication —
OidcFederationStrategyasconfig.authStrategy. Authenticates the client as the end user. This is the part that changed: per-operation CTS tokens were removed in protect-ffi 0.25, andLockContext.identify()is deprecated accordingly (identity/index.ts:119). - Key binding —
.withLockContext({ identityClaim }). Binds the data key to a claim, per operation. This did not go away and is still required to get identity-bound encryption.
What changed on native is (1). config.authStrategy replaced the old token-fetching ceremony — it did not replace .withLockContext(). The WASM entry picked up (1) and not (2), and the comment then described the gap as if (1) subsumed (2).
The capability exists below the wrapper
protect-ffi 0.30 accepts a lock context on both the single and bulk paths:
export type EncryptOptions = { plaintext; column; table; lockContext?: Context; unverifiedContext? }
export type EncryptPayload = { plaintext; column; table; lockContext?: Context }
export type Context = { identityClaim: string[] }The WASM binding's opts are typed any (dist/wasm/protect_ffi.d.ts) because they cross a serde boundary into the same Rust core, so the shape is almost certainly already accepted — it is simply not typed or plumbed on the JS side. Worth confirming against the Rust before scoping the work.
Impact
- Data written from an edge function cannot be identity-bound at all.
- Data written on Node with a lock context cannot be decrypted on the edge, because decrypt needs the same lock context. That is a silent split in what the two entries can read, on top of the schema nominal-typing incompatibility already documented in
skills/stash-edge. - Supabase Edge Functions are the flagship WASM use case and the most likely place to want per-user key binding, since the JWT is right there.
Suggested work
- Confirm the WASM binding accepts
lockContextin its serde payloads. - Plumb it through
WasmEncryptionClient—.withLockContext()if the chainable shape is wanted, or a per-call option consistent with whatever this entry settles on (see the bulk-shape issue). - Fix
wasm-inline.ts:665regardless of whether the feature lands: it currently teaches that an auth strategy is identity-bound encryption, which is wrong on both entries. - Fix the corresponding section in
skills/stash-edge(landing in feat(cli): add the stash-postgres and stash-edge skills — raw-SQL predicates and the WASM entry #777) — same error, and it ships to customers.
Docs note
The relationship between the two mechanisms is not clearly stated anywhere. Splitting it out into a dedicated auth skill is tracked separately.
Activity
coderdan commentedon Jul 25, 2026
Parent design issue: #798 — this is one of three symptoms of a single missing seam (the FFI backend is imported by the operation classes rather than injected, so
wasm-inlinecould not reuse them and reimplemented the surface).Worth settling the surface question in #798 first: taken independently, each of these three hard-codes an answer the other two have to live with.
docs(skills,stack): document the wasm entry as having no lock context
feat(cli): add the stash-postgres and stash-edge skills — raw-SQL pre…
feat(stack)!: WASM entry adopts the shared encrypt operation (#798, s…
feat(cli): add the stash-postgres and stash-edge skills — raw-SQL pre…
feat(cli): add the stash-postgres and stash-edge skills — raw-SQL pre…
tobyhede commentedon Sep 17, 2026
Closing as Completed for the documentation/rationale defect tracked here. The incorrect explanation for the missing WASM lock-context surface has been corrected in the current source and skills.
The capability itself remains open in #797, under the shared-operation-layer design in #798. Closing this issue does not claim that
.withLockContext()is already implemented on the WASM entry.