docs: reconcile sandbox policy and protocol results

This commit is contained in:
ms2k 2026-06-29 01:12:48 +09:00
commit 4f63842c90
4 changed files with 149 additions and 29 deletions

View file

@ -40,6 +40,7 @@ allow_extra_args = false
[profiles.gemini] [profiles.gemini]
image = "docker.io/library/debian:trixie" image = "docker.io/library/debian:trixie"
isolate_gitignored = true
persistent_dirs = ["/root", "/usr", "/var", "/etc", "/opt"] persistent_dirs = ["/root", "/usr", "/var", "/etc", "/opt"]
allow_egress = ["10.222.0.0/16"] allow_egress = ["10.222.0.0/16"]
env = { GEMINI_API_KEY = "env:GEMINI_API_KEY" } env = { GEMINI_API_KEY = "env:GEMINI_API_KEY" }
@ -49,6 +50,7 @@ volumes = [
[profiles.claude] [profiles.claude]
image = "docker.io/library/debian:trixie" image = "docker.io/library/debian:trixie"
isolate_gitignored = true
persistent_dirs = ["/root", "/usr", "/var", "/etc", "/opt"] persistent_dirs = ["/root", "/usr", "/var", "/etc", "/opt"]
allow_egress = ["10.222.0.0/16"] allow_egress = ["10.222.0.0/16"]
env = { ANTHROPIC_API_KEY = "env:ANTHROPIC_API_KEY" } env = { ANTHROPIC_API_KEY = "env:ANTHROPIC_API_KEY" }
@ -58,6 +60,7 @@ volumes = [
[profiles.codex] [profiles.codex]
image = "docker.io/library/debian:trixie" image = "docker.io/library/debian:trixie"
isolate_gitignored = true
persistent_dirs = ["/root", "/usr", "/var", "/etc", "/opt"] persistent_dirs = ["/root", "/usr", "/var", "/etc", "/opt"]
allow_egress = ["10.222.0.0/16"] allow_egress = ["10.222.0.0/16"]
env = { OPENAI_API_KEY = "env:OPENAI_API_KEY" } env = { OPENAI_API_KEY = "env:OPENAI_API_KEY" }
@ -67,6 +70,7 @@ volumes = [
[profiles.grok] [profiles.grok]
image = "docker.io/library/debian:trixie" image = "docker.io/library/debian:trixie"
isolate_gitignored = true
persistent_dirs = ["/root", "/usr", "/var", "/etc", "/opt"] persistent_dirs = ["/root", "/usr", "/var", "/etc", "/opt"]
allow_egress = ["10.222.0.0/16"] allow_egress = ["10.222.0.0/16"]
env = { XAI_API_KEY = "env:XAI_API_KEY" } env = { XAI_API_KEY = "env:XAI_API_KEY" }
@ -76,6 +80,7 @@ volumes = [
[profiles.opencode] [profiles.opencode]
image = "docker.io/library/debian:trixie" image = "docker.io/library/debian:trixie"
isolate_gitignored = true
persistent_dirs = ["/root", "/usr", "/var", "/etc", "/opt"] persistent_dirs = ["/root", "/usr", "/var", "/etc", "/opt"]
allow_egress = ["10.222.0.0/16"] allow_egress = ["10.222.0.0/16"]
env = { ANTHROPIC_API_KEY = "env:ANTHROPIC_API_KEY", OPENAI_API_KEY = "env:OPENAI_API_KEY", GEMINI_API_KEY = "env:GEMINI_API_KEY" } env = { ANTHROPIC_API_KEY = "env:ANTHROPIC_API_KEY", OPENAI_API_KEY = "env:OPENAI_API_KEY", GEMINI_API_KEY = "env:GEMINI_API_KEY" }

View file

@ -21,6 +21,7 @@ CONSTANTS
SessionFingerprint <- SessionFingerprintFixture SessionFingerprint <- SessionFingerprintFixture
DomainFingerprint <- DomainFingerprintFixture DomainFingerprint <- DomainFingerprintFixture
ConfigSafe <- ConfigSafeFixture ConfigSafe <- ConfigSafeFixture
SandboxIsolationSafe <- SandboxIsolationSafeFixture
RequestSession <- RequestSessionFixture RequestSession <- RequestSessionFixture
RequestCommand <- RequestCommandFixture RequestCommand <- RequestCommandFixture
CertificateValid <- CertificateValidFixture CertificateValid <- CertificateValidFixture
@ -35,6 +36,7 @@ CHECK_DEADLOCK FALSE
INVARIANTS INVARIANTS
TypeOK TypeOK
ConfigurationConfinement ConfigurationConfinement
SandboxNamespaceIsolation
OrderedNetworkStartup OrderedNetworkStartup
PublishedDomainsReady PublishedDomainsReady
RegisteredSessionsConnected RegisteredSessionsConnected
@ -45,6 +47,7 @@ INVARIANTS
TerminalIsolation TerminalIsolation
NoOrphanHostProcess NoOrphanHostProcess
StateAnchorOwnership StateAnchorOwnership
ResultDeliveryStateConsistent
PROPERTIES PROPERTIES
SessionEventuallyResolves SessionEventuallyResolves

View file

@ -4,8 +4,9 @@ EXTENDS Naturals, FiniteSets, Sequences, TLC
(*************************************************************************** (***************************************************************************
Application-wide protocol model for Lazaret. Application-wide protocol model for Lazaret.
The model covers Domain startup, ordered resource preparation, Client Session The model covers Domain startup, ordered resource preparation, sandbox launch
registration, host escalation, terminal ownership, failure, and teardown. isolation, Client Session registration, host escalation, correlated result
delivery, terminal ownership, failure, and teardown.
Cryptographic algorithms, serialization, container commands, filesystems, and Cryptographic algorithms, serialization, container commands, filesystems, and
network packet handling are refinement boundaries validated by implementation network packet handling are refinement boundaries validated by implementation
tests rather than modeled internally. tests rather than modeled internally.
@ -24,6 +25,7 @@ CONSTANTS
SessionFingerprint, SessionFingerprint,
DomainFingerprint, DomainFingerprint,
ConfigSafe, ConfigSafe,
SandboxIsolationSafe,
RequestSession, RequestSession,
RequestCommand, RequestCommand,
CertificateValid, CertificateValid,
@ -56,6 +58,7 @@ ASSUME
/\ SessionFingerprint \in [Sessions -> STRING] /\ SessionFingerprint \in [Sessions -> STRING]
/\ DomainFingerprint \in [Domains -> STRING] /\ DomainFingerprint \in [Domains -> STRING]
/\ ConfigSafe \in [Domains -> BOOLEAN] /\ ConfigSafe \in [Domains -> BOOLEAN]
/\ SandboxIsolationSafe \in [Domains -> BOOLEAN]
/\ RequestSession \in [Requests -> Sessions] /\ RequestSession \in [Requests -> Sessions]
/\ RequestCommand \in [Requests -> Commands] /\ RequestCommand \in [Requests -> Commands]
/\ CertificateValid \in [Requests -> BOOLEAN] /\ CertificateValid \in [Requests -> BOOLEAN]
@ -75,11 +78,24 @@ TerminalRoutes == {"Detached", "Sandbox", "Prompt", "HostProcess"}
ConnectionStates == {"Disconnected", "Connected"} ConnectionStates == {"Disconnected", "Connected"}
RequestPhases == { RequestPhases == {
"Unused", "Verified", "AwaitingApproval", "Approved", "Executing", "Unused", "Verified", "AwaitingApproval", "Approved", "Executing",
"CompletionPending", "DenialPending", "TimeoutPending",
"CancellationPending", "FailurePending",
"Completed", "Denied", "TimedOut", "Cancelled", "Failed" "Completed", "Denied", "TimedOut", "Cancelled", "Failed"
} }
ApprovalSources == {NoApproval, "AutoRule", "Developer"} ApprovalSources == {NoApproval, "AutoRule", "Developer"}
PendingResultPhases == {
"CompletionPending", "DenialPending", "TimeoutPending",
"CancellationPending", "FailurePending"
}
TerminalRequestPhases == {"Completed", "Denied", "TimedOut", "Cancelled", "Failed"} TerminalRequestPhases == {"Completed", "Denied", "TimedOut", "Cancelled", "Failed"}
DeliveredResultPhase(p) ==
CASE p = "CompletionPending" -> "Completed"
[] p = "DenialPending" -> "Denied"
[] p = "TimeoutPending" -> "TimedOut"
[] p = "CancellationPending" -> "Cancelled"
[] p = "FailurePending" -> "Failed"
(*************************************************************************** (***************************************************************************
Finite fixture operators used by Lazaret.cfg. The protocol itself depends only Finite fixture operators used by Lazaret.cfg. The protocol itself depends only
on the abstract constants above; other TLC configurations may override them with on the abstract constants above; other TLC configurations may override them with
@ -96,6 +112,7 @@ SessionFingerprintFixture ==
(S1 :> "config-a") @@ (S2 :> "config-a") (S1 :> "config-a") @@ (S2 :> "config-a")
DomainFingerprintFixture == D1 :> "config-a" DomainFingerprintFixture == D1 :> "config-a"
ConfigSafeFixture == D1 :> TRUE ConfigSafeFixture == D1 :> TRUE
SandboxIsolationSafeFixture == D1 :> TRUE
RequestSessionFixture == RequestSessionFixture ==
(R1 :> S1) @@ (R2 :> S2) @@ (R3 :> S1) (R1 :> S1) @@ (R2 :> S2) @@ (R3 :> S1)
RequestCommandFixture == RequestCommandFixture ==
@ -289,6 +306,7 @@ StartSandbox(d) ==
/\ domainPhase[d] = "Starting" /\ domainPhase[d] = "Starting"
/\ sidecarState[d] = "Ready" /\ sidecarState[d] = "Ready"
/\ sandboxState[d] = "Stopped" /\ sandboxState[d] = "Stopped"
/\ SandboxIsolationSafe[d]
/\ sandboxState' = [sandboxState EXCEPT ![d] = "Ready"] /\ sandboxState' = [sandboxState EXCEPT ![d] = "Ready"]
/\ UNCHANGED << /\ UNCHANGED <<
domainPhase, coordinatorState, stateAnchorState, sidecarState, domainPhase, coordinatorState, stateAnchorState, sidecarState,
@ -532,14 +550,14 @@ DeveloperDeny(r) ==
LET s == ReqSession(r) IN LET s == ReqSession(r) IN
/\ requestPhase[r] = "AwaitingApproval" /\ requestPhase[r] = "AwaitingApproval"
/\ sessionPhase[s] = "Prompting" /\ sessionPhase[s] = "Prompting"
/\ requestPhase' = [requestPhase EXCEPT ![r] = "Denied"] /\ requestPhase' = [requestPhase EXCEPT ![r] = "DenialPending"]
/\ sessionPhase' = [sessionPhase EXCEPT ![s] = "Attached"] /\ sessionPhase' = [sessionPhase EXCEPT ![s] = "Attached"]
/\ terminalRoute' = [terminalRoute EXCEPT ![s] = "Sandbox"] /\ terminalRoute' = [terminalRoute EXCEPT ![s] = "Sandbox"]
/\ activeRequest' = [activeRequest EXCEPT ![s] = NoRequest]
/\ UNCHANGED << /\ UNCHANGED <<
domainPhase, coordinatorState, stateAnchorState, sidecarState, domainPhase, coordinatorState, stateAnchorState, sidecarState,
sandboxState, trustReady, lockOwner, published, registered, connected, sandboxState, trustReady, lockOwner, published, registered, connected,
connectionState, approvalSource, approvedCommand, hostRunning connectionState, activeRequest, approvalSource, approvedCommand,
hostRunning
>> >>
StartHostExecution(r) == StartHostExecution(r) ==
@ -564,29 +582,76 @@ CompleteHostExecution(r) ==
LET s == ReqSession(r) IN LET s == ReqSession(r) IN
/\ requestPhase[r] = "Executing" /\ requestPhase[r] = "Executing"
/\ r \in hostRunning /\ r \in hostRunning
/\ requestPhase' = [requestPhase EXCEPT ![r] = "Completed"] /\ requestPhase' = [requestPhase EXCEPT ![r] = "CompletionPending"]
/\ hostRunning' = hostRunning \ {r} /\ hostRunning' = hostRunning \ {r}
/\ sessionPhase' = [sessionPhase EXCEPT ![s] = "Attached"] /\ sessionPhase' = [sessionPhase EXCEPT ![s] = "Attached"]
/\ terminalRoute' = [terminalRoute EXCEPT ![s] = "Sandbox"] /\ terminalRoute' = [terminalRoute EXCEPT ![s] = "Sandbox"]
/\ activeRequest' = [activeRequest EXCEPT ![s] = NoRequest]
/\ UNCHANGED << /\ UNCHANGED <<
domainPhase, coordinatorState, stateAnchorState, sidecarState, domainPhase, coordinatorState, stateAnchorState, sidecarState,
sandboxState, trustReady, lockOwner, published, registered, connected, sandboxState, trustReady, lockOwner, published, registered, connected,
connectionState, approvalSource, approvedCommand connectionState, activeRequest, approvalSource, approvedCommand
>> >>
TimeoutRequest(r) == TimeoutRequest(r) ==
LET s == ReqSession(r) IN LET s == ReqSession(r) IN
/\ requestPhase[r] \in {"Verified", "AwaitingApproval", "Approved"} /\ requestPhase[r] \in {"Verified", "AwaitingApproval", "Approved", "Executing"}
/\ activeRequest[s] = r /\ activeRequest[s] = r
/\ requestPhase' = [requestPhase EXCEPT ![r] = "TimedOut"] /\ requestPhase' = [requestPhase EXCEPT ![r] = "TimeoutPending"]
/\ sessionPhase' = [sessionPhase EXCEPT ![s] = "Attached"] /\ sessionPhase' = [sessionPhase EXCEPT ![s] = "Attached"]
/\ terminalRoute' = [terminalRoute EXCEPT ![s] = "Sandbox"] /\ terminalRoute' = [terminalRoute EXCEPT ![s] = "Sandbox"]
/\ hostRunning' = hostRunning \ {r}
/\ UNCHANGED <<
domainPhase, coordinatorState, stateAnchorState, sidecarState,
sandboxState, trustReady, lockOwner, published, registered, connected,
connectionState, activeRequest, approvalSource, approvedCommand
>>
CancelRequest(r) ==
LET s == ReqSession(r) IN
/\ requestPhase[r] \in {
"Verified", "AwaitingApproval", "Approved", "Executing"
}
/\ activeRequest[s] = r
/\ connectionState[s] = "Connected"
/\ requestPhase' = [requestPhase EXCEPT ![r] = "CancellationPending"]
/\ sessionPhase' = [sessionPhase EXCEPT ![s] = "Attached"]
/\ terminalRoute' = [terminalRoute EXCEPT ![s] = "Sandbox"]
/\ hostRunning' = hostRunning \ {r}
/\ UNCHANGED <<
domainPhase, coordinatorState, stateAnchorState, sidecarState,
sandboxState, trustReady, lockOwner, published, registered, connected,
connectionState, activeRequest, approvalSource, approvedCommand
>>
FailHostExecution(r) ==
LET s == ReqSession(r) IN
/\ requestPhase[r] = "Executing"
/\ activeRequest[s] = r
/\ connectionState[s] = "Connected"
/\ requestPhase' = [requestPhase EXCEPT ![r] = "FailurePending"]
/\ sessionPhase' = [sessionPhase EXCEPT ![s] = "Attached"]
/\ terminalRoute' = [terminalRoute EXCEPT ![s] = "Sandbox"]
/\ hostRunning' = hostRunning \ {r}
/\ UNCHANGED <<
domainPhase, coordinatorState, stateAnchorState, sidecarState,
sandboxState, trustReady, lockOwner, published, registered, connected,
connectionState, activeRequest, approvalSource, approvedCommand
>>
DeliverResult(r) ==
LET s == ReqSession(r) IN
/\ requestPhase[r] \in PendingResultPhases
/\ activeRequest[s] = r
/\ connectionState[s] = "Connected"
/\ sessionPhase[s] = "Attached"
/\ requestPhase' = [requestPhase EXCEPT
![r] = DeliveredResultPhase(@)]
/\ activeRequest' = [activeRequest EXCEPT ![s] = NoRequest] /\ activeRequest' = [activeRequest EXCEPT ![s] = NoRequest]
/\ UNCHANGED << /\ UNCHANGED <<
domainPhase, coordinatorState, stateAnchorState, sidecarState, domainPhase, coordinatorState, stateAnchorState, sidecarState,
sandboxState, trustReady, lockOwner, published, registered, connected, sandboxState, trustReady, lockOwner, published, registered, connected,
connectionState, approvalSource, approvedCommand, hostRunning sessionPhase, terminalRoute, connectionState, approvalSource,
approvedCommand, hostRunning
>> >>
(*************************************************************************** (***************************************************************************
@ -664,6 +729,9 @@ RequestProgress(r) ==
\/ StartHostExecution(r) \/ StartHostExecution(r)
\/ CompleteHostExecution(r) \/ CompleteHostExecution(r)
\/ TimeoutRequest(r) \/ TimeoutRequest(r)
\/ CancelRequest(r)
\/ FailHostExecution(r)
\/ DeliverResult(r)
Next == Next ==
\/ (\E s \in Sessions : \/ (\E s \in Sessions :
@ -710,6 +778,9 @@ TypeOK ==
ConfigurationConfinement == ConfigurationConfinement ==
\A d \in Domains : domainPhase[d] # "Absent" => ConfigSafe[d] \A d \in Domains : domainPhase[d] # "Absent" => ConfigSafe[d]
SandboxNamespaceIsolation ==
\A d \in Domains : sandboxState[d] = "Ready" => SandboxIsolationSafe[d]
OrderedNetworkStartup == OrderedNetworkStartup ==
\A d \in Domains : sandboxState[d] = "Ready" => sidecarState[d] = "Ready" \A d \in Domains : sandboxState[d] = "Ready" => sidecarState[d] = "Ready"
@ -764,9 +835,21 @@ StateAnchorOwnership ==
\A d \in Domains : stateAnchorState[d] = "Mounted" => \A d \in Domains : stateAnchorState[d] = "Mounted" =>
coordinatorState[d] \in {"Starting", "Ready", "Stopping"} coordinatorState[d] \in {"Starting", "Ready", "Stopping"}
ResultDeliveryStateConsistent ==
/\ \A r \in Requests : requestPhase[r] \in PendingResultPhases =>
LET s == ReqSession(r) IN
/\ activeRequest[s] = r
/\ connectionState[s] = "Connected"
/\ sessionPhase[s] = "Attached"
/\ r \notin hostRunning
/\ \A r \in Requests : requestPhase[r] \in TerminalRequestPhases =>
/\ activeRequest[ReqSession(r)] # r
/\ r \notin hostRunning
Safety == Safety ==
/\ TypeOK /\ TypeOK
/\ ConfigurationConfinement /\ ConfigurationConfinement
/\ SandboxNamespaceIsolation
/\ OrderedNetworkStartup /\ OrderedNetworkStartup
/\ PublishedDomainsReady /\ PublishedDomainsReady
/\ RegisteredSessionsConnected /\ RegisteredSessionsConnected
@ -777,6 +860,7 @@ Safety ==
/\ TerminalIsolation /\ TerminalIsolation
/\ NoOrphanHostProcess /\ NoOrphanHostProcess
/\ StateAnchorOwnership /\ StateAnchorOwnership
/\ ResultDeliveryStateConsistent
(*************************************************************************** (***************************************************************************
Liveness properties. TLC configurations may check these separately from the Liveness properties. TLC configurations may check these separately from the

View file

@ -47,8 +47,10 @@ terminal interactions; it is not an isolation boundary between processes in that
sandbox. sandbox.
The Agent Workspace is intentionally writable. Lazaret protects the host outside The Agent Workspace is intentionally writable. Lazaret protects the host outside
the explicitly mounted workspace and volumes; it does not protect workspace files the explicitly mounted workspace and volumes. Tracked and ordinary untracked
from modification or deletion by the agent. 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 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 must not be mounted into, or located beneath a host path mounted into, the Agent
@ -75,10 +77,12 @@ implementation plan, logs, and user interfaces.
| **Network Sidecar** | The trusted, shell-less gateway container that establishes the Domain's network policy before the Agent Sandbox starts. | | **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. | | **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. | | **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. | | **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 Lazaret protocol peers. | | **Trust Anchor** | The ephemeral Domain-scoped certificate authority used to authenticate Lazaret protocol peers. |
| **Escalation Client** | The Lazaret-provided executable through which a sandboxed process requests a host action. | | **Escalation Client** | The Lazaret-provided executable through which a sandboxed process requests a host action. |
| **Escalation Request ID** | A cryptographically random identifier used to correlate one host escalation request and response. | | **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. | | **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. | | **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. | | **Storage Root** | The trusted host directory beneath which Lazaret stores Domain Roots and profile-scoped state. |
@ -143,11 +147,22 @@ defined by the [TLA+ application protocol model](protocol/Lazaret.tla).
### 3.3 Filesystem and Git Model ### 3.3 Filesystem and Git Model
The Agent Workspace is mounted read-write and changes propagate in both The Agent Workspace is mounted read-write. Tracked paths and untracked paths that
directions. Workspace content, the Git index, `HEAD`, references, and objects are are not ignored propagate in both directions. The Git index, `HEAD`, references,
therefore shared with the host in real time. Lazaret does not create a separate and objects are shared with the host in real time. Lazaret does not create a
worktree or branch and does not promise that the host working tree remains separate worktree or branch and does not promise that the host working tree
unchanged. 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 Lazaret applies narrow protective mounts over host integration files that must not
be modified by the agent: be modified by the agent:
@ -182,10 +197,16 @@ The Network Sidecar establishes the Domain's egress routes before the Agent
Sandbox starts. The sidecar alone receives the network-administration capability Sandbox starts. The sidecar alone receives the network-administration capability
needed to install policy; the Agent Sandbox does not receive that capability. needed to install policy; the Agent Sandbox does not receive that capability.
Network policy is expressed as permitted and denied destination prefixes. Normal IPv4 destinations are permitted by default except for the RFC 1918 private
Linux longest-prefix routing determines the most specific matching route; an address ranges, which are denied by default. Explicit, more-specific allow rules
equal-prefix conflict must be rejected or resolved in favor of denial. IPv4 and may grant access within those ranges. Lazaret also installs the reachability
IPv6 must either receive equivalent policy or an address family must be disabled. 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 The topology and policy-installation sequence must ensure that all external Agent
Sandbox traffic traverses the configured enforcement path. Dynamic policy changes Sandbox traffic traverses the configured enforcement path. Dynamic policy changes
@ -206,8 +227,12 @@ the complete vector by default; permission for arbitrary trailing arguments is a
explicit property of an individual rule. explicit property of an individual rule.
Interactive approval may allow the developer to approve, deny, or edit a request. Interactive approval may allow the developer to approve, deny, or edit a request.
The request must reach a terminal state and terminal ownership must return to the Every accepted request must produce an Escalation Result for its Escalation
sandbox session after resolution. Client while its Client Session remains connected. The result reports approval,
denial, timeout, cancellation, or failure and, when a host command ran, its exit
status and standard-output and standard-error data. 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 Authentication, message correlation, approval, execution, cancellation, and
terminal-routing transitions are defined by the terminal-routing transitions are defined by the
@ -230,6 +255,8 @@ terminal-routing transitions are defined by the
| **SR-9** | Escalation requests shall be authenticated to a Domain and bound to a registered Session ID before routing or execution. | | **SR-9** | Escalation requests shall be authenticated to a Domain and bound to a registered Session ID before routing or execution. |
| **SR-10** | Domain sockets and coordination files shall reside in a host-private directory owned by the developer. | | **SR-10** | Domain sockets 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-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. |
### 4.2 Functional Requirements ### 4.2 Functional Requirements
@ -237,19 +264,20 @@ terminal-routing transitions are defined by the
| :--- | :--- | | :--- | :--- |
| **FR-1** | Lazaret shall derive a stable Domain ID from the selected Agent Profile and canonical Agent Workspace. | | **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-2** | Concurrent Client Sessions in one Domain shall share one coordinator, Agent Sandbox, Network Sidecar, and State Anchor. |
| **FR-3** | Workspace edits and supported Git operations shall propagate bidirectionally between host and sandbox. | | **FR-3** | Tracked and non-ignored workspace edits and supported Git operations shall propagate bidirectionally between host and sandbox. |
| **FR-4** | Selected sandbox paths shall persist across Domain restarts without copying Lazaret host configuration into the 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 an escalation prompt and response to the Client Session identified by the verified Session ID. | | **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-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. | | **FR-7** | Startup and teardown shall recover safely from interrupted or stale prior executions. |
| **FR-8** | Lazaret shall support interactive terminal sessions without degrading supported host terminal capabilities. | | **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. |
### 4.3 Developer-Experience Requirements ### 4.3 Developer-Experience Requirements
| ID | Requirement | | ID | Requirement |
| :--- | :--- | | :--- | :--- |
| **DX-1** | A developer shall be able to launch an Agent Profile in the current workspace with one command. | | **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 shall remain available on later launches of the same Domain. | | **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-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-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-5** | Failures shall report actionable diagnostics and shall not leave the developer's terminal in a corrupted mode. |
@ -259,6 +287,6 @@ terminal-routing transitions are defined by the
| ID | Requirement | | ID | Requirement |
| :--- | :--- | | :--- | :--- |
| **OR-1** | The coordinator shall maintain exclusive runtime ownership of Domain resources. | | **OR-1** | The coordinator shall maintain exclusive runtime ownership of Domain resources. |
| **OR-2** | Every protocol request shall resolve to a completed, denied, timed-out, cancelled, or failed state. | | **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-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-4** | Security-sensitive behavior shall be covered by integration tests on supported Podman, runsc, filesystem, and network configurations. Test evidence belongs in the journal. |