Security and Trust
RimZ runs as you: no daemon, no elevated privileges, no account of its own.
RimZ runs as you: no daemon, no elevated privileges, no account of its own. It reports on the agents you already run, inside the terminal you already use. Its security model follows from that. RimZ asks before it changes anything on your machine, every change has a matching undo, and the only places a config file can make RimZ run a command are decisions you make by hand.
This page covers three questions: what RimZ touches, what can execute, and what leaves your machine. The design commitments behind them are in DESIGN.md.
What RimZ changes on your machine
Three things, each previewable before it happens and reversible after.
Reporting hooks in your agent configs. To show an agent live in the sidebar, RimZ adds hook lines to that agent's own config (~/.claude/settings.json, ~/.codex/config.toml, and the like). Run rimz hooks install --dry-run for the exact per-agent diff before anything is written. The install is additive, so your existing hooks stay, and each hook is one rimz hooks feed line that reports events. Answering a prompt always stays with you, in the agent's own UI. rimz hooks uninstall removes exactly what was added and restores any statusline RimZ wrapped. The whole flow is walked in set up your machine.
Files under your home directory. Configuration lives under ~/.config/rimz/, durable room state under ~/.local/state/rimz/, both plain files you can read and edit. rimz uninstall removes hooks, rooms, runtime state, and the binary, with flags to also drop the stores and config (maintenance reference).
Disposable Copilot telemetry. A newly-born room can hold agent-telemetry/copilot-otel.jsonl under its mode-0700 runtime root. RimZ forces message-content capture off for this managed exporter and reads only allow-listed model/token metadata for the exact Copilot conversation. Reset and runtime GC remove the cache; RimZ neither rotates a live exporter file nor derives account history from it. An explicit user exporter remains user-owned and takes precedence.
A permission grant for its Zellij plugin. On Zellij, RimZ seeds a permission grant for the presence plugin it ships, so the first attach runs without an interrupting prompt. Your config.kdl stays untouched, and the grant is yours to revoke. Details in The Zellij presence plugin.
What can run commands
Inside a workspace, plenty already runs as you: hooks, postinstall scripts, generated binaries, test runners, and the agents themselves. Same-user isolation is no real boundary there, so RimZ does not lean on it. Instead it makes command execution an explicit choice in exactly two places.
Project trust
A cloned repository can ship a .rimz/config.toml that names agents, profiles, teams, loop tasks, hooks, and environment variables, and any of those can run a command. So RimZ keeps the whole file inert until you trust the workspace: on an untrusted clone it reads only structural metadata, and nothing the file declares can launch.
A first interactive rimz start in an untrusted workspace offers the grant, and the interactive rimz loop fire and project-task edit surfaces offer it inline with the surface diff when their trust gate is closed. Declining leaves the workspace inert; only the first-start prompt stays suppressed until .rimz/config.toml changes.
rimz trust grant pins a single hash over every command-running field in that config. Each later read re-hashes the live file. Edit one of those fields, or let an agent in the room edit the file, and the hash drifts: the workspace flips to stale, command execution turns back off, and rimz trust status prints a field-level diff of what changed before you re-grant. There is no background sweep. rimz trust status and rimz doctor both re-hash on the spot.
The hashed surface is every field that can cause a process to run:
[[agents]]:name,launch_command,env[profiles.<name>]:agent,mode,model,effort,system-prompt-file,append-system-prompt-file,args[agents.teams.<name>]:layout, and each role's profile and launch fields[tasks.<name>]: the loopagent,prompt,checkandverifycommands, and the run and schedule options[[hooks]]:event,command[env]: every key and value
Room layout stays out of a repo's reach: a project config carrying a [layout] table (including tmux status #(...) and popup commands) is refused, with the fix to move it to your per-machine config. Any field RimZ can execute must enter this hash, and a unit test fails if one slips out. The mechanics, the grant record, and the diff are in the trust internals; the command is in the trust reference.
Per-machine loop schedules are separate: a check = "<shell>" line in your own ~/.config/rimz/loop.toml is your command, not a repo's. A clone can supply only project [tasks], and those stay inert until you grant, unless a same-named machine task is already the one that runs.
Notification handlers
Notification handlers run a command of your choosing when a row needs attention ([[notifications.handler]], or the legacy [notifications].command). They live only in your per-machine ~/.config/rimz/config.toml, so a clone can never supply one. They run under your user id, spawned by the sidebar process, and often carry local push credentials. A handler that acts back on the room should treat pane text and transcripts as untrusted data: match a bounded prompt shape, and stay silent on anything else. Wiring is in the notifications internals.
The Zellij presence plugin
On Zellij, RimZ loads a small presence plugin into each session so the sidebar learns pane layout by push, and a tab switch lands back on your work instead of the sidebar (internals). Zellij normally prompts before a plugin gets permissions; RimZ seeds that grant ahead of load, keyed to the exact plugin path it materializes, so the first attach is not interrupted. The grant covers:
- Access Zellij state: the plugin watches pane and tab shape.
- Run commands: it runs the RimZ-owned
rimz sidebar wakeandrimz sidebar focuscalls, and nothing else. - Reconfigure: it applies the room's mouse options and, if you set one, binds the focus key, both at runtime without writing your
config.kdl. - Start web server: added only when
[web] enabled, sorimz web opencan share a running session.
The plugin's code, argv, and configuration are all RimZ-owned, never your config.kdl, and it ships no pane content anywhere. The grant lives in Zellij's own permission store, where its plugin manager can revoke it; revoking stops pane discovery until you restore it, and rimz doctor names the fix.
What leaves your machine
RimZ keeps your work local. Your prompts, transcripts, pane text, file paths, and credentials stay on the box. The network calls RimZ makes reuse logins you already hold and reach only services you already use:
- Provider usage. To fill the cost and budget meters, RimZ reads each provider's usage endpoint with the OAuth login the agent already holds, contacting only the provider you are signed in to.
- Pull-request status. The sidebar's PR marker runs your own
gh(GitHub) ortea(Gitea, Forgejo, Codeberg) against your forge, on your existing login. It reads no RimZ secrets and adds no config field, so it stays off the trust hash. - Pets. Enabling
[theme.pets]fetches a sprite sheet over HTTPS from the host you name (a built-in pet reaches the public Codex pets CDN; a local-path pet fetches nothing).RIMZ_PETS_OFFLINE=1makes the process tree cache-only. The request carries the asset URL and nothing else. - Off-box error reporting. Off by default and opt-in. See Off-box error reporting.
Hook payload privacy
Hook payloads can carry prompts, tool inputs, file paths, command arguments, and errors. They stay in RimZ's local state, and nothing forwards them off the box. Payload-fidelity and retention controls (a [privacy] block with payload_mode and retention_days) are a planned project surface, tracked in configuration.md against the hook adapter boundary.
Off-box error reporting
Release binaries ship without the reporting code, so a stock install makes no Sentry calls whatever the config. Reporting compiles in only under --features sentry, and even then it is off until you set [sentry] dsn (or RIMZ_SENTRY_DSN). The DSN is per-machine and never the committed project config, so a clone or pull cannot redirect a contributor's telemetry.
When on, it sends RimZ's own warn! and error! events and the provider conditions RimZ observes (rate limits, overload), tagged with low-cardinality facts: the release and build id, the running command, a fault class, and the agent or session id when known. The hostname is stripped, Sentry's default personal-data collection is off, and hook payloads, prompts, and transcripts never ride along. A failed account-usage probe reports the request's host, never its path or query. The full payload and the config knobs are in the diagnostics internals and configuration.md.
Pane text is data, not instructions
rimz pane capture returns raw terminal text. RimZ core treats it as data: it never parses it for correctness and never types on your behalf. If you script an answer through the pane primitives, match a bounded prompt shape and abstain when unsure. Feeding captured text into an LLM prompt as if it were a user message is the classic prompt-injection footgun, so keep it as data.
Unattended runs
An unattended run still decides permissions the agent's own way: either you keep every native prompt (each becomes a waiting row that routes to you) or you pass the agent's own bypass flag. The tradeoffs, and what an out-of-range agent version does to the prompt path, are in the loops permission posture.