Security

Security

Agor coordinates AI agents that read, write, and execute code on your machine. Treat that like any other powerful tool: scope access carefully, harden the host, and pick the deployment mode that matches your trust boundary.

This page covers the four deployment modes Agor supports, what each enforces, the trust boundaries you must understand, and the operational guidance for production-ish environments.

Status: Agor is pre-1.0. The core auth/authorization and execution model are solid; some features (message gateway, web terminal in simple mode) trade safety for ergonomics by design and must be opted into knowingly.

Security audit conducted using Agor itself

Pick a Deployment Mode

Agor has four canonical modes. Choose based on who shares the daemon and how much you trust them.

Modeworktree_rbacunix_user_modeWho’s it for?
Dev / SolofalsesimplePersonal machine, trusted team of one
Local TeamtruesimpleSmall team, RBAC for organization, no Unix isolation
InsulatedtrueinsulatedShared dev box, filesystem protection, no per-user processes
StricttruestrictProduction / compliance — full per-user OS isolation
# ~/.agor/config.yaml
execution:
  worktree_rbac: false        # default: false
  unix_user_mode: simple       # simple | insulated | strict
  executor_unix_user: agor_executor   # required for insulated

The full setup guide for Insulated and Strict is in Full Multiplayer Mode.


Mode 1: Dev / Solo (Default)

execution:
  worktree_rbac: false
  unix_user_mode: simple

What it is: Open access. All authenticated users can act on all worktrees. Everything runs as the daemon user.

What’s enforced:

  • ✅ Authentication — REST and WebSocket calls require a JWT
  • ✅ Role checks — privileged endpoints (terminals, env controls) require admin
  • ✅ Encrypted credentials at rest (AES-256-GCM with AGOR_MASTER_SECRET)
  • ✅ Path traversal protection on file browsing
  • ✅ Body size limits (10MB)

What’s NOT enforced:

  • ❌ Per-worktree permissions
  • ❌ Filesystem isolation between users
  • ❌ Process isolation (everything is the daemon user)

Use it for: Personal laptops, solo dev, single-tenant evaluation.

Don’t use it for: Anything multi-user where users shouldn’t impersonate each other.


Mode 2: Local Team (RBAC, no Unix isolation)

execution:
  worktree_rbac: true
  unix_user_mode: simple

App-layer permission checks for worktree ownership and access tiers. No OS-level isolation — all execution still runs as the daemon user.

Adds:

  • ✅ Worktree owners + others_can permission tiers (see below)
  • ✅ Settings UI for managing access
  • ✅ API enforcement of view/session/prompt/all permissions

Still missing:

  • ❌ Filesystem ownership (all files are daemon-owned)
  • ❌ Process identity (sessions don’t run as the user who created them)
  • ❌ Credential isolation (one user’s API keys are reachable by an admin)

Use it for: Small teams who want some structure but trust each other on the OS layer.


Mode 3: Insulated (Worktree groups + executor user)

execution:
  worktree_rbac: true
  unix_user_mode: insulated
  executor_unix_user: agor_executor

Adds per-worktree Unix groups (agor_wt_*) and a dedicated executor user. Filesystem permissions naturally enforce worktree boundaries.

Adds (on top of Mode 2):

  • ✅ Per-worktree Unix groups, scoped filesystem access
  • ✅ Executors run as a dedicated, less-privileged user
  • ✅ Sessions can’t reach files outside their worktree

Still missing:

  • ❌ Per-user identity for processes (everything still runs as the executor user)

Requires: Sudoers configuration, executor Unix user.

Use it for: Shared dev servers; you want filesystem protection but don’t need per-user attribution at the OS layer.


Mode 4: Strict (Full per-user OS isolation)

execution:
  worktree_rbac: true
  unix_user_mode: strict

Each Agor user must have a unix_username. Sessions execute as that user. Per-user $HOME, SSH keys, GitHub tokens, API credentials.

Adds (on top of Mode 3):

  • ✅ Each user gets their own Unix account (agor_alice, agor_bob, …)
  • ✅ Sessions run as the session creator’s Unix user
  • ✅ Per-user credential isolation (~/.ssh/, ~/.gitconfig, etc.)
  • ✅ Real audit trail in /var/log/auth.log and process ownership
  • ✅ Files created have correct ownership

Requires: Sudoers configuration, Unix user per Agor user.

Use it for: Production, compliance-sensitive deployments, any environment where “what would a malicious user do” is a real question.


Worktree Permission Tiers (RBAC)

When worktree_rbac: true is set, every worktree has owners plus an others_can tier that controls what non-owners may do:

TierWhat others can do
noneNo access — completely private to owners
viewRead worktrees, sessions, tasks, messages
session (default)Create their own sessions; prompt their own sessions only
promptPrompt any session, including others’ (but each session still runs as its creator’s identity)
allFull control (create/update/delete sessions, change permissions)

