ci: platform-prod-secrets mints a fresh link keyset and drops the JWT copy
What. .github/workflows/platform-prod-secrets.yml (dispatch-only, environment cmux-next-production) now puts two secrets on the cmux-platform-production Worker instead of three:
CLOUD_LINK_SIGNING_KEYS: when the Worker lacks it, the job mints a fresh value with the newscripts/cmux-next/release/mint-link-keyset.mjsand pipes it intowrangler secret put. The environment'ssecrets.CLOUD_LINK_SIGNING_KEYSis no longer read.CLOUD_FREESTYLE_API_KEY: unchanged (from the environment'sFREESTYLE_API_KEY, only when missing).JWT_PRIVATE_JWK: dropped from env and the loop.
Why. The platform takes over NEXT Cloud's CloudDO with a fresh namespace. Production has never issued a Cloud link token, and link tokens are verified only by Cloud VMs, against the keyset the platform itself serves (the bind answer and GET /v1/cloud/keyset, both forwarded to the platform). Nothing verifies a link across Workers, so a fresh keyset minted for the platform is correct and cmux-api's copy (a Worker secret only) is not needed. The platform verifies cmux-api's install tokens against cmux-api's public JWKS (a separate cmux-platform change), so it needs no JWT_PRIVATE_JWK copy.
Format. {"active":"<kid>","keys":{"<kid>":{"kty":"OKP","crv":"Ed25519","x":...,"d":...,"kid":"<kid>"}}}, kid production-<YYYY-MM-DD> (UTC), matching /^[A-Za-z0-9._-]{1,64}$/. This is what parseSigningKeys accepts in backend/apps/api/src/link-token.ts (and the identical cmux-platform parser). One kid with no published_at signs at once (signingKid), which is what we want since no production VM holds an older keyset.
Guarantees.
- Never prints or writes the value: the script writes only to stdout; the job holds it in a shell variable and pipes it to wrangler's stdin. No
echo, no file, noset -x. - Never overwrites: each name is put only when
wrangler secret listdoes not show it. - Fails closed:
set -euo pipefail, the mint is a command substitution (a failed mint stops the job), and an empty value is refused before anything is put. The script itself exits non-zero, printing nothing, on a bad kid or an unexpected JWK shape. runs-onis unchanged. The checkout is sparse (just the mint script) withpersist-credentials: false.
Tests.
- New
backend/apps/api/test-node/link-keyset-mint.test.ts(Node vitest, runs in the backend workflow'stest:nodestep; the mint script was added to that workflow's paths):parseSigningKeysaccepts the output, exactly one kid, it is the active kid,signingKid(keys, Date.now())returns it,publicKeysethas nod, two runs differ, and a bad prefix exits 2 with empty stdout. Passed locally (3/3). python3 tests/test_ci_linux_single_size.py,tests/test_ci_fork_runner_routing.py,tests/test_ci_no_single_size_pins.py,tests/test_ci_checkout_network_diagnostics.py,tests/test_ci_actionlint_covers_every_workflow.py: all pass.
Agent session: agent session
🤖 Generated with Claude Code
![Autofix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark-3x.png)
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Reworks platform-prod-secrets.yml so cmux-platform-production gets a freshly minted Cloud link signing keyset instead of a copy from cmux-next-production, and drops JWT_PRIVATE_JWK from the workflow.
Production never issued a link token, and link tokens are verified only by Cloud VMs against the keyset the platform itself serves. The platform verifies cmux-api's install tokens against cmux-api's public JWKS, so it needs no private JWK copy.
scripts/cmux-next/release/mint-link-keyset.mjsmints a single Ed25519 kid (production-<UTC date>) that signs immediately; the workflow pipes its stdout intowrangler secret putonly when the Worker lacks the secret.- The minted value is never printed or written to disk, and a failed or empty mint exits the job before anything is put. Existing secrets are never overwritten.
CLOUD_FREESTYLE_API_KEYis unchanged. - The secret list is re-read right before each put, and a failed or unreadable list stops the job instead of reading as "absent", narrowing the race window to the seconds of one put.
- New
link-keyset-mint.test.tsruns in the backendtest:nodestep and validates the keyset againstparseSigningKeys,signingKid, andpublicKeyset; the mint script was added to that workflow's trigger paths. - The workflow is dispatch-only, so the change applies on the next dispatch from
feat-cmux-next.