Skip to content

Commit c888cc3

Browse files
committed
feat(cli): make stash env mint real deployment credentials
The command was a gated stub (STASH_EXPERIMENTAL_ENV_CMD) waiting on "the CTS mint endpoint" — which turned out to already exist. It now mints working credentials from the device-code session using the same APIs the dashboard uses, all plain fetch against the session's own service URLs: 1. GET {cts}/api/workspaces → server-authoritative region → CS_WORKSPACE_CRN 2. POST {zerokms}/create-client → CS_CLIENT_ID / CS_CLIENT_KEY (hex-transcoded) 3. POST {cts}/api/access-keys → CS_CLIENT_ACCESS_KEY (member role, always — the CLI deliberately cannot mint admin keys; role is omitted so the server owns the default) Design points: - The ZeroKMS client is created before the access key, so a partial failure leaves an inert client record, never an unaccounted-for live credential. - The key name resolves before any profile/network access, so the non-interactive missing-name failure is credential-free — that is the seam the new pty-less e2e exercises (minting live keys from a dev machine's real session in tests is deliberately impossible). - --write emits .env.production.local with mode 0600 and refuses to overwrite non-interactively; --json emits one minted object or the shared { status: 'error' } envelope. Closes #663 (the wasm/edge credential gap, CIP-2997): edge runtimes that cannot read ~/.cipherstash (containerised supabase functions serve, Workers) now have a supported path — stash env + --env-file/secret store. Claude-Session: https://claude.ai/code/session_01MxTTPaPP16m6br7Hoab94w
1 parent 8b2551a commit c888cc3

9 files changed

Lines changed: 793 additions & 46 deletions

File tree

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
'stash': minor
3+
---
4+
5+
`stash env` now works: it mints deployment credentials from your device-code
6+
session and prints them as env vars — no dashboard copy-paste. The command
7+
creates a fresh ZeroKMS client and a member-role CipherStash access key (named
8+
via `--name`), then emits `CS_WORKSPACE_CRN`, `CS_CLIENT_ID`, `CS_CLIENT_KEY`,
9+
and `CS_CLIENT_ACCESS_KEY` to stdout, `--write` (`.env.production.local`, mode
10+
0600), or `--json`. Creating access keys requires the admin role in the
11+
workspace; the minted key itself is always member — the CLI deliberately
12+
cannot mint admin keys.
13+
14+
This is also the supported credential path for WASM/edge local development
15+
(Supabase Edge Functions, Cloudflare Workers, Deno), where the runtime cannot
16+
read the `~/.cipherstash` device profile: mint a key and feed it via
17+
`--env-file` or the platform's secret store.
18+
19+
The `STASH_EXPERIMENTAL_ENV_CMD` gate is removed.

packages/cli/src/bin/main.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ Commands:
125125
encrypt cutover Rename swap encrypted → primary column (EQL v2 only)
126126
encrypt drop Generate a migration to drop the plaintext column
127127
128-
env (experimental) Print production env vars for deployment
128+
env Mint deployment credentials and print them as env vars
129129
130130
Options:
131131
--help, -h Show help
@@ -532,7 +532,11 @@ async function dispatch(
532532
await runSchemaCommand(subcommand, flags, values)
533533
break
534534
case 'env':
535-
await envCommand({ write: flags.write })
535+
await envCommand({
536+
write: flags.write,
537+
json: flags.json,
538+
name: values.name,
539+
})
536540
break
537541
case 'manifest':
538542
// Pure metadata (no native code) — safe to run anywhere, including when

packages/cli/src/cli/registry.ts

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -530,15 +530,43 @@ export const registry: CommandGroup[] = [
530530
],
531531
},
532532
{
533-
title: 'Experimental',
533+
title: 'Deployment',
534534
commands: [
535535
{
536536
name: 'env',
537-
summary: '(experimental) Print production env vars for deployment',
537+
summary: 'Mint deployment credentials and print them as env vars',
538+
long: [
539+
'Mints a fresh ZeroKMS client and a CipherStash access key from your',
540+
'device-code session (`stash auth login`), then prints the four env',
541+
'vars a deployed app needs: CS_WORKSPACE_CRN, CS_CLIENT_ID,',
542+
'CS_CLIENT_KEY, CS_CLIENT_ACCESS_KEY.',
543+
'',
544+
'The access key is created with the member role (the CLI never mints',
545+
'admin keys) and is shown exactly once — pipe the output into your',
546+
'deployment secret store. Creating access keys requires your user to',
547+
'have the admin role in the workspace.',
548+
].join('\n'),
549+
examples: [
550+
'env --name my-app-prod',
551+
'env --name my-app-prod --write',
552+
'env --name edge-dev --json',
553+
],
538554
flags: [
555+
{
556+
name: '--name',
557+
value: '<name>',
558+
description:
559+
'Name for the minted access key and ZeroKMS client. Prompted for interactively; required in non-interactive runs.',
560+
},
539561
{
540562
name: '--write',
541-
description: 'Write the vars to a file instead of printing them.',
563+
description:
564+
'Write the vars to .env.production.local (mode 0600) instead of printing them.',
565+
},
566+
{
567+
name: '--json',
568+
description:
569+
'Emit a single machine-readable JSON object (or a { status: "error" } envelope) instead of a dotenv block. Implies no prompts.',
542570
},
543571
],
544572
},

0 commit comments

Comments
 (0)