docs: reconcile sandbox policy and protocol results
This commit is contained in:
parent
16123747da
commit
4f63842c90
4 changed files with 149 additions and 29 deletions
|
|
@ -40,6 +40,7 @@ allow_extra_args = false
|
|||
|
||||
[profiles.gemini]
|
||||
image = "docker.io/library/debian:trixie"
|
||||
isolate_gitignored = true
|
||||
persistent_dirs = ["/root", "/usr", "/var", "/etc", "/opt"]
|
||||
allow_egress = ["10.222.0.0/16"]
|
||||
env = { GEMINI_API_KEY = "env:GEMINI_API_KEY" }
|
||||
|
|
@ -49,6 +50,7 @@ volumes = [
|
|||
|
||||
[profiles.claude]
|
||||
image = "docker.io/library/debian:trixie"
|
||||
isolate_gitignored = true
|
||||
persistent_dirs = ["/root", "/usr", "/var", "/etc", "/opt"]
|
||||
allow_egress = ["10.222.0.0/16"]
|
||||
env = { ANTHROPIC_API_KEY = "env:ANTHROPIC_API_KEY" }
|
||||
|
|
@ -58,6 +60,7 @@ volumes = [
|
|||
|
||||
[profiles.codex]
|
||||
image = "docker.io/library/debian:trixie"
|
||||
isolate_gitignored = true
|
||||
persistent_dirs = ["/root", "/usr", "/var", "/etc", "/opt"]
|
||||
allow_egress = ["10.222.0.0/16"]
|
||||
env = { OPENAI_API_KEY = "env:OPENAI_API_KEY" }
|
||||
|
|
@ -67,6 +70,7 @@ volumes = [
|
|||
|
||||
[profiles.grok]
|
||||
image = "docker.io/library/debian:trixie"
|
||||
isolate_gitignored = true
|
||||
persistent_dirs = ["/root", "/usr", "/var", "/etc", "/opt"]
|
||||
allow_egress = ["10.222.0.0/16"]
|
||||
env = { XAI_API_KEY = "env:XAI_API_KEY" }
|
||||
|
|
@ -76,6 +80,7 @@ volumes = [
|
|||
|
||||
[profiles.opencode]
|
||||
image = "docker.io/library/debian:trixie"
|
||||
isolate_gitignored = true
|
||||
persistent_dirs = ["/root", "/usr", "/var", "/etc", "/opt"]
|
||||
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" }
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ CONSTANTS
|
|||
SessionFingerprint <- SessionFingerprintFixture
|
||||
DomainFingerprint <- DomainFingerprintFixture
|
||||
ConfigSafe <- ConfigSafeFixture
|
||||
SandboxIsolationSafe <- SandboxIsolationSafeFixture
|
||||
RequestSession <- RequestSessionFixture
|
||||
RequestCommand <- RequestCommandFixture
|
||||
CertificateValid <- CertificateValidFixture
|
||||
|
|
@ -35,6 +36,7 @@ CHECK_DEADLOCK FALSE
|
|||
INVARIANTS
|
||||
TypeOK
|
||||
ConfigurationConfinement
|
||||
SandboxNamespaceIsolation
|
||||
OrderedNetworkStartup
|
||||
PublishedDomainsReady
|
||||
RegisteredSessionsConnected
|
||||
|
|
@ -45,6 +47,7 @@ INVARIANTS
|
|||
TerminalIsolation
|
||||
NoOrphanHostProcess
|
||||
StateAnchorOwnership
|
||||
ResultDeliveryStateConsistent
|
||||
|
||||
PROPERTIES
|
||||
SessionEventuallyResolves
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@ EXTENDS Naturals, FiniteSets, Sequences, TLC
|
|||
(***************************************************************************
|
||||
Application-wide protocol model for Lazaret.
|
||||
|
||||
The model covers Domain startup, ordered resource preparation, Client Session
|
||||
registration, host escalation, terminal ownership, failure, and teardown.
|
||||
The model covers Domain startup, ordered resource preparation, sandbox launch
|
||||
isolation, Client Session registration, host escalation, correlated result
|
||||
delivery, terminal ownership, failure, and teardown.
|
||||
Cryptographic algorithms, serialization, container commands, filesystems, and
|
||||
network packet handling are refinement boundaries validated by implementation
|
||||
tests rather than modeled internally.
|
||||
|
|
@ -24,6 +25,7 @@ CONSTANTS
|
|||
SessionFingerprint,
|
||||
DomainFingerprint,
|
||||
ConfigSafe,
|
||||
SandboxIsolationSafe,
|
||||
RequestSession,
|
||||
RequestCommand,
|
||||
CertificateValid,
|
||||
|
|
@ -56,6 +58,7 @@ ASSUME
|
|||
/\ SessionFingerprint \in [Sessions -> STRING]
|
||||
/\ DomainFingerprint \in [Domains -> STRING]
|
||||
/\ ConfigSafe \in [Domains -> BOOLEAN]
|
||||
/\ SandboxIsolationSafe \in [Domains -> BOOLEAN]
|
||||
/\ RequestSession \in [Requests -> Sessions]
|
||||
/\ RequestCommand \in [Requests -> Commands]
|
||||
/\ CertificateValid \in [Requests -> BOOLEAN]
|
||||
|
|
@ -75,11 +78,24 @@ TerminalRoutes == {"Detached", "Sandbox", "Prompt", "HostProcess"}
|
|||
ConnectionStates == {"Disconnected", "Connected"}
|
||||
RequestPhases == {
|
||||
"Unused", "Verified", "AwaitingApproval", "Approved", "Executing",
|
||||
"CompletionPending", "DenialPending", "TimeoutPending",
|
||||
"CancellationPending", "FailurePending",
|
||||
"Completed", "Denied", "TimedOut", "Cancelled", "Failed"
|
||||
}
|
||||
ApprovalSources == {NoApproval, "AutoRule", "Developer"}
|
||||
PendingResultPhases == {
|
||||
"CompletionPending", "DenialPending", "TimeoutPending",
|
||||
"CancellationPending", "FailurePending"
|
||||
}
|
||||
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
|
||||
on the abstract constants above; other TLC configurations may override them with
|
||||
|
|
@ -96,6 +112,7 @@ SessionFingerprintFixture ==
|
|||
(S1 :> "config-a") @@ (S2 :> "config-a")
|
||||
DomainFingerprintFixture == D1 :> "config-a"
|
||||
ConfigSafeFixture == D1 :> TRUE
|
||||
SandboxIsolationSafeFixture == D1 :> TRUE
|
||||
RequestSessionFixture ==
|
||||
(R1 :> S1) @@ (R2 :> S2) @@ (R3 :> S1)
|
||||
RequestCommandFixture ==
|
||||
|
|
@ -289,6 +306,7 @@ StartSandbox(d) ==
|
|||
/\ domainPhase[d] = "Starting"
|
||||
/\ sidecarState[d] = "Ready"
|
||||
/\ sandboxState[d] = "Stopped"
|
||||
/\ SandboxIsolationSafe[d]
|
||||
/\ sandboxState' = [sandboxState EXCEPT ![d] = "Ready"]
|
||||
/\ UNCHANGED <<
|
||||
domainPhase, coordinatorState, stateAnchorState, sidecarState,
|
||||
|
|
@ -532,14 +550,14 @@ DeveloperDeny(r) ==
|
|||
LET s == ReqSession(r) IN
|
||||
/\ requestPhase[r] = "AwaitingApproval"
|
||||
/\ sessionPhase[s] = "Prompting"
|
||||
/\ requestPhase' = [requestPhase EXCEPT ![r] = "Denied"]
|
||||
/\ requestPhase' = [requestPhase EXCEPT ![r] = "DenialPending"]
|
||||
/\ sessionPhase' = [sessionPhase EXCEPT ![s] = "Attached"]
|
||||
/\ terminalRoute' = [terminalRoute EXCEPT ![s] = "Sandbox"]
|
||||
/\ activeRequest' = [activeRequest EXCEPT ![s] = NoRequest]
|
||||
/\ UNCHANGED <<
|
||||
domainPhase, coordinatorState, stateAnchorState, sidecarState,
|
||||
sandboxState, trustReady, lockOwner, published, registered, connected,
|
||||
connectionState, approvalSource, approvedCommand, hostRunning
|
||||
connectionState, activeRequest, approvalSource, approvedCommand,
|
||||
hostRunning
|
||||
>>
|
||||
|
||||
StartHostExecution(r) ==
|
||||
|
|
@ -564,29 +582,76 @@ CompleteHostExecution(r) ==
|
|||
LET s == ReqSession(r) IN
|
||||
/\ requestPhase[r] = "Executing"
|
||||
/\ r \in hostRunning
|
||||
/\ requestPhase' = [requestPhase EXCEPT ![r] = "Completed"]
|
||||
/\ requestPhase' = [requestPhase EXCEPT ![r] = "CompletionPending"]
|
||||
/\ hostRunning' = hostRunning \ {r}
|
||||
/\ sessionPhase' = [sessionPhase EXCEPT ![s] = "Attached"]
|
||||
/\ terminalRoute' = [terminalRoute EXCEPT ![s] = "Sandbox"]
|
||||
/\ activeRequest' = [activeRequest EXCEPT ![s] = NoRequest]
|
||||
/\ UNCHANGED <<
|
||||
domainPhase, coordinatorState, stateAnchorState, sidecarState,
|
||||
sandboxState, trustReady, lockOwner, published, registered, connected,
|
||||
connectionState, approvalSource, approvedCommand
|
||||
connectionState, activeRequest, approvalSource, approvedCommand
|
||||
>>
|
||||
|
||||
TimeoutRequest(r) ==
|
||||
LET s == ReqSession(r) IN
|
||||
/\ requestPhase[r] \in {"Verified", "AwaitingApproval", "Approved"}
|
||||
/\ requestPhase[r] \in {"Verified", "AwaitingApproval", "Approved", "Executing"}
|
||||
/\ activeRequest[s] = r
|
||||
/\ requestPhase' = [requestPhase EXCEPT ![r] = "TimedOut"]
|
||||
/\ requestPhase' = [requestPhase EXCEPT ![r] = "TimeoutPending"]
|
||||
/\ 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
|
||||
>>
|
||||
|
||||
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]
|
||||
/\ UNCHANGED <<
|
||||
domainPhase, coordinatorState, stateAnchorState, sidecarState,
|
||||
sandboxState, trustReady, lockOwner, published, registered, connected,
|
||||
connectionState, approvalSource, approvedCommand, hostRunning
|
||||
sessionPhase, terminalRoute, connectionState, approvalSource,
|
||||
approvedCommand, hostRunning
|
||||
>>
|
||||
|
||||
(***************************************************************************
|
||||
|
|
@ -664,6 +729,9 @@ RequestProgress(r) ==
|
|||
\/ StartHostExecution(r)
|
||||
\/ CompleteHostExecution(r)
|
||||
\/ TimeoutRequest(r)
|
||||
\/ CancelRequest(r)
|
||||
\/ FailHostExecution(r)
|
||||
\/ DeliverResult(r)
|
||||
|
||||
Next ==
|
||||
\/ (\E s \in Sessions :
|
||||
|
|
@ -710,6 +778,9 @@ TypeOK ==
|
|||
ConfigurationConfinement ==
|
||||
\A d \in Domains : domainPhase[d] # "Absent" => ConfigSafe[d]
|
||||
|
||||
SandboxNamespaceIsolation ==
|
||||
\A d \in Domains : sandboxState[d] = "Ready" => SandboxIsolationSafe[d]
|
||||
|
||||
OrderedNetworkStartup ==
|
||||
\A d \in Domains : sandboxState[d] = "Ready" => sidecarState[d] = "Ready"
|
||||
|
||||
|
|
@ -764,9 +835,21 @@ StateAnchorOwnership ==
|
|||
\A d \in Domains : stateAnchorState[d] = "Mounted" =>
|
||||
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 ==
|
||||
/\ TypeOK
|
||||
/\ ConfigurationConfinement
|
||||
/\ SandboxNamespaceIsolation
|
||||
/\ OrderedNetworkStartup
|
||||
/\ PublishedDomainsReady
|
||||
/\ RegisteredSessionsConnected
|
||||
|
|
@ -777,6 +860,7 @@ Safety ==
|
|||
/\ TerminalIsolation
|
||||
/\ NoOrphanHostProcess
|
||||
/\ StateAnchorOwnership
|
||||
/\ ResultDeliveryStateConsistent
|
||||
|
||||
(***************************************************************************
|
||||
Liveness properties. TLC configurations may check these separately from the
|
||||
|
|
|
|||
|
|
@ -47,8 +47,10 @@ 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; it does not protect workspace files
|
||||
from modification or deletion by the agent.
|
||||
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
|
||||
|
|
@ -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 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 Lazaret protocol peers. |
|
||||
| **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 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. |
|
||||
|
|
@ -143,11 +147,22 @@ defined by the [TLA+ application protocol model](protocol/Lazaret.tla).
|
|||
|
||||
### 3.3 Filesystem and Git Model
|
||||
|
||||
The Agent Workspace is mounted read-write and changes propagate in both
|
||||
directions. Workspace content, the Git index, `HEAD`, references, and objects are
|
||||
therefore 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.
|
||||
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:
|
||||
|
|
@ -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
|
||||
needed to install policy; the Agent Sandbox does not receive that capability.
|
||||
|
||||
Network policy is expressed as permitted and denied destination prefixes. 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.
|
||||
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
|
||||
|
|
@ -206,8 +227,12 @@ the complete vector by default; permission for arbitrary trailing arguments is a
|
|||
explicit property of an individual rule.
|
||||
|
||||
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
|
||||
sandbox session after resolution.
|
||||
Every accepted request must produce an Escalation Result for its Escalation
|
||||
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
|
||||
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-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-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
|
||||
|
||||
|
|
@ -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-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-4** | Selected sandbox paths 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-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. |
|
||||
| **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
|
||||
|
||||
| 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 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-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. |
|
||||
|
|
@ -259,6 +287,6 @@ terminal-routing transitions are defined by the
|
|||
| ID | Requirement |
|
||||
| :--- | :--- |
|
||||
| **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-4** | Security-sensitive behavior shall be covered by integration tests on supported Podman, runsc, filesystem, and network configurations. Test evidence belongs in the journal. |
|
||||
|
|
|
|||
Loading…
Reference in a new issue