# Lazaret schemas This document is the normative design schema for Lazaret records and protocol data shapes. The [specification](specification.md) defines their physical use, while the TLA+ models define lifecycle and ordering above the implementation abstraction. The diagrams define logical fields, variants, and relationships; they do not select a programming-language representation. ## Domain Store ```mermaid classDiagram class DomainStore class StoreMetadata { schema_id schema_version } class Domain { domain_id profile_name canonical_workspace } class Generation { domain_id generation_id configuration_fingerprint coordinator_id network_sidecar_id sandbox_id durable_decision } class Participant { generation_id role state ownership } class ParticipantState { <> Intent LeaseHeld Prepared Committed ReleaseIntent Clean } class ParticipantOwnership { <> PreparedLower StateAnchor GitRouting Network Helper Sandbox Publication } class Session { generation_id session_id cli_id session_anchor_id session_phase } class SessionTerminal { generation_id session_id state container_id } class SessionTerminalState { <> Intent Running ReleaseIntent Clean } class Mailbox { generation_id identity_path inbox_path outbox_path state } DomainStore "1" *-- "1" StoreMetadata DomainStore "1" *-- "1" Domain Domain "1" --> "0..*" Generation Generation "1" --> "0..*" Participant Participant --> ParticipantState Participant --> "0..1" ParticipantOwnership Generation "1" --> "0..*" Session Session "1" --> "0..1" SessionTerminal SessionTerminal --> SessionTerminalState Generation "1" --> "0..1" Mailbox Mailbox --> ParticipantState ``` `durable_decision` and `session_phase` contain values defined by the application protocol model. This schema records them without redefining either state machine. 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 { <> PidfsV1 LegacyUnverifiedV1 } class PersistedHostProcessOwnership { <> Unassigned Staged Committed } class HostProcessOwnershipSlot { <> SandboxHostCoordinator SandboxRuntimeSupervisor SandboxAttachedRunsc StateAnchorDaemon GitRoutingDaemon NetworkRuntimeSupervisor HelperConnectivityHelper HelperRuntimeSupervisor SessionTerminalHostCoordinator SessionTerminalExecClient EscalationHostProcess } class ProcessOwnershipMessageVersion { <> 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 ```mermaid classDiagram class SessionTerminal { generation_id session_id state container_id } class PtyIdentity { master_fd device inode } class GuestProcessIdentity { leader_pid leader_start_time process_group_id } SessionTerminal --> PersistedHostProcessOwnership : host_coordinator SessionTerminal --> PtyIdentity : pty_master SessionTerminal --> PersistedHostProcessOwnership : exec_client SessionTerminal --> GuestProcessIdentity : guest_process ``` Host process ownership uses the phase-aware pidfs dispatch above. Guest process identity uses the leader PID and start time observed inside the exact generation-bound runsc container; its process-group ID is relative to that 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 identity. `SessionTerminalState` is a physical recovery state below the 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 ```mermaid classDiagram class SessionTerminalEvent { <> } class TerminalInput { session_id bytes } class TerminalResize { session_id rows columns pixel_width pixel_height } class TerminalSignal { session_id signal } class TerminalSignalKind { <> Interrupt Quit Suspend } class SessionClose { session_id } class SessionTerminalMessage { <> } class TerminalOutput { session_id bytes } class SessionExit { session_id outcome exit_code } class SessionExitOutcome { <> Exited Terminated RuntimeLost } SessionTerminalEvent --> TerminalInput SessionTerminalEvent --> TerminalResize SessionTerminalEvent --> TerminalSignal SessionTerminalEvent --> SessionClose TerminalSignal --> TerminalSignalKind SessionTerminalMessage --> TerminalOutput SessionTerminalMessage --> SessionExit SessionExit --> SessionExitOutcome ``` `exit_code` is present only for `Exited` and is the status collected from the non-detached runsc exec client. Interactive PTY output is one merged byte stream; it does not preserve stdout and stderr as separate channels. ## Protocol cryptography ```mermaid classDiagram class GenerationIdentity { agent_profile domain_id generation_id configuration_fingerprint coordinator_id sandbox_id } class TrustManifest { protocol_version generation_identity tls_ca_sha256 protocol_issuer_public_key issuer_signature } class TlsCredential { generation_identity role subject_id certificate_der certificate_sha256 } class ProtocolPublicationIdentity { protocol_version generation_identity tls_ca_certificate_der trust_manifest_sha256 coordinator_tls_certificate_sha256 } class SandboxCredentialBundle { protocol_version generation_identity trust_manifest coordinator_protocol_credential sandbox_protocol_credential sandbox_private_key } class TlsRole { <> SessionCoordinator ClientCLI } class ProtocolCredential { protocol_version credential_id generation_identity role subject_id public_key issuer_signature } class ProtocolCredentialRole { <> SessionCoordinator AgentSandbox } class SessionBinding { generation_identity cli_id session_id session_anchor_public_key } class SessionRequestCapability { protocol_version capability_id session_binding coordinator_credential_id sandbox_credential_id coordinator_signature } TrustManifest --> GenerationIdentity TlsCredential --> TlsRole TlsCredential --> GenerationIdentity ProtocolPublicationIdentity --> GenerationIdentity ProtocolPublicationIdentity --> TrustManifest ProtocolPublicationIdentity --> TlsCredential : coordinator TLS leaf SandboxCredentialBundle --> GenerationIdentity SandboxCredentialBundle --> TrustManifest SandboxCredentialBundle --> ProtocolCredential : coordinator verifier SandboxCredentialBundle --> ProtocolCredential : sandbox signer ProtocolCredential --> GenerationIdentity ProtocolCredential --> ProtocolCredentialRole SessionBinding --> GenerationIdentity SessionRequestCapability --> SessionBinding SessionRequestCapability --> ProtocolCredential : coordinator signer SessionRequestCapability --> ProtocolCredential : sandbox subject ``` Trust Manifests, credentials, capabilities, and private keys are ephemeral protocol material, not Domain Store records. ```mermaid classDiagram class SignedProtocolRecord { <> payload_bytes signature } class MailboxRequest { protocol_version session_binding request_id session_capability sandbox_credential_id command_argv justification } class MailboxCancellation { protocol_version session_binding request_id session_capability sandbox_credential_id } class MailboxResultAck { protocol_version session_binding request_id request_sha256 session_capability sandbox_credential_id highest_contiguous_sequence } class DeveloperDecision { protocol_version session_binding request_id request_sha256 decision approved_command_argv } class DeveloperDecisionKind { <> Approve Deny } class MailboxResult { protocol_version session_binding request_id request_sha256 coordinator_credential_id outcome exit_code chunk_count stdout_sha256 stderr_sha256 } class MailboxResultChunk { protocol_version session_binding request_id request_sha256 coordinator_credential_id sequence stream bytes } class ResultStream { <> Stdout Stderr } class EscalationOutcome { <> Completed Denied TimedOut Cancelled Failed } SignedProtocolRecord --> MailboxRequest SignedProtocolRecord --> MailboxCancellation SignedProtocolRecord --> MailboxResultAck SignedProtocolRecord --> DeveloperDecision SignedProtocolRecord --> MailboxResult SignedProtocolRecord --> MailboxResultChunk MailboxRequest --> SessionBinding MailboxRequest --> SessionRequestCapability MailboxCancellation --> SessionBinding MailboxCancellation --> SessionRequestCapability MailboxResultAck --> SessionBinding MailboxResultAck --> SessionRequestCapability DeveloperDecision --> SessionBinding DeveloperDecision --> DeveloperDecisionKind MailboxResult --> SessionBinding MailboxResult --> EscalationOutcome MailboxResultChunk --> SessionBinding MailboxResultChunk --> ResultStream ``` `SessionCoordinator` is the logical protocol identity served physically by the Host Coordinator. It is not a distinct Host Coordinator principal. Physical encoding, signing, size, and validation rules are defined by the specification. ### Participant ownership ```mermaid classDiagram class ParticipantOwnership { <> } class NamespaceIdentity { device inode } class MountIdentity { mount_id device inode } class PreparedLowerOwnership { optional prepared_artifact_id optional source_identity optional lease_state optional path optional device optional inode } class StateAnchorOwnership { optional lower_artifact_id optional upper_path optional work_path optional merged_path daemon optional mount } class GitRoutingOwnership { optional mountpoint optional host_root optional isolated_root daemon optional mount } class NetworkOwnership { optional derived_policy_digest optional readiness_fingerprint optional readiness_evidence optional namespace runtime_supervisor } class HelperOwnership { optional network_sidecar_id connectivity_helper runtime_supervisor } class SandboxOwnership { optional container_id optional runtime_root optional bundle_digest host_coordinator runtime_supervisor attached_runsc } class PublicationOwnership { optional socket_path optional identity_path } ParticipantOwnership --> PreparedLowerOwnership ParticipantOwnership --> StateAnchorOwnership ParticipantOwnership --> GitRoutingOwnership ParticipantOwnership --> NetworkOwnership ParticipantOwnership --> HelperOwnership ParticipantOwnership --> SandboxOwnership ParticipantOwnership --> PublicationOwnership StateAnchorOwnership --> PersistedHostProcessOwnership : daemon StateAnchorOwnership --> MountIdentity : mount GitRoutingOwnership --> PersistedHostProcessOwnership : daemon GitRoutingOwnership --> MountIdentity : mount NetworkOwnership --> NamespaceIdentity : namespace NetworkOwnership --> PersistedHostProcessOwnership : runtime_supervisor NetworkOwnership --> NetworkReadinessEvidence : readiness_evidence HelperOwnership --> PersistedHostProcessOwnership : connectivity_helper HelperOwnership --> PersistedHostProcessOwnership : runtime_supervisor SandboxOwnership --> PersistedHostProcessOwnership : host_coordinator SandboxOwnership --> PersistedHostProcessOwnership : runtime_supervisor 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 ```mermaid classDiagram class StorageCatalog class StoreMetadata { schema_id schema_version } class PreparedArtifact { prepared_artifact_id source_identity selected_manifest_digest policy_id id_map_digest path device inode state } class ArtifactState { <> Available Reclaiming } class ArtifactLease { prepared_artifact_id domain_id generation_id } class PrepareOperation { operation_id expected_artifact_id source_identity stage_path device inode } StorageCatalog "1" *-- "1" StoreMetadata StorageCatalog "1" *-- "0..*" PreparedArtifact StorageCatalog "1" *-- "0..*" ArtifactLease StorageCatalog "1" *-- "0..*" PrepareOperation PreparedArtifact --> ArtifactState PreparedArtifact "1" --> "0..*" ArtifactLease PrepareOperation "0..*" --> "0..1" PreparedArtifact : publishes ``` An artifact lease identifies one prepared artifact, Domain, and generation. Its Domain and generation fields correspond to a Domain Store generation; the stores do not form one transactional database. `ReleaseIntent` is a physical recovery state. It does not redefine the modeled lease lifecycle: the specification defines how Domain Store release intent, physical unmount, and Storage Catalog lease removal refine model unmount and release actions. After Host Coordinator loss, the exact Runtime Supervisor may unmount under the retained Prepared ownership record, but the Artifact Lease remains until bootstrap Host Coordinator recovery records release intent. ## Derived network policy ```mermaid classDiagram class DerivedNetworkPolicy { schema_id address_families default_actions prefix_rules resolvers } class AddressFamilyDisposition { family disposition } class DefaultAction { family action } class PrefixRule { family network action } class Resolver { family address } class AddressFamily { <> IPv4 IPv6 } class PolicyAction { <> Allow Deny } DerivedNetworkPolicy "1" *-- "2" AddressFamilyDisposition DerivedNetworkPolicy "1" *-- "1..2" DefaultAction DerivedNetworkPolicy "1" *-- "0..*" PrefixRule DerivedNetworkPolicy "1" *-- "1..*" Resolver AddressFamilyDisposition --> AddressFamily DefaultAction --> AddressFamily DefaultAction --> PolicyAction PrefixRule --> AddressFamily PrefixRule --> PolicyAction Resolver --> AddressFamily ``` The policy has exactly one disposition for each address family. Each enabled family has exactly one default action. Prefixes and addresses are canonical, and equal-prefix action conflicts are invalid. Policy identity is the SHA-256 digest of its canonical form with a schema-specific domain separator. ## Network readiness ```mermaid classDiagram class NetworkReadinessEvidence { domain_id generation_id network_sidecar_id readiness_fingerprint } class NetworkReadinessAttestation { schema_id domain_id generation_id configuration_fingerprint network_sidecar_id derived_policy_digest enforcement_namespace runtime_supervisor connectivity_helper observation } class NetworkObservation { policy_delivery helper_ready observed_routes routes_attested } class PolicyDelivery { <> SealedMemfd } class NetworkReadinessIdentity { schema_id domain_id generation_id configuration_fingerprint network_sidecar_id derived_policy_digest enforcement_namespace } class NamespaceIdentity { device inode } class NetworkReachabilityProbe { domain_id generation_id enforcement_namespace } class DeniedDestinationObservation { destination unreachable } class PermittedDestinationObservation { destination reachable } class DnsObservation { resolver query_name resolved_addresses } NetworkReadinessEvidence --> NetworkReadinessAttestation : accepts NetworkReadinessEvidence --> NetworkReadinessIdentity : fingerprints NetworkReadinessEvidence --> NetworkReachabilityProbe : includes NetworkReadinessAttestation --> NamespaceIdentity : enforcement_namespace NetworkReadinessAttestation --> PidfsProcessIdentityV1 : runtime_supervisor NetworkReadinessAttestation --> PidfsProcessIdentityV1 : connectivity_helper NetworkReadinessAttestation --> NetworkObservation NetworkObservation --> PolicyDelivery NetworkReadinessIdentity --> NamespaceIdentity : enforcement_namespace NetworkReadinessAttestation ..> NetworkReadinessIdentity : projects NetworkReachabilityProbe --> NamespaceIdentity : enforcement_namespace NetworkReachabilityProbe "1" *-- "1..*" DeniedDestinationObservation NetworkReachabilityProbe "1" *-- "1..*" PermittedDestinationObservation NetworkReachabilityProbe "1" *-- "1..*" DnsObservation ``` The readiness fingerprint is the SHA-256 digest of the canonical readiness identity with a schema-specific domain separator. Process identities and network observations are attestation evidence but are not part of that fingerprint. The Host Coordinator binds the accepted attestation and generation-bound reachability probe into Network Readiness Evidence before publication. `NetworkReadinessAttestation.schema_id` is exactly `lazaret-network-attestation-v1`. `NetworkReadinessIdentity.schema_id` is exactly `lazaret-network-readiness-identity-v1`. They are independent version-dispatch domains: the v1 attestation projects only to the v1 readiness identity, and any other identifier or version is refused without fallback or cross-domain interpretation. The readiness-fingerprint domain separator is the UTF-8 bytes of `lazaret-network-readiness-identity-v1` followed by exactly one zero byte. This fixes the domain-separation context only; it does not select the byte encoding of the canonical readiness-identity fields.