Skip to content

Declare release credential surfaces once and probe them weekly - #1505

Open
schickling-assistant wants to merge 3 commits into
schickling-assistant/2026-07-27-release-registry-verifyfrom
schickling-assistant/2026-07-27-release-credential-surfaces
Open

Declare release credential surfaces once and probe them weekly#1505
schickling-assistant wants to merge 3 commits into
schickling-assistant/2026-07-27-release-registry-verifyfrom
schickling-assistant/2026-07-27-release-credential-surfaces

Conversation

@schickling-assistant

Copy link
Copy Markdown
Collaborator

Stacked on #1504. Base is schickling-assistant/2026-07-27-release-registry-verify; review that first. Retarget to main once #1504 merges.

Problem

The production deploy steps depend on three long-lived credentials — Netlify, Cloudflare, Mixedbread. Their names were repeated as string literals across five env: blocks in release.yml.genie.ts.

That duplication is how #1284 happened: release.yml referenced MXBAI_VECTOR_STORE_ID_PROD, a secret that never existed, and nothing noticed until the publish job reached that step.

Separately, nothing checks these credentials until a release needs them. validate-release-plan already exercises release code on every PR in the real Nix environment — but a dry-run cannot prove that a token which expires on wall-clock time is still accepted. That is the only genuine gap.

Solution

Declare each credential surface once in genie/release-credentials.ts — its secrets, plus a read-only probe that proves they are still accepted. Both consumers derive from that declaration:

Consumer Derives
release.yml production deploy steps the env: block passed to the devenv task
health-release-credentials.yml (new) the weekly liveness probe per surface

Same idea as genie/ci.ts keeping required status checks aligned with matrix expansions: shared generator data keeps two emitted artifacts consistent.

The refactor is behavior-preserving — regenerating leaves release.yml byte-identical, so the only new workflow content is the health check itself.

