Concepts
Nodes, capabilities, credentials and addresses — the vocabulary every other page uses.
Nodes and capabilities
A node is one live connection to a Zyris server. It dials out over a single websocket, so it needs no open port, and it stays a node for exactly as long as that connection is up.
A capability is a named, integer-versioned set of tools. terminal version 3 is a
capability; terminal.exec is one of its tools. Every tool has a JSON Schema for its request and its
response, and a description, so a model can decide whether and how to call it without reading any
code.
A node announces the capabilities it serves after the handshake. The announcement is a
full replacement: announcing again without a capability withdraws it, and calls already in flight on
it fail with capability_unavailable.
Direction symmetry
Once the handshake is done, there is no client and no server. Either peer may:
- announce capabilities,
- call a tool the other side announced,
- open streams.
This is how one connection carries traffic both ways. A node serves terminal to the agents on
its account, and on the same socket Attacca announces attacca_api, which the node can call to
start a session or send a message. See Consuming attacca_api.
Systems, credentials and nodes
Three things are named, and together they make a node's address:
| Thing | What it is | Example |
|---|---|---|
| System | A machine. | laptop |
| Credential | A long-lived zc_ bearer, issued to one program on one system. | zyris-code on laptop |
| Node | One live connection made with a credential. | myrepo |
A node's address is the path system/program/node, for example laptop/zyris-code/myrepo. Every
segment is a slug of [a-z0-9-], at most 32 characters, with the display name kept beside it.
A name that is already taken gets the lowest free suffix: myrepo-2, myrepo-3, and so on. For
nodes, "taken" means taken by a live node at the same system/program path, counted across every
credential that shares the program name. Two instances started from the same credential with the
same name end up as myrepo and myrepo-2, and neither displaces the other.
Read the address back from Link::address() rather than assuming the name you asked for. It is
the path tool calls are routed by.
Presence
A node exists while its connection does. When the connection drops, a grace period (30 seconds
on Attacca) starts. A node that reconnects within it with the same credential and the same name gets
its old identity back — same node_id, same address — so a crash does not turn desktop into
desktop-2. After the grace, the node is gone and its name is free again.
Because of that, node_id is new on every connect that did not land inside the grace. Nothing
should store one durably.
Reconnects are deliberately lossy
On disconnect, every in-flight request on both sides fails with connection_lost, which is
retriable, and every open stream is dead. Resuming interrupted work is the application's job, by
design: a turn stream resumes from a cursor, a file transfer from a byte offset, a PTY by reopening
and redrawing. A streaming tool should document how it resumes.
Deployments
The protocol fixes the wire; a deployment chooses the rest — what a credential looks like, how it is issued, which scopes exist, which capability names are reserved. Attacca is the reference deployment, and these docs use it as the worked example wherever the protocol leaves a choice open. The protocol specification marks which parts are which.