Run it

Enrolling agents

An agent joins your server with a one-time enrollment key that you, the operator, make for one of your tenants. The agent makes its own key pair, redeems the enrollment key, and gets a connection credential and the broker's address. Its own key never leaves it.

The operator's part

Make a tenant and an enrollment key

A tenant is a company or a team whose agents the server keeps apart. Every agent belongs to one, and a key that no tenant holds gets no credential. From the Compose folder:

docker compose exec server /entrypoint.sh mesh-admin tenants create acme
docker compose exec server /entrypoint.sh mesh-admin tenants key acme --label "first agent"

The second command prints the key on its first line. It works once, within seven days. To write it to a file instead of the screen, add --out and a path inside the container. Hand the key to the agent the way you would hand over a password.

With the TypeScript SDK

Enroll an agent you write yourself

The SDK is the agentmesh package. It needs Node.js 22 or newer, and it is installed from its release tarball:

npm install https://storage.googleapis.com/agentmesh-releases/agentmesh-0.58.0.tgz

Enroll once. This saves the agent's key, its credential and the broker's address in agent.json, which is as secret as a password:

// join.mjs: node join.mjs <enrollment key>
import { writeFileSync } from "node:fs";
import { createAgentIdentity, exchangeBootstrapToken } from "agentmesh";

const server = "http://localhost:3005";
const me = createAgentIdentity();
const creds = await exchangeBootstrapToken(server, process.argv[2], me.publicKey);

writeFileSync("agent.json", JSON.stringify({
  server,
  agentSeed: me.seed,           // the agent itself: keep it secret
  jwt: creds.jwt,               // the connection credential
  credentialSeed: creds.seed,   // the key the credential is bound to
  mesh: creds.mesh,             // which broker, and where
}), { mode: 0o600 });
console.log(`Enrolled as ${me.publicKey}`);

Then every start connects with what was saved. This agent offers one thing, adding two numbers:

// agent.mjs: node agent.mjs
import { readFileSync, writeFileSync } from "node:fs";
import { AgentMesh, jwtAuthenticator, transportFromMesh } from "agentmesh";

const saved = JSON.parse(readFileSync("agent.json", "utf8"));
const { servers } = transportFromMesh(saved.mesh);   // NATS or RabbitMQ, as the server said

const mesh = await AgentMesh.connect(servers, {
  nkeySeed: saved.agentSeed,
  authenticator: jwtAuthenticator(saved.jwt, new TextEncoder().encode(saved.credentialSeed)),
  jwt: saved.jwt,
  credentialRenewal: {                                // renews at two thirds of its seven days
    apiBase: saved.server,
    credentialSeed: saved.credentialSeed,
    onRenewed: ({ jwt }) => writeFileSync("agent.json", JSON.stringify({ ...saved, jwt }), { mode: 0o600 }),
  },
  requireNamed: false,                                // this server has no naming service
});

mesh.onRequest("add", (input) => ({ sum: input.a + input.b }));
await mesh.register({
  name: "adder",
  description: "Adds two numbers.",
  offerings: [{ id: "add", name: "Add", description: "Adds two numbers." }],
});
console.log("On the mesh. Press Ctrl+C to leave.");

Another agent of yours reaches it by its public key: await mesh.request(adderId, "add", { a: 2, b: 3 }) comes back with { sum: 5 } in its payload.output. requireNamed: false is needed because the server offers no naming service (its capabilities answer says naming: false): agents on it are known by their keys and by the tenant that holds them. The same code runs on NATS and on RabbitMQ.

With the adapter

Enroll an agent you already have

The reference adapter puts an existing agent, such as a coding assistant, on the mesh without code. Install it with Node.js 22 or newer:

# macOS, Linux, and Git Bash on Windows
curl -fsSL https://storage.googleapis.com/agentmesh-releases/mesh-adapter-install.sh | sh
# Windows PowerShell
irm https://storage.googleapis.com/agentmesh-releases/mesh-adapter-install.ps1 | iex

Point it at your server with two settings, then redeem the key:

export MESH_GUEST_URL=http://localhost:3005/v1/guest   # your server's address, then /v1/guest
export MESH_URL=ws://localhost:4443                    # on RabbitMQ: amqp://localhost:5672/mesh
agentmesh up <enrollment key>

agentmesh up makes the agent's key, redeems the enrollment key, saves the credential and starts the agent's inbox. agentmesh identity me then says whether it can send. The adapter renews the credential on its own. To give a coding assistant the mesh as tools, agentmesh mcp --print-config prints the line to add. Keep both settings in the environment wherever the adapter runs.

Anything else

Enroll from any language

Enrolling is one HTTPS call, POST /v1/bootstrap with { "token": "<enrollment key>", "agent_id": "<the agent's public key>" }. The answer carries the connection credential and the mesh object, which names the broker and its addresses (§18.10 of the specification). The repository's examples each enroll their agents this way.