meta-issue-creation
How an agent files a GitHub issue on cipherstash repos — required structure (Background / Problem / Proposal), dumbed-down…
How a CipherStash client authenticates — the canonical skill for credentials, auth strategies, and lock context. Covers the service-token model (every request to a CipherStash service carries a CTS-minted token; strategies exist to obtain one), the three separable concerns
$ npx -y skills add cipherstash/stack --skill stash-auth --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/stash-authContext preview
The summary Claude sees to decide when to auto-load this skill.
How a CipherStash client authenticates — the canonical skill for credentials, auth strategies, and lock context. Covers the service-token model (every request to a CipherStash service carries a CTS-minted token; strategies exist to obtain one), the three separable concerns
name: stash-auth description: How a CipherStash client authenticates — the canonical skill for credentials, auth strategies, and lock context. Covers the service-token model (every request to a CipherStash service carries a CTS-minted token; strategies exist to obtain one), the three separable concerns people conflate (client credentials, end-user identity, key binding), the `@cipherstash/auth` strategies (`AutoStrategy`, `AccessKeyStrategy`, `OidcFederationStrategy`, `DeviceSessionStrategy`) and the Result-unwrap trap on `create()`, the four `CS_*` variables and `stash env`, credential discovery vs explicit config on the native and WASM entries, client lifetime with user-scoped strategies, `.withLockContext` and what is deprecated around it, and CTS failure codes. Use when an operation fails with 401/NOT_AUTHENTICATED/WORKSPACE_MISMATCH, when choosing between an access key and OIDC federation, when wiring per-user identity-bound encryption, when minting deployment credentials, or whenever another skill mentions "credentials" or `config.authStrategy` — this skill is the canonical source for that model.
Every request a Stack client makes to a CipherStash service authenticates with a **CipherStash service token**: a short-lived signed JWT minted by **CTS** (the CipherStash token service). That is the only credential ZeroKMS accepts. Access keys and identity-provider JWTs are never sent to ZeroKMS — they are *exchanged* at CTS for a service token, and everything in this skill is machinery for doing that exchange correctly and automatically.
This skill is canonical for authentication. Where other skills (`stash-edge`, `stash-encryption`, `stash-cli`, `stash-deployment`) touch credentials in passing and disagree with this one, this one wins. For what a token *authorizes* — keysets, clients, grants — see `stash-zerokms`: authentication and keyset access are separate gates, and a perfectly valid token still fails operations on a keyset its client was never granted.
| Concern | What it decides | Where it's configured | |---|---|---| | **Client credentials** | Which workspace (`CS_WORKSPACE_CRN`) and — more granular than the workspace — which ZeroKMS *client* is acting (`CS_CLIENT_ID`, `CS_CLIENT_KEY`) and the access key that authenticates it (`CS_CLIENT_ACCESS_KEY`) | `CS_*` variables / dev profile / `config.accessKey` | | **End-user identity** | Authenticating the client *as* a specific user | `config.authStrategy: OidcFederationStrategy` | | **Key binding (lock context)** | Who can *retrieve a value's data key* — binds key access to a claim | `.withLockContext({ identityClaim })` per operation |
Note the granularity in the first row: only the CRN is about the *workspace*. The other three variables identify a specific **client** within it — its id, its key material, and its access key — so "the credentials" always means a particular client's identity, not a workspace-wide secret (see `stash-zerokms` for what a client is and what it can reach).
The classic conflation is between the last two: **an auth strategy decides who the client is; a lock context decides who can get a value's data key back.** They are orthogonal. Authenticating as a user via `OidcFederationStrategy` does not by itself bind any value to that user — without `.withLockContext()`, any client with the right keyset access can retrieve the value's key. Lock context *requires* `OidcFederationStrategy` on the Stack surface (there must be a user claim in the service token to bind to), but not the other way around.
CTS's authorize endpoint exchanges exactly one of two credential kinds for a service token:
services/CI.
(Clerk, Supabase, Auth0, or Okta — or CipherStash's own login) — end-user identity. CTS verifies it against the provider's JWKS and checks workspace membership.
The minted token carries: the subject (`CS|<user-id>` or `CS|CSAK<key-id>`), the workspace, scopes derived from the credential's role (member or admin — see `stash-zerokms` for what the scopes gate), and a built-in **service discovery mechanism** — the token itself tells the client where the workspace's services (ZeroKMS) live. The workspace CRN (`crn:<region>.<provider>:<workspace-id>`) names the region, CTS resolves the regional endpoints, and the client reads them from the token. You don't need to know how it works; the one fact that matters is that **a CipherStash-issued service token is only valid for services in the region it was issued for** — it cannot be replayed against another region. Endpoints are never hand-configured — the `CS_*_HOST` override variables are debug-only and must not appear in CI, examples, or docs.
CipherStash runs in a variety of regions across the world, and the set is **subject to change** — `stash auth regions` (add `--json` for machine-readable output) lists the current ones; see `stash-cli`. As of this writing:
| Region | Location | |---|---| | `us-east-1` | Virginia, USA | | `us-east-2` | Ohio, USA | | `us-west-1` | California, USA | | `us-west-2` | Oregon, USA | | `eu-west-1` | Dublin, Ireland | | `eu-central-1` | Frankfurt, Germany | | `ap-southeast-2` | Sydney, Australia |
A workspace lives in one region, chosen at creation (the `stash auth login` region picker / `--region`); the CRN records it thereafter.
CTS failures are deliberate and descriptive: `401` with a reason ("Principal X is not a member of workspace Y", "No OIDC provider found for issuer: …", "Access key was malformed: …"), and `402` ("Insufficient balance…") when the organisation is over its usage limit. A 402 is a billing problem, not a credentials problem — don't rotate keys over it.
A token failure is normally transient (network, an
Searchable, application-level encryption for building privacy-first apps
How an agent files a GitHub issue on cipherstash repos — required structure (Background / Problem / Proposal), dumbed-down…
How an agent authors branches, commits, and pull requests on cipherstash/stack — naming, signed commits, the…
Drive CipherStash setup and encryption migrations through the `stash` CLI — `init`, `plan`, `impl`, `status`, `auth login`, `eql…
Deploy a CipherStash encryption rollout to a live environment without losing data — the multi-deploy ladder (schema-add +…
Integrate CipherStash encryption with Drizzle ORM using @cipherstash/stack-drizzle (EQL v3). Covers the types.* encrypted column…
Integrate CipherStash encryption with Amazon DynamoDB using @cipherstash/stack/dynamodb and EQL v3 schemas. Covers item and bulk…