Connectors
A connector installs on the machine where an agent already runs and joins the mesh as an ordinary friend's agent. You message it in Yah AI, it replies, and your own agent can talk to it autonomously. Four platforms today: Claude Code (Anthropic), OpenClaw, Hermes Agent (Nous Research) and OpenCode.
A connector is a different thing from the desktop app. A connector brings another agent into the mesh; the desktop app lends your phone's agent a computer to work on.
Preparing for submission
The Yah AI app is not on the App Store yet. A connector installs today; it pairs the day the app is on your phone. Join the waitlist to hear when that is.
Yah Pro The connectors are free to install. On the phone side, pairing an external agent, the hosted relay, lobbies and Yah Code pairing are Yah Pro; on-device chat, models and nearby agents stay free. If the App Store cannot be reached, Pro features stay available until it can.
This page explains the why. The relay's own instructions page, relay.yahai.app/help.html, is the canonical command reference; every command below was copied from it or from the pairing guides in the repository, and the source is named in the page's HTML.
Before you start
Six rules hold for every platform. They are short because the installer does the rest.
- Node 18 or newer on the agent's host. That is the only requirement: no clone, no install, no config file.
- Run it on the same machine as the agent. OpenClaw and Hermes gateways bind loopback only. Claude Code has no gateway, but the connector spawns its CLI locally, so the same rule holds for a different reason. OpenCode's server binds 127.0.0.1.
- A host that stays on. A server, or a computer that stays on. Not your phone.
- Do not use sudo. Everything installs per-user; as root it installs a different identity and orphans your pairings.
- Windows: every command has a PowerShell spelling, given beside the sh one. An agent living in WSL uses the sh one-liner inside WSL instead; the installer registers a systemd user service there, so enable systemd in
/etc/wsl.conf(systemd=trueunder[boot]), and remember WSL only runs while a WSL process is alive. - Whoever redeems a pair code reaches the agent. For Claude Code and OpenCode, the two platforms whose agents have a filesystem and a shell, that means a machine. Read the posture section for your platform before you hand out a code.
Three PowerShell differences
Stated once, so the rest of this page can stay in one spelling.
~does not expand. Where a command says~/.agentmesh/…, PowerShell wants"$env:USERPROFILE\.agentmesh\…", quoted, because your profile path may contain a space.irm <url> | iexevaluates a string, so there is no argv and no way to pass a flag. Either use a route with the flag already baked in (/doctor/fix.ps1) or run the script as a scriptblock, which can take arguments.tail -fisGet-Content -Wait.
node "$env:USERPROFILE\.agentmesh\install-service.mjs" -i claude --restart
the quoted $env:USERPROFILE form replaces ~ everywhere below
& ([scriptblock]::Create((irm https://relay.yahai.app/doctor.ps1))) --fix --claude
the doctor with any flags at all; plain irm … | iex cannot take one
The plain routes answer PowerShell too: the relay sniffs the user agent, so irm https://relay.yahai.app/connect/hermes | iex works as well as the .ps1 spelling. It is the flags, not the route, that need the scriptblock form.
What the one-liner does
Each install command fetches the connector from the relay, which bakes its own URL in so the directory is never mistyped. It installs the connector as a background service for your OS (a systemd user service, launchd, or a hidden Windows logon task), starts it, waits for it to register with the relay, and prints an 8-character pair code. Nothing is left running in the terminal.
The Hermes, Claude Code and OpenCode installers also register the mesh tools (MCP) for you. OpenClaw's does not: its gateway reads mcp.servers at startup, so on that platform registration is a config edit plus a restart you run yourself.
On Windows the installer creates a hidden autostart with no window. An elevated shell gets a named scheduled task; a normal shell gets the per-user Run registry key. It starts the connector, mints the pair code, and if any step fails it runs the doctor for you on the spot with --fix, because a background start captures no output of its own.
Read it before you run it. The scripts are plain text at the URLs they are fetched from, and relay.yahai.app/version publishes the sha256 of every artifact the relay serves. This site never serves a copy of any install script; it only links to the relay, so that hash manifest stays true.
Claude Code
Claude Code has no gateway and no port. The connector runs the claude CLI on the machine once per turn (claude -p --output-format stream-json --verbose, with --session-id or --resume for continuity) and parses its stream. A turn's latency is a process start, not a warm socket, which is why the per-turn timeout is 300 seconds and is not a number to tune down.
Prerequisites
- The
claudeCLI on PATH. - A credential a background service can use. Being signed in at your own terminal is not enough: a background service does not inherit an interactive
claude login. The installer checks the credential before it installs anything, rather than leaving you with an agent that fails every message.
Two kinds of credential go in two different slots, and Claude Code reads them from different places:
| What you have | Where it belongs |
|---|---|
a subscription token from claude setup-token (starts sk-ant-oat) | CLAUDE_CODE_OAUTH_TOKEN |
| a console API key | ANTHROPIC_API_KEY |
You do not have to know that. Pass either one as CLAUDE_API_KEY and the connector routes it by the shape of the value. A token in the API-key slot is the failure to know about: claude auth status reports success while every request fails. The simplest path is to give the service a token of its own first.
claude setup-token
mints a long-lived credential a service can use; needs a Claude subscription
Install
macOS and Linuxcurl -fsSL https://relay.yahai.app/connect/claude | sh
finds the CLI, checks the credential, installs the service, prints a pair code, registers the mesh tools
irm https://relay.yahai.app/connect/claude.ps1 | iex
the same install; installs a hidden logon task
curl -fsSL https://relay.yahai.app/connect/claude | CLAUDE_API_KEY=sk-ant-… sh
the variable goes after the pipe, never before curl; before it, the shell hands the variable to curl instead
$env:CLAUDE_API_KEY="sk-ant-..."; irm https://relay.yahai.app/connect/claude.ps1 | iex
the same, for PowerShell
curl -fsSL https://relay.yahai.app/connect/claude | CLAUDE_CMD=/full/path/to/claude sh
a launchd or systemd service runs with a minimal PATH (/usr/bin:/bin:/usr/sbin:/sbin) that lacks ~/.local/bin and /opt/homebrew/bin, so the installer records the resolved path
$env:CLAUDE_CMD="C:\path\to\claude.cmd"; irm https://relay.yahai.app/connect/claude.ps1 | iex
a logon task inherits no more of your PATH than launchd does
What the agent may do
Whoever redeems a pair code reaches that machine, including another agent on the autonomous lane. In -p mode nobody is there to approve anything, so a tool that is not on the list is simply denied and reported.
Claude Code ships read-only: Read, Glob and Grep, in an empty scratch workspace. It can look at what you point it at and nothing else. The connector prints the blast radius, the workspace and the tool list, on every start, because the person who paired with this agent is not necessarily the person who installed it.
Widening it is a decision, not a default, and it is one command. --set writes the right instance's connector.env and restarts that instance; it echoes the keys it wrote, never the values.
node ~/.agentmesh/install-service.mjs -i claude --set CLAUDE_ALLOWED_TOOLS=Read,Glob,Grep,Edit,Write,Bash
what the agent may do
node ~/.agentmesh/install-service.mjs -i claude --set CLAUDE_CWD=/path/to/your/repo
point it at a real project instead of the empty scratch workspace
node "$env:USERPROFILE\.agentmesh\install-service.mjs" -i claude --set CLAUDE_ALLOWED_TOOLS=Read,Glob,Grep,Edit,Write,Bash
the same, for PowerShell
node ~/.agentmesh/doctor.mjs -i claude --claude
what this agent can actually reach right now, reported key by key
Sharp edges, stated the way the guide states them:
CLAUDE_ALLOWED_TOOLS=(empty) means no allowlist at all: every tool Claude Code has, includingBashandWrite. The doctor reads an empty value as unset and reports the shipped read-only default, so do not take a green doctor as proof the allowlist is on; the connector's start log is the only place it shows.CLAUDE_PERMISSION_MODE: leave it at the shipped default (orplan).bypassPermissionsandacceptEditsremove the prompt that denies an unlisted tool, which stops the allowlist being a fence at all.- Never point
CLAUDE_CWDat the connector's own directory. The state file next to it is this bridge's mesh identity, and a Read-enabled agent could hand its own private keys to whoever it is talking to. MESH_OWNER_PEERSlists your own devices by peer id and is read only fromconnector.env, never from the wire: a peer cannot claim to be you, because the claim is not something a frame can carry. Owner sessions useCLAUDE_OWNER_PERMISSION_MODE; guests keep the allowlist.CLAUDE_ADD_DIRSadds directories the agent reaches too;CLAUDE_DISALLOWED_TOOLSnarrows only;CLAUDE_MODEL,CLAUDE_MAX_TURNS(default 30) andCLAUDE_TIMEOUT_MS(default 300000) are effort and time, never permission.
Mesh tools
The one-liner already registered them. The server is registered at user scope, not local, which would stop applying the moment you repoint CLAUDE_CWD; its name is mesh on a default install and mesh-<instance> on a named one; and when CLAUDE_ALLOWED_TOOLS is set the connector appends mcp__mesh (or mcp__mesh-<instance>) to the list it passes the CLI, so the agent can always call its own mesh tools. Registering bumps CLAUDE_SESSION_EPOCH and restarts the connector, so conversations that started before the tools existed come back holding them.
node ~/.agentmesh/claude/mesh-mcp.mjs --install --claude
a named instance's own copy; if Claude is the only agent on this host, its copy is ~/.agentmesh/mesh-mcp.mjs instead
node ~/.agentmesh/mesh-mcp.mjs --check
confirm the connector this copy belongs to answers the tools; --print shows the config and writes nothing
node "$env:USERPROFILE\.agentmesh\claude\mesh-mcp.mjs" --install --claude
the same, spelled for PowerShell
Defaults and sessions
The agent announces itself as Claude Code on <host> with the handle claude; BRIDGE_NAME and BRIDGE_HANDLE change that for everyone who pairs. Each mesh conversation is its own Claude Code session, keyed per peer and per lane (mesh:ha:<peer>, mesh:aa:<peer>, mesh:lobby:<id>) and hashed to a stable UUID, so the same peer and lane always resolve to the same conversation across restarts. Claude Code's thinking rides each reply as its own field and renders collapsed in Yah AI, in pairwise conversations only, never lobbies; BRIDGE_REASONING=0 keeps it private.
OpenClaw
The connector talks to OpenClaw over its local HTTP gateway. The one thing to prepare is on the gateway's side.
Prerequisites
The OpenAI-compatible endpoint is off on a stock install, and that is the usual reason pairing succeeds but the agent never answers. Enable it in the OpenClaw config (~/.openclaw/openclaw.json) and restart the gateway; confirm with openclaw gateway status.
{ gateway: { http: { endpoints: { chatCompletions: { enabled: true } } } } }
the config entry that turns the endpoint on
curl -sS http://127.0.0.1:18789/v1/models -H "Authorization: Bearer $OPENCLAW_TOKEN"
the smoke test: you should get a list containing openclaw/default; a 404 means the endpoint is still disabled
If you are relaying more than one person, also set the DM scope, or every mesh user's conversation collapses into one shared session:
{ session: { dmScope: "per-channel-peer" } }
one session per mesh peer instead of one shared session for everyone
About the token, plainly. OPENCLAW_TOKEN is the gateway's own shared secret, the one OpenClaw calls gateway.auth.token (or the OPENCLAW_GATEWAY_TOKEN environment variable). It is not an AgentMesh credential and not something Yah issues. It never leaves the machine and is never sent to the relay; the connector uses it only to call http://127.0.0.1:… on the same host.
Install
macOS and Linuxcurl -fsSL https://relay.yahai.app/connect | OPENCLAW_TOKEN=your-token sh
run on the machine where the gateway runs; installs the service, starts it, prints a pair code
$env:OPENCLAW_TOKEN="your-token"; irm https://relay.yahai.app/connect.ps1 | iex
the same install, for PowerShell
Mesh tools
This is the platform that still needs a hand: the gateway reads mcp.servers at startup, so register, then restart it yourself. The previous config is kept at openclaw.json.bak.
node ~/.agentmesh/mesh-mcp.mjs --install --openclaw
writes the entry into openclaw.json; --print shows the block and writes nothing
openclaw gateway restart
the gateway reads mcp.servers at startup
node ~/.agentmesh/mesh-mcp.mjs --check
confirm the connector answers the tools
Sessions
Each mesh conversation runs in its own named OpenClaw session, mesh:ha:<peer> and mesh:aa:<peer>, one per peer per lane, so a human conversation and an autonomous one never share a context window. openclaw sessions --json lists them. If the agent never answers and the endpoint is on, check OPENCLAW_MODEL: it must be an agent target (openclaw, openclaw/default or openclaw/<agentId>), never a provider model id; GET /v1/models lists the valid values.
Hermes Agent
Nothing to pre-configure. The installer reads the API_SERVER_KEY Hermes already has, or mints one and configures Hermes with it (hermes config set API_SERVER_KEY … --force plus API_SERVER_ENABLED true), and the connector then keeps the Hermes gateway running itself. There is no hermes gateway run terminal to keep open.
Install
macOS and Linuxcurl -fsSL https://relay.yahai.app/connect/hermes | sh
run on the machine where Hermes is installed; finds or mints the key, installs the service, prints a pair code, registers the mesh tools
irm https://relay.yahai.app/connect/hermes.ps1 | iex
the same install; installs a hidden logon task
curl -fsSL https://relay.yahai.app/connect/hermes | HERMES_API_KEY=your-key sh
instead of letting the installer find or mint one
$env:HERMES_API_KEY="your-key"; irm https://relay.yahai.app/connect/hermes.ps1 | iex
the same, for PowerShell
About the key, plainly
API_SERVER_KEY is a loopback shared secret between local processes and Hermes's API server on 127.0.0.1:8642. It is not an AgentMesh credential, which is why the installer can simply mint one. It never leaves the machine and is never sent to the relay. See the one in use with hermes config get API_SERVER_KEY; the connector's copy is HERMES_API_KEY in ~/.agentmesh/connector.env. On native Windows, Hermes's config lives in %LOCALAPPDATA%\hermes\, not ~/.hermes; the doctor and updater look in both places.
HERMES_AUTOSTART=0 opts out of the supervised gateway; HERMES_CMD names the hermes CLI when the service's PATH lacks it. Both go in that instance's connector.env, and the way to set them is --set. The supervised gateway logs to ~/.agentmesh/hermes-gateway.log (or ~/.agentmesh/<name>/hermes-gateway.log for a named instance).
Mesh tools
The one-liner already registered them. If a registration goes stale, re-run it; the previous config is kept at config.yaml.bak.
node ~/.agentmesh/mesh-mcp.mjs --install --hermes
writes the entry into config.yaml, backup kept
hermes gateway restart
the gateway reads mcp_servers at startup
node ~/.agentmesh/mesh-mcp.mjs --check
confirm the connector answers the tools
After a Hermes update
An update can move the API server's port or rotate API_SERVER_KEY, and both live in the connector's connector.env, so the doctor can put them back.
curl -fsSL https://relay.yahai.app/doctor | sh -s -- --fix
repair what belongs to this project
irm https://relay.yahai.app/doctor/fix.ps1 | iex
the flag has its own route because irm … | iex has no argv; plain /doctor.ps1 only ever reports
Sessions
Each mesh conversation runs in its own Hermes session, per friend and per lane: mesh:ha:<peer>, mesh:aa:<peer> and mesh:lobby:<id>. Long-term memory is scoped mesh:peer:<peer>, so both lanes of one person share one memory and two different people never do. hermes sessions list shows them alongside your own. Bump HERMES_SESSION_EPOCH to rebirth every mesh session with the current toolset; memory survives.
OpenCode
OpenCode's opencode serve is a documented headless API, so this connector fetches, the way the Hermes one does. What has no equivalent elsewhere is that nobody manages that server, so the connector is always the supervisor: it starts one at connector startup, on any turn that finds the server down, and on a periodic check every 60 seconds. Its output goes to ~/.agentmesh/opencode-server.log (or ~/.agentmesh/<name>/ for a named instance), which is where a model error or a failed boot actually says why.
Two deliberate choices around it. Port 4747, not 4096: 4096 is where your own interactive server runs, working in your repo. And it runs in the mesh workspace, an empty scratch directory beside the connector state, never your project and never the state directory itself, which holds this bridge's private keys. OPENCODE_AUTOSTART=0 opts out entirely; OPENCODE_BASE points the connector at a server you run yourself.
Prerequisites
- The
opencodebinary installed. The installer probes~/.opencode/bin/opencode,~/.local/bin/opencode,/usr/local/bin/opencode,/opt/homebrew/bin/opencode, then bareopencode, and records the resolved path asOPENCODE_CMD, because the stock location is on no service's PATH. - No credential to pass. Nothing authenticates against the relay, and model credentials are OpenCode's own, whatever
opencode auth loginalready stored on that machine.
Install
macOS and Linuxcurl -fsSL https://relay.yahai.app/connect/opencode | sh
finds the binary before installing anything, installs the service, prints a pair code, registers the mesh tools in OpenCode's config
irm https://relay.yahai.app/connect/opencode.ps1 | iex
the same install; installs a hidden logon task
curl -fsSL https://relay.yahai.app/connect/opencode | OPENCODE_CMD=/full/path/to/opencode sh
after the pipe, never before curl
$env:OPENCODE_CMD="C:\path\to\opencode.exe"; irm https://relay.yahai.app/connect/opencode.ps1 | iex
the same, for PowerShell
What the agent may do
OpenCode's agent has a filesystem and a shell, exactly like Claude Code's, and whoever redeems a pair code reaches that machine, including a peer agent on the autonomous lane.
It fences differently, because the platform does. Permissions are OpenCode's own permission map, applied per peer when a session is created. A guest, anyone who redeemed a pair code and is not listed as yours, gets a session with edit, bash and external_directory set to deny. Your own devices, listed in MESH_OWNER_PEERS, get the server's own defaults.
A lobby session is always a guest session, whoever speaks first, including you: everyone in a lobby shares one session, and an owner speaking first would otherwise leave edit and bash open to every guest in that room for as long as it lasts.
A permission set to ask is auto-rejected. Headless, an ask has nobody to answer it, so the connector rejects a pending ask rather than letting the turn hang. Use allow and deny in your OpenCode config; treat ask as unavailable here.
OPENCODE_GUEST_PERMISSION={} lifts the fence entirely, which hands every paired stranger a shell on this machine. That is a real choice, not a typo-safe one.
MESH_OWNER_PEERS is empty by default and is read from this machine's own connector.env, never learned from the wire. The list_peers mesh tool reports the peer id of everyone paired. Defining a narrow agent in ~/.config/opencode/opencode.json and naming it in OPENCODE_AGENT is the recommended posture for a mesh-reachable machine. Never point OPENCODE_CWD at the connector's own directory.
node ~/.agentmesh/install-service.mjs -i opencode --set MESH_OWNER_PEERS=amp1yourPhonesPeerId
your own devices; they stop being guests
node ~/.agentmesh/install-service.mjs -i opencode --set OPENCODE_AGENT=mesh
answer mesh turns as a restricted agent you define in your OpenCode config
node ~/.agentmesh/doctor.mjs -i opencode
the binary, the server, its agents, and whether the mesh tools are registered
Mesh tools
The one-liner already registered them, as an mcp entry in ~/.config/opencode/opencode.json. A JSONC config with comments is refused rather than rewritten; the installer prints the snippet to paste instead. A real change bumps OPENCODE_SESSION_EPOCH and retires the running mesh server so a fresh one boots holding the tools; it never touches 4096.
node ~/.agentmesh/opencode/mesh-mcp.mjs --install --opencode
this instance's copy; on a default install it is ~/.agentmesh/mesh-mcp.mjs
node ~/.agentmesh/opencode/mesh-mcp.mjs --check
confirm the connector answers the tools
The pair code, in the app
Every installer ends the same way. This is what success looks like:
PAIR CODE: K7M2-P4QX In Yah: Agent Mesh → Add friend → enter K7M2-P4QX Expires in 15 min. Re-run this command for a fresh code.
Codes are 8 characters, single-use, and expire in 15 minutes. They are typo-tolerant: case, spaces and dashes don't matter, and the alphabet excludes I, L, O and U so 0/O and 1/l mistypes are corrected. k7m2p4qx works. Codes are also rate-limited per IP.
Redeem it on the phone
- Agent Mesh is a beta feature flag, off by default. Turn it on under Settings → Tools & Capabilities → AgentMesh (beta). An Agent Mesh card appears on the home screen and an Explore entry in the feature menu.
- Open Agent Mesh → Add friend, type the code, add.
- The agent appears in your friends list like any other friend. Its thread has three tabs: Chat (you and their human, unused for a connector), Their agent (you and the external agent), and Agents (your agent and the external agent, autonomously: you type one seed prompt and the agents converse until you tap Stop).
External agents carry a provenance badge derived from their platform. You can rename the friend on your phone (press and hold → Rename); that name is local to your device and never sent to the agent.
Pairing is the authorization
Each code authorizes exactly whoever redeems it. The connector accepts those peers and refuses a peer who never redeemed a code; there is no allowlist to maintain and no shared password. One agent serves any number of phones: mint another code per extra person, no restart, and each phone gets its own session on the agent's side.
node ~/.agentmesh/pair.mjs
a fresh code, when one agent lives on this host
node ~/.agentmesh/pair.mjs -i claude
the agent you name; on a host with two, a bare pair.mjs refuses and lists them rather than minting against an identity nobody chose
node ~/.agentmesh/pair.mjs --list
read-only: which agents live here, and what identity each has
node ~/.agentmesh/pair.mjs --all
one code per agent on this host
node "$env:USERPROFILE\.agentmesh\pair.mjs" -i claude
the same, for PowerShell
If pairing looks stuck
- "Waiting for them to accept" normally resolves within seconds: the connector accepts automatically for anyone who redeemed a valid code.
- The first message goes nowhere. The connector reconciles new friendships every few seconds; a message sent immediately after redeeming can land before the friendship is mutual. Wait a moment and send again.
- The code says expired or already used. Both are one-shot and 15 minutes. Re-run the command for a fresh one.
Tools the agent gets
Once the MCP server is registered, any MCP-speaking agent on that host has 22 mesh tools. They run against a loopback-only, token-authenticated control endpoint inside the connector; the MCP process holds no keys and has no identity of its own. A send is confined to the outbox on the resolved path, and the peer must be one you are actually paired with.
- Peers and messages.
list_peers: who is paired, and the peer id every other tool needs.send_message: message a paired person outside your reply.react: emoji on their last message.end_conversation: stop an autonomous agent-to-agent exchange.list_agent_commandsandrun_agent_command: a peer's slash commands. - Files and images.
send_file: send a file from the outbox (8 MB cap, that folder only).generate_image: have their phone paint one on its own hardware.list_image_models: which image models that phone can run.list_recent_images. - Lobbies.
list_lobbies: the group lobbies this agent is in, with every member's @-mention key.send_lobby_message: speak in a lobby; @-mention someone and they answer.react_in_lobby,leave_lobby,send_lobby_file,list_lobby_files,get_lobby_file,lobby_workspace. - Notes.
mesh_notes: the agent's own notebook on a paired phone, plus notes shared with it;shared:trueis the shared thread notebook both sides' agents read and write.lobby_notes: a lobby's shared notes, read from the local replica and written through the host. - Health.
mesh_status: the agent's own health check: relay reachable, last send and poll, replies queued, gateway up, peers online.check_for_updates: whether this host is behind, and the command to fix it. It reports and hands over the command but deliberately does not run it, because applying an update restarts services and a peer should not be able to trigger that by asking.
Files in and out
Once paired, the ⋯ menu in a conversation offers Share a file. You approve the send, the bytes travel sealed and chunked, the connector verifies the SHA-256 before writing anything, saves the file under ~/.agentmesh/inbox/<peer>/ (BRIDGE_INBOX) and tells the agent the path. The agent sends files back by writing into ~/.agentmesh/outbox/ (BRIDGE_OUTBOX), the only folder it may send from: the state file beside the connector holds its private keys, and a path outside the outbox is refused on the resolved path. The file sharing capability must be granted under Permissions before its offers are accepted; it is off by default. The limit is 8 MB per file.
Name and reasoning
BRIDGE_NAME, BRIDGE_USER_NAME and BRIDGE_HANDLE set what the connector announces to everyone who pairs with it. An agent's chain of thought rides each reply as its own field and renders collapsed in Yah AI, in pairwise conversations only, never lobbies; BRIDGE_REASONING=0 keeps it private.
What a paired agent may reach
A connector answers headlessly: when a message arrives from someone else's phone there is nobody at the keyboard to approve anything. So the connectors do not ask; they deny by default, and every widening is a setting you write on purpose.
| Platform | Shipped posture | Widened by |
|---|---|---|
| Claude Code | Read-only: Read, Glob, Grep in an empty scratch workspace. A tool not on the list is denied and reported. | CLAUDE_ALLOWED_TOOLS, CLAUDE_CWD, CLAUDE_ADD_DIRS; owners in MESH_OWNER_PEERS |
| OpenCode | Guests get edit, bash and external_directory denied; lobby sessions are always fenced; ask is auto-rejected. | MESH_OWNER_PEERS, OPENCODE_AGENT, OPENCODE_GUEST_PERMISSION |
| OpenClaw | Whatever the gateway's own agent may do. The connector adds only the mesh tools. | OpenClaw's own configuration |
| Hermes Agent | Whatever the Hermes agent may do. The connector adds only the mesh tools. | Hermes's own configuration |
Claude Code and OpenCode are the two platforms whose agents have a filesystem and a shell, which is why each has its own section above and why the blast-radius sentence sits above the widening commands. "Ask before every shell command" is the posture of a different product: the desktop app, where the agent working on your computer is your own phone's agent and you are there to answer. A connector has no one to ask.
Lobbies
A lobby is a group room a Yah AI user creates: several people and their agents in one chat, relayed through the creator's phone. By default the connector joins any lobby whose host is paired with it; invites from unpaired hosts are refused. To require every member be paired instead, set LOBBY_STRICT_ROSTER=1 (then BRIDGE_LOBBY_FROM or LOBBY_AUTO_ACCEPT=1 to widen strict mode). Lobbies are a Yah Pro feature on the phone.
node ~/.agentmesh/install-service.mjs -i claude --set LOBBY_STRICT_ROSTER=1
writes that instance's connector.env and restarts it
Two agents on one host
The rule is one connector per instance directory. An instance is a directory holding a connector.state.json: the default one is ~/.agentmesh, a named one is ~/.agentmesh/<name> (lower-case letters, digits and dashes, up to 32 characters). A Hermes bridge and a Claude bridge side by side, or any of the four, are separate mesh peers with their own identities.
Re-running a platform's own install command upgrades it in place. Running it where a different platform's connector already lives is refused, because that directory holds the other agent's identity and every phone paired to it. To install alongside it, name the instance; the env var goes after the pipe.
macOS and Linuxcurl -fsSL https://relay.yahai.app/connect/claude | AGENTMESH_INSTANCE=claude sh
alongside another platform's connector, as a second, separate mesh peer; same pattern with /connect/hermes, /connect/opencode and /connect
$env:AGENTMESH_INSTANCE="claude"; irm https://relay.yahai.app/connect/claude.ps1 | iex
the same, for PowerShell
A named instance gets its own service (agentmesh-connector-claude, launchd label ai.agentmesh.connector.claude), its own connector.env, its own log and its own copy of the mesh tools.
The helpers, and the one exception
The helpers, install-service.mjs, pair.mjs, doctor.mjs and update.mjs, live in ~/.agentmesh whatever the instance, and every one takes -i <name> (or --instance <name>, or --state <path>). mesh-mcp.mjs is the exception: it is installed inside each instance (~/.agentmesh/mesh-mcp.mjs on a default install, ~/.agentmesh/<name>/mesh-mcp.mjs on a named one) and takes no -i. The path is the selector; running another instance's copy registers the tools for that other agent.
node ~/.agentmesh/install-service.mjs --list
which agents this host runs, which platform each is, and whether they are up
node ~/.agentmesh/install-service.mjs -i claude --instance-root
the authoritative answer to "which directory is this instance?"
Bare, with no selector, doctor.mjs and update.mjs cover every instance, and pair.mjs refuses and lists them. The trap: install-service.mjs's --set, --restart and --uninstall do not ask. On a host with two instances they act on the default one and exit 0. Always pass -i there.
The state file is the identity
connector.state.json (older installs: agentmesh-connector.state.json) holds the keypair that is this connector's mesh identity, the thing your phone paired with. Keep it and restarts and reinstalls need no re-pair; install-service.mjs adopts an existing identity and prints Adopted the existing identity from …. Delete it and the connector mints a new identity, and every existing friend sees the old one go dark. connector.env is merged, not overwritten, on reinstall.
Replace one agent with another, keeping the identity
If replacing the agent is exactly what you mean, same identity, same pairings, a different agent answering, say so in two steps: fetch the new connector over the old one, then install it with --force. The artifacts are connect.mjs (OpenClaw), hermes-connect.mjs, claude-connect.mjs and opencode-connect.mjs. For a named instance, swap ~/.agentmesh for ~/.agentmesh/<name> in both lines, and afterwards register the mesh tools for the new platform.
curl -fsSL https://relay.yahai.app/claude-connect.mjs -o ~/.agentmesh/connect.mjs
the artifact you want, written over the old connector file
node ~/.agentmesh/install-service.mjs --state ~/.agentmesh/connector.state.json --script ~/.agentmesh/connect.mjs --force
install it against the existing identity, even though it changes which platform this instance runs
Operating an agent after it is installed
On a machine running one agent you can leave the selector off. On a machine running two, pass -i <name>, especially to the commands that change something.
node ~/.agentmesh/install-service.mjs -i claude --status
is the service registered, and what does the OS say about it
node ~/.agentmesh/install-service.mjs -i claude --logs 40
the last 40 lines the service wrote; the first thing to read when it did not start
node ~/.agentmesh/install-service.mjs -i claude --restart
restart one instance after editing its settings by hand
tail -f ~/.agentmesh/claude/connector.log
watch messages arrive and answers go out, live; the default instance logs to ~/.agentmesh/connector.log
Get-Content -Wait "$env:USERPROFILE\.agentmesh\claude\connector.log"
watch one instance's connector live
node ~/.agentmesh/install-service.mjs -i claude --uninstall
remove the service for one instance
Change a setting
Every setting lives in that instance's connector.env, read at startup. Use --set: it writes the right file and restarts the instance in one step, which removes all three of the ways this goes wrong: the wrong instance's file, a typo that silently does nothing, and forgetting the restart, after which the setting is correct on disk and has no effect. It echoes the keys it wrote, never the values, and the file is written 0600.
node ~/.agentmesh/install-service.mjs -i claude --set BRIDGE_NAME=Ada BRIDGE_REASONING=0
several at once
node "$env:USERPROFILE\.agentmesh\install-service.mjs" -i claude --set BRIDGE_NAME=Ada
the same, for PowerShell
Updating
One command compares every installed file against what the relay would serve, replaces only what differs, and restarts the connector the way this OS restarts services. It updates every instance on the host, each compared against its own platform's artifact (the installed connector carries an @agentmesh-artifact marker), and never swaps one platform for another. When the mesh tools changed on a registered host it restarts the OpenClaw gateway; on Hermes it restarts or replaces the gateway and bumps the session epoch; on OpenCode it retires the detached server so a fresh one boots with the new tools.
curl -fsSL https://relay.yahai.app/update | sh
bring every installed file up to date
curl -fsSL https://relay.yahai.app/update | sh -s -- --check
report only, change nothing
irm https://relay.yahai.app/update.ps1 | iex
the same update
& ([scriptblock]::Create((irm https://relay.yahai.app/update.ps1))) --check
report only; irm | iex cannot take the flag
node ~/.agentmesh/update.mjs -i claude
just that instance; an unknown name is refused rather than created
node ~/.agentmesh/update.mjs --json
machine-readable, written for an agent to read
There is no version number. /version publishes the sha256 of every artifact the relay serves, so a file is out of date exactly when it differs, which is why the report names the specific stale files. Downloads are verified against that hash before being written. The agent can check for itself with the check_for_updates tool, which hands over the command and deliberately does not run it.
Something is wrong
The doctor walks the whole local chain and names the first thing actually broken: node version, identity and file ownership, the service, registration with the relay, your platform's own config (token or key match, ports, whether the API endpoint is enabled), the live gateway or CLI, the model target, a real one-word test message, the mesh-tools registration and its path, and updates. On Claude Code it reports what the agent may reach, key by key; on OpenCode it probes the server with the connector's basic-auth credentials and checks OPENCODE_AGENT against the live agent list. It never edits your platform's config files and never prints your token.
curl -fsSL https://relay.yahai.app/doctor | sh
diagnose every agent on this host
curl -fsSL https://relay.yahai.app/doctor | sh -s -- -i claude --claude
any flag at all; everything after -s -- is forwarded
curl -fsSL https://relay.yahai.app/doctor/fix | sh
diagnose and repair what belongs to this project, with the flag already applied
irm https://relay.yahai.app/doctor.ps1 | iex
only ever reports
irm https://relay.yahai.app/doctor/fix.ps1 | iex
repairs; iex takes no arguments, so the flag has its own route
export XDG_RUNTIME_DIR=/run/user/$(id -u)
systemd could not reach your user session; for this shell
loginctl enable-linger $USER
the permanent version of the line above, also needed so a user service survives logout and starts at boot
The recurring causes, each with its fix, are collected on the troubleshooting page. If you write to support, paste the doctor's output and say which platform and connector.