State machines

This page is generated: cargo run --release --example state_machines > docs/STATE_MACHINES.md. CI fails if it is stale.

Every edge below was observedexamples/state_machines.rs drives the real types and records the transitions they take, so an edge the code can no longer take disappears from the page. A hand-drawn diagram is a claim nobody re-checks, and this repository has shipped one of those before: the router flow said "id seen, or expired?" for months after expiry stopped existing.

These are the two machines the September audit named as unreviewed, and writing the tests that produce them turned up a live fault in each.

Double Ratchet — what happens to a received frame

stateDiagram-v2
  Established --> Refused: fails to authenticate — nothing committed
  Established --> Refused: gap past MAX_SKIP — refuse without computing
  Established --> Refused: n < nr — already consumed
  Established --> Opened: n == nr — derive from chain
  Established --> SkippedBanked: n > nr — bank the gap
  NoSendingChain --> Established: first message opens — DH ratchet
  SkippedBanked --> Refused: banked key already spent
  SkippedBanked --> Refused: older than the skip TTL — dropped
  SkippedBanked --> Opened: the banked key opens it
  [*] --> NoSendingChain: init_bob

Mix — the release queue

stateDiagram-v2
  BelowThreshold --> BelowThreshold: fewer than min_batch due — hold
  BelowThreshold --> BelowThreshold: held >= min_batch but not enough *due* — hold
  BelowThreshold --> Empty: min_batch due — shuffle and release
  Empty --> BelowThreshold: add
  [*] --> Empty: new(min_batch)

Hub — an interface's life

stateDiagram-v2
  NoInterfaces --> Sending: register
  NoInterfaces --> PullOnly: register_pull (no sender)
  PullOnly --> PullOnly: nothing is pushed to it, it answers WANT from the store
  Retired --> Retired: a flood is not delivered to it
  Retired --> Sending: a new register takes a fresh id, never this hole
  Retired --> Retired: unregister again, or on an unknown id — no-op
  Sending --> Sending: a flood reaches it
  Sending --> Retired: unregister — sender dropped
  [*] --> NoInterfaces: Hub new