Add phase-aware pidfs process ownership records
This commit is contained in:
parent
f840a48376
commit
de57e77dc8
5 changed files with 3513 additions and 246 deletions
|
|
@ -2,6 +2,7 @@ use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub const OPAQUE_ID_BYTES: usize = 32;
|
pub const OPAQUE_ID_BYTES: usize = 32;
|
||||||
pub const SHA256_BYTES: usize = 32;
|
pub const SHA256_BYTES: usize = 32;
|
||||||
|
pub const BOOT_ID_BYTES: usize = 16;
|
||||||
|
|
||||||
macro_rules! opaque_identity {
|
macro_rules! opaque_identity {
|
||||||
($($name:ident),+ $(,)?) => {
|
($($name:ident),+ $(,)?) => {
|
||||||
|
|
@ -98,15 +99,34 @@ digest_identity!(
|
||||||
|
|
||||||
numeric_identity!(
|
numeric_identity!(
|
||||||
GenerationId(u64),
|
GenerationId(u64),
|
||||||
ProcessId(u32),
|
HostPidLocator(u32),
|
||||||
ProcessStartTime(u64),
|
ProcessStartTimeTicks(u64),
|
||||||
|
GuestProcessId(u32),
|
||||||
|
GuestProcessStartTimeTicks(u64),
|
||||||
GuestProcessGroupId(u32),
|
GuestProcessGroupId(u32),
|
||||||
FileDescriptor(i32),
|
FileDescriptor(i32),
|
||||||
DeviceId(u64),
|
DeviceId(u64),
|
||||||
InodeId(u64),
|
InodeId(u64),
|
||||||
|
PidfsDeviceId(u64),
|
||||||
|
PidfsInodeId(u64),
|
||||||
MountId(u64),
|
MountId(u64),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
|
||||||
|
pub struct BootId([u8; BOOT_ID_BYTES]);
|
||||||
|
|
||||||
|
impl BootId {
|
||||||
|
#[must_use]
|
||||||
|
pub const fn new(bytes: [u8; BOOT_ID_BYTES]) -> Self {
|
||||||
|
Self(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub const fn as_bytes(&self) -> &[u8; BOOT_ID_BYTES] {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
|
||||||
pub struct HostPath(Vec<u8>);
|
pub struct HostPath(Vec<u8>);
|
||||||
|
|
||||||
|
|
@ -137,10 +157,79 @@ impl SourceIdentity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
|
||||||
|
pub struct PidfsProcessAuthorityV1 {
|
||||||
|
pub boot_id: BootId,
|
||||||
|
pub pidfs_st_dev: PidfsDeviceId,
|
||||||
|
pub pidfs_st_ino: PidfsInodeId,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||||
pub struct ProcessIdentity {
|
pub struct PidfsProcessIdentityV1 {
|
||||||
pub pid: ProcessId,
|
authority: PidfsProcessAuthorityV1,
|
||||||
pub start_time: ProcessStartTime,
|
host_pid: HostPidLocator,
|
||||||
|
start_time_ticks: ProcessStartTimeTicks,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PidfsProcessIdentityV1 {
|
||||||
|
#[must_use]
|
||||||
|
pub const fn new(
|
||||||
|
authority: PidfsProcessAuthorityV1,
|
||||||
|
host_pid: HostPidLocator,
|
||||||
|
start_time_ticks: ProcessStartTimeTicks,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
authority,
|
||||||
|
host_pid,
|
||||||
|
start_time_ticks,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub const fn authority(&self) -> PidfsProcessAuthorityV1 {
|
||||||
|
self.authority
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub const fn host_pid(&self) -> HostPidLocator {
|
||||||
|
self.host_pid
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub const fn start_time_ticks(&self) -> ProcessStartTimeTicks {
|
||||||
|
self.start_time_ticks
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn has_same_authority(&self, other: &Self) -> bool {
|
||||||
|
self.authority == other.authority
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||||
|
pub struct LegacyUnverifiedProcessIdentityV1 {
|
||||||
|
host_pid: HostPidLocator,
|
||||||
|
start_time_ticks: ProcessStartTimeTicks,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LegacyUnverifiedProcessIdentityV1 {
|
||||||
|
#[must_use]
|
||||||
|
pub const fn new(host_pid: HostPidLocator, start_time_ticks: ProcessStartTimeTicks) -> Self {
|
||||||
|
Self {
|
||||||
|
host_pid,
|
||||||
|
start_time_ticks,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub const fn host_pid(&self) -> HostPidLocator {
|
||||||
|
self.host_pid
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub const fn start_time_ticks(&self) -> ProcessStartTimeTicks {
|
||||||
|
self.start_time_ticks
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||||
|
|
@ -162,8 +251,11 @@ mod tests {
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
ContainerId, CoordinatorId, DomainId, GenerationId, MountIdentity, NamespaceIdentity,
|
BootId, ContainerId, CoordinatorId, DeviceId, DomainId, GenerationId, GuestProcessId,
|
||||||
NetworkSidecarId, PreparedArtifactId, ProcessIdentity, RequestId, SandboxId, SessionId,
|
GuestProcessStartTimeTicks, HostPidLocator, LegacyUnverifiedProcessIdentityV1,
|
||||||
|
MountIdentity, NamespaceIdentity, NetworkSidecarId, PidfsDeviceId, PidfsInodeId,
|
||||||
|
PidfsProcessAuthorityV1, PidfsProcessIdentityV1, PreparedArtifactId, ProcessStartTimeTicks,
|
||||||
|
RequestId, SandboxId, SessionId,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -178,7 +270,17 @@ mod tests {
|
||||||
TypeId::of::<RequestId>(),
|
TypeId::of::<RequestId>(),
|
||||||
TypeId::of::<PreparedArtifactId>(),
|
TypeId::of::<PreparedArtifactId>(),
|
||||||
TypeId::of::<ContainerId>(),
|
TypeId::of::<ContainerId>(),
|
||||||
TypeId::of::<ProcessIdentity>(),
|
TypeId::of::<BootId>(),
|
||||||
|
TypeId::of::<PidfsProcessAuthorityV1>(),
|
||||||
|
TypeId::of::<PidfsProcessIdentityV1>(),
|
||||||
|
TypeId::of::<LegacyUnverifiedProcessIdentityV1>(),
|
||||||
|
TypeId::of::<HostPidLocator>(),
|
||||||
|
TypeId::of::<ProcessStartTimeTicks>(),
|
||||||
|
TypeId::of::<GuestProcessId>(),
|
||||||
|
TypeId::of::<GuestProcessStartTimeTicks>(),
|
||||||
|
TypeId::of::<PidfsDeviceId>(),
|
||||||
|
TypeId::of::<PidfsInodeId>(),
|
||||||
|
TypeId::of::<DeviceId>(),
|
||||||
TypeId::of::<NamespaceIdentity>(),
|
TypeId::of::<NamespaceIdentity>(),
|
||||||
TypeId::of::<MountIdentity>(),
|
TypeId::of::<MountIdentity>(),
|
||||||
];
|
];
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
266
docs/schema.md
266
docs/schema.md
|
|
@ -98,7 +98,7 @@ classDiagram
|
||||||
Domain "1" --> "0..*" Generation
|
Domain "1" --> "0..*" Generation
|
||||||
Generation "1" --> "0..*" Participant
|
Generation "1" --> "0..*" Participant
|
||||||
Participant --> ParticipantState
|
Participant --> ParticipantState
|
||||||
Participant --> ParticipantOwnership
|
Participant --> "0..1" ParticipantOwnership
|
||||||
Generation "1" --> "0..*" Session
|
Generation "1" --> "0..*" Session
|
||||||
Session "1" --> "0..1" SessionTerminal
|
Session "1" --> "0..1" SessionTerminal
|
||||||
SessionTerminal --> SessionTerminalState
|
SessionTerminal --> SessionTerminalState
|
||||||
|
|
@ -108,7 +108,121 @@ classDiagram
|
||||||
|
|
||||||
`durable_decision` and `session_phase` contain values defined by the application
|
`durable_decision` and `session_phase` contain values defined by the application
|
||||||
protocol model. This schema records them without redefining either state machine.
|
protocol model. This schema records them without redefining either state machine.
|
||||||
Each store carries its own schema identity and version.
|
Each store carries its own schema identity and version. The current Domain Store
|
||||||
|
identity is `lazaret-domain-store-v2` with version `2`. Domain Store v1 metadata
|
||||||
|
dispatches only to legacy migration handling; it is never decoded as v2. The
|
||||||
|
Storage Catalog remains `lazaret-storage-catalog-v1` with version `1`.
|
||||||
|
|
||||||
|
### Host process identity and staged ownership
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
classDiagram
|
||||||
|
class PidfsProcessAuthorityV1 {
|
||||||
|
boot_id
|
||||||
|
pidfs_st_dev
|
||||||
|
pidfs_st_ino
|
||||||
|
}
|
||||||
|
|
||||||
|
class PidfsProcessIdentityV1 {
|
||||||
|
host_pid
|
||||||
|
start_time_ticks
|
||||||
|
}
|
||||||
|
|
||||||
|
class LegacyUnverifiedProcessIdentityV1 {
|
||||||
|
host_pid
|
||||||
|
start_time_ticks
|
||||||
|
}
|
||||||
|
|
||||||
|
class HostProcessIdentity {
|
||||||
|
<<union>>
|
||||||
|
PidfsV1
|
||||||
|
LegacyUnverifiedV1
|
||||||
|
}
|
||||||
|
|
||||||
|
class PersistedHostProcessOwnership {
|
||||||
|
<<union>>
|
||||||
|
Unassigned
|
||||||
|
Staged
|
||||||
|
Committed
|
||||||
|
}
|
||||||
|
|
||||||
|
class HostProcessOwnershipSlot {
|
||||||
|
<<enumeration>>
|
||||||
|
SandboxHostCoordinator
|
||||||
|
SandboxRuntimeSupervisor
|
||||||
|
SandboxAttachedRunsc
|
||||||
|
StateAnchorDaemon
|
||||||
|
GitRoutingDaemon
|
||||||
|
NetworkRuntimeSupervisor
|
||||||
|
HelperConnectivityHelper
|
||||||
|
HelperRuntimeSupervisor
|
||||||
|
SessionTerminalHostCoordinator
|
||||||
|
SessionTerminalExecClient
|
||||||
|
EscalationHostProcess
|
||||||
|
}
|
||||||
|
|
||||||
|
class ProcessOwnershipMessageVersion {
|
||||||
|
<<enumeration>>
|
||||||
|
V1
|
||||||
|
}
|
||||||
|
|
||||||
|
class StagedProcessOwnership {
|
||||||
|
version
|
||||||
|
domain_id
|
||||||
|
generation_id
|
||||||
|
coordinator_id
|
||||||
|
slot
|
||||||
|
identity
|
||||||
|
}
|
||||||
|
|
||||||
|
class ProcessOwnershipCommitAck {
|
||||||
|
version
|
||||||
|
committed
|
||||||
|
}
|
||||||
|
|
||||||
|
PidfsProcessIdentityV1 --> PidfsProcessAuthorityV1 : authority
|
||||||
|
HostProcessIdentity --> PidfsProcessIdentityV1 : PidfsV1
|
||||||
|
HostProcessIdentity --> LegacyUnverifiedProcessIdentityV1 : LegacyUnverifiedV1
|
||||||
|
PersistedHostProcessOwnership --> PidfsProcessIdentityV1 : Staged
|
||||||
|
PersistedHostProcessOwnership --> HostProcessIdentity : Committed
|
||||||
|
StagedProcessOwnership --> ProcessOwnershipMessageVersion
|
||||||
|
StagedProcessOwnership --> HostProcessOwnershipSlot
|
||||||
|
StagedProcessOwnership --> PidfsProcessIdentityV1
|
||||||
|
ProcessOwnershipCommitAck --> ProcessOwnershipMessageVersion
|
||||||
|
ProcessOwnershipCommitAck --> StagedProcessOwnership : committed
|
||||||
|
```
|
||||||
|
|
||||||
|
`boot_id` is the canonical 16-byte UUID value from the current Linux boot ID;
|
||||||
|
`pidfs_st_dev` and `pidfs_st_ino` are distinct unsigned 64-bit pidfs types. Only
|
||||||
|
their typed `PidfsProcessAuthorityV1` tuple is positive durable authority.
|
||||||
|
`host_pid` is an unsigned 32-bit host-PID-namespace locator and
|
||||||
|
`start_time_ticks` is unsigned 64-bit negative evidence. Equality of the latter
|
||||||
|
two fields cannot produce or upgrade authority.
|
||||||
|
|
||||||
|
Every persisted host-process edge uses `PersistedHostProcessOwnership`.
|
||||||
|
`Unassigned` records that no process ownership has been acquired. `Staged`
|
||||||
|
contains only a current pidfs identity. It is positive authority for exact
|
||||||
|
same-handle cleanup and `PidfsRecoveryMatch`, including bootstrap recovery, but
|
||||||
|
cannot satisfy readiness, publication, dependent-resource creation, or
|
||||||
|
adoption. `Committed` may contain a current pidfs identity or a
|
||||||
|
legacy-unverified v1 PID/start-only value. Only committed current pidfs
|
||||||
|
ownership is serviceable; the legacy variant always refuses positive process
|
||||||
|
action. There is no observation-based conversion from legacy data to
|
||||||
|
`PidfsProcessIdentityV1`.
|
||||||
|
|
||||||
|
Participant `Intent` and `ReleaseIntent` may retain any process-slot phase.
|
||||||
|
Every active participant state and Session Terminal `Running` requires current
|
||||||
|
committed ownership in every role-required process slot. `Clean` requires every
|
||||||
|
process slot to be `Unassigned`.
|
||||||
|
|
||||||
|
`StagedProcessOwnership` binds one acquisition to its Domain, generation,
|
||||||
|
generation-scoped Session Coordinator ID, exact ownership slot, and complete
|
||||||
|
pidfs identity. `ProcessOwnershipCommitAck.committed` echoes that exact staged
|
||||||
|
value. The receiver accepts one matching v1 acknowledgment only while that
|
||||||
|
exact launch barrier is pending on the authenticated generation channel. A
|
||||||
|
mismatched, duplicated after release, unknown-version, lost, or unauthenticated
|
||||||
|
acknowledgment cannot release a barrier. These shapes define correlation and
|
||||||
|
the durable boundary; they do not select the authentication or transport codec.
|
||||||
|
|
||||||
### Session terminal ownership
|
### Session terminal ownership
|
||||||
|
|
||||||
|
|
@ -127,31 +241,35 @@ classDiagram
|
||||||
inode
|
inode
|
||||||
}
|
}
|
||||||
|
|
||||||
class ProcessIdentity {
|
|
||||||
pid
|
|
||||||
start_time
|
|
||||||
}
|
|
||||||
|
|
||||||
class GuestProcessIdentity {
|
class GuestProcessIdentity {
|
||||||
leader_pid
|
leader_pid
|
||||||
leader_start_time
|
leader_start_time
|
||||||
process_group_id
|
process_group_id
|
||||||
}
|
}
|
||||||
|
|
||||||
SessionTerminal --> ProcessIdentity : host_coordinator
|
SessionTerminal --> PersistedHostProcessOwnership : host_coordinator
|
||||||
SessionTerminal --> PtyIdentity : pty_master
|
SessionTerminal --> PtyIdentity : pty_master
|
||||||
SessionTerminal --> ProcessIdentity : exec_client
|
SessionTerminal --> PersistedHostProcessOwnership : exec_client
|
||||||
SessionTerminal --> GuestProcessIdentity : guest_process
|
SessionTerminal --> GuestProcessIdentity : guest_process
|
||||||
```
|
```
|
||||||
|
|
||||||
Host process identities use host pid and start time. Guest process identity uses
|
Host process ownership uses the phase-aware pidfs dispatch above. Guest
|
||||||
the leader PID and start time observed inside the exact generation-bound runsc
|
process identity uses the leader PID and start time observed inside the exact
|
||||||
container; its process-group ID is relative to that container's root PID
|
generation-bound runsc container; its process-group ID is relative to that
|
||||||
namespace.
|
container's root PID namespace and never converts to host process authority.
|
||||||
The PTY identity is exact only in combination with its Host Coordinator process
|
The PTY identity is exact only in combination with its Host Coordinator process
|
||||||
identity. `SessionTerminalState` is a physical recovery state below the
|
identity. `SessionTerminalState` is a physical recovery state below the
|
||||||
application protocol model; it does not redefine `session_phase`.
|
application protocol model; it does not redefine `session_phase`.
|
||||||
|
|
||||||
|
`SessionTerminal=Intent` permits partial acquisition: `container_id`,
|
||||||
|
`pty_master`, and `guest_process` are optional, while both process slots use the
|
||||||
|
three-phase ownership union. `Running` requires all three non-process identities
|
||||||
|
and current committed HC and exec-client ownership. `ReleaseIntent` retains all
|
||||||
|
recorded evidence, including staged current pidfs authority. `Clean` requires
|
||||||
|
all three optional identities absent and both process slots unassigned. A bare
|
||||||
|
Intent may become this explicit cleared snapshot only after runtime absence is
|
||||||
|
proven.
|
||||||
|
|
||||||
## Session terminal protocol
|
## Session terminal protocol
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
|
|
@ -452,11 +570,6 @@ classDiagram
|
||||||
<<union>>
|
<<union>>
|
||||||
}
|
}
|
||||||
|
|
||||||
class ProcessIdentity {
|
|
||||||
pid
|
|
||||||
start_time
|
|
||||||
}
|
|
||||||
|
|
||||||
class NamespaceIdentity {
|
class NamespaceIdentity {
|
||||||
device
|
device
|
||||||
inode
|
inode
|
||||||
|
|
@ -469,57 +582,57 @@ classDiagram
|
||||||
}
|
}
|
||||||
|
|
||||||
class PreparedLowerOwnership {
|
class PreparedLowerOwnership {
|
||||||
prepared_artifact_id
|
optional prepared_artifact_id
|
||||||
source_identity
|
optional source_identity
|
||||||
lease_state
|
optional lease_state
|
||||||
path
|
optional path
|
||||||
device
|
optional device
|
||||||
inode
|
optional inode
|
||||||
}
|
}
|
||||||
|
|
||||||
class StateAnchorOwnership {
|
class StateAnchorOwnership {
|
||||||
lower_artifact_id
|
optional lower_artifact_id
|
||||||
upper_path
|
optional upper_path
|
||||||
work_path
|
optional work_path
|
||||||
merged_path
|
optional merged_path
|
||||||
daemon
|
daemon
|
||||||
mount
|
optional mount
|
||||||
}
|
}
|
||||||
|
|
||||||
class GitRoutingOwnership {
|
class GitRoutingOwnership {
|
||||||
mountpoint
|
optional mountpoint
|
||||||
host_root
|
optional host_root
|
||||||
isolated_root
|
optional isolated_root
|
||||||
daemon
|
daemon
|
||||||
mount
|
optional mount
|
||||||
}
|
}
|
||||||
|
|
||||||
class NetworkOwnership {
|
class NetworkOwnership {
|
||||||
derived_policy_digest
|
optional derived_policy_digest
|
||||||
readiness_fingerprint
|
optional readiness_fingerprint
|
||||||
readiness_evidence
|
optional readiness_evidence
|
||||||
namespace
|
optional namespace
|
||||||
runtime_supervisor
|
runtime_supervisor
|
||||||
}
|
}
|
||||||
|
|
||||||
class HelperOwnership {
|
class HelperOwnership {
|
||||||
network_sidecar_id
|
optional network_sidecar_id
|
||||||
connectivity_helper
|
connectivity_helper
|
||||||
runtime_supervisor
|
runtime_supervisor
|
||||||
}
|
}
|
||||||
|
|
||||||
class SandboxOwnership {
|
class SandboxOwnership {
|
||||||
container_id
|
optional container_id
|
||||||
runtime_root
|
optional runtime_root
|
||||||
bundle_digest
|
optional bundle_digest
|
||||||
host_coordinator
|
host_coordinator
|
||||||
runtime_supervisor
|
runtime_supervisor
|
||||||
attached_runsc
|
attached_runsc
|
||||||
}
|
}
|
||||||
|
|
||||||
class PublicationOwnership {
|
class PublicationOwnership {
|
||||||
socket_path
|
optional socket_path
|
||||||
identity_path
|
optional identity_path
|
||||||
}
|
}
|
||||||
|
|
||||||
ParticipantOwnership --> PreparedLowerOwnership
|
ParticipantOwnership --> PreparedLowerOwnership
|
||||||
|
|
@ -529,20 +642,62 @@ classDiagram
|
||||||
ParticipantOwnership --> HelperOwnership
|
ParticipantOwnership --> HelperOwnership
|
||||||
ParticipantOwnership --> SandboxOwnership
|
ParticipantOwnership --> SandboxOwnership
|
||||||
ParticipantOwnership --> PublicationOwnership
|
ParticipantOwnership --> PublicationOwnership
|
||||||
StateAnchorOwnership --> ProcessIdentity : daemon
|
StateAnchorOwnership --> PersistedHostProcessOwnership : daemon
|
||||||
StateAnchorOwnership --> MountIdentity : mount
|
StateAnchorOwnership --> MountIdentity : mount
|
||||||
GitRoutingOwnership --> ProcessIdentity : daemon
|
GitRoutingOwnership --> PersistedHostProcessOwnership : daemon
|
||||||
GitRoutingOwnership --> MountIdentity : mount
|
GitRoutingOwnership --> MountIdentity : mount
|
||||||
NetworkOwnership --> NamespaceIdentity : namespace
|
NetworkOwnership --> NamespaceIdentity : namespace
|
||||||
NetworkOwnership --> ProcessIdentity : runtime_supervisor
|
NetworkOwnership --> PersistedHostProcessOwnership : runtime_supervisor
|
||||||
NetworkOwnership --> NetworkReadinessEvidence : readiness_evidence
|
NetworkOwnership --> NetworkReadinessEvidence : readiness_evidence
|
||||||
HelperOwnership --> ProcessIdentity : connectivity_helper
|
HelperOwnership --> PersistedHostProcessOwnership : connectivity_helper
|
||||||
HelperOwnership --> ProcessIdentity : runtime_supervisor
|
HelperOwnership --> PersistedHostProcessOwnership : runtime_supervisor
|
||||||
SandboxOwnership --> ProcessIdentity : host_coordinator
|
SandboxOwnership --> PersistedHostProcessOwnership : host_coordinator
|
||||||
SandboxOwnership --> ProcessIdentity : runtime_supervisor
|
SandboxOwnership --> PersistedHostProcessOwnership : runtime_supervisor
|
||||||
SandboxOwnership --> ProcessIdentity : attached_runsc
|
SandboxOwnership --> PersistedHostProcessOwnership : attached_runsc
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`Participant.ownership=None` is the single encoding of a bare role-bound
|
||||||
|
Intent. Once a role snapshot exists it never returns to `None`. `Intent` admits
|
||||||
|
a non-empty partial matching snapshot. `ReleaseIntent` admits a non-empty
|
||||||
|
partial or complete matching snapshot and retains every exact acquired value.
|
||||||
|
`Clean` requires a matching explicit empty snapshot: all optional physical
|
||||||
|
fields are absent and all process slots are unassigned. This distinguishes
|
||||||
|
"nothing acquired yet" from "absence was proved and cleanup committed."
|
||||||
|
|
||||||
|
The only serviceable role/state pairs are:
|
||||||
|
|
||||||
|
| Role | Active state | Required physical evidence |
|
||||||
|
|---|---|---|
|
||||||
|
| `PreparedLower` | `LeaseHeld` | artifact ID, source, `lease_state=LeaseHeld`, path, device, inode |
|
||||||
|
| `StateAnchor` | `Prepared` | lower artifact, three paths, mount, current committed daemon |
|
||||||
|
| `GitRouting` | `Prepared` | three paths, mount, current committed daemon |
|
||||||
|
| `Network` | `Committed` | policy digest, fingerprint, readiness evidence, namespace, current committed RS |
|
||||||
|
| `Helper` | `Committed` | sidecar ID, current committed helper and RS |
|
||||||
|
| `Sandbox` | `Committed` | container, runtime root, bundle digest, current committed HC, RS, and runsc |
|
||||||
|
| `Publication` | `Committed` | socket and identity paths |
|
||||||
|
|
||||||
|
Network readiness evidence must name the enclosing Participant generation and
|
||||||
|
match its policy digest, fingerprint, namespace, and committed RS identity.
|
||||||
|
|
||||||
|
Intent acquisition is monotonic: optional fields move only absent-to-present,
|
||||||
|
and newly acquired child slots move only `Unassigned` to `Staged` to exact
|
||||||
|
matching current `Committed`. A current committed owner or supervisor may
|
||||||
|
enter a newly created dependent-role snapshot directly and is then immutable;
|
||||||
|
legacy ownership cannot use this rule. Recorded evidence cannot be replaced or
|
||||||
|
cleared. A transition to ReleaseIntent preserves all evidence; PreparedLower
|
||||||
|
changes only its lease phase to `ReleaseIntent`. ReleaseIntent becomes Clean
|
||||||
|
only after independent absence proof and atomically clears every physical
|
||||||
|
field. A bare Intent may become Clean only after the same sealed runtime-absence
|
||||||
|
check.
|
||||||
|
|
||||||
|
The direct existing-process edges are Network RS, Helper RS, Sandbox HC, and
|
||||||
|
Session Terminal HC. State Anchor daemon, Git-routing daemon, Helper process,
|
||||||
|
Sandbox RS, attached runsc, and Session Terminal exec client are acquired child
|
||||||
|
edges and require staging.
|
||||||
|
No bare Intent moves directly to an active state. PreparedLower must also move
|
||||||
|
from recorded `lease_state=Intent` to `LeaseHeld`; it cannot acquire the catalog
|
||||||
|
lease from an absent lease phase.
|
||||||
|
|
||||||
## Storage Catalog
|
## Storage Catalog
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
|
|
@ -720,11 +875,6 @@ classDiagram
|
||||||
inode
|
inode
|
||||||
}
|
}
|
||||||
|
|
||||||
class ProcessIdentity {
|
|
||||||
pid
|
|
||||||
start_time
|
|
||||||
}
|
|
||||||
|
|
||||||
class NetworkReachabilityProbe {
|
class NetworkReachabilityProbe {
|
||||||
domain_id
|
domain_id
|
||||||
generation_id
|
generation_id
|
||||||
|
|
@ -751,8 +901,8 @@ classDiagram
|
||||||
NetworkReadinessEvidence --> NetworkReadinessIdentity : fingerprints
|
NetworkReadinessEvidence --> NetworkReadinessIdentity : fingerprints
|
||||||
NetworkReadinessEvidence --> NetworkReachabilityProbe : includes
|
NetworkReadinessEvidence --> NetworkReachabilityProbe : includes
|
||||||
NetworkReadinessAttestation --> NamespaceIdentity : enforcement_namespace
|
NetworkReadinessAttestation --> NamespaceIdentity : enforcement_namespace
|
||||||
NetworkReadinessAttestation --> ProcessIdentity : runtime_supervisor
|
NetworkReadinessAttestation --> PidfsProcessIdentityV1 : runtime_supervisor
|
||||||
NetworkReadinessAttestation --> ProcessIdentity : connectivity_helper
|
NetworkReadinessAttestation --> PidfsProcessIdentityV1 : connectivity_helper
|
||||||
NetworkReadinessAttestation --> NetworkObservation
|
NetworkReadinessAttestation --> NetworkObservation
|
||||||
NetworkObservation --> PolicyDelivery
|
NetworkObservation --> PolicyDelivery
|
||||||
NetworkReadinessIdentity --> NamespaceIdentity : enforcement_namespace
|
NetworkReadinessIdentity --> NamespaceIdentity : enforcement_namespace
|
||||||
|
|
|
||||||
|
|
@ -194,6 +194,158 @@ mounts, `slirp4netns`, PTYs, runsc clients, durable store encoding, and exact
|
||||||
ownership records). Physical recovery enumerates and inspects those resources
|
ownership records). Physical recovery enumerates and inspects those resources
|
||||||
independently of process liveness.
|
independently of process liveness.
|
||||||
|
|
||||||
|
#### Host process identity and causal acquisition refinement
|
||||||
|
|
||||||
|
Lazaret supports host-process authority only on an exactly source/build-qualified
|
||||||
|
Linux x86-64 kernel at or above 6.9. Each supported upstream or vendor build
|
||||||
|
preserves the boot-monotonic 64-bit pidfs inode allocation used by the qualified
|
||||||
|
build and passes its qualification suite. A release string, version floor,
|
||||||
|
partial backport, or successful runtime smoke cannot substitute for exact build
|
||||||
|
qualification. Linux 32-bit, other architectures, kernels below 6.9, and
|
||||||
|
unqualified vendor builds refuse before a generation or external runtime
|
||||||
|
resource is created.
|
||||||
|
|
||||||
|
On every boot Lazaret runs the finite `PidfsHostSupportProbe` before the first
|
||||||
|
generation and caches success only under the current boot ID. The probe has the
|
||||||
|
repository-registered operation sequence and bounds. It checks pidfd operations
|
||||||
|
and pidfs magic, same-live-process concurrent and last-close/reopen identity,
|
||||||
|
distinct simultaneous processes, stale-handle non-action, and
|
||||||
|
exit/reap/drop-all/later-process non-reuse. Any missing operation, failed
|
||||||
|
observation, cleanup uncertainty, timeout, or unexpected equality refuses
|
||||||
|
support for that boot. This bounded smoke checks the running path; it does not
|
||||||
|
prove global inode non-reuse or qualify the kernel source/build.
|
||||||
|
|
||||||
|
`PidfsProcessIdentityV1` separates three kinds of data:
|
||||||
|
|
||||||
|
- `PidfsProcessAuthorityV1 = (boot_id, pidfs_st_dev, pidfs_st_ino)` is the only
|
||||||
|
positive durable host-process authority. The boot ID is stored as the 16 UUID
|
||||||
|
bytes in canonical UUID field order; both pidfs values are unsigned 64-bit
|
||||||
|
types distinct from general filesystem device and inode identities.
|
||||||
|
- `host_pid` is an unsigned 32-bit lookup locator interpreted only in the host
|
||||||
|
PID namespace where Host Coordinator and bootstrap recovery run.
|
||||||
|
- `start_time_ticks` is unsigned 64-bit `/proc/<pid>/stat` negative evidence.
|
||||||
|
Equality of PID and start time adds no authority; inequality or an unreadable
|
||||||
|
required observation refuses.
|
||||||
|
|
||||||
|
A retained pidfd is the primary live authority. Polling, waiting, signaling,
|
||||||
|
and termination act through that exact retained descriptor. Production process
|
||||||
|
control has no numeric-PID signal or reopen-and-signal fallback.
|
||||||
|
|
||||||
|
`CausalChildAcquisitionV1` applies to every Lazaret-owned child that can become
|
||||||
|
durable process ownership. Each Host Coordinator and Runtime Supervisor has one
|
||||||
|
exclusive in-process broker for all spawn, `SIGCHLD`, and reap operations. The
|
||||||
|
process has default non-auto-reaping `SIGCHLD`; no same-parent spawn or reap and
|
||||||
|
no dependency process API bypasses the broker while acquisition is active. The
|
||||||
|
broker performs this sequence:
|
||||||
|
|
||||||
|
1. Persist the owning participant or terminal intent without positive process
|
||||||
|
ownership.
|
||||||
|
2. Hold process-wide spawn/reap exclusion and create an inherited fail-closed
|
||||||
|
launch barrier.
|
||||||
|
3. Spawn a direct repository-owned wrapper that blocks before target work;
|
||||||
|
barrier EOF or malformed release exits without executing the target.
|
||||||
|
4. Capture the host-visible child PID, call `pidfd_open` exactly once, and call
|
||||||
|
`waitid(P_PIDFD, WEXITED | WNOHANG | WNOWAIT)` on that descriptor. `ECHILD`
|
||||||
|
or any other error refuses; `WNOWAIT` never reaps the child.
|
||||||
|
5. `fstat` the same pidfd, read the current boot ID and diagnostic start time,
|
||||||
|
and form `PidfsProcessIdentityV1`.
|
||||||
|
6. Persist `Staged` ownership. An RS-launched child is sent as the exact
|
||||||
|
`StagedProcessOwnership` tuple to HC, and RS waits for an authenticated
|
||||||
|
`ProcessOwnershipCommitAck` that echoes the same Domain, generation,
|
||||||
|
generation-scoped coordinator ID, ownership slot, and complete process
|
||||||
|
identity. Lost, mismatched, unknown-version, unauthenticated, or duplicate
|
||||||
|
after release acknowledgment is never success.
|
||||||
|
7. Release the wrapper barrier only after the exact staged ownership commit is
|
||||||
|
acknowledged. The receiver consumes the acknowledgment once while that
|
||||||
|
exact barrier is pending.
|
||||||
|
8. The wrapper arms its parent-death action, rechecks the expected parent pidfs
|
||||||
|
authority to close the arming race, and execs the foreground,
|
||||||
|
non-daemonizing target in place.
|
||||||
|
9. Promote ownership to `Committed` only after target-specific readiness.
|
||||||
|
Staged ownership cannot satisfy readiness, publication, dependent-resource
|
||||||
|
creation, or adoption. It remains positive authority for exact same-handle
|
||||||
|
cleanup and `PidfsRecoveryMatch`, including bootstrap recovery.
|
||||||
|
|
||||||
|
Every durable host-process slot is exactly `Unassigned`,
|
||||||
|
`Staged(PidfsProcessIdentityV1)`, or `Committed(HostProcessIdentity)`. Only a
|
||||||
|
current pidfs identity may be staged. A committed legacy-unverified identity is
|
||||||
|
retained only for migration and refusal. Current staged and committed identity
|
||||||
|
both authorize exact matched cleanup; only committed current ownership may
|
||||||
|
satisfy an active role, readiness, publication, or adoption.
|
||||||
|
|
||||||
|
A role-bound Participant is first committed as bare `Intent` with no ownership
|
||||||
|
snapshot. Once physical acquisition begins, its role snapshot is present and
|
||||||
|
never returns to absent. Every non-process physical field is optional while
|
||||||
|
acquisition is partial; every process field uses the three-phase slot above.
|
||||||
|
Within Intent, optional fields move only absent-to-present. A newly acquired
|
||||||
|
child slot moves only `Unassigned` to `Staged` to the exact same current
|
||||||
|
identity in `Committed`. An already committed current owner or supervising
|
||||||
|
process may enter a newly created dependent-role snapshot directly, but that
|
||||||
|
recorded identity then remains immutable. Legacy data cannot use this
|
||||||
|
existing-process rule. Recorded evidence cannot be replaced or cleared.
|
||||||
|
The direct existing-process edges are Network RS, Helper RS, Sandbox HC, and
|
||||||
|
Session Terminal HC. State Anchor daemon, Git-routing daemon, Helper process,
|
||||||
|
Sandbox RS, attached runsc, and Session Terminal exec client are acquired child
|
||||||
|
edges and require staging.
|
||||||
|
No bare Intent moves directly to an active state: a non-empty acquisition
|
||||||
|
snapshot is committed first. PreparedLower additionally records
|
||||||
|
`lease_state=Intent` before the catalog insertion changes both participant and
|
||||||
|
lease phase to `LeaseHeld`.
|
||||||
|
|
||||||
|
The active Participant pairs are `prepared_lower=LeaseHeld`,
|
||||||
|
`state_anchor=Prepared`, `git_routing=Prepared`, and
|
||||||
|
`network|helper|sandbox|publication=Committed`. An active snapshot contains all
|
||||||
|
role-required physical evidence and current committed pidfs ownership in every
|
||||||
|
required process slot. `SessionTerminal=Running` likewise requires its
|
||||||
|
container, PTY, guest identity, and current committed HC and exec-client slots.
|
||||||
|
Staged, unassigned, or legacy process ownership cannot satisfy either boundary.
|
||||||
|
Network readiness evidence must also name the enclosing generation and match
|
||||||
|
its policy, fingerprint, namespace, and committed RS identity internally.
|
||||||
|
|
||||||
|
Any partial or active snapshot moves to `ReleaseIntent` while preserving every
|
||||||
|
recorded identity. A staged current identity remains exact cleanup and recovery
|
||||||
|
authority in ReleaseIntent. `ReleaseIntent` becomes `Clean` only after exact or
|
||||||
|
independent absence proof; that transaction clears every process slot to
|
||||||
|
Unassigned and every optional physical field to absent. Clean retains the
|
||||||
|
logical generation key, role, and state as an explicit empty role snapshot. A
|
||||||
|
bare Intent may become that explicit Clean snapshot only after the sealed
|
||||||
|
runtime-absence check. No ordinary lifecycle transition may clear evidence or
|
||||||
|
produce Clean.
|
||||||
|
|
||||||
|
An exited, unreaped child remains pinned while acquisition completes. The
|
||||||
|
descriptor-based child check rejects a foreign replacement, while the exclusive
|
||||||
|
same-parent broker prevents a replacement child from passing the same-parent
|
||||||
|
control case. Implementations preserve these as safety conditions rather than
|
||||||
|
timing assumptions.
|
||||||
|
|
||||||
|
`PidfsRecoveryMatch` is the only positive recovery reacquisition path:
|
||||||
|
|
||||||
|
1. Read and validate the current boot ID.
|
||||||
|
2. On boot mismatch, do not look up the stored PID. Reconcile only resources
|
||||||
|
whose independent exact ownership survives reboot.
|
||||||
|
3. Verify that the stored locator is a host-PID-namespace PID; nested or
|
||||||
|
ambiguous mappings refuse before lookup.
|
||||||
|
4. Call `pidfd_open` exactly once for that locator. `ESRCH` or any other error
|
||||||
|
cannot authorize process action.
|
||||||
|
5. `fstat` the resulting pidfd and compare its device and inode exactly with the
|
||||||
|
durable authority. Either mismatch refuses and closes it without action.
|
||||||
|
6. Apply start-time and expected namespace observations only as negative checks.
|
||||||
|
7. Use that same matched pidfd for every action. An action-time `ESRCH` or
|
||||||
|
observation failure refuses without numeric fallback.
|
||||||
|
|
||||||
|
Domain Store v1 PID/start-only values dispatch to
|
||||||
|
`LegacyUnverifiedProcessIdentityV1`. They cannot be observed, converted, or
|
||||||
|
infallibly migrated into positive authority. They refuse live process action
|
||||||
|
until reboot, independently proven absence, or explicit operator resolution
|
||||||
|
allows the ordinary recovery state machine to reconcile the surrounding record.
|
||||||
|
Boot mismatch fences all old pidfs authority before PID lookup.
|
||||||
|
|
||||||
|
The contract accepts two residual assumptions: Linux boot-ID collision is
|
||||||
|
operationally negligible but not mathematically impossible, and the qualified
|
||||||
|
boot-monotonic 64-bit pidfs inode counter does not wrap during one boot. Codecs
|
||||||
|
and comparisons support `u64::MAX` and refuse every unequal authority tuple;
|
||||||
|
they do not claim to recognize a hypothetical equal tuple after wrap.
|
||||||
|
|
||||||
#### Protocol cryptography refinement
|
#### Protocol cryptography refinement
|
||||||
|
|
||||||
Before starting the Agent Sandbox, the Host Coordinator creates one ephemeral
|
Before starting the Agent Sandbox, the Host Coordinator creates one ephemeral
|
||||||
|
|
@ -291,15 +443,17 @@ connection is already lost, the outcome is not retained for reattachment.
|
||||||
Physical Session Terminal acquisition is ordered as follows:
|
Physical Session Terminal acquisition is ordered as follows:
|
||||||
|
|
||||||
1. The Host Coordinator records `SessionTerminal=Intent` in the Domain Store.
|
1. The Host Coordinator records `SessionTerminal=Intent` in the Domain Store.
|
||||||
2. It allocates the PTY and forks a wrapper with the expected HC pid/start
|
2. It allocates the PTY and starts the exec wrapper through
|
||||||
identity. The wrapper arms its HC parent-death edge, re-reads its parent pid
|
`CausalChildAcquisitionV1` with the expected committed HC pidfs authority.
|
||||||
and start identity, and aborts before invoking runsc if either differs. It may
|
The wrapper arms its HC parent-death edge, rechecks that authority, and aborts
|
||||||
create the guest process only after that recheck succeeds.
|
before invoking runsc if it differs. It may create the guest process only
|
||||||
|
after staged exec-client ownership is durably committed and the barrier is
|
||||||
|
released.
|
||||||
3. It establishes the guest process group and obtains the guest leader PID,
|
3. It establishes the guest process group and obtains the guest leader PID,
|
||||||
start time, and group ID.
|
start time, and group ID.
|
||||||
4. One Domain Store transaction records the exact Host Coordinator, PTY, exec
|
4. One Domain Store transaction promotes the exact Host Coordinator and exec
|
||||||
client, container, guest leader, and guest process-group identities and sets
|
client ownership to committed, records the PTY, container, guest leader, and
|
||||||
`SessionTerminal=Running`.
|
guest process-group identities, and sets `SessionTerminal=Running`.
|
||||||
5. Only then may the Host Coordinator accept terminal events for the session.
|
5. Only then may the Host Coordinator accept terminal events for the session.
|
||||||
|
|
||||||
Failure before `Running` never admits the terminal. A live Host Coordinator uses
|
Failure before `Running` never admits the terminal. A live Host Coordinator uses
|
||||||
|
|
@ -418,11 +572,12 @@ defined by the protocol model. The product store encodes that generation documen
|
||||||
in typed tables under a schema/db version (unknown newer versions fail closed
|
in typed tables under a schema/db version (unknown newer versions fail closed
|
||||||
until an explicit Host Coordinator migration), including generation identity,
|
until an explicit Host Coordinator migration), including generation identity,
|
||||||
minted runtime component IDs, and participant ownership material (for example
|
minted runtime component IDs, and participant ownership material (for example
|
||||||
prepared-artifact ID, pid/start, mount identity, network readiness fingerprint).
|
prepared-artifact ID, staged or committed pidfs process ownership, mount
|
||||||
|
identity, and network readiness fingerprint).
|
||||||
Participant roles are `prepared_lower`, `state_anchor`, `git_routing`, `network`,
|
Participant roles are `prepared_lower`, `state_anchor`, `git_routing`, `network`,
|
||||||
`helper`, `sandbox`, and `publication`. The `helper` participant independently
|
`helper`, `sandbox`, and `publication`. The `helper` participant independently
|
||||||
records durable intent and exact connectivity-helper pid/start identity before
|
records durable intent and exact committed connectivity-helper pidfs ownership
|
||||||
the `network` participant records readiness. Sessions and the generation mailbox
|
before the `network` participant records readiness. Sessions and the generation mailbox
|
||||||
are separate generation records. Deterministic paths are lookup hints for
|
are separate generation records. Deterministic paths are lookup hints for
|
||||||
physical recovery, not ownership proofs.
|
physical recovery, not ownership proofs.
|
||||||
|
|
||||||
|
|
@ -492,9 +647,11 @@ bootstrap recovery may advance it to release intent. These rules make every
|
||||||
interrupted release conservative and prevent a mounted State Anchor from
|
interrupted release conservative and prevent a mounted State Anchor from
|
||||||
becoming unleased.
|
becoming unleased.
|
||||||
|
|
||||||
Store version acceptance occurs before physical resource inspection. The first
|
Store version acceptance occurs before physical resource inspection. The
|
||||||
product version has no product predecessor. Future migration steps create and
|
Domain Store v1-to-v2 migration preserves every PID/start-only value as
|
||||||
validate destination rows in one redb transaction and update the version last.
|
legacy-unverified and never observes it into pidfs authority. Migration steps
|
||||||
|
create and validate destination rows in one redb transaction and update the
|
||||||
|
version last. Future versions follow the same fail-closed ordering.
|
||||||
|
|
||||||
Files not represented in the upper layer follow the selected base image. Files,
|
Files not represented in the upper layer follow the selected base image. Files,
|
||||||
directories, and whiteouts recorded in the upper layer remain authoritative when
|
directories, and whiteouts recorded in the upper layer remain authoritative when
|
||||||
|
|
@ -530,9 +687,10 @@ inherited memfd. The Runtime Supervisor verifies the seals and digest, installs
|
||||||
the policy, and returns observed route, helper, process, and namespace evidence
|
the policy, and returns observed route, helper, process, and namespace evidence
|
||||||
over an inherited Host Coordinator-created private `SOCK_SEQPACKET` channel.
|
over an inherited Host Coordinator-created private `SOCK_SEQPACKET` channel.
|
||||||
Before helper launch, the Host Coordinator commits the independent `helper`
|
Before helper launch, the Host Coordinator commits the independent `helper`
|
||||||
participant as `Intent`. After launch it verifies and commits the exact helper
|
participant as `Intent`. It acquires the helper through
|
||||||
and supervising Runtime Supervisor pid/start identities before accepting network
|
`CausalChildAcquisitionV1`; after the exact helper and supervising Runtime
|
||||||
readiness evidence and advancing the `network` participant.
|
Supervisor pidfs ownership is committed, it may accept network readiness
|
||||||
|
evidence and advance the `network` participant.
|
||||||
The Host Coordinator independently reads the Runtime Supervisor's namespace
|
The Host Coordinator independently reads the Runtime Supervisor's namespace
|
||||||
identity and persists the attestation, fingerprint, and generation-bound
|
identity and persists the attestation, fingerprint, and generation-bound
|
||||||
deny/permit/DNS probe result. Persisted readiness files are evidence after this
|
deny/permit/DNS probe result. Persisted readiness files are evidence after this
|
||||||
|
|
@ -648,13 +806,13 @@ not duplicated here.
|
||||||
| **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-3** | Tracked and non-ignored 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 and Ignored Workspace State 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-7** | Product recovery shall inspect external runtime resources independently of owner-process liveness and compare typed pid/start, namespace, mount, artifact, path, and inode evidence before physical cleanup. |
|
| **FR-7** | Product recovery shall inspect external runtime resources independently of owner-process liveness. Host-process action shall require the retained pidfd or `PidfsRecoveryMatch` against staged or committed current `PidfsProcessIdentityV1`; legacy PID/start-only ownership, unequal pidfs authority, boot or namespace ambiguity, and unreadable negative evidence shall refuse without numeric-PID signaling. Staged ownership authorizes only exact cleanup/recovery, never service or adoption. Recovery shall also compare typed namespace, mount, artifact, path, and inode evidence before physical cleanup. |
|
||||||
| **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. |
|
| **FR-9** | Trusted host configuration shall allow the developer to disable Git-ignored path isolation for an Agent Profile. |
|
||||||
| **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. |
|
| **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. |
|
||||||
| **FR-14** | The Host Coordinator shall own one host PTY master per interactive Client Session; the PTY slave shall attach through non-detached `runsc exec` standard streams. The root attached `runsc run` client shall remain terminal-neutral. Production Session PTYs shall not depend on `runsc exec -console-socket`. |
|
| **FR-14** | The Host Coordinator shall own one host PTY master per interactive Client Session; the PTY slave shall attach through non-detached `runsc exec` standard streams. The root attached `runsc run` client shall remain terminal-neutral. Production Session PTYs shall not depend on `runsc exec -console-socket`. |
|
||||||
| **FR-15** | For each interactive Client Session, Lazaret shall preserve the Session Terminal record and protocol in the normative design schema; serialize bounded terminal events and merged output by Session ID; map resize and the `Interrupt`, `Quit`, and `Suspend` events to the specified exact process-group operations; collect exit status from the non-detached `runsc exec` client; and follow the specified release and hard-loss recovery ordering without reattachment or mismatched cleanup. |
|
| **FR-15** | For each interactive Client Session, Lazaret shall preserve the Session Terminal record and protocol in the normative design schema; serialize bounded terminal events and merged output by Session ID; map resize and the `Interrupt`, `Quit`, and `Suspend` events to the specified exact process-group operations; collect exit status from the non-detached `runsc exec` client; and follow the specified release and hard-loss recovery ordering without reattachment or mismatched cleanup. |
|
||||||
| **FR-16** | Domain network connectivity shall use `slirp4netns` as the production connectivity helper. The Domain Store shall record independent `helper` intent before launch and exact helper and supervising Runtime Supervisor pid/start identities before network readiness. |
|
| **FR-16** | Domain network connectivity shall use `slirp4netns` as the production connectivity helper. The Domain Store shall record independent `helper` intent before launch and exact committed helper and supervising Runtime Supervisor pidfs ownership before network readiness. |
|
||||||
| **FR-17** | The Domain Store shall be opened only by the Host Coordinator process holding the Domain `flock`; mutations shall use whole redb transactions. Storage Catalog opens shall be bounded by `storage-catalog.lock` and one transaction. Cross-store lease release shall follow the durable release-intent, exact-unmount, catalog-delete, Domain-clean ordering defined by the durable generation refinement. |
|
| **FR-17** | The Domain Store shall be opened only by the Host Coordinator process holding the Domain `flock`; mutations shall use whole redb transactions. Storage Catalog opens shall be bounded by `storage-catalog.lock` and one transaction. Cross-store lease release shall follow the durable release-intent, exact-unmount, catalog-delete, Domain-clean ordering defined by the durable generation refinement. |
|
||||||
| **FR-18** | Product network-readiness evidence shall conform to the normative design schema and include the readiness fingerprint plus the generation-bound deny/permit/DNS probe result. |
|
| **FR-18** | Product network-readiness evidence shall conform to the normative design schema and include the readiness fingerprint plus the generation-bound deny/permit/DNS probe result. |
|
||||||
| **FR-19** | The Host Coordinator shall derive generation-bound network policy from trusted host configuration; the Runtime Supervisor shall install only that derived policy and attest readiness by observed namespace state. Durable network participant state shall record the network readiness fingerprint. |
|
| **FR-19** | The Host Coordinator shall derive generation-bound network policy from trusted host configuration; the Runtime Supervisor shall install only that derived policy and attest readiness by observed namespace state. Durable network participant state shall record the network readiness fingerprint. |
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue