---
title: "Run a draft node from your machine (live node override)"
description: "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."
category: guide
surfaces: [cli, console]
related: [guides/create-a-node-python, guides/manage-secrets, guides/debug-a-flow, reference/cli/axiom-dev, concepts/sandboxing-and-tenancy]
last_reviewed: 2026-08-16
---

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

`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

```sh
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`

```sh
axiom dev --tunnel
```

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

```text
  ✓ 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:

```sh
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 |
