The shortlist
Everything from the four-page thread, consolidated. Nine decisions, seven actions in dependency order, one open question.
Decided
tbase serve, not a product.A correction you prompted
You were right: it is not only for quick answers
You asked why the return path would carry only yes-or-no gates rather than normal commands. It should not, and I over-narrowed it. "Send this prompt to session X" is just as much a typed verb as "allow once".
The rule that matters is verbs, never raw keystrokes. The client already types prompts into live sessions locally and already refuses to do so when a dialog is up. Driving that from the phone is the same operation with a remote trigger, protected by the same existing gate. So the return path carries two verbs, not one.
Actions, in dependency order
Fix the event shape before writing more events
client + hubEvery recorded event names the actor and the object: agent, action, device, account, time, outcome. Not a rendered row.
Do this first. Everything below writes events, and re-instrumenting a year of history that never recorded the actor is the expensive version of this task.
Wire the provider seam to one real provider
clientcrobot first. The seam has zero implementations and nothing in the app or CLI calls it, so this is the first time it can be known to work rather than assumed to.
It is also the shortest path to the thing you want soonest, and it introduces no new architecture.
Render the cross-machine agent list
hubThe data already arrives: a heartbeat per device every 20 seconds, a page per agent, accounts and device tokens in the schema. This is a rendering job over data in hand.
Respond and dispatch to cloud agents from the hub
hub + gatewayA button calls the provider API. The gateway holds the credential so a phone never does. This completes "drive your agents from anywhere" without touching the Mac.
The return path: two verbs on the heartbeat
client + hubRead the response body the heartbeat already receives and discards. Queue a few pending verbs per device; execute and ack on the next beat. Answer a gate, and send a prompt.
Lower priority than it looked at the start of this thread, since you would accept SSH for the rare case. Still worth it: it is the away-channel promise, and it rides a loop that already runs and already authenticates.
Answer permission gates through the seam
clientImplement respond(to:with:) so a gate takes allowOnce, allowAlways, rejectOnce or rejectAlways. This is what would eventually let the launch default stop being a full bypass.
Match merged PRs by head SHA
clientBranch names get reused, so matching a closed or merged PR by branch will eventually show a stale one. Small fix, free lesson, taken from Paseo.
Explicitly not doing
- A daemon or a relay. Ruled out, with SSH as the escape hatch.
- A worktree or workspace manager. Setup hooks, service ports, reverse proxies, merge UI.
- A plugin platform. ACP delivers third-party providers without one.
- A trigger service of our own for local agents. Triggers belong beside the cloud agents.
- An execution-profile system. Org policy is a gateway scope, not a client feature.
- Deeper repo-host integration. Everyone shells out to
ghanyway. - Usage and quota scrapers. Out of scope, unchanged.
The one open question
You said AGPL, then wondered whether the shape is really "whatever Outline does". Those are different instruments, and the difference matters. Outline is BSL 1.1, not EPL.
| Licence | Stops a closed fork | Stops a hosted competitor | Counts as open source | Who picked it |
|---|---|---|---|---|
| MIT | No | No | Yes | Us, today |
| Apache-2.0 | No | No | Yes | Paseo |
| AGPL-3.0 | Yes | No | Yes | Warp |
| BSL 1.1 | Yes | Yes, until the change date | No | Outline |
AGPL is the right call, and your first instinct was correct
BSL would buy protection you do not need. The client is not the moat, the gateway is. Someone who forks and hosts the client still has no managed credentials, no metering and no grant, so a hosted competitor built on our client is not the threat BSL exists to stop.
What AGPL does buy is the thing that is actually worth having: a fork that gets hosted has to publish its changes. And it keeps the "open source" label honest, which BSL does not, at no cost to a business whose value sits one layer behind.
Cheap now, expensive later: relicensing gets much harder once outside contributors arrive. Worth doing as a deliberate change rather than a drift.
Closes the thread that ran across the six-decision scoping page, the return path, host or client, and where the state lives. All four are on this hub.