HOSTRunning a host
| serve [--port 4140] [--loopback] | Run this host: the app and the gateway on loopback, and on your tailnet when Tailscale is up. --loopback stays off the tailnet and the directory. It prints a sign-in link for the app. |
| open [--port 4140] | Open this host’s app in your browser, already signed in. The host must be running. |
| logs [--channel <ref>] [--trace <id>] [--level warn] [--since 1h] [--grep <text>] [-n 200] [-f] [--json] | Every host process’s logs, merged in time order. -f follows them. |
bun app build && bun ace serve bun ace open bun ace logs --channel fix-login --level warn --since 1h
The desktop app’s Ace Helper uses the same port and data directory as serve. Run one or the other on a machine, not both.
CHANNELSChannels
| new [--project <dir>] [--name <name>] [--model <provider/id>] | Create a channel in a project, which must be a Git repository with a commit. Prints its name and ID. |
| ls [--all] [--team] | This host’s channels: name, ID, state (running, dormant or archived), model and project. --all includes archived ones; --team adds reachable teammates’ channels, with their host and owner. |
| info <channel> | The channel’s details, as JSON. |
| rename <channel> <name> | Rename the channel. Its lanes keep their names. |
| fork <channel> | Start a sibling channel from the channel’s summary and lane, and print its name. |
| agents <channel> on|off | Agent access: whether teammates can ask the channel’s agent, which runs commands as you. New channels start off; with it off, teammates can still read along and post. |
| desktop <channel> on|off | Whether the channel’s agents can operate this Mac’s apps. New channels start off. |
| stop <channel> [--chat <id>] | Stop a chat’s current run. The chat stays usable. |
| kill <channel> | Stop all work in the channel now, child chats and processes included. Nothing is deleted. |
| archive <channel> / unarchive <channel> | Put a channel away, or bring it back to work in. History is kept. |
| delete <channel> [--yes] | Remove a channel for good, even if it isn’t archived. It asks first while a run is in progress; --yes skips that. Its lane branches stay in the project. |
ace new --project ~/code/app --name fix-login --model anthropic/claude-opus-5-5 ace ls --all ace agents fix-login off
MESSAGESMessages and chats
This is how scripts and other agents drive a channel. Text arguments are joined with spaces, so quoting is optional. Without --chat, commands use the channel’s first chat.
info, say, ask, answer, watch, chat and stop also take a teammate’s channel, by name or ID, through this machine’s running host. Their host decides who may ask its agents.
| ask <channel> [--chat <id>] [--model <provider/id>] [--detach] <text…> | Post a message and ask the agent, then stream the run until it ends. If the chat is busy, the message steers the run. --detach returns once the message is accepted. Exits 1 if the run fails or is stopped. |
| say <channel> [--chat <id>] <text…> | Post a message without asking the agent. |
| answer <channel> [--chat <id>] <text…> | Answer the question the chat’s agent is waiting on, in writing. |
| watch <channel> [--chat <id>] | Replay the chat’s transcript, then follow it live. |
| chat <channel> [--model <provider/id>] | Open another chat in the channel and print its ID. |
ace ask fix-login Run the tests and fix what fails ace ask fix-login --detach "Open a pull request when you're done" ace say fix-login "FYI: CI is red on main" ace watch fix-login
Ctrl C during ask detaches without stopping the run; use stop for that. ask and watch print readable text. For structured events, use the gateway.
LANESLanes
Ace ships lane, Luke Edwards’ copy-on-write Git worktrees, and makes every lane with it. A chat’s agent starts, switches and lists lanes with its lane tool; ask in plain words, such as “start a lane for the auth work, based on release”.
New lanes live at .lane/trees/<prefix>/<name> in your project. Ignored files such as dependencies and build caches are cloned by reference, so a lane starts warm and costs almost no disk. Each lane records its base, which lane push rebases onto when it lands; lane prune cleans up after a merge. See lane’s usage for the rest.
KEYSKeys and models
| key set <NAME> | Store a key in your system keychain. It’s read from stdin, so it stays out of your shell history. |
| key rm <NAME> | Remove a key from the keychain. |
| models | Models with credentials on this host, one provider/id per line. |
| model [provider/id | auto] | Show or set this host’s default model for a chat’s first run. auto clears it. |
ace key set ANTHROPIC_API_KEY ace model anthropic/claude-opus-5-5
Ace looks for a credential in ACE_<NAME>, then <NAME>, then the keychain. Running channels pick up keychain changes on their next model call. Agents’ tools never see credentials: the host strips any variable ending in _KEY, _TOKEN, _SECRET, _PASSWORD or _CREDENTIALS.
TEAMProjects and team
| project access <project> | Who can see a project, and why. Name it by folder or owner/name. |
| project share|unshare <project> team|<github login> | Give the team, or one person, every channel in a project, or take it back. Only people with direct access can. |
| directory [<url> | off] | Show, set or clear a deployed directory. A running host checks it with ACE_SECRET first. A directory a Mac on your tailnet serves needs none of this: hosts find and join it on their own. The app does the same from Settings → Team. |
| web [<url> | off] | Show, set or clear a deployed web app your own devices may use with this host. HTTPS only. |
| move <channel> --compute | Move an idle channel to a cloud workspace. Its agent commits and pushes each lane first. It can’t move back. |
| move <channel> --local | Bring an idle channel back to this host from the hosted channel service it moved to before. It keeps its ID and lanes. |
ace project access ~/code/app ace project share githubnext/ace team ace move fix-login --compute
Your tailnet is your team; there are no accounts. A deployed directory and the compute service need the team’s shared ACE_SECRET.‡ Run in cloud, Stop compute and Start compute are app-only for now. See setting it up.
APPWindows and tabs
| tabs [--json] | Open app windows and the tabs of each window’s current channel. |
| tab rename <window> <channel> <tab> <name> [--json] | Rename one tab. "" restores its default label. |
ace tabs --json ace tab rename <window> <channel> <tab> "Build checks"
Both need a running host on this machine and only work over loopback. A tab’s name is layout only; rename changes a channel’s name.
DATABackups
| backup <channel> <new-directory> | A verified snapshot of a local, hosted or cloud channel, safe to take while it runs. Lanes and project files aren’t included. |
| restore <directory> [--project <dir>] [--name <name>] | Restore a backup as a new local channel. |
| export <channel> <file> | Save a channel’s chats as an editable seed file. |
| import <file> [--project <dir>] [--name <name>] [--now] | Add a seed as a new local channel. --now moves its newest message to now. |
ace backup fix-login ~/backups/fix-login-2026-10-15 ace restore ~/backups/fix-login-2026-10-15 --project ~/code/app
ENVEnvironment
| ACE_HOME | The host’s data directory. Default ~/.local/state/ace. |
| ACE_PORT | The loopback gateway port. Default 4140 (4142 for Canary); the tailnet’s HTTPS port is this plus 1000. |
| ACE_MODEL | Force the default model, over ace model. |
| ACE_USER | Your name in channels. Otherwise your Tailscale login, then your OS user. |
| ACE_DIRECTORY / ACE_WEB_APP | Override directory and web. |
| ACE_SECRET | The team’s shared secret for a deployed directory and the compute service. |
| ANTHROPIC_API_KEY, OPENAI_API_KEY | Provider keys, if you’d rather not use the keychain. |
| ACE_DEBUG=1 | Write debug log lines. |
APIThe gateway
Unstable. The gateway is how the app talks to a host. It’s versioned and will change; build on it for tools on your own machine, and expect to follow along.
A host listens on 127.0.0.1:4140. GET /health says it’s Ace. GET /ws is a WebSocket that needs the owner token from $ACE_HOME/host.token, sent as Authorization: Bearer <token>. On the tailnet, Tailscale identifies callers and no token is used; settings, keys, projects and terminals are refused there.
Each frame is one JSON object. Requests are {id, op, …}; replies are {id, ok, value}, {id, ok: false, error} or streamed {id, event}. The host also pushes channels, projects and people as they change.
{"id": 1, "op": "channels"}
{"id": 2, "op": "create", "project": "/Users/you/code/app", "name": "fix-login"}
{"id": 3, "op": "channel", "channel": "<channel-id>",
"request": {"op": "ask", "author": "", "text": "Run the tests and fix what fails"}}
{"id": 4, "op": "channel", "channel": "<channel-id>", "request": {"op": "watch"}}
Channel requests include info, say, ask, wait, chat, stop, kill, answer, skip, edit, delete, react, share (agent access), rename, changes, patch and watch. watch streams messages, replies, tool calls, results, captions and recaps, then live once it has caught up. The host fills in author with the person it verified. Types live in @ace/channel/protocol.