generated from coulomb/repo-seed
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:
83
docs/adr/0001-implementation-language-and-skeleton.md
Normal file
83
docs/adr/0001-implementation-language-and-skeleton.md
Normal 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).
|
||||
184
docs/adr/0002-rego-in-markdown-policy-format.md
Normal file
184
docs/adr/0002-rego-in-markdown-policy-format.md
Normal 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).
|
||||
87
docs/adr/0003-topaz-aligned-mvp.md
Normal file
87
docs/adr/0003-topaz-aligned-mvp.md
Normal 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 T02–T06).
|
||||
- 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.
|
||||
Reference in New Issue
Block a user