Erlang ADK 0.6.0 release contract

View Source

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: per-user credentials, interactive OAuth/OIDC, short-lived tokens, and production secret-manager ownership;
  • MCP 2025-11-25 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: 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: 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

AreaStarting statusv0.6 release requirement
Incoming OIDC/JWTImplemented release sliceStrict 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 credentialsImplemented release sliceImmutable 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/OIDCImplemented release sliceSupervised 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.
MCPHardened partialVersion 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.0Hardened partialPublic 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.
OpenAPIImplemented supported subsetBounded 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 gatewayImplemented agent-run surfaceAuthenticated 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 companionImplemented reference companionThe 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 UIImplemented local toolLoopback-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.