Skip to content

gni/agents-container

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

23 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

agents container

this project provides a hardened container virtualization and security sandboxing environment to run untrusted developer or coding agents safely. it utilizes docker-in-docker, gvisor kernel virtualization, and the zero-trust l7 proxy.

the zero-trust l7 proxy is powered by ottergate, which inspects outbound DNS and HTTPS traffic to enforce domain-level access control.

operational commands

setup and boot infrastructure: build base images, start the nested docker daemon, and spin up the zero trust proxy.

./ac start

list active sandboxes and workspaces: check running containers, status details, and provisioned workspaces.

./ac list

check status and resource metrics: inspect container health, cpu/memory utilization, and disk space usage.

./ac status

run agent sandbox in headless mode: execute an agent blueprint in non-interactive background mode.

./ac run pi my_pi_run

run interactive shell inside sandbox: spawn a secure shell inside the agent container for interactive debugging.

./ac shell pi my_pi_debug

limit agent sandbox resources: restrict cpu, memory allocation, or gpu devices on sandbox start.

./ac run pi my_pi_run --cpus 2 --memory 1g

view host logs: stream operational logs of the outer docker-in-docker host container.

./ac logs host

view zero trust proxy logs: inspect domain routing, dns resolution, and network allowlist decisions.

./ac logs proxy

view gvisor kernel logs: trace user-space kernel events, system calls, and platform logs.

./ac logs gvisor

view specific agent logs: retrieve the terminal output of an active or stopped sandbox container.

./ac logs agent my_pi_run

clean sandbox credentials: wipe transient git/github/gitlab credentials while preserving local workspace files.

./ac clean my_pi_run

destroy sandbox workspace: stop the sandbox container and delete both its credentials and workspace files.

./ac destroy my_pi_run

teardown nested environments: stop the outer host daemon, remove all nested containers, and clear cache storage.

./ac down

supported agent blueprints

the sandbox supports several pre-configured agent blueprints, located in the agents/blueprints directory. you can instantiate any of them using ./ac run <blueprint_name> <instance_name>:

  • antigravity: specialized sandbox for the antigravity coding agent CLI.
  • gemini: pre-configured container environment tailored for google gemini code assistants.
  • claude: pre-configured container environment tailored for anthropic claude developer agents.
  • hermes: customized runtime environment for the hermes developer agent.
  • codex: optimized developer environment for openai codex and chat-based coding agents.
  • opencode: sandbox designed for open-source code interpreter agents (opencodeinterpreter).
  • pi: sandbox optimized for inflection pi and general-purpose developer agents.
  • base: a clean, minimal developer container with standard debugging and build utilities.

architecture and security model

the security boundaries are constructed across nested virtualization, system call virtualization, privilege control, and network policies:

graph TD
    Host["πŸ’» Physical Host OS (runc)"]
    
    subgraph DinD ["🐳 Docker-in-Docker Host (ac-dind-host)"]
        direction TB
        Dockerd["βš™οΈ Nested Docker Daemon (with runsc/gVisor & crun)"]
        
        subgraph Net ["πŸ”’ Sandbox Network (172.20.0.0/16 - Internal Only)"]
            Agent["πŸ€– coding agent (runtime: runsc / gVisor)"]
            Ottergate["🦦 ottergate (runtime: runc)"]
        end
        
        ExtNet["🌐 External Network (Bridge to Internet)"]
    end
    
    Host -->|runc / privileged| DinD
    Agent -->|DNS / L7 HTTP Proxy| Ottergate
    Ottergate -->|allowlisted traffic| ExtNet
    ExtNet -->|NAT| Internet["🌍 Public Internet"]
    
    style Host fill:#1e1e2e,stroke:#313244,stroke-width:2px,color:#cdd6f4
    style DinD fill:#313244,stroke:#45475a,stroke-width:2px,color:#cdd6f4
    style Net fill:#181825,stroke:#eba0ac,stroke-width:2px,stroke-dasharray: 5 5,color:#cdd6f4
    style Agent fill:#f38ba8,stroke:#a6adc8,stroke-width:2px,color:#11111b
    style Ottergate fill:#fab387,stroke:#a6adc8,stroke-width:2px,color:#11111b
