Skip to content

Releases: ThirdKeyAI/AgentPin

v0.3.0 — A2A AgentCard + DNS TXT, four-language parity

14 May 21:37
55821c8

Choose a tag to compare

[0.3.0] - 2026-05-14

Added

Four-language parity (Rust, JavaScript, Python, Go) for the A2A AgentCard
extension surface and DNS TXT cross-verification. Cards signed in any of the
four SDKs verify cleanly in the other three; signature canonicalisation is
byte-identical across implementations.

A2A AgentCard extension types, signed builder, and verifier

  • A2aAgentCard + supporting types — minimal A2A AgentCard subset
    (A2aAgentCard, A2aAgentCapabilities, A2aAgentSkill) plus the
    AgentPin-specific AgentpinExtension payload (agentpin_endpoint,
    public_key_jwk, signature). Inline definition rather than depending on
    the upstream a2a-types crate while the A2A spec is still draft.
  • A2aAgentCardBuilder (Rust) / buildAndSignAgentCard (JS) /
    build_and_sign_agent_card (Python) / a2a.BuildAndSignAgentCard (Go) —
    turns an AgentDeclaration into a signed A2aAgentCard. Maps capabilities
    to skills via capability_to_skill; propagates allowed_domains from the
    source constraints into A2aAgentCapabilities.allowed_domains. Detached
    ECDSA P-256 signature covers the canonical bytes of the AgentCard with the
    extension cleared.
  • verify_agentpin_extension(card) — verifies the AgentPin extension
    signature against the JWK embedded in the extension. Sorted-key canonical
    JSON shared across all four SDKs; mirrors the canonicalisation pattern
    used by SchemaPin.
  • AllowedDomains typed wrapper — extracted from
    Constraints::allowed_domains and exposed for cross-protocol use
    (SchemaPin v1.4's A2aVerificationContext scopes tool verification to the
    intersection of caller and provider domains). Empty list = no restriction
    (all domains trusted) by convention; intersect(unrestricted, X) = X.
  • a2a_endpoint field on DiscoveryDocument — optional URL of the
    entity's A2A AgentCard endpoint, enabling cross-protocol discovery.

Discovery resolvers for A2A AgentCards

  • LocalAgentCardStore (all SDKs) — in-memory store of pre-registered
    AgentCards keyed by their AgentPin discovery domain. Verifies the
    AgentPin extension signature at registration time and pre-derives a
    DiscoveryDocument so the rest of the AgentPin verification stack runs
    unchanged. Supports Symbiont's push-based external-agent registration
    flow where the coordinator receives AgentCard JSON inline rather than
    fetching it from a .well-known endpoint.
  • A2aAgentCardResolver (all SDKs; gated on fetch in Rust) — fetches
    https://{domain}/.well-known/agent-card.json, verifies the AgentPin
    extension, cross-checks that the embedded agentpin_endpoint host
    matches the fetched domain, and derives a DiscoveryDocument. Exposes
    the original A2A representation alongside the derived doc for callers
    that want both.

DNS TXT cross-verification at _agentpin.{domain}

  • dns module (all SDKs) — parse_txt_record, verify_dns_match,
    txt_record_name. Wire format mirrors SchemaPin's _schemapin.{domain}
    record with the version tag changed:
    "v=agentpin1; kid=...; fp=sha256:<hex>". Whitespace-tolerant parser,
    case-insensitive on fp, ignores unknown fields for forward
    compatibility.
  • fetch_dns_txt(domain) — Rust: async lookup behind the new dns
    Cargo feature (hickory-resolver, tokio, async-trait). JavaScript:
    uses Node's built-in dns/promises. Python: uses the optional
    dnspython package. Go: dns.LookupTxt(ctx, resolver, domain) over the
    standard-library net.Resolver.
  • Multi-key match semantics — AgentPin discovery docs may carry several
    keys for rotation; a published TXT record need only match one of them.
    When the TXT carries an explicit kid, the matching key MUST also carry
    the same kid.
  • Fail-closed on mismatch — a publisher who intentionally publishes a
    TXT record has signaled DNS is part of their trust chain. Divergence
    between DNS and .well-known indicates compromise of one channel and is
    treated as a hard failure.

Go SDK (fourth-language port)

  • Initial go/ SDK — wire-compatible with Rust, JavaScript, and Python.
    Mirrors the package layout of the SchemaPin Go SDK. Module path:
    github.com/ThirdKeyAi/agentpin/go.
  • Packages: crypto, jwk, jwt, types, discovery, credential,
    verification, revocation, pinning, delegation, mutual, nonce,
    bundle, resolver, plus the new a2a and dns packages added in this
    release.
  • CLI: cmd/agentpin with keygen, issue, verify, bundle
    subcommands matching the Rust binary.
  • ES256-only enforcement implemented inline using crypto/ecdsa. The
    JWT verifier rejects none, HS256, RS256, ES384, and any other
    algorithm before any signature work. No third-party JWT dependency.
  • CI: .github/workflows/go.yml runs go test, go vet, and
    gofmt -l on every PR touching go/**. The version-consistency check
    in .github/workflows/release.yml validates the Go SDK's declared
    version against the Rust/JavaScript/Python versions.

Changed

  • Cross-SDK version coordination — Rust, JavaScript, Python, and Go SDKs
    all release as 0.3.0 together. The earlier 0.3.0-alpha.1 Rust
    preview is superseded by this entry.
  • Rust MSRV bumped from 1.70 to 1.86. Downstream ecosystem crates
    (getrandom, clap_builder, the icu_* family) have moved to edition
    2024 and/or to declared rust-version 1.86, making the previously-
    declared 1.70 floor unbuildable from scratch in practice. The CI
    matrix's MSRV row now tests against 1.86.

Notes

  • This release is the unblock for Symbiont v1.8.0 Phase 3 (AgentPin-
    verified AgentCards, A2A auth middleware) and SchemaPin v1.4.0's
    A2aVerificationContext (which consumes AllowedDomains for tool-
    verification scoping). Both downstream releases were waiting on this
    surface.
  • DNS TXT defends against HTTPS-origin compromise (compromised hosting
    account, expired domain not removed from CDN, ACME ownership-validation
    bypass) and TLS cert mis-issuance — the DNS credential chain (registrar,
    DNS provider, optionally DNSSEC) is independent of the HTTPS hosting
    chain. Spec § 4.8.3 reserved this slot in v0.1; this release ships the
    implementation across all SDKs.
  • All additions are purely additive — v0.2.0 callers are unaffected.
    Discovery documents without a2a_endpoint, AgentCards without an
    agentpin extension, and absent _agentpin TXT records all behave
    exactly as before.