Initial commit

This commit is contained in:
ms2k 2026-06-28 04:00:43 +09:00
commit d3096285d4

321
docs/specification.md Normal file
View file

@ -0,0 +1,321 @@
# Lazaret: Unified Sandbox Quarantine & Host Escalation CLI
## Architecture & Technical Specification
**Lazaret** is a secure developer-experience (DX) tool written in Rust. It orchestrates rootless, gVisor-sandboxed container environments for AI agents, isolating them from the host network and system namespace by default, while providing an interactive, human-in-the-loop (HITL) **Host Escalation** protocol for controlled actions.
## Glossary
| Term | Definition |
| :-------------------- | :--------- |
| **Agent Workspace** | The bind-mounted host project directory inside the Agent Sandbox (at `/workspace`). |
| **Agent Sandbox** | The gVisor (`runsc`) container running a profile. |
| **Network Sidecar** | The shell-less gateway container (from `scratch`) running the Netlink router under `runc`. |
| **Git Shadow** | The tmpfs masking + read-only shadow mount of the host `.git` + selective write-back binds + symlink redirection for config and HEAD. |
| **Host Escalation** | The secure UDS channel (with ephemeral mTLS) from the Escalation Client to the host TUI for approved host actions. |
| **Escalation Client** | The statically linked `lazaret-escalate` binary. |
| **Escalation Socket** | The profile-specific Unix domain socket. |
| **Profile Root** | `<data_root>/<profile_name>`. |
| **Storage Root** | The configured `data_root` for persistent profile data. |
## Fixed In-Container Paths
Lazaret-managed files and sockets inside every Agent Sandbox live under `/run/lazaret/`:
| Path | Description |
| :---------------------- | :---------- |
| `/run/lazaret/escalate` | The Escalation Client binary (volume-mounted, read-only). |
| `/run/lazaret/escalate.sock`| The Escalation Socket. |
| `/run/lazaret/git` | Target directory for the Git Shadow (read-only bind). |
| `/run/lazaret/certs` | Ephemeral mTLS client certificates (memory-backed tmpfs, read-only). |
The **Agent Workspace** is mounted at `/workspace`.
---
## 1. Problem Statement & Goals
### 1.1 The Security vs. Capability Dilemma
Modern AI agents require powerful execution capabilities (e.g., running code, interacting with git, testing microservices). However, running untrusted agent-generated code directly on a developer's host machine poses severe security risks:
* **Host Privilege Escalation:** Untrusted code can access SSH keys, local credentials, or modify system files.
* **Network Escapes:** A rogue agent can scan local subnets, exfiltrate data, or interact with sensitive internal services (RFC 1918 ranges).
* **Git Hook Injection:** An agent can write a malicious git hook (e.g., `.git/hooks/post-checkout`) that executes code on the host machine when the developer runs simple git commands.
* **Git Config Exploitation:** An agent can write a malicious git pager config (e.g., `core.pager = malicious_command`) which executes on the host when the developer runs git log or git diff.
### 1.2 The Developer Experience (DX) Friction
Existing container-based isolation solutions (like standard Docker/Podman running on runc) introduce high friction:
* **Terminal Degradation:** Terminfo mismatches (e.g., running Ghostty, Kitty, or Alacritty) cause terminal applications to crash inside the container.
* **Transient State Loss:** Re-authenticating with developer tools, subscriptions, or package manager registries (npm, cargo, etc.) on every sandbox restart is highly disruptive.
* **Impoverished Git Integration:** Masking the entire `.git` directory prevents agents from running basic queries (like `git diff` or `git status`), severely crippling their capabilities.
### 1.3 Core Objectives
* **Deep Virtualization-Based Isolation:** Run AI agent workspaces in rootless Podman containers utilizing the **gVisor (`runsc`)** sandbox runtime, establishing a strong kernel boundary.
* **Network Sidecar:** Route container traffic through a statically compiled, shell-less network gateway container (`scratch`) running a custom Netlink router that dynamically applies egress policies, bypassing gVisor Netlink capabilities limits.
* **Persistent State & Subscriptions:** Provide configurable persistence folders so that packages, tools, and developer authentications (OAuth, CLI subscriptions) survive container restarts via host-persistent directory mapping.
* **Git Hook & Config Protection with Full Git DX:** Neutralize git hook injection and config pager-script exploits to protect the host, while maintaining complete local git write (commits, staging, branch checking) and read functionality inside the sandbox, ensuring real-time configuration visibility.
* **Host Escalation:** Provide a secure **Escalation Socket** (UDS) channel with ephemeral mTLS that allows the **Escalation Client** inside the Agent Sandbox to request host-bound actions, displaying an interactive terminal prompt to the user for approval or modification.
---
## 2. Architecture & Data Flow
```mermaid
graph TD
subgraph Host Machine
Orchestrator[Lazaret Host CLI / Daemon]
Config[~/.config/lazaret/config.toml]
Storage[Sandbox Storage Root / NVMe]
TUI[Interactive HITL TUI Prompt]
UDS_Server[Unix Domain Socket Server]
end
subgraph Container Isolation Boundary
subgraph Gateway Sidecar (scratch - runc)
RouteDaemon[Netlink Router Daemon]
end
subgraph Agent Sandbox (Debian Trixie - runsc/gVisor)
AgentProcess[Agent Process / CLI]
EscalateCLI[lazaret-escalate CLI]
end
end
Orchestrator -->|Loads| Config
Orchestrator -->|Initializes Storage| Storage
Orchestrator -->|Spins up Sidecar| RouteDaemon
Orchestrator -->|Launches Sandbox| AgentProcess
RouteDaemon -->|Configures Netlink| GatewaySidecarNetwork
AgentProcess -->|Binds Network Namespace| RouteDaemon
AgentProcess -->|Requests host command| EscalateCLI
EscalateCLI -->|JSON Request via UDS| UDS_Server
UDS_Server -->|Triggers Prompt| TUI
TUI -->|User Approval / Modification| Orchestrator
Orchestrator -->|Executes Command on Host| HostEnv[Host Environment]
HostEnv -->|Sends Stdout / Stderr| UDS_Server
UDS_Server -->|JSON Response| EscalateCLI
```
---
## 3. Configuration Contract
Configuration files are parsed hierarchically (lowest precedence to highest):
1. User-wide global default: `~/.config/lazaret/config.toml`
2. Workspace local override: `./lazaret.toml` (in the active working directory)
3. Direct override command-line argument: `--config <file>`
A concrete template demonstrating all 5 MVP agent profiles is located in the repository as `example.toml`.
### 3.1 Schema Contract Specification
#### Section `[storage]`
| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| `data_root` | String | Yes | Absolute path on the host where persistent directories (home, usr, etc.) are initialized and mounted. |
#### Section `[network]`
| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| `default_allow` | Array of Strings | Yes | Global whitelist of destination CIDR networks permitted for all containers. |
| `default_deny` | Array of Strings | Yes | Global blacklist of destination CIDR networks (e.g., RFC 1918) blackholed for all containers. |
#### Section `[escalation]`
| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| `enabled` | Boolean | Yes | Activates or suspends the host-side escalation daemon. |
| `socket_dir` | String | Yes | Path on the host filesystem (e.g. `/run/user/1000/lazaret/` or `/tmp/lazaret`) where profile-specific Unix escalation sockets are created. |
| `auto_approve` | Array of Strings | No | List of user-defined CLI commands allowed to execute on the host automatically without interactive confirmation. |
#### Section `[profiles.<profile_name>]`
| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| `image` | String | Yes | The fully-qualified OCI container image to pull and launch. To prevent background updates and local host state drift, specific image tags or SHA-256 digests should be pinned. |
| `persistent_dirs` | Array of Strings | No | Paths inside the container copied out and persisted under the host's storage directory (e.g. `/usr`, `/var`). |
| `allow_egress` | Array of Strings | No | CIDRs allowed specifically for this agent, merged with the default global network whitelist. |
| `env` | Map of Key-Value | No | Environment variables forwarded to the container. Setting a value prefix of `env:VAR_NAME` pulls dynamically from the host. |
| `volumes` | Array of Objects | No | Custom volumes mapped from host to container. Hosts support the `{profile_dir}` expansion. |
#### Custom Volume Object
* `host`: String. Host directory or file path. May use `{profile_dir}` which resolves to `<data_root>/<profile_name>/`.
* `container`: String. Target path inside the container's directory tree.
* `mode`: String. Mount properties (e.g., `z` for SELinux sharing, `ro` for read-only bind mounts).
### 3.2 Variable Expansion, Templating & Sanitation Specification
To maintain a secure sandbox quarantine and provide an ergonomic configuration experience, Lazaret implements a custom, zero-dependency, safe variable substitution engine rather than a heavy template engine like Jinja or Handlebars. This minimizes the attack surface and compilation footprint.
#### 3.2.1 Linux-Only Tilde (`~`) Expansion
Because the core orchestration dependencies of Lazaret (**rootless Podman** and **gVisor `runsc`**) run exclusively on Linux architectures, the codebase is compiled and optimized solely for Linux systems. Path resolution and tilde expansion are handled using Linux standards:
* Lazaret retrieves the host user's home directory by reading the `$HOME` environment variable, falling back to query the host's `/etc/passwd` entry if `$HOME` is unpopulated.
* **Strict Validation:** Tilde is only expanded if it is the absolute first character of the path string (e.g. `~/config` resolves to `/home/user/config`, but `some/path/~` is treated as a literal directory named `~`).
#### 3.2.2 Expansion Tokens
Lazaret parses and expands a small, strictly defined whitelist of local variables inside configuration fields:
* `{profile_name}`: Evaluates to the lowercase alphanumeric name of the active agent profile.
* `{profile_dir}`: Expands dynamically on the host to: `{data_root}/{profile_name}`.
* `{workspace_dir}`: Evaluates to the current host working directory (`std::env::current_dir()`).
#### 3.2.3 Sanitation & Directory Traversal Protection
Because AI agents might attempt to manipulate configuration templates or profile names to trigger directory escapes on the host machine, Lazaret enforces three strict structural validation boundaries:
1. **Profile Name Sanitation:**
Profile names must strictly match the regex pattern: `^[a-z0-9_-]{2,32}$`. Any profile name containing dots (`.`), slashes (`/`, `\`), or shell metacharacters is immediately rejected, preventing injection attacks when creating paths like `{data_root}/{profile_name}`.
2. **Path Traversal Interception:**
Before resolving any expanded path, Lazaret passes the resulting string to a sanitation function that checks for the presence of relative path segments. If a resolved path contains a dot-dot segment (`..`), the execution is aborted immediately with a security panic, preventing the agent from escaping the storage boundaries.
3. **Absolute Path Verification:**
All host paths used for mounts, storage, and volumes are canonicalized using Rust's `std::fs::canonicalize` or resolved to absolute paths before being passed to Podman execution commands. This prevents relative symlink tricks or path obfuscation from bypassing rootless boundaries.
---
## 4. Key Security & Functional Mechanisms
### 4.1 Git Hook & Config Isolation (Git Shadow)
To completely secure the developer's host environment against git hook injection and config pager exploits while preserving complete Git capabilities and real-time configuration visibility, Lazaret implements a strict filesystem mount decoupling strategy.
Direct single-file read-only bind mounts of `.git/config` are prohibited due to the Unix **stale inode problem**: when git configuration is updated on the host, Git atomically unlinks and replaces `.git/config`, causing single-file bind mounts to remain permanently bound to the stale, unlinked file inode.
#### 4.1.1 The Decoupling Protocol
Rather than mounting `.git` directly as a read-write or simple read-only directory, the host orchestrator constructs a hybrid virtual directory structure inside the container's mount namespace:
1. **Agent Workspace Mount:** The active **Agent Workspace** is bind-mounted read-write (with the standard `:z` flag for rootless SELinux compatibility).
2. **Git Directory Masking:** The orchestrator overlays a memory-backed `tmpfs` volume directly over the `.git` directory inside the Agent Workspace namespace, hiding the host's physical `.git` directory.
3. **Shadow Mount Injection:** The host's actual `.git` directory is bind-mounted **read-only** to the Git Shadow target inside the container.
4. **Write-Back Bind Mounts:** To support standard Git modifications inside the Agent Workspace (such as staging changes, recording commits, and managing branches), the host's writeable Git directories are bind-mounted read-write directly into the memory-backed `.git/` inside the Agent Workspace:
* Host objects directory → write-back object database inside the Agent Workspace.
* Host refs directory → write-back refs inside the Agent Workspace.
* Host logs directory → write-back logs inside the Agent Workspace.
5. **Read-Only Symlink Redirection:** Critical configurations inside the Agent Workspace are exposed using symbolic links pointing to the secure, read-only Git Shadow target:
* `.git/config` is symlinked to the Git Shadow.
* `.git/HEAD` is symlinked to the Git Shadow.
#### Filesystem Mount Mapping Table
| Path in Agent Workspace | Mount Type | Properties / Permissions | Purpose |
| :--- | :--- | :--- | :--- |
| `/workspace` | Bind Mount | Read / Write (`z` shared) | Shares the active project folder with the Agent Sandbox. |
| `/workspace/.git` | `tmpfs` | Memory-only, transient | Masks the git folder inside the Agent Workspace, blocking direct host hook writes. |
| `/workspace/.git/objects`| Bind Mount | Read / Write (`z` shared) | Write-back object database (host objects mounted into Agent Workspace). |
| `/workspace/.git/refs` | Bind Mount | Read / Write (`z` shared) | Write-back refs (branches/stashes) into the Agent Workspace. |
| `/workspace/.git/logs` | Bind Mount | Read / Write (`z` shared) | Write-back reflogs into the Agent Workspace. |
| `/workspace/.vscode` | `tmpfs` | Memory-only | Masks user's host VSCode preferences inside the Agent Workspace. |
| `/workspace/.idea` | `tmpfs` | Memory-only | Masks user's host IntelliJ configurations inside the Agent Workspace. |
| `/run/lazaret/git` | Bind Mount | Read-Only (`ro`, `z` shared) | Git Shadow target directory (read-only). |
#### 4.1.2 Security & Operational Guarantees
* **Immutable Target Integrity:** Any direct write or append attempt targeting `.git/config` inside the Agent Workspace fails at the kernel level with `EROFS` (Read-only file system) because the resolved symlink target resides on the read-only Git Shadow.
* **Container Bypass Neutralization:** If a malicious agent process deletes the symlink and writes a new, rogue configuration file at that path, **the write remains entirely isolated within the local memory-backed `tmpfs`.** The host's actual `.git/config` is never touched, and host commands remain completely secure.
* **Instant Configuration Visibility:** Because the Git Shadow target is mounted as a **directory**, any host-side atomic replacements of files within it (such as a developer running `git config` on the host, which unlinks and replaces `.git/config`) are instantly visible inside the Git Shadow. The container's symlink immediately resolves to the updated configuration without getting stuck on a stale, unlinked inode.
* **Safe Isolated Branch Switching:** The Agent Sandbox can safely execute `git checkout` to switch branches. Git writes branch reference updates to the write-back refs location (which safely propagates back to the host), but writes the active branch state to `HEAD`. If the write to the read-only `HEAD` symlink fails, the container's git CLI simply deletes the symlink and writes a real, local `HEAD` file to the writeable `tmpfs`. This isolates the active sandbox branch state, preventing container-side branch switches from disrupting the host developer's active branch workspace.
### 4.2 Sandbox Git Worktree/Branch Isolation
To prevent the agent from cluttering the developer's working index, Lazaret supports working on isolated branch namespaces:
* **Dedicated Branch:** Lazaret can auto-derive a branch `lazaret/agent-<name>` from the active host commit.
* **Stash Protection:** The developer's host working directory remains intact. The developer can inspect changes on the host with `git diff lazaret/agent-<name>` or merge them explicitly.
### 4.3 Image-Agnostic State Persistence via Host-Side `fuse-overlayfs`
When a sandbox initializes, Lazaret provides state persistence (e.g., preserving installed packages, dependencies, and tools) across container deletions and recreations without copying out entire filesystems or bloating host storage. This is achieved by orchestrating a host-side user-space overlay filesystem (`fuse-overlayfs`) under the rootless container namespace.
#### 4.3.1 Directory Layout on Host
For each profile, the host orchestrator establishes the following directory structure under `{data_root}/{profile_name}/` (which host volume `{profile_dir}` evaluates to):
* `usr_changes/`: The persistent, writeable layer (`upperdir`) that stores user-installed packages, configurations, and modifications. Initially empty.
* `usr_work/`: The private metadata workspace directory (`workdir`) required by overlayfs.
* `usr_merged/`: The virtual, merged mount point directory (`mergeddir`) exposed to the sandbox.
#### 4.3.2 Host-Side Mounting Protocol
Because gVisor (`runsc`) restricts overlayfs mounts inside the sandbox virtual kernel, and standard host overlays require root privileges, Lazaret runs mounting operations on the host inside the rootless container namespace:
1. **Locate Base Image Mount:** The orchestrator creates a lightweight placeholder container and retrieves its base-image filesystem mount path on the host using `podman mount` (e.g., `/home/user/.local/share/containers/storage/overlay/.../merged/`).
2. **Mount User-Space Overlay (`fuse-overlayfs`):** The orchestrator executes `fuse-overlayfs` on the host, setting the base image's system directory as the read-only `lowerdir`, and the profile-specific `usr_changes` and `usr_work` as the writable layers. To support rootless execution and avoid permission errors, this command is executed within the rootless user namespace:
```bash
podman unshare fuse-overlayfs -o lowerdir=<base_image_mount>/usr,upperdir=usr_changes,workdir=usr_work usr_merged
```
3. **Sandbox Mount Injection:** The host orchestrator launches the gVisor sandbox, bind-mounting the virtual, merged directory (`usr_merged`) over the container's `/usr` directory:
```bash
podman run --runtime=runsc -v <data_root>/<profile_name>/usr_merged:/usr:z ...
```
#### 4.3.3 Resilience to Image Updates, Drift & Podman Compatibility
Unlike static copy-out methods which seal system libraries in time and bypass container patches, this overlay structure naturally inherits base image updates:
* When the base OCI image is updated (e.g., pulling a new release containing critical CVE patches), the orchestrator mounts the new image's read-only `/usr` as the `lowerdir` on the next run.
* `fuse-overlayfs` merges this new, patched baseline with the existing `usr_changes` layer dynamically.
* Because standard system libraries (such as glibc) maintain strict backwards-compatibility, user-installed packages survive base image updates and execute flawlessly on top of the updated, secure OS.
* **Podman Integration Compatibility Blacklist:** When applying user-space overlays over system directories (such as `/etc`), Lazaret dynamically blacklists and bypasses Podman-critical dynamic files (specifically `/etc/resolv.conf` and `/etc/hosts`) to allow Podman to inject standard network configurations seamlessly on startup, ensuring name resolution inside the sandbox. The detailed blacklist rules are managed in the development journal.
---
## 5. Agent-to-Host Escalation Protocol
### 5.1 Defining Escalation Boundaries
AI agents inside the container run as `root` in their user namespace. They can execute standard operations (such as installing packages with `apt-get install` or compiling binaries) locally within their sandbox.
**Escalation is strictly required when the agent needs access to resources outside the gVisor quarantine:**
* **Credential Delegation:** Querying host-level credential helpers (e.g. AWS token fetchers) or host keys.
* **Host Resource Modification:** Accessing host-level files outside the Agent Workspace or requesting configuration changes to host services.
### 5.2 JSON Message Schema over UDS with Ephemeral mTLS
The **Escalation Client** is placed inside the Agent Sandbox. It communicates with the host orchestrator over the **Escalation Socket** (UDS) utilizing a secure Mutual TLS (mTLS) handshake to cryptographically prove sandbox identity.
To prevent container-based file manipulation or hijacking, the host orchestrator creates the **Escalation Socket** file inside a secure, host-private directory owned by the host developer (defined by `socket_dir` in the configuration) and bind-mounts only that socket into the Agent Sandbox, leaving it completely decoupled from the writable **Agent Workspace**.
#### 5.2.1 Cryptographic Identity Verification (Local Ephemeral CA)
To prevent PID-reuse spoofing vulnerabilities (CWE-377 / CWE-362) and host socket hijacking risks, Lazaret establishes container identity cryptographically rather than relying solely on kernel-level or process-level credentials:
1. **Host CA Generation:** On startup, the host orchestrator acts as a private, ephemeral Certificate Authority (CA), generating a self-signed root CA certificate and private key in-memory.
2. **Sandbox Client Certificate:** For each active sandbox container, the orchestrator generates a unique, short-lived client certificate and private key. This certificate encodes the active container's profile name and unique sandbox ID inside its Subject Alternative Name (SAN).
3. **Mount Injection:** The host orchestrator mounts these certificates into the container namespace using a memory-backed, read-only `tmpfs` volume (see Fixed In-Container Paths).
4. **mTLS Handshake & Host Credentials Pre-flight Check:** When the **Escalation Client** initiates a request, it performs a complete mutual TLS handshake over the **Escalation Socket**. The host verifies that the client certificate presented by the connection is signed by the host's active in-memory root CA, and checks the profile SAN to confirm exact container identity before any payloads are parsed. To provide defense-in-depth, the host also runs a lightweight pre-flight credentials check (`SO_PEERCRED`) on the socket to ensure the socket connection is initiated exclusively by the local developer's rootless user space.
#### Command Request (Sandbox -> Host JSON payload over mTLS)
* `request_id`: Unique identifier (e.g., UUID or hashed string).
* `profile`: The current agent profile requesting escalation.
* `command`: Raw CLI command to run on the host.
* `justification`: Detailed justification explaining why host resources are needed.
#### Command Response (Host -> Sandbox JSON payload over mTLS)
* `request_id`: Matches the original command request ID.
* `approved`: Boolean indicating if the user permitted execution.
* `exit_code`: Process exit code (only set if approved is true).
* `stdout`: Combined standard output text.
* `stderr`: Combined standard error text.
### 5.3 HITL Interactive Console Interface & Whitelisting
When a request is received on the host, the `lazaret` orchestrator temporarily halts standard TTY forwarding and renders a dedicated interactive console prompt using `ratatui`. The prompt clearly isolates the request ID, profile, justification, and the requested command, offering the user four distinct actions:
* **[A] Approve & Run:** Immediately runs the command on the host.
* **[D] Deny Request:** Rejects the escalation, returning an access denied response to the sandbox.
* **[E] Edit Command:** Prompts the developer to edit the command in-place before executing it.
* **[Q] Abort:** Stops the daemon and suspends container operation.
#### 5.3.1 Whitelisting & Injection Prevention (Argument Vector Prefix Matching)
To provide high developer experience (DX) without introducing command injection vulnerabilities, the orchestrator implements a secure command validation pipeline:
1. **Shell Metacharacter Auto-Approve Bypass:**
Any requested command containing shell-interpreted metacharacters, control characters, or redirections (such as `;`, `|`, `&`, `$()`, `` ` ` ``, `>`, `<`, `\n`, `\r`, or backslashes) is strictly ineligible for auto-approval. This bypasses the automatic whitelisting engine and demotes the command to require interactive human-in-the-loop (HITL) approval, ensuring that no command chaining, backgrounding, or output redirection can be executed on the host without explicit developer inspection.
2. **Argument Vector Prefix Whitelisting:**
A requested command string is parsed into an argument vector utilizing a POSIX-compliant shell words parser (e.g., the `shell-words` crate in Rust) to resolve quotes and escape spaces correctly. The requested vector is compared against the parsed vectors of the configured `auto_approve` list. To prevent **argument injection vulnerabilities** (where an agent appends malicious flags like `--exec-path` or `--pager` to hijack general binaries), the auto-approve check verifies that the whitelisted vector is a strict prefix vector of the requested vector.
* **Unsafe / Banned Commands:** Broad binaries or commands that support execution redirection (such as raw `"git"` or any general `"git "` subcommands) are strictly banned from the auto-approve whitelist.
* **Safe Whitelist Example:** Whitelist prefix vector `["aws", "sts", "get-caller-identity"]` safely matches requested command vector `["aws", "sts", "get-caller-identity", "--query", "Account"]` but rejects any other executable.
3. **Direct Execve Execution:**
Once verified, the orchestrator passes the literal argument vector directly to the host's execution engine (`execve` or Rust `std::process::Command`), launching the binary directly without invoking any intermediate host shell, rendering any argument-based shell escape vectors completely inert.
---
## 6. Functional & Non-Functional Requirements
### 6.1 Functional Requirements
| ID | Requirement | Description |
| :--- | :--- | :--- |
| **FR-1** | TOML Profile Loading | Dynamically parses and loads config from `~/.config/lazaret/config.toml` or override paths. |
| **FR-2** | Podman & gVisor Integration | Configures container executions using `--runtime=runsc` for sandboxes and `--runtime=runc` for sidecars. |
| **FR-3** | Network Sidecar Lifecycle | Ensures the shell-less **Network Sidecar** running in `runc` is active before spawning any Agent Sandbox. |
| **FR-4** | Git Hooks Isolation | Automatically isolates the container git environment using the Git Shadow. |
| **FR-5** | Host Escalation | Establishes private host **Escalation Socket** listener with ephemeral mTLS certificate verification and SO_PEERCRED pre-flight checks, parses JSON requests, and displays interactive HITL terminal prompt. |
### 6.2 Non-Functional Requirements
| ID | Requirement | Description |
| :--- | :--- | :--- |
| **NFR-1** | Minimal Sidecar Footprint | The **Network Sidecar** binary must compile statically (via `musl`) with minimal external dependencies to maintain a small footprint suitable for `scratch`. |
| **NFR-2** | Robust Async Cancellation | Utilizes Tokio's task cancellation to cleanly close containers, clean up **Network Sidecar** instances, and close sockets on SIGINT. |
| **NFR-3** | Dynamic Terminfo Mapping | Locates host terminal terminfo configurations (Ghostty, Alacritty, Kitty) and bind-mounts them read-only into the Agent Sandbox. |
| **NFR-4** | Zero Shell Footprint | The **Network Sidecar** container has no shell (`sh`, `bash`), preventing any remote execution/hijacking. |