From ad9766d38ebe0a243ca38a413a85ed7eadcc8ae1 Mon Sep 17 00:00:00 2001 From: Divine Date: Mon, 13 Apr 2026 15:12:24 -0400 Subject: [PATCH 1/2] fix: complete pattern component mapping + correct signing key docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Finding 2: Pattern component table now maps all 8 EAC v1.3 components with correct numbering per docs/concepts.md. Component 6 (Mutual Authentication / mutauth) documented as present but not wired into the broker. Components 7 (Delegation) and 8 (Observability) added with their Go packages, types, and functions. Finding 3: Removed false claim in Security Assumptions that "all previously issued tokens become unverifiable after restart (new signing keys)." The Ed25519 signing key is persistent via internal/keystore — tokens survive broker restarts. Corrected the single-broker-instance note to describe the actual split-brain risk (in-memory transient state, not the signing key). --- CHANGELOG.md | 5 +++++ docs/architecture.md | 10 ++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bce3fff..34997dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed — Architecture component mapping + signing key documentation (2026-04-13) + +- Pattern component table now maps all 8 EAC v1.3 components with correct numbering. Component 6 (Mutual Authentication) documented as present but not wired. Component 7 (Delegation) and 8 (Observability) added. +- Security Assumptions: removed false claim that "all previously issued tokens become unverifiable after restart (new signing keys)." The signing key is persistent via `internal/keystore` — tokens survive restarts. Single-broker note corrected to describe the actual split-brain risk (in-memory state, not signing keys). + ### Fixed — SPDX identifier + gate + middleware diagram (2026-04-13) - SPDX identifier corrected from `PolyForm-Internal-Use-1.0.0` to `LicenseRef-PolyForm-Internal-Use-1.0.0` across LICENSE and all 77 Go files. PolyForm Internal Use is not on the SPDX License List — `LicenseRef-` prefix is required by spec for unlisted licenses. diff --git a/docs/architecture.md b/docs/architecture.md index 0ac7c7d..ad1b8ea 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -126,7 +126,7 @@ Each service is initialized in `cmd/broker/main.go` with explicit constructor in ## Pattern Components Mapped to Code -The 8-component Ephemeral Agent Credentialing pattern maps directly to Go packages: +The 8-component Ephemeral Agent Credentialing pattern maps to Go packages. Components 1–5, 7, and 8 are fully implemented. Component 6 (Agent-to-Agent Mutual Authentication) has a package (`mutauth`) but is not wired into the broker — it is planned work. | Pattern Component | Go Packages | Key Types | Key Functions | |---|---|---|---| @@ -139,7 +139,9 @@ The 8-component Ephemeral Agent Credentialing pattern maps directly to Go packag | 3. Zero-Trust Enforcement | `authz`, `handler` | `ValMw`, `RateLimiter` | `ValMw.Wrap()`, `ValMw.RequireScope()`, `ValMw.RequireAnyScope()`, `ScopeIsSubset()` | | 4. Automatic Expiration & Revocation | `revoke`, `token`, `handler` | `RevSvc`, `Revoker`, `RevokeHdl`, `ReleaseHdl` | `RevSvc.Revoke()`, `RevSvc.RevokeByJTI()`, `RevSvc.IsRevoked()`, `RevSvc.LoadFromEntries()` | | 5. Immutable Audit Logging | `audit`, `handler` | `AuditLog`, `AuditEvent`, `AuditHdl`, `RecordOption` | `AuditLog.Record()`, `AuditLog.Query()`, `WithOutcome()`, `WithResource()` | -| 6. Delegation Chain Verification | `deleg`, `handler` | `DelegSvc`, `DelegHdl`, `DelegRecord` | `DelegSvc.Delegate()` | +| 6. Mutual Authentication | `mutauth` *(not wired)* | `MutAuthHdl`, `Discovery`, `Heartbeat` | Package exists but is not registered in `cmd/broker/main.go`. Planned. | +| 7. Delegation Chain Verification | `deleg`, `handler` | `DelegSvc`, `DelegHdl`, `DelegRecord` | `DelegSvc.Delegate()` | +| 8. Operational Observability | `obs`, `handler` | `HealthHdl`, `MetricsHdl` | `obs.Ok()`, `obs.Warn()`, `obs.Fail()`, `obs.Trace()`, `/v1/health`, `/v1/metrics` | --- @@ -315,9 +317,9 @@ These are explicit trust boundaries and limitations of the current implementatio - **X-Forwarded-For trusted unconditionally.** The `clientIP()` function in `internal/authz/rate_mw.go` trusts the first entry in `X-Forwarded-For` without validation. In production, the broker must sit behind a trusted reverse proxy that sets this header correctly. Without a trusted proxy, rate limiting can be bypassed via header spoofing. -- **Persistent and transient state split.** Audit events, revocations, and app registrations are persisted to SQLite and reloaded on startup. Nonces, agent records, and launch tokens are transient (memory only). All previously issued tokens become unverifiable after restart (new signing keys). The split is intentional — audit and revocation are security-critical; nonces and agent records are ephemeral by design. +- **Persistent and transient state split.** Audit events, revocations, and app registrations are persisted to SQLite and reloaded on startup. The Ed25519 signing key is persisted to disk (`AA_SIGNING_KEY_PATH`), so tokens issued before a restart remain verifiable. Nonces, agent records, and launch tokens are transient (memory only) and cleared on restart. The split is intentional — audit, revocation, and the signing key are security-critical; nonces and agent records are ephemeral by design. -- **Single broker instance.** There is no replication, consensus, or shared state mechanism. The broker is a single process. Running multiple instances would result in split-brain token verification (each instance has its own signing key). +- **Single broker instance.** There is no replication, consensus, or shared state mechanism. The broker is a single process. Running multiple instances against separate databases would result in split-brain state for nonces, agent records, and revocation lists. The signing key can be shared via a common `AA_SIGNING_KEY_PATH`, but transient in-memory state cannot. - **Nonce window is 30 seconds.** Nonces expire after 30 seconds. Agents must complete the challenge-response flow within this window. Clock skew between agent and broker can cause failures. From f9f919ebbb017cd63c49fb7cb02b1e462e68f6ad Mon Sep 17 00:00:00 2001 From: Divine Date: Mon, 13 Apr 2026 15:14:48 -0400 Subject: [PATCH 2/2] fix: LicenseRef- prefix on remaining bare SPDX identifiers Missed spots from PR #24's LicenseRef rename: - Dockerfile OCI label (org.opencontainers.image.licenses) - docs/api/openapi.yaml license name field - docs/getting-started-operator.md OCI label reference - CHANGELOG.md historical entries updated to match corrected value Verified: zero bare PolyForm-Internal-Use-1.0.0 identifiers remain outside of human-readable prose ("PolyForm Internal Use License 1.0.0"). --- CHANGELOG.md | 6 +++--- Dockerfile | 2 +- docs/api/openapi.yaml | 2 +- docs/getting-started-operator.md | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 34997dd..0db27bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,14 +27,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added — SPDX headers on all Go source files (2026-04-13) -- Every `.go` file in `cmd/` and `internal/` (77 files) now carries `// SPDX-License-Identifier: PolyForm-Internal-Use-1.0.0` as the first line. +- Every `.go` file in `cmd/` and `internal/` (77 files) now carries `// SPDX-License-Identifier: LicenseRef-PolyForm-Internal-Use-1.0.0` as the first line. - CI `contamination` gate extended with an SPDX header check — new Go files without the header will fail the gate. ### Changed — License: AGPL-3.0 → PolyForm Internal Use 1.0.0 (2026-04-13) -- **`LICENSE`** — replaced AGPL-3.0 text with PolyForm Internal Use License 1.0.0 (source-available, permanent, no sunset). SPDX identifier: `PolyForm-Internal-Use-1.0.0`. Dual-license header added for commercial-use contact path. +- **`LICENSE`** — replaced AGPL-3.0 text with PolyForm Internal Use License 1.0.0 (source-available, permanent, no sunset). SPDX identifier: `LicenseRef-PolyForm-Internal-Use-1.0.0`. Dual-license header added for commercial-use contact path. - **`README.md`** — license badge updated; License section rewritten with the free / paid dual-license split and the `licensing@agentwrit.com` contact line. -- **`Dockerfile`** — `org.opencontainers.image.licenses` OCI label updated from `AGPL-3.0-only` to `PolyForm-Internal-Use-1.0.0`. +- **`Dockerfile`** — `org.opencontainers.image.licenses` OCI label updated from `AGPL-3.0-only` to `LicenseRef-PolyForm-Internal-Use-1.0.0`. - **`docs/api/openapi.yaml`** — OpenAPI `info.license` block updated to reference PolyForm Internal Use 1.0.0. - **`docs/getting-started-operator.md`** — OCI labels reference updated to match the new Dockerfile label. - **What this means for users:** diff --git a/Dockerfile b/Dockerfile index 3ebd237..7966d90 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,7 +24,7 @@ FROM alpine:3.21 AS broker LABEL org.opencontainers.image.title="AgentWrit" \ org.opencontainers.image.description="Ephemeral agent credentialing broker — short-lived, scope-attenuated tokens for AI agents" \ org.opencontainers.image.vendor="devonartis" \ - org.opencontainers.image.licenses="PolyForm-Internal-Use-1.0.0" \ + org.opencontainers.image.licenses="LicenseRef-PolyForm-Internal-Use-1.0.0" \ org.opencontainers.image.source="https://github.com/devonartis/agentwrit" \ org.opencontainers.image.url="https://github.com/devonartis/agentwrit" \ org.opencontainers.image.documentation="https://github.com/devonartis/agentwrit/blob/main/README.md" diff --git a/docs/api/openapi.yaml b/docs/api/openapi.yaml index 95cb1a8..c37e184 100644 --- a/docs/api/openapi.yaml +++ b/docs/api/openapi.yaml @@ -9,7 +9,7 @@ info: contact: name: AgentWrit license: - name: PolyForm-Internal-Use-1.0.0 + name: LicenseRef-PolyForm-Internal-Use-1.0.0 url: https://polyformproject.org/licenses/internal-use/1.0.0 servers: diff --git a/docs/getting-started-operator.md b/docs/getting-started-operator.md index 1bc007a..3d719b5 100644 --- a/docs/getting-started-operator.md +++ b/docs/getting-started-operator.md @@ -73,7 +73,7 @@ If verification fails, **do not run the image** — it was not produced by this - Platforms: `linux/amd64`, `linux/arm64` - Binary: `/broker` (entry point) - Ports exposed: `8080` -- OCI labels: `org.opencontainers.image.source`, `revision`, `created`, `title`, `description`, `licenses=PolyForm-Internal-Use-1.0.0` +- OCI labels: `org.opencontainers.image.source`, `revision`, `created`, `title`, `description`, `licenses=LicenseRef-PolyForm-Internal-Use-1.0.0` ### Docker Hub listing