FFleetnook

Run an agent in your environment

Private beta · Linux · Node.js 24+ · Docker Engine · No Kubernetes

  1. Prepare the customer hostUse a dedicated Linux VM with Node.js 24+ and Docker. The runner’s OS user needs Docker access. For private images, follow Private image registries below to sign in using the runner service’s Docker configuration. The host needs outbound HTTPS to Fleetnook and its registry, and network access to the selected Temporal server.
  2. Connect your Temporal serverCreate an environment in Fleetnook with an endpoint, namespace and task queue. Choose your own Temporal Cloud namespace or self-hosted server. The Temporal UI link opens your existing UI; Fleetnook does not proxy or authenticate it.
  3. Enroll the runnerAs the customer or workspace owner, open the environment and choose Set up runner. Follow the signed release download and verification steps, then from the extracted release directory run node fleetnook-runtime.mjs doctor https://fleetnook.com to check Docker and HTTPS access, then execute the enrollment command and paste the one-use token. State and the private signing key are stored in .harbor/runtime/ with restrictive permissions. Keep this directory persistent and backed up. Use a different HARBOR_RUNTIME_DIR for every environment. If enrollment is interrupted, repeat the same command and directory: it preserves the existing identity and credentials and finishes any missing setup.
  4. Configure credentials locallyEdit .harbor/runtime/agent.env. It supplies TEMPORAL_ADDRESS, TEMPORAL_NAMESPACE, TEMPORAL_TASK_QUEUE and TEMPORAL_TLS to the agent. Add TEMPORAL_API_KEY if your agent uses it. For mTLS, your image must accept local credentials through this env file, for example base64-encoded PEM values. No credential is sent to Fleetnook. The image owns its SDK connection implementation.
  5. Start the runnerRun node fleetnook-runtime.mjs doctor to check local configuration, then node fleetnook-runtime.mjs run from the same working directory. Use the systemd setup below to keep it running after logout and start it at boot, directly on the Linux host. Only one controller or enrollment process may use each state directory. Stop the existing controller before upgrading, and do not share its directory between machines or containers. The runner polls every 30 seconds and backs off when Fleetnook is unavailable. Existing containers continue running during a control-plane outage.
  6. Deploy an immutable imageThe vendor requests registry/image@sha256:digest with resource limits. The customer reviews and approves it in Fleetnook. The runner pulls and launches it, then reports its Docker health check. To update, stop the previous deployment and wait for Stopped. Stopping means the runner has not yet confirmed removal, so a replacement remains blocked. A disconnected runner must reconnect before confirmation; if its connection was revoked, remove the agent on the customer host. Before an update, drain in-flight Temporal work and ensure workflow compatibility before switching versions.

Keep the runner running with systemd

Runner 0.2.3 and newer can generate a system service for the current OS account. Keep the verified runner and its state in persistent directories. From the same working directory and account used for enrollment, with the same HARBOR_RUNTIME_DIR and Docker settings, run:

umask 077
node fleetnook-runtime.mjs doctor
node fleetnook-runtime.mjs systemd-unit > fleetnook-customer1.service
systemd-analyze verify fleetnook-customer1.service

Review the generated file before installing it. It records the current user ID, absolute Node.js and runner paths, state directory, PATH and selected Docker settings. It does not read your signing key, registry credentials or agent.env, install a service, or contact Fleetnook. Generate it as the intended runner account; use sudo only for the installation commands below.

Use a different service name and state directory for each environment. Stop any manually running controller before starting its service. For a new service, the guard below refuses to overwrite an existing unit:

sudo test ! -e /etc/systemd/system/fleetnook-customer1.service &&
sudo test ! -L /etc/systemd/system/fleetnook-customer1.service &&
sudo install -o root -g root -m 0644 fleetnook-customer1.service /etc/systemd/system/fleetnook-customer1.service &&
sudo systemctl daemon-reload &&
sudo systemctl enable --now fleetnook-customer1.service
sudo systemctl is-active fleetnook-customer1.service

Confirm the environment is Online in Fleetnook. The service restarts a crashed controller and uses the existing signing state. A normal service stop or runtime revocation leaves the agent container running; stop an agent through Fleetnook. Enable Docker separately so it is available after reboot. Rootless Docker has its own user-service startup requirements.

For local diagnosis, use sudo journalctl -u fleetnook-customer1.service -n 30 --no-pager. The generated service retains DOCKER_CONFIG and, when set, DOCKER_HOST, DOCKER_CONTEXT, DOCKER_TLS_VERIFY and DOCKER_CERT_PATH. Registry authentication must work without an interactive login session. Other shell variables and credentials are not copied into the unit.

For an upgrade, verify the new bundle, stop this service, regenerate its unit with the new runner path and the same state directory, then deliberately replace that unit and run daemon-reload followed by start. Regenerate it if the Node.js executable moves too. Keep the previous verified runner and unit for recovery; never re-enroll or reset the sequence just to update the controller.

Try the sample agent

Start with a working Temporal agent that returns a fixed greeting. It makes no model calls and reports no token usage. The download includes the source, Dockerfile, pinned dependencies and a walkthrough. No GitHub access is needed.

Download and verify the signed Fleetnook bundle. It includes the sample archive alongside the runner. From the extracted release directory:

tar -xzf fleetnook-example-agent.tar.gz
cd fleetnook-example-agent

Open README.md for the build, registry push, local credentials and workflow commands. You need a registry you can push to and a Temporal namespace. Choose at least 1024 MB of memory for this sample, then request customer approval for its image digest in Fleetnook.

Verify the enclosing Fleetnook bundle before extracting this archive, then inspect the source before building. The bundle signature does not sign the image you build or verify its behavior.

Container contract

Run your Temporal worker as UID/GID 10001:10001. The root filesystem is read-only; /tmp is a writable 64 MB tmpfs. No host volumes, inbound ports or Docker socket are exposed to the agent.

Define a Docker HEALTHCHECK that succeeds only when your worker is connected and polling its task queue. A running process alone is not readiness. Worker health is self-reported by the image.

The example checks its running state and an authenticated DescribeNamespace read with a 1.5-second deadline. It reports Temporal outages and denied reads, then recovers automatically. Custom Temporal permissions must allow that read. Add checks for your application dependencies and successful task polling.

Use the environment variables above to connect. Handle SIGTERM and drain gracefully. Images must work with a 128-process limit, no Linux capabilities, no privilege escalation, and the selected CPU/memory limits.

A container is not a sufficient boundary for hostile customer code. Use one dedicated VM per customer and restrict network egress, especially cloud metadata endpoints. Fleetnook does not install a network firewall.

Usage contract

Optionally write cumulative counters as JSON to /tmp/harbor-usage.json, using an atomic rename:

{
  "meterId": "unique-process-or-durable-meter-id",
  "inputTokens": 1234,
  "outputTokens": 456,
  "workflowRuns": 7
}

Keep counters monotonic for the same meter ID. If counters reset, use a new random meter ID. Preserve counters across restarts when possible. The runner reads them every 30 seconds; usage produced and lost between samples cannot be recovered.

Only these numeric counters and the meter identifier are uploaded. Unknown fields are rejected. No instrumentation means no token usage, not an estimate. Use actual provider usage where available.

The signed ledger is suitable for beta reconciliation, not tamper-proof billing. Customers control the host; charging, invoices and provider reconciliation are not enabled.

Support and data boundaries

Support grants expose only container running state, health-check status and runner version. The customer approves a 5, 15 or 30 minute window, can revoke it immediately, and every view is audited. There is no remote shell or log streaming.

Runtime requests use server-authenticated HTTPS plus an enrolled Ed25519 client identity, sequence numbers and signed usage receipts. This beta does not use transport mTLS. Do not expose the local prototype consoles to the internet.

Fleetnook’s telemetry schema excludes business payloads. Your agent and Temporal server have their own data flows. Choosing Temporal Cloud puts workflow history outside the customer’s infrastructure; self-hosting and reviewed agent egress are needed for a fully local boundary.

Diagnosing setup

node fleetnook-runtime.mjs --help lists commands and state-directory settings. doctor checks Docker access, Fleetnook HTTPS connectivity and private local configuration files without modifying state or uploading credentials. It does not authenticate to Temporal; the deployed agent’s health check must verify that connection.

If Docker fails, run docker info as the same OS user that runs Fleetnook. If a local configuration check fails, inspect agent.env and its permissions privately. The state directory should be mode 700, with state and credentials mode 600. Keep the same absolute HARBOR_RUNTIME_DIR in your service-manager configuration. Do not paste these files into support requests.

