sklet — the node agent

sklp talks to a node, sklet is a node

sklet is the node-side agent. It supervises the spaces deployed on its machine, runs the reconcile loop, and serves the control API that sklp deploy pilots from a workstation. It ships without the dev, CI and publish surface: a production node has no use for a pipeline engine.

sklpsklet
Runs onthe workstationthe node
Doesapply, inspect, logs, exec, contexts, loginsupervise, reconcile, serve the control API
Reaches the nodeover the control APIis the node

First-time setup of a node is in Deploy on a VM.

Verbs

VerbPurpose
applyApply .sklp/<stack>/deploy.yaml on this node.
agentRun an empty control-plane node waiting for a remote deploy.
reconcilePull the repos this node deploys from and apply what changed.
host setupCheck or apply the rootless host prerequisites.
host configureRender the sklet-agent unit from node.yaml and restart it.
token rotateReplace the control API token.
gateway muxRun the ingress-per-space front door.
space migrate-ownerHand a stopped space's volumes to its own root, or back.
versionPrint the binary version.
gcRemove images this node no longer runs.
upgradeInstall a published sklet and restart the agent.

apply

sklet apply                              # no stack: list the choices
sklet apply core --space production
sklet apply core --foreground            # debug: supervise in this terminal
FlagDefaultMeaning
--spaceproductionSpace to deploy (named or ephemeral).
--repo.Repository root.
--foregroundSupervise in this terminal instead of detaching.
--listenAlso expose the control API on this TCP address (TLS + Bearer).
--forceAdopt a space deployed by a different stack.
--gateway-modesingletonper-space (native mux) or singleton (traefik).

agent

The normal mode of a production node: open the control API and wait for sklp deploy apply. Configured through node.yaml and rendered into the unit by sklet host configure — not by hand.

FlagMeaning
--listenControl API address, default :6464. Never :9080/:9443 (the front door).
--gateway-modeRequired: per-space or singleton. Never inferred.
--advertisePublic host/IP written into the generated node config.
--advertise-viaOn a private origin: advertise https://<node-id>.<domain>:<port>.
--node-relay-domainOn a front: relay control API connections by SNI to mesh peers (ADR-019).
--mesh-authority-urlmeshauthority URL; empty disables the mesh.
--mesh-endpointThis node's dialable IP:port for WireGuard.
--mesh-cert-fingerprintmeshauthority certificate SHA-256, required for https://.
--public-nic-interface, --public-nic-source, --public-nic-table, --public-nic-markKeep a public NIC's policy routing and SMTP egress in place.

Mesh flags are detailed in The mesh.

reconcile

The node's pull loop (ADR-014). It polls every repo in reconcile.yaml and reconciles those whose branch moved. Nothing reaches the node, so a node behind NAT works the same — a push lands within the poll interval.

# ~/.config/sklp/reconcile.yaml
interval: 1m          # floored at 10s
targets:
  - repo: https://<token>@github.com/acme/app.git
    branch: main      # default
    name: app         # default: last path element
FlagDefaultMeaning
--config$XDG_CONFIG_HOME/sklp/reconcile.yamlConfig path.
--onceOne pass, then exit.
--spaceproductionPicks which host-wide control credentials file to use.
--control-urlhttps://127.0.0.1:6464Control API applied against over loopback.

No file, or no targets, means the node pulls nothing and only serves what an operator applied.

host

sudo sklet host setup        # one-time: uidmap, subuid/subgid, linger, launcher
sklet host setup             # unprivileged: check only
sklet host configure         # after any edit of node.yaml / node.env
sklet host configure --dry-run

host configure (ADR-020) renders the sklet-agent user unit from ~/.config/sklp/node.yaml (flags and node-owned settings, parsed strictly) and ~/.config/sklp/node.env (secrets, 0600, loaded through EnvironmentFile=), reloads systemd, enables and restarts the agent. It is idempotent and unprivileged. node.yaml keys include listen, gateway_mode, advertise, advertise_via, node_relay_domain, data_root, acme_email, otlp_endpoint, mesh, policy (trusted_registries, require_image_digest, acme_storage_dir) and public_nic.

token

sklet token rotate

Replaces the control API token and prints the new one; the old one stops working immediately. From a workstation, prefer sklp deploy token rotate, which also saves it into the context.

gateway

sklet gateway mux --listen :9080 --listen-tls :9443

Routes external traffic to each space's ingress — by Host header on HTTP, by SNI (passthrough) on TLS. An empty --listen-tls disables HTTPS.

space

sklet space migrate-owner <space>
sklet space migrate-owner --back <space>

Moves a stopped space's volumes to its own root (child user namespace), or back to the operator with --back. Only the operator's files move. Refused while the space may be running.

version and gc

sklet version          # -s / --short for the bare tag
sklet gc --dry-run

The agent already garbage-collects images hourly and after every apply; gc runs it by hand.

upgrade — the only restart path

sklet upgrade                     # latest published, health-checked
sklet upgrade --version <tag>
sklet upgrade -force              # reinstall and restart, even on the same version
FlagMeaning
--versionVersion to install (default latest).
--distDistribution base URL.
--forceReinstall and restart even when already on that version.

upgrade verifies the download against SHA256SUMS, swaps the binary, restarts the agent and rolls back if it does not stay up. No sudo once the launcher is installed by host setup.

sklet upgrade -force is the only way to restart an agent. Never systemctl restart, pkill or kill it: the agent supervises runc containers, overlay mounts and its own state, and an outside kill desyncs all of it. Check that the latest published version is the one running before forcing.

Control API

TransportAddressAuth
Unix socketper space, parent dir 0700file permissions
TCP:6464 (agent --listen)TLS (pinned self-signed cert) + Bearer token

sklp deploy reaches the TCP listener with a context holding the server, token and CA fingerprint. Behind a front, the control API is reached through the front's SNI relay (Front and private origins).

Supervision

sklet apply and sklet agent detach a supervisor (sklet __supervise, internal) under systemd --user when available, else as a daemonless setsid session. The terminal is freed either way; --foreground keeps it attached for debugging. Lingering, enabled by host setup, keeps the user units alive without a login.

Child user namespace per space

Each service runs as a root of its own (ADR-022): its holder is spawned in a child user namespace of the space worker's, so container uid 0 maps to a subuid that owns nothing on the host. One root per space, taken from the operator's second delegated subuid range (a persistent slot in child-roots.json); the service's other uids keep the same host owners. Existing volumes move with sklet space migrate-owner. This is the wall behind the admission policy (no host binds, refused capabilities).

Front and traefik notes

FactConsequence
The front routes domains, a space routes paths (ADR-021)A front route names space:; path and options belong to the origin's manifest.
traefik never loads route files written after it startedRelaunch with sklet upgrade -force on the front, never systemctl.
A route written before its backend is ready has empty servers404 until re-applied once the backend is up.
traefik Host() has no wildcardDeclare one route per subdomain.
The control API must not use :9080/:9443Those belong to the front door.