Model terminal interaction authority and bounded verification

This commit is contained in:
ms2k 2026-08-02 14:02:32 +09:00
commit 6d3a2cfe16
7 changed files with 785 additions and 14 deletions

View file

@ -47,6 +47,11 @@ CONSTANTS
AutoRuleMatches <- AutoRuleMatchesFixture AutoRuleMatches <- AutoRuleMatchesFixture
CommandRequiresShell <- CommandRequiresShellFixture CommandRequiresShell <- CommandRequiresShellFixture
CommandContentSafe <- CommandContentSafeFixture CommandContentSafe <- CommandContentSafeFixture
TerminalEventPlan <- TerminalNoEventPlanFixture
TerminalAuthorityPlan <- TerminalNoAuthorityPlanFixture
TerminalAdministrativeAuthorityValid <-
TerminalNoAdministrativeAuthorityValidFixture
TerminalRoutingFault = "Correct"
SPECIFICATION Spec SPECIFICATION Spec
\* Fully closed application states intentionally have no successor. \* Fully closed application states intentionally have no successor.
@ -86,6 +91,8 @@ INVARIANTS
HostExecutionAuthorized HostExecutionAuthorized
AutoApprovalNoShell AutoApprovalNoShell
TerminalIsolation TerminalIsolation
TerminalInteractionAuthoritySeparated
LocalTerminationAvailableWhileTransportBlocked
NoOrphanHostProcess NoOrphanHostProcess
StateAnchorOwnership StateAnchorOwnership
FailedDomainIsolation FailedDomainIsolation

View file

