Join nodes
A node needs only outbound access to the edge on ports 7233 and 7443. It creates its own key when it joins; the key never leaves the machine.
With one command
On the web UI's Nodes page, Add nodes creates a join token and shows one command for Linux and macOS:
curl -fsSk https://edge.example.com:7443/v1/ca.crt -o /tmp/oz0-ca.crt \
&& echo '<sha256 of the CA> /tmp/oz0-ca.crt' | (sha256sum -c --quiet || shasum -a 256 -c --quiet) \
&& curl -fsS --cacert /tmp/oz0-ca.crt https://edge.example.com:7443/install.sh | sh -s -- --token 'K10...'
It downloads the cluster's CA certificate, checks it against the hash in the command, and fetches the install script over TLS that trusts only that CA, so nothing it runs can have been changed on the way. The script downloads orchestrator-zero-node for the machine's platform and checks its digest, installs it in /usr/local/bin (or ~/.local/bin without root), joins, and starts the node in the foreground. On its first start the node downloads the runtime from the edge too.
install.sh takes --name, --data-dir, --dir for the binary, and --no-run to install and join only. The edge serves what make dist built; see Install.
With a join token
A token admits nodes without an operator approving each one. Create it on the operator side:
orchestrator-zero token create --tenant default --max-uses 10 --ttl 24h --label gpu=a100
The token is shown once. It carries the cluster's CA hash, so the node can check the server before it sends anything. On the machine:
OZ0_JOIN_TOKEN='K10...' orchestrator-zero-node join --server https://edge.example.com:7443
orchestrator-zero-node run
Labels on the token are given to every node that joins with it.
By approval
Without a token, the node waits until an operator accepts it, as with salt-key -a. Get the CA hash first:
orchestrator-zero cluster info # shows the CA hash
On the machine:
orchestrator-zero-node join --server https://edge.example.com:7443 --ca-hash sha256:...
The node prints its fingerprint and waits. On the operator side, compare the fingerprint and accept it:
orchestrator-zero node list --pending
orchestrator-zero node accept node-7f3a01b2c4d5 --tenant default --label zone=eu
node reject refuses it instead.
What run does
orchestrator-zero-node run runs in the foreground. On first start it installs a pinned uv and a managed Python, and the runtime: the bundle given with --runtime-bundle, or else the one the edge serves, checked against its digest. Then it:
- opens the control stream to the edge and keeps it open,
- runs a local Temporal proxy and a local LLM proxy, both holding the node's certificate,
- starts the agent runtime, which polls
oz0.default, the node's ownnode.<id>queue, the queues of the agents it has, and any queues listed in itsqueueslabel, - downloads, builds and starts the plugins its tenant has installed for it.
Certificates renew themselves. A blocked node stops its runtime and asks again every ten seconds.
Labels and queues
Labels are key-value pairs: gpu=a100, zone=eu-north, team=data. They decide which plugins a node gets (plugin install --select) and which agents it may run (requires.labels in an agent). Set them on the token or at node accept.
The special label queues lists extra task queues the node polls, for example --label queues=reports,batch. Only operators set it.
Run a node as a service
Built-in service installation (systemd, launchd and Windows services) is planned. Until then, use your service manager. A systemd unit, for example:
[Unit]
Description=Orchestrator Zero node
After=network-online.target
Wants=network-online.target
[Service]
User=oz0
ExecStart=/usr/local/bin/orchestrator-zero-node run
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
Join once as the same user (sudo -u oz0 orchestrator-zero-node join ...) before you enable the service, and run nodes as a user without root: plugins run with the node's permissions.
Small machines
A Raspberry Pi with a 64-bit OS is a linux/arm64 node like any other, good for agents that drive local hardware or run light tools. Give it a label such as device=pi and require it in the agents that need it.