Settings::reject_placeholder_secrets fails startup when a secret still holds a known template value. It covers ec.passphrase, publisher.proxy_secret, handler passwords, and ec.partners[].api_token — but not ec.partners[].ts_pull_token, and its api_token list has drifted away from the values the template actually ships.
What's wrong
Two separate gaps, both in crates/trusted-server-core/src/settings.rs:
-
reject_placeholder_secrets (~line 3084) loops over ec.partners and checks only api_token. ts_pull_token is never checked at all. Its only validation is a secret-key-reference shape check in config.rs:402 (store mode only) and a present/non-empty check in ec/registry.rs:336 (only when pull_sync_enabled). An unmodified template value therefore passes startup and is sent as the outbound bearer token in ec/pull_sync.rs:201.
-
EcPartner::API_TOKEN_PLACEHOLDERS (~line 447) lists partner-api-token-32-bytes-minimum and replace-with-partner-api-token-32-bytes-minimum. Neither appears in trusted-server.example.toml, which ships partner_api_token (line 99) and ts_pull_token = "partner_ts_pull_token" (line 101). So the check that does exist misses the literal an operator is most likely to leave behind.
Note on an earlier draft: this issue previously named the string replace-with-partner-ts-pull-token. That value does not exist anywhere in the repo — don't go looking for it. The real values are the two partner_* literals above.
Acceptance criteria
reject_placeholder_secrets rejects ec.partners[].ts_pull_token when it holds a template placeholder, reporting the field as ec.partners[<source_domain>].ts_pull_token to match the existing api_token branch.
- The placeholder lists cover the literals
trusted-server.example.toml actually ships (partner_api_token, partner_ts_pull_token), keeping the existing entries.
- Regression tests: one asserting a placeholder pull token fails, one asserting a realistic non-placeholder token still passes. Follow the shape of
reject_placeholder_secrets_includes_handler_passwords (~line 5391).
cargo test-fastly and cargo clippy-fastly pass.
Guardrail
reject_placeholder_secrets runs at startup via config.rs:271, so an over-broad list hard-fails live deployments. Only add literals that genuinely appear in the template — nothing that merely looks placeholder-ish. partner-api-token-32-bytes-minimum is unused by the template but harmless to keep; removing entries is out of scope.
Pointers
crates/trusted-server-core/src/settings.rs:447 — API_TOKEN_PLACEHOLDERS, is_placeholder_api_token
crates/trusted-server-core/src/settings.rs:3084 — the partner loop to extend
crates/trusted-server-core/src/settings.rs:5391 — test pattern to copy
trusted-server.example.toml:99-101 — the template values in question
crates/trusted-server-core/src/ec/pull_sync.rs:201 — where the token is consumed
Scope is one constant, one if let block, and two tests. reject_placeholder_secrets has a single production caller, and no existing test fixture uses the affected literals.
Settings::reject_placeholder_secretsfails startup when a secret still holds a known template value. It coversec.passphrase,publisher.proxy_secret, handler passwords, andec.partners[].api_token— but notec.partners[].ts_pull_token, and itsapi_tokenlist has drifted away from the values the template actually ships.What's wrong
Two separate gaps, both in
crates/trusted-server-core/src/settings.rs:reject_placeholder_secrets(~line 3084) loops overec.partnersand checks onlyapi_token.ts_pull_tokenis never checked at all. Its only validation is a secret-key-reference shape check inconfig.rs:402(store mode only) and a present/non-empty check inec/registry.rs:336(only whenpull_sync_enabled). An unmodified template value therefore passes startup and is sent as the outbound bearer token inec/pull_sync.rs:201.EcPartner::API_TOKEN_PLACEHOLDERS(~line 447) listspartner-api-token-32-bytes-minimumandreplace-with-partner-api-token-32-bytes-minimum. Neither appears intrusted-server.example.toml, which shipspartner_api_token(line 99) andts_pull_token = "partner_ts_pull_token"(line 101). So the check that does exist misses the literal an operator is most likely to leave behind.Note on an earlier draft: this issue previously named the string
replace-with-partner-ts-pull-token. That value does not exist anywhere in the repo — don't go looking for it. The real values are the twopartner_*literals above.Acceptance criteria
reject_placeholder_secretsrejectsec.partners[].ts_pull_tokenwhen it holds a template placeholder, reporting the field asec.partners[<source_domain>].ts_pull_tokento match the existingapi_tokenbranch.trusted-server.example.tomlactually ships (partner_api_token,partner_ts_pull_token), keeping the existing entries.reject_placeholder_secrets_includes_handler_passwords(~line 5391).cargo test-fastlyandcargo clippy-fastlypass.Guardrail
reject_placeholder_secretsruns at startup viaconfig.rs:271, so an over-broad list hard-fails live deployments. Only add literals that genuinely appear in the template — nothing that merely looks placeholder-ish.partner-api-token-32-bytes-minimumis unused by the template but harmless to keep; removing entries is out of scope.Pointers
crates/trusted-server-core/src/settings.rs:447—API_TOKEN_PLACEHOLDERS,is_placeholder_api_tokencrates/trusted-server-core/src/settings.rs:3084— the partner loop to extendcrates/trusted-server-core/src/settings.rs:5391— test pattern to copytrusted-server.example.toml:99-101— the template values in questioncrates/trusted-server-core/src/ec/pull_sync.rs:201— where the token is consumedScope is one constant, one
if letblock, and two tests.reject_placeholder_secretshas a single production caller, and no existing test fixture uses the affected literals.