Skip to content

Add secret-store references for app-config secrets - #873

Closed
prk-Jr wants to merge 5 commits into
mainfrom
846-secrets-to-secret-store
Closed

Add secret-store references for app-config secrets#873
prk-Jr wants to merge 5 commits into
mainfrom
846-secrets-to-secret-store

Conversation

@prk-Jr

@prk-Jr prk-Jr commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Stops ts config push from persisting secret values in the config store: an opt-in [secrets] mode makes secret-bearing config fields hold secret-store key names, resolved from the platform secret store at settings load.
  • Keeps the config blob free of plaintext secrets while preserving existing behavior — inline mode is unchanged and the default.
  • Fastly-first: the Fastly adapter resolves against its secret store; other adapters fail closed on store-mode blobs until wired in follow-ups.

Changes

File Change
crates/trusted-server-core/src/secret_refs.rs New. Resolver that walks the verified blob JSON and swaps secret-ref key names (nested + array paths) for values from PlatformSecretStore; mirrors EdgeZero secret_walk semantics.
crates/trusted-server-core/src/settings.rs Add [secrets] section (enabled, store); SecretFieldMode split; validate_secret_key_names; gate admin-password check by mode.
crates/trusted-server-core/src/config.rs Deploy validation branches on mode (key-name shape vs placeholder rejection); registry built with mode-aware token checks.
crates/trusted-server-core/src/config_payload.rs settings_from_config_blob_with_secrets resolves refs between envelope verify and parse; fails closed when store-mode is enabled but no secret store is wired.
crates/trusted-server-core/src/ec/registry.rs from_config_with_secret_mode defers the API-token length check to runtime in key-name mode.
crates/trusted-server-core/src/settings_data.rs Loader takes Option<&dyn PlatformSecretStore>.
crates/trusted-server-adapter-fastly/src/app.rs Pass FastlyPlatformSecretStore into the config load path.
crates/trusted-server-adapter-axum/src/app.rs Pass None (fail closed on store-mode; wiring is a follow-up).
crates/trusted-server-core/src/lib.rs Register secret_refs module.
fastly.toml Local Viceroy ts_secrets entries for ec_passphrase / proxy_secret / admin_password.
trusted-server.example.toml Document store mode and the seed-values-first / rollout-order guidance.
docs/superpowers/plans/2026-07-09-secrets-to-secret-store.md Implementation plan.

Closes

Closes #846

Test plan

  • cargo test-fastly && cargo test-axum && cargo test-cloudflare && cargo test-spin
  • cargo clippy-fastly && cargo clippy-axum && cargo clippy-cloudflare && cargo clippy-cloudflare-wasm && cargo clippy-spin-native && cargo clippy-spin-wasm (1.95.0)
  • cargo fmt --all -- --check
  • Parity: cargo test --manifest-path crates/trusted-server-integration-tests/Cargo.toml --test parity
  • Manual testing via fastly compute serve: store-mode config booted with secrets resolved from ts_secrets (200 render); removing a secret entry fail-closed with a 500 naming the missing ref (ec.passphrase), no value leaked.
  • JS tests / JS format / docs format — no JS changes; docs formatting deferred to CI.

Hardening note

Store-mode resolution runs before Settings parse; the runtime then runs full validation against the resolved values (min-length passphrase, placeholder rejection, admin-password check). A missing or invalid secret returns a Configuration error, which the Fastly entry point surfaces via the startup-error router (fail-closed) — no panic!/unwrap!/expect! on the config-derived path. Push/deploy validation only checks key-name shape (non-empty, no whitespace/control chars).

Checklist

  • Changes follow CLAUDE.md conventions
  • No unwrap() in production code
  • Uses log macros (not println!)
  • New code has tests
  • No secrets or credentials committed (local ts_secrets values are obvious dev placeholders)

Secret-bearing config fields (ec.passphrase, publisher.proxy_secret,
ec.partners[].api_token, ec.partners[].ts_pull_token, handlers[].password)
previously rode in the app-config blob as plaintext, so `ts config push`
persisted them into the config store. Add an opt-in `[secrets]` mode where
those fields instead hold secret-store key names, resolved from the platform
secret store at settings load.

Validation splits along the EdgeZero 3.3.8 boundary: push/deploy validates
key-name shape and skips value-shape checks (a key name is not the secret);
runtime resolves the references first, then runs full validation against the
real values and fails closed if a referenced secret is missing or invalid.

Fastly wires FastlyPlatformSecretStore into the config load path; the other
adapters pass None and fail closed on store-mode blobs until wired in
follow-ups. Local Viceroy secrets and a store-mode template are documented.

Closes #846
@prk-Jr prk-Jr self-assigned this Jul 9, 2026
prk-Jr and others added 3 commits July 9, 2026 12:58
Reject TRUSTED_SERVER__ secret env overrides at push/diff/validate time when
store mode is enabled, so the push env overlay cannot overwrite a secret-ref
key name with a plaintext value in the config blob (independent of --no-env).

Omit the [secrets] section from the serialized blob whenever store mode is
disabled, not only at the exact default, so inline-mode blobs stay compatible
with binaries that predate the field under deny_unknown_fields.

Fail closed in the resolver when a covered array section (ec.partners,
handlers) is present in a non-array encoding instead of silently skipping it.
Keep the partner API-token non-empty check in key-name mode and defer only the
minimum-length rule.

Correct the docs: the Fastly CLI reads secret values from stdin or a file
(there is no --secret flag), and list every covered override including partner
ts_pull_token.
@prk-Jr
prk-Jr marked this pull request as ready for review July 9, 2026 08:48
ChristianPavilonis

This comment was marked as low quality.

Store mode defers ec.partners[].api_token value checks (length,
token-hash uniqueness, pull-sync consistency) from deploy time, but the
runtime load path only deserialized settings and rejected placeholders
without building the partner registry. A mis-seeded partner secret could
let the service start and serve unrelated routes, failing only on
partner-specific routes instead of failing closed at startup.

Build the partner registry in settings_from_config_blob_with_secrets so
resolved-value validators run at load in both inline and store mode.
@aram356 aram356 added this to the 202608 milestone Aug 17, 2026
@ChristianPavilonis

Copy link
Copy Markdown
Collaborator

Superseded by #1036

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.

Move secrets from trusted-server.toml to secret store

3 participants