Loading

host bootstrap pipeline

the host bootstrap process starts the outer dind container, generates certificates, sets up the nested docker network, and launches default network proxy utilities:

graph TD
    Start["πŸš€ User: ./ac start"] --> DinDHost["🐳 Start dind-host Container (runc)"]
    DinDHost --> Entrypoint["πŸ“œ entrypoint-dind.sh Runs"]
    
    subgraph DinDHostSetup ["βš™οΈ Host-Level Bootstrap"]
        Entrypoint --> NestedDockerd["🐳 Start Nested Docker Daemon (gVisor & crun)"]
        Entrypoint --> TLSGen["πŸ”‘ Generate TLS Certificates"]
        Entrypoint --> BuildBase["πŸ“¦ Build agent-base Image"]
        Entrypoint --> Ottergate["🦦 Start Ottergate Proxy (runc)"]
        Entrypoint --> NetworkRules["πŸ”’ Run update-iptables.sh"]
    end

    style Start fill:#89b4fa,stroke:#a6adc8,stroke-width:2px,color:#11111b
    style DinDHostSetup fill:#1e1e2e,stroke:#313244,stroke-width:2px,color:#cdd6f4
Loading

sandbox instance startup pipeline

when running a specific sandbox instance, the agent provisioning scripts configure local vault spaces before dropping privileges to start execution:

graph TD
    StartRun["πŸš€ User: ./ac run/shell"] --> ProvisionInstance["πŸ“ Create instance directory, env & secrets"]
    ProvisionInstance --> LaunchAgent["πŸ€– Launch Agent Sandbox (runsc/gVisor)"]
    
    subgraph AgentSetup ["πŸ€– Nested Sandbox Bootstrap"]
        LaunchAgent --> InitGuard["πŸ›‘οΈ init-guard Runs (root)"]
        InitGuard --> CAConfig["πŸ“œ Load CA Certificates"]
        InitGuard --> GitConfig["βš™οΈ Configure git helper"]
        InitGuard --> VaultDaemon["πŸ”‘ Start vault-daemon (root)"]
        InitGuard --> SecureSecrets["πŸ”’ Secure secrets (chmod 0000)"]
        InitGuard --> DropPrivs["πŸ‘€ Drop privileges to node (UID 1000)"]
        DropPrivs --> RunAgent["πŸƒ Exec Agent script / shell"]
    end

    style StartRun fill:#89b4fa,stroke:#a6adc8,stroke-width:2px,color:#11111b
    style AgentSetup fill:#181825,stroke:#eba0ac,stroke-width:2px,color:#11111b
    style RunAgent fill:#a6e3a1,stroke:#a6adc8,stroke-width:2px,color:#11111b
Loading

credential vault security model

tokens are mounted to a secure directory, copied to root-only storage, cleared from memory, and accessed through process verification checks:

sequenceDiagram
    autonumber
    actor Agent as πŸ€– Agent Process (node)
    participant Wrapper as πŸ›‘οΈ vault-wrapper (setuid root)
    participant Daemon as βš™οΈ vault-daemon (root)
    participant Vault as πŸ”’ /vault/gh_* (root-only)

    Note over Agent: Runs 'git clone' or 'gh repo list'
    Agent->>Wrapper: Executes wrapped gh/glab command
    Note over Wrapper: Escalates to root via setuid
    Wrapper->>Daemon: Connects via Unix Socket /vault/vault.sock
    Note over Daemon: getsockopt peer credentials gets caller PID and exe path
    Daemon->>Daemon: Traces process chain to verify caller
    alt Caller is authorized Git/Agent process
        Daemon->>Vault: Reads decrypted GitHub/GitLab token
        Vault-->>Daemon: Returns token
        Daemon-->>Wrapper: Returns token over Socket
        Wrapper->>Wrapper: drops privileges to user 'node'
        Wrapper->>Agent: Pipes token to gh/glab command
    else Caller is unauthorized
        Daemon-->>Wrapper: Blocks connection / Returns empty
        Wrapper-->>Agent: Access denied
    end
