---
title: "@treeship/a2a"
description: Drop-in Treeship attestation for A2A (Agent2Agent) servers and clients.
---

import { Callout } from 'fumadocs-ui/components/callout';

A2A solves *how* agents communicate. It does not solve *what proof exists that they did what they claimed*. The `@treeship/a2a` package fills that gap. Every A2A task receipt, completion, and handoff becomes a signed Treeship artifact, and every outbound A2A artifact carries a receipt URL the receiving agent can verify.

> A2A makes agents interoperable. Treeship makes that interoperability trustworthy and auditable.

## Install

```bash
npm install @treeship/a2a
```

The package is framework-agnostic, it does not import any specific A2A SDK. You wire its hooks into whichever A2A server you already run.

## What gets attested

| Phase | Artifact created |
|-------|------------------|
| Task arrives | **Intent**, who sent it, which skill, A2A task and message IDs |
| Task completes | **Receipt** chained to the intent, elapsed time, status, artifact digest |
| Outbound artifact | `treeship_artifact_id` and `treeship_receipt_url` injected into metadata |
| Handoff to another A2A agent | Signed handoff, from-agent, to-agent, context |
| AgentCard at `/.well-known/agent.json` | A `treeship.dev/extensions/attestation/v1` extension publishing your ship ID |

## Publish a Treeship-attested AgentCard

```ts
import { buildAgentCard } from '@treeship/a2a';

app.get('/.well-known/agent.json', (_req, res) => {
  res.json(
    buildAgentCard(
      {
        name: 'OpenClaw Research Agent',
        version: '1.2.0',
        url: 'https://openclaw.example/a2a',
        capabilities: { streaming: true, pushNotifications: true },
        skills: [
          { id: 'web-research', name: 'Web Research' },
        ],
      },
      {
        ship_id: process.env.TREESHIP_SHIP_ID!,
        verification_key: 'ed25519:abc123...',
      },
    ),
  );
});
```

The output AgentCard contains the canonical Treeship extension:

```json
{
  "extensions": [
    {
      "uri": "treeship.dev/extensions/attestation/v1",
      "required": false,
      "params": {
        "ship_id": "shp_4a9f2c1d",
        "receipt_endpoint": "https://treeship.dev/receipt"
      }
    }
  ]
}
```

## Wrap your task handler

```ts
import { TreeshipA2AMiddleware } from '@treeship/a2a';

const treeship = new TreeshipA2AMiddleware({
  shipId: process.env.TREESHIP_SHIP_ID!,
});

app.post('/a2a/tasks', async (req, res) => {
  const { taskId, skill, from, messageId } = req.body;

  await treeship.onTaskReceived({ taskId, skill, fromAgent: from, messageId });

  const start = Date.now();
  let status: 'completed' | 'failed' = 'completed';
  let artifact;
  try {
    artifact = await runMyAgent(req.body);
  } catch (e) {
    status = 'failed';
    throw e;
  } finally {
    const result = await treeship.onTaskCompleted({
      taskId,
      elapsedMs: Date.now() - start,
      status,
      artifactDigest: artifact ? TreeshipA2AMiddleware.digestArtifact(artifact) : undefined,
    });
    if (artifact) artifact = treeship.decorateArtifact(artifact, result);
  }

  res.json(artifact);
});
```

The artifact your peer receives now carries a verifiable trail:

```json
{
  "artifactId": "research-output-001",
  "parts": [{ "kind": "text", "text": "Research findings..." }],
  "metadata": {
    "treeship_artifact_id": "art_7f8e9d0a1b2c3d4e",
    "treeship_receipt_url": "https://treeship.dev/receipt/ssn_01HR9W2D4Q4M7A0C",
    "treeship_session_id": "ssn_01HR9W2D4Q4M7A0C",
    "treeship_ship_id": "shp_4a9f2c1d"
  }
}
```

<Callout type="info">
Only digests and metadata enter the artifact. Raw task content is never stored. The digest proves which data was involved without exposing the data itself.
</Callout>

## Verify a peer before delegating

```ts
import { fetchAgentCard, hasTreeshipExtension, verifyArtifact } from '@treeship/a2a';

const card = await fetchAgentCard('https://partner-agent.example');
if (!hasTreeshipExtension(card)) {
  throw new Error('Refusing to delegate: peer is not Treeship-attested');
}

// ... send your A2A task ... and when the artifact comes back:
const verification = await verifyArtifact(remoteArtifact.metadata);
if (!verification || !verification.withinDeclaredBounds) {
  throw new Error('Peer artifact failed Treeship verification');
}
```

## Record a handoff

```ts
await treeship.onHandoff({
  toAgent: 'agent://openclaw',
  taskId: 'a2a-task-7f8e9d',
  context: 'Research phase delegated: find comparable Merkle MMR implementations',
  messageId: 'msg_abc123',
});
```

This is the same artifact `treeship attest handoff` produces from the CLI, it appears in the parent session's receipt as a delegation boundary.

## Environment variables

| Variable | Effect |
|----------|--------|
| `TREESHIP_DISABLE=1` | Skips all attestation. Hooks return undefined. |
| `TREESHIP_SESSION_ID` | Inherited from `treeship session start`; auto-included in payloads. |
| `TREESHIP_DEBUG=1` | Logs attestation failures to stderr. |

## Design rules

- Treeship errors **never** fail the underlying A2A handler.
- Intent attestation is awaited so the proof exists before the agent runs.
- The middleware has zero runtime dependencies and is framework-agnostic.
- Handoffs and AgentCard extensions are opt-in but on by default.