Docker inspection failed

If the runner cannot inspect its container, it confirms absence through a separate Docker listing that includes stopped containers. If the container still exists or that check also fails, reports pause and the runner retries. An existing agent keeps running; a requested stop stays Stopping and blocks replacement until removal is confirmed. The environment may appear offline while inspection is unavailable. Check Docker locally using the same service user, repair access, and let the runner retry. Keep the existing state directory and signing identity.

Runner already running or invalid lock

Each state directory belongs to one controller. If startup reports that a runner is already using it, check the existing service before starting another. doctor checks the PID marker without claiming the controller lock, changing files or stopping processes. A live PID can belong to another application after PID reuse; the check does not prove which process owns the state.

A normal crash is recovered automatically once the recorded process has exited. An empty or malformed legacy runner.lock requires local operator recovery:

  1. Stop every controllerStop the service manager, scheduled launchers and any manually started Fleetnook controllers that use this exact state directory. Confirm they have exited and cannot restart during recovery. Stopping the controller leaves existing agent containers running.
  2. Establish ownership locallyInspect the recorded PID, process command, working directory and service ownership locally. Do not kill an unrelated process just because its PID appears in the marker. For an invalid marker, check all controller launchers because the PID cannot identify the old owner. If you cannot establish that no controller remains, leave the marker in place and involve the host administrator.
  3. Preserve state and credentialsOnly after those checks, move runner.lock to a unique saved filename inside the same private state directory. Preserve state.json and agent.env exactly; do not re-enroll or reset the signed sequence. Do not include private state or process environments in support requests.
  4. Restart one controllerRun doctor using the same absolute HARBOR_RUNTIME_DIR, service user and Docker configuration. Start one supervised controller and confirm its environment returns to Online in Fleetnook.

The check is advisory; startup still acquires its exclusive operating-system lock. Never share a state directory between hosts or containers, or remove a marker to bypass an active controller.

Private image registries

Sign in on the customer host as the OS user that runs Fleetnook. Use the same DOCKER_CONFIG directory as the runner service; a login on your build machine or in a different directory does not authorize the runner. Use a registry with trusted HTTPS and a pull-only credential where available.

Choose a persistent private directory that the service can read, then run:

export DOCKER_CONFIG=/absolute/path/to/runner-docker-config
install -d -m 700 "$DOCKER_CONFIG"
docker login registry.example.com --username YOUR_REGISTRY_USER

Docker prompts for the password or token. Set that same absolute DOCKER_CONFIG path in your service configuration. For the supplied demo service, the path is /opt/fleetnook/runtime/docker. For non-interactive login, use --password-stdin; never put a password in command arguments. Registry credentials stay in the host’s Docker configuration or credential helper. Keep them out of agent.env, Fleetnook and support requests.

If you use a credential helper, it must be on the service’s PATH and its store must be accessible under that service’s user and filesystem restrictions. A successful interactive login alone does not prove the service can read it. Check the approved deployment becomes ready; the runner’s doctor command does not test private-registry authentication.

Image pulls and startup failures

If a registry pull fails, the runner keeps the approved deployment in Starting and retries after 1, 2 and 4 minutes, for at most four attempts. The attempt count and next retry time survive runner restarts. Every retry follows a successful control-plane poll; a stop or revoked connection prevents another attempt. Registry errors and credentials stay on the customer host.

The runner keeps polling during image downloads and checks approval again before launch. A customer stop, revoked connection or failed control-plane poll terminates the pull client and prevents that attempt from launching an agent. Docker may still finish an in-flight registry request. Startup waits for a successful poll before trying again; a cancelled pull does not consume the registry retry budget. Existing running containers remain up during a control-plane outage or connection revocation.

After four failed pulls, check the image digest, registry availability and docker login using the runner’s OS user, DOCKER_CONFIG and credential helper. Stop the failed deployment in Fleetnook and request a new approval after fixing access. Images without a readiness health check and other container-start errors fail immediately. Inspect those errors locally; repeatedly restarting Fleetnook does not reset a failed deployment.

Stopping and disconnecting

Stop deployments is applied on the next successful runtime poll. Confirm the reported runtime state. Revoke connection cuts off the control channel, revokes outstanding diagnostic grants and deletes their cached reports. Local agents keep running. Stop those directly with Docker if required. Re-enroll using a new Fleetnook environment after a permanent revocation.