Loading

network traffic redirection and proxying

the agent network is internal-only, redirecting all outbound connections through custom system hooks to the zero-trust filtering firewall:

graph LR
    subgraph Step1 ["1. DNS Resolution"]
        Agent["πŸ€– Agent"] -->|Queries domain| DNS["🌐 Ottergate DNS"]
        DNS -->|Check allowlist| DNSResult{"Allowed?"}
    end

    subgraph Step2 ["2. Interception"]
        DNSResult -->|Yes| Connect["πŸ”Œ syscall: connect"]
        DNSResult -->|No| NXDOMAIN["🚫 Block connection"]
        Connect -->|net_proxy.so intercepts| Redirect["πŸ”€ Redirect to 172.20.0.53"]
    end

    subgraph Step3 ["3. L7 Filtering"]
        Redirect -->|Hits Ottergate Proxy| Proxy["πŸ”’ Ottergate Proxy"]
        Proxy -->|Inspect SNI/Host| L7Result{"Allowlisted?"}
    end

    subgraph Step4 ["4. Egress"]
        L7Result -->|Yes| Internet["🌍 Public Internet"]
        L7Result -->|No| Drop["🚫 Drop connection"]
    end

    style Step1 fill:#181825,stroke:#313244,stroke-width:1px,color:#cdd6f4
    style Step2 fill:#181825,stroke:#313244,stroke-width:1px,color:#cdd6f4
    style Step3 fill:#181825,stroke:#313244,stroke-width:1px,color:#cdd6f4
    style Step4 fill:#181825,stroke:#313244,stroke-width:1px,color:#cdd6f4
    style Agent fill:#f38ba8,stroke:#a6adc8,stroke-width:2px,color:#11111b
    style DNS fill:#89b4fa,stroke:#a6adc8,stroke-width:2px,color:#11111b
    style Internet fill:#a6e3a1,stroke:#a6adc8,stroke-width:2px,color:#11111b
Loading

system virtualization stack

the runtime environment uses a nested container layout to segregate privileges, restrict system calls, and inspect outbound requests:

graph TD
    subgraph Host ["πŸ’» Physical Host OS (runc)"]
        subgraph DinD ["🐳 Docker-in-Docker Host (runc privileged)"]
            subgraph Daemon ["βš™οΈ Nested Docker Daemon"]
                subgraph Network ["πŸ”’ Sandbox Network (172.20.0.0/16)"]
                    subgraph AgentContainer ["πŸ€– Agent Sandbox (gVisor / runsc)"]
                        subgraph EnvHooks ["πŸ”Œ System Hooks (LD_PRELOAD)"]
                            AgentApp["πŸ€– Coding Agent Application"]
                        end
                    end
                    Ottergate["🦦 Ottergate Router & Proxy (runc)"]
                end
            end
        end
    end

    style Host fill:#1e1e2e,stroke:#313244,stroke-width:2px,color:#cdd6f4
    style DinD fill:#181825,stroke:#45475a,stroke-width:2px,color:#cdd6f4
    style Daemon fill:#313244,stroke:#585b70,stroke-width:2px,color:#cdd6f4
    style Network fill:#11111b,stroke:#89b4fa,stroke-dasharray: 5 5,stroke-width:2px,color:#cdd6f4
    style AgentContainer fill:#313244,stroke:#f38ba8,stroke-width:2px,color:#cdd6f4
    style EnvHooks fill:#181825,stroke:#fab387,stroke-width:2px,color:#cdd6f4
    style AgentApp fill:#f5e0dc,stroke:#f2cdcd,stroke-width:2px,color:#11111b
    style Ottergate fill:#a6e3a1,stroke:#a6adc8,stroke-width:2px,color:#11111b
Loading

author

author: Lucian BLETAN

license

licensed under the Apache License, Version 2.0.