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:
51
.github/workflows/ci.yml
vendored
Normal file
51
.github/workflows/ci.yml
vendored
Normal 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
6
.gitignore
vendored
@@ -1,3 +1,9 @@
|
|||||||
|
# ---> Go
|
||||||
|
bin/
|
||||||
|
*.test
|
||||||
|
*.out
|
||||||
|
coverage.txt
|
||||||
|
|
||||||
# ---> Python
|
# ---> Python
|
||||||
# Byte-compiled / optimized / DLL files
|
# Byte-compiled / optimized / DLL files
|
||||||
__pycache__/
|
__pycache__/
|
||||||
|
|||||||
26
.golangci.yml
Normal file
26
.golangci.yml
Normal 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
|
||||||
@@ -44,6 +44,11 @@ authorization logic sprawl across applications.
|
|||||||
|
|
||||||
## Responsibility Boundary
|
## 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
|
### key-cape / NetKingdom Owns Identity
|
||||||
|
|
||||||
- OIDC discovery and token issuance.
|
- OIDC discovery and token issuance.
|
||||||
|
|||||||
54
Makefile
Normal file
54
Makefile
Normal 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
|
||||||
10
README.md
10
README.md
@@ -4,11 +4,15 @@ Policy-as-code authorization registry and control plane for NetKingdom-aligned
|
|||||||
systems.
|
systems.
|
||||||
|
|
||||||
Start with [INTENT.md](INTENT.md) for the project boundary and direction.
|
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
|
The product boundary is captured in [SCOPE.md](SCOPE.md), and the current
|
||||||
Product Requirements Document is
|
Product Requirements Document is
|
||||||
[docs/ProductRequirementsDocument.md](docs/ProductRequirementsDocument.md).
|
[docs/ProductRequirementsDocument.md](docs/ProductRequirementsDocument.md).
|
||||||
|
|
||||||
Initial workplans live in [workplans/](workplans/), with sequencing captured
|
The 2026-05-15 pre-implementation assessment that shapes the current
|
||||||
in [docs/workplan-planning-map.md](docs/workplan-planning-map.md).
|
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).
|
||||||
|
|||||||
39
SCOPE.md
39
SCOPE.md
@@ -72,11 +72,15 @@ can be coordinated behind a stable flex-auth API.
|
|||||||
|
|
||||||
## Current State
|
## Current State
|
||||||
|
|
||||||
The repository contains the intent baseline, authorization landscape research,
|
The repository contains the intent baseline, authorization landscape
|
||||||
and initial workplans. `FLEX-WP-0001` is complete. Current implementation work
|
research, initial workplans, and the pre-implementation assessment and
|
||||||
starts with `FLEX-WP-0002`, the standalone policy-as-code core. Markitect
|
ADR set produced on 2026-05-15. `FLEX-WP-0001` is complete. Implementation
|
||||||
consumer integration and delegated PDP/directory adapters are planned after
|
now proceeds through `FLEX-WP-0005 Foundations and Topaz Alignment` —
|
||||||
the core contracts stabilize.
|
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:
|
State Hub integration is present through:
|
||||||
|
|
||||||
@@ -120,17 +124,34 @@ local diagnostics.
|
|||||||
|
|
||||||
## Related / Overlapping
|
## 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.
|
- Markitect: first protected-system consumer and policy enforcement point.
|
||||||
- Topaz: candidate MVP delegated backend combining local directory and
|
- Topaz: aligned evaluator. Per ADR-003 the standalone core is shaped
|
||||||
OPA/Rego evaluation.
|
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.
|
- OpenFGA and SpiceDB: candidate relationship authorization backends.
|
||||||
- OPA and Cedar: candidate rule and typed-policy engines.
|
- OPA and Cedar: candidate rule and typed-policy engines.
|
||||||
- Keycloak Authorization Services: adapter path for Keycloak-centric
|
- 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
|
- Entra, Graph, SCIM, LDAP, and Keycloak APIs: directory and group resolver
|
||||||
sources.
|
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
|
## Provided Capabilities
|
||||||
|
|
||||||
```capability
|
```capability
|
||||||
|
|||||||
30
cmd/flex-auth/main.go
Normal file
30
cmd/flex-auth/main.go
Normal 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
|
||||||
|
}
|
||||||
9
cmd/flex-auth/main_test.go
Normal file
9
cmd/flex-auth/main_test.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestVersionDefault(t *testing.T) {
|
||||||
|
if version == "" {
|
||||||
|
t.Fatal("version must not be empty")
|
||||||
|
}
|
||||||
|
}
|
||||||
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.
|
||||||
142
docs/pre-implementation-assessment.md
Normal file
142
docs/pre-implementation-assessment.md
Normal 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`
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
# Flex-Auth Workplan Planning Map
|
# Flex-Auth Workplan Planning Map
|
||||||
|
|
||||||
Date: 2026-05-04
|
Date: 2026-05-15
|
||||||
|
|
||||||
## Purpose
|
## 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
|
## Priority Scale
|
||||||
|
|
||||||
@@ -20,27 +20,39 @@ This document captures the initial sequencing view for flex-auth workplans.
|
|||||||
| Workplan | Priority | Status | Depends On | Current View |
|
| Workplan | Priority | Status | Depends On | Current View |
|
||||||
| --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
| `FLEX-WP-0001` | complete | done | none | Repo intent, boundaries, and authorization landscape research are complete. |
|
| `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-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-0003` | P1 | todo | `FLEX-WP-0002` | Markitect consumer integration: resource namespace, manifest import, action vocabulary, decision fixtures, integration docs. |
|
| `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-0004` | P2 | todo | `FLEX-WP-0002` | Delegated PDP and directory adapters: Topaz, OpenFGA/SpiceDB, OPA/Cedar, Keycloak Authorization Services, Entra/Graph/SCIM. |
|
| `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
|
## Dependency Notes
|
||||||
|
|
||||||
`FLEX-WP-0002` should come first because the protected-system-facing API must
|
`FLEX-WP-0005` is inserted between `0001` and `0002` per the
|
||||||
be stable before flex-auth delegates decisions to external engines.
|
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
|
`FLEX-WP-0002` comes after `0005` so the standalone evaluator embeds the
|
||||||
consumer. Markitect has already completed its side of the initial contract in
|
OPA Rego library and produces decision envelopes shaped to match the
|
||||||
`MKTT-WP-0014`, but flex-auth must still implement the service-side registry
|
delegated-mode envelopes added later.
|
||||||
and decision behavior.
|
|
||||||
|
|
||||||
`FLEX-WP-0004` should wait for the standalone core so delegated engines do not
|
`FLEX-WP-0003` follows the core. Markitect has already completed its
|
||||||
define the whole architecture accidentally.
|
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
|
## 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-0003 -> FLEX-WP-0002`
|
||||||
- `FLEX-WP-0004 -> 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
18
examples/README.md
Normal 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
|
||||||
|
```
|
||||||
8
internal/adapters/doc.go
Normal file
8
internal/adapters/doc.go
Normal 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
6
internal/audit/doc.go
Normal 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
10
internal/decision/doc.go
Normal 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
11
internal/policy/doc.go
Normal 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
7
internal/registry/doc.go
Normal 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
10
pkg/api/doc.go
Normal 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
14
schemas/README.md
Normal 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/`.
|
||||||
@@ -10,8 +10,9 @@ planning_priority: P0
|
|||||||
planning_order: 20
|
planning_order: 20
|
||||||
depends_on_workplans:
|
depends_on_workplans:
|
||||||
- FLEX-WP-0001
|
- FLEX-WP-0001
|
||||||
|
- FLEX-WP-0005
|
||||||
created: "2026-05-04"
|
created: "2026-05-04"
|
||||||
updated: "2026-05-04"
|
updated: "2026-05-15"
|
||||||
state_hub_workstream_id: "aa60e183-9a87-4e03-99b0-15786bfa11ae"
|
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
|
authorization system before delegating to Topaz, OpenFGA, OPA, or other
|
||||||
external policy engines.
|
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
|
## Design Direction
|
||||||
|
|
||||||
The core should define flex-auth's own stable vocabulary:
|
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:
|
Define machine-readable schemas for:
|
||||||
|
|
||||||
- protected system manifest
|
- protected system manifest
|
||||||
- resource manifest
|
- resource manifest (consumes `FlexAuthResourceManifest` pinned in
|
||||||
- subject/group/team manifest
|
`FLEX-WP-0005 T03`)
|
||||||
- relationship fact manifest
|
- subject/group/team manifest (vocabulary aligned with the Topaz
|
||||||
- policy package
|
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
|
- policy fixture/test case
|
||||||
- check request
|
- check request
|
||||||
- decision envelope
|
- decision envelope (provenance fields identical for local and
|
||||||
|
delegated evaluation per ADR-003)
|
||||||
- audit event
|
- 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
|
## 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
|
Load policy-as-code packages with metadata, rules, fixtures, tests, and
|
||||||
activation metadata.
|
activation metadata.
|
||||||
|
|
||||||
The first implementation may use a simple declarative rule format as long as
|
Per ADR-002, packages are Markdown documents with YAML frontmatter,
|
||||||
the package boundary leaves room for OPA/Rego, Cedar, and Topaz later.
|
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
|
## P2.4 - Implement deterministic check and batch_check APIs
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ depends_on_workplans:
|
|||||||
related_workplans:
|
related_workplans:
|
||||||
- FLEX-WP-0003
|
- FLEX-WP-0003
|
||||||
created: "2026-05-04"
|
created: "2026-05-04"
|
||||||
updated: "2026-05-04"
|
updated: "2026-05-15"
|
||||||
state_hub_workstream_id: "99a82976-d376-42b0-89cc-c44e01c0bec6"
|
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
|
provider examples after flex-auth's own request, decision, registry, and audit
|
||||||
vocabulary are stable.
|
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
|
```task
|
||||||
id: FLEX-WP-0004-T001
|
id: FLEX-WP-0004-T001
|
||||||
@@ -37,10 +44,28 @@ priority: high
|
|||||||
state_hub_task_id: "9046418c-2b78-42c6-8bfa-76d6ed0050dd"
|
state_hub_task_id: "9046418c-2b78-42c6-8bfa-76d6ed0050dd"
|
||||||
```
|
```
|
||||||
|
|
||||||
Evaluate Topaz because it combines a local directory, relation modeling, and
|
Implement the Topaz adapter behind flex-auth's stable PDP and directory
|
||||||
OPA/Rego policy evaluation.
|
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
|
## P4.2 - Add relationship PDP adapter boundary
|
||||||
|
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ decisions.
|
|||||||
|
|
||||||
```task
|
```task
|
||||||
id: FLEX-WP-0005-T002
|
id: FLEX-WP-0005-T002
|
||||||
status: in_progress
|
status: done
|
||||||
priority: high
|
priority: high
|
||||||
state_hub_task_id: "8ac73c33-6d36-4963-990d-28b0d1d60947"
|
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
|
- CI configuration (GitHub Actions or equivalent) running
|
||||||
`make lint test build`.
|
`make lint test build`.
|
||||||
|
|
||||||
Exit: `make lint test build` succeeds locally on a fresh clone; CI is
|
Exit: `make ci` (vet + lint + test + build) succeeds locally on a fresh
|
||||||
green; an SBOM is published and ingested via `ingest_sbom_tool` so the
|
clone; GitHub Actions CI is green. SBOM generation works via
|
||||||
repo's `last_sbom_at` becomes non-null.
|
`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
|
## P5.3 - Pin FlexAuthResourceManifest schema
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user