Land foundations: assessment, ADR-001/002/003, FLEX-WP-0005, Go skeleton

Pre-implementation assessment and boundary review
(docs/pre-implementation-assessment.md) lead to three ADRs:
- ADR-001 Go + repo skeleton
- ADR-002 Rego-in-Markdown policy package format
- ADR-003 Topaz-aligned MVP (Topaz spike moves into foundations)

New workplan FLEX-WP-0005 (Foundations and Topaz Alignment) is inserted
between WP-0001 (done) and WP-0002 (core). WP-0002 pins Rego-in-Markdown
for P2.3; WP-0004 P4.1 refocused from Topaz evaluation to Topaz adapter.

Go skeleton at repo root: cmd/flex-auth + internal/{registry,policy,
decision,audit,adapters} + pkg/api + Makefile + .golangci.yml + GitHub
Actions CI. make ci green locally; bin/flex-auth --version works.

INTENT/SCOPE cite the NetKingdom IAM Profile and add the ops-warden /
ops-bridge disjoint-surface clarifications.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-16 01:54:44 +02:00
parent 485b3992de
commit 55120ec20a
26 changed files with 905 additions and 45 deletions

View File

@@ -0,0 +1,83 @@
# ADR-0001: Implementation Language and Repo Skeleton
Date: 2026-05-15
Status: Accepted
Deciders: Bernd, with assessment from Claude (Opus 4.7)
Supersedes: —
## Context
flex-auth is a policy-as-code authorization registry and control plane. It
must run as a CLI and, later, a service. Its peers in the NetKingdom
ecosystem are written in a mix of languages: `key-cape` is Go, `ops-bridge`
and `ops-warden` are Python, the State Hub itself is Python. There is a
recorded State Hub decision noting that Go was the right call for key-cape
because of orchestration-heavy HTTP adapter code, fast iteration, and
clean domain boundaries.
flex-auth shares the relevant traits with key-cape: HTTP/gRPC adapters to
multiple PDPs and directory backends, latency-sensitive check paths, and
a need to ship a single static binary for local-development ergonomics.
## Decision
- **Language: Go.**
- **Module path: `github.com/netkingdom/flex-auth`** (placeholder; adjust
if the repo moves under a different GitHub org during publication).
- **Minimum Go version: matching key-cape at time of skeleton landing.**
- **Repo layout:**
```text
cmd/flex-auth/ CLI entrypoint
cmd/flex-authd/ service entrypoint (added when the service layer lands)
internal/registry/ resource / subject / relationship store
internal/policy/ policy package model, Rego evaluation, fixtures
internal/decision/ check, batch_check, list_allowed, explain, decision log
internal/audit/ compact decision-envelope persistence
internal/adapters/ pluggable PDP and directory adapters (later WPs)
pkg/api/ public types and OpenAPI schemas
schemas/ JSON Schema for manifests and envelopes
examples/ runnable example manifests, policies, fixtures
docs/adr/ this ADR series
```
- **Build, lint, test:** `Makefile` targets `build`, `test`, `lint`,
`tidy`, `sbom`. Linting via `golangci-lint`. Tests via the standard
`go test ./...` plus contract fixtures.
- **SBOM:** generate on each release tag and on `make sbom`; register via
the State Hub `ingest_sbom_tool` so `last_sbom_at` stops being `null`.
## Rationale
- Aligns with the only language decision in the NetKingdom ecosystem that
has already been validated in production (KeyCape v0.1).
- Single static binary makes the standalone-first mode trivial to ship
for local development across NetKingdom repos.
- Strong concurrency primitives suit batch-check and list-allowed paths.
- Excellent OPA tooling for Go (`open-policy-agent/opa/rego`) means the
Rego evaluator chosen in ADR-0002 has first-class library support.
- Topaz (the target alignment from ADR-0003) is Go-native — adapter work
in FLEX-WP-0004 stays in the same language.
## Consequences
- New flex-auth contributors need Go in their toolchain. Python is still
used elsewhere in the ecosystem; cross-repo work that hits the State
Hub or ops-bridge must accept the language switch.
- The Go decision is reversible while the repo is empty. Once `cmd/` and
`internal/` have been populated by FLEX-WP-0005 T01, reversal becomes
expensive — flag any reservations during the skeleton task, not later.
## Out of Scope
- Database choice (SQLite vs Postgres vs file-backed) is settled in
FLEX-WP-0002 T02 and recorded in a later ADR.
- Service framework (net/http vs Connect vs gRPC) is deferred to the
service-skeleton task in FLEX-WP-0002 T07.
## Related
- ADR-0002: Rego-in-Markdown policy format.
- ADR-0003: Topaz-aligned MVP.
- State Hub recorded decision: "Implementation language for KeyCape: Go"
(resolved 2026-03-25).

View File

