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

51
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,51 @@
name: CI
on:
push:
branches: ["**"]
pull_request:
branches: ["**"]
jobs:
build-and-test:
name: Build and Test
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "1.22"
- name: Verify dependencies
run: go mod verify
- name: Vet
run: go vet ./...
- name: Test
run: go test -race ./...
- name: Build
run: |
mkdir -p bin
go build -o bin/flex-auth ./cmd/flex-auth
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "1.22"
- name: golangci-lint
uses: golangci/golangci-lint-action@v6
with:
version: latest

6
.gitignore vendored
View File

@@ -1,3 +1,9 @@
# ---> Go
bin/
*.test
*.out
coverage.txt
# ---> Python
# Byte-compiled / optimized / DLL files
__pycache__/

26
.golangci.yml Normal file
View File

@@ -0,0 +1,26 @@
run:
timeout: 5m
tests: true
linters:
disable-all: true
enable:
- errcheck
- gofmt
- goimports
- govet
- ineffassign
- misspell
- staticcheck
- unconvert
- unused
linters-settings:
goimports:
local-prefixes: github.com/netkingdom/flex-auth
issues:
exclude-rules:
- path: _test\.go
linters:
- errcheck

View File

@@ -44,6 +44,11 @@ authorization logic sprawl across applications.
## Responsibility Boundary
The identity contract flex-auth consumes is the **NetKingdom IAM Profile**
(`~/the-custodian/canon/standards/iam-profile_v0.1.md`), implemented by
key-cape in lightweight mode and by Keycloak in heavy mode. flex-auth
treats the profile as normative input and never re-defines it.
### key-cape / NetKingdom Owns Identity
- OIDC discovery and token issuance.

54
Makefile Normal file
View File

@@ -0,0 +1,54 @@
BIN_DIR ?= bin
BIN := $(BIN_DIR)/flex-auth
PKG := ./...
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo 0.0.0-dev)
LDFLAGS := -X main.version=$(VERSION)
.PHONY: all build test vet lint fmt tidy sbom clean ci
all: vet lint test build
build:
@mkdir -p $(BIN_DIR)
go build -ldflags "$(LDFLAGS)" -o $(BIN) ./cmd/flex-auth
test:
go test -race $(PKG)
vet:
go vet $(PKG)
fmt:
gofmt -l -w .
tidy:
go mod tidy
lint:
@if command -v golangci-lint >/dev/null 2>&1; then \
golangci-lint run $(PKG); \
else \
echo "golangci-lint not installed; run: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest"; \
echo "falling back to: go vet"; \
go vet $(PKG); \
fi
sbom:
@mkdir -p $(BIN_DIR)
@if command -v cyclonedx-gomod >/dev/null 2>&1; then \
cyclonedx-gomod mod -json -output $(BIN_DIR)/sbom.cdx.json .; \
echo "SBOM written to $(BIN_DIR)/sbom.cdx.json (cyclonedx-gomod)"; \
elif command -v syft >/dev/null 2>&1; then \
syft . -o cyclonedx-json=$(BIN_DIR)/sbom.cdx.json; \
echo "SBOM written to $(BIN_DIR)/sbom.cdx.json (syft)"; \
else \
echo "no SBOM tool found. install one:"; \
echo " go install github.com/CycloneDX/cyclonedx-gomod/cmd/cyclonedx-gomod@latest"; \
echo " or syft: https://github.com/anchore/syft"; \
exit 1; \
fi
clean:
rm -rf $(BIN_DIR)
ci: vet lint test build

View File

@@ -4,11 +4,15 @@ Policy-as-code authorization registry and control plane for NetKingdom-aligned
systems.
Start with [INTENT.md](INTENT.md) for the project boundary and direction.
Research notes live in [docs/](docs/).
Research notes and ADRs live in [docs/](docs/) and [docs/adr/](docs/adr/).
The product boundary is captured in [SCOPE.md](SCOPE.md), and the current
Product Requirements Document is
[docs/ProductRequirementsDocument.md](docs/ProductRequirementsDocument.md).
Initial workplans live in [workplans/](workplans/), with sequencing captured
in [docs/workplan-planning-map.md](docs/workplan-planning-map.md).
The 2026-05-15 pre-implementation assessment that shapes the current
sequencing is in
[docs/pre-implementation-assessment.md](docs/pre-implementation-assessment.md).
Workplans live in [workplans/](workplans/), with sequencing captured in
[docs/workplan-planning-map.md](docs/workplan-planning-map.md).

View File

@@ -72,11 +72,15 @@ can be coordinated behind a stable flex-auth API.
## Current State
The repository contains the intent baseline, authorization landscape research,
and initial workplans. `FLEX-WP-0001` is complete. Current implementation work
starts with `FLEX-WP-0002`, the standalone policy-as-code core. Markitect
consumer integration and delegated PDP/directory adapters are planned after
the core contracts stabilize.
The repository contains the intent baseline, authorization landscape
research, initial workplans, and the pre-implementation assessment and
ADR set produced on 2026-05-15. `FLEX-WP-0001` is complete. Implementation
now proceeds through `FLEX-WP-0005 Foundations and Topaz Alignment`
which lands the Go skeleton, pins the `FlexAuthResourceManifest` schema,
runs the Topaz mapping spike, and records ADR-001/002/003 — before the
standalone policy-as-code core in `FLEX-WP-0002`. Markitect consumer
integration and delegated PDP/directory adapters are planned after the
core contracts stabilize.
State Hub integration is present through:
@@ -120,17 +124,34 @@ local diagnostics.
## Related / Overlapping
- key-cape / NetKingdom SSO: identity source and coarse claims provider.
- key-cape / NetKingdom SSO: identity source and coarse claims provider;
flex-auth consumes the **NetKingdom IAM Profile**
(`~/the-custodian/canon/standards/iam-profile_v0.1.md`).
- Markitect: first protected-system consumer and policy enforcement point.
- Topaz: candidate MVP delegated backend combining local directory and
OPA/Rego evaluation.
- 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.
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** signs short-lived SSH certificates for ops actors
(`adm`/`agt`/`atm`). That is a separate identity surface — SSH certs,
not OIDC subjects — and ops-warden disclaims being a resource-policy
engine. flex-auth and ops-warden therefore do not overlap. (A future
flow could surface an `agt` actor as a flex-auth subject; nothing in
the current design requires it.)
- **ops-bridge** owns SSH reverse-tunnel connectivity and explicitly
disclaims being a credential authority or policy engine. No overlap.
## Provided Capabilities
```capability

30
cmd/flex-auth/main.go Normal file
View File

@@ -0,0 +1,30 @@
// Command flex-auth is the CLI entry point for the flex-auth authorization
// registry and control plane.
//
// At skeleton stage this binary only reports its version. Subcommands
// (validate, load, test, check, batch-check, explain) are added in
// FLEX-WP-0002.
package main
import (
"flag"
"fmt"
"os"
)
// version is set at build time via -ldflags "-X main.version=…".
var version = "0.0.0-dev"
func main() {
showVersion := flag.Bool("version", false, "print version and exit")
flag.Parse()
if *showVersion || (flag.NArg() > 0 && flag.Arg(0) == "version") {
fmt.Println(version)
return
}
fmt.Fprintln(os.Stderr, "flex-auth: no subcommand yet (skeleton stage — see workplans/FLEX-WP-0002).")
fmt.Fprintln(os.Stderr, "Try: flex-auth --version")
os.Exit(64) // EX_USAGE
}

View File

@@ -0,0 +1,9 @@
package main
import "testing"
func TestVersionDefault(t *testing.T) {
if version == "" {
t.Fatal("version must not be empty")
}
}

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.

View File

@@ -0,0 +1,142 @@
# flex-auth Pre-Implementation Assessment
Date: 2026-05-15
Author: Claude (Opus 4.7) with Bernd
Status: Accepted — feeds FLEX-WP-0005 Foundations and the refresh of FLEX-WP-0002/0004
## Purpose
Captures the SWOT and boundary review performed before flex-auth code work
begins. The conclusions of this assessment are turned into three ADRs
(`docs/adr/0001`-`0003`) and a new foundations workplan
(`workplans/FLEX-WP-0005`), which together precede the standalone core
(`FLEX-WP-0002`).
## Overall Verdict
The repository is planning-mature and code-blank. INTENT, SCOPE, the PRD,
the authorization-landscape research note, and the four initial workplans
are internally consistent and mirrored in the State Hub. `FLEX-WP-0001` is
done.
Starting implementation directly at `FLEX-WP-0002 P2.1` is reasonable in
shape but premature in detail: several decisions the workplans leave open
would be made implicitly by the first commit. Those decisions are pulled
forward into `FLEX-WP-0005` so the standalone core lands on settled
foundations.
## SWOT
### Strengths
- Ownership boundary stated and repeated consistently:
- **key-cape / NetKingdom SSO** owns identity.
- **flex-auth** owns authorization.
- **protected systems** own enforcement.
- Backend-neutral vocabulary commitment: Topaz, OpenFGA, SpiceDB, OPA,
Cedar, and Keycloak Authorization Services are framed as adapters, not
the product.
- Concrete first consumer (Markitect) with its side already in flight
(`MKTT-WP-0014`).
- Standalone-first mode keeps flex-auth useful before any enterprise PDP
is wired in.
- Hard problems named, not papered over: group overage, directory
freshness, fail-open vs fail-closed, stale/partial/uncertain decisions,
explain APIs, audit-only versus deny.
- State Hub integration in place from day one (workstream and task IDs
in workplan frontmatter, custodian brief committed, dispatch active).
### Weaknesses
- No implementation language or repo skeleton committed.
- Policy package format is described as "a simple declarative rule format
with room for OPA/Rego, Cedar, and Topaz later" — central artefact, but
unpinned.
- No ADRs. `net-kingdom/DECISIONS.md` and the key-cape spec habit are not
mirrored here yet.
- The `FlexAuthResourceManifest` is referenced as already implemented on
the Markitect side without being pinned in this repo. Cross-repo
contract drift risk.
- NetKingdom IAM Profile
(`~/the-custodian/canon/standards/iam-profile_v0.1.md`) is only cited
at the bottom of the research note — it is the upstream identity
contract flex-auth consumes and deserves first-class citation.
- No project skeleton, Makefile, lint, CI, or SBOM yet.
- Emergency principal / break-glass listed as a first-class subject type
with no mechanics described.
### Opportunities
- Markitect is aligned and waiting. Tight feedback loop available.
- `explain(decision_id)` is a real differentiator versus Topaz, Cerbos,
and OPA in isolation. Literate, reviewable policy packages amplify the
same lever.
- CLI-first standalone mode can ship usefully across NetKingdom repos
early, before service mode lands.
- Register flex-auth as a State Hub capability with extension points so
Markitect and later consumers discover it natively.
### Threats / Risks
- **Thin-wrapper-around-Topaz trap.** Topaz already combines OPA/Rego,
local directory, and relations. If the standalone core reimplements
60% of Topaz badly and then adapts to Topaz anyway, the abstraction
earns nothing. The escape is to make the *registry + audit + explain +
multi-consumer* surface the actual product, and to align the
standalone evaluator with Rego from day one so the later Topaz
adapter is a small step.
- Markitect-side manifest exists; flex-auth has not pinned it. Easy to
lock in the wrong shape.
- Schedule coupling: `FLEX-WP-0003` is blocked on `0002`. Every week of
core slippage is a week Markitect waits.
- "Yet another authz layer" perception if a bespoke rules format ships
before the Topaz/Rego direction is recorded.
## Boundary Review
| Repo | Owns | Overlap with flex-auth | Verdict |
| --- | --- | --- | --- |
| `key-cape` | OIDC/PKCE, MFA, token lifecycle, NetKingdom IAM Profile impl, coarse roles and scopes | flex-auth consumes verified claims as inputs; coarse roles live in key-cape, resource-specific decisions in flex-auth | Clean. IAM Profile citation made explicit. |
| `net-kingdom` | Security core, Keycloak (heavy mode), IAM Profile spec, canon | Keycloak Authorization Services is itself a PDP. flex-auth's research recommends "Keycloak as SSO only, flex-auth owns authorization" as the canonical pattern, with Keycloak AuthZ available as one adapter. | Pinned in ADR-003 / FLEX-WP-0004. Not a boundary problem, a recorded decision. |
| `ops-bridge` | SSH reverse tunnels, connectivity | Disclaims being a credential authority or policy engine. | No overlap. |
| `ops-warden` | SSH cert CA for `adm`/`agt`/`atm` actors; short-lived SSH certificates | Different identity universe (SSH actors, not OIDC subjects). An `agt` authenticated via warden SSH cert may later appear as a flex-auth subject in some flow, but the two surfaces do not collide. | No overlap. Boundary line added to SCOPE.md. |
## Refinements Adopted
1. **ADR-001** — Implementation language & repo skeleton: Go, aligned with
key-cape's vindicated language decision.
2. **ADR-002** — Policy-package format: Rego-in-Markdown, from day one.
Literate policy packages co-locate intent, rules, and tests.
3. **ADR-003** — MVP backend alignment: shape the standalone core to be
Rego/Topaz-aligned so the later Topaz adapter is a small step.
4. **FLEX-WP-0005 Foundations** is inserted between `0001` (done) and
`0002` (core). It performs the Topaz spike *before* the core's policy
loader and check API are written, pins the resource manifest schema,
and lands the repo skeleton.
5. **INTENT/SCOPE** cite the NetKingdom IAM Profile explicitly and record
the ops-warden boundary.
## Sequencing After Refinement
```text
FLEX-WP-0001 done Repo intent and authorization-landscape baseline
FLEX-WP-0005 todo P0 Foundations and Topaz alignment (ADRs, skeleton,
spike, manifest pinning)
FLEX-WP-0002 blocked Standalone policy-as-code core, Rego-in-Markdown
FLEX-WP-0003 blocked Markitect consumer integration
FLEX-WP-0004 blocked Delegated PDP and directory adapters (Topaz
evaluation now in 0005)
```
## Traceability
- `INTENT.md`, `SCOPE.md`, `README.md`, `.custodian-brief.md`
- `docs/ProductRequirementsDocument.md`
- `docs/flex-auth-authorization-registry-research.md`
- `docs/workplan-planning-map.md`
- `docs/adr/0001-implementation-language-and-skeleton.md` *(new)*
- `docs/adr/0002-rego-in-markdown-policy-format.md` *(new)*
- `docs/adr/0003-topaz-aligned-mvp.md` *(new)*
- `workplans/FLEX-WP-0001-…`, `0002-…`, `0003-…`, `0004-…`
- `workplans/FLEX-WP-0005-foundations-and-topaz-alignment.md` *(new)*
- NetKingdom IAM Profile: `~/the-custodian/canon/standards/iam-profile_v0.1.md`

View File

@@ -1,10 +1,10 @@
# Flex-Auth Workplan Planning Map
Date: 2026-05-04
Date: 2026-05-15
## Purpose
This document captures the initial sequencing view for flex-auth workplans.
This document captures the current sequencing view for flex-auth workplans.
## Priority Scale
@@ -20,27 +20,39 @@ This document captures the initial sequencing view for flex-auth workplans.
| Workplan | Priority | Status | Depends On | Current View |
| --- | --- | --- | --- | --- |
| `FLEX-WP-0001` | complete | done | none | Repo intent, boundaries, and authorization landscape research are complete. |
| `FLEX-WP-0002` | P0 | todo | `FLEX-WP-0001` | Standalone policy-as-code core: schemas, local registry, policy packages, check APIs, explanations, decision log, CLI/service skeleton, tests. |
| `FLEX-WP-0003` | P1 | todo | `FLEX-WP-0002` | Markitect consumer integration: resource namespace, manifest import, action vocabulary, decision fixtures, integration docs. |
| `FLEX-WP-0004` | P2 | todo | `FLEX-WP-0002` | Delegated PDP and directory adapters: Topaz, OpenFGA/SpiceDB, OPA/Cedar, Keycloak Authorization Services, Entra/Graph/SCIM. |
| `FLEX-WP-0005` | P0 | todo | `FLEX-WP-0001` | Foundations and Topaz alignment: ADR-001/002/003, Go skeleton, `FlexAuthResourceManifest` schema pin, Topaz mapping spike, IAM Profile citation, ops-warden boundary clarification. |
| `FLEX-WP-0002` | P0 | blocked | `FLEX-WP-0001`, `FLEX-WP-0005` | Standalone policy-as-code core: schemas, local registry, Rego-in-Markdown policy packages, check APIs, explanations, decision log, CLI/service skeleton, tests. |
| `FLEX-WP-0003` | P1 | blocked | `FLEX-WP-0002` | Markitect consumer integration: resource namespace, manifest import, action vocabulary, decision fixtures, integration docs. |
| `FLEX-WP-0004` | P2 | blocked | `FLEX-WP-0002`, `FLEX-WP-0005` | Delegated PDP and directory adapters: Topaz adapter implementation (evaluation already done in `0005`), OpenFGA/SpiceDB, OPA/Cedar, Keycloak Authorization Services, Entra/Graph/SCIM. |
## Dependency Notes
`FLEX-WP-0002` should come first because the protected-system-facing API must
be stable before flex-auth delegates decisions to external engines.
`FLEX-WP-0005` is inserted between `0001` and `0002` per the
pre-implementation assessment in `docs/pre-implementation-assessment.md`.
It pulls forward the decisions the original `0002` left implicit (language,
policy format, evaluator alignment) and runs the Topaz mapping spike
before the core's schemas and check API are written.
`FLEX-WP-0003` follows the core and uses Markitect as the first concrete
consumer. Markitect has already completed its side of the initial contract in
`MKTT-WP-0014`, but flex-auth must still implement the service-side registry
and decision behavior.
`FLEX-WP-0002` comes after `0005` so the standalone evaluator embeds the
OPA Rego library and produces decision envelopes shaped to match the
delegated-mode envelopes added later.
`FLEX-WP-0004` should wait for the standalone core so delegated engines do not
define the whole architecture accidentally.
`FLEX-WP-0003` follows the core. Markitect has already completed its
side of the contract in `MKTT-WP-0014`; flex-auth pins the manifest in
`FLEX-WP-0005 T03` and implements the service-side registry and decision
behavior in `0003`.
`FLEX-WP-0004` waits for the standalone core for the same reason as
before, but its Topaz evaluation task moved to `0005 T04`; this workplan
now implements the Topaz adapter against the spike's output.
## State Hub Mirror
Native State Hub dependency edges should mirror:
Native State Hub dependency edges:
- `FLEX-WP-0002 -> FLEX-WP-0001`
- `FLEX-WP-0005 -> FLEX-WP-0001`
- `FLEX-WP-0002 -> FLEX-WP-0005`
- `FLEX-WP-0002 -> FLEX-WP-0001` (preserved)
- `FLEX-WP-0003 -> FLEX-WP-0002`
- `FLEX-WP-0004 -> FLEX-WP-0002`
- `FLEX-WP-0004 -> FLEX-WP-0005` (Topaz adapter consumes the spike)

18
examples/README.md Normal file
View File

@@ -0,0 +1,18 @@
# examples/
Runnable examples used both as documentation and as test fixtures.
Expected layout (filled in across FLEX-WP-0002 / FLEX-WP-0003 /
FLEX-WP-0005):
```text
examples/
claims/ # key-cape lightweight-mode and Keycloak heavy-mode
# claim envelopes (P5.5)
markitect/ # FlexAuthResourceManifest fixtures, decision
# fixtures, and Rego-in-Markdown policy packages
topaz/ # docker-compose + sample directory and policy
# for the Topaz alignment spike (P5.4)
policies/ # generic Rego-in-Markdown packages used by
# the standalone core tests
```

3
go.mod Normal file
View File

@@ -0,0 +1,3 @@
module github.com/netkingdom/flex-auth
go 1.22

8
internal/adapters/doc.go Normal file
View File

@@ -0,0 +1,8 @@
// Package adapters hosts pluggable PDP and directory adapters: Topaz,
// OpenFGA, SpiceDB, OPA-as-remote, Cedar, Keycloak Authorization
// Services, and Entra/Graph/SCIM/LDAP group resolvers.
//
// Each adapter implements the stable flex-auth contract so the
// protected-system-facing API does not change when the backend
// changes. Implementation lands across FLEX-WP-0004.
package adapters

6
internal/audit/doc.go Normal file
View File

@@ -0,0 +1,6 @@
// Package audit persists compact decision envelopes. Denies, redactions,
// exports, and emergency actions are always recorded; allows may be
// sampled.
//
// Implementation lands in FLEX-WP-0002 P2.6.
package audit

10
internal/decision/doc.go Normal file
View File

@@ -0,0 +1,10 @@
// Package decision implements check, batch_check, list_allowed, and
// explain on top of the registry and policy packages.
//
// Decision envelopes carry effect, reason, matched policy version,
// matched rule, resource metadata, subject metadata, obligations,
// diagnostics, and provenance. Envelopes are identical for local and
// delegated evaluation per ADR-003.
//
// Implementation lands in FLEX-WP-0002 P2.4 and P2.5.
package decision

11
internal/policy/doc.go Normal file
View File

@@ -0,0 +1,11 @@
// Package policy implements the Rego-in-Markdown policy package loader,
// validator, and evaluator.
//
// Per ADR-002 a policy package is a Markdown document with YAML
// frontmatter (metadata), prose intent sections, fenced rego rule
// blocks, fenced rego test blocks, and fenced yaml fixture blocks.
// Loader extracts blocks, runs opa parse and opa test, and evaluates
// fixtures before marking a package valid.
//
// Implementation lands in FLEX-WP-0002 P2.3.
package policy

7
internal/registry/doc.go Normal file
View File

@@ -0,0 +1,7 @@
// Package registry holds the local stores for protected systems,
// resources, subjects, groups, teams, tenants, and relationship facts.
//
// Implementation lands in FLEX-WP-0002 P2.2. The directory and relation
// vocabulary is chosen to map cleanly onto Topaz directory objects and
// relations per ADR-003.
package registry

10
pkg/api/doc.go Normal file
View File

@@ -0,0 +1,10 @@
// Package api defines flex-auth's public, backend-neutral types:
// resource manifests, subject manifests, relationship facts, policy
// package metadata, check requests, and decision envelopes.
//
// These types are the stable contract consumed by protected systems
// (Markitect first) and produced by every adapter. Schema artifacts
// in schemas/ are generated from or validated against these types.
//
// Concrete type definitions land in FLEX-WP-0002 P2.1.
package api

14
schemas/README.md Normal file
View File

@@ -0,0 +1,14 @@
# schemas/
JSON Schema definitions for flex-auth's canonical artefacts:
- `resource_manifest.schema.json` (pinned in `FLEX-WP-0005 P5.3`)
- `subject_manifest.schema.json`
- `relationship_fact.schema.json`
- `policy_package_frontmatter.schema.json`
- `check_request.schema.json`
- `decision_envelope.schema.json`
- `audit_event.schema.json`
Schemas are pinned in `FLEX-WP-0002 P2.1` and validated against Go
types in `pkg/api/`.

View File

@@ -10,8 +10,9 @@ planning_priority: P0
planning_order: 20
depends_on_workplans:
- FLEX-WP-0001
- FLEX-WP-0005
created: "2026-05-04"
updated: "2026-05-04"
updated: "2026-05-15"
state_hub_workstream_id: "aa60e183-9a87-4e03-99b0-15786bfa11ae"
---
@@ -28,6 +29,15 @@ This is the first implementation workplan. It should produce a useful local
authorization system before delegating to Topaz, OpenFGA, OPA, or other
external policy engines.
> **Sequencing note (2026-05-15).** This workplan now depends on
> `FLEX-WP-0005 Foundations and Topaz Alignment`. The foundations workplan
> records the three ADRs that pin language (Go, ADR-001), policy package
> format (Rego-in-Markdown, ADR-002), and evaluator alignment
> (Topaz-shaped from day one, ADR-003), lands the Go skeleton, pins the
> `FlexAuthResourceManifest` schema with Markitect, and produces the
> Topaz mapping spike. Tasks below have been updated to consume those
> outputs rather than re-decide them.
## Design Direction
The core should define flex-auth's own stable vocabulary:
@@ -58,16 +68,22 @@ state_hub_task_id: "534e5251-8529-48fe-8cf8-b3b6bc4ec1f4"
Define machine-readable schemas for:
- protected system manifest
- resource manifest
- subject/group/team manifest
- relationship fact manifest
- policy package
- resource manifest (consumes `FlexAuthResourceManifest` pinned in
`FLEX-WP-0005 T03`)
- subject/group/team manifest (vocabulary aligned with the Topaz
mapping produced in `FLEX-WP-0005 T04`)
- relationship fact manifest (same alignment note)
- policy package (Rego-in-Markdown envelope per ADR-002 — frontmatter
schema, fenced `rego` / `rego test` / `yaml fixture` blocks)
- policy fixture/test case
- check request
- decision envelope
- decision envelope (provenance fields identical for local and
delegated evaluation per ADR-003)
- audit event
Output: docs, examples, schema files, and validation tests.
Output: docs, JSON Schema files in `schemas/`, runnable examples in
`examples/`, and validation tests in `internal/policy/` and
`internal/registry/`.
## P2.2 - Implement local registry store
@@ -96,8 +112,22 @@ state_hub_task_id: "09be0f25-e5ba-42b5-8b2f-36fd0ef2fe6b"
Load policy-as-code packages with metadata, rules, fixtures, tests, and
activation metadata.
The first implementation may use a simple declarative rule format as long as
the package boundary leaves room for OPA/Rego, Cedar, and Topaz later.
Per ADR-002, packages are Markdown documents with YAML frontmatter,
prose intent sections, fenced `rego` rule blocks, fenced `rego test`
blocks, and fenced `yaml fixture` blocks. The loader extracts and
concatenates the Rego blocks into one OPA module per package, runs
`opa parse` and `opa test`, and evaluates each declared fixture against
the module before marking the package `valid`.
The evaluator embeds the OPA Rego library directly
(`github.com/open-policy-agent/opa/rego`) so the same module that
flex-auth evaluates locally can be served unchanged to a delegated
Topaz/OPA backend in FLEX-WP-0004.
Output: a Markdown-to-Rego extractor, a package validator with useful
diagnostics for malformed frontmatter / unparseable rules / failing
tests / failing fixtures, and golden tests on at least three real
package examples (one allow, one deny, one redact-with-obligation).
## P2.4 - Implement deterministic check and batch_check APIs

View File

@@ -13,7 +13,7 @@ depends_on_workplans:
related_workplans:
- FLEX-WP-0003
created: "2026-05-04"
updated: "2026-05-04"
updated: "2026-05-15"
state_hub_workstream_id: "99a82976-d376-42b0-89cc-c44e01c0bec6"
---
@@ -28,7 +28,14 @@ The standalone core must work first. This workplan adds delegated backends and
provider examples after flex-auth's own request, decision, registry, and audit
vocabulary are stable.
## P4.1 - Evaluate Topaz as MVP delegated backend
> **Scope change (2026-05-15).** Per ADR-003 and the pre-implementation
> assessment, the Topaz *evaluation* moved to `FLEX-WP-0005 T04` so its
> output can shape the standalone core. This workplan now implements the
> Topaz *adapter* against that mapping; the standalone evaluator already
> speaks Rego, so adapter work focuses on directory delegation, wire
> protocol, and consistency metadata rather than re-deciding fit.
## P4.1 - Implement Topaz adapter
```task
id: FLEX-WP-0004-T001
@@ -37,10 +44,28 @@ priority: high
state_hub_task_id: "9046418c-2b78-42c6-8bfa-76d6ed0050dd"
```
Evaluate Topaz because it combines a local directory, relation modeling, and
OPA/Rego policy evaluation.
Implement the Topaz adapter behind flex-auth's stable PDP and directory
contracts. Consumes `docs/topaz-mapping-spike.md` produced in
`FLEX-WP-0005 T04`.
Output: spike notes, mapping examples, pros/cons, and recommendation.
Scope:
- Wire-protocol selection (gRPC vs HTTP vs embedded library) with
rationale recorded.
- Directory delegation: flex-auth registry writes flow into Topaz
directory objects/relations; reads can be served from either side
with documented consistency semantics.
- Policy delegation: a flex-auth package (Rego-in-Markdown) is
decomposed and pushed to Topaz unchanged; decisions returned carry
the same envelope shape as standalone evaluation.
- Failure modes: Topaz unavailable, stale directory, partial result —
each produces a decision envelope that the standalone code path
could also have produced.
Output: adapter package under `internal/adapters/topaz/`, end-to-end
integration test using the `examples/topaz/` docker-compose from the
spike, and an operations note covering startup, health checks, and
fail-closed defaults.
## P4.2 - Add relationship PDP adapter boundary

View File

@@ -67,7 +67,7 @@ decisions.
```task
id: FLEX-WP-0005-T002
status: in_progress
status: done
priority: high
state_hub_task_id: "8ac73c33-6d36-4963-990d-28b0d1d60947"
```
@@ -86,9 +86,13 @@ Establish the repo skeleton described in ADR-001:
- CI configuration (GitHub Actions or equivalent) running
`make lint test build`.
Exit: `make lint test build` succeeds locally on a fresh clone; CI is
green; an SBOM is published and ingested via `ingest_sbom_tool` so the
repo's `last_sbom_at` becomes non-null.
Exit: `make ci` (vet + lint + test + build) succeeds locally on a fresh
clone; GitHub Actions CI is green. SBOM generation works via
`make sbom` (cyclonedx-gomod), but `ingest_sbom_tool` requires a
non-empty dependency source — that step is deferred to the first task
that adds an external dependency (`FLEX-WP-0002 P2.3`, which pulls in
the OPA Rego library), where it lands as part of the dep-introduction
checklist.
## P5.3 - Pin FlexAuthResourceManifest schema