Add §6.8 Channel mode (dev-experience #4)
Channel mode (CHANNEL = 0x0E context) is a continuous, bi-directional,
message-typed stream on top of an established Link. Distinct from
§11 REQUEST/RESPONSE (single-shot, client-server) and §10 Resources
(large unidirectional). NomadNet uses it for the live-channel API
beyond simple page fetches.
Six sub-sections:
§6.8.1 Wire form: 6-byte big-endian fixed-prefix header
(msgtype(2) || sequence(2) || length(2)) followed by
payload, Token-encrypted by the link session key.
§6.8.2 Reserved system types: SMT_STREAM_DATA = 0xff00.
Application-defined types stay in 0x0000..0xfeff.
§6.8.3 MSGTYPE registration: both endpoints must register
matching message classes via register_message_type
before sending/receiving that type.
§6.8.4 Reliable delivery via §6.5 PROOF + sliding window with
the same window-growth dynamics as §10 Resources.
§6.8.5 Decision matrix: REQUEST/RESPONSE for one-shot RPC,
Resources for one-shot large transfers, Channel for
continuous bi-directional streams.
§6.8.6 Source map across Channel.py.
Old §6.8 Source moved to §6.9.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f13985846c
commit
7703cee748
2 changed files with 83 additions and 3 deletions
77
SPEC.md
77
SPEC.md
|
|
@ -1155,9 +1155,82 @@ For a clean-room implementation that wants links to survive idle periods longer
|
|||
5. On every inbound `LINKCLOSE`, decrypt, verify body equals `link_id`, transition to `CLOSED`.
|
||||
6. On `CLOSED`, zero the session keys and cancel any in-progress Resources.
|
||||
|
||||
### 6.8 Source
|
||||
### 6.8 Channel mode (`CHANNEL = 0x0E`)
|
||||
|
||||
`RNS/Link.py`, `RNS/Packet.py::prove`, `RNS/Identity.py::prove`, `RNS/PacketReceipt.py::validate_proof`. The webclient's `reference/js-reference/link.js` is a faithful port.
|
||||
A Channel is a **continuous, bi-directional, message-typed stream** on top of an established Link. Distinct from §11 REQUEST/RESPONSE (single-shot, client-server) and §10 Resources (large unidirectional transfers): Channel messages are short, can flow in either direction at any time, and carry an application-defined type byte the receiver dispatches on. NomadNet uses it for its "channel" API (live chat over a Link), and any application can register custom message types via `RNS.Channel.Channel.register_message_type`.
|
||||
|
||||
#### 6.8.1 Wire form
|
||||
|
||||
A Channel message rides as a single Link DATA packet with `context = CHANNEL (0x0E)`. The body is **6-byte fixed-prefix header + variable-length payload** (`RNS/Channel.py:192-198`):
|
||||
|
||||
```
|
||||
msgtype(2) || sequence(2) || length(2) || data(length bytes)
|
||||
```
|
||||
|
||||
All three header fields are **big-endian unsigned 16-bit integers** (Python `struct.pack(">HHH", msgtype, sequence, length)`):
|
||||
|
||||
| Field | Width | Meaning |
|
||||
|---|---|---|
|
||||
| `msgtype` | uint16 BE | Application-defined message type. Distinguishes the payload schema. |
|
||||
| `sequence` | uint16 BE | Per-direction sequence number, starting at 0 and incrementing each emission. Wraps at 65536. |
|
||||
| `length` | uint16 BE | Length in bytes of the payload that follows. |
|
||||
|
||||
The whole 6-byte header + payload is the Link DATA packet's **plaintext**, which is then Token-encrypted by the link's session key (§3.1 link-derived form, no eph_pub prefix) before transmission.
|
||||
|
||||
#### 6.8.2 Reserved system message types
|
||||
|
||||
`RNS/Channel.py:45-46`:
|
||||
|
||||
```python
|
||||
class SystemMessageTypes(enum.IntEnum):
|
||||
SMT_STREAM_DATA = 0xff00
|
||||
```
|
||||
|
||||
`0xff00` is reserved for upstream's stream-over-channel implementation. Application-defined message types should stay in the `0x0000..0xfeff` range to avoid collisions with reserved system types. There's no centralized registry — each Link's `Channel` instance maintains its own `message_factories` dict mapping `msgtype` to a constructor.
|
||||
|
||||
#### 6.8.3 MSGTYPE registration
|
||||
|
||||
Both endpoints of a Link must register matching message types via `Channel.register_message_type(message_class)` before they can send or receive that type. The constructor must implement:
|
||||
|
||||
```python
|
||||
class MyMessage(MessageBase):
|
||||
MSGTYPE = 0x1234 # uint16 in [0x0000, 0xfeff]
|
||||
def pack(self) -> bytes: ...
|
||||
def unpack(self, raw: bytes): ...
|
||||
```
|
||||
|
||||
A receiver that gets a `msgtype` it didn't register raises `ChannelException(ME_NOT_REGISTERED)` and drops the message. A sender that tries to send a class without `MSGTYPE` defined raises `ChannelException(ME_NO_MSG_TYPE)`.
|
||||
|
||||
#### 6.8.4 Reliable delivery
|
||||
|
||||
Channel uses the standard §6.5 mandatory PROOF receipt mechanism for each message — every Channel DATA packet generates a PROOF, and the sender's `Channel` retries on timeout up to a per-packet limit. Reliability is the main reason to use Channel over plain Link DATA: the application doesn't need to implement its own retransmit logic.
|
||||
|
||||
The receiver-side `Channel` uses a **sliding window** with the same window-growth dynamics as §10 Resources (`Channel.WINDOW = 2` initial, with rate-thresholded growth/shrink). Sequence numbers in the channel header let the receiver detect gaps and request retransmits; out-of-order arrivals are buffered until the gap fills.
|
||||
|
||||
#### 6.8.5 When to use Channel vs the alternatives
|
||||
|
||||
| Use case | Best mechanism |
|
||||
|---|---|
|
||||
| One-shot small request → one-shot small response | §11 REQUEST/RESPONSE |
|
||||
| One-shot large transfer (file, page) | §10 Resource |
|
||||
| Continuous bi-directional small messages (chat, telemetry stream, command/event flow) | §6.8 Channel |
|
||||
| Continuous bi-directional large transfers | §10 Resources sequenced in time, OR Channel with `SMT_STREAM_DATA` chunks |
|
||||
|
||||
A clean-room client that only implements opportunistic LXMF can ignore Channel entirely. NomadNet-aware clients need it for the channel API; custom-RPC applications may prefer it over §11 for its bi-directional nature.
|
||||
|
||||
#### 6.8.6 Source map
|
||||
|
||||
| File | What |
|
||||
|---|---|
|
||||
| `RNS/Channel.py:174-211` | `Envelope` — wire-form pack/unpack |
|
||||
| `RNS/Channel.py:214-...` | `Channel` — windowed reliable delivery on a Link |
|
||||
| `RNS/Channel.py:45-46` | `SMT_STREAM_DATA = 0xff00` reserved system type |
|
||||
| `RNS/Channel.py:317-325` | `register_message_type` — per-Link MSGTYPE dispatch table |
|
||||
| `RNS/Packet.py:86` | `CHANNEL = 0x0E` context constant |
|
||||
|
||||
### 6.9 Source
|
||||
|
||||
`RNS/Link.py`, `RNS/Packet.py::prove`, `RNS/Identity.py::prove`, `RNS/PacketReceipt.py::validate_proof`, `RNS/Channel.py`. The webclient's `reference/js-reference/link.js` is a faithful port.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue