Public beta — not for production use. Data may be wiped at any time. Questions? Contact us.
Documentation menu

Run a draft node from your machine (live node override)

Attach `axiom dev --tunnel` so platform flows execute your local, hot-reloading code for a draft node — edit, save, invoke, no push or deploy — after a human arms the tunnel in the console.

View as Markdown

axiom dev compiles and runs your package locally with hot reload. With --tunnel it also attaches that local server to the platform as the live implementation of your draft nodes: while attached, every platform invocation of those nodes — from a flow, from axiom invoke, from the console — is served by the code running on your machine. Save a file, the local server reloads, the next invocation runs the new code. No axiom push, no build, no deploy.

This closes the edit → push → deploy loop for node authoring to edit → save.

When to use it

  • You are iterating on a node's code and want to exercise it inside a real flow — edge conditions, joins, facades, the secrets as actually delivered — without waiting for a container build per change.
  • The node is a draft: pushed under your handle, not published to the marketplace, not released.

It is not for: published or released nodes (they never tunnel), instance nodes (an instance has no code of its own — tunnel its generic instead), or anything that must survive your laptop closing (a tunnel session is ephemeral).

How it works, in one paragraph

Your local axiom dev server dials the platform over a WebSocket. When a flow reaches a tunneled node, the platform's own sidecar — the same one that stamps your tenant, narrows the node's secrets and collects its telemetry — sends the node's request down that socket instead of to the node's deployed service, and your local process answers. Nothing about what the node receives changes; only where it runs. Because that means your machine receives whatever the flow would deliver to the node — including secrets you have dev-armed for it — opening a tunnel is a human, in-console consent with a 2-hour expiry, exactly like arming a secret for a draft node.

Step 1 — push the draft

axiom push          # tenant-private draft; repeatable

The package must be pushed at least once so the platform knows the node (its name, its schema, its ULID). Re-pushing later does not disturb an attached tunnel; publishing or releasing the version ends it (see below).

Step 2 — arm the tunnel in the console (human action)

In the Axiom console's flow editor, select the node on the canvas and, in its properties panel (General tab), find Dev tunnel → Arm for 2 h. The section only appears on your own, unreleased nodes. The arm lasts 2 hours; re-arm to extend; Revoke ends it at once. Arming is a console-only action — an API key or CLI session cannot arm it, by design: the workspace that attaches will receive the node's deliveries, so the consent must come from a person in a browser, not from an agent holding your token.

The console calls POST /app/nodes/{node-ulid}/dev-tunnel (and DELETE to revoke) on your behalf.

Step 3 — start axiom dev --tunnel

axiom dev --tunnel

For each node in axiom.yaml the CLI attaches one tunnel and prints:

  ✓ tunnel Handler (you/my-pkg@0.1.0) — invocations of this node now run locally; expires 3:42PM
  → tunnel Handler: execution 01J… → local Handler

If a node is not armed yet you see a warning naming the console, and the CLI keeps retrying every 20 s — arm it in the console and watch it attach, no restart needed. If the platform drops the socket (an ingress restart, a network blip) the CLI re-attaches automatically.

Step 4 — invoke, edit, invoke

Run the flow or the node exactly as usual:

axiom invoke you/my-pkg/Handler --input '{"text":"hi"}'
axiom flow run you/my-flow --input '{...}'

The invocation is served by your local process (the CLI prints the line). Edit the node's source, save; axiom dev recompiles/reloads; the next invocation runs the new code — the marker you just added shows up in the output, with no push in between.

Ending it

A tunnel ends when any of these happen:

  • you stop axiom dev (Ctrl-C) or the socket closes,
  • the arm's 2 hours lapse (the CLI prints the reason; re-arm and it re-attaches),
  • a human revokes the arm in the console,
  • the version is published or released, or the version is deleted — these end draft-hood, and the tunnel is revoked automatically; the very next invocation runs the deployed node.

What to expect and what not to

  • Same delivery rules. The node receives exactly what its deployed twin would: your tenant, the flow reflection, and only the secrets the delivery rules allow (a slot you have not dev-armed stays withheld over the tunnel too). The tunnel changes transport, never delivery.
  • Ephemeral. No durable execution rows are created for the tunnel session, nothing is replayed, and the session is pinned to one platform pod. Runs of the flow are recorded as usual — the node's span is simply marked as tunneled.
  • No silent fallback. If the tunnel is attached but your local process is unreachable mid-invocation, the invocation fails with an error naming the tunnel; the platform does not quietly run the deployed code instead. That is deliberate: "my change isn't taking effect" must never be invisible.
  • One tenant. Only your invocations of your draft node route to your machine. Nobody else's flow can reach it, and a node that is not yours cannot be tunneled.

Troubleshooting

SymptomCauseFix
dev tunnel refused (HTTP 403): dev tunnel is not armedNo active armArm the node in the console; the CLI retries by itself
HTTP 409: only a draft node … can be tunneledThe version is published or releasedBump the version and push a new draft
HTTP 409: an instance node cannot be tunneledYou pointed it at an instanceRun axiom dev --tunnel in the generic's package
HTTP 404: node not foundNot pushed yet, or not your packageaxiom push first; check axiom whoami handle vs axiom.yaml name
Invocation fails with dev tunnel … detachedLocal process died or socket dropped mid-runCheck the axiom dev terminal; it re-attaches, then re-run