Skip to content

feat(assertions): add a secret source for downstream header injection #93

Description

@terylt

Description

The assertions: block renders engine-derived state onto the upstream request as headers, and SourcePath (crates/ppe-core/src/assertions/source.rs:122) enumerates the slots an entry may read. None of them is a secret, so a target sitting behind a static API key has no path today short of a token delegator, which is per-request exchange machinery that a shared static credential does not need.

Add a secret.<name> source resolving to a value declared in the secrets.values block. The engine renders the header; no plugin and no PDP ever holds the bytes.

assertions:
  request:
    - header: X-API-Key
      from: secret.legacy_api_key

The path names a declared secret, not a provider and a reference. That is what keeps the module's existing property intact: the engine originates every value a request entry asserts, so the legitimate set is finite and anything unnamed can be withheld. A raw reference in the path would make the addressable set whatever the provider's credentials can reach, with no list an operator could audit.

Acceptance criteria

  • SourcePath gains a secret variant, authored as secret.<name>. Provider names and references never appear in an assertion path.
  • The name must resolve to a declared secrets.values entry at config load. An unknown name is a config error, not a runtime miss.
  • The read is sync, from the value resolved at startup. No fetch on the request path.
  • Valid in the request direction only. A response entry naming a secret is a config error, since a secret is not something an upstream told us.
  • on_missing applies as it does for any other source. The response floor is unaffected.
  • The effective-policy artifact prints the secret name and never the value.
  • The value never reaches a PDP, a plugin payload, a log, a deny message, or the audit record.
  • SourcePath::capability() returns the capability that would gate a plugin's read of this slot, consistent with the existing table, even though nothing reads it on this path.
  • Tests: injection onto the upstream request, an unknown name rejected at load, a response-direction entry rejected, a rotated secret picked up after refresh, and redaction in the artifact.

Notes

This is the outbound half of what #65 describes. Whether PPE also wants an inbound API key verifier, where a caller presents a key and PPE maps it to an identity, is a separate call; it is a different feature that happens to share the words "API key", and it is what #64 is a dependency of.

Depends on

#92

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions