Skip to content

Security Best Practices

Maciej Mensfeld edited this page Apr 9, 2026 · 9 revisions

Security Best Practices

COI provides multiple layers of security to protect your host system from potentially malicious code generated or modified by AI tools.

Automatic Path Protection (Default)

COI automatically mounts security-sensitive paths as read-only by default. This prevents containers from modifying files that could execute automatically on your host system.

Default Protected Paths

Path Risk Protection
.git/hooks Git hooks execute on commits, pushes, etc. Read-only mount
.git/config Can set core.hooksPath to bypass hooks protection Read-only mount
.husky Husky git hooks manager Read-only mount
.vscode VS Code tasks.json can auto-execute, settings.json can inject shell args Read-only mount

How It Works

When you start a session, COI:

  1. Detects which protected paths exist in your workspace
  2. Mounts them as separate read-only devices over the workspace mount
  3. Reports which paths were protected in the startup output
Protected paths (mounted read-only): .git/hooks, .git/config, .vscode

Opting Out

If you need the AI to manage git hooks or other protected paths:

# Via project config (.coi/config.toml in workspace)
[git]
writable_hooks = true

Configuring Protected Paths

You can customize which paths are protected via the [security] config section.

Add Additional Paths

Protect additional paths without replacing the defaults:

# ~/.coi/config.toml or .coi/config.toml
[security]
additional_protected_paths = [".idea", "Makefile", ".gradle"]

Replace Default Paths

Replace the default list entirely (use with caution):

[security]
protected_paths = [".git/hooks"]  # Only protect .git/hooks

Disable Protection

Not recommended, but available if needed:

[security]
disable_protection = true

Committing AI-Generated Code

Even with automatic protection, it's good practice to disable hooks when committing AI-generated code as an extra safety layer:

# Commit with hooks disabled
git -c core.hooksPath=/dev/null commit --no-verify -m "your message"

# Create an alias for convenience
alias gcs='git -c core.hooksPath=/dev/null commit --no-verify'

Why This Extra Step?

  • Belt and suspenders: Multiple layers of protection are better than one
  • Covers edge cases: Protects against scenarios where protection might be disabled
  • Good habit: Useful even outside of COI when reviewing AI-generated code

Full Protection Command

Disable both hooks and git attribute filters:

git -c core.hooksPath=/dev/null -c core.attributesFile=/dev/null commit --no-verify -m "msg"

# As a shell function
safe_commit() {
    git -c core.hooksPath=/dev/null -c core.attributesFile=/dev/null commit --no-verify "$@"
}

Attack Vectors COI Protects Against

Git Hooks Injection

Risk: AI modifies .git/hooks/pre-commit to execute malicious code on your next commit.

Protection: .git/hooks is mounted read-only by default.

Git Config core.hooksPath Bypass

Risk: AI sets core.hooksPath in .git/config to point to a malicious hooks directory elsewhere in the workspace.

Protection: .git/config is mounted read-only by default.

Husky Hooks Manipulation

Risk: AI modifies .husky/pre-commit or other husky hooks.

Protection: .husky directory is mounted read-only by default.

VS Code Task Injection

Risk: AI creates/modifies .vscode/tasks.json with tasks that auto-execute when you open the project in VS Code.

Protection: .vscode directory is mounted read-only by default.

VS Code Settings Injection

Risk: AI modifies .vscode/settings.json to inject malicious shell arguments via terminal.integrated.shellArgs.

Protection: .vscode directory is mounted read-only by default.

Symlink Security

COI rejects symlinked protected paths to prevent attacks where a symlink could trick COI into mounting arbitrary host paths as read-only (or failing to protect the real path).

  • Symlinked .git directories (git worktrees) are safely skipped
  • .git as a file (submodules) is safely handled
  • Symlinked protected paths like .vscode -> /etc/passwd are rejected

Permission Mode

By default, COI bypasses all tool permission prompts so sessions run autonomously. For higher-security workflows, you can enable interactive mode so the tool asks before running each command:

# .coi/config.toml or ~/.coi/config.toml
[tool]
permission_mode = "interactive"

This affects both Claude Code and opencode — see the Supported Tools page for details on how each tool behaves in interactive mode.

SSH Agent and Environment Variable Forwarding

COI provides opt-in mechanisms to selectively share host resources with containers:

  • SSH agent forwarding ([ssh] forward_agent = true) — Bridges the host's SSH agent socket into the container. The container can use your SSH keys for git operations without the keys themselves being copied. Disabled by default.
  • Environment variable forwarding (forward_env in config) — Forwards specific host env vars by name. Values are read at session start and never stored in config.

Security considerations:

  • Only enable SSH agent forwarding when you need git-over-SSH inside the container
  • Only forward the minimum set of environment variables needed (e.g., API keys for the AI tool)
  • Forwarded env vars are accessible to all processes in the container, including the AI tool
  • For maximum isolation, prefer passing API keys via the tool's config file rather than env vars

Network Isolation

In addition to path protection, COI provides network isolation to prevent data exfiltration:

  • Restricted mode (default): Blocks access to private networks (RFC1918)
  • Allowlist mode: Only allows access to specific domains
  • Open mode: No restrictions (use only for trusted projects)

Real-Time Security Monitoring

In addition to passive protections, COI includes active Security Monitoring that:

  • Detects threats in real-time: Reverse shells, credential scanning, data exfiltration
  • Responds automatically: Pause or kill containers based on threat severity
  • Logs everything: Audit trail in JSON Lines format for post-session review

Enable monitoring in config:

[monitoring]
enabled = true

See the Security Monitoring page for full details.

Summary

COI's defense-in-depth approach:

  1. Container isolation: AI runs in an isolated Incus container
  2. Privileged container guard: COI refuses to start when security.privileged=true is detected — this setting silently defeats all container isolation (seccomp, AppArmor, UID mapping)
  3. Security posture verification: coi health checks that seccomp and AppArmor are active and warns on custom overrides (raw.seccomp, raw.apparmor)
  4. Kernel version enforcement: Warns on host kernels below 5.15 that may lack security features for safe container isolation
  5. Path protection: Security-sensitive paths mounted read-only
  6. Credential isolation: SSH keys and env vars never exposed unless explicitly opted in
  7. Permission mode: Optional human-in-the-loop approval for tool commands
  8. Network isolation: Prevents unauthorized network access
  9. Security monitoring: Real-time threat detection and automated response
  10. Sandbox context: AI tools know their constraints via ~/SANDBOX_CONTEXT.md
  11. Safe commit practices: Extra protection when committing changes

These layers work together to minimize the blast radius if an AI tool is compromised or generates malicious code.

Clone this wiki locally