@ -26,8 +26,11 @@ multi-process presence predicates and generation-scoped genCoordinatorId.
Durable participant intent is multi-role; networkReady gates publication; Durable participant intent is multi-role; networkReady gates publication;
preparedLowerLeased and helperProc refine physical ownership under RS. preparedLowerLeased and helperProc refine physical ownership under RS.
Cryptographic algorithms, serialization, container commands, filesystems, typed Cryptographic algorithms, serialization, container commands, filesystems, typed
redb tables, PTY descriptor identities, process identifiers, signal mechanics, redb tables, PTY descriptor identities, process identifiers, terminal byte and
and network packet handling remain refinement boundaries. signal mechanics, and network packet handling remain refinement boundaries.
The model does distinguish keyboard PTY delivery, explicit guest-group signals,
local Client controls, and terminal metadata because those carry different
authorities.
***************************************************************************) ***************************************************************************)
CONSTANTS CONSTANTS
@ -69,6 +72,10 @@ CONSTANTS
AutoRuleMatches, AutoRuleMatches,
CommandRequiresShell, CommandRequiresShell,
CommandContentSafe, CommandContentSafe,
TerminalEventPlan,
TerminalAuthorityPlan,
TerminalAdministrativeAuthorityValid,
TerminalRoutingFault,
D1, D1,
S1, S1,
S2, S2,
@ -138,6 +145,24 @@ SessionPhases == {
} }
TerminalRoutes == {"Detached", "Sandbox", "Prompt", "HostProcess"} TerminalRoutes == {"Detached", "Sandbox", "Prompt", "HostProcess"}
SessionTerminalStates == {"Clean", "Intent", "Running", "ReleaseIntent"} SessionTerminalStates == {"Clean", "Intent", "Running", "ReleaseIntent"}
TerminalEventKinds == {
"Keyboard", "ExplicitSignal", "Resize", "Break", "LocalSuspend",
"LocalResume", "LocalDisconnect", "HostSignal"
}
TerminalAuthorities == {
"None", "Client", "Administrator", "LocalClient", "HostProcess"
}
TerminalDispositions == {"None", "Delivered", "Refused"}
TerminalTargets == {
"None", "GuestPtyForeground", "RecordedGuestGroup", "LocalClient",
"ExactSessionPty"
}
TerminalRoutingFaults == {
"Correct", "KeyboardRecordedGroup", "KeyboardDuplicated",
"CrossSession", "ClientAdministrativeSignal",
"BlockedTransportPreventsLocalControl"
}
TerminalTransportStates == {"Ready", "Blocked"}
ConnectionStates == {"Disconnected", "Connected"} ConnectionStates == {"Disconnected", "Connected"}
RequestPhases == { RequestPhases == {
"Unused", "Verified", "AwaitingApproval", "Approved", "Executing", "Unused", "Verified", "AwaitingApproval", "Approved", "Executing",
@ -185,6 +210,48 @@ EmptyDurableIntent == [role \in DurableRoles |-> FALSE]
SessionAnchorsFixture == {"anchor-1", "anchor-2"} SessionAnchorsFixture == {"anchor-1", "anchor-2"}
SessionDomainFixture == (S1 :> D1) @@ (S2 :> D1) SessionDomainFixture == (S1 :> D1) @@ (S2 :> D1)
TerminalEventPlanFixture ==
(S1 :> <<
"Keyboard", "Resize", "LocalSuspend", "LocalResume",
"LocalDisconnect"
>>) @@
(S2 :> <<
"ExplicitSignal", "ExplicitSignal", "Break", "Keyboard", "HostSignal"
>>)
TerminalAuthorityPlanFixture ==
(S1 :> <<
"Client", "Client", "LocalClient", "LocalClient", "LocalClient"
>>) @@
(S2 :> <<
"Client", "Administrator", "LocalClient", "Client", "HostProcess"
>>)
TerminalAdministrativeAuthorityValidFixture ==
(S1 :> <<FALSE, FALSE, FALSE, FALSE, FALSE>>) @@
(S2 :> <<FALSE, TRUE, FALSE, FALSE, FALSE>>)
TerminalNoEventPlanFixture ==
(S1 :> <<>>) @@ (S2 :> <<>>)
TerminalNoAuthorityPlanFixture ==
(S1 :> <<>>) @@ (S2 :> <<>>)
TerminalNoAdministrativeAuthorityValidFixture ==
(S1 :> <<>>) @@ (S2 :> <<>>)
TerminalRoutingEventPlanFixture ==
(S1 :> <<"Keyboard", "Resize">>) @@
(S2 :> <<"ExplicitSignal", "ExplicitSignal">>)
TerminalRoutingAuthorityPlanFixture ==
(S1 :> <<"Client", "Client">>) @@
(S2 :> <<"Client", "Administrator">>)
TerminalRoutingAdministrativeAuthorityValidFixture ==
(S1 :> <<FALSE, FALSE>>) @@
(S2 :> <<FALSE, TRUE>>)
TerminalLocalControlEventPlanFixture ==
(S1 :> <<"LocalSuspend", "LocalResume", "LocalDisconnect">>) @@
(S2 :> <<"Break", "HostSignal">>)
TerminalLocalControlAuthorityPlanFixture ==
(S1 :> <<"LocalClient", "LocalClient", "LocalClient">>) @@
(S2 :> <<"LocalClient", "HostProcess">>)
TerminalLocalControlAdministrativeAuthorityValidFixture ==
(S1 :> <<FALSE, FALSE, FALSE>>) @@
(S2 :> <<FALSE, FALSE>>)
SessionFingerprintFixture == SessionFingerprintFixture ==
(S1 :> "config-a") @@ (S2 :> "config-a") (S1 :> "config-a") @@ (S2 :> "config-a")
DomainFingerprintFixture == D1 :> "config-a" DomainFingerprintFixture == D1 :> "config-a"
@ -279,7 +346,22 @@ VARIABLES
sessionTerminalState, sessionTerminalState,
sessionPtyOpen, sessionPtyOpen,
sessionExecPresent, sessionExecPresent,
sessionGuestKnown sessionGuestKnown,
terminalEventIndex,
terminalLastEventId,
terminalLastEvent,
terminalLastSourceSession,
terminalLastAuthority,
terminalLastAdministrativeAuthorityValid,
terminalLastDisposition,
terminalLastDestination,
terminalLastTarget,
terminalLastByteDeliveries,
terminalLastSignalDeliveries,
terminalLastLocalDeliveries,
terminalLastMetadataDeliveries,
sessionClientSuspended,
sessionTerminalTransportState
baseVars == << baseVars == <<
domainPhase, domainPhase,
@ -318,13 +400,35 @@ baseVars == <<
helperProc helperProc
>> >>
terminalVars == << terminalLifecycleVars == <<
sessionTerminalState, sessionTerminalState,
sessionPtyOpen, sessionPtyOpen,
sessionExecPresent, sessionExecPresent,
sessionGuestKnown sessionGuestKnown
>> >>
terminalEventRecordVars == <<
terminalEventIndex,
terminalLastEventId,
terminalLastEvent,
terminalLastSourceSession,
terminalLastAuthority,
terminalLastAdministrativeAuthorityValid,
terminalLastDisposition,
terminalLastDestination,
terminalLastTarget,
terminalLastByteDeliveries,
terminalLastSignalDeliveries,
terminalLastLocalDeliveries,
terminalLastMetadataDeliveries
>>
terminalInteractionVars == <<
terminalEventRecordVars, sessionClientSuspended, sessionTerminalTransportState
>>
terminalVars == <<terminalLifecycleVars, terminalInteractionVars>>
vars == <<baseVars, terminalVars>> vars == <<baseVars, terminalVars>>
Dom(s) == SessionDomain[s] Dom(s) == SessionDomain[s]
@ -520,6 +624,22 @@ Init ==
/\ sessionPtyOpen = [s \in Sessions |-> FALSE] /\ sessionPtyOpen = [s \in Sessions |-> FALSE]
/\ sessionExecPresent = [s \in Sessions |-> FALSE] /\ sessionExecPresent = [s \in Sessions |-> FALSE]
/\ sessionGuestKnown = [s \in Sessions |-> FALSE] /\ sessionGuestKnown = [s \in Sessions |-> FALSE]
/\ terminalEventIndex = [s \in Sessions |-> 1]
/\ terminalLastEventId = [s \in Sessions |-> 0]
/\ terminalLastEvent = [s \in Sessions |-> "None"]
/\ terminalLastSourceSession = [s \in Sessions |-> NoSession]
/\ terminalLastAuthority = [s \in Sessions |-> "None"]
/\ terminalLastAdministrativeAuthorityValid =
[s \in Sessions |-> FALSE]
/\ terminalLastDisposition = [s \in Sessions |-> "None"]
/\ terminalLastDestination = [s \in Sessions |-> NoSession]
/\ terminalLastTarget = [s \in Sessions |-> "None"]
/\ terminalLastByteDeliveries = [s \in Sessions |-> 0]
/\ terminalLastSignalDeliveries = [s \in Sessions |-> 0]
/\ terminalLastLocalDeliveries = [s \in Sessions |-> 0]
/\ terminalLastMetadataDeliveries = [s \in Sessions |-> 0]
/\ sessionClientSuspended = [s \in Sessions |-> FALSE]
/\ sessionTerminalTransportState = [s \in Sessions |-> "Ready"]
(*************************************************************************** (***************************************************************************
Domain and Session lifecycle Domain and Session lifecycle
@ -883,8 +1003,9 @@ DisconnectSession(s) ==
Session Terminal and PTY lifecycle Session Terminal and PTY lifecycle
The model retains durable state plus abstract PTY, exec-client, and guest The model retains durable state plus abstract PTY, exec-client, and guest
identity facts. Descriptor identities, PID reuse checks, and signal delivery identity facts. It distinguishes interaction authorities and target classes.
remain physical refinements. Descriptor identities, PID reuse checks, control-character values, terminal
modes, and physical byte or signal delivery remain refinements.
***************************************************************************) ***************************************************************************)
BeginSessionTerminal(s) == BeginSessionTerminal(s) ==
@ -898,6 +1019,7 @@ BeginSessionTerminal(s) ==
/\ sessionExecPresent' = [sessionExecPresent EXCEPT ![s] = TRUE] /\ sessionExecPresent' = [sessionExecPresent EXCEPT ![s] = TRUE]
/\ sessionGuestKnown' = [sessionGuestKnown EXCEPT ![s] = FALSE] /\ sessionGuestKnown' = [sessionGuestKnown EXCEPT ![s] = FALSE]
/\ UNCHANGED baseVars /\ UNCHANGED baseVars
/\ UNCHANGED terminalInteractionVars
CommitSessionTerminal(s) == CommitSessionTerminal(s) ==
/\ sessionPhase[s] = "Attached" /\ sessionPhase[s] = "Attached"
@ -909,6 +1031,7 @@ CommitSessionTerminal(s) ==
/\ sessionTerminalState' = [sessionTerminalState EXCEPT ![s] = "Running"] /\ sessionTerminalState' = [sessionTerminalState EXCEPT ![s] = "Running"]
/\ sessionGuestKnown' = [sessionGuestKnown EXCEPT ![s] = TRUE] /\ sessionGuestKnown' = [sessionGuestKnown EXCEPT ![s] = TRUE]
/\ UNCHANGED <<baseVars, sessionPtyOpen, sessionExecPresent>> /\ UNCHANGED <<baseVars, sessionPtyOpen, sessionExecPresent>>
/\ UNCHANGED terminalInteractionVars
DisconnectCleanSession(s) == DisconnectCleanSession(s) ==
/\ sessionTerminalState[s] = "Clean" /\ sessionTerminalState[s] = "Clean"
@ -921,6 +1044,7 @@ DisconnectLiveSessionTerminal(s) ==
/\ sessionTerminalState' = /\ sessionTerminalState' =
[sessionTerminalState EXCEPT ![s] = "ReleaseIntent"] [sessionTerminalState EXCEPT ![s] = "ReleaseIntent"]
/\ UNCHANGED <<sessionPtyOpen, sessionExecPresent, sessionGuestKnown>> /\ UNCHANGED <<sessionPtyOpen, sessionExecPresent, sessionGuestKnown>>
/\ UNCHANGED terminalInteractionVars
CompleteSessionTerminal(s) == CompleteSessionTerminal(s) ==
/\ sessionTerminalState[s] = "Running" /\ sessionTerminalState[s] = "Running"
@ -929,6 +1053,7 @@ CompleteSessionTerminal(s) ==
[sessionTerminalState EXCEPT ![s] = "ReleaseIntent"] [sessionTerminalState EXCEPT ![s] = "ReleaseIntent"]
/\ sessionExecPresent' = [sessionExecPresent EXCEPT ![s] = FALSE] /\ sessionExecPresent' = [sessionExecPresent EXCEPT ![s] = FALSE]
/\ UNCHANGED <<sessionPtyOpen, sessionGuestKnown>> /\ UNCHANGED <<sessionPtyOpen, sessionGuestKnown>>
/\ UNCHANGED terminalInteractionVars
ExitSessionTerminalBeforeGuestIdentity(s) == ExitSessionTerminalBeforeGuestIdentity(s) ==
/\ sessionTerminalState[s] = "Intent" /\ sessionTerminalState[s] = "Intent"
@ -938,6 +1063,7 @@ ExitSessionTerminalBeforeGuestIdentity(s) ==
/\ sessionPtyOpen' = [sessionPtyOpen EXCEPT ![s] = FALSE] /\ sessionPtyOpen' = [sessionPtyOpen EXCEPT ![s] = FALSE]
/\ sessionExecPresent' = [sessionExecPresent EXCEPT ![s] = FALSE] /\ sessionExecPresent' = [sessionExecPresent EXCEPT ![s] = FALSE]
/\ UNCHANGED sessionGuestKnown /\ UNCHANGED sessionGuestKnown
/\ UNCHANGED terminalInteractionVars
RetainFailedSessionTerminal(s) == RetainFailedSessionTerminal(s) ==
/\ sessionPhase[s] = "Failed" /\ sessionPhase[s] = "Failed"
@ -947,6 +1073,7 @@ RetainFailedSessionTerminal(s) ==
/\ sessionPtyOpen' = [sessionPtyOpen EXCEPT ![s] = FALSE] /\ sessionPtyOpen' = [sessionPtyOpen EXCEPT ![s] = FALSE]
/\ sessionExecPresent' = [sessionExecPresent EXCEPT ![s] = FALSE] /\ sessionExecPresent' = [sessionExecPresent EXCEPT ![s] = FALSE]
/\ UNCHANGED <<baseVars, sessionGuestKnown>> /\ UNCHANGED <<baseVars, sessionGuestKnown>>
/\ UNCHANGED terminalInteractionVars
CleanKnownSessionTerminal(s) == CleanKnownSessionTerminal(s) ==
/\ sessionPhase[s] \in {"Closed", "Failed"} /\ sessionPhase[s] \in {"Closed", "Failed"}
@ -958,6 +1085,7 @@ CleanKnownSessionTerminal(s) ==
/\ sessionExecPresent' = [sessionExecPresent EXCEPT ![s] = FALSE] /\ sessionExecPresent' = [sessionExecPresent EXCEPT ![s] = FALSE]
/\ sessionGuestKnown' = [sessionGuestKnown EXCEPT ![s] = FALSE] /\ sessionGuestKnown' = [sessionGuestKnown EXCEPT ![s] = FALSE]
/\ UNCHANGED baseVars /\ UNCHANGED baseVars
/\ UNCHANGED terminalInteractionVars
BootstrapCleanSessionTerminalAfterRuntimeAbsence(s) == BootstrapCleanSessionTerminalAfterRuntimeAbsence(s) ==
/\ sessionPhase[s] \in {"Closed", "Failed"} /\ sessionPhase[s] \in {"Closed", "Failed"}
@ -968,7 +1096,200 @@ BootstrapCleanSessionTerminalAfterRuntimeAbsence(s) ==
/\ HostCoordinatorAlive(Dom(s)) /\ HostCoordinatorAlive(Dom(s))
/\ sessionTerminalState' = [sessionTerminalState EXCEPT ![s] = "Clean"] /\ sessionTerminalState' = [sessionTerminalState EXCEPT ![s] = "Clean"]
/\ sessionGuestKnown' = [sessionGuestKnown EXCEPT ![s] = FALSE] /\ sessionGuestKnown' = [sessionGuestKnown EXCEPT ![s] = FALSE]
/\ UNCHANGED <<baseVars, sessionPtyOpen, sessionExecPresent>> /\ UNCHANGED <<baseVars, sessionPtyOpen, sessionExecPresent,
terminalInteractionVars>>
TerminalInteractionAdmissible(s) ==
/\ sessionPhase[s] = "Attached"
/\ connectionState[s] = "Connected"
/\ terminalRoute[s] = "Sandbox"
/\ sessionTerminalState[s] = "Running"
/\ HostCoordinatorAlive(Dom(s))
TerminalEventAvailable(s) ==
terminalEventIndex[s] <= Len(TerminalEventPlan[s])
CurrentTerminalEvent(s) ==
IF TerminalEventAvailable(s)
THEN TerminalEventPlan[s][terminalEventIndex[s]]
ELSE "None"
CurrentTerminalAuthority(s) ==
IF TerminalEventAvailable(s)
THEN TerminalAuthorityPlan[s][terminalEventIndex[s]]
ELSE "None"
CurrentTerminalAdministrativeAuthorityValid(s) ==
IF TerminalEventAvailable(s)
THEN TerminalAdministrativeAuthorityValid[s][terminalEventIndex[s]]
ELSE FALSE
OtherSession(s) == CHOOSE t \in Sessions : t # s
TerminalDelivery(disposition, destination, target, bytes, signals, local, metadata) ==
[disposition |-> disposition,
destination |-> destination,
target |-> target,
byteDeliveries |-> bytes,
signalDeliveries |-> signals,
localDeliveries |-> local,
metadataDeliveries |-> metadata]
NoTerminalDelivery ==
TerminalDelivery("None", NoSession, "None", 0, 0, 0, 0)
RefusedTerminalDelivery ==
TerminalDelivery("Refused", NoSession, "None", 0, 0, 0, 0)
ExpectedTerminalDelivery(s, event, authority, administrativeAuthorityValid) ==
CASE /\ event = "Keyboard"
/\ authority = "Client" ->
TerminalDelivery("Delivered", s, "GuestPtyForeground", 1, 0, 0, 0)
[] /\ event = "ExplicitSignal"
/\ authority = "Administrator"
/\ administrativeAuthorityValid ->
TerminalDelivery("Delivered", s, "RecordedGuestGroup", 0, 1, 0, 0)
[] event = "ExplicitSignal" -> RefusedTerminalDelivery
[] /\ event = "Resize"
/\ authority = "Client" ->
TerminalDelivery("Delivered", s, "ExactSessionPty", 0, 0, 0, 1)
[] /\ event = "Break"
/\ authority = "LocalClient" ->
TerminalDelivery("Delivered", s, "ExactSessionPty", 0, 0, 0, 1)
[] /\ event \in {"LocalSuspend", "LocalResume", "LocalDisconnect"}
/\ authority = "LocalClient" ->
TerminalDelivery("Delivered", s, "LocalClient", 0, 0, 1, 0)
[] /\ event = "HostSignal"
/\ authority = "HostProcess" ->
TerminalDelivery("Delivered", s, "LocalClient", 0, 0, 1, 0)
[] OTHER -> RefusedTerminalDelivery
RoutedTerminalDelivery(s, event, authority, administrativeAuthorityValid) ==
LET expected == ExpectedTerminalDelivery(
s, event, authority, administrativeAuthorityValid) IN
CASE /\ TerminalRoutingFault = "KeyboardRecordedGroup"
/\ event = "Keyboard" ->
TerminalDelivery("Delivered", s, "RecordedGuestGroup", 0, 1, 0, 0)
[] /\ TerminalRoutingFault = "KeyboardDuplicated"
/\ event = "Keyboard" ->
TerminalDelivery("Delivered", s, "GuestPtyForeground", 1, 1, 0, 0)
[] /\ TerminalRoutingFault = "CrossSession"
/\ expected.disposition = "Delivered" ->
[expected EXCEPT !.destination = OtherSession(s)]
[] /\ TerminalRoutingFault = "ClientAdministrativeSignal"
/\ event = "ExplicitSignal"
/\ authority = "Client" ->
TerminalDelivery("Delivered", s, "RecordedGuestGroup", 0, 1, 0, 0)
[] OTHER -> expected
RecordTerminalEvent(s) ==
LET event == CurrentTerminalEvent(s)
authority == CurrentTerminalAuthority(s)
administrativeAuthorityValid ==
CurrentTerminalAdministrativeAuthorityValid(s)
delivery == RoutedTerminalDelivery(
s, event, authority, administrativeAuthorityValid)
IN
/\ terminalEventIndex' =
[terminalEventIndex EXCEPT ![s] = @ + 1]
/\ terminalLastEventId' =
[terminalLastEventId EXCEPT ![s] = terminalEventIndex[s]]
/\ terminalLastEvent' = [terminalLastEvent EXCEPT ![s] = event]
/\ terminalLastSourceSession' =
[terminalLastSourceSession EXCEPT ![s] = s]
/\ terminalLastAuthority' =
[terminalLastAuthority EXCEPT ![s] = authority]
/\ terminalLastAdministrativeAuthorityValid' =
[terminalLastAdministrativeAuthorityValid EXCEPT
![s] = administrativeAuthorityValid]
/\ terminalLastDisposition' =
[terminalLastDisposition EXCEPT ![s] = delivery.disposition]
/\ terminalLastDestination' =
[terminalLastDestination EXCEPT ![s] = delivery.destination]
/\ terminalLastTarget' =
[terminalLastTarget EXCEPT ![s] = delivery.target]
/\ terminalLastByteDeliveries' =
[terminalLastByteDeliveries EXCEPT ![s] = delivery.byteDeliveries]
/\ terminalLastSignalDeliveries' =
[terminalLastSignalDeliveries EXCEPT ![s] = delivery.signalDeliveries]
/\ terminalLastLocalDeliveries' =
[terminalLastLocalDeliveries EXCEPT ![s] = delivery.localDeliveries]
/\ terminalLastMetadataDeliveries' =
[terminalLastMetadataDeliveries EXCEPT ![s] = delivery.metadataDeliveries]
DeliverPtyTerminalEvent(s) ==
/\ TerminalInteractionAdmissible(s)
/\ TerminalEventAvailable(s)
/\ CurrentTerminalEvent(s) \in {
"Keyboard", "ExplicitSignal", "Resize", "Break"
}
/\ ~sessionClientSuspended[s]
/\ sessionTerminalTransportState[s] = "Ready"
/\ RecordTerminalEvent(s)
/\ UNCHANGED <<baseVars, terminalLifecycleVars, sessionClientSuspended,
sessionTerminalTransportState>>
SuspendLocalTerminalClient(s) ==
/\ TerminalInteractionAdmissible(s)
/\ TerminalEventAvailable(s)
/\ CurrentTerminalEvent(s) = "LocalSuspend"
/\ ~sessionClientSuspended[s]
/\ RecordTerminalEvent(s)
/\ sessionClientSuspended' =
[sessionClientSuspended EXCEPT ![s] = TRUE]
/\ UNCHANGED <<baseVars, terminalLifecycleVars,
sessionTerminalTransportState>>
ResumeLocalTerminalClient(s) ==
/\ TerminalInteractionAdmissible(s)
/\ TerminalEventAvailable(s)
/\ CurrentTerminalEvent(s) = "LocalResume"
/\ sessionClientSuspended[s]
/\ RecordTerminalEvent(s)
/\ sessionClientSuspended' =
[sessionClientSuspended EXCEPT ![s] = FALSE]
/\ UNCHANGED <<baseVars, terminalLifecycleVars,
sessionTerminalTransportState>>
DisconnectTerminalFromLocalAuthority(s, event) ==
/\ event \in {"LocalDisconnect", "HostSignal"}
/\ TerminalEventAvailable(s)
/\ CurrentTerminalEvent(s) = event
/\ sessionTerminalState[s] \in {"Intent", "Running"}
/\ \/ TerminalRoutingFault # "BlockedTransportPreventsLocalControl"
\/ sessionTerminalTransportState[s] = "Ready"
/\ DisconnectSession(s)
/\ sessionTerminalState' =
[sessionTerminalState EXCEPT ![s] = "ReleaseIntent"]
/\ RecordTerminalEvent(s)
/\ sessionClientSuspended' =
[sessionClientSuspended EXCEPT ![s] = FALSE]
/\ UNCHANGED <<sessionPtyOpen, sessionExecPresent, sessionGuestKnown,
sessionTerminalTransportState>>
BlockSessionTerminalTransport(s) ==
/\ TerminalInteractionAdmissible(s)
/\ sessionTerminalTransportState[s] = "Ready"
/\ sessionTerminalTransportState' =
[sessionTerminalTransportState EXCEPT ![s] = "Blocked"]
/\ UNCHANGED <<baseVars, terminalLifecycleVars, terminalEventRecordVars,
sessionClientSuspended>>
UnblockSessionTerminalTransport(s) ==
/\ sessionTerminalTransportState[s] = "Blocked"
/\ sessionTerminalTransportState' =
[sessionTerminalTransportState EXCEPT ![s] = "Ready"]
/\ UNCHANGED <<baseVars, terminalLifecycleVars, terminalEventRecordVars,
sessionClientSuspended>>
TerminalInteraction(s) ==
\/ DeliverPtyTerminalEvent(s)
\/ SuspendLocalTerminalClient(s)
\/ ResumeLocalTerminalClient(s)
\/ BlockSessionTerminalTransport(s)
\/ UnblockSessionTerminalTransport(s)
\/ (\E event \in {"LocalDisconnect", "HostSignal"} :
DisconnectTerminalFromLocalAuthority(s, event))
BeginTeardown(d) == BeginTeardown(d) ==
/\ domainPhase[d] = "Ready" /\ domainPhase[d] = "Ready"
@ -2129,12 +2450,16 @@ Next ==
\/ /\ BaseNext \/ /\ BaseNext
/\ UNCHANGED terminalVars /\ UNCHANGED terminalVars
\/ (\E s \in Sessions : SessionTerminalProgress(s)) \/ (\E s \in Sessions : SessionTerminalProgress(s))
\/ (\E s \in Sessions : TerminalInteraction(s))
\* Resume transitions are enabled but not weakly fair: permanent stop is a \* Resume transitions are enabled but not weakly fair: permanent stop is a
\* modeled stuck state for safety, not a guaranteed progress path. \* modeled stuck state for safety, not a guaranteed progress path.
Spec == SafetySpec ==
/\ Init /\ Init
/\ [][Next]_vars /\ [][Next]_vars
Spec ==
/\ SafetySpec
/\ \A d \in Domains : /\ \A d \in Domains :
WF_vars(DomainServiceProgress(d) /\ UNCHANGED terminalVars) WF_vars(DomainServiceProgress(d) /\ UNCHANGED terminalVars)
/\ \A s \in Sessions : /\ \A s \in Sessions :
@ -2148,6 +2473,15 @@ Safety properties
***************************************************************************) ***************************************************************************)
TypeOK == TypeOK ==
/\ TerminalEventPlan \in [Sessions -> Seq(TerminalEventKinds)]
/\ TerminalAuthorityPlan \in [Sessions -> Seq(TerminalAuthorities)]
/\ TerminalAdministrativeAuthorityValid \in [Sessions -> Seq(BOOLEAN)]
/\ \A s \in Sessions :
/\ Len(TerminalEventPlan[s]) = Len(TerminalAuthorityPlan[s])
/\ Len(TerminalEventPlan[s]) =
Len(TerminalAdministrativeAuthorityValid[s])
/\ TerminalRoutingFault \in TerminalRoutingFaults
/\ (TerminalRoutingFault = "CrossSession" => Cardinality(Sessions) > 1)
/\ domainPhase \in [Domains -> DomainPhases] /\ domainPhase \in [Domains -> DomainPhases]
/\ stateAnchorState \in [Domains -> MountStates] /\ stateAnchorState \in [Domains -> MountStates]
/\ sidecarState \in [Domains -> ResourceStates] /\ sidecarState \in [Domains -> ResourceStates]
@ -2186,6 +2520,23 @@ TypeOK ==
/\ sessionPtyOpen \in [Sessions -> BOOLEAN] /\ sessionPtyOpen \in [Sessions -> BOOLEAN]
/\ sessionExecPresent \in [Sessions -> BOOLEAN] /\ sessionExecPresent \in [Sessions -> BOOLEAN]
/\ sessionGuestKnown \in [Sessions -> BOOLEAN] /\ sessionGuestKnown \in [Sessions -> BOOLEAN]
/\ \A s \in Sessions :
terminalEventIndex[s] \in 1..(Len(TerminalEventPlan[s]) + 1)
/\ \A s \in Sessions :
terminalLastEventId[s] \in 0..Len(TerminalEventPlan[s])
/\ terminalLastEvent \in [Sessions -> TerminalEventKinds \cup {"None"}]
/\ terminalLastSourceSession \in [Sessions -> Sessions \cup {NoSession}]
/\ terminalLastAuthority \in [Sessions -> TerminalAuthorities]
/\ terminalLastAdministrativeAuthorityValid \in [Sessions -> BOOLEAN]
/\ terminalLastDisposition \in [Sessions -> TerminalDispositions]
/\ terminalLastDestination \in [Sessions -> Sessions \cup {NoSession}]
/\ terminalLastTarget \in [Sessions -> TerminalTargets]
/\ terminalLastByteDeliveries \in [Sessions -> 0..1]
/\ terminalLastSignalDeliveries \in [Sessions -> 0..1]
/\ terminalLastLocalDeliveries \in [Sessions -> 0..1]
/\ terminalLastMetadataDeliveries \in [Sessions -> 0..1]
/\ sessionClientSuspended \in [Sessions -> BOOLEAN]
/\ sessionTerminalTransportState \in [Sessions -> TerminalTransportStates]
SessionTerminalPhysicalConsistency == SessionTerminalPhysicalConsistency ==
\A s \in Sessions : \A s \in Sessions :
@ -2375,6 +2726,38 @@ TerminalIsolation ==
\A s \in Sessions : sessionPhase[s] = "Prompting" => \A s \in Sessions : sessionPhase[s] = "Prompting" =>
terminalRoute[s] = "Prompt" terminalRoute[s] = "Prompt"
ObservedTerminalDelivery(s) ==
TerminalDelivery(
terminalLastDisposition[s],
terminalLastDestination[s],
terminalLastTarget[s],
terminalLastByteDeliveries[s],
terminalLastSignalDeliveries[s],
terminalLastLocalDeliveries[s],
terminalLastMetadataDeliveries[s])
TerminalInteractionAuthoritySeparated ==
\A s \in Sessions :
/\ terminalEventIndex[s] = terminalLastEventId[s] + 1
/\ IF terminalLastEventId[s] = 0
THEN /\ terminalLastEvent[s] = "None"
/\ terminalLastSourceSession[s] = NoSession
/\ terminalLastAuthority[s] = "None"
/\ ~terminalLastAdministrativeAuthorityValid[s]
/\ ObservedTerminalDelivery(s) = NoTerminalDelivery
ELSE LET event == TerminalEventPlan[s][terminalLastEventId[s]]
authority == TerminalAuthorityPlan[s][terminalLastEventId[s]]
administrativeAuthorityValid ==
TerminalAdministrativeAuthorityValid[s][terminalLastEventId[s]]
IN /\ terminalLastEvent[s] = event
/\ terminalLastSourceSession[s] = s
/\ terminalLastAuthority[s] = authority
/\ terminalLastAdministrativeAuthorityValid[s] =
administrativeAuthorityValid
/\ ObservedTerminalDelivery(s) =
ExpectedTerminalDelivery(
s, event, authority, administrativeAuthorityValid)
NoOrphanHostProcess == NoOrphanHostProcess ==
\A r \in hostRunning : \A r \in hostRunning :
LET s == ReqSession(r) IN LET s == ReqSession(r) IN
@ -2544,6 +2927,7 @@ Safety ==
/\ HostExecutionAuthorized /\ HostExecutionAuthorized
/\ AutoApprovalNoShell /\ AutoApprovalNoShell
/\ TerminalIsolation /\ TerminalIsolation
/\ TerminalInteractionAuthoritySeparated
/\ NoOrphanHostProcess /\ NoOrphanHostProcess
/\ StateAnchorOwnership /\ StateAnchorOwnership
/\ FailedDomainIsolation /\ FailedDomainIsolation
@ -2551,6 +2935,65 @@ Safety ==
/\ ResultDeliveryStateConsistent /\ ResultDeliveryStateConsistent
/\ NetworkReadinessBoundToSandbox /\ NetworkReadinessBoundToSandbox
TerminalEventAdvanced(s) ==
/\ terminalLastEventId'[s] = terminalEventIndex[s]
/\ terminalEventIndex'[s] = terminalEventIndex[s] + 1
TerminalSessionProjection(s) == <<
sessionPhase[s], terminalRoute[s], connectionState[s], activeRequest[s],
sessionTerminalState[s], sessionPtyOpen[s], sessionExecPresent[s],
sessionGuestKnown[s], terminalEventIndex[s], terminalLastEventId[s],
terminalLastEvent[s], terminalLastSourceSession[s],
terminalLastAuthority[s], terminalLastAdministrativeAuthorityValid[s],
terminalLastDisposition[s],
terminalLastDestination[s], terminalLastTarget[s],
terminalLastByteDeliveries[s], terminalLastSignalDeliveries[s],
terminalLastLocalDeliveries[s], terminalLastMetadataDeliveries[s],
sessionClientSuspended[s], sessionTerminalTransportState[s]
>>
DomainRuntimeProjection(d) == <<
domainPhase[d], stateAnchorState[d], sidecarState[d], sandboxState[d],
trustReady[d], domainLockHeld[d], hostCoordinatorProc[d],
runtimeSupervisorProc[d], attachedRunscProc[d], runtimeCausalEdge[d],
supervisorLease[d], domainGeneration[d], durableDecision[d],
durableIntent[d], genCoordinatorId[d], genSidecarId[d], genSandboxId[d],
networkReady[d], preparedLowerLeased[d], helperProc[d]
>>
TerminalPeerIsolationAction ==
\A s \in Sessions :
TerminalEventAdvanced(s) =>
\A t \in Sessions \ {s} : UNCHANGED TerminalSessionProjection(t)
TerminalPeerIsolation ==
[][TerminalPeerIsolationAction]_vars
LocalTerminalControlKinds == {
"LocalSuspend", "LocalResume", "LocalDisconnect", "HostSignal"
}
TerminalLocalControlPreservesDomainAction ==
\A s \in Sessions :
/\ TerminalEventAdvanced(s)
/\ CurrentTerminalEvent(s) \in LocalTerminalControlKinds
/\ \E t \in Sessions \ {s} : connectionState[t] = "Connected"
=> UNCHANGED DomainRuntimeProjection(Dom(s))
TerminalLocalControlPreservesDomain ==
[][TerminalLocalControlPreservesDomainAction]_vars
LocalTerminationAvailableWhileTransportBlocked ==
\A s \in Sessions :
/\ sessionTerminalTransportState[s] = "Blocked"
/\ sessionPhase[s] = "Attached"
/\ connectionState[s] = "Connected"
/\ sessionTerminalState[s] \in {"Intent", "Running"}
/\ TerminalEventAvailable(s)
/\ CurrentTerminalEvent(s) \in {"LocalDisconnect", "HostSignal"}
=> ENABLED DisconnectTerminalFromLocalAuthority(
s, CurrentTerminalEvent(s))
(*************************************************************************** (***************************************************************************
Liveness properties. Liveness properties.

View file

@ -0,0 +1,67 @@
CONSTANTS
D1 = d1
S1 = s1
S2 = s2
R1 = r1
R2 = r2
R3 = r3
ExactCmd = exactCmd
PromptCmd = promptCmd
ShellCmd = shellCmd
EditedCmd = editedCmd
Identities <- IdentitiesFixture
SessionAnchors <- SessionAnchorsFixture
Domains <- DomainsFixture
Sessions <- SessionsFixture
Requests <- RequestsFixture
Commands <- CommandsFixture
NoSession = NoSession
NoRequest = NoRequest
NoCommand = NoCommand
NoApproval = NoApproval
SessionDomain <- SessionDomainFixture
SessionFingerprint <- SessionFingerprintFixture
DomainFingerprint <- DomainFingerprintFixture
DomainCoordinatorIdentity <- DomainCoordinatorIdentityFixture
DomainSidecarIdentity <- DomainSidecarIdentityFixture
DomainSandboxIdentity <- DomainSandboxIdentityFixture
SessionCliIdentity <- SessionCliIdentityFixture
SessionAnchorIdentity <- SessionAnchorIdentityFixture
CoordinatorCertificateValid <- CoordinatorCertificateValidFixture
SidecarCertificateValid <- SidecarCertificateValidFixture
SandboxCertificateValid <- SandboxCertificateValidFixture
CliCertificateValid <- CliCertificateValidFixture
ConfigSafe <- ConfigSafeFixture
SandboxIsolationSafe <- SandboxIsolationSafeFixture
RequestSession <- RequestSessionFixture
RequestCommand <- RequestCommandFixture
RequestSandboxIdentity <- RequestSandboxIdentityFixture
RequestCliIdentity <- RequestCliIdentityFixture
RequestSessionAnchor <- RequestSessionAnchorFixture
RequestCredentialValid <- RequestCredentialValidFixture
RequestSignatureValid <- RequestSignatureValidFixture
ApprovalCliIdentity <- ApprovalCliIdentityFixture
ApprovalSessionAnchor <- ApprovalSessionAnchorFixture
ApprovalSignatureValid <- ApprovalSignatureValidFixture
ApprovalCommand <- ApprovalCommandFixture
AutoRuleMatches <- AutoRuleMatchesFixture
CommandRequiresShell <- CommandRequiresShellFixture
CommandContentSafe <- CommandContentSafeFixture
TerminalEventPlan <- TerminalLocalControlEventPlanFixture
TerminalAuthorityPlan <- TerminalLocalControlAuthorityPlanFixture
TerminalAdministrativeAuthorityValid <-
TerminalLocalControlAdministrativeAuthorityValidFixture
TerminalRoutingFault = "Correct"
SPECIFICATION SafetySpec
CHECK_DEADLOCK FALSE
INVARIANTS
TypeOK
SessionTerminalPhysicalConsistency
TerminalInteractionAuthoritySeparated
LocalTerminationAvailableWhileTransportBlocked
PROPERTIES
TerminalPeerIsolation
TerminalLocalControlPreservesDomain

View file

@ -0,0 +1,66 @@
CONSTANTS
D1 = d1
S1 = s1
S2 = s2
R1 = r1
R2 = r2
R3 = r3
ExactCmd = exactCmd
PromptCmd = promptCmd
ShellCmd = shellCmd
EditedCmd = editedCmd
Identities <- IdentitiesFixture
SessionAnchors <- SessionAnchorsFixture
Domains <- DomainsFixture
Sessions <- SessionsFixture
Requests <- RequestsFixture
Commands <- CommandsFixture
NoSession = NoSession
NoRequest = NoRequest
NoCommand = NoCommand
NoApproval = NoApproval
SessionDomain <- SessionDomainFixture
SessionFingerprint <- SessionFingerprintFixture
DomainFingerprint <- DomainFingerprintFixture
DomainCoordinatorIdentity <- DomainCoordinatorIdentityFixture
DomainSidecarIdentity <- DomainSidecarIdentityFixture
DomainSandboxIdentity <- DomainSandboxIdentityFixture
SessionCliIdentity <- SessionCliIdentityFixture
SessionAnchorIdentity <- SessionAnchorIdentityFixture
CoordinatorCertificateValid <- CoordinatorCertificateValidFixture
SidecarCertificateValid <- SidecarCertificateValidFixture
SandboxCertificateValid <- SandboxCertificateValidFixture
CliCertificateValid <- CliCertificateValidFixture
ConfigSafe <- ConfigSafeFixture
SandboxIsolationSafe <- SandboxIsolationSafeFixture
RequestSession <- RequestSessionFixture
RequestCommand <- RequestCommandFixture
RequestSandboxIdentity <- RequestSandboxIdentityFixture
RequestCliIdentity <- RequestCliIdentityFixture
RequestSessionAnchor <- RequestSessionAnchorFixture
RequestCredentialValid <- RequestCredentialValidFixture
RequestSignatureValid <- RequestSignatureValidFixture
ApprovalCliIdentity <- ApprovalCliIdentityFixture
ApprovalSessionAnchor <- ApprovalSessionAnchorFixture
ApprovalSignatureValid <- ApprovalSignatureValidFixture
ApprovalCommand <- ApprovalCommandFixture
AutoRuleMatches <- AutoRuleMatchesFixture
CommandRequiresShell <- CommandRequiresShellFixture
CommandContentSafe <- CommandContentSafeFixture
TerminalEventPlan <- TerminalRoutingEventPlanFixture
TerminalAuthorityPlan <- TerminalRoutingAuthorityPlanFixture
TerminalAdministrativeAuthorityValid <-
TerminalRoutingAdministrativeAuthorityValidFixture
TerminalRoutingFault = "Correct"
SPECIFICATION SafetySpec
CHECK_DEADLOCK FALSE
INVARIANTS
TypeOK
SessionTerminalPhysicalConsistency
TerminalInteractionAuthoritySeparated
LocalTerminationAvailableWhileTransportBlocked
PROPERTIES
TerminalPeerIsolation

View file

@ -0,0 +1,105 @@
CONSTANTS
D1 = d1
S1 = s1
S2 = s2
R1 = r1
R2 = r2
R3 = r3
ExactCmd = exactCmd
PromptCmd = promptCmd
ShellCmd = shellCmd
EditedCmd = editedCmd
Identities <- IdentitiesFixture
SessionAnchors <- SessionAnchorsFixture
Domains <- DomainsFixture
Sessions <- SessionsFixture
Requests <- RequestsFixture
Commands <- CommandsFixture
NoSession = NoSession
NoRequest = NoRequest
NoCommand = NoCommand
NoApproval = NoApproval
SessionDomain <- SessionDomainFixture
SessionFingerprint <- SessionFingerprintFixture
DomainFingerprint <- DomainFingerprintFixture
DomainCoordinatorIdentity <- DomainCoordinatorIdentityFixture
DomainSidecarIdentity <- DomainSidecarIdentityFixture
DomainSandboxIdentity <- DomainSandboxIdentityFixture
SessionCliIdentity <- SessionCliIdentityFixture
SessionAnchorIdentity <- SessionAnchorIdentityFixture
CoordinatorCertificateValid <- CoordinatorCertificateValidFixture
SidecarCertificateValid <- SidecarCertificateValidFixture
SandboxCertificateValid <- SandboxCertificateValidFixture
CliCertificateValid <- CliCertificateValidFixture
ConfigSafe <- ConfigSafeFixture
SandboxIsolationSafe <- SandboxIsolationSafeFixture
RequestSession <- RequestSessionFixture
RequestCommand <- RequestCommandFixture
RequestSandboxIdentity <- RequestSandboxIdentityFixture
RequestCliIdentity <- RequestCliIdentityFixture
RequestSessionAnchor <- RequestSessionAnchorFixture
RequestCredentialValid <- RequestCredentialValidFixture
RequestSignatureValid <- RequestSignatureValidFixture
ApprovalCliIdentity <- ApprovalCliIdentityFixture
ApprovalSessionAnchor <- ApprovalSessionAnchorFixture
ApprovalSignatureValid <- ApprovalSignatureValidFixture
ApprovalCommand <- ApprovalCommandFixture
AutoRuleMatches <- AutoRuleMatchesFixture
CommandRequiresShell <- CommandRequiresShellFixture
CommandContentSafe <- CommandContentSafeFixture
TerminalEventPlan <- TerminalEventPlanFixture
TerminalAuthorityPlan <- TerminalAuthorityPlanFixture
TerminalAdministrativeAuthorityValid <-
TerminalAdministrativeAuthorityValidFixture
TerminalRoutingFault = "Correct"
SPECIFICATION SafetySpec
CHECK_DEADLOCK FALSE
INVARIANTS
TypeOK
SessionTerminalPhysicalConsistency
ConfigurationConfinement
SandboxNamespaceIsolation
OrderedNetworkStartup
PublishedDomainsReady
DomainLockLifetime
RuntimeCausalEdgeArmedBeforeSandbox
RuntimeCausalEdgeOwnerPresent
SandboxRunningImpliesCoordinatorPresent
PublishedRuntimeAuthorityPresent
SupervisorLeaseConsistency
RuntimeResourceOwnership
CoordinatorPhysicalConsistency
HostCoordinatorRequiresDomainOwnership
NoVanishPresentRuntime
OrderedLeaseTeardown
DomainGenerationBounds
DurableIntentBeforeSandboxReady
DurableDecisionPublicationConsistent
PathMismatchNotAdopted
ExclusiveBootstrapOwnership
TeardownNotJoinable
RegisteredSessionsConnected
AttachedSessionsValid
AuthenticatedRequestsOnly
RuntimeRoleIdentitySeparation
RegisteredSessionIdentityUniqueness
LiveRequestIdentityAuthorized
DeveloperApprovalAuthenticated
HostExecutionAuthorized
AutoApprovalNoShell
TerminalIsolation
TerminalInteractionAuthoritySeparated
LocalTerminationAvailableWhileTransportBlocked
NoOrphanHostProcess
StateAnchorOwnership
FailedDomainIsolation
OrderedFailureRecovery
ResultDeliveryStateConsistent
MultiRoleDurableIntent
NetworkReadinessBoundToSandbox
GenerationScopedIdentitiesDistinct
HelperUnderSupervisor
PreparedLowerLeaseBeforeMount
NetworkReadyBeforePublish

View file

@ -703,18 +703,89 @@ class VerificationTests(unittest.TestCase):
def test_model_paths_are_current_and_complete(self) -> None: def test_model_paths_are_current_and_complete(self) -> None:
self.assertEqual( self.assertEqual(
{module.name for module, _ in VERIFY.MODEL_CONFIGURATIONS},
{ {
(module.name, config.name)
for module, config in VERIFY.MODEL_CONFIGURATIONS
},
{
(
"IgnoredWorkspaceHardlinkRouting.tla", "IgnoredWorkspaceHardlinkRouting.tla",
"IgnoredWorkspaceRouting.tla", "IgnoredWorkspaceHardlinkRouting.cfg",
"Lazaret.tla", ),
"PreparedLower.tla", ("IgnoredWorkspaceRouting.tla", "IgnoredWorkspaceRouting.cfg"),
("Lazaret.tla", "Lazaret.cfg"),
("Lazaret.tla", "LazaretTerminalLocalControl.cfg"),
("Lazaret.tla", "LazaretTerminalPeerIsolation.cfg"),
("Lazaret.tla", "LazaretTerminalSafety.cfg"),
("PreparedLower.tla", "PreparedLower.cfg"),
}, },
) )
for module, config in VERIFY.MODEL_CONFIGURATIONS: for module, config in VERIFY.MODEL_CONFIGURATIONS:
self.assertTrue(module.is_file()) self.assertTrue(module.is_file())
self.assertTrue(config.is_file()) self.assertTrue(config.is_file())
def test_model_metadata_directories_are_unique_per_configuration(self) -> None:
commands = []
manager = mock.MagicMock()
manager.__enter__.return_value = "/verification"
manager.__exit__.return_value = False
with (
mock.patch.object(VERIFY.shutil, "which", return_value="/usr/bin/tlc"),
mock.patch.object(VERIFY, "temporary_directory", return_value=manager),
mock.patch.object(
VERIFY,
"run",
side_effect=lambda command, cwd: commands.append(command),
),
):
VERIFY.run_models()
self.assertEqual(
[
(
Path(command[-1]).name,
Path(command[command.index("-config") + 1]).name,
)
for command in commands
],
[
(module.name, config.name)
for module, config in VERIFY.MODEL_CONFIGURATIONS
],
)
metadata = [command[command.index("-metadir") + 1] for command in commands]
self.assertEqual(len(metadata), len(set(metadata)))
self.assertEqual(
[Path(path).name for path in metadata],
[
f"{module.stem}-{config.stem}"
for module, config in VERIFY.MODEL_CONFIGURATIONS
],
)
def test_lazaret_terminal_configs_retain_common_constants(self) -> None:
configs = [
ROOT / "docs/protocol/Lazaret.cfg",
ROOT / "docs/protocol/LazaretTerminalSafety.cfg",
ROOT / "docs/protocol/LazaretTerminalPeerIsolation.cfg",
ROOT / "docs/protocol/LazaretTerminalLocalControl.cfg",
]
prefixes = {
config.read_text().partition(" TerminalEventPlan <-")[0]
for config in configs
}
self.assertEqual(len(prefixes), 1)
def test_lazaret_full_event_safety_retains_canonical_invariants(self) -> None:
def invariants(config: Path) -> tuple[str, ...]:
body = config.read_text().split("INVARIANTS\n", 1)[1]
return tuple(body.split("\n\n", 1)[0].split())
self.assertEqual(
invariants(ROOT / "docs/protocol/LazaretTerminalSafety.cfg"),
invariants(ROOT / "docs/protocol/Lazaret.cfg"),
)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

View file

@ -20,6 +20,18 @@ MATERIALIZED_FUSER_MANIFEST = (
).resolve() ).resolve()
MODEL_CONFIGURATIONS = ( MODEL_CONFIGURATIONS = (
(ROOT / "docs/protocol/Lazaret.tla", ROOT / "docs/protocol/Lazaret.cfg"), (ROOT / "docs/protocol/Lazaret.tla", ROOT / "docs/protocol/Lazaret.cfg"),
(
ROOT / "docs/protocol/Lazaret.tla",
ROOT / "docs/protocol/LazaretTerminalSafety.cfg",
),
(
ROOT / "docs/protocol/Lazaret.tla",
ROOT / "docs/protocol/LazaretTerminalPeerIsolation.cfg",
),
(
ROOT / "docs/protocol/Lazaret.tla",
ROOT / "docs/protocol/LazaretTerminalLocalControl.cfg",
),
( (
ROOT / "docs/protocol/IgnoredWorkspaceRouting.tla", ROOT / "docs/protocol/IgnoredWorkspaceRouting.tla",
ROOT / "docs/protocol/IgnoredWorkspaceRouting.cfg", ROOT / "docs/protocol/IgnoredWorkspaceRouting.cfg",
@ -613,7 +625,7 @@ def run_models() -> None:
for module, config in MODEL_CONFIGURATIONS: for module, config in MODEL_CONFIGURATIONS:
require(module.is_file(), f"missing model: {module.relative_to(ROOT)}") require(module.is_file(), f"missing model: {module.relative_to(ROOT)}")
require(config.is_file(), f"missing model config: {config.relative_to(ROOT)}") require(config.is_file(), f"missing model config: {config.relative_to(ROOT)}")
metadata = workdir / module.stem metadata = workdir / f"{module.stem}-{config.stem}"
run( run(
[ [
tlc, tlc,