anymo Docs
Core concepts

Safety and permissions

Anymo's agents act on your machine, so capability is gated, audited, and reversible. Tools are safe by default, every call passes one policy engine, and there is no bypass anywhere in the codebase.

Three permission modes, nothing else

Every run executes under exactly one of three modes. There is no fourth mode and no "dangerously skip permissions" flag.

ModeBehavior
autoLow-risk tools proceed without friction. High- and critical-risk actions (shell, browser, desktop control, MCP tools) always stop and ask.
manualEvery tool call asks for approval before it runs.
planRead-only. The run can inspect and propose; anything destructive or with side effects is denied.

Set the mode per run with --permission auto|manual|plan, or from the permission pill in the desktop composer. Goal turns and skill runs obey the same mode.

One gate for every tool

All tool calls flow through the same policy engine, registry, and hooks: built-in tools, skills, MCP tools, browser tools, and desktop control. Denials are recorded as events. There is no second path.

Tools declare their needs

Every tool provides a ToolSpec that declares, up front:

  • required_permissions: a closed set (FileRead, FileWrite, FileList, NetworkFetch, BrowserControl, ShellExecute, PythonExecute, FinanceRead, MemoryWrite, SkillWrite, ArtifactWrite)
  • risk_level: none, low, medium, high, or critical
  • dry_run_supported, approval_required (a call always needs approval regardless of policy defaults), and executor_enabled (whether this build will actually run it)

A tool can never quietly need more than it declares.

The policy engine decides every call

Before a tool runs, the policy engine evaluates the request and returns allow, require_approval, or deny. The default policy is:

  • Shell and code execution always require approval.
  • Destructive file operations (overwrite, delete) require approval.
  • Otherwise, gate by risk: none, low, and medium are allowed; high and critical require approval.

Low-risk reads and ordinary in-workspace writes flow without friction, and only genuinely risky actions interrupt you. That keeps approvals meaningful instead of training you to rubber-stamp prompts.

Approvals are explicit, recorded, and time-boundable

When a call needs approval, the run enters awaiting_approval. You grant or deny, and the decision (approval.requested, granted, denied, expired) is appended to the event log. Granted actions proceed; denied actions never execute. Approvals can expire, so a forgotten prompt fails closed rather than blocking forever.

Respond from the CLI
anymo approvals --websocket 127.0.0.1:9300 --token <token>
anymo approve --websocket 127.0.0.1:9300 --token <token> --approval-id <id> \
  --decision approve --reason "looks safe"

Workspace containment

All agent file access goes through a confined workspace rooted at an allowlisted directory. It enforces, in order:

  • Workspace-relative paths only. Absolute paths and .. traversal are rejected lexically.
  • Symlink-escape denial. The nearest existing ancestor is canonicalized and must stay under the canonical root, so a symlink cannot point outside.
  • Size limits on reads and writes (default 10 MiB) and refusal of binary-as-text.
  • Atomic writes. Content is staged to a temp file, then renamed.

Tool outputs and stored values report workspace-relative, redaction-safe paths, never the absolute host path, so machine-specific paths never reach events, logs, the UI, or memory.

Secrets

How keys are held depends on how you run Anymo:

  • Desktop app: saved provider keys and connector tokens are sealed at rest, encrypted on disk with a master secret held in the OS keychain, with an encrypted-file fallback.
  • Headless CLI: a key is never persisted. The CLI reads ANYMO_PROVIDER_API_KEY from the environment each run.

In both cases, keys never appear in events, logs, artifacts, or the UI. Rendered provider errors redact bearer keys and custom header values before they enter the log.

Shell and code execution

shell.run and python.run exist as declared contracts whose executors are disabled in the current builds. They are registered so the protocol and UI know about them and so skills can reference them, but calling them returns an ExecutorDisabled error. Real process execution is a separate sandbox workstream with its own approval flow. Where process execution does exist today (local actions, the project harness, runtime coordination), it always runs through the guarded process path with an interactive approval.

Everything on the record

Approvals, denials, provider fallbacks, and tool results are events in the durable log. A run can be audited and replayed step by step, and what you were shown is exactly what replays. See Architecture for how the event log works.

Reporting security issues

The threat model and reporting process live in SECURITY.md.

Anymo ยท Apache-2.0 GitHub