**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.
| **Agent Workspace** | The bind-mounted host project directory inside the **Agent Sandbox** (at `/workspace`). |
| **Agent Sandbox** | The gVisor (`runsc`) container running an agent profile, scoped uniquely to a **Domain Anchor** (representing a specific agent and canonical workspace path pairing). |
| **Client Session** | A dynamic pairing of an active host `lazaret` CLI process and its corresponding containerized shell session inside the **Agent Sandbox**, secured by a **Session Anchor** and a **Trust Anchor**. |
| **Session Anchor** | The ephemeral asymmetric keypair $(K_{pub\_proc}, K_{priv\_proc})$ generated by a host CLI process upon startup, used to cryptographically sign process-level escalation requests. |
| **Session Coordinator**| The background-detached, decentralized host coordinator process that manages the container, **Network Sidecar**, and **State Anchor** overlays for a specific **Domain Anchor**. |
| **Network Sidecar** | The shell-less gateway container (from `scratch`) running the Netlink router under `runc`, scoped uniquely to a **Domain Anchor** to enforce network egress policies. |
| **Git Sentry** | The hybrid nesting layout consisting of the read-write mounted host `.git` under `/workspace/.git`, paired with a transient `tmpfs` mask over `hooks`, and a read-only bind mount of `.git/config`. |
| **State Anchor** | The host-side `fuse-overlayfs` structure (`usr_changes`, `usr_work`, `usr_merged`) that merges a read-only container base OS directory with a persistent, writable host layer (scoped under the **Domain Root**), bind-mounted over container system paths (e.g., `/usr`) to preserve state for that specific agent-workspace pairing across container restarts. |
| **Trust Anchor** | The in-memory, local ephemeral Certificate Authority (CA) managed by the host orchestrator or **Session Coordinator** on startup to issue short-lived cryptographic client certificates to sandboxes for secure mutual TLS (mTLS) identity verification over the **Escalation Socket**. |
| **Host Escalation** | The secure UDS channel (secured by mTLS via the **Trust Anchor**) from the **Escalation Client** to the host TUI for approved host actions. |
| **Escalation Client** | The statically compiled `lazaret-escalate` binary, volume-mounted read-only inside the sandbox. |
| **Escalation Socket** | The UNIX domain socket scoped to a **Domain Anchor** used for routing **Host Escalation** commands. |
| **Domain Anchor** | The deterministic, cryptographically derived boundary (representing the pairing of a specific agent profile and a unique absolute host workspace path) used to scope and namespace the sandbox container name, the **Session Coordinator**'s control socket, the advisory lockfile, and the persistent **State Anchor** storage. |
| **Domain Root** | The directory on the host filesystem (`<data_root>/domains/<domain_anchor_hash>/`) under the **Storage Root** where the **State Anchor** and other domain-scoped files are stored. |
| **Storage Root** | The host directory configured via the `data_root` property, serving as the base folder for all persistent agent profiles and domain storage. |
The primary objectives of Lazaret are to establish a secure, rootless, and isolated sandbox for untrusted AI agent execution while maintaining a functional developer experience through secure host escalation, persistent state, and real-time Git synchronization.
* **Deep Virtualization-Based Isolation:** Run AI agent workspaces in rootless Podman containers utilizing the **gVisor (`runsc`)** sandbox runtime to establish a host-container 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.
* **Persistent State:** Provide configurable persistence folders via a **State Anchor** to persist designated system and workspace directories across container restarts.
* **Git Hook & Config Protection:** Enforce the **Git Sentry** isolation pattern to block git hook writes and config pager modifications, while permitting standard local git commands and tracking upstream configuration changes.
* **Host Escalation:** Provide a secure UNIX Domain Socket (UDS) **Escalation Socket** channel, verified via the **Trust Anchor** (using mTLS), to route structured command execution requests from the **Escalation Client** inside the Agent Sandbox to the host orchestrator for user approval or modification.
| `enabled` | Boolean | Yes | Activates or suspends the host-side **Escalation Socket** listener during the lifecycle of the CLI command. |
| `socket_dir` | String | Yes | Path on the host filesystem (e.g. `/run/user/1000/lazaret/` or `/tmp/lazaret`) where profile-specific Unix **Escalation Socket**s 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).
* 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}`.
*`{domain_hash}`: Evaluates to the 12-character SHA-256 hexadecimal hash representing the Domain Anchor of the active `(agent_name, canonical_workspace_path)` pair.
*`{domain_dir}`: Expands dynamically on the host to the Domain Root directory: `{data_root}/domains/{domain_hash}`.
*`{workspace_dir}`: Evaluates to the current host working directory.
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.
All host paths used for mounts, storage, and volumes are canonicalized (resolving all symlinks and relative segments to absolute paths) before being passed to Podman execution commands. This prevents relative symlink tricks or path obfuscation from bypassing rootless boundaries.
Lazaret isolates the container's Git environment and prevents modifications to host configuration and hook files using a nested ("Inside Mount") layout called **Git Sentry**.
When a git configuration is updated on the host, Git atomically unlinks and replaces `.git/config`. A direct single-file read-only bind mount of `.git/config` remains bound to the stale, unlinked file inode inside the container namespace (the Unix stale inode problem). To ensure that the container namespace dynamically reflects host-side updates, the read-only file mount must be nested inside a parent read-write bind-mounted directory, allowing the kernel to resolve the updated inode path dynamically.
1.**Agent Workspace Mount:** The active **Agent Workspace** is bind-mounted read-write at `/workspace` (utilizing the `:z` flag for rootless SELinux compatibility). This exposes the host's `.git/index` to enable standard git status and staging operations from within the container.
2.**Hooks Sandbox Mount:** A memory-backed `tmpfs` volume is mounted over `/workspace/.git/hooks` inside the container to isolate hook creation and execution to transient container memory.
3.**Config Read-Only Mount:** The host's `.git/config` file is bind-mounted read-only over `/workspace/.git/config`. Because this mount is nested inside the parent bind-mounted `/workspace` directory, parent directory resolution propagates host-side atomic replacements of `.git/config` to the container namespace, bypassing the stale inode problem.
* **Immutable Target Integrity:** Write or append attempts targeting `/workspace/.git/config` inside the container must fail at the kernel level with `EROFS`.
* **Deletion Blocked:** Deletion or unlinking of `/workspace/.git/config` inside the container must be blocked by the kernel VFS layer with `EBUSY` (Device or resource busy).
* **Instant Configuration Visibility:** Updates to `.git/config` on the host must propagate instantly to the container's `/workspace/.git/config` mount via parent directory resolution.
* **State Propagation:** Branch modifications and reference writes made by the container to `/workspace/.git/HEAD` and `/workspace/.git/refs` must propagate to the host in real-time.
Lazaret isolates sandbox git actions to a dedicated branch namespace:
* **Dedicated Branch:** Lazaret must automatically derive an agent-specific branch name (`lazaret/agent-<name>`) from the active host commit.
* **Stash Protection:** The host workspace active working tree must remain unmodified during sandbox execution. Changes made by the agent within the sandbox must be committed or written only to the dedicated branch, allowing the user to inspect differences on the host with `git diff` or merge changes explicitly.
Lazaret scopes persistent state, socket routing, and container lifecycles to a cryptographically derived **Domain Anchor**.
#### 4.3.1 Domain Anchor Derivation Protocol
For every CLI invocation, the host orchestrator must compute a unique, deterministic 12-character hexadecimal identifier (the `domain_hash`) representing the active boundary.
The hash is derived as follows:
1. Retrieve the lowercase alphanumeric agent profile name.
2. Canonicalize the absolute filesystem path of the active **Agent Workspace** on the host.
3. Compute the SHA-256 digest of the UTF-8 encoded string: `"<agent_name>:<canonical_workspace_path>"`.
4. Truncate the resulting hexadecimal string to its first 12 characters.
This identifier must be used to isolate host-side resources. The resource mapping rules are defined below:
| Resource Type | Host Pathing Rule | Format / Name Scheme |
The host orchestrator establishes a user-space overlay filesystem (such as `fuse-overlayfs`) on the host within the rootless user namespace before launching the container:
1.**Locate Base Image Mount:** The orchestrator must query the OCI runtime to resolve the base-image read-only root mount path on the host.
2.**Mount User-Space Overlay:** The orchestrator must mount a user-space overlay filesystem (e.g. `fuse-overlayfs`) within the rootless user namespace, using the base-image mount `/usr` directory as the read-only lower directory (`lowerdir`), and the domain-scoped directories `usr_changes/` and `usr_work/` as the `upperdir` and `workdir`, respectively.
3.**Sandbox Mount Injection:** The orchestrator must launch the container with the merged user-space overlay directory (`usr_merged/`) bind-mounted read-write over the container's `/usr` path.
The State Anchor overlay structure dynamically tracks updates to the underlying OCI container base image.
* **Base Image Update Inheritance:** Upon pulling an updated base image, the orchestrator uses the new image's `/usr` directory as the read-only `lowerdir` for subsequent user-space overlay mounts, preserving modifications in `usr_changes/`.
* **Podman Integration Compatibility Exclusion:** When applying State Anchor overlays over system directories (such as `/etc`), the orchestrator must exclude runtime-injected configuration files (specifically `/etc/resolv.conf`, `/etc/hosts`, and `/etc/hostname`) from the overlay merge to preserve container DNS and hostname resolution.
Lazaret coordinates concurrent CLI processes interacting with the same **Domain Anchor** using a decentralized, background-detached **Session Coordinator** pattern.
##### Step 1: The "Hold-to-Bind" Advisory Locking Protocol
During CLI startup, the process must handle concurrency via Linux advisory file locking (`flock`):
1.**Acquire Lock:** The process attempts to acquire an exclusive, non-blocking lock (`LOCK_EX | LOCK_NB`) on `/tmp/lazaret_coord_{domain_hash}.lock`.
2.**If Successful (Leader):**
* The process must check if the `Control Socket` exists. If not, it must spawn the **Session Coordinator** in the background, fully detached from the parent's process group (e.g., creating a new session via standard POSIX `setsid` or equivalent OS APIs, detaching it from the controlling terminal).
* The leader process must **poll** the host filesystem for the `Control Socket` to bind (up to 10 seconds).
* The leader must not release its exclusive lock on the lockfile until the control socket has successfully bound and is ready to accept connections.
3.**If Unsuccessful (Follower):**
* The process blocks on the lock (`LOCK_EX`) until the leader completes coordinator setup and releases the lock.
* Once unblocked, the follower immediately releases the lock (`LOCK_UN`) and safely connects to the active `Control Socket`.
##### Step 2: Reference Counted Teardown
The Session Coordinator keeps an active map of connected CLI processes.
* Whenever a CLI process terminates, it disconnects from the control socket.
* When the active connection count on the Coordinator reaches **zero**, the Coordinator must initiate an automatic clean teardown: stopping the sandboxed container, unmounting the `fuse-overlayfs` State Anchor, and unlinking the control socket.
To maintain exact terminal isolation and prevent spoofing or PID-reuse hijacking during interactive prompts:
1. The host CLI process generates a **Session Anchor** (an ephemeral asymmetric keypair $(K_{pub\_proc}, K_{priv\_proc})$) and registers $K_{pub\_proc}$ alongside a unique `LAZARET_SESSION_ID` (represented by a cryptographically secure unique identifier) with the Session Coordinator.
2. When launching a shell or command session inside the container (via `podman exec`), the host CLI process injects a cryptographically signed capability token or delegates a short-lived keypair derived from $K_{priv\_proc}$ into that specific process's environment.
3. When the in-container **Escalation Client** (`lazaret-escalate`) triggers an escalation request, it must sign the JSON payload (including the request ID and `LAZARET_SESSION_ID`) with the injected session credential.
4. The Session Coordinator receives the request over mTLS, verifies the container certificate, retrieves the registered $K_{pub\_proc}$ matching the `LAZARET_SESSION_ID`, and validates the signature.
5. Once verified, the Session Coordinator routes the interactive terminal user interface (TUI) prompt exclusively to the corresponding host terminal socket matching that verified ID.
---
### 4.4 Embedded State Store & Audit Trail Persistence
Lazaret implements a local persistence layer using an embedded transactional key-value store to manage session state transitions, lock statuses, and host escalation audit trails.
The state database file (stored inside the Domain Root) is strictly owned and opened exclusively by the background-detached Session Coordinator process.
* **Process-Level Exclusive Locking:** The embedded transactional key-value store enforces absolute process-level exclusive locking on the physical file. Any attempt by other processes (such as secondary CLI follower processes or standalone status query commands) to open the state file directly will fail immediately.
* **IPC-Facade Query Pattern:** Consequently, all secondary CLI follower sessions, dashboard widgets, or status utilities must never access the state store directly on the filesystem. Instead, they must communicate with the active Session Coordinator over the Control Socket using Unix Domain Sockets, and the Coordinator must perform all queries, registrations, and updates on their behalf.
To prevent async event loop starvation from blocking database operations, all database transactions must be executed on a designated synchronous threadpool managed by the asynchronous runtime.
#### 4.4.3 Manual Schema Migration Protocol
To maintain zero external C-dependencies for frictionless static compilation, the persistence layer utilizes a lightweight, typed key-value table layout.
* **Manual Evolution:** The store does not support automated relational schema migration engines.
* **Table-to-Table Copying:** Any schema evolution or configuration structural changes across feature updates must be coordinated by manual, programmatically written migration routines. These routines must query old tables, convert the underlying serializations, and write them to newer table versions inside a single atomic transaction.
#### 4.4.4 Division of Labor between State Store, Session Coordinator, and OS
To prevent race conditions, memory leaks, and stale process tracking, the active runtime state is decoupled from passive storage:
1.**Passive ACID State Store:** Responsible solely for persisting completed session metadata, verified escalation approvals, and tamper-safe host command audit logs. It does not track real-time process heartbeats or actively coordinate system actions.
2.**Active Session Coordinator:** Acts as the exclusive in-memory broker, managing async UNIX sockets, mTLS handshake parsing, active client connection reference counting, and executing actual container launches or workspace mounts.
3.**OS Kernel Primitives:** Responsible for process death detection. The Session Coordinator and CLI processes utilize advisory file locking (`flock`) on the Lock File to manage active-leader lifecycles and clean up sockets upon process termination (including hard crashes such as `SIGKILL`).
### 5.1 Cryptographic Identity Verification & Message Transport
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 verified by the **Trust Anchor** to cryptographically prove sandbox identity.
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, keeping it decoupled from the writable **Agent Workspace**.
#### 5.1.1 Cryptographic Identity Verification via Trust Anchor and Session Anchor
Lazaret establishes process-level and container-level identities using the **Trust Anchor** and **Session Anchor** protocols:
1.**Host CA Generation:** On startup, the host orchestrator instantiates the **Trust Anchor** as an in-memory, local 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 **Trust Anchor** 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 Trust Anchor certificates into the container namespace using a memory-backed, read-only `tmpfs` volume (see Fixed In-Container Paths).
4.**mTLS Handshake 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 active **Trust Anchor** root CA, and checks the profile SAN to confirm exact container identity before any payloads are parsed.
5.**Session Signature Verification:** Once the mTLS connection is verified, the Session Coordinator reads the payload's `session_id`, retrieves the public key of the corresponding **Session Anchor** registered on startup, and verifies the payload's cryptographic `signature`. Only requests carrying a valid signature produced by that session's injected private key are approved for TUI routing or auto-approval.
*`signature`: Cryptographic signature of the request payload produced by the process's injected private credential, verifiable using the Session Anchor's registered public key.
When a request is received on the host, the `lazaret` orchestrator must temporarily suspend standard TTY forwarding and render a dedicated interactive terminal user interface prompt. The interface must isolate the request ID, profile, justification, and the requested command, offering the user four distinct actions:
* **[A] Approve & Run:** Executes 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 execution.
* **[Q] Abort:** Terminates the CLI orchestrator process, initiating a graceful shutdown of the container and **Network Sidecar** operations.
Any requested command containing shell-interpreted metacharacters, control characters, or redirections (including `;`, `|`, `&`, `$()`, `` `` ``, `>`, `<`, `\n`, `\r`, and backslashes) must bypass the automatic whitelisting engine. Such commands must require interactive human-in-the-loop (HITL) approval to prevent arbitrary command chaining, background execution, or output redirection on the host.
The requested command string must be parsed into an argument vector using a POSIX-compliant shell words parser to resolve quotes and escape spaces. This requested vector must be validated against the parsed vectors defined in the `auto_approve` configuration.
To prevent argument injection vulnerabilities (where arbitrary parameters are appended to authorized binaries), the auto-approve verification must assert that the whitelisted vector is a strict prefix of the requested vector.
* **Banned Commands:** Executables that permit arbitrary command redirection (including raw `git` or any `git` subcommands) must not be permitted on the auto-approve whitelist.
Following verification, the orchestrator must pass the literal argument vector directly to the host's native process execution engine (such as the standard `execve` system call or equivalent system process APIs), launching the binary directly without invoking an intermediate host shell.
| **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 native asynchronous task cancellation and signal-handling mechanisms to cleanly close containers, clean up **Network Sidecar** instances, and close sockets on SIGINT or other termination events. |
| **Terminal User Interface Library** | "interactive terminal user interface library" or "terminal user interface" | `ratatui` | Terminal rendering and layout primitives for the interactive prompt. |