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 Markdownaxiom 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; repeatableThe 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 --tunnelFor 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 HandlerIf 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
| Symptom | Cause | Fix |
|---|---|---|
dev tunnel refused (HTTP 403): dev tunnel is not armed | No active arm | Arm the node in the console; the CLI retries by itself |
HTTP 409: only a draft node … can be tunneled | The version is published or released | Bump the version and push a new draft |
HTTP 409: an instance node cannot be tunneled | You pointed it at an instance | Run axiom dev --tunnel in the generic's package |
HTTP 404: node not found | Not pushed yet, or not your package | axiom push first; check axiom whoami handle vs axiom.yaml name |
Invocation fails with dev tunnel … detached | Local process died or socket dropped mid-run | Check the axiom dev terminal; it re-attaches, then re-run |