Skip to main content
Everything outside docs/ implements the specification’s Identity profile. Delegated authority (§5) is opt-in: see Delegated authority. Example implementations and seed data are provided as reference. The code calls Brands customers (CUSTOMER_ID, the customers table). These apps are local demos, not production-ready services. The Brand app uses fixed demo credentials and in-memory account data. Before deploying publicly, replace demo authentication, add rate limits, and restrict outbound JWKS requests to public IP addresses after DNS resolution.

Run it locally

Node 20+ and pnpm 11.21.0.
Four processes, each in its own terminal:
DATABASE_POOL_MAX=1 matters: PGlite is a single-session database, so with the default pool of 5 the Provider’s connections interleave and it fails with prepared statement "" requires N parameters. Once the database is up, migrate and seed it. Any PostgreSQL works; point DATABASE_URL at it. PA_ variables and -pa ids refer to the personal agent:
The seed creates three Brands and registers the demo personal agent (demo-pa): Put them in reference/personal-agent/client/.env.local:
Chat at http://localhost:3001. With OPENAI_API_KEY set on either side the agents use OpenAI; without it they use scripted replies.

Delegated authority (optional)

With DELEGATION_ENABLED=1 on the Provider, Skyline Airways’ card also offers OAuth 2.0 device-code delegation (§5) with three scopes: flights:upcoming:read, flights:history:read and flights:rebook. The other Brands stay Identity-only. Without the variable, nothing changes. Roles follow the spec: the Provider is the authorization server (device and token endpoints, consent page, signing key, token checks on every message). The Brand app owns login, accounts and the account API. After login it posts a signed, single-use assertion (keys at {BRAND_URL}/.well-known/jwks.json) to the Provider’s consent page. Start the Brand app and restart the Provider with delegation on:

Demo hostnames

For browser-facing demo URLs that look like a production deployment, start the Provider with:
Start the Brand with:
Then open http://agent.localhost:3001. This mirrors production, where consent is served by the Provider on a Brand subdomain (Provider guide). In the demo personal agent, ask “Can you check my upcoming Skyline flight?”. Skyline replies TASK_STATE_AUTH_REQUIRED, and the agent shows a Sign in with Skyline Airways card that opens the Brand login in a new tab (demo account alex.rivera@example.com / skyline). Choose scopes on the consent page; the agent polls the token endpoint, re-sends the message with X-A2A-User-Delegation, and shows the signed receipt on the Skyline thread. Asking to rebook without flights:rebook triggers step-up. Delegated conformance tests (skipped when the card has no delegation):

Register a personal agent

The reference Provider registers personal agents at POST {PROVIDER_URL}/api/platforms (the spec leaves registration to each Provider):
REGISTRATION_TOKEN is a JWT signed with the personal agent’s key: iss = issuer, sub = iss, aud = the endpoint URL, exp ≤ 300 s. The JWKS URI must share the issuer’s origin. 201 registers (audience = A2A_AUDIENCE), 409 means the name or issuer is taken. The demo personal agent’s Register button makes this call.

Conformance tests

PA_PRIVATE_JWK comes from the environment or the personal-agent client’s .env.local. Against another Provider add E2E_PROVIDER=any, which skips only the reference Provider’s seeded card text and canned replies. E2E_TEST_TIMEOUT_MS (default 60000) bounds each test.