Getting Started
Go from zero to your first cross-organization message. You’ll provision a free Layr8 node, write a simple agent that sends a message to the echo agent in the developer sandbox, and see the response.
What You’ll Build
Your Environment Layr8 Developer Sandbox┌──────────────────────────┐ ┌──────────────────────────────┐│ │ │ ││ ┌────────────────────┐ │ │ ┌────────────────────────┐ ││ │ Your Agent │ │ DIDComm │ │ Sandbox Echo Agent │ ││ │ (~30 lines) │──┼──────────────►│ │ Echoes your messages │ ││ └────────┬───────────┘ │ │ └────────────┬───────────┘ ││ │ │ │ │ ││ ┌────────┴───────────┐ │ │ ┌────────────┴───────────┐ ││ │ Your Node │ │ encrypted │ │ Sandbox Node │ ││ │ (free, managed) │◄─┼──────────────►│ │ (alice-sandbox │ ││ │ *.layr8.cloud │ │ │ │ .layr8.cloud) │ ││ └────────────────────┘ │ │ └────────────────────────┘ ││ │ │ │└──────────────────────────┘ └──────────────────────────────┘Your agent connects to your node via WebSocket. Your node handles encryption, identity resolution, and policy enforcement, then routes messages to the sandbox node — which delivers them to the echo agent. The echo agent responds, and the reply follows the same path back. Your agent never talks directly to the sandbox; the nodes handle everything.
The Developer Sandbox
Layr8 runs a developer sandbox — a set of hosted nodes on *.layr8.cloud with agents you can interact with while building and testing your own:
alice-sandbox.layr8.cloud— the requester-side node, hosting agents that initiate cross-org requests (PostgreSQL proxy, REST proxy)bob-sandbox.layr8.cloud— the resource-side node, hosting agents that serve data and respond to requests- Echo agent (
did:web:alice-sandbox.layr8.cloud:echo) — the simplest sandbox agent: send it a message, get the same message back
The echo agent is useful for verifying your setup end-to-end before integrating with a real partner. The postgres and REST agents demonstrate more complex patterns — see Example Agents for details.
Prerequisites
- Go 1.21+ — Install Go
- A terminal
- Node.js 20+ — Install Node.js
- A terminal
- Python 3.11+ — Install Python
- A terminal
Step 1: Create Your Free Node
- Go to portal.layr8.io and create an account
- Create an organization
- Provision a free node — this takes about a minute
- When the node is ready, click the Console button to open your node’s admin console
Your node is your organization’s messaging endpoint. It handles encryption, identity resolution, and message routing.
Step 2: Configure Your Node
From the admin console:
- Create an API key — your agents use this key to connect to your node. You can create multiple keys for different agents or environments. Never share your API key with another person or organization — it’s how your agent establishes trust with your node.
- Add DIDs to the allow list — your node only accepts messages from DIDs on its allow list. Add the DIDs of any agents or partners you want to communicate with. You can use a trailing wildcard to allow an entire domain (e.g.,
did:web:alice-sandbox.layr8.cloud:*). For this tutorial, adddid:web:alice-sandbox.layr8.cloud:echoso you can message the sandbox echo agent. - Note your node URL from the console — you’ll need it to connect your agent.
Step 3: Set Up Your Project
export GOPRIVATE=github.com/layr8/*mkdir my-first-agent && cd my-first-agentgo mod init my-first-agentgo get github.com/layr8/go-sdkThe SDK is in a private repository. GOPRIVATE tells Go to skip the public module proxy. You also need Git configured for SSH access to GitHub — see Troubleshooting if you get authentication errors.
mkdir my-first-agent && cd my-first-agentnpm init -ynpm install github:layr8/node-sdkAdd "type": "module" to your package.json to enable ESM imports.
mkdir my-first-agent && cd my-first-agentpython3 -m venv .venvsource .venv/bin/activatepip install git+https://github.com/layr8/python-sdk.gitStep 4: Write Your First Agent
Create main.go:
package main
import ( "context" "fmt" "log" "os" "os/signal" "time"
layr8 "github.com/layr8/go-sdk")
type EchoResponse struct { Echo string `json:"echo"`}
func main() { client, err := layr8.NewClient(layr8.Config{}) if err != nil { log.Fatal(err) }
// Register as an echo protocol speaker — required to connect client.Handle("https://layr8.io/protocols/echo/1.0/request", func(msg *layr8.Message) (*layr8.Message, error) { log.Printf("received echo request: %v", msg.Body) return &layr8.Message{ Type: "https://layr8.io/protocols/echo/1.0/response", Body: msg.Body, }, nil }, )
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt) defer stop()
if err := client.Connect(ctx); err != nil { log.Fatal(err) } defer client.Close()
log.Printf("agent running as %s", client.DID())
// Send a message to the sandbox echo agent reqCtx, cancel := context.WithTimeout(ctx, 15*time.Second) defer cancel()
resp, err := client.Request(reqCtx, &layr8.Message{ Type: "https://layr8.io/protocols/echo/1.0/request", To: []string{"did:web:alice-sandbox.layr8.cloud:echo"}, Body: map[string]string{"message": "hello from my first agent!"}, }) if err != nil { log.Fatal(err) }
var echo EchoResponse if err := resp.UnmarshalBody(&echo); err != nil { log.Fatal(err) } fmt.Printf("Echo replied: %s\n", echo.Echo)
// Keep running — press Ctrl+C to exit log.Println("listening for messages (Ctrl+C to exit)") <-ctx.Done()}Create index.mjs:
import { Layr8Client } from "@layr8/sdk";
const client = new Layr8Client();
// Register as an echo protocol speaker — required to connectclient.handle( "https://layr8.io/protocols/echo/1.0/request", async (msg) => { console.log("received echo request:", msg.body); return { type: "https://layr8.io/protocols/echo/1.0/response", body: msg.body, }; },);
await client.connect();console.log(`agent running as ${client.did}`);
// Send a message to the sandbox echo agentconst resp = await client.request( { type: "https://layr8.io/protocols/echo/1.0/request", to: ["did:web:alice-sandbox.layr8.cloud:echo"], body: { message: "hello from my first agent!" }, }, { signal: AbortSignal.timeout(15_000) },);
console.log("Echo replied:", resp.body);
// Keep running — press Ctrl+C to exitconsole.log("listening for messages (Ctrl+C to exit)");process.on("SIGINT", () => { client.close(); process.exit(0);});Create main.py:
import asynciofrom layr8 import Client, Config, Message
client = Client(Config())
# Register as an echo protocol speaker — required to connect@client.handle("https://layr8.io/protocols/echo/1.0/request")async def echo(msg: Message) -> Message | None: print(f"received echo request: {msg.body}") return Message( type="https://layr8.io/protocols/echo/1.0/response", body=msg.body, )
async def main(): async with client: print(f"agent running as {client.did}")
# Send a message to the sandbox echo agent resp = await client.request( Message( type="https://layr8.io/protocols/echo/1.0/request", to=["did:web:alice-sandbox.layr8.cloud:echo"], body={"message": "hello from my first agent!"}, ), timeout=15.0, ) print(f"Echo replied: {resp.body}")
# Keep running — press Ctrl+C to exit print("listening for messages (Ctrl+C to exit)") await asyncio.Event().wait()
asyncio.run(main())What each part does:
- Create a client — config falls back to environment variables (
LAYR8_NODE_URL,LAYR8_API_KEY,LAYR8_AGENT_DID) - Register a handler — declares which protocol your agent speaks. The server requires at least one. Here, your agent handles echo requests (and can respond to them too).
- Connect — opens the WebSocket connection to your node
- Send a request — sends a DIDComm message to the sandbox echo agent and waits for the response
- Stay running — after sending, the agent keeps listening for incoming messages until you press Ctrl+C
For language-specific details, see the Go SDK, Node.js SDK, or Python SDK reference.
Step 5: Run Your Agent
Set your environment variables and run:
export LAYR8_NODE_URL="wss://your-node.layr8.cloud/plugin_socket/websocket"export LAYR8_API_KEY="your-api-key-from-console"export LAYR8_AGENT_DID="did:web:your-node.layr8.cloud:my-agent"LAYR8_AGENT_DID is optional — if you omit it, your node assigns a temporary DID that changes on each connect. Set it to get a stable, addressable identity that other agents can message.
go run .node index.mjspython main.pyYou should see:
agent running as did:web:your-node.layr8.cloud:my-agentEcho replied: hello from my first agent!listening for messages (Ctrl+C to exit)What Just Happened
Your agent just communicated across organizational boundaries — from your node to the developer sandbox — using identity, not URLs:
- Identity-based addressing — messages addressed by DID, not endpoint URLs
- Node-to-node encryption — messages encrypted between nodes using authenticated encryption
- No shared secrets — no API keys exchanged between organizations
- Decentralized — no central broker or authority in the middle
This is coordination at Layer 8. Read How Layr8 Works for the full picture.
Alternative: Use Claude Code to Write Your Agent
If you use Claude Code, each SDK includes a skill that teaches Claude the full Layr8 API. Install it once and Claude can build agents for you:
# Clone the SDK repo (if you haven't already)git clone https://github.com/layr8/go-sdk.git /tmp/layr8-go-sdk
# Copy the skill into your projectmkdir -p .claude/skillscp /tmp/layr8-go-sdk/.claude/skills/build-layr8-agent.md .claude/skills/# Clone the SDK repo (if you haven't already)git clone https://github.com/layr8/node-sdk.git /tmp/layr8-node-sdk
# Copy the skill into your projectmkdir -p .claude/skillscp /tmp/layr8-node-sdk/.claude/skills/build-layr8-agent-node.md .claude/skills/# Clone the SDK repo (if you haven't already)git clone https://github.com/layr8/python-sdk.git /tmp/layr8-python-sdk
# Copy the skill into your projectmkdir -p .claude/skillscp /tmp/layr8-python-sdk/.claude/skills/build-layr8-agent-python.md .claude/skills/Then describe what you want:
“Build me an echo agent that responds to messages with the same content.”
Claude writes a complete, working agent — config, handler registration, connection management, and graceful shutdown.
Troubleshooting
connection error: join rejected: error
Check your LAYR8_NODE_URL and LAYR8_API_KEY. The most common cause is an incorrect API key or a malformed WebSocket URL. The URL should look like wss://your-node.layr8.cloud/plugin_socket/websocket.
protocols_already_bound
Another agent is already connected with the same DID. Each DID can only have one active connection. Either close the other agent or use a different LAYR8_AGENT_DID.
Messages sent but no response (timeout)
Check your node’s allow list in the admin console. Your node only delivers messages from DIDs on its allow list. For the echo agent, add did:web:alice-sandbox.layr8.cloud:echo (or did:web:alice-sandbox.layr8.cloud:* for all sandbox agents).
Python: SSLCertVerificationError on macOS
macOS Python installations sometimes lack root certificates. Fix with:
pip install certifiexport SSL_CERT_FILE=$(python -c "import certifi; print(certifi.where())")Go: could not read Username for 'https://github.com'
The Go SDK is in a private repository. Set GOPRIVATE and ensure Git is configured for SSH access to GitHub:
export GOPRIVATE=github.com/layr8/*git config --global url."git@github.com:".insteadOf "https://github.com/"For more on error handling in DIDComm, see Problem Reports.
Next Steps
- Example Agents — Echo, Postgres, REST proxy, IoT — working code to learn from
- SDK Reference — Go · Node.js · Python — Core concepts, patterns, and API details
- Architecture — How a Layr8 Node fits into your infrastructure
- How Layr8 Works — Core concepts and the paradigm shift