diff --git a/README.md b/README.md index abf0f38..2799765 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@
-
+
Keeping one contact alive — through framing, sessions, and a change of transport.
-
-
-
-
+ Machine contact lifecycle, framing, sessions and transport migration for the
+ Machine Contact Layer (MCL): move a live machine-to-machine contact between
+ BLE, IP and acoustic links without starting over. Freestanding C99.
- Use the SDK instead ·
- Specifications ·
- mcl-wire
+
+
+
+
+ SDK · + MCL overview · + Specifications · + mcl-wire · + Report a defect +
-## Why this exists +--- A contact that dies when the radio changes is not a contact. MCL Link is what survives the change: framing, session identity, negotiation, refusal, and migration from one transport to another without starting over. -This is the layer every binding maps onto, which is why a `HAZARD` means the -same thing over a loudspeaker and over UDP. In one physical campaign a single logical -contact was preserved across **104 physical-medium changes**, including 100 -alternating BLE/IP migrations. - -**Link major 1 is Stable.** That migration campaign carried major-0 traffic; -Wire 1 inside Link 1 is evidenced separately, decoded over air on an embedded -target. - -It is transport-profile neutral. MCL-AP, MCL-IP, MCL-BLE, MCL-UWB, and future bindings implement the same link/session contract through different physical transports. - -## Scope - -- contact discovery lifecycle -- ephemeral node/session identifiers -- framing and multiplexing -- request/reply timing -- capability exchange -- transport/profile negotiation -- session establishment and teardown -- QoS and semantic priority handling -- replay/freshness hooks -- fallback and transport handoff -- extension negotiation +Every transport binding — [IP](https://github.com/machine-contact-layer/mcl-ip), +[BLE](https://github.com/machine-contact-layer/mcl-ble), +[acoustic](https://github.com/machine-contact-layer/mcl-ap), +[UWB](https://github.com/machine-contact-layer/mcl-uwb) — implements this same +link and session contract, which is why an object means the same thing over a +loudspeaker and over UDP. On real hardware, one logical contact was carried +across **104 changes of medium**, including 100 alternating BLE/IP migrations. + +> **Building a product?** Start with [**mcl-sdk**](https://github.com/machine-contact-layer/mcl-sdk), +> which drives this layer for you. Come here for the frame layout, the state +> machines and the migration protocol. + +## What MCL Link provides + +- **The Link frame** — frame class, session reference, sequence, addressing and + a frame check, with nine Stable frame classes +- **Contact lifecycle** — discovery, capabilities, negotiation, establishment, + close +- **Transport negotiation** — agreeing which bearer a contact moves to +- **Transport migration as an on-wire protocol** — offer, accept, path + validation, commit and confirm +- **Endpoint rendezvous** — resolving an `endpoint_token` to a real endpoint on + the candidate transport +- **Deterministic refusal** — malformed, truncated, reserved and future-major + frames are rejected before any semantic decoding ## Two state machines, and neither derives the other -A node holds two, and they answer different questions. Conflating them is the -mistake this section exists to prevent. +A node holds two, and they answer different questions. -**`mcl_link_t` — the protocol lifecycle.** Nine states, defined normatively in +**`mcl_link_t` — the protocol lifecycle.** Nine states, defined in [`spec/link-v0.md`](spec/link-v0.md): ```text @@ -77,7 +75,7 @@ IDLE → DISCOVERED → CAPABILITIES → NEGOTIATING → ESTABLISHED ``` **`mcl_contact_t` — transport continuity.** Which medium carries this contact, -and is a change of medium under way: +and whether a change of medium is under way: ```text ACTIVE → OFFERED → AGREED → VALIDATING → VALIDATED → COMMITTING → ACTIVE @@ -85,101 +83,92 @@ ACTIVE → OFFERED → AGREED → VALIDATING → VALIDATED → COMMITTING → AC CLOSED ``` -**Neither implies the other, and neither is derived from the other.** A machine -can be settled on a transport having negotiated nothing; it can be -mid-negotiation with no migration in sight. An earlier revision provided a -function claiming a mapping between them, and it was removed: a third source of -truth beside two independently mutable ones drifts from both. +Neither implies the other. A machine can be settled on a transport having +negotiated nothing; it can be mid-negotiation with no migration in sight. -They cross in exactly **one** place, and the SDK owns it — a migration may only -be driven while the lifecycle is `ESTABLISHED` or `HANDOFF`, and the lifecycle -may not leave those states while a migration is outstanding. Recorded in +They cross in exactly one place, and the SDK owns it: a migration may only be +driven while the lifecycle is `ESTABLISHED` or `HANDOFF`, and the lifecycle may +not leave those states while a migration is outstanding. See [`spec/link-contact-ownership-v0.1.md`](spec/link-contact-ownership-v0.1.md). -Sending and receiving ordinary frames is **not** gated on the lifecycle. First -contact necessarily happens before establishment, and a layer whose first frame -required an established session could never send one. - -Acoustic-specific sounding and spectrum convergence are defined in `mcl-ap`, not here. - -## Security research track - -MCL's normal condition is that neither machine yet has a reason to trust the -other, and its first-contact medium is assumed observable. Nothing in MCL today -provides confidentiality, authenticity, or peer authentication, and no part of -the codebase implies otherwise. - -Two research notes state the problem before any mechanism is chosen: - -- [`research/secure-contact-threat-model.md`](research/secure-contact-threat-model.md) - — what a secure contact must withstand, which security properties are distinct - and must stay distinct, and what is already true in the code. -- [`research/secure-contact-candidate.md`](research/secure-contact-candidate.md) - — prior art worth adopting rather than reinventing, and the one property - (contact continuity across a transport change) that is MCL's own to define. -- [`research/contact-continuity-experiment.md`](research/contact-continuity-experiment.md) - — the next experiment, and the eight attacks it has to survive. - -The central result so far is a negative one: **proving knowledge of the contact -transcript proves nothing**, because first contact is observable and any -listener can compute the same value. A continuity proof must depend on secret -state both peers committed *during* the contact, which forces the key exchange -to begin on the first medium and finish on the second. - -The Link frame's `frame_check` is a CRC-32. It detects accidental corruption and -provides no protection against a deliberate modification. It is named so that it -cannot be mistaken for a cryptographic mechanism. - -## Migration as an on-wire protocol - -`TRANSPORT_OFFER` and `TRANSPORT_ACCEPT` are Wire semantic objects with -canonical bytes. The rest of the migration — `PATH_CHALLENGE`, -`PATH_RESPONSE`, `COMMIT`, `CONFIRM` — belongs to Link, because it describes -this Link's own change of transport and means nothing outside it. - -- [`spec/link-handoff-control-v0.1.md`](spec/link-handoff-control-v0.1.md) — - the normative bytes -- [`registries/handoff-ops-v0.1.json`](registries/handoff-ops-v0.1.json) — - the operation registry -- [`conformance/vectors/handoff-v0.1.json`](conformance/vectors/handoff-v0.1.json) - — positive and negative vectors -- [`include/mcl/handoff.h`](include/mcl/handoff.h) — the codec -- [`include/mcl/endpoint_rendezvous.h`](include/mcl/endpoint_rendezvous.h) — resolving the - `endpoint_token` to a real endpoint on the candidate transport +Sending and receiving ordinary frames is **not** gated on the lifecycle: first +contact necessarily happens before establishment. + +## Transport migration on the wire + +`TRANSPORT_OFFER` and `TRANSPORT_ACCEPT` are Wire objects with canonical bytes. +The rest of the migration belongs to Link, because it describes this Link's own +change of transport: + +```text +TRANSPORT_OFFER / TRANSPORT_ACCEPT current transport +PATH_CHALLENGE / PATH_RESPONSE candidate transport +COMMIT / CONFIRM candidate transport +``` -Until this existed the four controls were local function calls, and the sequence -diagram in `contact.h` named four messages that had no representation on any -wire. **A hardware demonstration driven by direct calls to `mcl_contact_*` on -both machines proves the radios work, not that the migration is specified.** +- [`spec/link-handoff-control-v0.1.md`](spec/link-handoff-control-v0.1.md) — the normative bytes +- [`registries/handoff-ops-v0.1.json`](registries/handoff-ops-v0.1.json) — the operation registry +- [`conformance/vectors/handoff-v0.1.json`](conformance/vectors/handoff-v0.1.json) — positive and negative vectors +- [`include/mcl/handoff.h`](include/mcl/handoff.h) — the codec +- [`include/mcl/endpoint_rendezvous.h`](include/mcl/endpoint_rendezvous.h) — endpoint token resolution -Two decisions worth knowing before reading the spec: +Two design decisions worth knowing before you implement it: - **There is no ABORT.** Negative outcomes are expressed by absence and by the caller-enforced validity of the offer, because this library has no clock. An abort would add a faster path to a state the timeout already reaches, and one - an observer of the references could send. -- **A lost `CONFIRM` is repaired by retransmission, not by abort.** Without it, a - single dropped frame leaves one peer on the new transport and the other back - on the old one, permanently, with no adversary involved. See §8 of the spec - and `mcl_contact_commit_repeat`. + an observer could send. +- **A lost `CONFIRM` is repaired by retransmission.** Otherwise a single dropped + frame would leave one peer on the new transport and the other on the old one. + See §8 of the specification and `mcl_contact_commit_repeat`. -## Status +Once `COMMIT` is transmitted there is no rollback. -**Link major 1 is cut and is part of MCL v1.0.** What major 1 freezes is the -class dispositions in +## Stable surface: Link major 1 + +Link major 1 is Stable. What it freezes is the class dispositions in [`spec/link-class-disposition-v1.md`](spec/link-class-disposition-v1.md) — nine -Stable frame classes, `ADAPT` permanently reserved and refused — not the frame -layout, which is byte-identical to major 0. The bump exists because the -project's version policy reserves major 0 for pre-standard work. - -`mcl_link_frame_encode` still emits major 0, because v1.0 promises source -compatibility and silently moving an existing call to a new major would break -it invisibly. Use `mcl_link_frame_encode_at_major` to choose. - -Other documents in this repository have their own explicit dispositions. -[`spec/link-v0.md`](spec/link-v0.md), -[`spec/link-negotiation-v1.md`](spec/link-negotiation-v1.md), and the contact -ownership and handoff-control specifications are Stable for their stated -major-1 scope; research documents remain below Stable. -`mcl-core/SPECIFICATION_INDEX.md` is the per-document answer, generated from -the tree rather than written by hand. +Stable frame classes, with `ADAPT` permanently reserved and refused. The frame +layout itself is byte-identical to major 0. + +**Choosing the major.** `mcl_link_frame_encode` still emits major 0, because +v1.0 promises source compatibility. Use `mcl_link_frame_encode_at_major` to emit +the Stable major. + +## Security + +MCL Link provides no confidentiality, authenticity or peer authentication. +`frame_check` is a CRC-32: it detects accidental corruption and provides no +protection against deliberate modification. `session_ref` correlates frames to +a conversation; it does not identify the peer. See +[`SECURITY.md`](https://github.com/machine-contact-layer/mcl-core/blob/main/SECURITY.md). + +Design notes on secure contact across a transport change are in +[`research/`](research/). + +## Specifications + +| Document | Maturity | +|---|---| +| [`spec/link-v0.md`](spec/link-v0.md) | Stable for the Link frame layout at major 1 | +| [`spec/link-class-disposition-v1.md`](spec/link-class-disposition-v1.md) | Stable | +| [`spec/link-negotiation-v1.md`](spec/link-negotiation-v1.md) | Stable | +| [`spec/link-contact-ownership-v0.1.md`](spec/link-contact-ownership-v0.1.md) | Stable for the contact lifecycle and ownership rule | +| [`spec/link-handoff-control-v0.1.md`](spec/link-handoff-control-v0.1.md) | Stable for the migration control sequence | +| [`registries/transport-ids-v0.1.json`](registries/transport-ids-v0.1.json) | Transport identifiers | + +Other documents in `spec/` are Research Drafts. The per-document answer is +[`mcl-core/SPECIFICATION_INDEX.md`](https://github.com/machine-contact-layer/mcl-core/blob/main/SPECIFICATION_INDEX.md). + +## Related repositories + +[mcl-wire](https://github.com/machine-contact-layer/mcl-wire) (canonical bytes) · +[mcl-sdk](https://github.com/machine-contact-layer/mcl-sdk) (developer SDK) · +[mcl-ip](https://github.com/machine-contact-layer/mcl-ip) · +[mcl-ble](https://github.com/machine-contact-layer/mcl-ble) · +[mcl-ap](https://github.com/machine-contact-layer/mcl-ap) · +[mcl-uwb](https://github.com/machine-contact-layer/mcl-uwb) + +## License + +Apache-2.0. See [`LICENSE`](LICENSE).