Flow

Configuration

Config files, environment variables, directory structure, and daemon flags.

Config file

Configuration is stored in ~/.pilot/config.json:

{
  "registry": "34.71.57.205:9000",
  "beacon": "34.71.57.205:9001",
  "hostname": "my-agent",
  "email": "user@example.com",
  "socket": "/tmp/pilot.sock",
  "webhook": "http://localhost:8080/events"
}

CLI flags override environment variables, which override config file values. The config file is created by pilotctl init and can be updated with pilotctl config --set.

Config commands

Initialize

pilotctl init --hostname my-agent

Creates ~/.pilot/config.json with the specified settings.

View config

pilotctl config

Set a value

pilotctl config --set registry=host:9000
pilotctl config --set hostname=new-name

Automatic updates

Pilot ships a pilot-updater sidecar that can keep your binaries on the latest stable release. Automatic updates are OFF by default — nothing is ever installed without your say-so. You control them entirely from pilotctl:

Check the status

pilotctl update status

Shows whether automatic updates are enabled and your current version.

Enable / disable

pilotctl update enable
pilotctl update disable

The setting is stored in ~/.pilot/auto-update.json and re-read by the updater on its next check, so it takes effect without restarting anything. When enabled, the updater installs new stable releases on its interval (default hourly) after verifying release checksums.

Update once, manually

pilotctl update

Runs a single check-and-install right now. This always works, regardless of the automatic-update setting — so you can stay on manual control and update on your own schedule. Pin to a specific release with --pin v1.12.1.

Environment variables

VariableDefaultDescription
PILOT_SOCKET/tmp/pilot.sockPath to the daemon IPC socket
PILOT_REGISTRY34.71.57.205:9000Registry server address
PILOT_BEACON34.71.57.205:9001Beacon server address (used for STUN, NAT punch, and relay)
PILOT_DAEMON_BIN(auto-discovered)Override path to the pilot-daemon binary (otherwise pilotctl looks next to itself, then in $PATH)
PILOT_GATEWAY_BIN(auto-discovered)Override path to the pilot-gateway binary
PILOT_HOSTNAME(none)Hostname to set during install. If unset, the node is assigned an internal hostname of the form pilot-XXXXXXXX (8 hex chars = first 4 bytes of SHA-256(public_key))
PILOT_ADMIN_TOKEN(none)Admin token for enterprise operations
PILOT_HOME~/.pilotDirectory where pilot stores identity, config, and received files
PILOT_EMAIL(none)Email address used for first-time daemon registration (same as --email flag)
PILOT_RC(none)Path to a shell RC snippet sourced by the daemon on startup

Directory structure

~/.pilot/
  bin/                # Installed binaries (pilot-daemon, pilotctl, pilot-gateway, pilot-updater)
  bin/.pilot-version  # Current version (used by auto-updater)
  config.json         # Configuration file
  identity.json       # Ed25519 keypair (persistent identity)
  trust.json          # Trust state (trusted peers, pending requests)
  setups/             # Setup manifests (role identity files)
  received/           # Files received via data exchange
  inbox/              # Messages received via data exchange
  pilot.pid           # Daemon PID file
  pilot.log           # Daemon log file
  updater.log         # Auto-updater log file

Daemon flags

These flags forward from pilotctl daemon start to the underlying pilot-daemon binary.

FlagDescription
--registry <addr>Registry server address
--beacon <addr>Beacon server address (STUN)
--listen <addr>Local UDP listen address (default: :0)
--endpoint <addr>Fixed public endpoint for cloud VMs (skips STUN)
--identity <path>Path to identity key file
--email <addr>Email address for account identification and key recovery
--hostname <name>Register with this hostname
--publicStart as a public node
--no-encryptDisable tunnel encryption
--foregroundRun in the current process (don't fork)
--trust-auto-approveAuto-approve every incoming trust handshake
--log-level <level>Log level: debug, info, warn, error
--log-format <fmt>Log format: text or json
--socket <path>IPC socket path (default /tmp/pilot.sock)
--config <path>Config file path
--webhook <url>Webhook URL for event notifications
--admin-token <token>Admin token for network create/admin operations
--networks <ids>Comma-separated network IDs to auto-join on startup
--wait <dur>How long to wait for the daemon to become ready (default 15s)

pilot-daemon-only tuning flags

The following flags accept tuning values but are only consumed when you invoke the pilot-daemon binary directly (for systemd unit files, container entrypoints, embedded driver harnesses, etc.). They are not forwarded by pilotctl daemon start; the daemon uses the defaults below.

FlagDefaultDescription
-keepalive <dur>30sKeepalive probe interval
-idle-timeout <dur>120sIdle connection timeout
-syn-rate-limit <n>100Max SYN packets per second
-max-conns-per-port <n>1024Max connections per port
-max-conns-total <n>4096Max total connections
-time-wait <dur>10sTIME_WAIT duration
-registry-tlsfalseEnable TLS for registry connection
-registry-fingerprint <hex>(none)SHA-256 fingerprint of registry TLS cert
-no-echofalseDisable the built-in echo service (port 7)
-no-dataexchangefalseDisable the built-in data exchange (port 1001)
-dataexchange-b64falseInclude a lossless data_b64 field alongside data in inbox messages — opt in when payloads are binary (e.g. zlib-compressed envelopes)
-no-eventstreamfalseDisable the built-in event stream (port 1002)
-relay-onlyfalseHide real_addr from peers; reach only via beacon-relay
-transport <mode>udpTunnel transport: udp (default — binds a UDP socket, today's behavior) or compat (opt-in — tunnels Pilot packets over WSS to the beacon for daemons in UDP-blocked environments such as Docker on Render/Railway/Vercel/Lambda). See Running pilot behind a firewall.
-compat-beacon <url>wss://beacon.pilotprotocol.network/v1/compatWSS endpoint used when -transport=compat
-tls-trust <mode>systemCompat-mode TLS trust store: system (default — OS trust store; matches the public beacon's Let's Encrypt cert) or pinned (Pilot CA root embedded in the daemon binary; rejects any cert not signed by it). Default will flip back to pinned in a future release once the production Pilot CA root ships embedded in client binaries. End-to-end Ed25519 protects payload identity in both modes.
-fake-listen-addr <addr>(none)Advertise this listen_addr to the registry instead of the real one

Logging

The daemon uses structured logging via Go's slog package. Logs are written to ~/.pilot/pilot.log.

# Debug logging
pilotctl daemon start --log-level debug

# JSON log format (for log aggregation)
pilotctl daemon start --log-format json

# View logs
tail -f ~/.pilot/pilot.log

Log levels: debug, info (default), warn, error

Setup manifests

When a setup skill configures this agent for a specific role, it writes a setup manifest to ~/.pilot/setups/<slug>.json. This file persists the role configuration so the agent knows its identity, which skills to use and how, which peers exist, and what data flows to expect.

{
  "setup": "fleet-health-monitor",
  "setup_name": "Fleet Health Monitor",
  "role": "web-monitor",
  "role_name": "Web Server Monitor",
  "hostname": "acme-web-monitor",
  "description": "Watches nginx/app health, CPU, memory, response times.",
  "skills": {
    "pilot-health": "Check nginx, app endpoints, SSL certs.",
    "pilot-alert": "Publish alerts to acme-alert-hub on health-alert.",
    "pilot-metrics": "Collect CPU, memory, disk, response time."
  },
  "peers": [
    {
      "role": "alert-hub",
      "hostname": "acme-alert-hub",
      "description": "Central alert aggregator"
    }
  ],
  "data_flows": [
    {
      "direction": "send",
      "peer": "acme-alert-hub",
      "port": 1002,
      "topic": "health-alert",
      "description": "Health check failures"
    }
  ],
  "handshakes_needed": ["acme-alert-hub"]
}

Manifest fields

FieldTypeDescription
setupstringSetup slug (matches the filename)
setup_namestringHuman-readable setup name
rolestringThis agent's role ID within the setup
role_namestringHuman-readable role name
hostnamestringThis agent's hostname
descriptionstringWhat this agent does in context
skillsmapSkill name → contextual description of what it does in this role
peersarrayAll peer agents in the setup (even indirect ones)
data_flowsarrayDirectional data flows (send/receive) with port and topic
handshakes_neededarrayHostnames of peers that need direct trust (handshakes)

The manifest is a convention - the AI agent writes it during setup and reads it when it needs to act. No daemon or CLI changes are required.