Skip to content

docs: add scoped authorization receipt extension example#1915

Open
dinpd wants to merge 3 commits into
a2aproject:mainfrom
dinpd:docs/authorization-receipt-extension-example
Open

docs: add scoped authorization receipt extension example#1915
dinpd wants to merge 3 commits into
a2aproject:mainfrom
dinpd:docs/authorization-receipt-extension-example

Conversation

@dinpd

@dinpd dinpd commented Jun 5, 2026

Copy link
Copy Markdown

Summary

  • Adds a non-normative scoped authorization receipt extension example to the A2A extensions guide.
  • Shows how an Agent Card can advertise receipt transport, binding requirements, verifier metadata, and a receipt_profile_uri without changing the core Agent Card schema.
  • Notes that domain-specific receipt profiles should define closed outcome vocabularies, while the Agent Card remains generic.

Context

Resolves #1909.

This follows the discussion in #1909 around separating generic Agent Card discovery metadata from domain-specific receipt profile semantics.

Validation

  • git diff --check
  • Parsed the JSON examples in docs/topics/extensions.md

Full MkDocs validation was not run locally because the local Python environment does not have mkdocs installed.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds a new section to the documentation detailing a Scoped Authorization Receipt Extension example. The feedback suggests updating the JSON example to use supportedInterfaces instead of a root-level url field to comply with the core schema, and clarifying the terminology from 'agent-originated action' to 'agent-targeted action' or 'client-requested action' for better accuracy.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread docs/topics/extensions.md Outdated
"name": "Expense Approval Agent",
"description": "An agent that can prepare and submit expense approvals.",
"version": "1.0.0",
"url": "https://example.com/agents/expense-approval",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The AgentCard schema defined in specification/a2a.proto does not have a root-level url field. Instead, URLs and protocol bindings are specified under the required supported_interfaces field (serialized as supportedInterfaces in ProtoJSON). Updating this example to use supportedInterfaces ensures it is fully compliant with the core schema.

Suggested change
"url": "https://example.com/agents/expense-approval",
"supportedInterfaces": [
{
"url": "https://example.com/agents/expense-approval",
"protocolBinding": "HTTP+JSON",
"protocolVersion": "1.0"
}
],

Comment thread docs/topics/extensions.md
Comment on lines +128 to +130
server, while a scoped authorization receipt can show that a specific
agent-originated action was admitted for a particular task, resource, request
digest, policy version, and expiry. The A2A server or downstream provider still

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low

The term agent-originated action might be confusing here, as the client is the one presenting the receipt to the agent to prove authorization before the agent processes it. It might be clearer to refer to it as an agent-targeted action or client-requested action.

@dinpd dinpd force-pushed the docs/authorization-receipt-extension-example branch from f334440 to 44ce128 Compare June 5, 2026 19:30
@dinpd dinpd changed the title Add scoped authorization receipt extension example docs: add scoped authorization receipt extension example Jun 5, 2026
@chopmob-cloud

chopmob-cloud commented Jun 5, 2026

Copy link
Copy Markdown

+1 on keeping this non-normative and pushing domain semantics out to receipt_profile_uri rather than into the core Agent Card. We run essentially this split in production (multi-chain x402 payment gateway) and it holds up.

1. Closed vocabulary is the right call. The AlgoVoi payment-compliance profile uses a small closed outcome set, each value tied to a specific regulatory trigger. That closed set is what makes the receipt independently auditable — open-ended status strings don't survive audit.

2. Binding needs a canonicalisation rule, not just a field list. receipt_binds says what to bind, but replay/scope-drift resistance depends on how those fields are serialised before signing. Worth recommending profiles pin a canonical serialisation so two semantically-equal receipts hash identically and verifiers agree.

3. Basis vs. data minimisation. The basis behind a verdict can be privacy-sensitive. We keep it out of the signed bytes and surface only the categorical outcome, holding detail in operator records. The profile might allow basis-by-reference rather than inline.

4. Keep receipt_verification.algorithms open. The example pins ES256; the jwks_uri + JWS transport happily covers other signature schemes, so suggest the algorithm list stay profile-defined.

One scope note: this example covers classical JWS/ES256 receipts only — it doesn't address post-quantum signatures or zero-knowledge attestations. Those sit in a separate trust layer in the AlgoVoi deployment and aren't implied by this extension. Flagging so the example isn't read as the full authority/trust surface.

Net: matches what we landed on independently. authorization_contract_uri + receipt_profile_uri maps cleanly onto an on-chain authority contract + off-chain receipt profile.

Live agent card / A2A explorer if you want to probe a working deployment: https://dash.algovoi.co.uk/a2a


AlgoVoi (chopmob-cloud) -- Acquisition enquiries: https://docs.algovoi.co.uk/acquisition

