lazaret/docs/specification.md
ms2k 7800e9ddf6 Point specification at external journal and implementation plan
Journal content and the implementation plan now live only in
development-journal; the project keeps normative docs and the local
docs/journal symlink.
2026-07-09 21:26:15 +09:00

351 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. Current implementation strategy, experimental results,
and historical rationale live in the development journal (local checkout:
[journal index](journal/00_INDEX.md), including the
[implementation plan](journal/implementation-plan.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. |