Edge nodes — run jobs on your own machine

Turn a machine you own into a Spike Edge Node: a one-command bootstrap for a fresh Linux box or EC2 instance, the manual path for an existing machine, how to attach it to a cluster, and the agent-side API.

The Spikefrost Team3 Aug 20269 min read

An edge node is a computer you own running a Spike worker. It pulls jobs from Spikefrost, executes them locally with your own Claude Code or Codex seat, and streams the result back into the conversation that asked for it.

Reach for one when the work has to happen on your hardware: a private repository, an internal network the cloud can't route to, a machine that already holds the credentials and tooling. As a side effect, the model usage bills to your own coding-agent account rather than your Spikefrost balance.

The connector is the cluster. Agents address the cluster; whichever paired machine picks up the job executes it locally and streams the result back into the same conversation.

Agent
edgeComputerTool({ alias })

spike-edge-node
connector = the cluster

your Mac
sf edge service

EC2 / Linux box
systemd user service

local Claude / Codex
your seat

result → back into
the same conversation

The connector is the cluster. Agents address the cluster; whichever paired machine picks up the job executes it locally and streams the result back into the same conversation.

Fast path — a fresh Linux box or EC2 instance

One command as root turns a clean machine into a ready node:

curl -fsSL https://edge.spikefrost.com/sf/ec2-bootstrap.sh | sudo bash

Works over SSH on any systemd distribution, and works equally as cloud-init user-data pasted at instance launch — the instance comes up with the node already running.

