Documentation
¶
Overview ¶
Package a2a fetches and verifies Agent2Agent (A2A) AgentCards for the remote-agent allowlist (ADR-0024): bounded, SSRF-safe https fetches, the card digest Halos pins into policy and releases, and the card's JWS signatures (A2A specification v1.0, section 8.4: RFC 7515 JWS over the RFC 8785 canonical card without its signatures field).
Index ¶
Constants ¶
const ( MaxCardBytes = 256 << 10 DefaultTimeout = 10 * time.Second )
Limits of a card or JWKS fetch.
Variables ¶
var ( ErrDigestMismatch = errors.New("a2a: AgentCard digest does not match the pinned digest") ErrSignature = errors.New("a2a: AgentCard signature verification failed") ErrBlockedAddress = errors.New("a2a: destination address is not public") )
Errors callers branch on.
Functions ¶
func Canonicalize ¶
Canonicalize returns the RFC 8785 JSON Canonicalization Scheme form of the single JSON value in b: object members sorted by UTF-16 code units, no insignificant whitespace, ECMAScript number and string serialization.
func Digest ¶
Digest is the pin Halos stores for a card: "sha256:<hex>" over its RFC 8785 canonical form, signatures included, so whitespace and member order do not matter but any change of content does.
func VerifySignatures ¶
VerifySignatures checks that at least one of the card's signatures verifies with one of keys. The payload is the RFC 8785 form of the card without its signatures member; a card signed after dropping empty (default-valued) members, as section 8.4.1 asks, verifies too. A jku header is never followed: keys come from policy only.
Types ¶
type AgentCardSignature ¶
type AgentCardSignature struct {
Protected string `json:"protected"`
Signature string `json:"signature"`
Header map[string]any `json:"header,omitempty"`
}
AgentCardSignature is one JWS over the card (A2A section 4.4.7).
type Card ¶
type Card struct {
Name string `json:"name"`
Description string `json:"description"`
Version string `json:"version"`
SupportedInterfaces []Interface `json:"supportedInterfaces"`
URL string `json:"url"` // pre-1.0 cards
SecuritySchemes map[string]any `json:"securitySchemes"`
Skills []Skill `json:"skills"`
Signatures []AgentCardSignature `json:"signatures"`
}
Card is the part of an AgentCard Halos reads. Unknown fields are kept in the raw bytes (the digest and signatures cover the whole card).
func ParseCard ¶
ParseCard decodes a card. It must be one JSON object without duplicate keys. Fields are read by exact key from the same decoded value the digest covers: json.Unmarshal matches keys ignoring case, so a card holding both "skills" and "SKILLS" could show Halos one skill list and an A2A client another.
type Expect ¶
type Expect struct {
Digest string // sha256:<hex>; "" = not checked
PublicKey string // PEM
JWKSURL string
}
Expect is what policy pins for a card.
type Fetcher ¶
type Fetcher struct {
Timeout time.Duration // 0 = DefaultTimeout
// AllowPrivate permits loopback, private, link-local and other
// non-public destinations (an agent on the internal network).
AllowPrivate bool
// RootCAs overrides the system roots (tests, private PKI).
RootCAs *x509.CertPool
}
Fetcher fetches cards and key sets: https only, a size and time limit, at most three redirects and only to the same host, and (unless AllowPrivate) only to public addresses, checked on the address actually dialed so DNS rebinding cannot reach an internal service.
type Interface ¶
type Interface struct {
URL string `json:"url"`
ProtocolBinding string `json:"protocolBinding"`
ProtocolVersion string `json:"protocolVersion"`
}
Interface is one AgentInterface (transport binding and URL).
type Key ¶
type Key struct {
ID string // JWK kid; "" for a pinned PEM key (matches any kid)
Pub crypto.PublicKey
}
Key is a public key that may have signed a card.
type Result ¶
type Result struct {
Card Card `json:"card"`
Digest string `json:"digest"`
// Signed: the card carries signatures; Verified: one verified with the
// pinned key or JWKS (false when none is configured).
Signed bool `json:"signed"`
Verified bool `json:"verified"`
}
Result is a fetched and verified card.