- cmd/flex-auth -> cmd/access-engine; usage and serving lines say access-engine - cmd/flex-auth: prints the rename and exits 2, with a test - image: entrypoint /usr/local/bin/access-engine, flex-auth stub alongside; live pins pass only args, so their next roll is unaffected - Makefile, CI, verify-posture, tenancy.yaml, live docs updated; workplans, decisions and ADRs left as written - runtime names (module, audience, k8s, image package, /opt paths) unchanged Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 735967@bnt-lap001 Assistant-Session: 5971967f-77f8-4ced-9e02-c6ef67040ffc
89 lines
4.7 KiB
Markdown
89 lines
4.7 KiB
Markdown
# Decision input freshness
|
|
|
|
Status: published
|
|
Date: 2026-09-02
|
|
Standard: security-layer-model_v0.7 §9.7.2
|
|
|
|
A flex-auth decision is a join over sources that refresh independently. One
|
|
number at a PDP would be either a fiction or the worst case. This document
|
|
states a **visibility deadline per input class**, with the mechanism that
|
|
bounds it.
|
|
|
|
Provenance identifies which snapshot and package a decision read
|
|
(`registry_snapshot_digest`, `policy_package_digest`, `directory_etag`,
|
|
`input_claim_digests`). Without those digests a deadline is unfalsifiable.
|
|
|
|
Operational bound shared by every class: an allow is never valid past
|
|
`lifetime.expires_at` (default TTL 15m). That is the consumer-visible ceiling.
|
|
The class deadlines below say when a **new Check** is guaranteed to see a
|
|
revocation of that class of input.
|
|
|
|
## Approval-claim freshness
|
|
|
|
| | |
|
|
| --- | --- |
|
|
| What is joined | Request-time approval and identity claims on `CheckRequest.context` (and related subject attributes) |
|
|
| Mechanism | No PDP-side cache of claims. Each Check evaluates the claims as presented. |
|
|
| Deadline | **Immediate** on the next Check (0). A revoked claim is visible as soon as the consumer stops sending it. |
|
|
| Cached allow | Bounded by `lifetime`. Replay of a previous allow after the claim is revoked is refused once the digest no longer matches or the TTL ends. |
|
|
| Provenance | `input_claim_digests.context` |
|
|
|
|
flex-auth does not store or mutate the approval object (`approval-engine`).
|
|
|
|
## Registry snapshot cadence
|
|
|
|
| | |
|
|
| --- | --- |
|
|
| What is joined | Protected systems, resources, subjects, groups, teams, tenants, relationship facts |
|
|
| Mechanism | In-memory `registry.Store` loaded at process start and by `access-engine load-registry`. There is no background watch. |
|
|
| Deadline | A registry-borne revocation is visible after the operator reloads the snapshot **or** when the current allow TTL ends, whichever is first. On a running process that has not reloaded, the snapshot is otherwise unchanged. |
|
|
| Provenance | `registry_snapshot_digest` — SHA-256 of canonical snapshot JSON |
|
|
|
|
The digest makes the deadline checkable: a later Check whose digest differs
|
|
read a different snapshot.
|
|
|
|
## Policy package activation
|
|
|
|
| | |
|
|
| --- | --- |
|
|
| What is joined | The validated Rego-in-Markdown package (`id` / `version` / compiled module) |
|
|
| Mechanism | Loaded at process start and by CLI `--policy`. No hot swap. |
|
|
| Deadline | A package change is visible after reload **or** when the current allow TTL ends, whichever is first. |
|
|
| Provenance | `policy_package`, `policy_version`, `policy_package_digest` |
|
|
|
|
## Directory ETag
|
|
|
|
| | |
|
|
| --- | --- |
|
|
| What is joined | Delegated directory state (Topaz objects/relations, relationship-PDP tuples) and optional group-resolver evidence |
|
|
| Mechanism | Topaz returns `DirectoryETag`; relationship adapters return a consistency token. Group resolvers (`Graph`, `SCIM`, `LDAP`, `Keycloak`) attach `Freshness.MaxAge` when configured. Verdicts are not cached by flex-auth. |
|
|
| Deadline | **Next Check** against the directory for ETag-bearing adapters (0 relative to the directory's own visibility). For resolvers, the configured `MaxAge`; if `MaxAge` is unset, directory-borne group evidence is unbounded except by the allow TTL. |
|
|
| Provenance | `directory_etag`; resolver freshness is subject-attribute evidence, not a second decision |
|
|
|
|
A stale-directory signal from Topaz fails closed (`topaz_directory_stale`).
|
|
|
|
## Lifetime versus an artifact issued under the decision
|
|
|
|
`lifetime.expires_at` bounds the **decision**, not anything a consumer creates
|
|
while acting on it. It is authority to issue, not authority to use.
|
|
|
|
Where a PEP's enforcement produces a durable credential — ops-warden's SSH
|
|
certificate is the shipped case — the credential's own TTL governs it, and no
|
|
flex-auth field reaches it. A short decision lifetime bounds how long a revoked
|
|
principal can keep obtaining *new* credentials, which is the part a PDP can
|
|
bound; it does not recall one already issued. The residue between the two is the
|
|
consumer's §9.7.2 window to state, and it is correctly stated at the PEP.
|
|
|
|
The lever that shortens the artifact's own life is the requested TTL as a policy
|
|
input, not the decision lifetime. See `FLEX-DEC-2026-004`.
|
|
|
|
## How to read this as a consumer
|
|
|
|
1. Do not cache a verdict past `lifetime.expires_at`.
|
|
2. To know whether a stored allow still names the same facts, compare
|
|
`registry_snapshot_digest`, `policy_package_digest`, and `directory_etag`
|
|
to a fresh Check.
|
|
3. Approval revocation is a claim-class problem: stop presenting the claim,
|
|
and do not replay a digest that included it.
|
|
4. Do not read `lifetime` as a bound on a credential you issued under the
|
|
allow. Bound that credential with its own TTL.
|