Node Agent
lattice-node-agent is a host binary, not the main Docker deployment path. It dials out to the server, reports node state, leases reviewed tasks, and posts results. It has no inbound listener.
Download
Linux release artifacts are published from the lattice-node-agent repository:
lattice-agent-linux-amd64
lattice-agent-linux-arm64
SHA256SUMSInstall the matching architecture:
VERSION=v0.3.3
ARCH=amd64
curl -fsSL --proto '=https' --tlsv1.2 -O "https://github.com/LatticeNet/lattice-node-agent/releases/download/${VERSION}/lattice-agent-linux-${ARCH}"
curl -fsSL --proto '=https' --tlsv1.2 -O "https://github.com/LatticeNet/lattice-node-agent/releases/download/${VERSION}/SHA256SUMS"
grep "lattice-agent-linux-${ARCH}$" SHA256SUMS | sha256sum -c -
sudo install -d -m 0755 /opt/lattice
sudo install -m 0755 "lattice-agent-linux-${ARCH}" /opt/lattice/lattice-agentFor arm64, set ARCH=arm64.
Minimal enrollment
Create the node from the dashboard:
- Sign in to the Lattice dashboard.
- Open the Nodes panel.
- Enter a stable node id such as
gmami-jp1. - Select
Enroll. - Copy the generated Linux service command or manual token before leaving the page.
node-token is a per-node bearer token generated by the server at enrollment time. It is not a GitHub token, Cloudflare token, or operator password. The server stores only a hash of it, so the plain token is shown once. If it is lost, rotate or re-enroll the node and update /opt/lattice/lattice-agent.env on that host.
The dashboard Linux service command is intentionally one-line and Nezha-like:
curl -fsSL --proto '=https' --tlsv1.2 'https://raw.githubusercontent.com/LatticeNet/lattice-node-agent/main/scripts/install.sh' -o lattice-agent-install.sh \
&& chmod +x lattice-agent-install.sh \
&& env LATTICE_SERVER='https://lattice.example.com' LATTICE_NODE_ID='gmami-jp1' LATTICE_NODE_TOKEN='<node-token>' ./lattice-agent-install.shThe install script downloads the selected Linux release artifact, verifies it against SHA256SUMS, installs /opt/lattice/lattice-agent, writes /opt/lattice/lattice-agent.env, and enables lattice-agent.service. Missing checksum tooling or a missing SHA256SUMS manifest aborts before the binary is written. Automatic service installation currently supports Linux/systemd on amd64 and arm64; other platforms should use the manual binary path.
For a quick foreground test, use the printed command:
/opt/lattice/lattice-agent \
-server https://lattice.example.com \
-node-id gmami-jp1 \
-token '<node-token>'The token is sent as Authorization: Bearer, not in the JSON request body. Remote cleartext http:// is refused by default.
The foreground command is useful for smoke testing. Use systemd for persistent nodes.
Browser terminal
The dashboard Terminal page uses outbound, agent-side PTY sessions. It is not an inbound SSH server and it does not require storing SSH credentials in Lattice. The agent keeps its no-inbound-listener model. In lattice-node-agent v0.2.9+, it keeps an outbound control WebSocket to the server. When an operator opens a terminal, the server pushes a terminal.open control message and the agent dials the terminal stream on demand. If the control socket is unavailable, the agent falls back to low-frequency HTTP discovery polling.
The dashboard renders terminal sessions as a dedicated xterm workspace, not as a command input form. Operators can open it from Operations -> Terminal or from an individual node in the Nodes page; the node entry opens /terminal?node_id=... in a new browser tab and starts or resumes that node's latest live session.
Terminal mode requires lattice-agent 0.2.2+ and is off by default. The low-CPU on-demand control channel requires lattice-agent 0.2.9+. Enable it only on nodes where interactive shell access through Lattice is acceptable:
lattice-agent \
-server https://lattice.example.com \
-node-id gmami-jp1 \
-token '<node-token>' \
-allow-terminal=trueEquivalent environment variable:
LATTICE_AGENT_ALLOW_TERMINAL=1For modern interactive use, prefer stream transport:
LATTICE_AGENT_ALLOW_TERMINAL=1
LATTICE_TERMINAL_TRANSPORT=streamTransport modes:
| Mode | Use |
|---|---|
stream | Preferred. Browser WebSocket -> server splice -> agent WebSocket -> PTY. Low latency, resize control frames, reconnect backoff, bounded output replay, and v0.2.9 control-socket wakeups. |
poll | Legacy compatibility. Input, resize, and output are exchanged through bounded HTTP JSON requests. Safer for old agents but noticeably slower. |
Dashboard access requires the terminal:open scope. Initial superuser accounts with * include it. If the agent process runs as root, terminal mode is refused unless -allow-root-exec=true is also set; prefer a dedicated least-privilege service user for nodes where browser terminal access is enabled.
The server keeps live terminal I/O in bounded process memory, not as permanent audit transcripts. It limits each node to four active terminal sessions, expires unaccepted sessions after 10 minutes, bounds detached stream sessions to a short reattach window, and prunes closed state after 30 minutes. Open, attach, close, and agent close events are audited separately. When an operator clicks close, the dashboard sends an explicit stream close frame before marking the session closed; poll sessions still receive a close input through the legacy queue.
Debug mode
For temporary troubleshooting on the node itself, enable verbose non-secret diagnostics:
lattice-agent \
-server https://lattice.example.com \
-node-id gmami-jp1 \
-token '<node-token>' \
-debugThe equivalent environment variable is:
LATTICE_AGENT_DEBUG=1Debug logs include poll-cycle progress, POST paths, payload key names, metric summaries, monitor counts, task IDs, and task exit status. They do not print the node token, task script body, proxy usage secret, or client secret values.
Server-controlled debug
lattice-agent 0.2.1+ also polls a server-owned debug policy from /api/agent/config. From the dashboard, open Nodes -> node detail -> Diagnostics:
- Enable agent debug mode makes the node-agent emit the same non-secret debug lines to the node machine's normal service logs.
- Collect debug lines in server Logs additionally ships those lines back to the server. This is enabled by default when server-controlled debug is turned on. The server stores them under the managed log source
agent-debug://<node_id>. - Disable collection while leaving debug enabled when you want local node diagnostics only.
Equivalent API calls:
# Enable debug and collect centrally (default collect=true).
curl -fsS -b /tmp/lattice.cookies \
-H "X-Lattice-CSRF: $csrf" \
-H 'Content-Type: application/json' \
-d '{"node_id":"gmami-jp1","enabled":true}' \
https://lattice.example.com/api/nodes/debug
# Keep debug enabled on the node, but do not collect it on the server.
curl -fsS -b /tmp/lattice.cookies \
-H "X-Lattice-CSRF: $csrf" \
-H 'Content-Type: application/json' \
-d '{"node_id":"gmami-jp1","enabled":true,"collect":false}' \
https://lattice.example.com/api/nodes/debug
# Disable server-controlled debug.
curl -fsS -b /tmp/lattice.cookies \
-H "X-Lattice-CSRF: $csrf" \
-H 'Content-Type: application/json' \
-d '{"node_id":"gmami-jp1","enabled":false}' \
https://lattice.example.com/api/nodes/debugIf server log storage is disabled, the server still tells the agent to emit local debug logs, but the returned policy disables central collection.
systemd service
The current installer-standard Linux layout is:
/opt/lattice/lattice-agent
/opt/lattice/lattice-agent.env
lattice-agent.serviceOlder nodes may still use /opt/lattice/node-agent/lattice-agent or /usr/local/bin/lattice-agent. The server-controlled update script detects the running process and systemd unit before installing, so these layouts can be upgraded without re-enrolling the node or changing its node id.
Create /etc/systemd/system/lattice-agent.service:
[Unit]
Description=Lattice node agent
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
EnvironmentFile=/opt/lattice/lattice-agent.env
ExecStart=/opt/lattice/lattice-agent
Restart=always
RestartSec=5s
[Install]
WantedBy=multi-user.targetCreate /opt/lattice/lattice-agent.env:
LATTICE_SERVER=https://lattice.example.com
LATTICE_NODE_ID=gmami-jp1
LATTICE_NODE_TOKEN=replace-with-node-token
LATTICE_LOG_STATE_DIR=/var/lib/lattice-agent/logtail
# Optional, for short-lived troubleshooting only:
# LATTICE_AGENT_DEBUG=1
# Optional, high risk: enables dashboard Terminal PTY sessions.
# LATTICE_AGENT_ALLOW_TERMINAL=1Enable it:
sudo mkdir -p /opt/lattice /var/lib/lattice-agent/logtail
sudo chmod 0600 /opt/lattice/lattice-agent.env
sudo systemctl daemon-reload
sudo systemctl enable --now lattice-agent.service
/opt/lattice/lattice-agent -versionPrivileged operations
Execution is off unless you enable it. Add these only on nodes where reviewed host mutation is acceptable:
Server-controlled updates
The dashboard's Agent update UI defaults to official-release mode:
- target version:
latestor a concrete version such as0.3.3 - binary URL: empty
- SHA-256: empty
When a plan is created, the server resolves the trusted release repository, chooses the node's platform artifact, fetches SHA256SUMS, and embeds the concrete URL and digest in the approval plan. The node only mutates after that plan is approved and the agent has exec/root-exec capability.
If an update approval becomes stale, do not approve it. In Operations -> Approvals, stale agentupdate approvals show a stale marker and a Create fresh plan action for operators with planning rights. If the node already reports the requested version, Approvals asks before Force fresh plan creates a replacement pending approval. Fleet -> Nodes -> Agent & updates and Platform -> Agent Updates expose the same safety as Force plan. A forced plan still creates only a pending approval; it does not approve or apply.
Custom artifact mode is still supported for forked or emergency binaries, but the URL and SHA-256 must be supplied together. Do not use custom artifacts for normal upgrades; they bypass the official release affordances and are harder to audit.
LATTICE_AGENT_ALLOW_EXEC=1
LATTICE_AGENT_ALLOW_ROOT_EXEC=1LATTICE_NO_EXEC=1 is a kill switch and wins over the enable flags.
Public and internal IP discovery
The agent reports internal host addresses separately from public addresses. That split matters for maps, DDNS, and routing:
- Internal IPs come from local interfaces and can be private addresses such as
10.x,172.16/12, or192.168/16. - Public IPs should be globally routable addresses. The agent rejects private, loopback, link-local, multicast, documentation, and carrier-grade NAT ranges for public-IP script output.
Default public-IP discovery is auto: use a static override if present, otherwise probe resolver URLs. Operators can set per-node overrides from the node detail page:
| Mode | Use |
|---|---|
auto | Static value if configured, otherwise resolver probe. |
static | Use only the operator-provided public IPv4/IPv6. |
resolver | Always probe IP-echo resolver URLs. |
script | Run an operator-provided shell script on the node and parse public IPv4/IPv6 from stdout. |
Script mode is high-trust host code. It only works when execution is explicitly enabled on that agent with -allow-exec=true; if the agent runs as root, -allow-root-exec=true is also required. The server stores the script for agent delivery, but normal node views show only a short SHA-256 confirmation hash. Leave the script textarea blank when editing an existing script if you want to keep the stored body.
Script mode is the escape hatch for DDNS and unusual provider networks. Example stdout formats are deliberately simple:
curl -fsS https://ifconfig.co
# or:
printf '%s %s\n' "$(curl -fsS https://api.ipify.org)" "$(curl -fsS https://api64.ipify.org)"The first public IPv4 and first public IPv6 found in stdout are used. A failing script keeps the previous public IP values and logs the failure instead of silently replacing them with blanks.
Topology and group leaders
Current Lattice node-agent topology is hub-and-spoke:
each node-agent -> primary lattice-serverEvery node should point at the primary server URL in -server / LATTICE_SERVER. There is no production group-leader or relay-agent mode in the current protocol. The dashboard role and tags fields are organizational metadata for filtering, planning, and display; they do not make one node forward metrics, tasks, logs, or approvals for other nodes.
If you need regional organization today, use stable node IDs plus tags such as region:jp, group:tokyo, or role:edge. A true leader/child topology must add server-side parent/leader fields, enrollment semantics, health propagation, token-bound delegation, and failure handling before agents can safely point at a leader instead of the primary server.
Update policy tuple
Agent updates are reviewed server tasks. The update policy uses:
target version + HTTPS binary URL + SHA-256 + install path + service nameFor the default service above:
target version: 0.3.3
binary URL: https://github.com/LatticeNet/lattice-node-agent/releases/download/v0.3.3/lattice-agent-linux-amd64
SHA-256: value from SHA256SUMS
install path: /opt/lattice/lattice-agent
service name: lattice-agent.serviceThe server downloads the candidate, verifies the SHA-256, requires lattice-agent -version to match the target version, backs up the old binary, installs atomically, and restarts lattice-agent.service after the task result is posted.
Keep auto-plan disabled until the release artifact URL and SHA are final.