@dinpd dinpd force-pushed the docs/authorization-receipt-extension-example branch from 44ce128 to 4c5af92 Compare June 5, 2026 19:43
@chopmob-cloud

chopmob-cloud commented Jun 5, 2026

Copy link
Copy Markdown

Quick follow-up — we went ahead and wired the extension shape into our live agent card (issuer/verifier side: we produce these receipts rather than require them, so required: false). It advertises the closed outcome vocabulary, the EdDSA/JWS verification metadata with a real jwks_uri, and a receipt_profile_uri — all populated from production config. Rolling out to api.algovoi.co.uk/.well-known/agent.json shortly if you'd like a real instance to probe against.

I hope this helps.


AlgoVoi (chopmob-cloud) -- Acquisition enquiries: https://docs.algovoi.co.uk/acquisition

@dinpd

dinpd commented Jun 5, 2026

Copy link
Copy Markdown
Author

Thanks, this is very helpful. The required: false producer/verifier case is a useful example because it shows the same extension shape can advertise receipt issuance and verification metadata without forcing every client interaction to present a receipt.

The production config detail also reinforces why the Agent Card should stay generic: it can expose transport, verification metadata, and receipt_profile_uri, while the profile defines the closed outcome vocabulary and verifier rules.

Once the live card is available, it would be useful implementation evidence for this extension pattern.

@chopmob-cloud

chopmob-cloud commented Jun 5, 2026

Copy link
Copy Markdown

It's live now — the extension is being served from production:

https://api.algovoi.co.uk/.well-known/agent.jsoncapabilities.extensions[0]

You can probe it directly; it passes A2A v0.3 validation with the extension present. The receipt_verification.jwks_uri, receipt_profile_uri, and authority_manifest_uri all resolve, and receipt_outcomes shows the closed ALLOW/REFER/DENY vocabulary in practice.

If you need anything else from our side — a different binding, a worked receipt example, or anything to help the example land — just shout.


AlgoVoi (chopmob-cloud) -- Acquisition enquiries: https://docs.algovoi.co.uk/acquisition

@dinpd

dinpd commented Jun 5, 2026

Copy link
Copy Markdown
Author

Thanks, this is excellent implementation evidence.

I probed the live card and the extension shape is visible under capabilities.extensions[0]: required: false, issuer/verifier role, closed ALLOW | REFER | DENY outcomes, JWS/EdDSA verification metadata, and resolving jwks_uri, receipt_profile_uri, and authority_manifest_uri.

That is useful validation for keeping the A2A example non-normative: the same extension shape can describe a real producer/verifier deployment while leaving the profile to define the outcome vocabulary and verifier rules.

@chopmob-cloud

chopmob-cloud commented Jun 5, 2026

Copy link
Copy Markdown

Here's a live, end-to-end-verifiable receipt for the worked example. Anyone can reproduce it — no auth required.

