- Replaces stream/socket escalation with a sandboxed regular-file Escalation Mailbox to satisfy runsc isolation boundaries without exposing `host-uds=open`. - Expands the TLA+ protocol model with role-bound certificates, tuple authorization, and structured domain-failure recovery paths. - Adds physical integration journals for host terminfo injection, persistent /etc DNS shims, rootless Podman network topology, and static client delivery. - Establishes specification requirements for safe host-only State Anchor inspection and crash-consistent reset operations.
350 lines
22 KiB
Markdown
350 lines
22 KiB
Markdown
# Lazaret Specification
|
|
|
|
This document is the normative product specification for Lazaret. It defines the
|
|
problem, stable vocabulary, architecture, and requirements. The
|
|
[TLA+ application protocol model](protocol/Lazaret.tla) defines lifecycle and communication
|
|
semantics. The [implementation plan](implementation-plan.md) records current
|
|
technology choices, configuration schema, sequencing, and validation work.
|
|
Experimental results and historical rationale belong in the [journal](journal.md).
|
|
|
|
## 1. Problem Statement
|
|
|
|
AI coding agents need broad local capabilities: they run arbitrary tools, modify a
|
|
project, use Git, install dependencies, and access selected network services. Code
|
|
produced or invoked by an agent is not trusted to run directly on the developer's
|
|
host. A conventional container reduces risk, but poor terminal behavior, transient
|
|
tool state, restricted Git access, and manual setup can make the safe path too
|
|
inconvenient for routine work.
|
|
|
|
Lazaret's north star is:
|
|
|
|
> Make isolated agent execution the easiest way to work, while ensuring that every
|
|
> transition from the sandbox to host authority is explicit, attributable, and
|
|
> controlled by the developer.
|
|
|
|
Lazaret therefore provides a rootless gVisor sandbox with a bidirectionally shared
|
|
workspace, persistent sandbox-local tooling, controlled network egress, and a
|
|
human-in-the-loop host escalation path.
|
|
|
|
### 1.1 Trust Model
|
|
|
|
The following components are trusted:
|
|
|
|
- The developer and host operating system.
|
|
- The Lazaret CLI and the domain's Session Coordinator.
|
|
- Host configuration selected by the developer.
|
|
- The Network Sidecar and the policy given to it by Lazaret.
|
|
|
|
The following components are untrusted:
|
|
|
|
- The agent and every process it launches.
|
|
- Code, tools, and package installation scripts executed inside the Agent Sandbox.
|
|
- Data received from the network by sandboxed processes.
|
|
|
|
All Client Sessions in the same Domain intentionally share an Agent Sandbox and
|
|
are one sandbox trust domain. Session identity distinguishes ownership and routes
|
|
terminal interactions; it is not an isolation boundary between processes in that
|
|
sandbox.
|
|
|
|
The Agent Workspace is intentionally writable. Lazaret protects the host outside
|
|
the explicitly mounted workspace and volumes. Tracked and ordinary untracked
|
|
workspace paths remain writable, while Git-ignored paths are isolated into
|
|
Domain-scoped sandbox state by default. The developer may explicitly disable that
|
|
isolation in trusted host configuration.
|
|
|
|
Host configuration is read only by host Lazaret processes. A configuration file
|
|
must not be mounted into, or located beneath a host path mounted into, the Agent
|
|
Sandbox.
|
|
|
|
## 2. Glossary
|
|
|
|
These terms and identifiers are stable across the specification, protocol model,
|
|
implementation plan, logs, and user interfaces.
|
|
|
|
| Term | Definition |
|
|
| :--- | :--- |
|
|
| **Agent Profile** | A developer-selected host configuration describing an agent image and its granted storage, network, environment, and escalation capabilities. |
|
|
| **Agent Workspace** | The canonical host project directory mounted read-write at `/workspace` in the Agent Sandbox. |
|
|
| **Agent Sandbox** | The rootless gVisor container in which an agent and its child processes execute. One sandbox is shared by all Client Sessions in a Domain. |
|
|
| **Agent Sandbox ID** | A runtime-unique identifier assigned to one Agent Sandbox instance and bound to its Domain protocol identity. |
|
|
| **Client CLI ID** | A runtime-unique identifier assigned to one host Lazaret CLI process. It is authenticated as a protocol identity and bound to one Client Session. |
|
|
| **Client Session** | One active host Lazaret CLI process and the interactive or non-interactive command it attaches to the Agent Sandbox. |
|
|
| **Session ID** | A cryptographically random identifier unique to one Client Session. |
|
|
| **Session Anchor** | Ephemeral session credentials that bind protocol messages to a registered Session ID and route them to the owning host CLI. |
|
|
| **Session Coordinator** | The ephemeral host process that exclusively owns and coordinates a Domain's runtime resources and Client Sessions. |
|
|
| **Session Coordinator ID** | A runtime-unique identifier assigned to one Session Coordinator instance and bound to its Domain protocol identity. |
|
|
| **Domain** | The shared runtime and persistent-state scope for one `(Agent Profile, canonical Agent Workspace)` pair. |
|
|
| **Domain ID** | A deterministic identifier derived from the Agent Profile name and canonical Agent Workspace path. It namespaces Domain resources but is not an authentication credential. |
|
|
| **Domain Root** | The host-only directory containing persistent state and metadata for one Domain. |
|
|
| **Network Sidecar** | The trusted, shell-less gateway container that establishes the Domain's network policy before the Agent Sandbox starts. |
|
|
| **Network Sidecar ID** | A runtime-unique identifier assigned to the Network Sidecar instance owned by a Domain. |
|
|
| **Git Sentry** | The protective mount layout that preserves bidirectional workspace and Git access while preventing writes to the host's default Git hooks and repository configuration. |
|
|
| **Ignored Workspace State** | Domain-scoped sandbox-local storage presented at Git-ignored Agent Workspace paths instead of exposing the corresponding host content. |
|
|
| **State Anchor** | The Domain-scoped persistent writable filesystem layer merged over selected paths from the sandbox base image. |
|
|
| **Trust Anchor** | The ephemeral Domain-scoped certificate authority used to authenticate role-bound Lazaret protocol identities. |
|
|
| **Protocol Identity** | An authenticated binding of a component role, Agent Profile, Domain ID, and runtime component ID. Identifiers name components but do not authenticate them by themselves. |
|
|
| **Escalation Client** | The Lazaret-provided executable through which a sandboxed process requests a host action. |
|
|
| **Escalation Mailbox** | Host-private, Domain-scoped regular-file message paths mounted into the Agent Sandbox for signed escalation requests and correlated results. |
|
|
| **Escalation Request ID** | A cryptographically random identifier used to correlate one host escalation request and response. |
|
|
| **Escalation Result** | The terminal response to an accepted Host Escalation, correlated by Escalation Request ID and carrying its outcome plus command exit status and output when execution occurred. |
|
|
| **Host Escalation** | A request by a sandboxed process for Lazaret to execute a developer-approved or auto-approved action with host authority. |
|
|
| **Configuration Fingerprint** | A cryptographic digest of the validated host configuration that created or joined a running Domain. |
|
|
| **Storage Root** | The trusted host directory beneath which Lazaret stores Domain Roots and profile-scoped state. |
|
|
|
|
## 3. Architecture
|
|
|
|
### 3.1 System Boundaries
|
|
|
|
```mermaid
|
|
flowchart LR
|
|
subgraph Host[Trusted host]
|
|
CLI[Client CLI]
|
|
Config[Host configuration]
|
|
HostWorkspace[Agent Workspace]
|
|
Coordinator[Session Coordinator]
|
|
State[State Anchor]
|
|
Gateway[Network Sidecar]
|
|
Mailbox[Escalation Mailbox]
|
|
HostExec[Approved host process]
|
|
end
|
|
|
|
subgraph Sandbox[Untrusted Agent Sandbox - rootless runsc]
|
|
Agent[Agent and child processes]
|
|
Workspace[/workspace mount]
|
|
Escalate[Escalation Client]
|
|
end
|
|
|
|
Config --> CLI
|
|
HostWorkspace <--> Workspace
|
|
CLI <--> Coordinator
|
|
Coordinator --> State
|
|
Coordinator --> Gateway
|
|
Coordinator <--> Mailbox
|
|
Agent <--> Workspace
|
|
Agent --> Escalate
|
|
Escalate <--> Mailbox
|
|
Coordinator --> HostExec
|
|
Agent --> Gateway
|
|
Gateway --> Network[Permitted destinations]
|
|
```
|
|
|
|
The Agent Sandbox uses gVisor's application-kernel isolation under rootless
|
|
Podman. Rootless operation limits the host authority of the runtime, while gVisor
|
|
interposes a separate userspace kernel between untrusted processes and the host
|
|
kernel. The Network Sidecar is a separate trusted component and is not part of the
|
|
untrusted agent environment.
|
|
|
|
### 3.2 Domain and Session Lifecycle
|
|
|
|
The Session Coordinator is the single owner of a Domain's Agent Sandbox, Network
|
|
Sidecar, State Anchor, host-only sockets, Escalation Mailbox, and active-session
|
|
registry. Concurrent CLI processes for the same Domain join the existing
|
|
coordinator and attach separate Client Sessions to the shared sandbox. Different
|
|
Domains never share these resources.
|
|
|
|
The first Client Session starts the Domain. The coordinator prepares persistent
|
|
state, establishes network policy, starts the Agent Sandbox, and then accepts the
|
|
session. When no Client Sessions remain, it tears down transient resources while
|
|
retaining Domain-scoped persistent state.
|
|
|
|
Startup, registration, cancellation, recovery, and teardown transitions are
|
|
defined by the [TLA+ application protocol model](protocol/Lazaret.tla).
|
|
|
|
A Domain failure first withdraws publication, invalidates active trust material,
|
|
disconnects sessions, and terminates associated host processes. It does not
|
|
assume that already-created sandbox, sidecar, or mount resources disappeared.
|
|
Recovery stops the sandbox, then the sidecar, then unmounts the State Anchor
|
|
before the Domain may return to the absent state and accept a new startup leader.
|
|
|
|
The Trust Anchor authenticates distinct Protocol Identities for the Session
|
|
Coordinator, Client CLI, Network Sidecar, and Agent Sandbox. Certificates bind a
|
|
peer's role, Agent Profile, Domain ID, and runtime component ID; a certificate for
|
|
one role or Domain cannot authorize another. Session registration additionally
|
|
binds the authenticated Client CLI ID to its Session ID and Session Anchor.
|
|
|
|
Authorization uses the relevant current identity tuple rather than any identifier
|
|
alone. Network attachment binds the `(Domain ID, Network Sidecar ID, Agent Sandbox
|
|
ID)` tuple. Session control binds the `(Domain ID, Session Coordinator ID, Client
|
|
CLI ID, Session ID)` tuple. Host Escalation binds the authenticated Agent Sandbox
|
|
ID to the registered `(Client CLI ID, Session ID, Session Anchor)` tuple in that
|
|
Domain.
|
|
|
|
### 3.3 Filesystem and Git Model
|
|
|
|
The Agent Workspace is mounted read-write. Tracked paths and untracked paths that
|
|
are not ignored propagate in both directions. The Git index, `HEAD`, references,
|
|
and objects are shared with the host in real time. Lazaret does not create a
|
|
separate worktree or branch and does not promise that the host working tree
|
|
remains unchanged.
|
|
|
|
By default, paths matched by the repository's effective Git ignore rules are
|
|
redirected to Ignored Workspace State. Existing host content at those paths is not
|
|
exposed to the Agent Sandbox, and sandbox writes remain Domain-scoped and persist
|
|
across restarts. This prevents agent-modified dependency trees, virtual
|
|
environments, generated executables, caches, and other ignored artifacts from
|
|
later being executed by host tools. Trusted host configuration may disable this
|
|
behavior when fully bidirectional ignored paths are required.
|
|
|
|
Ignored-path isolation is defense in depth and does not replace the mandatory Git
|
|
Sentry protections below.
|
|
|
|
Lazaret applies narrow protective mounts over host integration files that must not
|
|
be modified by the agent:
|
|
|
|
- The host repository's `.git/config` is exposed read-only.
|
|
- The host repository's `.git/hooks` is hidden behind sandbox-local transient
|
|
storage.
|
|
- Supported host editor configuration directories may be hidden behind
|
|
sandbox-local transient storage.
|
|
|
|
These protections do not make the workspace immutable. Their purpose is to stop
|
|
the sandbox from writing the protected host Git configuration and default hook
|
|
directory while preserving normal Git operations and bidirectional source edits.
|
|
|
|
### 3.4 Persistent Sandbox State
|
|
|
|
The State Anchor preserves developer tooling and selected sandbox system paths for
|
|
one Domain. It combines a read-only lower directory from the selected base image
|
|
with a Domain-scoped writable upper directory.
|
|
|
|
Files not represented in the upper layer follow the selected base image. Files,
|
|
directories, and whiteouts recorded in the upper layer remain authoritative when
|
|
the lower image changes. This permits installed tools to survive restarts while
|
|
allowing unmodified base-image content to follow the selected image.
|
|
|
|
Persistent state is sandbox state, not host configuration. It must not contain or
|
|
override Lazaret's host control plane.
|
|
|
|
### 3.5 Network Isolation
|
|
|
|
The Network Sidecar establishes the Domain's egress routes before the Agent
|
|
Sandbox starts. The sidecar alone receives the network-administration capability
|
|
needed to install policy; the Agent Sandbox does not receive that capability.
|
|
The Agent Sandbox may join only the sidecar's prepared network namespace, not the
|
|
host network namespace.
|
|
|
|
IPv4 destinations are permitted by default except for the RFC 1918 private
|
|
address ranges, which are denied by default. Explicit, more-specific allow rules
|
|
may grant access within those ranges. Lazaret also installs the reachability
|
|
needed for the Domain's selected DNS resolvers. Other permitted and denied
|
|
destination prefixes come from trusted host configuration.
|
|
|
|
Normal Linux longest-prefix routing determines the most specific matching route;
|
|
an equal-prefix conflict must be rejected or resolved in favor of denial. IPv4
|
|
and IPv6 must either receive equivalent policy or an address family must be
|
|
disabled.
|
|
|
|
The topology and policy-installation sequence must ensure that all external Agent
|
|
Sandbox traffic traverses the configured enforcement path. Dynamic policy changes
|
|
are not a requirement unless the implementation can apply them without weakening
|
|
that property.
|
|
|
|
### 3.6 Host Escalation
|
|
|
|
The Escalation Client exchanges request and result records through the
|
|
Escalation Mailbox. Records are regular files written by atomic rename beneath
|
|
host-private, Domain-scoped paths. The sandbox-writable request path and
|
|
sandbox-readable result path may have different mount modes. The mailbox is not
|
|
trusted as an authentication boundary: each request carries authenticated Domain
|
|
credentials and a Session Anchor signature binding it to a registered Client
|
|
Session and to the exact request content. A verified request is routed only to
|
|
that session's host CLI. Host-only CLI/coordinator control may use host-private
|
|
Unix sockets that are never mounted into the Agent Sandbox.
|
|
|
|
A host command may run only when the developer approves it interactively or when
|
|
it matches an auto-approval rule in trusted host configuration. Auto-approved
|
|
commands are launched directly as an argument vector without a shell. Rules match
|
|
the complete vector by default; permission for arbitrary trailing arguments is an
|
|
explicit property of an individual rule.
|
|
|
|
Interactive approval may allow the developer to approve, deny, or edit a request.
|
|
Every accepted request must produce an Escalation Result for its Escalation
|
|
Client while its Client Session remains connected. The result reports completion,
|
|
denial, timeout, cancellation, or failure and, when a host command ran, its exit
|
|
status and standard-output and standard-error data. Approval is an authorization
|
|
decision, not a terminal result. A request reaches its terminal state only after
|
|
this result is delivered or its Client Session disconnects.
|
|
Terminal ownership must return to the sandbox session after resolution.
|
|
|
|
Authentication, message correlation, approval, execution, cancellation, and
|
|
terminal-routing transitions are defined by the
|
|
[TLA+ application protocol model](protocol/Lazaret.tla).
|
|
|
|
### 3.7 State Inspection and Reset
|
|
|
|
The host CLI provides read-only discovery, inspection, structural verification,
|
|
and explicit reset operations for Domain State Anchors. These operations resolve
|
|
a Domain through trusted host metadata and never accept an arbitrary host
|
|
filesystem path as an upper-layer target.
|
|
|
|
Inspection reports metadata and overlay changes without printing file contents by
|
|
default. Structural verification requires an inactive Domain and exclusive
|
|
lifecycle ownership so its result is not invalidated by concurrent writes. A
|
|
reset may target one configured persistent container path or all State Anchors in
|
|
the Domain. It discards the selected upper-layer state so the next launch follows
|
|
the current base-image lower layer; it does not modify the base image, Agent
|
|
Workspace, Ignored Workspace State, or unselected State Anchors.
|
|
|
|
Reset is permitted only after Lazaret holds the Domain's exclusive lifecycle lock
|
|
and has established that no Client Session, coordinator, sandbox, sidecar, host
|
|
process, or overlay mount is using the selected state. Confirmation flags may
|
|
suppress the human prompt but may not bypass these safety conditions. Reset must
|
|
be crash-consistent and must never follow a symlink outside the Domain Root.
|
|
|
|
## 4. Requirements
|
|
|
|
### 4.1 Security Requirements
|
|
|
|
| ID | Requirement |
|
|
| :--- | :--- |
|
|
| **SR-1** | Agent processes shall execute in a rootless gVisor sandbox and shall not receive host root authority. |
|
|
| **SR-2** | Host configuration shall never be mounted into the Agent Sandbox or located beneath a host path mounted into it. |
|
|
| **SR-3** | Host paths outside the Agent Workspace, developer-configured volumes, and Lazaret-owned fixed control mounts shall not be accessible to the Agent Sandbox. |
|
|
| **SR-4** | The sandbox shall be unable to modify the host repository's `.git/config` or default `.git/hooks` directory through the workspace mount. |
|
|
| **SR-5** | The Network Sidecar shall establish network policy before the Agent Sandbox starts, and the Agent Sandbox shall not receive network-administration capability. |
|
|
| **SR-6** | Network policy shall cover every enabled address family and shall deny equal-specificity policy conflicts. |
|
|
| **SR-7** | A host action shall execute only after interactive developer approval or a match against a trusted host auto-approval rule. |
|
|
| **SR-8** | Auto-approved host commands shall execute without a shell and shall permit trailing arguments only when the matched rule explicitly grants them. |
|
|
| **SR-9** | Every protocol peer shall be authenticated to its component role, Agent Profile, Domain ID, and runtime component ID. Authorization shall require the relevant current identity tuple; an identifier or certificate for another role, Domain, component instance, or Client Session shall not be accepted. |
|
|
| **SR-10** | Domain sockets, mailboxes, and coordination files shall reside in a host-private directory owned by the developer. |
|
|
| **SR-11** | Runtime and persistent resources belonging to different Domain IDs shall remain isolated. |
|
|
| **SR-12** | The Agent Sandbox shall not join host user, PID, IPC, UTS, cgroup, or network namespaces; the only shared network namespace permitted is the Domain's Network Sidecar enforcement namespace. |
|
|
| **SR-13** | Git-ignored Agent Workspace paths shall use Ignored Workspace State by default, preventing the sandbox from reading or modifying corresponding host content; only trusted host configuration may disable this behavior. |
|
|
| **SR-14** | State Anchor maintenance shall remain confined to the resolved Domain Root. Destructive maintenance shall require exclusive lifecycle ownership and an inactive, unmounted target. |
|
|
|
|
### 4.2 Functional Requirements
|
|
|
|
| ID | Requirement |
|
|
| :--- | :--- |
|
|
| **FR-1** | Lazaret shall derive a stable Domain ID from the selected Agent Profile and canonical Agent Workspace. |
|
|
| **FR-2** | Concurrent Client Sessions in one Domain shall share one coordinator, Agent Sandbox, Network Sidecar, and State Anchor. |
|
|
| **FR-3** | Tracked and non-ignored workspace edits and supported Git operations shall propagate bidirectionally between host and sandbox. |
|
|
| **FR-4** | Selected sandbox paths and Ignored Workspace State shall persist across Domain restarts without copying Lazaret host configuration into the sandbox. |
|
|
| **FR-5** | The coordinator shall route each escalation prompt to the Client Session identified by the verified Session ID and deliver the correlated Escalation Result to the requesting Escalation Client. |
|
|
| **FR-6** | The final Client Session leaving a Domain shall trigger teardown of transient Domain resources. |
|
|
| **FR-7** | Startup and teardown shall recover safely from interrupted or stale prior executions without assuming that external runtime resources disappeared with their owner process. |
|
|
| **FR-8** | Lazaret shall support interactive terminal sessions without degrading supported host terminal capabilities. |
|
|
| **FR-9** | Trusted host configuration shall allow the developer to disable Git-ignored path isolation for an Agent Profile. |
|
|
| **FR-10** | Session registration shall bind one authenticated Client CLI ID, Session ID, and Session Anchor, and escalation routing and approval shall preserve that binding. |
|
|
| **FR-11** | The host CLI shall list, inspect, structurally verify, and reset all or selected State Anchors without requiring direct manipulation of Lazaret's storage layout. |
|
|
|
|
### 4.3 Developer-Experience Requirements
|
|
|
|
| ID | Requirement |
|
|
| :--- | :--- |
|
|
| **DX-1** | A developer shall be able to launch an Agent Profile in the current workspace with one command. |
|
|
| **DX-2** | Tools installed in configured persistent sandbox paths and dependencies stored in Ignored Workspace State shall remain available on later launches of the same Domain. |
|
|
| **DX-3** | Normal source editing, `git status`, staging, committing, and reference updates shall work from inside the Agent Sandbox. |
|
|
| **DX-4** | Escalation prompts shall identify the requesting profile, session, command, and justification without mixing terminal input or output between sessions. |
|
|
| **DX-5** | Failures shall report actionable diagnostics and shall not leave the developer's terminal in a corrupted mode. |
|
|
| **DX-6** | State inspection shall provide human-readable and machine-readable output without exposing persisted file contents by default, and reset shall show its resolved Domain and exact scope before confirmation. |
|
|
|
|
### 4.4 Operational Requirements
|
|
|
|
| ID | Requirement |
|
|
| :--- | :--- |
|
|
| **OR-1** | The coordinator shall maintain exclusive runtime ownership of Domain resources. |
|
|
| **OR-2** | Every accepted protocol request shall resolve to a delivered completed, denied, timed-out, cancelled, or failed result, unless its Client Session disconnects first. |
|
|
| **OR-3** | Cancellation and process termination shall not strand an active host command or permanently block another Client Session. |
|
|
| **OR-4** | Security-sensitive behavior shall be covered by integration tests on supported Podman, runsc, filesystem, and network configurations. Test evidence belongs in the journal. |
|
|
| **OR-5** | An interrupted State Anchor reset shall recover to either the prior generation or a new empty generation; it shall not leave a partially reset active upper layer. |
|