Document exact native signer and CLI admission requirements
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a140c4-354a-7823-8f0d-8f717397f786
This commit is contained in:
tegwick 2026-10-16 10:48:40 +02:00
parent ff1a13dabd
commit 5a2099f400
6 changed files with 351 additions and 36 deletions

View file

@ -23,20 +23,20 @@ $ getent hosts this-service-does-not-exist.flex-auth.svc.cluster.local
`*.svc.cluster.local` name answers with one unrelated public address. Sending
there ships the CheckRequest body and our bearer token to a third party.
`flex-auth.decision-record.v1` supports a detached Ed25519 signature on the
producer side (FLEX-WP-0024), but this consumer does not yet verify it against
independently admitted keys. Without that verification, every binding digest
is either sent by us or published in flex-auth's repo, so a forger can
reproduce those digests. Digests establish integrity of the binding, never
authenticity of the source. As flex-auth put it in `FLEX-DEC-2026-010`:
As of source `ff1a13d` (2026-10-16), this consumer requires the producer's
detached Ed25519 signature using independently admitted local keys and exact
signed evaluator/mode/policy pin. The complete CLI wheel-plus-helper bundle and
strict task handoff are implemented in source. Native signer custody, key/pin
admission, installed placement and actual consumer receipts remain unaccepted;
source enforcement does not establish those facts. See
[the scoped native preparation](proposals/native-cli-admission/README.md).
> Fail-closed protects against a PDP that is ABSENT, not against one that LIES.
The literal loopback address restriction limits the destination; it does not
prove responder identity. The operator must verify the cluster and named pod
behind the port-forward. Signed consumer enforcement, independently admitted
verification keys, rotation and downgrade tests remain open in SECRETS-WP-0014
and Custodian G13. Producer support alone does not close that gap.
Historical 2026-10-12 finding: prior consumer source did not verify producer
signatures, so a forger could reproduce public request digests. The literal
loopback address restriction limited the destination and did not prove responder
identity. That source defect is repaired; FLEX-DEC-2026-010's historical warning that an absent
PDP and a lying responder require different controls remains relevant. The
independently admitted native trust and rollout obligations in SECRETS-WP-0014 T02/T03 remain open.
The Check client disables automatic redirects, environment/system proxies and
the process-global urllib opener. A 3xx response fails closed without contacting
@ -51,9 +51,12 @@ apply when the approval flow invokes the PDP client.
Regression tests use disposable local HTTP servers and synthetic credentials;
they do not demonstrate identity or signature verification on the native pin.
## The supported path
## Historical transport procedure and current admission gate
Use literal loopback `kubectl port-forward` to the admitted named pod after
The following transport example records the earlier supported access shape; it
is incomplete for current native use without independently admitted signing trust,
the reviewed CLI bundle and exact owner configuration. It grants no token mint,
installation or live action. Use literal loopback `kubectl port-forward` to the admitted named pod after
verifying the cluster identity and pod identity. That path rides the Kubernetes
API server's TLS with the operator's cluster credentials; the client cannot infer
that binding from its local URL. Note this
@ -92,8 +95,10 @@ false` and no role bindings — it exists to be a caller identity and nothing el
v1 shipped and was deployed with no `input.tenant` rule at all; a `rotate` under
`tenant:coulomb` returned **allow**. v2 supersedes rather than amends it so the
change is visible in the version string, and this engine refuses a v1 decision
outright. See `docs/tenant-alignment.md`.
change is visible in the version string. Current consumer configuration and signed
trust must explicitly pin the admitted v2 artifact; the validator does not impose
a universal hard-coded package/version default. A v1 configuration is not admitted
by this proposal. See `docs/tenant-alignment.md`.
## The digest join, and why it is not a digest comparison
@ -114,19 +119,18 @@ and the fix follows it rather than inventing one:
> statement of what it hashed. To recompute independently, hash the same
> normalized tuple the binding carries.
`validate_decision_envelope` now does exactly that:
The bullets in earlier revisions described the September structured-enrichment
check. Current source has since moved to the producer's submitted-request replay
contract: `validate_decision_envelope` validates the exact submitted digest,
opaque enriched digest shape, approval-free correspondence, policy pin and
lifetime on an authenticated envelope. It does not independently reconstruct the
full enriched tuple or native five-field claim in the generic CLI path.
- **Structured correspondence.** Everything we proposed — tenant, action,
context, `subject.id`/`type`, `resource.id`/`type`/`system` and every
attribute we sent — must survive unchanged in the binding.
- **Enrichment only where the contract permits it.** A ref may gain `type`,
`tenant` and `attributes`; any other added field is refused. An enriched
`tenant` must be the request's tenant, so a cross-tenant binding cannot arrive
wearing our request's clothes.
- **Digest self-consistency.** `request_digest` is recomputed over the
normalized tuple the binding carries, per the contract's own instruction. It
is still checked — just for coherence of the binding rather than against
material we never sent.
The separate strict `task_execution_handoff` additionally recomputes the evaluated
and approval-free digests, verifies exact descriptor/context/native claim joins
(including the served native digest), and binds signed enforced caller provenance.
That source seam is not an `execute-ccr` CLI command and does not admit the proposed
service caller mapping or enable either task policy's runtime false gate.
The lesson worth keeping: the replay fixtures could not catch this, because
`_request_from()` rebuilds the request from the binding, so the tests hashed the