The session tier is the safe default — collaborators can work independently without impersonating each other’s OS identity.

Session Sharing (dangerously_allow_session_sharing)

A worktree-level toggle, off by default. When OFF, spawning/forking attributes the new child to the caller — it runs as the caller’s Unix user. When ON (legacy behavior), children inherit the parent’s created_by, meaning a collaborator can effectively spawn agents that execute as the original session owner.

Cross-user spawns under the legacy path emit [SECURITY] warning logs. See context/explorations/session-sharing.md for the full discussion.


What’s Enforced Today

Independent of mode:

  • Authentication on every call — REST and Socket.IO require a JWT, API key, or valid session token. There is no anonymous / unauthenticated path.
  • Role-based authorizationviewer / member / admin / owner gate privileged endpoints (terminals, env controls, config, user management).
  • Encrypted credentials at rest — Passwords with bcrypt (12 rounds), API keys and per-user env vars with AES-256-GCM under AGOR_MASTER_SECRET.
  • Path traversal guards — File browsing is restricted to context/ within each worktree with traversal protection.
  • MCP token security — Short-lived (24h default) JWTs in Authorization: Bearer header. Query-string tokens are rejected.
  • Request size limits — 10MB body cap to prevent oversized-payload DoS.
  • Executor isolation — SDKs run in separate processes; no direct DB access; just-in-time API keys via IPC. See Architecture.

Things to Know Before You Go Multi-User

The Master Secret (AGOR_MASTER_SECRET)

Agor encrypts stored credentials with a master secret. Set it before starting the daemon in any environment that handles real keys:

export AGOR_MASTER_SECRET="$(openssl rand -hex 32)"
agor daemon start

If unset, Agor falls back to plaintext storage and logs a warning. Never run a shared deployment without it.

Web Terminal Trust Boundary

The browser terminal is enabled by default for member+ users. In unix_user_mode: simple, the terminal runs as the daemon user. That means a member-tier user can shell out and read ~/.agor/config.yaml, agor.db, the JWT secret, and every API key the daemon knows about.

Three ways to handle this:

  1. unix_user_mode: strict — terminal runs as the user’s Unix account. Safe.
  2. unix_user_mode: insulated — terminal runs as the executor user, scoped by worktree group. Mostly safe.
  3. execution.allow_web_terminal: false — disables the terminal entirely.

The daemon prints a startup warning when the unsafe combination is active.

Message Gateway is High-Trust

Anyone who can DM the Slack bot (or @agor-mention on a wired GitHub repo) can prompt an agent to read, write, and execute code in the configured worktree. Treat enabling the Message Gateway as giving message senders SSH access to that worktree.

Mitigations: dedicated worktrees, restricted bot distribution, supervised permission mode, and Unix isolation.

Custom Config Path Is Half-Wired

--config and AGOR_CONFIG_PATH set the config file location, but not every code path threads through it (notably getDatabaseUrl). If you rely on an alternate config location and hit weird DB resolution issues, file an issue.

Per-User Secrets Still Live on the Daemon

Per-user API keys and env vars are encrypted at rest and only decrypted when needed. But the daemon hosts the secrets. A malicious admin or compromised host can still recover them. Invite admins you’d trust with your tool credentials.

Admins Have Wide Reach

Users with the admin role can manage other users, configure MCP servers, view system health, and (in simple mode) effectively reach everything on disk via terminal. Limit admin assignments and use Unix isolation modes for shared environments.


Deployment Guidance

  • Keep the daemon behind a firewall, VPN, or private network. Pair with a reverse proxy if you need SSO or IP allowlists.
  • Patch the host like any shared dev box. Monitor logs, rotate credentials, audit users.
  • Prefer scoped or temporary API credentialsOPENAI_API_KEY for an artifact session shouldn’t be your team’s master key.
  • Rotate the master secret periodically. Re-encrypt at rest by triggering a settings update flow.
  • Audit /var/log/auth.log in Unix isolation modes — sudo activity is the trail.
  • Pilot before scaling — run the first weeks of multi-user use with logs open.

What’s on the Roadmap

  • Rate limiting & quotas — Per-user limits on session count and API spend
  • Enhanced audit log UI — In-product viewer for security events
  • Hardened MCP token management — More granular scoping, revocation
  • Resource usage tracking — Per-user cost dashboards (partial today via Settings → Analytics)
  • Formal pen testing — Ongoing; until then, keep daemons inside trusted network boundaries

Reporting a Vulnerability

Please email security@preset.io (or open a private security advisory on GitHub) before filing a public issue. We’ll respond promptly.


BSL 1.1 © 2026 Maxime Beauchemin