Current section

Files

Jump to
erlang_adk docs VERSION_0_6_0.md
Raw

docs/VERSION_0_6_0.md

# Erlang ADK 0.6.0 release contract
> **Status:** frozen historical contract, completed on 2026-07-14 at branch
> checkpoint `6448793`. The final gate passed 899 EUnit tests, six Common Test
> scenarios, and Dialyzer over 170 project files. Unchecked items remain
> documented limitations and release exceptions.
This document is the implementation and release contract for the
`version_0.6.0` branch. Version 0.6 focuses on **authentication, protocols,
and a production-capable UI**. It inherits the v0.5 artifact, memory, context,
runtime, and developer-tooling behavior; it does not reinterpret a shared
local developer bearer token as production user authorization.
The target is observable behavior, not a line-for-line port of another ADK.
Long-running work remains in independently supervised BEAM processes,
unrelated principals and sessions should overlap, and network or browser
lifetimes must not own agent-run lifetimes.
Primary external contracts:
- [ADK tool authentication](https://adk.dev/tools-custom/authentication/):
per-user credentials, interactive OAuth/OIDC, short-lived tokens, and
production secret-manager ownership;
- [MCP 2025-11-25 authorization](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization):
RFC 9728 protected-resource metadata, authorization-server discovery,
RFC 8707 resource indicators, PKCE S256, scope challenges, and exact token
audience validation;
- [A2A 1.0](https://a2a-protocol.org/latest/specification/): declared Agent
Card security, authentication on every JSON-RPC/task request, public Agent
Card discovery, operation/data
authorization, TLS, and principal-scoped tasks;
- [Phoenix LiveView security](https://hexdocs.pm/phoenix_live_view/security-model.html):
authenticate both HTTP and connected mounts and authorize every event.
## Release truth at branch start
The v0.5 baseline already provides:
- strict bearer parsing and Oidcc-backed JWT/JWKS validation with issuer,
audience, algorithm, time, subject, scope, and claim policy;
- private scoped credential references, CAS refresh-token rotation, bounded
supervised single-flight refresh, and seeded secret redaction;
- MCP stdio and 2025-11-25 Streamable HTTP plus an authenticated,
loopback-first bounded server;
- A2A 1.0 Agent Card, JSON-RPC/SSE tasks, principal-scoped visibility, and an
outbound client;
- an authenticated, bounded `/dev/v1` REST/SSE console and packaged CLI;
- stable supervised runs with bounded credit/ack replay, cancellation, and
durable pause/resume semantics;
- a documented Phoenix pattern, but no compiled Phoenix application.
The audit found release-blocking gaps:
1. Token callers can select the credential-consuming provider module and
arbitrary provider context.
2. Token audience is only a cache discriminator and is not sent as an RFC
8707 resource indicator.
3. Credential deletion/revocation is not coupled to token-cache eviction;
built-in credential storage is volatile and unbounded.
4. The PKCE helper stores a verifier but does not own state, nonce, redirect,
code exchange, replay protection, or cleanup as one supervised flow.
5. MCP sessions are not bound to authenticated principals, unsupported
initialization versions are rejected instead of negotiated, and MCP OAuth
discovery/challenges are absent.
6. Public A2A can be configured over clear HTTP without authentication; the
client trust policy does not yet prevent cross-origin token forwarding or
private-address discovery.
7. A2A optional-operation errors, extensions, catalog/data byte bounds,
streaming backpressure, and durable distributed task behavior are partial.
8. OpenAPI credential resolution is serialized inside one broker process and
several transport/version edge cases remain.
9. `/dev` has one local administrator token and accepts caller-selected
app/user/session paths. It must remain loopback developer tooling.
10. Runs are globally discoverable by ID and are not bound to an authenticated
owner at registration.
11. The Phoenix example is an uncompiled README excerpt rather than a tested,
deployable companion.
## Architectural decisions
1. **Authentication and authorization are separate.** Oidcc/JWT policy proves
identity. A default-deny authorizer decides each exact operation/resource.
2. **Provider configuration is trusted and immutable.** A token request may
name a configured provider and opaque credential reference; it may not
choose the module that receives raw credentials or inject provider context.
3. **Credentials never enter model/session/UI data.** Browser sessions contain
opaque session material. Access/refresh tokens, client secrets, PKCE
verifiers, and provider callbacks stay outside LiveView assigns, events,
prompts, telemetry, and public errors.
4. **Protocol sessions inherit identity.** MCP sessions and A2A tasks bind to
stable issuer/principal scopes at creation. Cross-principal access is
indistinguishable from an unknown resource.
5. **The Phoenix application is a BFF on the same BEAM.** It calls an Erlang
gateway directly; there is no required REST hop. Each browser/LiveView is a
lightweight process, while stable runs outlive browser disconnects.
6. **`/dev` stays local.** It is an operator/developer surface and is never the
production end-user API. Public protocol and production UI listeners do not
accidentally expose it.
7. **Credential-bearing and network callback boundaries hardened in v0.6 are
bounded.** Authentication, authorization, provider, broker, discovery,
parser, and stream work in that slice has absolute deadlines,
byte/count/heap limits, caller monitoring, and supervised cleanup. Trusted
application modules and boot-time configuration loaders are not presented
as a hostile-code sandbox or as protection from unsafe NIFs/VM-wide side
effects.
8. **Stable protocol versions are explicit.** v0.6 targets released MCP
2025-11-25 and A2A 1.0. Draft MCP versions are rejected/negotiated through
version dispatch rather than silently changing semantics.
## Capability matrix
| Area | Starting status | v0.6 release requirement |
| --- | --- | --- |
| Incoming OIDC/JWT | Implemented release slice | Strict bounded verification plus default-deny operation authorization, access-token `aud`/scope policy, separate OIDC ID-token `azp` rules, and local fixtures are implemented. IdP revocation and back-channel logout remain deployment integration. |
| Outbound credentials | Implemented release slice | Immutable provider profiles, least-authority token requests, RFC 8707 resource targeting, bounded private cache/admission/storage, explicit invalidation, and secret-free failures/status are implemented. A durable encrypted store and provider-specific revocation remain adapters. |
| Interactive OAuth/OIDC | Implemented release slice | Supervised state/nonce/S256 flow ownership, exact redirect/provider/client/scope binding, internal code exchange, one-time completion, bounded expiry cleanup, and opaque HITL correlation are implemented. IdP logout/revocation remains an integration hook. |
| MCP | Hardened partial | Version negotiation, principal-bound sessions, per-operation authorization, RFC 9728 server metadata/challenges, destination/TLS/DNS/token isolation, absolute deadlines, and bounds are implemented. Automatic client-side OAuth discovery/PKCE and fully incremental concurrent SSE delivery are not claimed. |
| A2A 1.0 | Hardened partial | Public bind/TLS policy, strict card/security/extension validation, canonical optional-operation errors, outbound SSRF/token isolation, absolute deadlines, byte/count limits, and slow-subscriber detachment are implemented. The outbound SSE response is bounded but buffered, and tasks remain node-local. |
| OpenAPI | Implemented supported subset | Bounded broker workers, immutable auth profiles, numeric version parsing, safer IPv6/redirect/method behavior, compilation bounds, and regression coverage are implemented for the documented subset. |
| Production web gateway | Implemented agent-run surface | Authenticated identity mapping, server-owned agents, exact operation scopes, immutable run ownership/resume inheritance, bounded HITL input, and redacted decisions are implemented. Artifact/memory/context administration and durable audit/revocation remain separate privileged APIs/adapters. |
| Phoenix companion | Implemented reference companion | The checked Phoenix 1.8 project provides OIDC code+S256 PKCE, private opaque sessions, secure origin/CSRF/header policy, bounded LiveView delivery, typed fail-closed HITL, reconnect/cancel/replay-gap handling, deterministic tests, assets, and release assembly. Multi-node session state, IdP SLO, and privileged resource panels remain explicit limitations. |
| Local developer UI | Implemented local tool | Loopback-only startup, bounded browser/reconnect behavior, fail-closed unknown pause types, and explicit separation from production routes are implemented. It remains a single-operator development surface. |
## Delivery phases
### A. Trusted identity, authorization, and run ownership
- [x] Add a default-deny authorizer with exact issuer, principal, operation,
and required-scope validation.
- [x] Add a production `adk_web_gateway` which resolves agents from a
server-owned catalog and derives `user_id` from the OIDC principal.
- [x] Bind a stable run atomically to an opaque owner scope and make resumed
runs inherit it even if a caller supplies different options.
- [x] Make cross-owner run lookup return the same result as an unknown run.
- [ ] Extend the gateway to exact-scope session/artifact/memory/context
operations with explicit approver/operator/admin policies.
- [ ] Add bounded durable security-audit adapters and revocation-driven
disconnect of active browser sessions.
### B. Outbound authentication and credential lifecycle
- [x] Replace caller-selected provider modules/context with immutable trusted
provider profiles.
- [x] Enforce profile grant, scope, audience/resource, TTL, and concurrency
limits before credential resolution.
- [x] Add explicit opaque-reference token invalidation for local cache
eviction.
- [ ] Couple credential deletion/provider revocation to token invalidation in
deployments that expose those lifecycle operations.
- [ ] Add provider revocation and persistent credential-generation binding.
- [x] Pass RFC 8707 resource indicators through supported OAuth grants and
validate JWT access-token audience where possible.
- [ ] Add a durable encrypted credential-store adapter whose key resolver can
be backed by KMS/HSM/secret-manager infrastructure; keep ETS documented as
a development adapter.
- [x] Add private bounded cache/storage, expiry sweep, worker limits, global
admission, bounded waiters, and secret-free status/errors.
- [ ] Add durable negative backoff and independently configurable
per-principal refresh admission.
### C. Interactive OAuth/OIDC
- [x] Add a supervised Oidcc authorization-flow manager owning state, nonce,
S256 verifier, redirect URI, client/provider/resource/scopes, deadline, and
one-time callback claim.
- [x] Exchange authorization codes internally and store only a validated
credential behind the original opaque reference.
- [x] Bind the authenticated subject/issuer/client/scopes to the paused
invocation and reject replay, mix-up, redirect mismatch, and expiry.
- [x] Add bounded cancellation and expiry cleanup.
- [ ] Add provider logout, token revocation, and back-channel session-disconnect
hooks.
### D. Protocol hardening
- [x] Correct MCP initialization version negotiation.
- [x] Bind MCP sessions and deletes to authenticated principal scope.
- [x] Serve RFC 9728 protected-resource metadata and Bearer challenges with
authoritative scopes.
- [ ] Add automatic MCP client authorization-server discovery and a managed
PKCE S256 flow; applications can currently wire the generic supervised flow.
- [x] Bound MCP HTTP authentication, connection, response, pending work, body,
caller cleanup, and schema processing under an absolute deadline.
- [ ] Deliver concurrent MCP HTTP SSE responses incrementally to callers; the
current client deliberately uses one supervised client per independent
stream and a bounded response buffer.
- [x] Make public A2A require authentication plus TLS or an explicitly trusted
TLS proxy; never co-expose `/dev` on that listener.
- [x] Add canonical A2A optional-operation errors, bounded extension handling,
structural card security validation, and fail-closed single-scheme client
selection.
- [ ] Add a multi-credential client for compound A2A AND requirements; server
hook-to-card semantic alignment remains an operator policy responsibility.
- [x] Add A2A client SSRF/DNS/redirect/same-origin token policy and bounded
subscription responses.
- [x] Add A2A task/history/artifact/metadata byte limits, subscriber/admission
limits, and one absolute operation deadline.
- [ ] Replace buffered outbound A2A SSE decoding with a credit-based
incremental callback API; server-side subscribers are already bounded and
detached on overflow.
- [x] Fix OpenAPI broker/transport/version edge cases and add regression tests.
### E. Production Phoenix UI
- [x] Check in a Phoenix 1.8/LiveView companion application with a lock file,
deterministic provider, tests, assets, runtime configuration, and release.
- [x] Use OIDC Authorization Code plus S256 PKCE for enterprise identity; keep
generated local auth with a maintained password hasher as an optional
deployment choice, not a second identity inside the Erlang core.
- [x] Enforce secure/HttpOnly/SameSite cookies, session rotation, CSRF, exact
WebSocket origin, CSP, HSTS, parser/body bounds, and per-event authorization.
- [ ] Apply route and identity-aware login/callback rate limits at the trusted
deployment edge; the companion documents this operator requirement.
- [x] Use bounded LiveView streams/assigns, credit/ack ADK subscriptions,
stable session/run IDs, reconnect/replay-gap recovery, cancellation, and
graceful detach/drain.
- [x] Implement typed HITL components with attribution,
double-submit protection, and fail-closed unknown pause types.
- [ ] Add user/approver/operator/admin route groups and exact-scope
artifact/memory/context panels.
### F. Integrated developer tooling and documentation
- [x] Enforce loopback-only `/dev` for application, CLI, and direct startup.
- [x] Bound transcript/trace DOM and reconnect attempts; preserve cursor on
manual attach; fail closed for unknown pause types.
- [x] Replace the old Phoenix README excerpt with commands for the checked-in
companion and keep production/developer topology explicit.
- [x] Add every new README fence to `README_EXAMPLE_COVERAGE.md` and remove
stale references to nonexistent tests.
- [x] Reconcile `FEATURE_PARITY.md`, protocol guides, security operations,
proxy/TLS deployment, key rotation, backup/restore, and incident response.
### G. Release evidence
- [x] Focused authentication, gateway, protocol, and Phoenix suites pass with
no skipped deterministic cases.
- [x] `./rebar3 do clean, compile, eunit, ct, dialyzer` passes from clean: 899
EUnit tests, six deterministic Common Test cases, and warning-free Dialyzer
over 170 project files.
- [x] README examples, escript packaging, doctor, and config validation pass.
- [x] Phoenix format/compile/test/assets/release gates pass from its locked
project: 46 tests pass, production assets/release assembly complete, and the
packaged release returns HTTP 200 from `/health` on loopback in both
trusted-proxy and direct-TLS modes before a clean stop.
- [ ] Resolve the two documented Cowlib 2.18.0 advisories reported by
`mix hex.audit`; the audit was run and remains an explicit non-zero release
exception until an official complete upstream fix is released.
- [ ] MCP and A2A cross-SDK interoperability jobs pass in both directions.
- [x] Local-CA TLS, malicious discovery/redirect, auth timeout/crash/heap,
cross-principal, replay, and secret-leak suites pass.
- [x] Concurrency/soak tests record bounded process counts/mailboxes and no
orphan refresh/request/subscription workers.
- [x] The separate `gemini-3.1-flash-lite` live suite was run with no skips:
14 cases passed, while Search grounding and context-cache creation each
failed explicitly on HTTP 429 after one bounded retry. Provider/quota
failures are reported and are never counted as passes.
## Explicit non-goals unless completed later on this branch
- The local `/dev` bearer is not a production identity system.
- The core does not store end-user passwords. Phoenix-generated local auth or
an external IdP owns them.
- ETS credentials are not durable or encrypted-at-rest production storage.
- A TLS-terminating proxy assertion is not accepted from arbitrary forwarding
headers; it is an explicit trusted deployment configuration.
- Node-local A2A tasks and run registry do not imply horizontal failover.
Sticky affinity/single-node runtime is documented until a distributed
locator/store/lease contract passes restart and partition tests.
- MCP optional roots, sampling, elicitation, completion, and unsolicited
server GET/SSE are not claimed merely because authentication is hardened.
- A2A push notifications are not enabled without callback SSRF policy,
authentication/signing, retry/backoff, dedupe, and durable outbox behavior.
- Gemini Live remains a separate bidirectional session protocol; REST SSE is
not presented as Live support.