Outbound node daemon for Lattice.
The agent has no inbound listener. It authenticates with a per-node token,
reports metrics and slow-changing HostFacts inventory telemetry, polls for
queued tasks, executes bounded tasks only when explicitly enabled, and posts
results back to the server. It can also report proxy-core traffic counters from
a bounded local JSON snapshot file or a loopback-only HTTP JSON source for the
server-owned usage rollup. Interactive browser terminal sessions are supported
only when explicitly enabled; they are outbound, agent-side PTY sessions, not an
inbound SSH service.
Node tokens are sent in the Authorization: Bearer header, not in JSON bodies.
For rollback-protected firewall apply tasks, the binary also supports
--selfcheck-controlplane, a one-shot unauthenticated /api/health reachability
check used after nft commit; this mode does not require or send the node token.
HostFacts are best-effort advisory facts (OS, arch, CPU cores/model,
memory/swap, platform, kernel, hostname, boot time, virtualization hint). They
are collected with stdlib and local platform files such as /proc and
/etc/os-release; missing fields are left empty and never block the agent.
go run ./cmd/lattice-agent \
-server http://127.0.0.1:8088 \
-node-id demo-node \
-token '<enrollment-token>' \
-allow-exec=false-allow-exec=false is the safe default. Use -allow-exec=true only on nodes
where remote script execution is acceptable.
The node token is sent in the Authorization: Bearer header on every request.
The loopback http://127.0.0.1:8088 URL above is safe because the token never
leaves the host. For a remote server the agent refuses to start on a
cleartext http:// URL (it would leak the token) — use https:// instead. The
-allow-insecure-http flag exists only as a deliberate escape hatch and is off
by default.
Debug diagnostics:
go run ./cmd/lattice-agent \
-server https://lattice.example.com \
-node-id demo-node \
-token '<enrollment-token>' \
-debugLATTICE_AGENT_DEBUG=1 enables the same mode for systemd environments. Debug
logs include poll progress, request paths, payload key names, metrics 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.
The server can also enable debug mode per node for lattice-agent 0.2.1+
through /api/nodes/debug or the dashboard node detail panel.
Server-controlled debug writes to the node's normal service logs and, by
default, also uploads the same debug lines to the server Logs store under
agent-debug://<node_id>. Set collect=false on the server policy to keep debug
enabled on the node while preventing central collection.
Current topology is hub-and-spoke: every agent points directly at the primary
lattice-server. role and tags are metadata for filtering/planning; there is
no production group-leader or relay-agent mode yet.
Interactive terminal sessions:
go run ./cmd/lattice-agent \
-server https://lattice.example.com \
-node-id demo-node \
-token '<enrollment-token>' \
-allow-terminal=trueLATTICE_AGENT_ALLOW_TERMINAL=1 enables the same mode for systemd
environments. Terminal mode is off by default and is separate from reviewed
batch tasks: it opens a short-lived PTY on the node, polls the server for input,
and posts output back to the dashboard. The agent still has no inbound listener,
does not accept SSH connections, and does not store SSH credentials. Dashboard
access requires the operator scope terminal:open.
If the agent process runs as root, terminal mode is refused unless
-allow-root-exec=true is also set. Prefer running the agent as a dedicated
least-privilege service user when browser terminal access is needed.
Firewall apply selfcheck:
lattice-agent --selfcheck-controlplane -server https://203.0.113.99The selfcheck exits 0 only when GET /api/health returns HTTP 200. It reuses
the same transport safety guard as normal startup, so remote cleartext http://
is refused unless deliberately allowed.
Firewall apply domain-set update:
lattice-agent --update-nft-domain-set \
-host lattice.example.com \
-family inet \
-table lattice_policy \
-set lattice_control4 \
-set6 lattice_control6This mode resolves the hostname with Go's resolver, splits answers into IPv4
and IPv6 sets, sorts/deduplicates each family, then updates the existing nft
named sets using direct nft argv calls. -set may be used alone for the
legacy IPv4-only path; -set + -set6 updates both control-plane sets and
requires at least one A or AAAA answer. It does not require or send the node
token. It is intended for server-rendered, rollback-protected apply scripts;
empty resolution or invalid nft identifiers exit non-zero so the task can roll
back.
Proxy usage reporting bridge (file source):
lattice-agent \
-server https://lattice.example.com \
-node-id gmami-jp1 \
-token '<node-token>' \
-proxy-usage-file /run/lattice/proxy-usage.jsonThe file is read once per agent loop and posted to /api/agent/proxy-usage.
The agent overrides any node_id in the file with its configured node id,
defaults at when omitted, rejects empty user ids, rejects negative counters,
and refuses files over 1 MiB. The server performs monotonic diffing,
per-profile user eligibility filtering, quota status updates, and audit.
Minimal file shape:
{
"core_uptime_sec": 12345,
"user_bytes": {
"alice": 1048576,
"bob": 2097152
}
}This is an interim stable contract for sidecar collectors and future direct sing-box/xray collectors; it is not a general log or metrics ingestion channel.
Proxy usage reporting bridge (loopback HTTP JSON source):
lattice-agent \
-server https://lattice.example.com \
-node-id gmami-jp1 \
-token '<node-token>' \
-proxy-usage-url http://127.0.0.1:19090/stats \
-proxy-usage-secret-file /etc/lattice/proxy-usage.secret-proxy-usage-url is mutually exclusive with -proxy-usage-file. The URL must
use http:// or https:// and a loopback host (127.0.0.0/8, ::1, or
localhost); remote hosts and URL userinfo are refused before any request is
sent. The optional local bearer secret is sent as Authorization: Bearer to
that local source only. For persistent services, prefer
-proxy-usage-secret-file / LATTICE_PROXY_USAGE_SECRET_FILE over
-proxy-usage-secret so the secret is not exposed in process arguments or shell
history. Responses are capped at 1 MiB and fetched with -proxy-usage-timeout
(default 3s).
The HTTP source accepts the same Lattice snapshot shape as the file source, an
envelope {"snapshot": ...}, or V2Ray-style stats output:
{
"stat": [
{"name": "user>>>alice>>>traffic>>>uplink", "value": 1048576},
{"name": "user>>>alice>>>traffic>>>downlink", "value": 2097152}
]
}The agent sums uplink/downlink per user and posts the normalized
ProxyUsageSnapshot to /api/agent/proxy-usage. This keeps the server's
monotonic diffing, eligibility filtering, quota state, and audit as the
authoritative layer. Direct sing-box/xray gRPC adapters can reuse this parser
later without changing the server ingest contract.
- Interpreter allowlist:
sh,bash,python3,node. - Default timeout: 30 seconds.
- Maximum timeout: 10 minutes.
- Output cap: up to 256 KiB.
- Server-side task creation enforces the same interpreter, timeout, output, and script-size limits before a task can be leased.
- Server-side result ingestion also rejects stdout, stderr, or error text that exceeds the task's output cap.
- Temporary working directory and minimal environment.
- Leased tasks carry a server-issued
lease_id; the agent returns it with the result and exposes it to the task asLATTICE_TASK_LEASE_IDfor traceability. - Leased task payloads contain only execution fields; control-plane actor/token metadata is not sent to agents.
go test ./...
go build ./cmd/lattice-agentPush a v* tag to publish Linux binaries:
git tag v0.2.0
git push origin v0.2.0The release workflow builds:
lattice-agent-linux-amd64
lattice-agent-linux-arm64
SHA256SUMSThe tag version is injected into the binary with -X main.version=..., so:
lattice-agent -versionmust match the server update policy target version. Use the matching artifact
URL and SHA-256 digest from SHA256SUMS when configuring server-controlled
agent updates.