Skip to content

core: split secret resolution into a pure plan and an executor#126

Open
domenkozar wants to merge 4 commits into
mainfrom
refactor/resolution-plan-executor
Open

core: split secret resolution into a pure plan and an executor#126
domenkozar wants to merge 4 commits into
mainfrom
refactor/resolution-plan-executor

Conversation

@domenkozar

Copy link
Copy Markdown
Member

Closes #124.

Splits secret resolution into a pure, I/O-free plan and a small executor that consumes it, so deciding what to do (profile merge, effective config, address derivation, provider routing, grouping) no longer interleaves with doing it (fetching, fallbacks, defaults, generation). Shipped as four reviewable slices plus one behavior fix.

Slices

  1. Pure plan (secretspec/src/plan.rs, new): build_plan() turns the manifest + alias maps into an immutable ResolutionPlan with no I/O — fully unit-testable without any provider.
  2. Executor: execute_plan() builds a provider per primary-store group, fetches groups concurrently, walks fallbacks, generates, and applies defaults. validate_audited shrinks from ~380 lines to a reason-gate + build_plan → coordinate-check → execute_plan + one audit event.
  3. get/set on the plan: single-secret operations plan via the same plan_secret/route_for, so per-secret decisions (effective config, address, route) can't drift between single-secret ops and batch validation.
  4. Single-store ref coordinate validation: a ref routed at exactly one store (an explicit --provider, a single-provider chain, or the default) is checked for coordinate compatibility before any fetch; a ref on a multi-store chain defers to per-store checking at read time.

Behavior refinements (see CHANGELOG)

  • Fallback chains are tried strictly in order. The plan resolves only a chain's primary up front; each fallback link is resolved lazily, only when a read reaches it. So check/run/get stop at the first provider that answers and an undefined or unreachable link past it is never touched. This also fixes a pre-existing case where a live fallback sitting before an undefined alias wrongly failed the read.
  • Single-store ref coordinate mismatches fail fast (e.g. a field ref pointed at a .env file), with a clear message, before contacting the store — and, for the value-free report, without contacting it at all.

Invariants preserved

Wire formats and JSON schemas are unchanged (the plan is crate-internal, not serialized); exactly one Check audit event still fires per read with keys populated even on a route error; build_plan performs no provider construction or network I/O (the coordinate check that builds providers lives in the executor side).

Testing

cargo test --all green (388 secretspec unit tests + derive/ffi/node/py/integration/doc suites, 0 failures); clippy + rustfmt clean. New unit coverage for planning, routing, tried-in-order fallback, and coordinate validation. Independently reviewed by a verification pass that traced the fallback and coordinate logic and confirmed behavior preservation.

🤖 Generated with Claude Code

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 9, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
secretspec 20197a0 Commit Preview URL

Branch Preview URL
Jul 10 2026, 05:13 PM

domenkozar and others added 4 commits July 9, 2026 20:54
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
npm 12's in-place global self-install leaves libnpmpublish's bundled
sigstore dependency missing, breaking `napi pre-publish` provenance
with "Cannot find module 'sigstore'". Pin to npm@^11.5.1, which still
satisfies the Trusted Publishing minimum.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Isolate the "decide what to do" half of secret resolution into a new
`plan` module. `Secrets::build_plan` merges the profile, computes each
secret's effective config, resolves its address (native `ref` coords or
convention naming) and provider route, and groups secrets by primary
store — all without touching a provider. The batch resolver
(`validate_audited`) now consumes this plan for its grouping and configs
instead of deriving them inline; the executor half is unchanged and moves
onto the plan in a follow-up slice.

Planning is now unit-testable without any provider, and route resolution
is eager: an undefined alias anywhere in a `providers` fallback chain is
reported up front rather than only when the broken link is reached.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Code review follow-ups on the resolution plan refactor:

- A chain entry that fails to resolve (an undefined alias) is now one
  broken link, skipped with a warning like a provider that fails to
  construct or read, so a working provider later in the chain still
  answers. Previously it aborted the whole read, contradicting the
  "tried strictly in order" guarantee.
- get and set record an audit Error event when a secret's routing fails
  to resolve, matching how the batch path audits every planning failure.
- A chain entry misspelling onepassword as 1password now reaches the URI
  parser's "use onepassword instead" correction rather than a generic
  undefined-alias error.
- import iterates secrets in sorted name order so its summary output is
  stable, and reads descriptions from the planned (merged) config.
- The single-store ref coordinate check walks the plan's existing groups
  instead of re-deriving its own partition, and set's available-secrets
  listing reuses sorted_secret_names.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@domenkozar domenkozar force-pushed the refactor/resolution-plan-executor branch from 88cdbfa to 20197a0 Compare July 10, 2026 17:11
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.

core: split secret resolution into a pure plan and an executor

1 participant