What the health check deliberately does not do

  • No Nix/devenv setup. Every probe is an HTTP call, so there is no environment to reproduce. Approximating the release job's shell would only create a second environment that drifts from the first.
  • No tooling/PATH probes. release.yml runs its steps through devenv tasks; a which pnpm in this job would describe this runner, not that one. (This is exactly why the pre-flight in Add trusted-publisher + secrets pre-flight health check for the release pipeline #1291 could not have caught Run mxbai search sync via devenv task (pnpm not on PATH) #1285 — it ran pnpm/action-setup and then checked which pnpm, installing the very tool whose absence was the bug.)
  • No issue filing. A failed job with the surface name and HTTP status in the log is the alert. No dedup or recovery-close machinery to maintain.

Scope note on #1284

The shared declaration removes divergence between release.yml and the probe — they cannot disagree about which secrets a surface needs. It does not make a wrong name impossible: a typo in release-credentials.ts propagates to both consumers. What changes is timing — a secret that does not exist arrives empty and trips the probe's :? guard on the next weekly run, instead of surfacing when a release reaches that deploy step.

Validation

Probes fail closed, verified against the live endpoints with an invalid token:

Surface Endpoint Result with bad token
Netlify GET /api/v1/user HTTP 401 → job fails
Cloudflare GET /client/v4/user/tokens/verify .result.status absent → != active → job fails
Mixedbread GET /v1/vector_stores/$ID HTTP 401 → job fails

Cloudflare's endpoint reports token status, so a token that still authenticates but has been disabled or expired is caught rather than passing — gh secret list (as used by #1291) only proves a name exists.

  • actionlint clean on the generated workflow.
  • genie:run regenerates cleanly; release.yml unchanged.
  • tsc --build clean; oxlint/oxfmt clean on changed files; intent-layer invariant suite green.
  • No --location on any probe, so a redirect cannot replay the Authorization header to another host.

Intent layer

Adds LS.DEL.REL-R10 (declared credential surfaces) and a Credential Surfaces section in the release spec.

Supersedes

Partially closes #1291. That PR's useful half was credential liveness; this keeps it and drops the parts that could not work:

  • its pull_request trigger checked out ref: main, so a PR changing release.yml was validated against main's code and the PR's own changes were never examined;
  • gh secret list proves a name exists, not that the credential is valid;
  • the OIDC probe treated both 200 and 404 as healthy, so it could only fail if GitHub's API were down;
  • id-token: write was declared but unused.

npm is intentionally absent from the surface list: publishing uses OIDC trusted publishing and release.yml asserts no NPM_TOKEN/NODE_AUTH_TOKEN is set, so there is no long-lived npm credential to expire. (#1291 reached the same conclusion — correct against today's main.)

🤖 Generated with Claude Code

Posted on behalf of @schickling
field value
agent_name cl1-lagoon
agent_session_id 4f0de924-6d8e-4779-9a7d-c30c8fa3d2a4
agent_tool Claude Code
agent_tool_version 2.1.220
agent_runtime Claude Code 2.1.220
agent_model claude-opus-5
runtime_profile /nix/store/57hz15fbcg9c3hasm0ny3axd5pg33rh3-coding-agent-runtime-profile/share/coding-agents/profile.json
skills_manifest /nix/store/d9hvhhg1k9n3j20dgcch7sz3sg98l377-agent-skills-corpus/share/agent-skills/manifest.json
worktree livestore/schickling-assistant/2026-07-27-release-dist-tag-verify
machine dev3
tooling_profile dotfiles@b785b9f

@schickling-assistant schickling-assistant added area:ci CI workflows, runners, and pipeline configuration · Set: manual area:tooling Developer tooling, scripts, and utilities · Set: manual labels Jul 27, 2026
@github-actions

github-actions Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

PR preview

Subject Status Report Details Updated
LiveStore docs preview success Docs preview deployed (livestore-docs-dev) PR aliases: pr-1505-62bac6a, pr-1505 2026-07-27 22:30 UTC
web-linearlite success web-linearlite deployed (preview) Worker: example-web-linearlite-preview.livestore.workers.dev 2026-07-27 22:22 UTC
web-todomvc success web-todomvc deployed (preview) Worker: example-web-todomvc-preview.livestore.workers.dev 2026-07-27 22:22 UTC
web-todomvc-script success web-todomvc-script deployed (preview) Worker: example-web-todomvc-script-preview.livestore.workers.dev 2026-07-27 22:22 UTC
web-todomvc-sync-cf success web-todomvc-sync-cf deployed (preview) Worker: example-web-todomvc-sync-cf-preview.livestore.workers.dev 2026-07-27 22:22 UTC
Report history

PR 1505 · 2026-07-27 22:41 UTC

Subject Status Report Details Updated
LiveStore docs preview success Docs preview deployed (livestore-docs-dev) PR aliases: pr-1505-62bac6a, pr-1505 2026-07-27 22:30 UTC
web-linearlite success web-linearlite deployed (preview) Worker: example-web-linearlite-preview.livestore.workers.dev 2026-07-27 22:22 UTC
web-todomvc success web-todomvc deployed (preview) Worker: example-web-todomvc-preview.livestore.workers.dev 2026-07-27 22:22 UTC
web-todomvc-script success web-todomvc-script deployed (preview) Worker: example-web-todomvc-script-preview.livestore.workers.dev 2026-07-27 22:22 UTC
web-todomvc-sync-cf success web-todomvc-sync-cf deployed (preview) Worker: example-web-todomvc-sync-cf-preview.livestore.workers.dev 2026-07-27 22:22 UTC

PR 1505 · 2026-07-27 22:19 UTC

Subject Status Report Details Updated
LiveStore docs preview success Docs preview deployed (livestore-docs-dev) PR aliases: pr-1505-8fe1265, pr-1505 2026-07-27 22:11 UTC
web-linearlite success web-linearlite deployed (preview) Worker: example-web-linearlite-preview.livestore.workers.dev 2026-07-27 21:59 UTC
web-todomvc success web-todomvc deployed (preview) Worker: example-web-todomvc-preview.livestore.workers.dev 2026-07-27 21:59 UTC
web-todomvc-script success web-todomvc-script deployed (preview) Worker: example-web-todomvc-script-preview.livestore.workers.dev 2026-07-27 21:59 UTC
web-todomvc-sync-cf success web-todomvc-sync-cf deployed (preview) Worker: example-web-todomvc-sync-cf-preview.livestore.workers.dev 2026-07-27 21:59 UTC

PR 1505 · 2026-07-27 21:18 UTC

Subject Status Report Details Updated
LiveStore docs preview success Docs preview deployed (livestore-docs-dev) PR aliases: pr-1505-97ac281, pr-1505 2026-07-27 21:09 UTC
web-linearlite success web-linearlite deployed (preview) Worker: example-web-linearlite-preview.livestore.workers.dev 2026-07-27 20:58 UTC
web-todomvc success web-todomvc deployed (preview) Worker: example-web-todomvc-preview.livestore.workers.dev 2026-07-27 20:58 UTC
web-todomvc-script success web-todomvc-script deployed (preview) Worker: example-web-todomvc-script-preview.livestore.workers.dev 2026-07-27 20:58 UTC
web-todomvc-sync-cf success web-todomvc-sync-cf deployed (preview) Worker: example-web-todomvc-sync-cf-preview.livestore.workers.dev 2026-07-27 20:58 UTC

PR 1505 · 2026-07-27 21:10 UTC

Subject Status Report Details Updated
LiveStore docs preview success Docs preview deployed (livestore-docs-dev) PR aliases: pr-1505-28030f3, pr-1505 2026-07-27 21:03 UTC
web-linearlite success web-linearlite deployed (preview) Worker: example-web-linearlite-preview.livestore.workers.dev 2026-07-27 20:52 UTC
web-todomvc success web-todomvc deployed (preview) Worker: example-web-todomvc-preview.livestore.workers.dev 2026-07-27 20:52 UTC
web-todomvc-script success web-todomvc-script deployed (preview) Worker: example-web-todomvc-script-preview.livestore.workers.dev 2026-07-27 20:52 UTC
web-todomvc-sync-cf success web-todomvc-sync-cf deployed (preview) Worker: example-web-todomvc-sync-cf-preview.livestore.workers.dev 2026-07-27 20:52 UTC

PR 1505 · 2026-07-27 19:38 UTC

Subject Status Report Details Updated
web-linearlite success web-linearlite deployed (preview) Worker: example-web-linearlite-preview.livestore.workers.dev 2026-07-27 19:19 UTC
web-todomvc success web-todomvc deployed (preview) Worker: example-web-todomvc-preview.livestore.workers.dev 2026-07-27 19:19 UTC
web-todomvc-script success web-todomvc-script deployed (preview) Worker: example-web-todomvc-script-preview.livestore.workers.dev 2026-07-27 19:19 UTC
web-todomvc-sync-cf success web-todomvc-sync-cf deployed (preview) Worker: example-web-todomvc-sync-cf-preview.livestore.workers.dev 2026-07-27 19:19 UTC

PR 1505 · 2026-07-27 10:39 UTC

Subject Status Report Details Updated
LiveStore docs preview success Docs preview deployed (livestore-docs-dev) PR aliases: pr-1505-7e5af37, pr-1505 2026-07-27 10:33 UTC
web-linearlite success web-linearlite deployed (preview) Worker: example-web-linearlite-preview.livestore.workers.dev 2026-07-27 10:23 UTC
web-todomvc success web-todomvc deployed (preview) Worker: example-web-todomvc-preview.livestore.workers.dev 2026-07-27 10:23 UTC
web-todomvc-script success web-todomvc-script deployed (preview) Worker: example-web-todomvc-script-preview.livestore.workers.dev 2026-07-27 10:23 UTC
web-todomvc-sync-cf success web-todomvc-sync-cf deployed (preview) Worker: example-web-todomvc-sync-cf-preview.livestore.workers.dev 2026-07-27 10:23 UTC

@schickling-assistant
schickling-assistant force-pushed the schickling-assistant/2026-07-27-release-credential-surfaces branch from 0ebf486 to f63ae8d Compare July 27, 2026 19:10
@schickling-assistant
schickling-assistant force-pushed the schickling-assistant/2026-07-27-release-registry-verify branch 2 times, most recently from c3d26f0 to 0d0157b Compare July 27, 2026 20:40
@schickling-assistant
schickling-assistant force-pushed the schickling-assistant/2026-07-27-release-credential-surfaces branch 3 times, most recently from 336c0ce to 1303c4e Compare July 27, 2026 21:47
schickling-assistant and others added 3 commits July 28, 2026 00:04
The production deploy steps depend on three long-lived credentials (Netlify,
Cloudflare, Mixedbread). Their names were repeated as string literals across
five `env:` blocks in release.yml.genie.ts, which is how #1284 happened:
release.yml referenced MXBAI_VECTOR_STORE_ID_PROD, a secret that never existed,
and nothing noticed until the publish job reached that step.

Declare each surface once in `genie/release-credentials.ts` — its secrets plus
a read-only probe that proves they are still accepted. Both consumers derive
from it: release.yml's deploy steps derive their `env:` blocks, and a new
health-release-credentials.yml derives a weekly liveness probe. The two can no
longer disagree about which credentials a release needs, and the #1284 class
becomes unrepresentable rather than merely detectable — same idea as `ci.ts`
keeping required checks aligned with matrix expansions.

The refactor is behavior-preserving: regenerating leaves release.yml byte-identical.

The health check is deliberately narrow. validate-release-plan already exercises
release code on every PR in the real Nix environment; the only thing it cannot
prove is that a token which expires on wall-clock time is still valid. So the
check runs weekly, makes only HTTP calls, and sets up no toolchain — an API call
has no environment to reproduce, and approximating the release job's shell would
just create a second environment that drifts from the first. No tooling probes,
no issue filing.

Probes fail closed, verified against the live endpoints with an invalid token:
Netlify 401, Mixedbread 401, and Cloudflare's /user/tokens/verify returning a
non-active status — so a token that still authenticates but was disabled is
caught rather than passing. No --location, so a redirect cannot replay the
Authorization header to another host.

Adds LS.DEL.REL-R10.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The previous wording claimed the shared declaration makes a missing secret
"unrepresentable". It does not: a typo in `release-credentials.ts` propagates to
both consumers exactly as #1284 did. What the shared declaration removes is
divergence *between* release.yml and the probe.

The real improvement for #1284 is timing — a secret that does not exist arrives
empty and trips the probe's `:?` guard within a week, rather than surfacing when
a release reaches that deploy step.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
main #1507 gave each docs surface its own search index, so the prod docs sync
takes MXBAI_VECTOR_STORE_ID_PROD rather than MXBAI_VECTOR_STORE_ID. Updating the
one declaration moves both the release step's env block and the health probe,
which is the point of declaring surfaces once.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@schickling-assistant
schickling-assistant force-pushed the schickling-assistant/2026-07-27-release-registry-verify branch from 8897bd7 to 7945351 Compare July 27, 2026 22:04
@schickling-assistant
schickling-assistant force-pushed the schickling-assistant/2026-07-27-release-credential-surfaces branch from 1303c4e to 9b47f72 Compare July 27, 2026 22:04
@schickling-assistant
schickling-assistant marked this pull request as ready for review July 27, 2026 22:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:ci CI workflows, runners, and pipeline configuration · Set: manual area:tooling Developer tooling, scripts, and utilities · Set: manual

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant