These tables make the 050/060 ownership model executable. A connected carrier owns prefacing or one logical session; a direct pair owns two endpoints. Its runtime transport may own one duplex IO value or split reader and writer values without changing carrier state. Host cancellation retires that complete connected transport. Listener acceptance, process supervision, admission, and task and service ownership remain outside these tables. The numbered specifications remain authoritative for validation and wire encoding.
A phase deadline is one absolute deadline created when a stable
prefacing, establishment, or endpoint phase token changes. Partial IO,
event reads, replacement staging, and progress by the other endpoint do
not reset it. If the machine enters fixed point while the batch accepted
at that transition, the settled round’s final request item, remains
buffered, the driver retains that batch’s Requesting token and deadline
until it is fully written and flushed to the adopted IO; it does not
publish FixedPoint first.
CarrierState =
Prefacing {
cause: Initial | Reset,
local_operand: optional (operand, optional expected peer By),
local_preface: Pending(nonce) | Accepted(nonce),
peer_nonce: optional nonce,
}
| Running {
session: SessionMachine,
staged_replacement: optional InterlaceModule,
}
| Failed(SessionError)
| Closed(CloseReason)
CarrierRole::Listener derives protocol side0 and
CarrierRole::Connector derives protocol side1. The role is
fixed before decoding and is not inferred or negotiated. Carrier role,
proof assurance and binding, operand source, By-secret value, configured
stream encodings and port, limits, nonce seed, and next nonce sequence
survive logical-session replacement. The operand source is
Fixed(expected peer By), whose operand replacement changes,
or Selected, which asks the host for each session’s operand
and expected peer By. The carrier also retains the port of
the latest peer preface as a diagnostic. Construction constraints are
defined by Lace-050.
Endpoint configuration and diagnostics are not policy facts, plan or
proof inputs, or carrier authority. The carrier,
ConnectedConfig, and Rust runtime connected inputs retain
the nonce seed in the core zeroizing NonceSeed owner; each
config clone owns an independently erased seed, and unclaimed
participants and failed setup erase theirs on drop. These owners,
BySecret, and schnorr-blake3’s seed-derived
SigningKey use maintained zeroize for owned
secret storage. The guarantee covers current owned storage, not caller
copies, prior moves, compiler temporaries, registers, opaque hash state
or C-library scratch. See schnorr-blake3 for
the cryptographic boundary.
The running machine owns the current local operand: establishment owns it until compilation, then its immutable plan owns it. The carrier keeps no duplicate. A selecting carrier owns no operand in prefacing until the host answers.
| Owning state/input | Transition |
|---|---|
| new carrier | enter Prefacing(Initial) with the fixed operand and its
expected peer By, or no operand when selecting, and derived
local nonce; expose local preface, with the configured port,
immediately |
| local replacement on a selecting carrier | Policy call error; no transition |
local replacement in Prefacing |
replace the fixed local_operand; do not emit another
preface |
local replacement in Running |
replace staged_replacement; do not poll, retire, emit,
execute IO, or deliver observations |
| next poll with staged replacement | consume session, drop unaccepted machine output and unsettled
machine observations, discard old operand, enter
Prefacing(Reset) with staged operand, the fixed expected
peer By, and fresh nonce; clear the peer’s proved
By |
peer preface in Running |
consume session, extract a fixed carrier’s local operand or discard
a selected one, drop unaccepted output, enter
Prefacing(Reset) with fresh local nonce and received peer
nonce; retain the peer port and clear the peer’s proved
By |
first peer preface in Prefacing |
retain nonce and peer port; complete pair when local preface is accepted and the operand is owned |
| selecting, peer preface owned, no operand | after local-preface output, poll exposes
SelectOperand(peer port) and reads no peer input |
| host selects an operand | validate its expected peer By against assurance
(Integration) and its Blob size
(LimitExceeded), failing the carrier on error; otherwise
own it |
| host refuses the port | Failed(PeerProtocol("port refused")) |
| second peer preface before construction | Failed(PeerProtocol) |
| both prefaces and the operand owned | the codec selects each direction’s 065 encoding
from the two offers and installs fresh primed state; move operand, its
expected peer By, and nonce texts into one fresh
SessionMachine; enter Running.Side0Proof |
| peer proof validated under confidential or channel-bound assurance | retain its By as the peer’s proved By, the
session’s PeerConfidential key |
The decoder stops at a preface before parsing any bounded suffix. During initial prefacing, ordinary input is premature. During locally initiated reset prefacing, complete old peer items are decoded under the old encoding and discarded until the peer’s end mark, if any, and preface. Malformed or oversized input fails normally. One absolute prefacing deadline covers accepted old-writer drain, local-preface acceptance, discard, peer-preface receipt, and operand selection.
SelectOperand is a poll result, not a host callback: the
sans-IO carrier stays synchronous and owns no selector, and each runtime
driver answers it with select_operand from its own selector
before polling again. Rust drivers call their selector inline, Python on
the driving thread, and JavaScript awaits a returned promise. Until the
answer, the carrier has no session, effect, or legal peer input.
A connected writer owns at most one accepted batch, ending with the
direction’s flush. The codec validates the batch on acceptance, then
renders and encodes it into one bounded chunk at a time as the writer
takes bytes; an identity direction writes a record larger
than the free chunk room from the record’s own bytes. An accepted old
batch drains, then the old encoding’s end mark when it has one, before
the fresh raw preface; unaccepted old output is dropped. The fresh
preface is the next accepted output, and proof or operand bytes cannot
overtake it.
SessionState =
Establishing(EstablishmentState)
| Round(RoundState)
| FixedPoint(FixedPointState)
| Failed(SessionError)
| Closed(CloseReason)
| Owning state | Owning role action | Other role input | Result |
|---|---|---|---|
Side0Proof |
side0 emits ::as with the exact v3 proof Seal, or the
permitted unbound ::as unknown |
side1 validates shape, nonces, and local binding context, then
expected peer By |
on success retain optional peer By value; enter
Side1Proof |
Side1Proof |
side1 emits ::as with the exact v3 proof Seal, or the
permitted unbound ::as unknown |
side0 validates shape, nonces, and local binding context, then
expected peer By |
on success retain optional peer By value; enter
Side0Operand |
Side0Operand |
side0 emits ::policy h0 |
side1 holds h0 as its own operand or a cache entry within
max_operand_bytes, or marks it missing |
enter Side1Operand |
Side1Operand |
side1 emits ::policy h1, then
::policy-known if it holds h0, otherwise
::policy-missing |
side0 holds or marks h1 missing on the first item and notes whether to deliver h0 on the second | enter Side0Delivery |
Side0Delivery |
side0 emits h0’s Blob in ::policy-body if side1 sent
::policy-missing, then ::policy-known if it
holds h1, otherwise ::policy-missing |
side1 validates a requested Blob against h0, caches it, then reads the answer | if h1 is needed enter Side1Delivery; otherwise both
freeze fresh authority, derive the immutable plan and executable, and
enter Round.Advertising |
Side1Delivery |
side1 emits h1’s Blob in ::policy-body |
side0 validates it against h1 and caches it | freeze fresh authority; derive immutable plan and executable; enter
Round.Advertising |
| any establishment state | fresh peer preface | carrier consumes session and enters reset prefacing | |
| any establishment state | malformed/wrong-family input, limit, premature EOF, cancellation, or deadline | enter one typed terminal state; close carrier |
Rust establishment applies Lace-050’s proof validation before advancing the proof transitions above. Lace-060 defines the proof grammar.
Complete proof, policy announcement, policy answer, and policy-body
items, including their labels, count toward logical-session
establishment control. Proof and operands are control material, not
store admission. A direct pair bypasses establishment, derives its plan
from prepared modules and explicit authorities, and starts both
endpoints in Round.Advertising.
RoundPhase = Advertising | Requesting | Answering | Fencing
| Phase | State-local ownership |
|---|---|
Advertising |
local evaluation/output; frozen advertisement and commitments; optional peer advertisement; on a connected carrier, the session adapter’s reconciliation or delta state and one peer item slot |
Requesting |
both advertisements; local evaluation/output; frozen request set; optional peer request set |
Answering |
both request sets; incoming/outgoing obligations; bounded current response ingress; optional early peer fence |
Fencing |
local fence output; optional peer fence; both request sets |
A listing slot stores one complete listing and its change since the previous listing. The endpoint owns the session’s local listing, frozen as the previous local listing until the next round changes it; on a connected carrier the session adapter owns the previous peer listing, the entries each side requested in the previous round, the first-round reconciliation state, and the 060 delta and 061 reconciliation encodings, while the endpoint sees only complete listings with their changes. Starting a round drops all other round-local state. The two endpoints may occupy different phases.
| Local/peer progress | Result |
|---|---|
| phase entry | start one coherent local advertisement evaluation and establish the round’s local record-fact boundary |
evaluation creates StoreCall |
retain the endpoint-exclusive effect; accept no peer input until submission |
| evaluation completes | change the local listing by the held MaySend change,
loading projections only for added records, and freeze it as the round’s
commitments; expose one peer-output effect |
| direct route accepts the listing | insert the complete typed listing and its change into the sibling’s peer-listing slot |
| connected adapter accepts the listing, first round of the session | run Lace-061 reconciliation steps under this phase’s deadline; each step item, complete-listing answer, or unshared listing is one retained output batch |
| connected adapter accepts the listing, later round | encode the listing’s change as one Lace-060
::offers-changed item against the previous listing plus the
entries this side requested in the previous round |
valid peer reconciliation item or delta with no active
StoreCall |
advance reconciliation or apply the delta to the previous peer listing in place, first adding the entries the peer requested in the previous round; charge its bytes and the materialized entries to the round’s received-control counter; fill the peer-listing slot with the complete peer listing and its change once materialized |
| local output accepted and peer listing present | enter Requesting with a new phase token |
| duplicate, wrong-family, malformed, skipped, early, extra, out-of-window, or inconsistent input | typed peer failure |
| Local/peer progress | Result |
|---|---|
| phase entry | start request evaluation over the complete peer advertisement and the existing round-local record boundary |
evaluation creates StoreCall |
retain the endpoint-exclusive effect; accept no peer input until submission |
| evaluation completes | freeze request set and release the peer listing; expose one
peer-output effect; a connected adapter keeps the requested peer-listing
entries for the next delta’s base unless the plan omits this side’s
MaySend |
valid peer request with no active StoreCall |
fill peer-request slot; a connected adapter keeps the requested
local entries for the peer’s next delta’s base unless the plan omits the
peer’s MaySend |
| local output accepted and peer request present, either set nonempty | enter Answering with both directions frozen |
| local output accepted and peer request present, both sets empty | create fresh FixedPointId, boundary, and one entry
observation; the accepted request batch retains this phase’s deadline
until flushed |
| duplicate, wrong-family, malformed, or skipped input | typed peer failure |
A local commit after the round boundary freezes cannot affect either the advertisement or request snapshot in that round.
| Progress | Result |
|---|---|
| outgoing obligation | create exact-load StoreCall; accept no peer input until
submission |
| outgoing load succeeds | validate frozen commitment and expose record output |
| record output accepted | settle outgoing obligation |
| matching response received | validate record and projection; create admission
StoreCall; stop peer input until submission |
| admission returns stored or duplicate | settle incoming obligation |
| all incoming obligations settled, outgoing work remains, peer fence arrives | retain one early peer fence |
| all obligations settle | enter Fencing and expose fence output immediately |
| invalid, duplicate, unrequested, premature, or malformed input | typed peer failure |
A direct sibling endpoint may continue while this endpoint owns
StoreCall, but routing into the blocked endpoint waits.
Fencing occurs only in a round where either frozen request set is nonempty.
| Local fence | Peer fence | Result |
|---|---|---|
| not accepted | either | retain fence output |
| accepted | absent | wait |
| accepted | present | start fresh Round.Advertising |
Fixed point is entered from Requesting when both frozen
request sets are empty; a fence received for such a round is
PeerProtocol.
The machine reports:
FixedPointBoundary {
round: InterlaceRoundId,
wake_boundaries: [FixedPointWakeBoundary],
}
FixedPointWakeBoundary {
machine: MachineId,
fixed_point: FixedPointId,
position: StorePosition,
}
A connected machine reports one wake boundary; a direct pair reports one per protocol side. Same-Lace composition retains two independently tokened wake boundaries.
| Input at fixed point | Result |
|---|---|
| exact-boundary store advance | enter Advertising |
| peer listing (connected: one delta applied to the previous peer listing; the previous request sets were empty) | enter Advertising, fill peer slot, start local
evaluation |
| peer fence | Failed(PeerProtocol) |
| peer preface | carrier retires session and enters reset prefacing |
| boundary EOF | Closed(PeerClosed) |
| cancellation or Rust drop | offer a compressed direction’s end mark once to the idle writer;
Closed(Cancelled) |
A retained terminal failure from a runtime-owned Lace is host lifecycle, not a machine input: the driver retires this operation and reports the same typed failure retained by that Lace.
A position delivery must match the complete current boundary. A replacement, peer input, or newer fixed point invalidates the old identity; stale readiness is discarded. Lace terminal failure is never discarded as stale. Fixed-point entry commits the semantic result. A bounded host then stops delivering peer input and settles its route under the settled round’s Requesting deadline, as Lace-050 defines. The write rows and the read row progress concurrently:
| Bounded completion progress | Result |
|---|---|
| final batch, then a compressed direction’s end mark, written and flushed | shut down the writer |
| end-mark write fails | drop unwritten output; shut down the writer |
| until the peer direction ends | read it, decoding and discarding items |
| writer shut down and peer direction ended, end-mark write and shutdown succeeded | release IO; complete |
| writer shut down and peer direction ended at boundary EOF with no further item after a failed step | release IO; complete |
| writer shut down and peer direction ended otherwise after a failed step: item, truncation, or read failure | release IO; that failure |
| Requesting deadline expires | release IO; Timeout(Requesting) |
An endpoint owns at most one effect: StoreCall or peer
output. Only the admission of a received response starts a
StoreCall while peer output waits for the writer; the
machine then reports the StoreCall first. While
StoreCall is active, that endpoint consumes no peer input,
stages no replacement, and starts no other effect. The connected driver
races store execution, phase deadline, cancellation, and retained
terminal failure from its runtime-owned Lace. Lace failure retires the
semantic operation before already-started work can submit.
Durable-position publication or Lace poisoning and response submission
finish before ordinary polling resumes.
Every terminal transition retires active StoreCall in the semantic
owner. SessionMachine retires its connected endpoint effect
and DirectPair retires both endpoint effects;
ConnectedCarrier, hosts, and FFI have no terminal discard
input. Already-started work may retain its Lace long enough to publish a
confirmed durable mutation, but cannot submit after retirement.
A direct pair may expose one effect from each endpoint in the same
poll, including two StoreCall objects. A retained effect on
one side never hides the other. Direct replacement is staged
synchronously only through exclusive pair ownership and waits until both
endpoint store effects settle. At the next poll, before endpoint or
watch work, it atomically retires both endpoints and their plan, drops
unaccepted typed output and machine observations, and constructs a fresh
pair with the prepared operands, existing explicit authorities, and
limits.
Machine output remains borrowed until direct insertion or connected-writer acceptance. The writer retains one accepted batch, its current chunk and offset through partial writes. Replacement drains the entire accepted old batch and writes the old encoding’s end mark before writing a fresh preface. The IO adapter owns any protection/framing and delivers only authenticated plaintext when protected. Its flush completes when it has accepted ownership of preceding plaintext within bounded buffering; it then owns delivery or failure. Reads and writes progress independently while respecting legal-input and active-StoreCall gates. Terminal failure drops retained output and aborts IO immediately. The batch accepted at the transition into fixed point retains its original absolute phase deadline through full write and flush; that flush gates fixed-point observation, bounded completion and clean close.
ConnectedCarrier::peer_record_ingress directs each peer
record by the carrier’s state. The host’s codec state checks it at the
record’s start, when its canonical length is known, before any byte
reaches memory, staging or the store:
| Carrier state | Record |
|---|---|
| establishing, proof turn | into memory, at most the exact proof size |
| establishing, requested operand Blob | into memory, at most 1 MiB (PeerProtocol) and
max_operand_bytes (LimitExceeded) |
Answering with a requested response outstanding |
through the store’s record sink, within the round’s remaining
transferred-record budget (LimitExceeded) |
| locally initiated reset prefacing | validated through its framing LF and discarded, within the remaining
reset control budget (LimitExceeded); the whole item
length, label and framing LF included, is charged |
| any other state, including fixed point and bounded completion’s read to the peer end | PeerProtocol |
At most one decoded input may wait for immediate handoff. Without
active StoreCall, the carrier classifies an already decoded
item as accepted, immediately rejectable by core validation, or
backpressured; hosts do not reconstruct this from owning state. Input is
accepted only into the current state-local slot. Duplicate,
out-of-order, wrong-family, or unrequested input fails at handoff rather
than entering a semantic queue. The decoder retains item labels and
listing forms; a label or form that does not match the owning state
fails with PeerProtocol.
| State/input | Classification |
|---|---|
| adopted IO/protection error | Transport, never successful EOF |
| boundary EOF during prefacing, establishment, or active/mandatory round | PeerProtocol premature EOF |
| boundary EOF at fixed point | clean peer closure |
| EOF inside a preface, line item, list item, or record item, or before a compressed encoding’s end mark | PeerProtocol framing truncation |
| malformed encoded input or window/dictionary violation | PeerProtocol |
| phase deadline, including a stalled reconciliation, retained final-batch drain, and bounded completion | Timeout(phase) |
| local budget | LimitExceeded(limit) |
| malformed, oversized, or out-of-order peer input | PeerProtocol(detail) |
| policy, store, transport, or integration failure | corresponding typed category |
Transport establishment precedes Lace adoption and has no ILTP phase or deadline. The application owns its setup failures and cancellation. After adoption, EOF finalizes the ILTP decoder; a partial item or missing end mark still fails and only fixed-point boundary EOF is clean. Decoded canonical ILTP bytes alone count against ILTP control/transfer budgets.
Terminal handling drops retained output and staged replacement, writes no end mark, and immediately initiates carrier closure, without waiting for a peer close acknowledgment. Already platform-queued bytes may still arrive; no subsequent Lace work is submitted. Records already durably admitted remain stored. Successful fixed-point publication confirms settled transfer obligations, not permanent remote retention.