squad

Cloud agents that keep working while you sleep. Give one a standing job — watch a repo, track a rival, fix a check that broke — and it wakes on its own to do it, then writes back to tell you how it went.

On a machine of your ownWakes on its ownWrites back on Telegram
agents
+new agent
logs
config
↑↓ moves
rivaldeepseek-v4-flash $1.10 / $5.00

> every monday at 8, diff the other three's pricing and changelogs and mail me what moved

wake monday: diff the three, mail what moved

‹→ email Two moved. One put a seat price on the plan that was flat before, at $29; the other took the free tier down to a trial and said so nowhere but the pricing page.

>
↑↓ agents←→ history^U^D scroll/ commands! shell^C quit
Why the parts are shaped this way

Three problems decide the whole design

An unattended agent reads what strangers write

A GitHub webhook is authentic and still relays an issue body typed by anyone. So every event carries a trust level, and everything that is not from the operator arrives fenced, as data — in one place, so a new channel adapter cannot forget to do it.

Only an operator gives instructions.

A credential can be spent anywhere

So the agent never holds one. Egress goes through a proxy that matches the request against operator-approved grants and attaches the secret afterwards. An agent that talks itself into exfiltrating its own API key has nothing to send.

The agent never sees a key.

An agent can edit its own definition

Its repository holds a manifest, but a manifest is a request. Grants live in the control plane's config file, which the agent cannot write, and an agent may ask for a capability it does not have.

Nothing it says can grant it anything.

The boundary

The only road out is the proxy

The sandbox network is internal, which really is unrouted — a container on it cannot reach the host or the internet by any address. So the proxy is not a convenience the agent could route around, and reaching the model provider is a grant like any other, written onto the request on its way out.

A request that was denied, or came back 401 or 429, is said the moment it happens, because it is the reason the agent is about to misbehave. The ones that worked are counted rather than printed.

18:12:53  maxi      bash        pnpm -r test18:12:53  maxi      bash       after 12.4s: FAIL test/turn.test.ts > carries the failure detail18:12:53  maxi      read        packages/control-plane/src/turn.ts18:12:53  scout     egress     denied GET api.github.com/repos — no_matching_host18:12:53  maxi      answer      El test esperaba el mensaje viejo.18:12:53  maxi      spent       1m38s · 91.2k tokens · $0.02 · api.deepseek.com ×12

squad logs — what every agent runs, answers and spends.

Driving it

One control surface, and it is a terminal

A running plane listens on a unix socket in its state directory. There is no password because there is nothing to authenticate: the socket is 0600, and reaching it already means holding a file the operator owns. That is also why it is the only way in that carries operator trust.

squad on its own opens the console, because someone typing the command with nothing after it is asking to see the thing, not to be told a fact about it.

It is the one at the top of this page. The column on the left is every agent the plane has, up, stopped or mid-turn — thinking gets a mark of its own because with several agents on screen it is the one thing you cannot find out by asking again in a second. What each has spent today is on its row, yellow at four fifths of its ceiling and red at it. Under the last agent is the row that makes one. tab swaps the panel for the log feed, / opens the commands, and ! is the door into the box the agent lives in — the same directory, the same environment, the same proxy.

squad chat demotalk to one in the scrollback, turn after turn
squad lswhat each agent is and whether it is up
squad wake "check the open issues"take one turn, and wait for the answer
squad logsfollow what every agent runs, answers and spends
squad rm demo --purgethe sandbox, and with --purge the repository inside it

A line starting with / is a command about the agent rather than something said to it, answered without waking anything. It configures the plane from inside it: nothing here is a file you edit and redeploy.

/limitwhat it has spent today, and the ceiling for it
/modelwhat it thinks with, and what else there is
/mcpthe MCP servers it has, and the shelf to add from
/servea port inside it, on the machine you are sitting at
/telegramthe bot it answers on, and how to pair one
/emailthe address it is reached at, and how to connect a mailbox
Being reached

Three ways in, and only two of them may instruct

webhookSigned, on the one port that is published. The secret proves which system sent the request, never that a person meant what is inside it.
telegramA bot per agent, connected with /telegram and paired by a link. Anyone else who writes to it is heard as a participant.
emailOne mailbox for the whole plane, connected once with /email. Every agent is reached at its own tag — agents+scout@… — and answers in the thread.

A webhook's secret says which system sent a request. The other two say who: Telegram authenticates the account behind every message, and a mail is judged by the Authentication-Results header your own provider wrote at delivery, when it checked DKIM and DMARC. So those two can be paired to a person — with a phrase that is spent the moment it is used — and what that person writes is an instruction.

Neither costs a domain, a certificate or an open port: both reach out rather than being reached. Mail from anyone unpaired is left unread rather than fenced, because an address strangers already have is one where every message read would spend a turn.

Nothing has to write to it at all. wake_me asks for another turn and leaves the agent a note to be told then — a file the plane reads and removes, not a path out of the sandbox, so it is checked rather than trusted: one appointment at a time, between a second and a month, and never carrying operator trust however it asks.

What an agent is made of

A repository it owns

On its first boot an agent gets a repository in its own volume, at /home/agent/.self:

agent.yamlname, model, and the capabilities it asks an operator for
soul.mdwho it is; appended to the system prompt on every turn
skills/SKILL.md folders, loaded by pi
memory/what it chose to remember, partitioned by users, projects and reference
tools/scripts it wrote for itself

It is scaffolded once, git-initialised, and then left alone: turns run inside it, so what the agent learns and what it can do are files it edits and commits itself. The control plane never writes there again, because the second write would be the control plane overwriting the agent's own work.

Nothing in that repository grants anything. agent.yaml lists capability requests, and an operator answers them in the config file the agent cannot reach.

The pieces

Seven packages, no build step

It is a runtime, not a harness. The thinking is done by pi — squad gives it a machine to live on, a way to be woken, and a boundary to work inside.

eventsTrust levels, fencing, the durable event bus, and the renderer that turns a batch of events into one turn
proxyThe egress broker: a CONNECT-terminating MITM proxy with a local CA, grant matching and credential injection
sandboxThe Docker driver: container per agent, named volume for its repository, exec streams demultiplexed from the daemon's framing
schedulerCron and one-shot wakeups, persisted, with Vixie cron semantics and DST-correct wall-clock matching
channelsWhere events come from and replies go: a signed webhook, a Telegram bot per agent, and one mailbox for the whole plane
agent-repoThe agent's own git repository: manifest, soul, skills, memory, tools
control-planeWires it together, takes turns by running pi in the sandbox, and reads a YAML config
clientThe squad you install: picks where the plane lives, puts one there, and dials it — over a socket here or ssh vps squad relay
What is deliberately missing

The honest list

  • A long-lived pi session. Each wakeup runs pi --print against a per-agent session directory, so context carries across turns but the process does not. The plumbing for a persistent session is written and tested, and unused, because the published pi has no server entry point to run yet.
  • Slack, Discord and the rest. Webhooks, Telegram and mail are there, and a reply is routed by the channel of the event that caused it, so an agent answering a GitHub hook cannot be steered into replying elsewhere by anything in the payload. The others are adapters nobody has written.
  • Anything multi-tenant. One config file, one operator, one machine.
  • Isolation stronger than a container. One Docker container per agent, not a microVM — because if self-hosting needed microVMs nobody would run it. It is not a boundary to put hostile code inside.