secrets-engine probed the Service DNS name handed over in FLEX-WP-0021-T05 and found it resolves, from the workstation, to an unrelated public host. Reproduced here: search ad.binect.de answers wildcard, so flex-auth-secrets-engine.flex-auth.svc.cluster.local and this-service-does-not-exist.flex-auth.svc.cluster.local both resolve to 80.158.43.29, while the trailing-dot FQDN correctly fails. A bare Service name in a handover is not merely unreachable from there, it is a live misdirection, and the handover was ours. Had a deployment pointed at it, the CheckRequest body would have gone to that host: subject, tenant, lane and resource ids, stage, field names, purpose, plus the caller's bearer token. Trailing-dot FQDN and "in-cluster only" now replace the bare name in the example README, SCOPE.md, and the T05 note. Their real question was how the response channel is authenticated, and they declined to answer it locally because choosing a transport control for our service is not a consumer's call. Right boundary, so the answer is recorded here as FLEX-DEC-2026-010: it is not authenticated. Pins serve plain HTTP, the envelope carries no signature, and a responder that knows the package id and version can return a well-formed allow that passes every check a consumer performs. The part worth stating in the contract is that the digests do not help and look like they do. Every input to request_digest, policy_package_digest and registry_snapshot_digest is either sent by the caller or published in this repo, so a forger reproduces all three exactly. They establish integrity of the binding, never authenticity of the source — and publishing more digests makes a forged envelope look more authenticated, not less. For secrets-engine specifically: fail-closed protects against a PDP that is absent, not against one that lies. An unreachable PDP denies; a lying PDP allows. Third instance of one seam in three decisions. 008: a tenant carried into the digest and never compared — visible, not enforced. 009: a caller authenticated and never recorded — enforced, not visible. 010: a record verifiable and unauthentic — checkable, but not evidence. One nuance that changes the operator recommendation: kubectl port-forward does authenticate the responder, transitively — no DNS name, one named pod, API-server TLS. That is the exact reverse of the caller direction, where it bypasses the NetworkPolicy. Independent properties pointing opposite ways, so neither can be summarised as "the network protects it". FLEX-WP-0024 carries signing; key custody routes through warden/OpenBao rather than minting a key here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014aQMM1dPXaPiXVn6DwwtLd Assistant: claude-code Assistant-Model: opus Assistant-Process: 715613@bnt-lap001 Assistant-Session: fabd95c1-4c9e-4080-8849-8707ae025f80
306 lines
15 KiB
Markdown
306 lines
15 KiB
Markdown
# flex-auth Scope
|
|
|
|
## One-liner
|
|
|
|
flex-auth is a policy-as-code authorization registry and decision engine for
|
|
NetKingdom-aligned systems. Under the NetKingdom Security Layer Model
|
|
(`security-layer-model_v0.7`, accepted 2026-08-29) it is **Engine / PDP** — the
|
|
estate's only policy decision point.
|
|
|
|
## Core Idea
|
|
|
|
flex-auth turns verified identity claims, resource manifests, relationship
|
|
facts, policy packages, and request context into inspectable authorization
|
|
decisions. It is intentionally separate from identity providers and from the
|
|
applications that enforce decisions.
|
|
|
|
The product should start as a useful standalone local authorization system,
|
|
then grow into delegated mode where Topaz, OpenFGA, SpiceDB, OPA, Cedar,
|
|
Keycloak Authorization Services, Entra, Graph, SCIM, LDAP, and similar systems
|
|
can be coordinated behind a stable flex-auth API.
|
|
|
|
## In Scope
|
|
|
|
- Protected-system registration.
|
|
- Resource namespaces, resource hierarchy, and action vocabularies.
|
|
- Subject, group, team, tenant, role, and service-account records needed for
|
|
authorization.
|
|
- Relationship facts and inherited access.
|
|
- Versioned policy packages with metadata, fixtures, validation, tests, and
|
|
activation state.
|
|
- Deterministic check, batch-check, list-allowed, explain, and decision-record
|
|
capabilities.
|
|
- Local decision logs with policy version and provenance.
|
|
- CLI and service entry points for local development and integration tests.
|
|
- Markitect as the first concrete protected-system consumer.
|
|
- Delegated PDP and directory adapter boundaries after the standalone core is
|
|
stable.
|
|
- The **decision-record schema**, published as flex-auth's own contract
|
|
(`security-layer-model_v0.7` §17).
|
|
- The **canonical request digest** as the published replay test for consumers
|
|
(§6.4.2).
|
|
- **Decision lifetimes** on every allow, and **revocation visibility deadlines
|
|
stated per input class** (§9.7).
|
|
- **Registry-snapshot provenance**, so a decision that turned on registry
|
|
content is replayable from its own record (§6.1, §9.7.2).
|
|
|
|
## Out of Scope
|
|
|
|
- Acting as an identity provider.
|
|
- Replacing key-cape, NetKingdom SSO, Keycloak, Entra, or other identity
|
|
systems.
|
|
- Owning login, MFA, PKCE, token issuance, token lifecycle, or canonical
|
|
identity profile behavior.
|
|
- Embedding protected-system enforcement logic inside flex-auth.
|
|
- Making Markitect-specific assumptions part of the generic authorization
|
|
model.
|
|
- Forcing a single PDP backend or directory provider.
|
|
- Hiding policy behavior behind opaque administrative toggles.
|
|
- Expressing fail-open. When flex-auth is unreachable there is no evaluator in
|
|
the path; the residue is the consumer's declared stance (§9.3).
|
|
- Owning enforcement. A PEP's obligations under §6.4 are the consumer's,
|
|
including its published stance map.
|
|
- Owning the approval object, its storage, or its lifecycle — that is
|
|
`approval-engine`'s (§9.4). flex-auth validates approvals as input claims and
|
|
never mutates them.
|
|
- Owning evidence of authentication or assurance. flex-auth consumes assurance
|
|
claims and never redefines them; it owns evidence of the decision
|
|
(`FLEX-DEC-2026-002`, recorded as declined in §13).
|
|
- Authoring security doctrine. gate-house owns invariants, authority ceilings,
|
|
operating modes, and the authority context flex-auth consumes as claims.
|
|
|
|
## Relevant When
|
|
|
|
- A protected system needs resource-specific authorization decisions.
|
|
- A team needs policy-as-code packages that can be reviewed, tested, versioned,
|
|
and explained.
|
|
- Authorization must combine identity claims, groups, roles, tenants,
|
|
relationships, context, and resource metadata.
|
|
- A local development workflow needs deterministic authorization behavior
|
|
before enterprise PDP integrations exist.
|
|
- Markitect resources need central authorization decisions and durable audit
|
|
records.
|
|
- A delegated backend needs to be introduced without changing the
|
|
protected-system-facing API.
|
|
|
|
## Not Relevant When
|
|
|
|
- The task is only about authentication, login, token issuance, or MFA.
|
|
- The desired outcome can be handled by coarse identity-provider roles alone.
|
|
- The protected system only needs local development labels and no central
|
|
policy administration.
|
|
- A team is choosing identity-provider configuration rather than
|
|
resource-specific authorization.
|
|
|
|
## Current State
|
|
|
|
The standalone core is implemented and **all seven baseline workplans
|
|
(`FLEX-WP-0001` through `FLEX-WP-0007`) are complete.** The repository carries
|
|
the intent baseline, authorization landscape research, ADR set, and a working
|
|
Go service (`cmd/flex-auth`) with `validate`, `load-registry`, `serve`, and
|
|
`POST /v1/check` plus registry, policy, decision, audit, Markitect, and
|
|
delegated-adapter internals. The standalone policy-as-code core (`FLEX-WP-0002`),
|
|
Markitect consumer integration (`FLEX-WP-0003`, manifest ingest, decisions, and
|
|
fixtures), and the delegated PDP/directory adapter shapes (`FLEX-WP-0004`,
|
|
Topaz/OpenFGA/OPA/Cedar/Keycloak/Entra tradeoffs documented with at least one
|
|
controlled adapter shape) all landed in May 2026.
|
|
|
|
Standalone decisions include a structured `binding` containing the normalized
|
|
subject, action, resource, context, and a full request digest. This makes the
|
|
execute-time decision contract inspectable without parsing reason or diagnostic
|
|
prose. Durable human approval lifecycle, supersession, and authenticated
|
|
multi-approver evidence are `approval-engine` responsibilities; the proposed
|
|
cross-system object is documented in
|
|
`schemas/action_authorization.schema.json` and is not yet a deployed
|
|
`approval-engine` endpoint.
|
|
|
|
**secrets-engine is a shipped consumer as of 2026-09-06** (`FLEX-WP-0021`):
|
|
`secrets-engine.catalog-lane.lifecycle` v2 over `resource.type:
|
|
secret-catalog-lane`, twelve actions delivered by secrets-engine rather than
|
|
inferred, `destroy` gated on a dual-control approval claim, and a dedicated
|
|
`flex-auth-secrets-engine` pin at
|
|
`http://flex-auth-secrets-engine.flex-auth.svc.cluster.local.:8080` —
|
|
trailing dot required, in-cluster only — with `callerAuth.mode: warn`. Adoption is not complete: secrets-engine is a CLI
|
|
rather than a workload, and the pin's default-deny ingress admits a pod, so an
|
|
operator-run access path is still undecided.
|
|
|
|
v1 of that package **had no tenant rule and allowed a foreign tenant**; v2
|
|
supersedes rather than amends it, because a fail-open correction must be visible
|
|
to a consumer as a version change (`FLEX-DEC-2026-008`). The sweep that finding
|
|
prompted shows `tenant-engine` unscoped on tenant as well, carried by
|
|
`FLEX-WP-0022`.
|
|
|
|
The **first shipped protected-system consumer is ops-warden**: its opt-in
|
|
pre-sign gate calls `POST /v1/check` for `resource.type: ssh-certificate`,
|
|
`action: sign` decisions (`examples/ops-warden/`, policy package, allow/deny
|
|
fixtures, and tests). `FLEX-WP-0006` published that gate and `FLEX-WP-0007`
|
|
deployed flex-auth as a reachable production runtime for it. The joint
|
|
OpenBao-backed smoke is verified (2026-06-29: vault-backed allow recorded
|
|
`decision:032b096c433ad80c`; TTL-over-max denied `ttl_out_of_bounds` by
|
|
flex-auth before OpenBao). `FLEX-WP-0016` then shipped an independently rollable
|
|
in-cluster ops-warden pin and moved it to `callerAuth.mode: enforce` once
|
|
ops-warden adopted a calling identity (`WARDEN-WP-0031`): anonymous `/v1/check`
|
|
is 401, a token bound to another protected system is 403.
|
|
|
|
Production `policy.enabled` is **deliberately off, and is not coming back as a
|
|
boolean.** ops-warden's `ADR-0009` (2026-08-22, superseding `ADR-0006`) settles it:
|
|
`policy.enabled` is one repo-wide flag, and with `fail_closed: true` it makes
|
|
flex-auth a hard dependency of every `warden sign` — including the SSH
|
|
certificates the ops-bridge tunnels depend on, one of which carries the policy
|
|
call itself. `ADR-0009` retires `policy.enabled` and the global `policy.fail_closed`
|
|
outright and replaces them with a total per-zone stance map in the consumer PEP
|
|
(`ops-warden/pep-stance.yaml`): open for `z0`-`z2` and `unknown`, closed for
|
|
`z3-critical`. That map is the reference shape for `security-layer-model_v0.7`
|
|
§6.4 obligation 3. It is no longer the only published one: `secrets-engine`
|
|
published `pep-stance.yaml` (total over catalog stage plus `unknown`, runtime-read
|
|
and test-pinned), so §13.1's register now has **two rows** rather than the one the
|
|
standard recorded as itself the finding. flex-auth reviewed the pair in
|
|
`docs/stance-register-review.md` — the first exercise of the aggregate-divergence
|
|
capability it claimed on 2026-08-29 — and found that the two maps take opposite
|
|
stances on `unknown` (`fail_open` vs `fail_closed`) and scope on different axes
|
|
(security-zone vs catalog-stage), which makes the rows conformant but not
|
|
comparable.
|
|
|
|
flex-auth reviewed the zone model as the PDP on 2026-08-19 and holds one
|
|
boundary: zone **membership** compiles into
|
|
the registry snapshot flex-auth already consumes (no schema change needed), while
|
|
per-zone **stance** belongs in the versioned policy package, because registry
|
|
content is absent from decision provenance. **Fail-open is not expressible by a
|
|
PDP at all** and stays a consumer-side setting. The enforcing pins stay
|
|
enforcing regardless — an enforcing pin with no enforcing consumer still turns
|
|
the anonymous path into a 401 rather than a decision.
|
|
|
|
**Layer position (2026-08-29).** The NetKingdom Security Layer Model was
|
|
accepted at v0.7. flex-auth is **Engine / PDP**, declared machine-readably in
|
|
`INTENT.md` frontmatter per §11. Four of flex-auth's review findings were adopted
|
|
into the standard: §9.3's two-owner split for degraded mode, §6.4.2 scoped to the
|
|
decision's own binding with the canonical request digest as its mechanical test
|
|
and negative caching ruled explicitly, §9.7.2 split by role, and §17 moving the
|
|
decision-record schema to flex-auth. Conformance state is **conforming with one
|
|
declared gap** — the registry-snapshot digest in decision provenance, which §9.7.2
|
|
promotes to a conformance prerequisite. `FLEX-WP-0019` carries the work.
|
|
|
|
State Hub integration is present through:
|
|
|
|
- `.custodian-brief.md`
|
|
- `state_hub_workstream_id` fields in workplans
|
|
- `state_hub_task_id` fields in task blocks
|
|
- active State Hub dispatch for the `flex-auth` repo
|
|
|
|
## How It Fits
|
|
|
|
flex-auth sits between identity and protected systems:
|
|
|
|
```text
|
|
key-cape / NetKingdom SSO
|
|
-> verified identity claims
|
|
-> flex-auth authorization registry and policy decisions
|
|
-> protected systems such as Markitect
|
|
```
|
|
|
|
Identity systems establish who the caller is and provide coarse claims.
|
|
flex-auth decides what that caller can do to a specific resource under a
|
|
specific context. Protected systems enforce the decision and keep their own
|
|
local diagnostics.
|
|
|
|
## Terminology
|
|
|
|
- **Subject:** Human, service account, group, team, tenant, role, or emergency
|
|
principal.
|
|
- **Resource:** Protected object registered by a system.
|
|
- **Namespace:** Resource type and ownership boundary.
|
|
- **Action:** Operation requested against a resource.
|
|
- **Context:** Request, environment, assurance, workflow, or runtime
|
|
attributes.
|
|
- **Policy package:** Versioned policy-as-code bundle with metadata and tests.
|
|
- **Relationship fact:** Subject-resource or resource-resource relation.
|
|
- **Decision:** Allow, deny, redact, audit-only, or not-applicable outcome with
|
|
reason and provenance.
|
|
- **PDP:** Policy decision point, either flex-auth standalone logic or a
|
|
delegated backend.
|
|
- **PEP:** Policy enforcement point in the protected system.
|
|
|
|
## Related / Overlapping
|
|
|
|
- key-cape / NetKingdom SSO: identity source and coarse claims provider;
|
|
flex-auth consumes the **NetKingdom IAM Profile**
|
|
(`~/net-kingdom/canon/standards/iam-profile_v0.2.md`).
|
|
- ops-warden: first **shipped** protected-system consumer. Its opt-in
|
|
pre-sign gate calls flex-auth for `ssh-certificate` / `sign` decisions
|
|
before issuing a short-lived SSH certificate (`FLEX-WP-0006`,
|
|
`FLEX-WP-0007`). ops-warden owns the SSH CA, OpenBao signing, and actor
|
|
inventory; flex-auth owns the policy decision. ops-warden's routing
|
|
charter names flex-auth as the owner of every "may I perform action X?"
|
|
question.
|
|
- Markitect: first **knowledge-pipeline** consumer. Integration is complete on
|
|
the flex-auth side (`FLEX-WP-0003` — resource-manifest ingest, Markitect-
|
|
compatible decisions, and fixtures); a live Markitect runtime calling the gate
|
|
in production is the next consumer milestone after ops-warden.
|
|
- Topaz: aligned evaluator. Per ADR-003 the standalone core is shaped
|
|
to match Topaz's Rego + directory model from day one; the Topaz
|
|
adapter in `FLEX-WP-0004` is therefore a small step rather than a
|
|
conversion.
|
|
- OpenFGA and SpiceDB: candidate relationship authorization backends.
|
|
- OPA and Cedar: candidate rule and typed-policy engines.
|
|
- Keycloak Authorization Services: adapter path for Keycloak-centric
|
|
deployments. Default architecture is "Keycloak as SSO only,
|
|
flex-auth owns authorization"; Keycloak AuthZ is one optional
|
|
delegated PDP.
|
|
- Entra, Graph, SCIM, LDAP, and Keycloak APIs: directory and group resolver
|
|
sources.
|
|
|
|
## Disjoint From
|
|
|
|
- **ops-warden** is a flex-auth *consumer*, not an overlap (see Related /
|
|
Overlapping). The two remain disjoint on **identity surface**: ops-warden
|
|
issues SSH certificates for ops actors (`adm`/`agt`/`atm`) and is not a
|
|
resource-policy engine; flex-auth decides whether a given sign request is
|
|
allowed and never issues certificates. The once-hypothetical flow of
|
|
surfacing an `agt` actor as a flex-auth subject is now realized through
|
|
the signing policy gate.
|
|
- **ops-bridge** owns SSH reverse-tunnel connectivity and explicitly
|
|
disclaims being a credential authority or policy engine. No overlap.
|
|
|
|
## Provided Capabilities
|
|
|
|
```capability
|
|
type: orientation
|
|
title: Agent orientation
|
|
status: current
|
|
description: Repo intent, boundaries, research, workplan map, and State Hub agent brief are available for new coding agents.
|
|
```
|
|
|
|
```capability
|
|
type: workplan
|
|
title: Standalone policy core plan
|
|
status: current
|
|
description: Workplan for schemas, local registry, policy packages, check APIs, list/explain, decision log, CLI/service skeleton, tests, and examples.
|
|
```
|
|
|
|
```capability
|
|
type: workplan
|
|
title: Markitect consumer integration plan
|
|
status: current
|
|
description: Workplan for Markitect resource namespace, manifest import, action vocabulary, check fixtures, contract tests, and integration flow.
|
|
```
|
|
|
|
```capability
|
|
type: workplan
|
|
title: Delegated adapter plan
|
|
status: current
|
|
description: Workplan for Topaz evaluation, relationship PDP adapters, rule PDP adapters, Keycloak AuthZ path, directory resolvers, and operations docs.
|
|
```
|
|
|
|
```capability
|
|
type: contract
|
|
title: Decision-record contract
|
|
status: current
|
|
description: Published decision-record schema, canonical request digest as the replay test for consumers, and decision provenance — flex-auth's own contract under security-layer-model v0.7 section 17.
|
|
```
|
|
|
|
```capability
|
|
type: orientation
|
|
title: Layer declaration
|
|
status: current
|
|
description: Machine-readable Engine/PDP declaration in INTENT.md frontmatter with conformance state and the one declared gap, per security-layer-model v0.7 section 11.
|
|
```
|