Skip to content

Safety model

Handing an agent a machine is the whole point of this tool, which is why the limits are part of the structure rather than a warning in a README. Each one below is a place the code says no.

A command that needs a screen says so, up front

Section titled “A command that needs a screen says so, up front”

Every command declares NeedsDisplay. On a machine with no display it is refused before it runs, with the fix:

Terminal window
$ aos window list
aos: "window list" needs a display, and this machine has none
Start one with `aos headless start`, or check `aos headless status`.

The alternative — failing three layers deep inside a display-server call — gives an agent an error it cannot act on. See headless machines.

file delete will not delete the things you cannot undo

Section titled “file delete will not delete the things you cannot undo”

Filesystem roots, $HOME, and system directories are refused outright:

Terminal window
$ aos file delete /
aos: refusing to delete /
$ aos file delete $HOME
aos: refusing to delete /Users/muthuishere

This is not a confirmation prompt, because an agent will answer a prompt.

Serving this registry is remote control of the machine. Reaching it from anywhere else should be a deliberate act — an SSH tunnel, or an explicit --addr — not the default.

remote share is marked blocking, and blocking commands are excluded from the tool list. That is a security property, not a UX one: an agent connected over MCP cannot quietly open a desktop-control channel. A person has to start the share at a terminal, watch it run, and Ctrl-C to revoke it. The link lives exactly as long as the process a human is looking at.

Every service aos creates is named aos.<name>, and service list and service remove only ever see that namespace. This CLI cannot delete a service it did not create. Everything is per-user — a launchd user agent on macOS, a systemd --user unit on Linux, a Scheduled Task on Windows — so nothing here asks for admin or sudo.

A file in the config directory must not be able to change what a shipped command does. An adapter that collides with a built-in route loses, and a malformed adapter is reported by commands --check rather than silently ignored. Plugins follow the same rule: a built-in wins over an executable of the same route.

A span carries the route, the source (cli or mcp), the exit code, the duration, and the number of arguments. Never the arguments themselves — they routinely carry paths, message bodies and clipboard text. Nothing is sent anywhere; see observability.

watch clipboard hides the clipboard by default

Section titled “watch clipboard hides the clipboard by default”
Terminal window
$ aos watch clipboard
{"event":"clipboard","at":"…","seq":1,"length":34,"digest":"7a37ded45b5e"}

Length and digest, so a watcher can tell that something changed. --content is opt-in.

exec run, exec shell and exec capture run what you give them. There is no sandbox and no command allowlist — that would be a different product, and a false sense of one is worse than none. If an agent should not be able to run arbitrary commands on a machine, do not expose the exec group:

Terminal window
aos serve mcp --groups=window,capture,display