Security¶
Argus runs LLM agents that browse the web, execute code and talk to other agents, so it assumes that model output and anything an agent reads are untrusted. This page summarises the controls in place. To report a vulnerability, follow the security policy; do not open a public issue.
Containment¶
flowchart TB
subgraph host [Docker host]
web["web :8080<br/>nginx, strict CSP"]
api["api :8000 (127.0.0.1)"]
subgraph internal_b [backend network, no egress]
pg[(postgres)]
rd[(redis)]
end
subgraph internal_s [sandbox network, no egress]
sb[sandbox]
end
end
web --> api
api --> pg
api --> rd
api -->|token| sb
api -->|HTTPS, SSRF-checked| internet((Internet))
api -.->|"opt-in, token"| br["browser"]
br -.->|"only via egress proxy"| internet
sb -.-x|no route| internet
- Containers: non-root users, read-only root filesystems (
api,web,sandbox), every capability dropped,no-new-privileges, CPU and memory limits, and a pid limit on the sandbox. - Networks: Postgres and Redis are on an internal network with no egress; the sandbox is on its own internal network, reachable only by the API with a shared token.
- Sandbox:
shellcommands run with a tmpfs work area and per-command rlimits; the workspace is copied in and changed files are versioned back. Destructive shell commands are blocked by a deterministic guardrail before they reach it. - Web access:
web_fetchallows onlyhttpandhttps, enforces the run's domain allow list, rejects hosts that resolve to private, loopback, link-local or metadata addresses on every redirect, caps size and content types, and treats page text as data. Webhook targets on private or loopback addresses are refused too. - Browser: opt-in; see The live browser.
- Protocols: stdio MCP servers spawn local processes, so they are off unless
ARGUS_MCP_ALLOW_STDIO=trueand then admin-only. New MCP servers default to theasktrust level and remote-agent delegations torequires_approval: true.
The live browser¶
With the browser compose profile on, agents can read pages in a real Chrome (the browse tool)
and you can watch it. A real browser runs page scripts, so it is contained more tightly than
web_fetch:
- Read-only. The agent can
open,snapshot,click,scroll,get_text,back,forwardandwait. Typing, filling, selecting, key presses, uploads, downloads and script evaluation are never offered, are refused by the browser service, and are denied again in agent-browser's own action policy (default deny by command name). Downloads are denied. Every non-GET page request fails, so no form can be submitted, including through a service worker. Requests a page's own script sends withfetchor XHR (a POST, say) are not blocked; this is an accepted residual risk. - No way to the inside. The
browsercontainer is on an internal network with only theegressproxy. The proxy resolves every host itself, refuses any private, loopback, link-local, metadata or reserved address, pins the connection to the address it checked, and allows only ports 80 and 443. The browser never reaches your intranet, even if you letweb_fetchdo so withARGUS_WEB_FETCH_ALLOW_PRIVATE_URLS;egresshas no switch for it. - Allowed domains apply to navigations. The API checks the address before
openand the page after every action, and goes back if a click landed somewhere not allowed. Images, scripts and other subresources are held only to the private-address rule, or narrowed runs would load broken pages.file:,chrome:,javascript:anddata:URLs are refused. - Page text is data. It reaches the model wrapped in content boundaries with a random marker the page cannot know, but a page can still try to steer an agent; the usual approvals and guardrails apply to what the agent does next.
- Watching is read-only. Live frames are relayed only to the run's owner, only while they watch, and never stored. No input goes back to the page.
- Residual risks. Chrome runs with
--no-sandboxin the container, as the same user as the browser service, so a Chrome renderer exploit could read the service token and reach other runs' sessions (it still cannot leave throughegress). Chrome is pinned and needs regular refreshes. On Docker engines before 26 the embedded DNS resolver can forward queries from internal networks. See TM-13.
Identity and access¶
- Passwords are hashed with Argon2id. Access tokens are short-lived JWTs; refresh tokens are single-use and rotated.
- API keys carry write scopes per area (see Integration). Keys cannot create or revoke keys or change passwords.
- Roles:
admin,memberandviewer. Viewers are read-only everywhere, including the protocol endpoints, whatever their key's scopes. - Requests are rate-limited per user or API key (
ARGUS_RATE_LIMIT_PER_MINUTE).
Secrets and data¶
- Provider API keys are encrypted at rest (
ARGUS_ENCRYPTION_KEY, or a key derived fromARGUS_SECRET_KEY) and never returned; responses show a maskedkey_hint. - Production refuses to start without a strong
ARGUS_SECRET_KEY(at least 32 characters). - Secret-like values in agent output and tool output are redacted by the guardrails.
- Webhook secrets are shown once. Account exports never include credentials, hashes or secrets.
- Every state-changing request is audited, including failed sign-ins; the protocol ledger is hash-chained and verifiable (Protocols and Ledger).
Web UI and exports¶
- The web container serves the UI with a strict Content-Security-Policy (
default-src 'self', no inline scripts,frame-ancestors 'none'),nosniff,no-referrerand a restrictive Permissions-Policy. The API adds HSTS in production. - Exports escape raw HTML from model output and drop unsafe link schemes.
- Skill uploads are validated: frontmatter rules, text files only, no hidden files or path traversal, and size limits checked before a zip is extracted.
Hardening checklist¶
- [ ] Put a TLS-terminating proxy in front of port 8080 and set
ARGUS_CORS_ORIGINS. - [ ] Set
ARGUS_ALLOW_REGISTRATION=falseonce accounts exist. - [ ] Set a separate
ARGUS_ENCRYPTION_KEYand keepdeploy/.env(mode600) out of backups that leave the host unencrypted. - [ ] Set
ARGUS_METRICS_TOKENif you scrape metrics. - [ ] Keep
approval_requiredon forshell,remote_agentandnetwork_writeunless the run uses the isolated sandbox. - [ ] Give API keys the narrowest scopes they need, and use
vieweraccounts for read-only users. - [ ] Copy
backups/off the host on a schedule. - [ ] If you turn the live browser on, run Docker 26 or newer and refresh the pinned Chrome regularly.