1. Request a screen (POST https://api.algovoi.co.uk/compliance/screen):

{"recipient_address":"<addr>","network":"algorand:mainnet","amount_microunits":1000000,"asset":"USDC"}

2. The response carries the receipt + its detached signature. The compliance_receipt (the closed-vocabulary payload):

{
  "payer_ref": "sha256:45c8fd4b3a75c567b4fb8933e996f77c56d65ce60ae55568282660bd610107c6",
  "screen_result": "ALLOW",
  "screen_timestamp_ms": 1780689523721,
  "screen_provider_did": "did:key:z6MkgExzvcpvxrghf4Q3285xqSdenhRZHcP6wc5UvY6VVaz5",
  "jurisdiction_flags": ["UK", "EU"],
  "canon_version": "jcs-rfc8785-v1"
}

screen_result is the closed ALLOW / REFER / DENY vocabulary; payer_ref is a salted hash, not the raw address (data minimisation — the basis stays out of the signed bytes).

3. The compact JWS (compliance_receipt_jws, EdDSA / Ed25519):

eyJhbGciOiJFZERTQSIsImtpZCI6ImQwNDgxZGY0Y2JiZGE4ZThhYmE4NjcwOTQxOTg4NGVmIiwidHlwIjoiSldUIn0.eyJjYW5vbl92ZXJzaW9uIjoiamNzLXJmYzg3ODUtdjEiLCJqdXJpc2RpY3Rpb25fZmxhZ3MiOlsiVUsiLCJFVSJdLCJwYXllcl9yZWYiOiJzaGEyNTY6NDVjOGZkNGIzYTc1YzU2N2I0ZmI4OTMzZTk5NmY3N2M1NmQ2NWNlNjBhZTU1NTY4MjgyNjYwYmQ2MTAxMDdjNiIsInNjcmVlbl9wcm92aWRlcl9kaWQiOiJkaWQ6a2V5Ono2TWtnRXh6dmNwdnhyZ2hmNFEzMjg1eHFTZGVuaFJaSGNQNndjNVV2WTZWVmF6NSIsInNjcmVlbl9yZXN1bHQiOiJBTExPVyIsInNjcmVlbl90aW1lc3RhbXBfbXMiOjE3ODA2ODk1MjM3MjF9.8KYvvJV0AwR-0cC5PoEgZdn3FqjmTGuSnmUfKzuhgc_3_j-27lD0sofGEEE41MuZmPAYytCEksZzyux8qaBGAA

4. Verify it against the published JWKS — the kid in the JWS header (d0481df4…) resolves in https://api.algovoi.co.uk/.well-known/jwks.json:

import json, base64, urllib.request
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
b64u = lambda s: base64.urlsafe_b64decode(s + "="*(-len(s)%4))
h, p, sig = JWS.split(".")
kid = json.loads(b64u(h))["kid"]
jwks = json.load(urllib.request.urlopen("https://api.algovoi.co.uk/.well-known/jwks.json"))
key = next(k for k in jwks["keys"] if k["kid"] == kid)
Ed25519PublicKey.from_public_bytes(b64u(key["x"])).verify(b64u(sig), (h+"."+p).encode())
# no exception -> valid

This is exactly the receipt_verification block advertised on the agent card (format: JWS, algorithms: ["EdDSA"], the jwks_uri). The action itself is separately bound: action_ref = SHA-256(JCS({agent_id, action_type, scope, timestamp_ms})), returned alongside as f59966941c19…, which is the request_digest referenced in receipt_binds.

Hope this is a useful concrete reference for the example.


AlgoVoi (chopmob-cloud) -- Acquisition enquiries: https://docs.algovoi.co.uk/acquisition

@rpelevin

rpelevin commented Jun 6, 2026

Copy link
Copy Markdown

I would keep the Agent Card example generic, but make the profile contract slightly sharper.

The card can advertise that receipt metadata exists. The profile should define how a verifier reaches the same answer. I would make the example say that receipt_profile_uri points to rules for:

  • closed outcome vocabulary
  • canonical request digest construction
  • signed vs referenced fields
  • freshness and replay handling
  • verifier key discovery
  • profile versioning

The main thing to avoid is letting the Agent Card imply more than it proves. The card can say where receipt verification metadata lives; it should not imply that every action is authorized, that every interaction requires a receipt, or that the profile's domain vocabulary is part of A2A core.

One small docs addition that would help: a "what this extension does not prove" note. For example:

  • it does not prove the action was executed
  • it does not prove the external system accepted the outcome
  • it does not make the outcome vocabulary normative for all A2A agents
  • it does not expose private policy basis in the Agent Card

That would preserve the useful discovery layer while keeping authority and verification in the profile.

@dinpd dinpd force-pushed the docs/authorization-receipt-extension-example branch from 4c5af92 to cb81020 Compare June 6, 2026 16:02
@chopmob-cloud

Copy link
Copy Markdown

Useful example, and keeping the outcome vocabulary and canonical digest construction in the referenced profile rather than the Agent Card is the right split.

For a concrete, resolvable reference: AlgoVoi runs this same extension in production, under the same name and slug. The agent card advertises it and the profile URI actually dereferences:

Two production notes that may be worth folding into the guidance:

  1. The example leaves canonicalization as profile-defined. In practice that field is load-bearing: a verifier can only reach the issuer's answer if the profile pins one concrete scheme. Ours pins JCS (RFC 8785) so the signed bytes are reproducible across independent implementations; profile-defined should resolve to a single named canonicalization rather than stay open.

  2. The closed categorical outcome holds up well (we run ALLOW / REFER / DENY) precisely because the basis stays a categorical value or reference and not free text in the signed bytes, as your final paragraph already notes.

Live profile and verifier above if a working reference is useful.

AlgoVoi (chopmob-cloud) -- docs.algovoi.co.uk/substrate-2

@dinpd

dinpd commented Jun 9, 2026

Copy link
Copy Markdown
Author

Thanks, this is useful production evidence. I pushed a small update that folds in the two guidance points while keeping the Agent Card example generic:

  • changed the example from profile-defined to a concrete illustrative canonicalization value, jcs-rfc8785-v1;
  • clarified that the referenced profile should pin one named canonicalization/projection that independent issuers and verifiers can reproduce, rather than leaving canonicalization open at verification time;
  • tightened the basis guidance so sensitive rationale stays categorical or by-reference, not free-form text in signed receipt bytes.

Validation:

  • git diff --check
  • parsed the JSON examples in docs/topics/extensions.md

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Agent Card extension example for authority contract metadata

3 participants