Skip to content

Host runner (autonomous host tools)

Local tools let an agent reach your machine while you sit in martha chat --local-exec — you are the hands. The host runner removes the human: you attach a machine once as an agent's dedicated executor, and the agent then runs its host tools there on its own — from a chat someone else drives, a trigger, or a schedule — with nobody at a terminal.

Same tools (host_exec, host_read/host_write/host_edit, host_browser, local_agent, …), same grants, same audit. The only thing that changes is who executes: a long-lived martha runner process instead of your chat CLI, reached over a per-agent channel.

 chat / trigger / schedule
        │  agent calls host_exec

   Martha (cloud)  ──park──►  channel:{tenant}:{agent}  ──SSE──►  martha runner  (your machine)
        ▲                                                              │ executes in a scoped dir
        └──────────────── tool.result over /api/channel/ingress ◄──────┘
        │  turn resumes with the real output

Quick start

Two commands to set up, one to attach. You don't need to understand the pieces below to do this.

Onboarding

Starting from scratch? martha init --host-runner sets up your profile, a chat copilot, and wires it as a host runner in one guided flow.

bash
# 1. Create a Martha-hosted agent (its reasoning runs in Martha)
martha agents create --name ops_runner --reasoning martha \
  --model anthropic/claude-sonnet-4-6 \
  --prompt "You run host tools on an attached machine to do real work. Use them; report concrete results."

# 2. Make it drivable as a host runner — mints the key, grants the host tools,
#    lets a chat client drive it, enables chat takeover, and prints the exact
#    runner command to paste:
martha agents setup-host-runner ops_runner

setup-host-runner prints something like:

✓ 'ops_runner' is ready as a host runner
  • minted a runner key
  • granted host tools: host_exec, host_read, host_write, host_edit, host_output, host_kill
  • allowed chat client 'Acme' to drive it
  • enabled chat takeover (runs_as_chat)

Runner key (shown once — store it like a secret):
  martha_ak_…

On the machine you want the agent to drive:
  export MARTHA_TOKEN=martha_ak_…
  martha runner --tools --allow-host-exec --cwd ~/agent-workspace
bash
# 3. On the target machine, run exactly what it printed:
export MARTHA_TOKEN=martha_ak_…
martha runner --tools --allow-host-exec --cwd ~/agent-workspace

That's it. The runner prints attached as agent … channel connected. Now anything that makes ops_runner call a host tool runs on that machine.

Options

setup-host-runner accepts --client <nameOrId> (which chat client may drive the agent — defaults to the tenant's default client), --tools host_exec,host_read,… (override the default host-tool set), and --rotate-key (mint a fresh runner key, e.g. if you lost the old one).

Try it

bash
martha chat --agent ops_runner \
  --message "In your working dir: read sales.csv, compute total revenue per region, write summary.txt, then cat it back."

The agent calls host_exec / host_read / host_write in sequence; each parks, the runner executes it on your machine, and the turn resumes with the real output. Files it writes are really on your disk in the --cwd. The same origination happens with no chat at all — a trigger or scheduled run whose agent calls a host tool routes to the attached runner identically. That's the point: unattended.

Check who's attached at any time:

bash
martha agents runner-presence            # every host-runner agent
martha agents runner-presence ops_runner # just one
# ● connected  ops_runner  (my-laptop)

Something not working? martha agents doctor <agent> runs a green/red checklist (runner key, host tools, chat-client access, chat takeover, runner attached) and prints the exact fix for each failing item. Driving a mis-wired host-runner agent from martha chat --agent also prints that fix before the turn, instead of silently answering in plain text.

What setup-host-runner does (and doing it by hand)

The one-shot wires four things. You'll only need this section if you're scripting it yourself or debugging:

  1. Runner key — a scoped API key on the agent (api_key_scope=runner), authorizing channel tool.* frames only. (martha agents generate-key <agent>.)
  2. Host-tool grants — the agent may only run a tool it's been granted. (martha agents add-function <agent> host_exec ….)
  3. Chat-client access — a chat can only select an agent its client is granted. (martha clients grant <client> agent <agent>.)
  4. Chat takeover — with several agents on a client, a granted agent shows up as a delegatable sub-agent tool; selecting it as the primary chat agent only flattens its own host tools into the turn when it's marked runs_as_chat. (Set it in the admin UI: Definitions → Agents → edit → "Chat persona".)

Triggers and workflow nodes that target the agent don't need steps 3–4 — those only matter for driving it from chat.

Presence & revocation

Each runner connection is a live registry entry, so you can see and control who's attached. The admin agents page shows a precise Host runner: connected (host) / disconnected badge; the CLI shows it via martha agents runner-presence.

Kick a connection without rotating the key:

bash
martha agents revoke-runner ops_runner                    # all connections
martha agents revoke-runner ops_runner --connection <id>  # just one

The connect-token is invalidated immediately and the stream drops within one heartbeat. Note a still-running martha runner transparently reconnects (resilience by design) — to take a machine down for good, stop the runner process, or rotate the runner key (martha agents setup-host-runner <agent> --rotate-key), which invalidates the old key so it can't reconnect.

Safety model

The runner is powerful, so it's boxed in:

  • Scoped credential. The runner key is runner-scoped: channel tool.* frames only. It's rejected (403) at task-claim, event.emit, asks, and document/collection + citation reads — even though it authenticates as the agent. Its per-connection connect-token inherits exactly that scope.
  • Ephemeral connect-token. The durable key rides the wire only on the connect handshake; the steady-state stream + result posts carry a short-lived, per-connection, individually-revocable token.
  • Grant-gated tools. The agent can only run a host tool it was explicitly granted; server-set markers key off the canonical platform name, so a tenant can't name a function host_exec and inherit host execution.
  • Scoped workspace + consent. Commands run in the runner's --cwd; --allow-host-exec is the machine owner's opt-in. local_agent passes its task argv-only (never shell-interpreted).
  • Single-claim gate. A result is accepted only for a call actually awaiting it right now; stray, duplicate, or late results are rejected (409) instead of being signalled into the turn.
  • Audit. Dispatch and receipt are recorded like any other tool call.

Kill-switch

The whole channel is gated by CHANNEL_ENABLED on the server — off → /api/channel/* 404s and nothing parks. It's the CHANNEL_ENABLED deploy variable; set it to 0 (or unset) + redeploy to disable the host-runner channel platform-wide without a code change. (LOCAL_EXEC_ENABLED, the local-tools kill-switch, gates host-tool dispatch itself and is on by default.)

Host runner vs. chat --local-exec

chat --local-exec (local tools)Host runner (this guide)
Who executesyour interactive chat CLIa standalone martha runner
Attachedonly while you're in the chatas long as the runner runs
Triggeryour own messageany run that makes the agent call a host tool (chat / trigger / schedule)
Identityyour human sessionthe agent's scoped runner key
Use it forad-hoc, "do this on my laptop while I watch"giving an agent durable hands on a machine

Same tools underneath — pick the human-attached path for interactive work, the runner for autonomy.

Martha is built by aiaiai-pt.