Guide

Station Networks

A Station Network scales Station across processes or machines. One logical Headquarters accepts requests, presents fleet-wide state, and reconciles schedules. Execution stations advertise capacity and definitions, then atomically claim work from the shared adapters.

Queued work: any eligible worker can claim it
  1. Public entry pointHeadquartersAuthenticates a request, validates it and enqueues the run.
  2. Shared durable stateQueue + leasesAtomic claims assign one attempt to one owner. State is shared across the fleet.
  3. Private executionEligible workerChecks definitions, placement and capacity, then executes the claimed run.
Workers claim queued work; Headquarters does not push every job directly. Retries and lease recovery can repeat work, so external effects still need idempotency.

Roles and request flow

RoleResponsibility
headquartersAPI, schedules, broadcasts, routing and fleet inventory for the separate dashboard. It does not execute signals or beacons.
stationAdvertises local definitions and executes eligible signal runs and beacon instances.
standaloneBackwards-compatible single-node mode that performs both roles.

Headquarters enqueues a run once. Stations race to claim it in the shared queue; the adapter's atomic pending-to-running transition chooses exactly one owner. If that owner disappears, its lease expires and the run is recovered. Fencing tokens prevent the old owner from later completing the recovered attempt.

Specialized stations can also own Sandbox workspaces and Browser Use sessions. Their initial execution API routes requests to an explicitly selected owner through Headquarters; it does not use signal queue placement or migrate live sessions.

Live environments: return to their owning worker
  1. Client or agentWorkspace / session IDRequests another command, browser action or screenshot.
  2. Authenticated routingHeadquartersResolves the authorized worker target; tenant grants are operator-configured.
  3. Existing ownerSandbox or browserThe selected worker operates its retained workspace or live browser session.
Session routing is not queue placement. A different worker cannot automatically pick up an open terminal or browser just because both workers share a database.

For example, keep CPU build tools on one Station and browser sessions on another. Both can be private services behind Headquarters. A signal can invoke their APIs, but application code must retain resource IDs and handle interrupted or unknown outcomes. Read the Sandbox and Browser Use lifecycle guides before planning recovery.

A Station Network coordinates work; it does not provision a VPN or Docker-style network. Workers need access to shared adapters and Headquarters needs a reachable, authenticated endpoint for proxied execution. Configure DNS, TLS, firewalls and ingress in the deployment. Keep private worker credentials and tenant assignments in operator configuration; a heartbeat is discovery data, not permission to act as a tenant.

Configure Headquarters

import { defineConfig } from "station-daemon";import { PostgresAdapter } from "station-adapter-postgres";import { StationNetworkPostgresAdapter } from "station-adapter-postgres/network"; const connectionString = process.env.DATABASE_URL!; export default defineConfig({  role: "headquarters",  adapter: new PostgresAdapter({ connectionString }),  network: {    id: "production",    stationId: "hq-1",    name: "Production HQ",    adapter: new StationNetworkPostgresAdapter({ connectionString }),  },  signalsDir: "./signals", // catalog + validation; never executed here  scheduleAdapter,  beaconAdapter,});

Configure an execution station

export default defineConfig({  role: "station",  adapter: new PostgresAdapter({ connectionString }), // same queue  beaconAdapter,                                      // same beacon state  network: {    id: "production",    stationId: process.env.STATION_ID!,    name: "Kenya GPU worker",    adapter: new StationNetworkPostgresAdapter({ connectionString }),    labels: { region: "ke", gpu: "true" },    endpoint: "https://worker-ke.internal.example",  },  signalsDir: "./signals",  beaconsDir: "./beacons",  runner: { maxConcurrent: 12 },});

Use the matching /network export for SQLite, PostgreSQL, MySQL, or Redis. Every process must use the same durable queue and network backends. Share beacon state on nodes that coordinate beacons, and share schedule state across Headquarters replicas. The memory implementations are only for standalone mode and tests. SQLite requires a shared filesystem; use PostgreSQL, MySQL, or Redis across machines.

Capacity, placement, and draining

export const render = signal("render")  .input(RenderInput)  .concurrency({ station: 4, network: 20 })  .placement({ labels: { gpu: "true", region: "ke" } })  .run(async (input) => { /* ... */ }); export const gateway = beacon("gateway")  .placement({ labels: { region: "ke" } })  .run(async (ctx) => {    const server = await listen();    ctx.expose({ protocol: "http", port: server.port, path: "/gateway" });    ctx.ready();    await ctx.untilStopped();  });

Per-station concurrency limits local process pressure. Network concurrency uses shared controller leases and is enforced across the fleet. Placement labels require an exact match. Marking a stationdraining through the Stations dashboard or v1 API stops new claims while current work finishes.

Schedules and exact times

Runtime schedules support five-field cron plus an IANA timezone. The stored nextRunAt is an absolute timestamp and occurrences advance from the prior planned time, so polling latency does not create cumulative drift. Atomic occurrence claims prevent duplicate fires across control-plane processes. As with OS cron, the timestamp is when work becomes eligible; actual handler start can be delayed by polling, queue pressure, or unavailable capacity. See Schedules.

Beacon services

A networked beacon instance is protected by a single-owner lease. Calling ctx.expose() records its station, protocol, port, and base path. Headquarters proxies HTTP traffic at/api/v1/beacons/:name/instances/:id/proxy/*. The owning station must advertise a reachable network.endpoint; private/NAT-only stations need an operator-provided tunnel endpoint. The proxy requires a trigger or admin scope, removes the caller's authorization and cookie headers before forwarding, and does not proxy WebSocket upgrades. Protect direct station endpoints and do not treat the injected x-station-* headers as proof of identity on a publicly reachable service.

What happens when a worker disappears?

ResourceRecovery model
Signal attemptLease expiry makes recovery possible. Fencing rejects stale completion; it cannot undo an external effect already performed.
Beacon processShared intent and ownership leases coordinate a replacement. New process memory starts fresh.
Sandbox workspaceFiles may persist on its owner. Restart and service recovery depend on the adapter; there is no automatic cross-worker workspace migration.
Browser sessionThe live browser is interrupted. Retained profiles, recordings and explicit checkpoints can help reopen on a compatible owner.

Draining stops new queued claims. Also inspect worker-owned shells, services and browser sessions before maintenance; an empty signal queue does not prove the worker has no live environments.

Production checklist