Skip to content

fix(provenance): read signing key credstore-first via resolveSecret (#2308) - #2317

Merged
jung-thomas merged 1 commit into
DEVfrom
fix/provenance-signing-key-2308
Sep 15, 2026
Merged

jung-thomas merged 1 commit into
DEVfrom
fix/provenance-signing-key-2308

Conversation

@jung-thomas

Copy link
Copy Markdown
Contributor

Root cause (not just missing provisioning)

Issue #2308 is framed as "provision the PROD signing key", but a live probe + code read shows the issue's own plan (set via `/admin-ui/#secrets` + restart) cannot work as-is — it's a code bug:

  • `srv/lib/provenance-keys.js` read `process.env.PROVENANCE_SIGNING_KEY` directly.
  • `/admin-ui/#secrets` writes to the BTP Credential Store, and a CF credstore binding does not auto-inject entries into `process.env`.
  • So a key set via the admin UI lands in the credstore but provenance never reads it → `jwks.json` = `{"keys":[]}` and the provenance endpoint 503s. Probed live on PROD 2026-09-15 (both CDN + direct srv).
  • Every other secret (`CONTENT_API_KEY`, etc.) reads credstore-first via `resolveSecret(alias)`. Provenance was the only one bypassing it. The Executable, self-verifying, self-healing tutorials #2245 design spec explicitly said "same seam as CONTENT_API_KEY" — the implementation deviated.

Change

  • Route the read through `resolveSecret('PROVENANCE_SIGNING_KEY')` (credstore → `process.env` fallback → null). Env fallback preserved for local/dev.
  • Regression test: primes the shared resolver cache (not `process.env`) and asserts the signer loads — fails on the old code.
  • Register `PROVENANCE_SIGNING_KEY` in `scripts/seed-secrets.cjs` so the presence/expiry cron surfaces a missing key (would have caught this earlier) and it shows as a first-class row in `/admin-ui/#secrets`.
  • Fix the gotchas doc line that wrongly claimed the credstore binding surfaces the key as an env var.

Tests

```
vitest run test/unit/provenance-keys.test.js test/unit/provenance-envelope.test.js test/lib/provenance-endpoint.test.js
→ 3 files, 10 passed
```

Still required after this merges (ops — needs Tom)

This code fix is necessary but not sufficient; the key must still be provisioned:

  1. Merge → deploy through DEV → PROD (per the DEV→PROD flow; no main-hotfix path).
  2. On the PROD approuter, `/admin-ui/#secrets` → set `PROVENANCE_SIGNING_KEY` = an Ed25519 PKCS8 PEM. Generate with `openssl genpkey -algorithm ed25519 -out prov.pem` (or the node one-liner in gotchas). Never commit the PEM.
  3. `cf restart tutorials-srv` (PROD) — the key is loaded once per process.
  4. Verify live: `jwks.json` returns exactly one `{kty:OKP, crv:Ed25519, use:sig, alg:EdDSA}` key, and a sample tutorial `/provenance` returns `200 {jws, jwks_url}` whose `kid` matches the JWKS thumbprint.

Closes #2308 once the ops steps land + are verified live on PROD.

…2308)

provenance-keys.js read process.env.PROVENANCE_SIGNING_KEY directly, but a
BTP Credential Store binding does not auto-inject entries into process.env.
A key rotated in via /admin-ui/#secrets lands in the credstore, so provenance
never saw it -> jwks.json empty + provenance endpoint 503 on PROD.

Route the read through resolveSecret('PROVENANCE_SIGNING_KEY') (credstore ->
env fallback -> null), the same seam as CONTENT_API_KEY and every other secret,
matching the #2245 design intent. Env fallback is preserved for local/dev.

- add regression test that primes the resolver cache (not process.env) and
  asserts the signer loads
- register PROVENANCE_SIGNING_KEY in seed-secrets so the presence/expiry cron
  surfaces a missing key
- fix gotchas doc line that wrongly claimed the credstore binding surfaces the
  key as an env var
@jung-thomas
jung-thomas marked this pull request as ready for review September 15, 2026 16:06
@jung-thomas
jung-thomas merged commit 04456d2 into DEV Sep 15, 2026
5 checks passed
@jung-thomas
jung-thomas deleted the fix/provenance-signing-key-2308 branch September 15, 2026 16:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant