Skip to main content

Security model

What omac protects against

Network

An agent with outbound network access can exfiltrate source code and send data to unintended endpoints.

omac routes outbound TCP traffic through its own proxy. When the agent tries to reach a host that is not in your allow list, a native dialog asks you to approve or deny it — once, for the session, or permanently. If no dialog is available (CI, headless server), the request is denied by default.

The agent can supply an intent reason for each request (via the sandbox API). That text is displayed in the dialog but is sanitized before rendering: markup characters and control sequences are stripped so the agent cannot forge the dialog's own labels or add visual structure that could mislead the decision.

UDP and ICMP egress is blocked by a seccomp filter on Linux (in kernel-enforced mode); on macOS and in env-only mode, those protocols are not intercepted.

One port is pre-approved in the default profile: port 22 (SSH), to allow standard git-over-SSH operations. Traffic to port 22 on any host bypasses the proxy and is not subject to the domain allow/deny list.

Cloud instance-metadata endpoints (169.254.169.254, 100.100.100.200, 192.0.0.192, 169.254.170.2, fd00:ec2::254, metadata.google.internal, metadata.azure.internal) are blocked unconditionally and cannot be approved interactively. Any hostname resolving to a loopback, unspecified, link-local, or metadata address is also blocked.

host.docker.internal (the Docker bridge gateway, typically 172.17.0.1) is not in the hard-deny set. It is a private RFC 1918 address and is subject to the normal prompt or allow/deny policy. If you run Docker and want to prevent the agent from reaching it, add it to network.deny_domain.

Filesystem

An agent with broad filesystem access can read SSH keys, cloud credentials, or unrelated projects, and can write outside its intended scope.

omac gives the agent access only to what it needs:

  • Your working directory (read and write).
  • The config directories the active harness requires. Each harness declares exactly which directories it needs; omac injects them at launch.
  • Your development tools — compilers, package managers, and build systems such as Go, Rust's Cargo, and Node.js via nvm — are readable so the agent can compile and run code. These tools are installed in your home directory rather than system directories, so omac grants them explicitly. The full list is in the reference table below.

Even when you grant broader access, certain paths are always blocked: ~/.ssh, ~/.gnupg, ~/.aws, ~/.kube, and .env / .envrc files anywhere in the project. You can block additional file patterns by adding glob entries to filesystem.deny in ~/.config/omac/sandbox-profiles/default.json — for example, "*.key" blocks all files ending in .key inside any directory the agent can access. See Configuration for how to edit the sandbox profile.

Secrets

Integrations (GitHub, GitLab, Jira, email) need API tokens. If the agent holds a token directly, a prompt injection can leak it.

omac keeps tokens on the host side of the boundary. They are stored in the OS keychain (Keychain on macOS, Secret Service on Linux) and injected only into the sidecar process for the relevant skill as an environment variable. The agent calls that skill through the facade and never sees the actual token.

How isolation works

omac ships no kernel module or custom isolation layer. It uses security primitives built into the OS so confinement is enforced by the kernel.

macOSLinux
SandboxSeatbelt (sandbox-exec)bubblewrap + Landlock
Secret storeKeychainSecret Service
Prompt dialogAppleScriptzenity / kdialog

Sandbox access reference

The table below lists which paths and environment variables the sandbox can and cannot access.

Path or variableAccessWhy
<workdir>read + writeYour project files
Harness config dirs (e.g. ~/.claude, ~/.local/share/opencode, ~/.local/share/opentui)read + writeThe harness stores its state and credentials here; omac pre-creates declared first-use dirs (e.g. OpenCode's opentui tree-sitter grammar cache) before sandbox grant resolution
omac-managed tool cache (isolated from ~/.cache; see Cache)read + writeBuild artifacts and downloaded packages; isolated from your host caches
Language toolchain binaries (~/.cargo/bin, ~/go/bin, ~/.nvm, ~/.bun/bin, ~/.rustup)read-onlySo installed compilers and build tools can run
Shared skills dirs (~/.config/agents/skills, ~/.agents/skills)read-onlySo the agent can read skill descriptions (SKILL.md)
Git config (~/.gitconfig, ~/.gitignore_global)read-onlyRead commit settings and global ignores; write access would affect all your repos
System directories (/usr, /bin, /lib, /etc, …)read-onlyOS binaries and libraries needed to run any command (bash, git, python, …)
$TMPDIR (private per-launch dir)read + writeTemporary files during the agent's work; on Linux a private tmpfs is mounted over /tmp so nothing the agent writes there is visible on the host or to other sessions
Facade socket (in ~/.local/state/omac/run/ on Linux, ~/Library/Application Support/omac/run/ on macOS)connectThe socket the agent uses to reach skill sidecars; created by the facade at an unpredictable path the agent cannot pre-compute
~/.ssh, ~/.gnupg, ~/.aws, ~/.kube, …blockedSensitive credentials
~/.npmrcblocked; registry addresses can be shared as a stripped copyUsually holds an access token. See Private package registries
~/.config/omac (approval store, sandbox profiles, global registry)blockedThe agent must not be able to forge skill approvals; protected in the baseline even under broader grants
.env / .envrc files (including nested ones inside the project)blockedOften contain secrets
~/.cache, ~/Library/Caches (host cache roots)blockedPrevents cross-project cache poisoning; omac provides its own isolated cache
Files matching filesystem.deny patterns (e.g. *.key)blockedUser-defined extra restrictions
Environment variables in allow_vars (OMAC_*, HOME, PATH, LANG, …)passed throughOperational minimum
All other environment variables (cloud secrets, DOCKER_HOST, SSH_AUTH_SOCK, …)strippedNot needed and potentially sensitive

Self-authored skills

Skill sidecars run on your machine outside the sandbox. That is intentional, because skills need to make network requests and access API credentials — and those credentials must never reach the agent. But this creates a risk: the agent can write files in your working directory, including the skill source folders. If omac simply ran whatever skill code it found there, a malicious agent could write a skill, get it spawned, and use it to read your SSH keys or exfiltrate data.

omac prevents this with an approval store at ~/.config/omac/approvals.json. A skill can only run if it has been explicitly approved by a human running omac register in a real terminal, or by the marketplace sidecar after installing a skill. The sandbox never mounts ~/.config/omac, so the agent cannot create or modify approvals itself.

Skill names and descriptions shown during omac register and in the agent's system prompt are sanitized before display. Control sequences and markdown structure characters are stripped, so a skill directory with a hostile name cannot inject fake instructions into the approval output or the system prompt.

Each approval is tied to a specific version of the skill's code via a bundle hash. If the skill's files change after approval, the hash no longer matches and the spawn is refused. The agent cannot sneak in modified code by editing a skill after it was approved. At approval time, the skill directory is frozen into a host-only snapshot; the sidecar is always spawned from that snapshot, never from the still-agent-writable workdir.

When upgrading omac on a machine with existing registered skills, only user-global skills (registered outside any workdir) are automatically approved for the first run. Workdir-local skills — which the agent can write — always require an explicit omac register from a host terminal.

If you change an approved skill yourself, omac refuses to run it until you review the change and re-register it with omac register --force.

Launcher config trust

The launcher config (oh-my-agentic-coder.yaml) controls which sandbox command omac runs on your machine. That command executes before any confinement exists, with your full user environment, so it must not be under the project's control.

omac enforces this: security-sensitive launcher fields (sandbox.*, audit.*, facade.base_env_passthrough) are accepted only from your user-global config (~/.config/omac/config.yaml) or omac's compiled-in defaults. A project-local <workdir>/.opencode/oh-my-agentic-coder.yaml can only contribute operational settings (cache scope, facade timeouts). Opening a repository cannot change the sandbox runtime, disable the audit trail, or redirect audit logs.

Sandbox policy grants (filesystem paths, network hosts, environment variables) live in the sandbox profile (~/.config/omac/sandbox-profiles/default.json), which has no project-local equivalent. Profile paths referenced in launcher templates are likewise restricted to that directory.

Environment filtering

The sandbox does not inherit all environment variables from the shell that launched omac. It starts from an explicit allow list — the OMAC_* prefix, basic system variables (HOME, PATH, LANG, …), and the key the selected harness needs to call its AI provider — and strips everything else, including any ambient cloud tokens, before the agent starts.

Known limitation — the harness's AI provider credentials are reachable inside the sandbox. This is unavoidable, since the harness needs them to function. For harnesses like claude-code, the key arrives as an environment variable (ANTHROPIC_API_KEY). For harnesses like opencode, credentials are stored in the harness config directory (~/.local/share/opencode), which is mounted inside the sandbox. Either way, a sufficiently capable agent could read them. Skill secrets are fully isolated; harness credentials are not.