Skip to content

Go SDK

The Layr8 Go SDK is a minimal, idiomatic Go library for building DIDComm agents. It handles WebSocket connections, Phoenix Channel protocol, DIDComm message formatting, reconnection with backoff, and thread correlation. You write domain logic.

A basic agent is ~50 lines of Go. The SDK manages everything between your handler and the network.

Install

Terminal window
go get github.com/layr8/go-sdk

Core Concepts

Config

client, err := layr8.NewClient(layr8.Config{
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:

client.Handle("https://layr8.io/protocols/echo/1.0/request",
func(msg *layr8.Message) (*layr8.Message, error) {
// Return (*Message, nil) → send response to sender
// Return (nil, error) → send problem report to sender
// Return (nil, nil) → no response (fire-and-forget)
},
)

The protocol base URI is derived automatically from the message type and registered with the node on connect.

Send (Fire-and-Forget)

err := client.Send(ctx, &layr8.Message{
Type: "https://didcomm.org/basicmessage/2.0/message",
To: []string{"did:web:partner-node:agent"},
Body: map[string]string{"content": "Hello!"},
})

Request (Request/Response)

resp, err := client.Request(ctx, &layr8.Message{
Type: "https://layr8.io/protocols/echo/1.0/request",
To: []string{"did:web:partner-node:echo-agent"},
Body: EchoRequest{Message: "ping"},
})
// resp is the correlated response, matched by thread ID

Messages

type Message struct {
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
Body any // serialized to JSON
Context *MessageContext // populated on inbound messages
}

Use msg.UnmarshalBody(&target) to deserialize the body. 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:

client.Handle("https://layr8.io/protocols/order/1.0/created",
func(msg *layr8.Message) (*layr8.Message, error) {
var body any
msg.UnmarshalBody(&body)
// Write to disk first — if this fails, the message is
// NOT acked and the cloud-node will redeliver it.
if err := persistToDisk(msg.ID, body); err != nil {
return nil, err
}
msg.Ack() // safe to ack now
return nil, nil
},
layr8.WithManualAck(),
)

See examples/durable-handler 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:

Terminal window
mkdir -p .claude/skills
cp /path/to/go-sdk/.claude/skills/build-layr8-agent.md .claude/skills/

Once installed, Claude Code knows how to build Layr8 agents in every session. Example prompts:

  • “Build me a postgres query agent that forwards SQL queries to a partner’s database”
  • “Add a handler for order.created messages that validates the sender’s credentials”
  • “Convert this REST endpoint into a DIDComm agent”