🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@opena2a/a2a-idf

Package Overview
Dependencies
Maintainers
1
Versions
2
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@opena2a/a2a-idf

Reference TypeScript SDK for A2A-IDF (Agent-to-Agent Identity Framework) — sign/verify RFC 9421, resolve keyids, validate verification levels 0/1/2, attestations, and delegation chains.

latest
Source
npmnpm
Version
0.1.0
Version published
Maintainers
1
Created
Source

@opena2a/a2a-idf

Reference TypeScript SDK for A2A-IDF — the Agent-to-Agent Identity Framework specified in a2aproject/A2A#1496.

What this package does

Verifier-side first. Given a signed A2A request (RFC 9421 over Ed25519), this SDK answers four questions:

  • Is the signature valid against the keyid's published public key?
  • Has the request been replayed (stale created, seen nonce)?
  • What verification level applies (0 self-asserted / 1 domain-verified / 2 organization-verified)?
  • If the requesting agent is acting on behalf of someone else, does the delegation chain check out?

Signer-side is supported via a minimal sign() for clients that need to produce request signatures or build test fixtures.

Install

npm install @opena2a/a2a-idf

Runtime dependencies are exactly two: @noble/ed25519 and @noble/hashes. No transitive cryptography deps; nothing pulls in OpenSSL FFI or node-forge.

Quick start

import {
  sign,
  verify,
  resolveKeyid,
  ReplayCache,
} from "@opena2a/a2a-idf";

// 1. Sign an outbound request.
const signed = sign({
  method: "POST",
  path: "/api/task",
  body: new TextEncoder().encode(JSON.stringify({ task: "summarize" })),
  params: {
    keyid: "https://acme.example/keys/agent-1",
    created: Math.floor(Date.now() / 1000),
    nonce: crypto.randomUUID().replace(/-/g, ""),
  },
  privateKey, // 32-byte Ed25519 seed
});

// 2. Verify an inbound request.
const { publicKey } = await resolveKeyid(
  "https://acme.example/keys/agent-1",
);
const replay = new ReplayCache();
const result = verify({
  method: "POST",
  path: "/api/task",
  body: requestBody,
  headers: {
    "content-digest": headers["content-digest"],
    "signature-input": headers["signature-input"],
    signature: headers["signature"],
  },
  publicKey,
  checkNonce: replay.check.bind(replay),
});
if (!result.ok) {
  // result.reason is one of:
  //   missing-headers, malformed-signature-input, malformed-signature,
  //   unsupported-component, content-digest-mismatch, content-digest-missing,
  //   content-digest-unsupported-algorithm,
  //   timestamp-too-old, timestamp-future-skew,
  //   replay-detected, signature-invalid
}

Cross-implementation byte-match

This SDK byte-matches the Envoys §13 test vectors (spec v1.4.0, sha256 5dcc855e…c21b2e9):

VectorMethodPathBodyStatus
1GET/api/healthempty✓ matches XUpjUHt36NbHgAZrQkFY2fSNUR19tgmRlGO1dBhaZDgBv4wb55qgJf2buv3wgnTYwtT+1sH2jzSbcgG6FLGKCA==
2POST/api/tasksummarize JSON✓ matches i5tKcOHKhRTCztR2cazuzNAg9rPiRf47MKTOGve92Rs43gNmltuN5LVScedR6C08MGsQykMc7txJ21KCG8SEBQ==
3POST/api/echo{}✓ matches m2besJKk6Q0MIwFoTENobvvHxFan1fUTv7bzY4EB6OjfIlktqwKa7r/Ab0tDDWFGjQ0CbALgvWGcQfzDr/GeBQ==

See test/rfc9421.test.ts. The keypair is RFC 8032 §7.1 Test 1, fixed for reproducibility across implementations.

Verification levels

import { level1, level2 } from "@opena2a/a2a-idf";

// Level 1 — domain-verified
const l1 = await level1({
  signatureVerified: result.ok,
  domain: "acme.example",
  expectedToken: "https://acme.example/keys/agent-1",
  dns: yourDnsResolver, // implements resolveTxt(name) → { records, ttlSeconds }
});
// l1.warnings includes "dns-ttl-above-cap:<n>s > 300s" when the
// `_a2a-identity` record TTL exceeds the cap.

// Level 2 — organization-verified (Level 1 + trusted attestations)
const l2 = await level2({
  ...l1Inputs,
  attestationArray: agentCard.attestations,
  attestationOpts: {
    resolveIssuerKey: async (issuerKeyid) =>
      (await resolveKeyid(issuerKeyid)).publicKey,
  },
  trustedIssuers: new Set(["https://attestor.example/keys/issuer-1"]),
});

Delegation chains

import { verifyDelegationChain } from "@opena2a/a2a-idf";

const r = await verifyDelegationChain(agentCard.delegationChain, {
  resolveKey: async (keyid) => (await resolveKeyid(keyid)).publicKey,
  nowSeconds: Math.floor(Date.now() / 1000),
  maxDepth: 4,
});
if (r.ok) {
  console.log("effective scope:", r.effectiveScope);
}

Chain rules enforced: first link is a root (signed by the originating delegator); each subsequent link carries the previous link's previousSignature; scope narrows monotonically; expiry never widens; depth ≤ maxDepth; signing keys resolve through the same resolveKey callback.

Dual-shape keyid resolution

resolveKeyid() dispatches on Content-Type:

  • application/did+json → W3C DID Document with an Ed25519 verification method (publicKeyMultibase or publicKeyJwk with crv: "Ed25519").
  • anything else → Envoys §6 compact form { address, public_key: <PEM SPKI> }.

Both shapes return the same 32-byte raw Ed25519 key, so callers can pass it directly to verify({ publicKey }).

What this SDK does not ship at MVP

  • Algorithm agility (Ed25519 only — the spec reserves the field).
  • HTTP transport (consumers pass already-fetched bodies and headers).
  • Key generation / persistence (use a platform keychain or @opena2a/secretless).
  • Browser bundle (Node 24+ only at v0.1).
  • CLI verb wrappers.

These are scoped for v0.2 and onward — see A2A_IDF_CAMPAIGN.md for the trajectory.

Status

v0.1 — MVP. Not yet published to npm. All acceptance criteria from the A2A-IDF SDK pickup doc are met locally; npm publication follows the canonical-suite composition fixture (fixtures/composition/aim-did-rfc9421/signature-alone.json) landing in opena2a-org/a2a-idf-conformance and an opena2a.org/identity page going live.

Contributing

The conformance suite (opena2a-org/a2a-idf-conformance) is the canonical home for cross-implementation byte-match fixtures. SDK PRs that don't pair with a fixture update are still welcome, but cross-impl behavior changes belong in the suite first.

Keywords

a2a

FAQs

Package last updated on 13 May 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts