Peers and networking
Peers and networking
Run Holaryn on several of your own machines and let the agents talk: message or ask the agent on another box, and put a remote agent on a local team. Peering is deliberately personal scale — a handful of machines you own, paired over LAN auto-discovery in one click or manually with pairwise scoped tokens; there is no broker or central server.
LAN discovery and one-click pairing
On a shared local network you never need to copy an address or a key. In Settings → Networking → Discover instances on this network:
- Toggle discovery on (on both machines). Each instance announces itself over mDNS (
_holaryn._tcp.local.) and browses for the others — the toggle applies live, no restart. Give the instance a friendly name and description so the other machine knows what it found. - The other instance appears in the list within seconds. Click Pair.
- On the other machine, the pair request lands in the Approvals inbox (" wants to pair — Approve/Deny"), exactly like any gated action. Approve it, and both hosts exchange their inbound tokens automatically — each side auto-provisions a random token on first pairing, so nobody ever types one. Mail flows both ways immediately.
The same flow from the CLI: holaryn peer discovered lists what the running host sees, and holaryn peer pair <node_id> starts the handshake.
Notes and caveats:
- Pair requests are only accepted while discovery is on — a host that never enables it keeps its closed surface.
- Auto-accept (Settings → Networking → Pairing policy) skips the Approvals consent: anyone on the network can then pair without asking you. Convenient while setting up several machines; turn it back off afterwards.
- Windows Firewall prompts the first time discovery binds its multicast socket — allow it on private networks, or instances will not see each other.
- mDNS does not cross a tailnet or most routed networks. Machines that only share a Tailscale connection use manual pairing below; discovery is for the local network.
- Re-pairing the same instance heals a changed address (DHCP) or a rotated token in place, and re-enables a disabled connection — the fresh consent wins.
Manual pairing (e.g. over Tailscale)
Say your laptop and office PC share a tailnet. On each machine, enable the inbound peer inbox and serve on the tailnet address:
# office PC (tailnet IP 100.64.0.3)
holaryn peer token set <officepc-secret> # enables POST /api/peers/inbox
holaryn serve --host 100.64.0.3 --auth-token <operator-token-1>
# laptop (tailnet IP 100.64.0.2)
holaryn peer token set <laptop-secret>
holaryn serve --host 100.64.0.2 --auth-token <operator-token-2>
Then pair them — each side registers the other with that host's peer token:
# on the laptop
holaryn peer add officepc --url http://100.64.0.3:8765 --token <officepc-secret>
# on the office PC
holaryn peer add laptop --url http://100.64.0.2:8765 --token <laptop-secret>
holaryn peer add prints the node id this host introduces itself as (its hostname slug by default) and the exact command to run on the other machine. Pairing is mutual by construction: an inbound message must resolve to a registered peer or the inbox rejects it.
Two things must line up for a peer to reach you:
- The peer token must be set (
holaryn peer token set, Settings → Networking, or theHOLARYN_PEER_TOKENenv var). Until then the peer inbox does not exist (it answers 404) — peering is strictly opt-in. - The URL peers use must match an address your listener answers for:
holaryn serve --host <addr>allows its own bind address. Non-loopback serving also requires--auth-token— that is the operator token and is never accepted by the peer inbox.
Talking to a peer
holaryn peer list # registry + this host's node id
holaryn peer send officepc "pull the latest photos into the shared album"
holaryn peer ask officepc "is the backup job green?" # waits for the answer
The web Agents page → Peers section shows each paired machine with its URL, node id, reachability probe, and last contact. Agents get the same powers as tools in every session: message_peer, ask_peer, and list_peers (reversible, so autonomous-notify at the default posture).
Inbound mail lands in a durable per-peer mailbox and wakes that peer's mailbox worker, which drains the batch into one agent turn under the receiving host's default profile and posture. ask_peer parks the asker until the remote turn's final text is posted back; a plain message_peer gets no mechanical reply — the remote model decides whether to respond — so two agents cannot ping-pong forever. Mutual asks return the pending counter-question instead of deadlocking, and an unanswered ask times out with guidance (a late answer arrives as regular peer mail).
@-addressing: reach targets and peers from any chat
A message whose first token is @<target> routes deterministically at chat ingress — no local model turn. A leading space or backslash opts out (\@name sends the literal text). The token resolves locally first (profile name → enabled chat model id/nickname → coding backend), then splits at the first colon as @peer[:target]:
@Dad remind me what we planned # switch this chat to the Dad profile and send
@glm-4.6 summarize this thread # ONE turn on that model, then back to sticky
@claude-code fix the failing test # gated delegate_coding_task (ask-first as ever)
@officepc is the backup green? # relay to the peer; its reply lands in this chat
@officepc:Dad what's on the calendar? # the REMOTE host resolves the target
Model nicknames set in Providers & Models are first-class @-targets. If a name is ever shared, resolution precedence is profile > model nickname > model id > coding backend > peer.
For @peer:<target> the remote host resolves the target against its own profiles, enabled chat models, and coding backends; an unknown target answers with an error listing what that host offers. Discover a peer's targets with holaryn peer targets <peer> (a live fetch; results are cached in the registry for offline listing). Targeted runs on the remote stay unattended with approval parking — a targeted mention carries no more authority than any other peer message. The composer autocompletes local targets while you type the first @token.
Switching a peer off (and on)
Every paired machine has a connection switch — per-card Disable/Enable in Settings → Networking, holaryn peer disable|enable <name> from a terminal, or POST /api/operator/peers/enable. Disabled blocks both directions: inbound mail from that peer answers 403, and outbound message_peer/ask_peer refuse with a clear error. The entry and its keys are kept; mail that arrived before the switch stays queued and delivers when you re-enable.
Open network mode (trusted LANs only)
To take access keys out of the picture entirely, toggle Open network mode (Settings → Networking → Pairing policy, behind a confirmation). With it on, this host's peer surfaces accept requests without a token — but the sender must still resolve to a registered, enabled peer, unknown senders still get 403, the injection guardrail still screens every message, and gated actions still park for your approval.
The threat model is blunt: on an open-mode host, any device on the network can speak as a registered peer by claiming its name. That is acceptable on a home LAN where you trust every device, and not anywhere else. Never combine open mode with a listener reachable from untrusted networks.
Remote team members
Add a peer's agent to a local team with holaryn team add <team> <member> --peer <name> — see Agent Teams for how the bridged member behaves.
The Networking settings page
Settings → Networking carries everything in one place: LAN discovery with one-click pairing, the pairing policy (auto-accept, open network mode), the peer inbox token, this host's identity (instance name, description, node id), pairing/unpairing with a reachability view and per-peer enable/disable, and the webhook trigger token.
Security notes
- Scoped tokens. The peer token only opens the peer inbox — it is never the operator token, and the operator token is never accepted by the inbox. Token checks are constant-time; the registry file (
peers.json) holds the other hosts' tokens and is written owner-only on POSIX. - Discovery is advisory; pairing is consented. Browsing the LAN never mutates the registry — only the pairing handshake does, and its default is an explicit Approve in your Approvals inbox.
- Guardrail at ingress. Inbound peer text runs through the prompt-injection guardrail before it is persisted, and peer messages are rendered to the model as untrusted content.
- No cross-host approval authority. A peer message never approves anything: gated actions a remote message provokes park to the receiving owner's Approvals inbox under that host's own posture.
- Plain-HTTP caveat. The host listener speaks plain HTTP — tokens ride the wire unencrypted. Run peering over Tailscale (or another WireGuard-class private network) or terminate TLS in front with a reverse proxy. Do not expose the listener to the open internet.
More detail in Security and privacy.