Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -22,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:**
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion docs/api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
10 changes: 6 additions & 4 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
|---|---|---|---|
Expand All @@ -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` |

---

Expand Down Expand Up @@ -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.

Expand Down
2 changes: 1 addition & 1 deletion docs/getting-started-operator.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Loading