acdp — Node.js SDK

Node.js bindings for ACDP (acdp-node)

Thin NAPI-rs binding over the acdp Rust library. Implements the producer- and consumer-side crypto for the Agent Context Distribution Protocol v0.1.0 (RFC-ACDP-0001/0003/0008). HTTP is intentionally left to the caller — pair this with fetch / undici for transport.

Install (development)

npm install                  # installs @napi-rs/cli
npm run build:debug          # produces index.js + acdp.<platform>.node
node --test tests/           # in-process unit tests, no HTTP

Build a release binary

npm run build                # release mode (LTO + strip)

Quickstart

import { AcdpProducer, AcdpVerifier } from '@agentcontextdistributionprotocol/acdp';

const producer = AcdpProducer.generate(
  'did:web:agents.example.com:my-agent',
  'did:web:agents.example.com:my-agent#key-1',
);

const raw = producer.buildPublishRequest({
  title: 'Q1 snapshot',
  contextType: 'data_snapshot',
  summary: 'Quarter-end inventory',
});

// POST `raw` (the JSON string) to your registry. On retrieve:
const body = (await response.json()).body;
AcdpVerifier.verifyContentHash(JSON.stringify(body), body.content_hash);
AcdpVerifier.verifySignature(
  pubKeyB64,                  // resolved from the producer's did:web doc
  body.signature.value,
  body.content_hash,
);

Design rules

  • JSON across the FFI boundary. Every method accepts and returns JSON strings — never a Rust type, never a JS class instance you'd have to serialize before sending. The binary stays at ~500 lines of glue.
  • Crypto in Rust, HTTP in JS. Key generation, JCS + SHA-256 hashing, Ed25519 signing, and signature verification happen in the underlying acdp crate. Transport, retries, and observability are yours.
  • AcdpProducer stores a 32-byte seed. The Rust SigningKey is ZeroizeOnDrop and not Clone, so the binding rebuilds the signing key from the seed on each call.
  • Golden vector parity. golden content_hash + signature match sig-001 pins the JS-side content_hash and signature.value against the spec's sig-001 fixture — the same constants the Rust suite asserts. A drift on either side is a protocol break.

Layout

bindings/acdp-node/
├── Cargo.toml         # standalone [workspace]; depends on `acdp` via path
├── build.rs           # napi-build setup
├── package.json
├── README.md          # this file
├── index.js           # generated by `napi build`
├── index.d.ts         # generated by `napi build`
├── acdp.<platform>.node  # native binary
├── src/
│   ├── lib.rs         # module entry — re-exports the napi classes
│   ├── producer.rs    # AcdpProducer: build/sign publish requests
│   ├── verifier.rs    # AcdpVerifier: content_hash + signature verify
│   └── helpers.rs     # visibility / contextType parsers
└── tests/
    └── test.mjs