Summary
Several developer-facing surfaces in this repo still teach new LockContext().identify(jwt) for identity-aware encryption. Per-operation CTS tokens were removed in protect-ffi 0.25, and the shipped types now mark identify() @deprecated:
Per-operation CTS tokens were removed in protect-ffi 0.25. Authenticate the client as the user with an OidcFederationStrategy (config.authStrategy) instead, and pass the claim to .withLockContext(). The token fetched here is no longer used by encryption operations.
This is a quiet failure. Code using identify() compiles, logs a deprecation warning, and silently does not bind the value to an identity. Anyone following these examples believes they have per-user encryption and does not.
The docs site is being fixed in cipherstash/docs#57. This issue covers the surfaces that ship from this repo.
The correct API
import { Encryption, OidcFederationStrategy } from "@cipherstash/stack"
const client = await Encryption({
schemas: [users],
config: {
authStrategy: OidcFederationStrategy.create(workspaceCrn, () => getUserJwt()),
},
})
await client
.encrypt(value, { column: users.email, table: users })
.withLockContext({ identityClaim: ["sub"] })
What needs updating
1. Agent skills (highest impact)
The cipherstash/stack skill pack is installed into user projects with npx skills add cipherstash/stack, and its whole purpose is to stop AI coding agents from hallucinating API surfaces. Right now it teaches agents to generate the deprecated pattern, so agents will keep emitting silently-broken identity code until the skills are updated.
stash-encryption — advertises "Identity-aware encryption with LockContext and JWT-based access control"
stash-supabase — advertises "Identity-aware encryption with .withLockContext()"
Both should cover OidcFederationStrategy via config.authStrategy, and .withLockContext({ identityClaim }).
2. @cipherstash/stack README
The Identity-Aware Encryption section still shows the full new LockContext() → identify(userJwt) → .withLockContext(lockContext) flow. Note the repo's own root README already describes the correct API ("authenticate as the end user with OidcFederationStrategy ... bind the data key to their identity with .withLockContext({ identityClaim })"), so the two disagree.
3. TypeDoc doc comments
Two examples in the generated API reference are wrong independently of the deprecation:
EncryptionClient's class-level example passes the Result to .withLockContext(lockContext), where LockContext.identify() returns Result<LockContext, EncryptionError>. It should pass .data. LockContext's own docstring gets this right (identified.data), so the two examples contradict each other.
- The
LockContext docstring and Encryption's config.authStrategy docstring both pass OidcFederationStrategy.create(...) directly to authStrategy, while @cipherstash/auth's README unwraps the Result first (if (created.failure) ...; created.data). Whichever is right, they should agree.
Two API questions this surfaced
Supabase's withLockContext is typed narrower than the core operations. In dist/supabase/index.d.ts:
withLockContext(lockContext: LockContext): EncryptedQueryBuilder<T, FK>;
while the core operations accept LockContextInput = LockContext | Context. So on the Supabase path you cannot pass { identityClaim: ["sub"] } and must construct new LockContext({ context: { identityClaim: ["sub"] } }), which reads like the deprecated flow even though it isn't. Consider widening it to LockContextInput for consistency.
Is @cipherstash/nextjs still required? v4.1.1 (published this week) still exports protectClerkMiddleware and getCtsToken, and is still built around fetching a CTS token per request to hand to new LockContext({ ctsToken }). Since encryption operations no longer consume a CTS token, that middleware appears to have no effect on key derivation. If that's right, the package needs a deprecation note or a rewrite around OidcFederationStrategy; if it's wrong, the docs need to explain when it is still needed.
Environment
@cipherstash/stack 0.19.0
@cipherstash/auth 0.42.0
@cipherstash/protect-ffi 0.28.0
Summary
Several developer-facing surfaces in this repo still teach
new LockContext().identify(jwt)for identity-aware encryption. Per-operation CTS tokens were removed inprotect-ffi0.25, and the shipped types now markidentify()@deprecated:This is a quiet failure. Code using
identify()compiles, logs a deprecation warning, and silently does not bind the value to an identity. Anyone following these examples believes they have per-user encryption and does not.The docs site is being fixed in cipherstash/docs#57. This issue covers the surfaces that ship from this repo.
The correct API
What needs updating
1. Agent skills (highest impact)
The
cipherstash/stackskill pack is installed into user projects withnpx skills add cipherstash/stack, and its whole purpose is to stop AI coding agents from hallucinating API surfaces. Right now it teaches agents to generate the deprecated pattern, so agents will keep emitting silently-broken identity code until the skills are updated.stash-encryption— advertises "Identity-aware encryption withLockContextand JWT-based access control"stash-supabase— advertises "Identity-aware encryption with.withLockContext()"Both should cover
OidcFederationStrategyviaconfig.authStrategy, and.withLockContext({ identityClaim }).2.
@cipherstash/stackREADMEThe Identity-Aware Encryption section still shows the full
new LockContext()→identify(userJwt)→.withLockContext(lockContext)flow. Note the repo's own root README already describes the correct API ("authenticate as the end user withOidcFederationStrategy... bind the data key to their identity with.withLockContext({ identityClaim })"), so the two disagree.3. TypeDoc doc comments
Two examples in the generated API reference are wrong independently of the deprecation:
EncryptionClient's class-level example passes theResultto.withLockContext(lockContext), whereLockContext.identify()returnsResult<LockContext, EncryptionError>. It should pass.data.LockContext's own docstring gets this right (identified.data), so the two examples contradict each other.LockContextdocstring andEncryption'sconfig.authStrategydocstring both passOidcFederationStrategy.create(...)directly toauthStrategy, while@cipherstash/auth's README unwraps theResultfirst (if (created.failure) ...; created.data). Whichever is right, they should agree.Two API questions this surfaced
Supabase's
withLockContextis typed narrower than the core operations. Indist/supabase/index.d.ts:while the core operations accept
LockContextInput = LockContext | Context. So on the Supabase path you cannot pass{ identityClaim: ["sub"] }and must constructnew LockContext({ context: { identityClaim: ["sub"] } }), which reads like the deprecated flow even though it isn't. Consider widening it toLockContextInputfor consistency.Is
@cipherstash/nextjsstill required? v4.1.1 (published this week) still exportsprotectClerkMiddlewareandgetCtsToken, and is still built around fetching a CTS token per request to hand tonew LockContext({ ctsToken }). Since encryption operations no longer consume a CTS token, that middleware appears to have no effect on key derivation. If that's right, the package needs a deprecation note or a rewrite aroundOidcFederationStrategy; if it's wrong, the docs need to explain when it is still needed.Environment
@cipherstash/stack0.19.0@cipherstash/auth0.42.0@cipherstash/protect-ffi0.28.0