Skip to content

wasm-inline has no lock context — and explains its absence incorrectly #793

Description

@coderdan

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:

  1. AuthenticationOidcFederationStrategy as config.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, and LockContext.identify() is deprecated accordingly (identity/index.ts:119).
  2. 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

  1. Confirm the WASM binding accepts lockContext in its serde payloads.
  2. 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).
  3. Fix wasm-inline.ts:665 regardless of whether the feature lands: it currently teaches that an auth strategy is identity-bound encryption, which is wrong on both entries.
  4. 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.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions