Node.js SDK
The Layr8 Node.js SDK is a TypeScript/ESM library for building DIDComm agents. It handles WebSocket connections, Phoenix Channel protocol, DIDComm message formatting, and thread correlation. You write domain logic.
A basic agent is ~30 lines of TypeScript. The SDK manages everything between your handler and the network.
Install
npm install @layr8/sdkRequires Node.js >= 20 and ESM ("type": "module" in package.json).
Core Concepts
Config
import { Layr8Client } from "@layr8/sdk";
const client = new Layr8Client({ nodeUrl: "wss://your-node.node.layr8.io/plugin_socket/websocket", apiKey: "your-api-key", agentDid: "did:web:your-node.node.layr8.io:my-agent",});All fields fall back to environment variables if empty: LAYR8_NODE_URL, LAYR8_API_KEY, LAYR8_AGENT_DID. If agentDid is omitted, the node assigns an ephemeral DID on connect.
Handlers
Register handlers before calling connect. The handler’s return value determines what happens:
import type { Message } from "@layr8/sdk";
client.handle( "https://layr8.io/protocols/echo/1.0/request", async (msg: Message): Promise<Message | null> => { // Return Message → send response to sender // Return null → no response (fire-and-forget) // Throw → send problem report to sender },);The protocol base URI is derived automatically from the message type and registered with the node on connect.
Send (Fire-and-Forget)
await client.send({ type: "https://didcomm.org/basicmessage/2.0/message", to: ["did:web:partner-node:agent"], body: { content: "Hello!" },});send accepts Partial<Message> — only type, to, and body are required.
Request (Request/Response)
const resp = await client.request( { type: "https://layr8.io/protocols/echo/1.0/request", to: ["did:web:partner-node:echo-agent"], body: { message: "ping" }, }, { signal: AbortSignal.timeout(5_000) },);// resp is the correlated response, matched by thread IDMessages
interface Message { id: string; // auto-generated if empty type: string; // DIDComm message type URI from: string; // auto-filled with agent DID to: string[]; // recipient DIDs threadId: string; // auto-generated for request parentThreadId: string; body: unknown; // serialized to JSON context?: MessageContext;}Use unmarshalBody<T>(msg) to deserialize the body with type safety. On inbound messages, msg.context provides node metadata: authorized, recipient, and senderCredentials.
Durable Handlers (Persist-then-Ack)
For messages that must not be lost, use manual ack to persist before acknowledging:
import { ack } from "@layr8/sdk";
client.handle( "https://layr8.io/protocols/order/1.0/created", async (msg: Message): Promise<null> => { // Write to disk first — if this throws, the message is // NOT acked and the cloud-node will redeliver it. appendFileSync("messages.jsonl", JSON.stringify(msg) + "\n");
ack(msg as any); // safe to ack now return null; }, { manualAck: true },);See examples/durable-handler.ts for a complete working example.
Claude Code Skill
The SDK includes a Claude Code skill that teaches Claude the full API. Install it into any agent project:
mkdir -p .claude/skillscp /path/to/node-sdk/.claude/skills/build-layr8-agent-node.md .claude/skills/Once installed, Claude Code knows how to build Layr8 agents in every session. Example prompts:
- “Build me a webhook relay agent that forwards DIDComm messages to my REST API”
- “Add a handler for order.created messages that validates the sender’s credentials”
- “Convert this Express endpoint into a DIDComm agent”
Links
- Node.js SDK on GitHub — Full README, source code, and examples
- Example Agents — Working demo agents from beginner to advanced
- DIDComm Reference — Protocol details and message patterns
- Getting Started — End-to-end setup walkthrough