StackA2A
orchestrationtypescript

Snap Protocol

35

by WilliamPenrose

SNAP - Signed Network Agent Protocol. Secure agent-to-agent (A2A) communication with cryptographic identity, built on Bitcoin Schnorr signatures and Nostr.

1 starsUpdated 2026-02-11MIT
Quality Score35/100
Community
7
Freshness
81
Official
30
Skills
10
Protocol
30
🔒 Security
20

Getting Started

1Clone the repository
$ git clone https://github.com/WilliamPenrose/snap-protocol
2Navigate to the project
$ cd snap-protocol
3Install dependencies
$ npm install
4Run the agent
$ npm start

README

SNAP: Signed Network Agent Protocol

License: MIT Spec: CC BY 4.0

AI agents authenticate themselves with a keypair instead of API keys.

Agent generates a key. Agent signs its requests. Service verifies the signature. Done. No API keys to provision. No OAuth to configure. No CA to run.

Traditional:  Agent gets credentials from a trusted 3rd party (CA, IdP, admin) → service trusts the 3rd party
SNAP:         Agent generates its own keypair → signs requests → service verifies directly (no 3rd party needed)

How it works

POST /api/tasks
{
  "from": "bc1pabc123...",
  "method": "service/call",
  "payload": { "name": "query_database", "arguments": { "sql": "SELECT 1" } },
  "timestamp": 1770163200,
  "sig": "e5b7a9c3..."
}

The service verifies the Schnorr signature and checks from against an allowlist of public keys. No shared secret ever exists.

Why is this better than API keys? Public keys are not secrets. You can store allowlists in plaintext, commit them to git, even publish them. Leaking an allowlist has no cryptographic impact. Leaking an API key is a security incident. See Design Decisions for the full trade-off analysis.

Does this require Bitcoin? No. SNAP uses Bitcoin's cryptographic standards (Schnorr signatures, bech32m encoding) but has zero dependency on the Bitcoin network. No blockchain, no transactions. Details →

Quick start

npm install @snap-protocol/core

Sign a request (agent side):

import { randomBytes, randomUUID } from 'crypto';
import { KeyManager, MessageBuilder, MessageSigner } from '@snap-protocol/core';

const privateKey = randomBytes(32).toString('hex');
const { address } = KeyManager.deriveKeyPair(privateKey);
const signer = new MessageSigner(privateKey);

const signed = signer.sign(
  new MessageBuilder()
    .id(randomUUID())
    .from(address)
    .method('service/call')
    .payload({ name: 'query_database', arguments: { sql: 'SELECT 1' } })
    .timestamp(Math.floor(Date.now() / 1000))
    .build()
);

Verify (service side):

import { MessageValidator } from '@snap-protocol/core';

MessageValidator.validate(signed);   // throws if signature/timestamp invalid

const allowlist = ['bc1p...agent1', 'bc1p...agent2'];
if (!allowlist.includes(signed.from)) throw new Error('Not authorized');
// See security-practices.md for rate limiting and replay protection

That's Auth. For the full agent-to-agent experience (discovery, tasks, streaming), see the Tutorial.

Beyond auth: three independent layers

SNAP's core is authentication. Discovery and communication are optional layers you add when you need them:

┌─────────────────────────────────────────────┐
│  Communication (optional)                   │
│  message/send · tasks/* · streaming         │
├─────────────────────────────────────────────┤
│  Discovery (optional)                       │
│  Agent Card · Nostr relays · HTTP well-known│
├─────────────────────────────────────────────┤
│  Auth (core)                                │
│  Keypair identity · Schnorr signatures      │
│  Timestamp freshness · Replay protection    │
└─────────────────────────────────────────────┘

Auth (core) — Every agent has a cryptographic identity (P2TR address). Every message is signed. Services verify the signature and check an allowlist. This layer works standalone via service/call.

Discovery (optional) — Agents publish capability cards to Nostr relays or HTTP well-known endpoints. Other agents query by skill, name, or identity.

Communication (optional) — Structured methods (message/send, tasks/*), task lifecycle, and streaming over HTTP, WebSocket, or Nostr. Concepts inspired by A2A.

Use as much as you need. Auth alone replaces API keys. Add Discovery to find agents. Add Communication for full agent-to-agent collaboration.

More examples

Agent-to-agent — discover and collaborate

An agent publishes its card to Nostr:

{
  "kind": 31337,
  "tags": [
    ["d", "bc1p5d7rjq7g6rdk2yhzqnt9dp8wvscrplqk0z..."],
    ["name", "Code Assistant"],
    ["skill", "code-generation", "Code Generation"],
    ["endpoint", "http", "https://agent.example.com/snap"]
  ],
  "content": "{\"name\":\"Code Assistant\",\"identity\":\"bc1p5d7rjq7g6rdk2...\",\"endpoints\":[...],\"skills\":[...]}"
}

Another agent finds it and sends a request:

{
  "id": "msg-001",
  "from": "bc1pabc123...",
  "to": "bc1p5d7rjq7g6rdk2yhzqnt9dp8wvscrplqk0z...",
  "type": "request",
  "method": "message/send",
  "payload": {
    "message": {
      "messageId": "inner-001",
      "role": "user",
      "parts": [{ "text": "Write a login form in React" }]
    }
  },
  "timestamp": 1770163200,
  "sig": "a1b2c3d4..."
}

That's it. No API keys. No OAuth. No central registry.

Documentation

Start here:

Document Description
Tutorial Build your first SNAP agent (10 min)
Use Cases Why agents need self-sovereign identity
Core Concepts Identity, discovery, and authentication basics
Design Decisions Why SNAP chose X over Y (mTLS, JWT, DID, etc.)

Specification:

Document Description
Agent Card How agents describe themselves
Messages Request/response format and semantics
Transport HTTP, WebSocket, and Nostr transport
Authentication Schnorr signature authentication
Discovery Finding agents on Nostr
Constraints Field validation rules and limits
Errors Error codes and handling
Security Practices Implementation security guidance

For AI-assisted development:

Resource Description
Agent Skills Skills for AI coding agents (Claude Code, Cursor, Codex, etc.)
llms.txt Documentation index for MCP tools (Context7, mcpdoc)

Install skills: npx skills add WilliamPenrose/snap-protocol — see skills setup guide for IDE integration.

Relationship to other protocols

MCP defines how agents discover and call tools. SNAP adds an authentication layer on top — wrap any MCP tool call in a SNAP service/call envelope and the service can verify the caller's identity via signature instead of API keys.

A2A — SNAP is inspired by Google's A2A Protocol and adopts similar semantic concepts (Task, Message, AgentCard) but uses a different wire format with built-in Schnorr authentication. Not wire-compatible. See Design Decisions for a detailed comparison.

Project Status

🚧 v0.1 Draft — This is an early draft. Expect breaking changes. Feedback welcome!

  • Core message format
  • Identity layer (P2TR)
  • Discovery layer (Nostr + HTTP Well-Known)
  • Authentication (Schnorr)
  • Transport (HTTP/WS/Nostr)
  • TypeScript SDK (implementations/typescript/)
  • Agent-to-Service communication (service/call, optional to)
  • Test suite (510+ unit tests, 31 integration tests)

Contributing

We'd love your input. See CONTRIBUTING.md for guidelines.

License

  • Specification documents (docs/): CC BY 4.0 — see docs/LICENSE
  • Reference implementations (implementations/): MIT
  • Test vectors and schemas: CC0 1.0 (Public Domain)

SNAP is not affiliated with Google or the A2A Protocol project. We simply build on their excellent work.

Capabilities

StreamingPush NotificationsMulti-TurnAuth: none
agent-communicationagent-protocolagent-to-agentai-agentsbip-340bitcoinmulti-agentnostrprotocolschnorr
View on GitHub