dev
← All ADRs

3-Layer Security Model for the Agent

ACCEPTEDEngine2026-05-30

Context

autograph merges two projects (AutoGraph v0 and ADE) to create a development environment with a sandboxed AI agent. The AI agent (OpenCode) runs in a Docker container and needs access to certain project files to work (read code, write modifications), but must not be able to access secrets, execute arbitrary commands on the host, or modify the infrastructure that runs it.

We need to formalize a clear security model that:

  • Gives the agent enough access to be useful
  • Prevents the agent from compromising system security
  • Is simple to understand and implement via Docker volumes
  • Allows the agent to propose changes to files it cannot modify directly

Threat model

The AI development agent is treated as a semi-trusted component operating within a sandbox. While it assists with development, it processes external data (LLM responses, web content) and operates with broad filesystem access. The security model ensures that even if the agent's behavior is compromised, damage is contained.

Component Trust Rationale
Operator (human) Full Makes all final decisions
Agent code output Semi-trusted Must be reviewed before deployment
Agent file edits Semi-trusted Confined to workspace
LLM provider responses Untrusted External, potentially adversarial
Container runtime Trusted Platform infrastructure

Security principles

  1. Least privilege - The agent has the minimum access required: read/write within workspace, read-only on infrastructure, no execution, no secrets, no network access to application services.
  2. Secret isolation - Credentials never enter the workspace. The env/ directory is not mounted. Template files contain only placeholders.
  3. Network isolation - The agent and application services are on separate networks with no route between them.
  4. No execution privilege - Docker socket is not mounted. task is not available in the container. The agent proposes; the operator executes.

Options considered

Option A - Flat RW (everything accessible)

The agent has read-write access to the entire project.

  • Advantage: Simple, no friction
  • Disadvantage: The agent can modify the engine, Taskfiles, secrets, its own configuration. No defense-in-depth. A prompt injection could compromise the entire system.

Option B - 2 layers (RW + inaccessible)

Two zones: the workspace (RW) and everything else (inaccessible).

  • Advantage: Simple, decent security
  • Disadvantage: The agent cannot read the engine, Taskfiles, or config. It works "blind" without context on the surrounding infrastructure. It also cannot read Git history.

Option C - 3 layers (RW + RO + inaccessible)

Three zones:

  • L1 (Workspace): Read-Write - source code, docs, delivery

  • L2 (Reference): Read-Only - engine, packages, Taskfiles, .git

  • L3 (Inaccessible): Not mounted - secrets, Docker socket, host

  • Advantage: The agent can contextualize itself (read the engine, packages, Git history) without being able to modify them. Real defense-in-depth. Proposal mechanism (exchange zones) for cases where the agent needs to suggest L2 changes.

  • Disadvantage: More complex to implement (multiple Docker volumes with different modes). Requires solving the technical challenge of .git in RO.

Decision

Option C - 3 layers.

The 3-layer model offers the best balance between utility and security. The agent has enough context to work effectively (it can read the engine, understand packages, consult Git history) while being contained within a strict write perimeter.

Consequences

Positive

  • Defense-in-depth: even if the agent is compromised (prompt injection), it cannot modify the engine, packages, or access secrets
  • Contextualization: the agent understands its environment (it can read Taskfiles, the engine, packages) which makes it more effective
  • Proposal mechanism: exchange.dev.tmp/proposed/ (dev-agent) and exchange.lab.tmp/ (metalab) with mirror path allow the agent to propose L2/L3 modifications that the operator validates manually

Negative / Risks

  • Docker complexity: mounts must be correctly configured in the agent package's compose fragment. An oversight could open unintended access.
  • .git in RO: Git needs to write certain temporary files for read operations. Requires a technical solution (to be solved in a dedicated epic, non-blocking for v1).
  • Maintenance: each new package must declare its mounts and diagnos must validate them.

Implementation

Layer Docker mount Example path
L1 :rw ${AUGR_PATH_WORKSPACE}:/workspace:rw
L2 :ro,nosuid,nodev,noexec ${AUGR_PATH_ENGINE}:/hostspace/engine:ro,nosuid,nodev,noexec
L3 Not mounted env/, Docker socket

The nosuid,nodev,noexec flags on L2 add an extra protection layer: even in read mode, binaries cannot be executed and device files are ignored.

Boundaries

Agent boundary (what the agent controls):

  • File content in L1 workspace (read + write)
  • Proposals in exchange.dev.tmp/proposed/ for L2/L3 files (operator reviews and applies)

Operator boundary (what the operator controls):

  • Starting and stopping containers (lifecycle tasks)
  • Managing secrets (env/)
  • Reviewing and applying proposed changes
  • Git operations (commit, push, branch)
  • Infrastructure commands (task augr:*)

Security incident response

If the agent detects credentials in versioned files, unsafe configurations, or any security issue:

  1. Stop all other work immediately
  2. Report the issue to the operator
  3. Do not attempt to fix it autonomously (may require git history rewriting)
  4. Wait for operator guidance

This is an absolute, non-negotiable rule. Packages implementing agent containers (e.g., pkg.agent) provide detailed hardening documentation (container privileges, resource limits, AppArmor, user isolation) in their own docs/security/ directory.