It installs and configures, in order:

  1. git — a hard requirement of every snapshot task. Amazon Linux ships without it.
  2. The codex CLI (via npm) and the claude CLI (Anthropic's native installer, per-user). A claude failure is non-fatal: codex-only nodes work fine.
  3. The Spikefrost CLI and edge runner, which bring their own Node runtime.
  4. loginctl enable-linger — see below. This is the step people miss.
  5. The systemd user service, registered and started, surviving logout and reboot.

It finishes by printing the node's connection string and a next-steps banner.

The linger trap

Worth stating on its own, because it is the single most common support issue with Linux nodes:

Without loginctl enable-linger, systemd terminates your user service when the last SSH session closes. The symptom is distinctive: the node works perfectly all day while you are connected, and is offline the next morning.

The bootstrap enables it. If you set a machine up by hand, or inherited one that keeps disappearing, check it:

loginctl show-user $USER --property=Linger

Linger=yes is what you need. Anything else and the node will not survive your logout.

Two things the bootstrap deliberately does not do

Both are per-customer and interactive by design — the machine runs your own AI seat, so no script can do them for you.

1. Sign in to the AI CLI, as the user the service runs as:

codex login --device-auth
claude setup-token

Either is enough; the node uses whichever provider an agent asks for.

2. Attach the node to a cluster — the console step described next.

Sizing

Guidance
RAM Codex at high or xhigh effort against a real repository wants 2 GB or moret3.medium and up
Disk Workspaces accumulate per conversation, so size generously rather than tightly

Existing machine — step by step

For a Mac, or a Linux box you already run and would rather configure yourself. Install the CLI first if you haven't:

curl -fsSL https://edge.spikefrost.com/sf/install.sh | sh

Then print the node's connection string:

sf edge pair

Run the worker in the foreground to watch the startup checks:

sf edge run --verbose

Once it looks right, install it as a background service. This survives a restart, starts on login, and keeps the machine awake so dispatched jobs aren't missed:

sf edge service start

status, stop and restart do what you'd expect, and start installs the service first if it isn't installed yet. On Linux, remember linger.

Attaching the node to a cluster

This applies to both paths, and the direction matters — it is the opposite of what most people assume:

The machine prints its connection string. The console receives it. There is no code issued by the console that you type on the machine.

  1. In the console open Connectors and add a Spike edge node connector, or open the one you already have. The connector is the cluster — one connector holds several machines, and agents address the cluster, so you can add hardware later without touching agent code.
  2. Choose Add node and paste the connection string the machine printed.

The string looks like spike-edge-node://connect?token=…, and you can re-print it at any time:

sf edge pair

Treat it as a credential. It carries the node's pairing token, so don't paste it into a ticket, a chat channel, or anywhere you wouldn't paste a password. Rotate it whenever you need to:

sf edge regenerate-token

Verify it is working

Three commands, in the order worth running them:

sf edge status
sf edge logs --lines 20

You are looking for Gateway accepted edge-node session in the log. And on Linux:

loginctl show-user $USER --property=Linger

One log line deserves recognition rather than debugging:

Connected, but this node is NOT paired to a cluster yet — its pairing token is not registered.

That means the runner reached Spikefrost but its connection string was never added to a cluster. Finish the console step; the node binds itself on the next retry.

Give the cluster its environment

Variables set on the cluster are handed to every execution on every node in it — the right home for an internal API base or a key the jobs need locally:

sf edge env list
sf edge env set API_BASE https://internal.example.com

Using it from an agent

The machine becomes an agent's tool. Declare it on the agent class:

override tools = {
  workstation: this.edgeComputerTool({
    alias: 'my_mac_mini',        // the connector — i.e. the cluster
    provider: 'claude',          // or 'codex'
    repos: [                     // mounted beside the app, always pulled
      { provider: 'github', fullName: 'acme/api' },
    ],
  }),
};

Five behaviours decide whether your design works. They are not edge cases:

The turn ends at dispatch. The tool returns "started", not a result. The machine then works alone for minutes and the answer arrives later as a new message in the same conversation. Never await it, poll it, or re-dispatch — holding the turn open produces duplicate replies.

Dual mount, always fresh. The app's own code is always checked out, every declared repository beside it, and all of them pulled before the CLI starts. A tree with uncommitted work from a previous turn is not pulled — the agent is told so in its preamble rather than losing the work.

Two push verbs. sf app push publishes app code; git push publishes a mounted repository. The machine publishes neither on its own, and its workspace persists — so ending a turn mid-refactor is normal and safe.

The session is the conversation. Follow-up instructions resume the same CLI session on the machine, so the agent can refer back to earlier work. A different conversation gets a fresh session.

One job per computer and app. A second command for the same app while one is running is rejected with a typed error. Relay that to the user in plain language rather than retrying. A different app on the same computer runs concurrently.

And the constraint that shapes the prompt: no progress, no steering, no cancel mid-run. Describe the outcome you want, not the keystrokes, and expect nothing back until it finishes.

What the machine can reach

The node's access is yours to bound, and the defaults are conservative:

Control Default Effect
--workspace-root <path> Allowlist of directories the node may work in. Repeatable
--no-ai-cli AI enabled Disables agent execution entirely — the node still handles other local actions
--max-tasks <n> 5 Concurrent local actions
--max-runtime-ms <ms> Hard ceiling on a single local action
--max-output-bytes <n> Caps inline stdout/stderr captured back

Beyond that: a node only ever executes jobs dispatched to its own cluster, the pairing token is rotatable, and the credentials the work uses are the ones already on your machine — Spikefrost never sees your Claude or Codex login.

Configuration reference

Every flag has an environment-variable equivalent, which is usually the better choice for a background service.

Flag Environment variable
--gateway-url <url> SPIKE_EDGE_GATEWAY_URL
--workspace-root <path> SPIKE_EDGE_WORKSPACE_ROOTS
--no-ai-cli SPIKE_EDGE_ALLOW_AI_CLI=0
--max-tasks <n> SPIKE_EDGE_MAX_TASKS
--no-cli-health-check SPIKE_EDGE_CLI_HEALTH_CHECK=0
--cli-health-timeout-ms <ms> SPIKE_EDGE_CLI_HEALTH_TIMEOUT_MS
--no-auto-upgrade SPIKE_EDGE_AUTO_UPGRADE=0
--auto-upgrade-interval-ms <ms> SPIKE_EDGE_AUTO_UPGRADE_INTERVAL_MS
--verbose SPIKE_EDGE_VERBOSE=1
--stream-output SPIKE_EDGE_STREAM_OUTPUT=1
--stream-agent-events SPIKE_EDGE_STREAM_AGENT_EVENTS=1
--no-reconnect

sf edge help prints the complete list, including the upgrade-source overrides.

Keeping it current

sf edge version
sf edge upgrade

The runner checks for upgrades while idle by default. Pin the version on a machine where you don't want that, with --no-auto-upgrade or SPIKE_EDGE_AUTO_UPGRADE=0. Add --force to reinstall at the same version.

Troubleshooting

Symptom Cause
Node offline the morning after it worked all day systemd linger is off. loginctl show-user $USER --property=Linger must say Linger=yes
"Connected, but this node is NOT paired" The connection string was never added to a cluster. Finish the console step; it binds on retry
Looking for a pairing code to type in There isn't one. The machine prints a connection string and the console receives it
Startup stalls on CLI checks The node says hello to Claude/Codex on boot. If that seat isn't signed in, sign in — or skip the probe with --no-cli-health-check
Tasks fail on a fresh Amazon Linux box git is missing. The bootstrap installs it; a hand-built machine needs it added
Jobs run out of memory Codex at high effort wants 2 GB+. Move up an instance size
Dispatch rejected with a typed error Another job is already running for that computer and app. Wait, or dispatch to a different app
Jobs never arrive Check sf edge status, and that the agent's alias matches the connector
Work not visible in the repository The machine doesn't auto-publish. Use sf app push for app code or git push for a mounted repository
Agent describes work that didn't happen Read the trace, not the reply — see Observability and costs

Next

Frequently asked questions

What is an edge node?

A computer you own running a Spike worker. It pulls jobs from Spikefrost, executes them locally using your own Claude Code or Codex seat, and streams results back. The work happens on your hardware, with your credentials and your network access.

What is the fastest way to stand one up?

On a fresh Linux box or EC2 instance, one command as root installs and starts everything, then prints the node's connection string. You paste that string into your edge cluster in the console and sign in to your AI CLI. Nothing else is required.

Why did my node go offline overnight?

Almost certainly systemd linger. Without it, the user service is killed when your last SSH session closes — so the node works all day and is gone the next morning. The bootstrap enables it for you; check it with loginctl show-user $USER --property=Linger.

Does the node bill against my Spikefrost balance?

No. The machine runs your own Claude or Codex seat, so that usage bills to that account. Spikefrost dispatches the work and carries the result.

Do I paste a code into the machine, or a string into the console?

A string into the console. The machine generates and prints its own connection string; the console's edge cluster receives it via Add node. There is no code issued by the console that you type on the machine.

How big does the instance need to be?

Codex at high or extra-high effort against a real repository wants 2 GB or more of RAM, so t3.medium and up. Workspaces accumulate per conversation, so give the disk more room than feels necessary.