Usenet semantics
with SMS economics.
Core thesis: the mesh carries not the messages but the claim of existence. That turns the radio budget into a function of the event rate, not the data volume. MAUS/ZConnect semantics, IPFS data model, Reticulum transport. Guiding principle: robustness wins.
The problem
No system exists that carries structured, threaded, asynchronous discussion over radio. Existing approaches each cover one half.
| Approach | Can do | Missing |
|---|---|---|
| Meshtastic | transport | channels without threading, without IDs, without history |
| Meshtastic Store & Forward | buffering | no board structure, no subscriptions |
| TC²-BBS / meshing-around | BBS semantics | no differential sync, no duplicate detection |
| MeshCore | room server with history | no subscription model |
What's missing is exactly what MausTausch/ZConnect had solved from 1986 on: globally unique message IDs, reference chains (REF:) for thread reconstruction without full history, duplicate detection, and selective per-board subscriptions. Only then the cost function was the phone bill — today it's airtime.
Two message types, two economies
The incentive for short messages is audience, not price. Whoever posts a BEACON isn't read by mesh-only participants. Social sanction, no enforcement needed.
| INLINE | BEACON | |
|---|---|---|
| Content | 140 chars, in the packet | teaser + CID, full text on the fat net |
| Size | ~76 B | ~108 B |
| Mesh cost, author | 1 packet | 1 packet |
| Mesh cost, reader | 0 | 1 packet + fetch |
| Readable without internet | ✓ yes | ✗ no |
| Reach | all participants | fat-net class only |
A board is a rule-space
Not just sorting — it declares the economy in force. Policy is a signed IPLD object; the node checks incoming messages against it. Violation = drop, no relay.
/DE/COMP/MESH/CHAT accept: INLINE /DE/SCI/PAPERS accept: INLINE, BEACON | max_size: 512K /DE/REGIO/BERLIN accept: INLINE, BEACON | max_size: 64K | beacon_quota: 3/day/author
Architecture decisions (fixed)
| # | Decision | Consequence |
|---|---|---|
| A4 | Everyday network (not emergency) | Retention, moderation, identity are mandatory. Hubs need 24/7 + solar. |
| A3 | Teaser flows into the content CID | Beacon tamper-proof. Forgery detectable only on fetch → relay reputation needed. |
| A2 | LXMF / Reticulum as transport | Crypto + routing for free. Duty-cycle scheduling not included → build our own. |
| A1 | Duty cycle 1% (no LBT/AFA) | ~720 packets/day/node at SF11. Hub fan-out hard-capped at ~8–10 neighbours. |
| B1 | Single parent | Robustness over expressiveness. Multi-parent rejected. |
| B2 | INLINE/BEACON at board level | Educates rather than flexibilises. |
| B3 | Merkle batch signatures | No BLS/pairing crypto on the microcontroller. |
| B4 | Full CIDv1 (32 B) | +20 B/beacon, but real IPFS interop with no mapping layer. |
The stack
┌──────────────────────────────┐ │ NNTP frontend │ ← any newsreader ├──────────────────────────────┤ │ Board logic, subs, retention │ ← new (brettd) ├──────────────────────────────┤ │ IPLD dag-cbor + CIDv1 │ ← IPFS data model ├──────────────────────────────┤ │ Minisketch sync │ ← Bitcoin Erlay ├──────────────────────────────┤ │ Reticulum LXMF │ ← transport, crypto ├──────────────────────────────┤ │ LoRa PHY │ └──────────────────────────────┘
IPFS: what we take
CIDv1 / content addressing and IPLD / dag-cbor — the reference tree is a Merkle DAG. Pinning as a subscription model formally defines the hub role: a hub is a pinning service with an airtime budget. CID references for long-form are a free internet bridge.
What we don't: Kademlia DHT (O(log n) round-trips of 3 s = minutes per lookup), libp2p/IPNS (handshake blows the budget). Bitswap only on the fat net. The break between radio and net sits exactly at the layer boundary where IPFS already has it.
Wire format
{
b: h'a3f1', // 2 board
c: CID(32), // 34 content address
p: CID(32), // 34 parent (single)
a: h'9c2e4b81', // 4 author prefix
t: 29184301, // 4 ts, minutes
s: 3400, // 2 size hint
x: h'...' // ~28 teaser, ~40 chars
}
The CID is the integrity guarantee
No signature in the beacon — author authenticity comes from the content block (room there for a 64 B Ed25519). INLINE (~76 B): the body is the block, no content CID needed — much cheaper than BEACON.
Compression
For 140-character strings a pre-trained dictionary is the entire compression — LZ77 finds nothing in 140 bytes. The character limit is thus a derived quantity: the boundary is in bytes after compression, the limit in characters before. inline_max follows from measurement M3, not the other way round.
A dictionary is a content-addressed IPLD object (not a central enum), carries a 4-bit index in the header for self-description, is a domain object rather than a language object, and the compressor never rejects — mixed language just compresses worse, that's all. Physics sanctions, not policy.
Sync: Minisketch instead of push
ZConnect/Fido used push with watermarks — which assumes ordered, reliable connections. LoRa has neither ordering nor reliability. Instead: set reconciliation over IBLT/Minisketch.
Node A Node B
|-- SYNC_REQ(board, sketch[128B], T-7d) ->|
| | decode(sketch_A XOR sketch_B)
|<-- SYNC_RESP(want={CID5}, send={CID3,7}) |
|-- BEACON(CID5) ------------------------->|
|<-- BEACON(CID3), BEACON(CID7) -----------|
A sketch of 128 B reconstructs a difference of up to ~8 entries — regardless of whether the board holds 100 or 100,000 messages. A full weekly reconciliation at typical difference: ~7 packets ≈ 20 s airtime at SF11. One node keeps dozens of boards in sync within the daily budget.
The pager state machine
BEACON received (mesh) │ ├─ board subscribed? ──no──→ DROP ├─ CID already local? ──yes─→ DROP (dupe) ▼ WANTLIST += CID │ [wait. minutes. hours. days.] ▼ fat net up? ──no──→ teaser stays visible ▼ BULK FETCH → IPFS gateway / hub ▼ verify CID → Ed25519 → into DAG → full text
| State | Channel | Experience |
|---|---|---|
| Mesh only | LoRa | sees that a discussion happens, plus a snippet |
| Mesh + gateway | LoRa + uplink | full text on demand, with a wait |
| Fat net | Wi-Fi/LTE | a normal forum |
The CID is the same in all three states — no merge, no versioning, the DAG only grows. The beacon is a promise, the CID is the contract.
Topology
[uplink gateway] ←── internet / IPFS │ (LoRa, SF7, roof antenna) ┌─────┴─────┐ [hub west] [hub east] ← S&F, 24/7, solar, max 8 │ │ │ │ [N] [N] [N] [N] ← endnodes, opportunistic
The hierarchy isn't convenience, it's the cost function. A flat mesh that floods every message everywhere burns the budget quadratically. Fido had zones/nets/nodes for the same reason — only then the currency was the long-distance call.
Consequence of A1 and the fan-out cap: a city network needs many hubs, not big ones.
Duty-cycle governor: LXMF vs. 1%
LXMF/Reticulum knows link establishment, retries, announces — but no airtime accounting. The RNode layer drops instead of delaying. Exactly wrong: a dropped beacon is lost, a delayed one arrives. LXMF is opportunistic, duty cycle is deterministic — incompatible scheduling philosophies.
| Endnode | Hub | |
|---|---|---|
| Neighbours | 1–2 | up to 8 |
| Send volume | low | at the limit |
| Uptime | sporadic | 24/7 |
| Solution | LXMF vanilla | DutyCycleInterface |
At the layer boundary where airtime physically arises. Sees every packet, including Reticulum's own traffic:
Why not a governor above LXMF: Reticulum's own traffic (announces, path requests, handshakes) eats roughly 20–40% of the budget at 8 neighbours — a governor above it manages 60% and believes it's 100. The price of A2 is this governor — home-built, and the likeliest place for field-test surprises.
Governance
| # | Question | Decision |
|---|---|---|
| C1 | Who may be a hub? | Operator list + web of trust. Open won't do: 8 neighbour slots are a Sybil target. |
| C2 | Retention | Per board in the policy: retain: 180d / retain: pinned. The hub enforces. |
| C3 | Rate limiting | Token bucket per author prefix, in the protocol. At 720 packets/day it's existential. |
| C4 | Who creates boards? | A board exists once a hub pins its policy. Fido's zone coordinator without the title. |
MVP scope & measurements
M1 — announce overhead
2× RNode, 24 h, 8 simulated neighbours. Without this number every budget is a guess. < 15% → a governor above LXMF is defensible; above → DutyCycleInterface has no alternative.
M2 — RNS timeout tolerance
How long may an interface delay before links break? Determines the maximum queue depth.
M3 — compression bake-off
Brotli vs. Zstd --train vs. token-Huffman. Metric: rate at the 5th percentile. Determines the compressor and inline_max. Provisional pick: Zstd.
Known risks
| Risk | Assessment |
|---|---|
| A2 + A1 is the sore spot | LXMF knows nothing of duty cycle. The governor is home-built. |
| Fan-out 8 | Limits topology more than physics does. Many small hubs, not few big ones. |
| Asymmetric visibility | Two classes in one discussion. Someone replies to a teaser without having read the full text. Culturally tricky. |
| Wantlist growth | 3 months without the fat net = thousands of CIDs. Needs prioritisation + expiry. |
| No real time | Neighbouring board in minutes, distant in hours. MAUS-1991 behaviour — duty cycle today instead of modem latency. |