@@ -0,0 +1,184 @@
# ADR-0002: Rego-in-Markdown Policy Package Format
Date: 2026-05-15
Status: Accepted
Deciders: Bernd, with assessment from Claude (Opus 4.7)
Supersedes: the "simple declarative rule format" placeholder in
FLEX-WP-0002 P2.3.
## Context
flex-auth's product surface is policy-as-code with reviewable, testable,
versioned packages and an `explain(decision_id)` API. Three candidate
package formats were on the table:
1. **Bespoke YAML rules now, port to Rego later.** Lowest immediate
complexity, but every adapter spike (Topaz, OPA, OPAL) would need a
translator, and the "thin-wrapper-around-Topaz" trap (see assessment)
gets worse the longer the bespoke layer exists.
2. **Rego from day one.** Standard, battle-tested policy language with
first-class Go library support (`open-policy-agent/opa/rego`). Reuse
for Topaz, OPA, OPAL, Permit, and most ReBAC-adjacent ecosystems.
3. **Cedar.** Strong typing and analyzability; smaller ecosystem; less
alignment with Topaz, which is the MVP backend target in ADR-0003.
The product also values **intent + validation co-location**. Markdown is
already the lingua franca of the NetKingdom knowledge plane (Markitect is
the first consumer), and reviewable policy benefits more from prose
context than YAML or pure `.rego` files can provide on their own.
## Decision
A **policy package is a Markdown document** with:
1. YAML frontmatter describing package metadata (id, version, namespace,
action vocabulary scope, owner, status, activation, fixtures source).
2. Prose sections (free-form) capturing **intent**: what the policy
protects, why, what the failure modes look like, what break-glass
semantics apply.
3. Fenced `rego` blocks containing the rules.
4. Fenced `rego` blocks tagged `test` containing OPA-compatible tests.
5. Fenced `yaml` blocks tagged `fixture` containing decision fixtures
(input/expected-decision pairs) that the loader can also evaluate
against the Rego module.
The loader extracts and concatenates the `rego` blocks into one OPA
module per package (using the `package` declaration in frontmatter), the
`test` blocks into a sibling test module, and the `fixture` blocks into
a fixtures table. Validation runs `opa parse`, `opa test`, and the
fixtures evaluator before a package can be marked `valid`.
### Minimal example
```markdown
---
id: markitect.documents.internal-read
version: 0.1.0
namespace: markitect:document
package: flexauth.markitect.documents
actions: [read, query, search]
owner: team:platform-architecture
status: draft
fixtures:
- examples/markitect/internal_doc_allow.yaml
- examples/markitect/internal_doc_deny.yaml
---
# Internal Document Read
This package gates `read`, `query`, and `search` on documents labelled
`internal`. A subject must be in the `reader` group of the document's
owning team, or hold the `steward` role on the document's repository.
## Failure modes
- Stale group membership: resolver freshness must be within 15 minutes
or the decision becomes `audit_only` with a `stale_directory` reason.
- Break-glass: an `emergency_principal` claim with a logged reason
produces `allow` with an obligation to record an export receipt.
## Rules
```rego
package flexauth.markitect.documents
import future.keywords.if
import future.keywords.in
default decision := {"effect": "deny", "reason": "no_matching_rule"}
decision := {"effect": "allow", "reason": "reader_group"} if {
input.action in {"read", "query", "search"}
input.resource.labels[_] == "internal"
some g in input.subject.groups
g == sprintf("reader:%s", [input.resource.owner_team])
}
decision := {"effect": "allow", "reason": "steward_role"} if {
input.action in {"read", "query", "search"}
"steward" in input.subject.roles_on[input.resource.repository]
}
```
## Tests
```rego test
package flexauth.markitect.documents_test
import data.flexauth.markitect.documents
test_reader_group_allows_read if {
documents.decision.effect == "allow" with input as {
"action": "read",
"subject": {"groups": ["reader:platform-architecture"]},
"resource": {"labels": ["internal"], "owner_team": "platform-architecture"}
}
}
```
## Fixtures
```yaml fixture
- name: reader group allow
input:
action: read
subject: {groups: ["reader:platform-architecture"]}
resource: {labels: ["internal"], owner_team: "platform-architecture"}
expect: {effect: allow, reason: reader_group}
- name: no group deny
input:
action: read
subject: {groups: []}
resource: {labels: ["internal"], owner_team: "platform-architecture"}
expect: {effect: deny, reason: no_matching_rule}
```
```
(Fence backticks above are zero-width-spaced for documentation. The real
loader expects normal triple backticks.)
## Rationale
- **Intent and validation co-located.** A reviewer reading the package
sees *why* alongside *what*. ADR text and policy code don't drift.
- **Standard evaluator.** OPA's `rego` library is mature, well-tested,
and the dominant Rego implementation. flex-auth gets correctness and
performance work for free.
- **Topaz alignment from day one.** ADR-0003 commits to shaping the
standalone core so the Topaz adapter (FLEX-WP-0004 T01) is a small
step. Rego is Topaz's native policy language.
- **Markitect synergy.** The first consumer is a Markdown knowledge
system. Policy packages and the documents they protect share one
authoring substrate; Markitect renders policy packages natively;
policy package versions sit comfortably in the same review process as
any other document.
- **Tooling reuse.** `opa fmt`, `opa test`, `opa eval`, and the broader
OPA tool ecosystem just work on extracted modules.
## Consequences
- The loader must implement Markdown extraction. Off-the-shelf Goldmark
+ a small block walker covers it; tests must lock the fence syntax.
- Authors must learn Rego. This is the universally accepted cost of
Rego adoption. The literate format lowers the cliff by surrounding
the language with intent prose.
- Some pure-data fixtures still live in `examples/` and are referenced
from the frontmatter — keeps large fixture files out of the policy
document body.
- An ADR amendment will be needed if Cedar or a typed policy language
enters the picture as a *peer* (not adapter). The literate Markdown
envelope can accommodate other fenced languages, but `rego` is the
baseline.
## Out of Scope
- Whether activation, rollout, and rollback semantics live in
frontmatter or in a sibling activation manifest — settled in
FLEX-WP-0002 P2.3.
- The exact extraction library — implementation detail of P2.3.
## Related
- ADR-0001: Go implementation (provides the OPA library path).
- ADR-0003: Topaz-aligned MVP (provides the backend rationale).
- FLEX-WP-0002 P2.3 (policy package loader and validator).

