The app & agents
Coding agents live on your machines and keep working while your phone sleeps. The ironwire app is how you talk to them: Claude, Codex, or any agent of your own that speaks ACP.
How it works
An agent machine is an ordinary machine built from the wireling image. Besides the usual SSH login and HTTPS URL it runs a small daemon that the app talks to, and it ships the agents themselves: Claude Code and Codex as terminal tools you can use over SSH, and the same two as chat engines for the app. (The Gemini CLI is on the machine as a terminal tool too, but it is not an app chat engine.) Everything the agent does happens inside that machine; the phone only shows the conversation.
The app lists all of your machines, agent or not. Only agent machines open a chat; a plain machine opens its control sheet.
ssh
$ ssh dashboard@ironwire.sh create scout --image=wireling created scout url: https://scout-alice.ironwire.xyz ssh: scout@ironwire.sh
app
On the Machines screen, type a name (or leave it blank for a random one) and tap +. Machines created on the phone are always agent machines. In the web app on a computer, + New machine opens a dialog whose Image choice also offers Plain Ubuntu, a machine with no agent.
The daemon and the chat engines arrive through the platform, not the image, so every agent machine picks up new agent versions the next time its agent restarts (see restarting the agent). Updating or removing the claude or codex commands over SSH never breaks the app: the chat engines are separate copies.
Link your phone
The app has no password. It holds a device key, and you enrol that key into your account either with a passkey or with a one-time code, exactly as you would link any other device.
With a passkey (an account made on the web has one; add one from the dashboard's Passkeys), tap Sign in with passkey on the Connect this device screen and confirm with your phone's passkey prompt. Or use a code, valid for five minutes and good once:
ssh
$ ssh dashboard@ironwire.sh link device link code: 3F9A1C77B2E04D58 (valid 5m0s) on the new machine run: ssh dashboard@ironwire.sh then choose "link a device" and enter the code or scan this in the Wireling app: (QR code)
app
On the Connect this device screen, scan the QR code or type the code and tap Link. Signing out later keeps the device key, so a new code re-links the same device.
The phone appears in keys like any other device; unlink removes it and stops its notifications in the same step.
Sign in an agent
Each built-in agent signs in with its own provider, using the provider's own login command. The app never sees the credential: Connect opens a real terminal on your machine, runs the command there, and leaves the credential where the agent keeps it. You can do the same over SSH; both lanes read the same credential, so a login in one serves the other.
| Agent | Login command | Also accepts (env var) |
|---|---|---|
| Claude | claude auth login | CLAUDE_CODE_OAUTH_TOKEN or ANTHROPIC_API_KEY |
| Codex | codex login --device-auth | OPENAI_API_KEY or CODEX_API_KEY |
ssh
$ ssh scout@ironwire.sh root@scout:~# claude auth login
Or set the key once for every machine you will ever create: ssh dashboard@ironwire.sh env set ANTHROPIC_API_KEY sk-… (see environment variables).
app
Open the machine, tap Connect beside the agent, and follow the provider's prompts in the terminal. Links and device codes get a copy chip; tapped links open in your browser. The terminal stays open for 30 minutes.
A fresh machine has no agent selected: the first chat asks you to pick one, and a signed-out agent takes you to sign-in instead.
Chats
A chat belongs to one agent and one working directory, fixed when it starts; the default directory is /root/workspace, and you can browse to another or create a folder on the spot. Start another chat to work somewhere else. Only the model changes after the fact (chat settings → Model), and a change applies from the next turn without disturbing a running one.
Messages sent while the agent is busy queue up and run in order. The stop button interrupts the current turn. Every turn is written to the machine's own disk, so the conversation survives the app closing, the network dropping or the phone sleeping: on reconnect the app fetches only what it missed. Restarting or pausing the machine keeps the chats.
Typing / shows the agent's own slash commands. Attach up to eight images per message. The Changes view in the chat header shows what the agent has modified in the workspace as a git diff; Workspace in chat settings browses its files read-only.
Restarting the agent
Chat settings → Agent session → Restart agent restarts the agent software on the machine; every chat is kept. It is not a machine restart (that lives on the machine's sheet). This is also how a machine picks up newly delivered agent versions, and a changed or removed custom-agent file (a new one needs no restart). If the app ever reports this machine's agent software is out of date; restart it, this is the fix.
Notifications
When an agent finishes a reply and the app is not open on that machine, your phone gets a push. It carries the machine's name and the fixed text New message from ironwire, never any of the conversation: the content stays on the machine until the app fetches it. At most one push per chat every 30 seconds. A connection request from an agent is the one notification that also rings while the app is open.
The app asks for notification permission once, right after linking. There are no in-app settings; use the phone's own notification controls to quiet a machine.
Bring your own agent
The app is not limited to the agents a machine ships with. One JSON file in /etc/wireling/agents.d/ on an agent machine adds yours to the picker, within a second or two and with no restart. The file names one of two shapes:
ACP (bin) | A program the machine starts and talks to over the Agent Client Protocol (JSON-RPC over stdio, the protocol Zed and JetBrains use). For coding agents and engines. Nothing ironwire-specific is required of the agent; one that does not speak ACP needs an adapter in front of it, as pi does with pi-acp. |
Relay (relay) | A service already running on the machine that takes each message as a loopback HTTP POST and streams the reply back. For assistants that own their conversation: their own memory, schedules and messaging. |
Either way the agent runs behind the app exactly like the built-ins: same chats, same tool cards, same notifications. Custom agents is the full reference: every manifest field, how an ACP agent is run, the relay contract, and a complete working example of each.
Recipes below: OpenClaw · Hermes Agent.
Custom agents are set up over SSH, including signing in. The app has no Connect button for them and treats them as always ready; an agent that is not configured reports its own error as a failed turn. Set keys as environment variables on the machine or account (environment variables), or run the agent's own login in an SSH shell. Most agents' installers and setup wizards are interactive and want a real terminal: run them inside tmux (on every machine), which also keeps the wizard alive if the SSH connection drops. An agent doing the setup from inside the machine drives the wizard the same way: tmux new -d -s setup, then tmux send-keys -t setup 'myagent setup' Enter and tmux capture-pane -pt setup to read what it asks, answering with further send-keys and handing you anything only you can supply, such as the provider choice, an API key or a login link to open. The agents on the machine know this pattern; asking in chat, "install pi-next on this machine", is enough. The chosen model takes effect on the agent's next fresh session. An agent that cannot resume a saved session starts the chat fresh and says so once: this agent could not restore its memory of this chat, so it is continuing fresh.
pi is exactly this: a user-installed binary plus its ACP adapter and a three-line manifest, with no special treatment. Give it a key (ANTHROPIC_API_KEY, or a provider of its own) over SSH and it is ready in the picker.
Two assistants come with an agent machine when you ask for them, OpenClaw and Hermes, in the picker under Assistants: when you create the machine in the app, choose + Hermes & OpenClaw instead of Coding agents (in the web app on a computer, Coding + assistants under Image in the + New machine dialog). That machine has everything the coding one has, plus both assistants (about 3 GB more disk). Both are relay agents wired in by the platform, so neither needs anything installed: the sections below cover signing in, and the SSH route for what the app does not offer. A gateway runs only for an assistant you have signed into; one you never set up costs nothing.
Recipe: OpenClaw
docs.openclaw.ai · OpenClaw is an assistant with its own memory, scheduled jobs and tools, fronted by a gateway that serves its messaging channels. On an ironwire machine the app is one of those channels, and none of it needs setting up over SSH.
OpenClaw is already on your machine. It is installed the official way (npm, globally) on every agent machine, and the app channel, the ironwire tools and the agent file are wired in before you ever log in. In the app, pick OpenClaw, choose a provider — Claude or OpenAI — and sign in on the terminal that opens. The sheet tells you how each subscription bills before you choose. Sign-in is per machine, like every other agent. Until then OpenClaw reads Set up, never Ready, and a sign-in you abandon changes nothing.
When the sign-in finishes the gateway starts and you land in a chat that answers. What you get is OpenClaw as OpenClaw is: it remembers within a chat across restarts, its scheduled jobs report back into the chat that created them, its own /-commands work from the composer, and it can act on your other machines and your connections through the ironwire tools. Replies stream as they are written and each tool shows as a card. Config lives in ~/.openclaw/, and it is yours: the machine writes a value only when you have not set one. It is stock OpenClaw: openclaw update keeps it current.
Switching provider, signing out, or a provider the sheet does not offer is still an SSH job. openclaw is on the PATH, and a running gateway picks up a credential change without a restart:
$ ssh scout@ironwire.sh root@scout:~# openclaw models auth list # the profiles on this machine root@scout:~# openclaw models auth logout anthropic:manual # sign out: takes a profile id root@scout:~# openclaw models auth login --provider openai --device-code root@scout:~# systemctl start openclaw-gateway # only needed after a first sign-in over SSH root@scout:~# systemctl status openclaw-gateway # the machine's own unit root@scout:~# ss -ltn | grep 8647 # the app channel listens on 127.0.0.1:8647
With every profile signed out the app offers Set up again, which is also how you switch provider from the phone: sign out over SSH, then set up in the app.
- Do not run
openclaw onboardoropenclaw gateway installon an agent machine. The machine already runs the gateway asopenclaw-gateway.service; a second one, or the wizard, fights it. On hardware of your own, an outpost, OpenClaw's own install guide applies instead. - The first message after a boot can take half a minute. The gateway needs about 30 seconds to start listening. A message sent in that window is held and answered, not failed. A turn can also think for 20 seconds or more before its first word.
- The
openclawcommand in the first minute after boot may give up withalready owns this state databasewhile the gateway starts. Run it again. - It updates when you say so.
openclaw updatemoves it to OpenClaw's latest release. The app channel is ironwire's and updates with ironwire, kept working with current OpenClaw releases. - Memory search is keyword-based out of the box, because OpenClaw's default index needs a paid OpenAI Platform key. Recall inside a chat is unaffected; to search across chats by meaning, give the machine an embedding provider and set
memory.search.provideryourself. - Both assistants can be signed in at once and both then run: roughly 1.5 GB of memory for OpenClaw and 0.5 GB for Hermes. Size the machine accordingly.
- Logs:
journalctl -u openclaw-gateway. A unit shown as skipped rather than failed means nobody is signed in.
Recipe: Hermes Agent
hermes-agent.nousresearch.com/docs · Hermes is an assistant with its own memory, scheduled jobs and command approvals, fronted by a gateway that serves its messaging platforms. On an ironwire machine the app is one of those platforms, and none of it needs setting up over SSH.
Hermes is already on your machine. It is installed the official way (Hermes' own installer) on every agent machine, its gateway runs as a machine service, and the app plugin is wired in before you ever log in. In the app, pick Hermes, choose a provider — Claude or OpenAI — and sign in on the terminal that opens. The sheet tells you how each subscription bills before you choose. Sign-in is per machine, like every other agent: signing in on one machine does not sign in on another, and one machine's credential is never readable from another.
What you get is Hermes as Hermes is — it remembers across your messages, it asks in the chat before a dangerous command and takes your yes or no, its scheduled jobs report into the chat, its own /-commands work. Replies arrive as Hermes finishes them, a message at a time. Config lives in ~/.hermes/, and it is yours: the machine sets a value only when you have not, so a port or a model you change stays changed. It is stock Hermes: hermes update keeps it current, and hermes pm install … adds its optional parts (browser, computer use, voice and the rest).
Want a provider the sheet does not offer — an API key, OpenRouter? That is still a normal machine, so do it over SSH. hermes is on the PATH; the plugin, the platform config and the agent file are already in place, so only the provider is left:
$ ssh scout@ironwire.sh root@scout:~# hermes auth add openrouter --api-key … # or any provider Hermes supports root@scout:~# hermes config set model.provider openrouter root@scout:~# systemctl restart hermes-gateway # pick up the new provider root@scout:~# systemctl status hermes-gateway # the machine's own unit root@scout:~# ss -ltn | grep 8646 # the plugin listens on 127.0.0.1:8646
Hermes is already in the app's agent picker for this machine; this only changes which model answers.
Notes. The gateway is the machine's own service: systemctl status hermes-gateway, journalctl -u hermes-gateway. It runs only once Hermes is signed in: before that the app reads Set up and the unit is skipped, not failed. Hermes moves when you run hermes update; the app plugin is ironwire's and updates with ironwire. A gateway restart posts a note into your chat ("Hermes is shutting down…"); that is Hermes being polite, not an error. Its log also warns that systemd-run --user is unavailable (root has no user session on a machine): harmless for chatting — it only means a cron job's subprocess is not isolated from a gateway restart mid-job; if that matters, apt-get install -y dbus-user-session and loginctl enable-linger root provide the bus Hermes asks for. Hermes' approval policy is left at its default (approvals.mode: smart): low-risk commands run, the rest ask in the chat — reply yes or no. Hermes documents its own auto-approve switch if you want it. Output that names no chat (a job created from the CLI) lands in the machine's default agent chat, the same place wireling note without a chat goes. A photo you attach is handed to Hermes through its media cache; files arrive as paths in the message, like for every agent. Photos were not exercised in the run this recipe was written from.
The machine talks back
Anything running inside an agent machine can add to the owner's conversation with the wireling command, available on every agent machine: wireling note <text> posts a message without an agent turn (shown as from a script or from your agent), wireling run <task> starts a turn now, and wireling schedule add "daily 08:00" <task> runs one on a timer, in your account's timezone, surviving restarts. Agents already know these commands, so asking in chat is enough: "every morning at 8, summarise my email". Chat settings → Automation → Schedules lists every schedule on the machine and can cancel one; adding is conversational only. A machine can only ever reach its own owner.
Something you build on the machine, a script or your own agent, can find a chat with wireling chats, make one for its job with wireling chat new <title>, and say who it is with --from <name> on note, run and schedule add: the app then shows from <name> and started by <name>. Every command and its limits: reaching the owner.