Normative · Part
Errors, limits and timers
Every bound below is normative. Each links to the mechanism it constrains, because a constant without its mechanism is not implementable.
35. Fail-closed inventory
Section titled “35. Fail-closed inventory”The single most important table in this specification: every boundary where SSP/1 chooses safety over availability.
| Condition | Behaviour | Defined in |
|---|---|---|
| Invalid event signature | Drop and surface — never hold, so a forger cannot stall the impersonated node’s stream | semantics |
Unknown sender kid |
Drop and surface | data model |
| Unknown critical COSE header or content type | Reject | registry |
| Clock skew beyond bound | Reject, never clamp | data model |
| Watermark advance past a skew-rejected event | Never advance — the rejected HLC is above every honest one, so advancing would acknowledge the site’s whole unapplied backlog | data model |
| No HLC high-water mark covering every event the node has released, or a mark that fails its integrity check | Refuse to stamp further events under that site_id — never resume from the wall clock. Surface the two as distinct conditions |
data model |
| Out-of-scope event | Drop and log, never raise | semantics |
row_key null, empty, or naming any column the destination lacks |
Drop — never partially resolve a key, which would address every row sharing the surviving prefix | semantics |
| No matching policy row | Deny | scope |
| Incomparable maximal policy rows | Deny and log, never guess open | scope |
| Purge requested | Error, never downgrade to soft delete | scope |
Identity conflict on existing site_id |
Hard fail, never overwrite | identity |
| Pairing signature failure | Abort, no downgrade | identity |
| Pairing ceremony deadline exceeded | Abort — never extend the deadline, and never resume the ceremony that ran past it | identity |
Node below a policy-required key_protection level |
Refuse, never admit at a lower level | identity |
hardware_attested claimed with absent, malformed, expired, revoked or mismatched evidence |
Treat as software — never as hardware_unattested |
SOP/1 §5.2 |
| Attestation revocation data unobtainable or stale beyond the configured bound | Treat the attestation as unverified | SOP/1 §5.2 |
| Non-compressed encoding of a node root public key | Reject — never convert | signature profile |
| Non-low-S signature | Reject — never normalise | signature profile |
| DER-encoded or non-64-byte signature | Reject | signature profile |
| Public key failing point validation | Reject before use | signature profile |
| Hardware signer’s verify-after-sign check fails | Abort — never release the signature | SOP/1 §5.4 |
| Conformance claim naming a module identifier the registry does not register | Reject the claim — never ignore the identifier and evaluate the rest | registry |
supports_realtime: true about to be advertised by an implementation that does not claim SOVM-SYNC-RT, or on a binding that cannot carry unsolicited one-way bodies |
Advertise false — never advertise a capability not held |
messages |
Unknown ssp_version |
Refuse the exchange | messages |
| Malformed sync body | Reject | messages |
Unsolicited push without mutual supports_realtime |
Reject | messages |
| NaN or infinity in canonical CBOR | Refuse to serialize | data model |
Eight of the signature-path rows exist because a randomised signature algorithm with an ambiguous public-key encoding has more boundaries than a deterministic one with a unique encoding — the honest cost of a hardware-rooted identity, not a hidden one. The two conformance-claim rows are where the module-set assertion of registry §55.1 becomes checkable on the wire, because an assertion nothing checks is not an assertion.
The one deliberate exception is eviction, which fails open and keeps the row. The asymmetry is the justification: failing open wastes local storage, failing closed destroys data.
Volume is deliberately absent from the table, and its absence should not read as an oversight. An authorized peer that floods a receiver with well-formed events — including the existence-only upserts that semantics §13.6 makes cheap to produce — crosses no boundary at all: it is inside the trust boundary, its signatures verify, and every row above passes it through. Fail-closed has nothing to close on. The bound is §37 instead, whose receiver-side limits — materialization batch size and backfill rows per cycle — a receiver enforces on its own authority rather than trusting the sender’s events-per-response, together with whatever rate control the binding supplies.
36. Normative bounds
Section titled “36. Normative bounds”| Bound | Value | Rationale |
|---|---|---|
| Clock skew tolerance | 300 000 ms | Bounds a hostile clock’s ability to win every future comparison |
| Vouch validity | 86 400 s | Long enough to seat a node across a brief outage; short enough to bound replay |
| Vouch future-dating tolerance | 300 s | Accommodates ordinary clock drift between honest nodes |
| Vouch quorum k | 2 where the mesh has 3+ nodes, else 1 | A quorum is meaningless below three participants |
| SAS strength | ~39.9 bits | log₂(10¹²); see SAS |
| Fingerprint display | First 4 bytes of site_id |
Display aid only, never an authentication input |
37. Implementation-chosen limits
Section titled “37. Implementation-chosen limits”These MUST be documented but are not fixed by the protocol, because the right value depends on deployment rather than on correctness.
| Limit | Constraint |
|---|---|
| Events per sync response | Finite; MUST be enforced by the sender |
| Materialization batch size | Finite |
| Backfill rows per cycle | Finite |
| Sync cycle interval | The retry unit; there is no separate backoff protocol. MUST keep running in realtime mode |
| Realtime push cadence | Implementation- or policy-chosen; a fixed cadence trades latency for timing-channel flatness |
| HLC reservation window | Finite, and below the clock skew bound with margin. A node resumes at the reserved physical component, so a window at or above the bound resumes it into a value its peers MUST reject |
| Mailbox retention | Finite and stated |
| Mailbox quota | Optional; MUST reject rather than silently drop |
| KeyPackage stock size | The number of single-use packages a node maintains in the §25.2 directory |
| KeyPackage replenishment low-water mark | The stock level below which a node MUST replenish; MUST be above zero so last-resort is not the first response to a depleted stock in the normal case |
| Pairing ceremony deadline | Finite, fixed when the machine enters idle, and spanning the whole ceremony rather than restarting per transition. The only limit here gated on human reaction time rather than on a round trip, which is why nothing on the wire carries it and why too tight a value is its failure mode |
38. Error handling posture
Section titled “38. Error handling posture”SSP/1 distinguishes three responses, and conflating them is the common implementation error:
- Reject — refuse this item, surface it, retain nothing, continue processing others.
- Hold — refuse this group for now, do not advance the watermark past it, retry next cycle. For conditions that may resolve.
- Drop — discard permanently, advance the watermark. For conditions that will not resolve, and for anything a hostile peer could otherwise use to stall the apply loop.
Hold and drop each settle the watermark; reject does not settle it on its own, so every rejecting condition MUST state its own. A rejection that is a property of the bytes alone can never resolve, so the watermark advances past it. The clock skew bound is the one rejection that turns on receiver-local state rather than on the bytes — it resolves as the receiver’s clock advances — so there the watermark does not advance and the next re-offer succeeds.
An implementation MUST NOT raise an exception that halts the apply loop on peer-supplied input. A single malformed event from one peer must not stop replication with every other peer.