3-Layer Security Model for the Agent
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
- 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.
- Secret isolation - Credentials never enter the workspace. The
env/directory is not mounted. Template files contain only placeholders. - Network isolation - The agent and application services are on separate networks with no route between them.
- No execution privilege - Docker socket is not mounted.
taskis 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) andexchange.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.
.gitin 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
diagnosmust 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:
- Stop all other work immediately
- Report the issue to the operator
- Do not attempt to fix it autonomously (may require git history rewriting)
- 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.