Mori runs on your machine with your keys, and every risky action stops for your yes.
Mori is local-first. The agent, its tools, its memory, and its audit trail all live on your machine; the only outbound traffic is the model calls made with API keys you supply. There is no Mori cloud in the execution path, so there is no server-side actor that can run a tool on your behalf.
Every action passes through layered gates before it touches anything real:
Tool access is governed by isToolPermitted, which runs before every tool call. If agent mode is off, no tool is permitted, full stop. With agent mode on, the permissionLevel setting selects one of three tiers:
allowFileWriting, allowTerminalCommands, allowBrowserControl, allowAppLaunching. Raw keyboard simulation (type_text, press_keys, hotkey) is off unless you opt in with allowKeyboardControl — app-targeted typing is allowed, but blind keystrokes into whatever has focus are not.Read-only tools (read_file, list_files, git_status, search_history, and the rest of the verification set) are always permitted when agent mode is on — Mori can always check reality, even when it can't change it. A denied call doesn't fail silently: the denial is written to the action log with the exact reason, e.g. Permission denied: run_command is not allowed under current permission level (safe).
When you drive Mori from Discord, Telegram, or the phone app, confirmations are not a loose "reply yes" convention. Every approval request is a typed object with its own ID and a hard expiry:
An explicit "stop" or "cancel" from you on any surface halts the running task through the same machinery as the desktop Stop button, and that cancellation is logged too.
Mori's reply text is not treated as ground truth. After any turn that changed things, the post-turn verifier re-checks each successful mutation against reality: files it claims to have created are probed on disk, folders are listed, deletions are confirmed absent, and dev servers it says it started are probed over HTTP. The result is appended to the reply as a verdict line — ✓ Verified: 3/3 changes confirmed on disk — or a warning naming exactly what could not be verified. Verification is capped and time-boxed so it can never balloon a turn, and a pure-chat turn gets no verdict line at all.
A separate honesty check targets the highest-stakes claim: "I sent it." If the reply asserts an email or message went out but no send-capable tool actually succeeded that turn, Mori appends a correction stating that nothing was sent and the claim above is wrong. A fabricated "done" does not stand.
Before Mori overwrites or deletes a file, the prior bytes are copied into a per-file checkpoint history under the app's data directory. Snapshots are capped at ten per file and skip anything over 10 MB, so the history never bloats — and the snapshot step can never block or fail a write.
Three tools ride on this: list_checkpoints shows what's saved for a path, rollback_file restores the most recent snapshot, and search_history finds prior states. When Mori deletes a file it says so in the result: "a checkpoint was saved — rollback_file can restore it." Destructive file operations are undoable by design.
Mori can act as an MCP server so other agents — Claude Code, IDEs, scripts — can use it as a tool. That surface is deliberately minimal:
mori_create_task lands a card in your board's INBOX — a human triages it before any worker touches it.Each POST is handled by a fresh stateless server-transport pair, so nothing leaks between clients.
curl -X POST http://127.0.0.1:4519/mcp \
-H "Authorization: Bearer <token from Settings>" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'On Discord and Telegram, ownership is bound to your immutable platform account ID — captured once, never matched by display name — so impersonation by nickname is impossible. The phone app is owner-by-construction: it is signed into your account.
Anyone who is not the verified owner gets a hard-isolated experience. Strangers receive plain conversation from the model's own knowledge only: no tools, no screen, no files, no terminal, and no machine actions of any kind. Your saved memory is stripped from the prompt entirely for those chats (setSuppressMemory), so it cannot be socially engineered out of the model, and verification output from your concurrent tasks is never leaked into a stranger's reply. Their messages are treated as untrusted data — instructions to ignore rules, act on the machine, or reveal owner information are declined.
Underneath all of it sits the audit log: every completed tool call is appended to a per-profile audit.jsonl with timestamp, tool, argument summary, result, and status, alongside every approval decision. Auditing is best-effort by contract — it never blocks or breaks a task — but the record is durable, local, and yours to review or clear.