lazaret/docs/schema.md

19 KiB

Lazaret schemas

This document is the normative design schema for Lazaret records and protocol data shapes. The specification 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

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 {
        <<enumeration>>
        Intent
        LeaseHeld
        Prepared
        Committed
        ReleaseIntent
        Clean
    }

    class ParticipantOwnership {
        <<union>>
        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 {
        <<enumeration>>
        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 --> 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.

Session terminal ownership

classDiagram
    class SessionTerminal {
        generation_id
        session_id
        state
        container_id
    }

    class PtyIdentity {
        master_fd
        device
        inode
    }

    class ProcessIdentity {
        pid
        start_time
    }

    class GuestProcessIdentity {
        leader_pid
        leader_start_time
        process_group_id
    }

    SessionTerminal --> ProcessIdentity : host_coordinator
    SessionTerminal --> PtyIdentity : pty_master
    SessionTerminal --> ProcessIdentity : exec_client
    SessionTerminal --> GuestProcessIdentity : guest_process

Host process identities use host pid and start time. 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. 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.

Session terminal protocol

classDiagram
    class SessionTerminalEvent {
        <<union>>
    }

    class TerminalInput {
        session_id
        bytes
    }

    class TerminalResize {
        session_id
        rows
        columns
        pixel_width
        pixel_height
    }

    class TerminalSignal {
        session_id
        signal
    }

    class TerminalSignalKind {
        <<enumeration>>
        Interrupt
        Quit
        Suspend
    }

    class SessionClose {
        session_id
    }

    class SessionTerminalMessage {
        <<union>>
    }

    class TerminalOutput {
        session_id
        bytes
    }

    class SessionExit {
        session_id
        outcome
        exit_code
    }

    class SessionExitOutcome {
        <<enumeration>>
        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

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 {
        <<enumeration>>
        SessionCoordinator
        ClientCLI
    }

    class ProtocolCredential {
        protocol_version
        credential_id
        generation_identity
        role
        subject_id
        public_key
        issuer_signature
    }

    class ProtocolCredentialRole {
        <<enumeration>>
        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.

classDiagram
    class SignedProtocolRecord {
        <<union>>
        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 {
        <<enumeration>>
        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 {
        <<enumeration>>
        Stdout
        Stderr
    }

    class EscalationOutcome {
        <<enumeration>>
        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

classDiagram
    class ParticipantOwnership {
        <<union>>
    }

    class ProcessIdentity {
        pid
        start_time
    }

    class NamespaceIdentity {
        device
        inode
    }

    class MountIdentity {
        mount_id
        device
        inode
    }

    class PreparedLowerOwnership {
        prepared_artifact_id
        source_identity
        lease_state
        path
        device
        inode
    }

    class StateAnchorOwnership {
        lower_artifact_id
        upper_path
        work_path
        merged_path
        daemon
        mount
    }

    class GitRoutingOwnership {
        mountpoint
        host_root
        isolated_root
        daemon
        mount
    }

    class NetworkOwnership {
        derived_policy_digest
        readiness_fingerprint
        readiness_evidence
        namespace
        runtime_supervisor
    }

    class HelperOwnership {
        network_sidecar_id
        connectivity_helper
        runtime_supervisor
    }

    class SandboxOwnership {
        container_id
        runtime_root
        bundle_digest
        host_coordinator
        runtime_supervisor
        attached_runsc
    }

    class PublicationOwnership {
        socket_path
        identity_path
    }

    ParticipantOwnership --> PreparedLowerOwnership
    ParticipantOwnership --> StateAnchorOwnership
    ParticipantOwnership --> GitRoutingOwnership
    ParticipantOwnership --> NetworkOwnership
    ParticipantOwnership --> HelperOwnership
    ParticipantOwnership --> SandboxOwnership
    ParticipantOwnership --> PublicationOwnership
    StateAnchorOwnership --> ProcessIdentity : daemon
    StateAnchorOwnership --> MountIdentity : mount
    GitRoutingOwnership --> ProcessIdentity : daemon
    GitRoutingOwnership --> MountIdentity : mount
    NetworkOwnership --> NamespaceIdentity : namespace
    NetworkOwnership --> ProcessIdentity : runtime_supervisor
    NetworkOwnership --> NetworkReadinessEvidence : readiness_evidence
    HelperOwnership --> ProcessIdentity : connectivity_helper
    HelperOwnership --> ProcessIdentity : runtime_supervisor
    SandboxOwnership --> ProcessIdentity : host_coordinator
    SandboxOwnership --> ProcessIdentity : runtime_supervisor
    SandboxOwnership --> ProcessIdentity : attached_runsc

Storage Catalog

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 {
        <<enumeration>>
        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

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 {
        <<enumeration>>
        IPv4
        IPv6
    }

    class PolicyAction {
        <<enumeration>>
        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

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 {
        <<enumeration>>
        SealedMemfd
    }

    class NetworkReadinessIdentity {
        schema_id
        domain_id
        generation_id
        configuration_fingerprint
        network_sidecar_id
        derived_policy_digest
        enforcement_namespace
    }

    class NamespaceIdentity {
        device
        inode
    }

    class ProcessIdentity {
        pid
        start_time
    }

    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 --> ProcessIdentity : runtime_supervisor
    NetworkReadinessAttestation --> ProcessIdentity : 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.