Lets a remote AI agent (Claude Code or any MCP client) use a Mac — read/write files, run CLI commands with streaming output, and drive a real browser — through an intent-based approval system. Every operation becomes a structured intent that a human approves before it runs inside an on-the-fly seatbelt sandbox derived from the approved capabilities. Note what that does and does not mean: the profile is built from those capabilities, but it is not a tight fit around them — reads in particular are permitted more broadly. See docs/SANDBOX-BOUNDARY.md.
Implemented in TypeScript (Node + Electron). See DESIGN.md for the architecture and README-ts.md for the package/app layout.
The product is Plow Latch; the codebase is domo. The app ships, launches and
brands itself as Plow Latch. domo survives in the identifiers a rename must not
touch — the bundle id, the update-feed prefix, the @domo/* package scope,
DOMO_HOME, this repository's name, and the frozen vault Keychain identity
(DESIGN.md §11a-i, which records what renaming it cost the last time).
Agents reach this Mac today. The in-repo broker that used to route them here has been removed. A Mac now dials out to the Plow relay, which authenticates the calling agent and forwards MCP to an MCP server running in this app. Both halves exist here — that server and the outbound relay client — and so does the relay itself (see
CLAUDE.md§ Layout, "Rebuilt: a Mac dials out"). So the policy engine, the capability-derived sandbox, file operations, the audit log, the approval UI and the adversarial reviewer are not just covered by the test suite: they are the live path a real caller drives. What the relay leg's automated coverage does and does not reach is owned by README-ts.md § Integration coverage; the rest of that leg is verified by hand.
This repo is one of several that assemble a Plow agent. The map of which repo
owns what is in
plow-hermes-agent README § The repos;
read it before a change that touches a neighbour. The test is who else would
have to change if this fact changed — if the answer is a sibling, the change
belongs there; this repo only follows, by bumping its pin if it holds one.
Not here:
- The relay —
plow-pbc/plow,api/plow/relay/. It authenticates the calling agent and forwards MCP to this Mac. This repo owns the client that dials it (packages/relay-client,wire.ts) and the server it forwards to, not the relay. - Prompt framing of these tools —
plow-pbc/hermes-plugin-plow, theplow_chatplugin: the section that tells a runtime dropping MCPinstructionswhat the Mac tools are for.SERVER_INSTRUCTIONShere stays canonical; the plugin's section restates it for that one runtime, so a change to the wording here is a change to keep in step there. - Chat truth and the bounds on it —
plow-pbc/plow: the assistant name and its length bound, the daily payment cap, the roster. Plow stores and validates them. This Mac renders assistant names but does not edit them.
Examples:
- Adherence — #301 changed the canonical
SERVER_INSTRUCTIONSinpackages/mcp-server/src/handler.tsand re-guarded every compressed restatement inpackages/mcp-server/test/toolCopy.test.ts, so one asserter owns the sentence and a test fails when a copy drifts: #301
The justfile wraps every workflow behind named recipes. Run just with no
arguments to list them. Requires Node LTS and
just (brew install just).
just install # install workspace deps (first time)
just test # the test suite
just app # launch the desktop app| App / package | Role |
|---|---|
apps/desktop (Plow Latch) |
Electron app: runs the device core in its main process; approval windows, rules manager, audit viewer. |
packages/protocol |
Canonical JSON, Ed25519 identity, capabilities, intents, grants, rule keys. |
packages/transport |
The Connection seam and the outbound WebSocket client. |
packages/device-core |
The decision + execution path: policy engine, sandboxed executor, file ops, browsing subsystem, audit log. |
Intents carry a nonce and an expiry and are decided against stored always-allow rules keyed on the exact normalized capability set — never on goal text. Commands are sandboxed with per-invocation seatbelt profiles derived from the approved capabilities — derived from, not tightly fitted to; see docs/SANDBOX-BOUNDARY.md for what the profile actually permits. The audit log is append-only. See DESIGN.md §8.
Agent identity used to be an agent-held Ed25519 key pinned at access-grant time;
it is now asserted by the relay, which authenticates the agent before
forwarding. Note two things that are true of the code today: the device private
key is a plaintext seed in a 0600 file, while the Plow relay credential is
safeStorage-sealed where available and falls back to plaintext in the
owner-only 0600 settings.json.
Apache-2.0 — see LICENSE and NOTICE. Copyright 2026
The Plow Collective, Inc. The vendored vendor/browser-server/ directory
carries its own MIT license from upstream. The browser fetched at package
time (just package; just fetch-browser covers dev) is Camoufox (MPL-2.0),
which is not part of this repository; its license governs redistribution of
packaged builds that bundle it. The fingerprint pool is sampled at build time
with camoufox-js (MPL-2.0), a build-only dependency that does not ship.
"Plow" and the Plow logo are trademarks of The Plow Collective, Inc. The license grants no trademark rights.