feat(stack): fall back to env credentials on the wasm-inline entry#801
feat(stack): fall back to env credentials on the wasm-inline entry#801coderdan wants to merge 1 commit into
Conversation
The native entry resolves an omitted credential from `CS_*` env vars or
`~/.cipherstash`, inside protect-ffi. The WASM entry had no fallback, so
every edge caller plumbed four values by hand even in Deno, Node, and Bun
where the environment is right there.
`config` is now optional and each field falls back individually:
`clientId`/`CS_CLIENT_ID`, `clientKey`/`CS_CLIENT_KEY`,
`workspaceCrn`/`CS_WORKSPACE_CRN`, `accessKey`/`CS_CLIENT_ACCESS_KEY`.
An explicit value always wins, so nothing changes for callers passing a
full config.
This lives here rather than in protect-ffi deliberately. The stack already
depends on `@cipherstash/auth`, already builds `AccessKeyStrategy` for the
access-key path, and already owns the `config.authStrategy` concept.
protect-ffi's wasm entry is raw wasm-bindgen output with no JS layer, and
its auth seam is duck-typed (`{ getToken }`) — defaulting there would have
meant a new JS wrapper module plus a hard dependency on `@cipherstash/auth`,
inverting that seam for a fallback only this layer needs.
Three details that are the substance of the change:
- **`typeof process`, not `process?.env`.** This module is the edge entry.
In a Worker or browser `process` is UNDECLARED, so a bare read is a
`ReferenceError` on client construction, and optional chaining does not
help — it still evaluates the identifier. A test deletes the global to
reproduce that runtime honestly rather than asserting against an empty
`process.env`. This is the same trap that broke the wasm entry on import
in #798, so it is guarded from the start here.
- **`clientId`/`clientKey` are a keypair**, filled only when the environment
supplies both — matching protect-ffi's native reader. Mixing a stale env
`CS_CLIENT_ID` with a config `clientKey` would produce a mismatched client
that fails inside ZeroKMS, well away from the cause.
- **`accessKey` is not filled on the strategy path.** It is `never` on that
arm and mutually exclusive with a strategy, so filling it from a stray env
var would make `resolveStrategy` reject a config the caller wrote
correctly.
`CS_CLIENT_ACCESS_KEY` is read first because that is the name this repo
documents and the one the entry's own example uses; `CS_ACCESS_KEY` is
accepted after it because protect-ffi's native JS credential reader uses
that spelling, so both are in circulation.
Missing-credential errors now name only what is actually missing, the env
var that would have supplied it, and — when there is no `process` — say
that the fallback is unavailable in this runtime and point at the Worker
`env` argument. The existing strategy test asserted the old both-fields
message and is updated to the narrower one.
Verified: 1059 tests pass; `code:check` clean; both `process.env` reads in
the built `dist/wasm-inline.js` are `typeof`-guarded.
Skill and changeset updated — the skill now shows the no-config form, the
Worker `env` form, and warns against relying on the fallback in browser
builds, where bundlers inline `process.env` at build time and would ship
the access key to every visitor.
🦋 Changeset detectedLatest commit: e4013c5 The changes in this PR will be included in the next version bump. This PR includes changesets to release 11 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
📝 WalkthroughWalkthroughThe WASM inline client now fills missing credentials from ChangesWASM environment credential fallback
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related issues
Possibly related PRs
Suggested reviewers: Sequence Diagram(s)sequenceDiagram
participant Encryption
participant withEnvCredentials
participant resolveStrategy
participant wasmNewClient
Encryption->>withEnvCredentials: Resolve missing credentials from process.env
withEnvCredentials-->>Encryption: Return resolved configuration
Encryption->>resolveStrategy: Resolve authentication strategy
resolveStrategy-->>Encryption: Return strategy
Encryption->>wasmNewClient: Create client with resolved clientId and clientKey
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
packages/stack/src/wasm-inline.ts (1)
1411-1411: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winRemove the unnecessary
as never.
{}now satisfiesWasmClientConfig, so this can retain type checking.Proposed fix
- const resolvedConfig = withEnvCredentials(clientConfig ?? ({} as never)) + const resolvedConfig = withEnvCredentials(clientConfig ?? {})As per coding guidelines, avoid type-erasing assertions in source unless deliberately suppressed with a reason.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/stack/src/wasm-inline.ts` at line 1411, Remove the unnecessary `as never` assertion from the `clientConfig` fallback in the `resolvedConfig` initialization, passing the empty object directly to `withEnvCredentials` so normal type checking is preserved.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/stack/src/wasm-inline.ts`:
- Around line 1710-1717: Update the missing accessKey diagnostic in
packages/stack/src/wasm-inline.ts lines 1710-1717 to mention both
CS_CLIENT_ACCESS_KEY and CS_ACCESS_KEY, matching the fallbacks accepted by
withEnvCredentials. Update the related regex assertions in
packages/stack/__tests__/wasm-inline-strategy.test.ts lines 156-173 to require
both supported environment variable names.
---
Nitpick comments:
In `@packages/stack/src/wasm-inline.ts`:
- Line 1411: Remove the unnecessary `as never` assertion from the `clientConfig`
fallback in the `resolvedConfig` initialization, passing the empty object
directly to `withEnvCredentials` so normal type checking is preserved.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: d57e7fc9-f2ff-423e-a84f-9c92e2401c75
📒 Files selected for processing (5)
.changeset/wasm-env-credentials.mdpackages/stack/__tests__/wasm-inline-env-credentials.test.tspackages/stack/__tests__/wasm-inline-strategy.test.tspackages/stack/src/wasm-inline.tsskills/stash-encryption/SKILL.md
| const missing = [ | ||
| !cfg.workspaceCrn && '`config.workspaceCrn` (or `CS_WORKSPACE_CRN`)', | ||
| !cfg.accessKey && '`config.accessKey` (or `CS_CLIENT_ACCESS_KEY`)', | ||
| ].filter(Boolean) | ||
| throw new Error( | ||
| '[encryption]: `config.workspaceCrn` and `config.accessKey` are required when no auth strategy is provided.', | ||
| `[encryption]: ${missing.join(' and ')} ${ | ||
| missing.length > 1 ? 'are' : 'is' | ||
| } required when no auth strategy is provided.${envHint()}`, |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Mention the supported CS_ACCESS_KEY fallback in the error.
withEnvCredentials accepts both access-key variables, but this error only suggests CS_CLIENT_ACCESS_KEY, contrary to the supported fallback contract.
packages/stack/src/wasm-inline.ts#L1710-L1717: includeCS_ACCESS_KEYalongsideCS_CLIENT_ACCESS_KEYin the missingaccessKeydiagnostic.packages/stack/__tests__/wasm-inline-strategy.test.ts#L156-L173: update the regexes to assert both accepted access-key environment variables.
📍 Affects 2 files
packages/stack/src/wasm-inline.ts#L1710-L1717(this comment)packages/stack/__tests__/wasm-inline-strategy.test.ts#L156-L173
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/stack/src/wasm-inline.ts` around lines 1710 - 1717, Update the
missing accessKey diagnostic in packages/stack/src/wasm-inline.ts lines
1710-1717 to mention both CS_CLIENT_ACCESS_KEY and CS_ACCESS_KEY, matching the
fallbacks accepted by withEnvCredentials. Update the related regex assertions in
packages/stack/__tests__/wasm-inline-strategy.test.ts lines 156-173 to require
both supported environment variable names.
The native entry resolves an omitted credential from
CS_*env vars or~/.cipherstash, inside protect-ffi. The WASM entry had no fallback at all, so every edge caller plumbed four values by hand — even in Deno, Node, and Bun, where the environment is right there.configis now optional and each field falls back individually:clientIdCS_CLIENT_IDclientKeyCS_CLIENT_KEYworkspaceCrnCS_WORKSPACE_CRNaccessKeyCS_CLIENT_ACCESS_KEY(thenCS_ACCESS_KEY)An explicit value always wins, so nothing changes for callers passing a full config today.
Why here and not in protect-ffi
The stack already depends on
@cipherstash/auth, already buildsAccessKeyStrategyfor the access-key path, and already owns theconfig.authStrategyconcept.protect-ffi's wasm entry is raw wasm-bindgen output with no JS layer, and its auth seam is deliberately duck-typed (
{ getToken }). Defaulting there would have meant adding a JS wrapper module and a hard dependency on@cipherstash/auth— inverting that seam for a fallback only this layer needs. Its only runtime dependency today is@neon-rs/load.Three details that are the substance of the change
typeof process, notprocess?.env. This module is the edge entry. In a Worker or browserprocessis undeclared, so a bare read is aReferenceErroron client construction — and optional chaining does not help, since it still evaluates the identifier. The test deletes the global to reproduce that runtime honestly rather than asserting against an emptyprocess.env. This is the same trap that broke the wasm entry on import in #798, so it is guarded from the start here; bothprocess.envreads in the builtdist/wasm-inline.jsaretypeof-guarded.clientId/clientKeyare a keypair, filled only when the environment supplies both — matching protect-ffi's native reader. Mixing a stale envCS_CLIENT_IDwith a configclientKeywould produce a mismatched client that fails inside ZeroKMS, well away from the cause.accessKeyis not filled on the strategy path. It isneveron that arm and mutually exclusive with a strategy, so filling it from a stray env var would makeresolveStrategyreject a config the caller wrote correctly.Where it does not work, and why that is said out loud
Cloudflare Workers and browsers have no
process.env. Workers hand their environment to the fetch handler instead. Missing-credential errors now name the field, the env var that would have supplied it, and — when there is noprocess— say the fallback is unavailable in this runtime and point at the Workerenvargument. Without that, a Worker developer reads "setCS_CLIENT_ID" and reasonably tries exactly that, in a runtime where it can never be read.Browser builds should not use the fallback at all. Vite and webpack commonly inline
process.envat build time, which would bake the access key into client-side JavaScript. The skill now says so; pass a pre-builtauthStrategyinstead.Also
CS_CLIENT_ACCESS_KEYis read first because that is the name this repo documents and the entry's own example uses.CS_ACCESS_KEYis accepted after it because protect-ffi's native JS credential reader uses that spelling — both names are in circulation, and taking the documented one first keeps the docs true.The existing strategy test asserted the old "both fields are required" message; errors now name only what is actually missing, so it is updated to the narrower assertion.
Verification
1059 tests pass (12 new);
code:checkclean; build clean; bothprocess.envreads in the shipped bundle confirmed guarded.Skill and changeset updated per AGENTS.md — the skill now shows the no-config form, the Worker
envform, and the browser warning.Related
Follows from cipherstash/protectjs-ffi#142 / #143, where
strategywas renamed toauthStrategyand the wasm option types were declared. This PR does not yet bump protect-ffi — the rename is backwards-compatible, so that can land with the version bump.Summary by CodeRabbit
New Features
Documentation