View File

@@ -0,0 +1,87 @@
# ADR-0003: Topaz-Aligned MVP
Date: 2026-05-15
Status: Accepted
Deciders: Bernd, with assessment from Claude (Opus 4.7)
Supersedes: implicit "standalone first, evaluate Topaz later" sequencing
in the original FLEX-WP-0004 P4.1.
## Context
The pre-implementation assessment names a primary threat: the
"thin-wrapper-around-Topaz" trap. Topaz already combines OPA/Rego
evaluation with a local directory inspired by Zanzibar (users, groups,
resources, relations) and a local deployment story. If flex-auth's
standalone core reimplements 60% of Topaz badly and then adapts to Topaz
anyway, the abstraction earns no product value.
ADR-0002 commits to Rego from day one. That removes the largest source
of friction between the standalone evaluator and Topaz, because the
policy language is identical.
The remaining question is sequencing. The original workplans placed the
Topaz evaluation at FLEX-WP-0004 P4.1, *after* the standalone core
shapes its registry, check API, and decision log. That sequencing is
inverted: by the time the spike runs, the core has already made shape
decisions that the Topaz adapter will then have to translate.
## Decision
- **flex-auth's actual product surface is registry + audit + explain +
multi-consumer + literate policy packages.** The PDP is an
implementation detail behind a stable contract.
- **Shape the standalone core to be Rego/Topaz-aligned from day one.**
Concretely:
- The standalone evaluator embeds the OPA Rego library and evaluates
the same Rego modules a Topaz deployment would.
- The registry's resource/subject/relation model uses vocabulary that
maps trivially onto Topaz directory objects and relations.
- The decision envelope carries the same provenance fields whether
evaluation is local or delegated.
- **The Topaz evaluation spike moves earlier.** It runs in
FLEX-WP-0005 (Foundations), not FLEX-WP-0004, so its findings inform
the registry, policy loader, and check API before they are written.
- **FLEX-WP-0004 keeps Topaz only as one delegated-mode adapter**
alongside OpenFGA, SpiceDB, OPA-as-remote, Cedar, and Keycloak
Authorization Services. Topaz is no longer the sole evaluation
question of FLEX-WP-0004.
## Rationale
- Aligning vocabulary up front is cheap; aligning it retroactively is
not.
- The standalone mode remains genuinely useful (zero external services,
single binary, fixtures-first development) while staying Topaz-shaped
internally.
- Topaz remains an opinionated, single-vendor product. The product
surface (registry, audit, explain, multi-consumer) is where flex-auth
competes; the evaluation engine is where flex-auth integrates.
## Consequences
- The Topaz spike (FLEX-WP-0005 T04) becomes a hard prerequisite for
FLEX-WP-0002 P2.1 schemas. Its output is a mapping document showing
how flex-auth resources/relations correspond to Topaz directory
objects/relations, plus a recommendation on whether to embed Topaz's
directory contracts directly or restate them.
- FLEX-WP-0004 is slimmed (Topaz evaluation moves out, leaving the
Topaz adapter implementation in T01 and the relationship/rule/
Keycloak/directory adapter work in T02T06).
- Keycloak Authorization Services remains an adapter path for
Keycloak-centric deployments — flex-auth is the canonical PAP/PDP,
Keycloak AuthZ is one delegated PDP option.
## Out of Scope
- The exact wire protocol of the Topaz adapter (gRPC vs HTTP vs library
embed) is decided in FLEX-WP-0004 T01.
- The directory-store implementation in standalone mode (SQLite vs
file-backed vs in-memory) is decided in FLEX-WP-0002 T02.
## Related
- ADR-0001: Go implementation (matches Topaz's language).
- ADR-0002: Rego-in-Markdown policy format (matches Topaz's policy
language).
- FLEX-WP-0005 T04: Topaz mapping spike.
- FLEX-WP-0004: Delegated PDP and directory adapters.