refactor(rfc): RFC-0910 v0.2.0 - Zenoh-only architecture
This commit is contained in:
parent
435419ced4
commit
2de0089cb7
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
## The Submarine's Nervous System
|
## The Submarine's Nervous System
|
||||||
|
|
||||||
**Version:** 0.1.0
|
**Version:** 0.2.0
|
||||||
**Status:** DRAFT
|
**Status:** DRAFT
|
||||||
**Layer:** L0-L1 (Transport — Internal Process Communication)
|
**Layer:** L0-L1 (Transport — Internal Process Communication)
|
||||||
**Class:** ARCHITECTURAL
|
**Class:** ARCHITECTURAL
|
||||||
|
|
@ -13,209 +13,214 @@
|
||||||
|
|
||||||
## 0. ABSTRACT
|
## 0. ABSTRACT
|
||||||
|
|
||||||
This document specifies the **First-Class Messaging System** for internal process communication within Libertaria nodes. It introduces a dual-plane architecture using **Zenoh** for the data plane (content-routed pub/sub) and **NNG (nanomsg-next-generation)** for the control plane (pattern-oriented messaging).
|
This document specifies the **First-Class Messaging System** for internal process communication within Libertaria nodes. After architectural review, we converge on **Zenoh-only** (via zenoh-pico) as the unified messaging layer.
|
||||||
|
|
||||||
**Key Principles:**
|
**Key Principles:**
|
||||||
- **No-broker sovereignty:** No Kafka, no RabbitMQ, no central daemon
|
- **No-broker sovereignty:** No Kafka, no RabbitMQ, no central daemon
|
||||||
- **Kenya compliance:** ~50-200KB footprint per library
|
- **Kenya compliance:** ~50KB footprint (zenoh-pico)
|
||||||
- **Layered encryption:** LWF encryption (RFC-0000) overlays both transports
|
- **Unified semantics:** Everything is a key-space query
|
||||||
- **Pattern + Content:** Structural patterns (NNG) + namespace routing (Zenoh)
|
- **Pattern unification:** PUB/SUB, REQ/REP, SURVEY all map to `z_get()` with different key patterns
|
||||||
|
- **Single library:** One mental model, one failure mode, one update cycle
|
||||||
|
|
||||||
|
**Correction from v0.1.0:** Dual-plane (Zenoh + NNG) was rejected. For solo development, two libraries = two failure modes = too much complexity. Zenoh's key-space query unifies all messaging patterns.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1. MOTIVATION
|
## 1. MOTIVATION
|
||||||
|
|
||||||
### 1.1 The Gap
|
### 1.1 The Gap
|
||||||
Libertaria L0 (UTCP, LWF) handles *inter-node* communication. But *intra-node* communication—chatter between Membrane Agent, Feed processor, Sensor Oracle, and cognitive streams—lacks a first-class solution.
|
Libertaria L0 (UTCP, LWF) handles *inter-node* communication. But *intra-node* communication—between Membrane Agent, Feed processor, Sensor Oracle, and cognitive streams—lacks a first-class solution.
|
||||||
|
|
||||||
WebSockets are inappropriate (HTTP upgrade semantics where HTTP has no business). Raw TCP is too low-level. We need:
|
WebSockets are inappropriate (HTTP upgrade semantics where HTTP has no business). Raw TCP is too low-level. We need:
|
||||||
- **Brokerless operation** (sovereignty requirement)
|
- **Brokerless operation** (sovereignty requirement)
|
||||||
- **Pattern expressiveness** (REQ/REP, PUB/SUB, PIPELINE)
|
- **Unified patterns** (PUB/SUB, REQ/REP, SURVEY as one semantic)
|
||||||
- **Content-based routing** (subscribe to `sensor/berlin/pm25/**`)
|
- **Content-based routing** (subscribe to `sensor/berlin/pm25/**`)
|
||||||
- **Kenya compliance** (embedded-friendly footprint)
|
- **Kenya compliance** (embedded-friendly footprint)
|
||||||
|
|
||||||
### 1.2 The Dual-Plane Insight
|
### 1.2 The Zenoh-Only Insight
|
||||||
No single library satisfies all constraints. The solution is architectural:
|
After evaluating dual-plane (Zenoh + NNG), we reject it. **Two libraries = two failure modes = too much complexity for solo development.**
|
||||||
|
|
||||||
| Plane | Library | Use Case | Pattern |
|
The key realization: **Zenoh's key-space query unifies all messaging patterns.**
|
||||||
|-------|---------|----------|---------|
|
|
||||||
| **Data** | Zenoh | Sensor readings, Feed posts, economic signals | Content-routed pub/sub |
|
|
||||||
| **Control** | NNG | Agent negotiation, membrane stages, health checks | Pattern-oriented messaging |
|
|
||||||
|
|
||||||
> **Law: Zenoh is the data plane. NNG is the control plane. The submarine controls its own hull.**
|
| Pattern | Zenoh Equivalent |
|
||||||
|
|---------|------------------|
|
||||||
|
| PUB/SUB | `z_subscribe("sensor/berlin/pm25/*")` |
|
||||||
|
| REQ/REP | `z_get("query/qvl/trust/did:xxx")` (specific key) |
|
||||||
|
| SURVEY | `z_get("health/**")` with timeout (wildcard + aggregation) |
|
||||||
|
|
||||||
|
> **Law: Everything is a key-space query. The pattern is in the key, not the library.**
|
||||||
|
|
||||||
|
### 1.3 Why Not NNG?
|
||||||
|
NNG's PIPELINE pattern seems attractive for Membrane processing stages, but:
|
||||||
|
- **The Pipeline is not a network problem.** It's sequential function calls within a process.
|
||||||
|
- **Stages run synchronously** in the same address space. You need `fn process_frame()`, not a socket.
|
||||||
|
- **Adding NNG adds complexity** without solving a real problem.
|
||||||
|
|
||||||
|
> **Principle: Don't use message passing where function calls suffice.**
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. ZENOH: THE DATA PLANE
|
## 2. ZENOH: THE UNIFIED MESSAGING LAYER
|
||||||
|
|
||||||
### 2.1 What is Zenoh?
|
### 2.1 What is Zenoh?
|
||||||
Zero-overhead pub/sub with query semantics. Rust core, Eclipse Foundation lineage. Successor to DDS ecosystem.
|
Zero-overhead pub/sub with query semantics. Rust core, Eclipse Foundation lineage.
|
||||||
|
|
||||||
### 2.2 Why Zenoh for Data?
|
### 2.2 Why Zenoh-Only?
|
||||||
- **Key-expression routing:** `sensor/berlin/pm25/**` is first-class
|
- **Single mental model:** Everything is a key-space query
|
||||||
- **Query + Pub/Sub unified:** `get("sensor/berlin/pm25/latest")` AND `subscribe("sensor/berlin/pm25/*")`
|
- **Pattern unification:** PUB/SUB, REQ/REP, SURVEY all via `z_get()` with different key patterns
|
||||||
- **Peer-to-peer AND routed:** Brokerless by default, routers for scale
|
- **Peer-to-peer AND routed:** Brokerless by default, routers for scale
|
||||||
- **Wire efficiency:** 4-8 bytes overhead per message (binary protocol)
|
- **Wire efficiency:** 4-8 bytes overhead per message (binary protocol)
|
||||||
- **Storage alignment:** Built-in persistence backends (RocksDB, memory)
|
- **Storage alignment:** Built-in persistence backends (RocksDB, memory)
|
||||||
- **Zenoh-Pico:** C library, ~50KB footprint (Kenya-compliant)
|
- **zenoh-pico:** C library, ~50KB footprint (Kenya-compliant)
|
||||||
|
|
||||||
### 2.3 Zenoh Namespace Design
|
### 2.3 Pattern Mapping: Zenoh Replaces All
|
||||||
```
|
|
||||||
libertaria/
|
#### PUB/SUB → `z_subscribe()`
|
||||||
├── sensor/
|
```zig
|
||||||
│ └── {geohash}/
|
// Subscribe to all PM2.5 sensors
|
||||||
│ └── {metric_type}/
|
var sub = try session.declare_subscriber("sensor/+/pm25", .{
|
||||||
│ └── {reading} → sensor/9f4w/pm25/42.3
|
.callback = onSensorReading,
|
||||||
├── chapter/
|
});
|
||||||
│ └── {chapter_id}/
|
|
||||||
│ ├── feed/
|
// Publisher
|
||||||
│ │ └── {channel}/
|
var pub = try session.declare_publisher("sensor/berlin/pm25");
|
||||||
│ │ └── {post_id} → chapter/berlin/feed/world/post-123
|
try pub.put("42.3");
|
||||||
│ └── state/
|
|
||||||
│ └── {key} → chapter/berlin/state/governance
|
|
||||||
├── economy/
|
|
||||||
│ └── {token}/
|
|
||||||
│ └── {metric} → economy/scrap/velocity
|
|
||||||
└── agent/
|
|
||||||
└── {agent_id}/
|
|
||||||
└── {stream} → agent/janus-7/stream-2
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2.4 Zenoh Integration Points
|
#### REQ/REP → `z_get()` on specific key
|
||||||
| Component | Subscription | Publication |
|
```zig
|
||||||
|-----------|-------------|-------------|
|
// Requester: Query specific trust distance
|
||||||
| Membrane Agent | `sensor/+/pm25`, `chapter/+/feed/**` | Filtered feed items |
|
var reply = try session.get("query/qvl/trust/did:libertaria:abc123");
|
||||||
|
const trust_score = reply.payload; // "0.87"
|
||||||
|
|
||||||
|
// Replier: Declare queryable
|
||||||
|
var queryable = try session.declare_queryable("query/qvl/trust/*", .{
|
||||||
|
.callback = onTrustQuery,
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### SURVEY → `z_get()` with wildcards + timeout
|
||||||
|
```zig
|
||||||
|
// Surveyor: Ask all health modules, aggregate responses
|
||||||
|
var replies = try session.get("health/**", .{.timeout_ms = 500});
|
||||||
|
while (replies.next()) |reply| {
|
||||||
|
std.log.info("{s}: {s}", .{reply.key, reply.payload});
|
||||||
|
}
|
||||||
|
// Automatically aggregates all matching responses within deadline
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.4 Namespace Design (LWF Service-Type Registry)
|
||||||
|
The Zenoh key-space **IS** the LWF Service-Type Registry, only readable.
|
||||||
|
|
||||||
|
```
|
||||||
|
$MEMBRANE/ ← L0/L1 Signals
|
||||||
|
defcon/current ← Current defense level
|
||||||
|
defcon/history ← Level history (queryable)
|
||||||
|
pattern/alert/* ← Pattern detection events
|
||||||
|
stats/throughput ← Real-time metrics
|
||||||
|
|
||||||
|
$AGENT/ ← L1 Agent Communication
|
||||||
|
{did}/caps ← Agent capabilities
|
||||||
|
{did}/negotiate ← Negotiation channel
|
||||||
|
{did}/status ← Online/Offline/Occupied
|
||||||
|
|
||||||
|
$SENSOR/ ← RFC-0295 Sensor Oracle
|
||||||
|
{geohash}/{metric} ← sensor/u33dc0/pm25
|
||||||
|
{geohash}/{metric}/history ← Queryable storage
|
||||||
|
|
||||||
|
$FEED/ ← RFC-0830 Feed Protocol
|
||||||
|
{chapter}/world/{post_id} ← World posts
|
||||||
|
{chapter}/channel/{chan_id} ← Channel posts
|
||||||
|
{chapter}/group/{group_id} ← Group messages
|
||||||
|
{chapter}/dm/{thread_id} ← E2E encrypted DMs
|
||||||
|
|
||||||
|
$QUERY/ ← REQ/REP Replacement
|
||||||
|
qvl/trust/{did} ← Trust graph query
|
||||||
|
economy/scrap/velocity ← Economic metrics
|
||||||
|
health/{module} ← Health check responses
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.5 Integration Points
|
||||||
|
| Component | Subscription | Publication/Query |
|
||||||
|
|-----------|-------------|-------------------|
|
||||||
|
| Membrane Agent | `sensor/+/pm25`, `$MEMBRANE/defcon` | Filtered items to `$FEED/` |
|
||||||
| Sensor Oracle | `sensor/+/+` (all sensors) | Normalized readings |
|
| Sensor Oracle | `sensor/+/+` (all sensors) | Normalized readings |
|
||||||
| Feed Relay | `chapter/+/feed/channel/*` | Relayed posts |
|
| Feed Relay | `$FEED/{chapter}/+` | Relayed posts |
|
||||||
| Economic Engine | `economy/scrap/**` | Velocity updates |
|
| Economic Engine | `economy/scrap/**` | Velocity updates |
|
||||||
| Archive Negotiator | `chapter/+/feed/**` (selective) | ANP responses |
|
| Health Monitor | `health/**` (SURVEY mode) | Aggregated status |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3. NNG: THE CONTROL PLANE
|
## 3. ARCHITECTURE
|
||||||
|
|
||||||
### 3.1 What is NNG?
|
### 3.1 Node Interior Layout
|
||||||
Brokerless messaging library (nanomsg-next-generation). Pure C. Pattern-oriented.
|
|
||||||
|
|
||||||
### 3.2 Why NNG for Control?
|
|
||||||
- **Pattern elegance:** Declare *intent* (REQ/REP, PUB/SUB, PIPELINE), not plumbing
|
|
||||||
- **Zig interop:** Native C ABI, `@cImport` trivial
|
|
||||||
- **Transport agnostic:** `ipc://`, `tcp://`, `inproc://` (zero-copy same-process)
|
|
||||||
- **Zero broker:** The pattern *is* the infrastructure
|
|
||||||
- **Lightweight:** ~200KB shared library (Kenya-compliant)
|
|
||||||
- **SURVEY pattern:** Perfect for "ask all sensors, collect within deadline"
|
|
||||||
|
|
||||||
### 3.3 NNG Patterns Used
|
|
||||||
|
|
||||||
#### PUB/SUB — Status Broadcasts
|
|
||||||
```
|
|
||||||
PUBLISHER (Health Monitor)
|
|
||||||
└── PUB socket → "ipc:///tmp/libertaria/health.pub"
|
|
||||||
|
|
||||||
SUBSCRIBERS
|
|
||||||
├── Membrane Agent (subscribe: "membrane.")
|
|
||||||
├── Feed Processor (subscribe: "feed.")
|
|
||||||
└── Chapter Governor (subscribe: "governor.")
|
|
||||||
```
|
|
||||||
|
|
||||||
#### REQ/REP — Agent Negotiation
|
|
||||||
```
|
|
||||||
REQUESTER (Membrane Agent)
|
|
||||||
└── REQ socket → "ipc:///tmp/libertaria/governor.rep"
|
|
||||||
└── "AUTHORIZE: post-123"
|
|
||||||
|
|
||||||
REPLIER (Chapter Governor)
|
|
||||||
└── REP socket ← "ipc:///tmp/libertaria/governor.rep"
|
|
||||||
└── "PERMIT: entropy=valid,qvl=trusted"
|
|
||||||
```
|
|
||||||
|
|
||||||
#### PIPELINE — Membrane Processing Stages
|
|
||||||
```
|
|
||||||
PUSH (Entropy Checker)
|
|
||||||
└── PUSH socket → "inproc:///membrane/stage1"
|
|
||||||
|
|
||||||
PULL → PUSH (Graph Checker)
|
|
||||||
└── PULL ← "inproc:///membrane/stage1"
|
|
||||||
└── PUSH → "inproc:///membrane/stage2"
|
|
||||||
|
|
||||||
PULL → PUSH (Periscope AI)
|
|
||||||
└── PULL ← "inproc:///membrane/stage2"
|
|
||||||
└── PUSH → "inproc:///membrane/stage3"
|
|
||||||
|
|
||||||
PULL (Accept/Reject)
|
|
||||||
└── PULL ← "inproc:///membrane/stage3"
|
|
||||||
```
|
|
||||||
|
|
||||||
#### SURVEY — Sensor Aggregation
|
|
||||||
```
|
|
||||||
SURVEYOR (Sensor Oracle)
|
|
||||||
└── SURVEY socket → "tcp://*:7890"
|
|
||||||
└── "QUERY: pm25_24h_max"
|
|
||||||
└── Deadline: 500ms
|
|
||||||
|
|
||||||
RESPONDENTS (Sensor Nodes)
|
|
||||||
└── RESPONDENT socket ← "tcp://sensor-oracle:7890"
|
|
||||||
└── Reply with local reading
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. DUAL-PLANE ARCHITECTURE
|
|
||||||
|
|
||||||
### 4.1 Node Interior Layout
|
|
||||||
```
|
```
|
||||||
┌─────────────────────────────────────────────────────────┐
|
┌─────────────────────────────────────────────────────────┐
|
||||||
│ LIBERTARIA NODE │
|
│ LIBERTARIA NODE — Zenoh-Only Architecture │
|
||||||
│ │
|
│ │
|
||||||
│ CONTROL PLANE (NNG) DATA PLANE (Zenoh) │
|
│ ZENOH KEY-SPACE (Unified Messaging) │
|
||||||
│ ┌──────────────────┐ ┌──────────────────────┐ │
|
│ ┌──────────────────────────────────────────────────┐ │
|
||||||
│ │ REQ/REP │ │ sensor/berlin/pm25/* │ │
|
│ │ $MEMBRANE/ │ │
|
||||||
│ │ Agent negotiation│◄──────►│ chapter/+/feed/** │ │
|
│ │ defcon/current │ │
|
||||||
│ └──────────────────┘ │ economy/scrap/** │ │
|
│ │ pattern/alert/* │ │
|
||||||
│ └──────────────────────┘ │
|
│ ├──────────────────────────────────────────────────┤ │
|
||||||
│ ┌──────────────────┐ │
|
│ │ $SENSOR/ │ │
|
||||||
│ │ PIPELINE │ ┌──────────────────┐ │
|
│ │ {geohash}/{metric} │ │
|
||||||
│ │ Membrane stages │ │ Membrane Agent │ │
|
│ │ {geohash}/{metric}/history (queryable) │ │
|
||||||
│ │ (inproc://) │ │ (subscribes to │ │
|
│ ├──────────────────────────────────────────────────┤ │
|
||||||
│ └──────────────────┘ │ sensor/+/pm25) │ │
|
│ │ $FEED/ │ │
|
||||||
│ └──────────────────┘ │
|
│ │ {chapter}/world/{post_id} │ │
|
||||||
│ ┌──────────────────┐ ┌──────────────────┐ │
|
│ │ {chapter}/channel/{chan_id} │ │
|
||||||
│ │ SURVEY │ │ Feed Processor │ │
|
│ ├──────────────────────────────────────────────────┤ │
|
||||||
│ │ Health checks │ │ (subscribes to │ │
|
│ │ $QUERY/ │ │
|
||||||
│ └──────────────────┘ │ chapter/+/feed) │ │
|
│ │ qvl/trust/{did} (REQ/REP pattern) │ │
|
||||||
│ └──────────────────┘ │
|
│ │ health/** (SURVEY pattern with timeout) │ │
|
||||||
|
│ └──────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ COMPONENTS │
|
||||||
|
│ ┌─────────────────┐ ┌─────────────────┐ │
|
||||||
|
│ │ Membrane Agent │ │ Sensor Oracle │ │
|
||||||
|
│ │ (sub+pub) │ │ (sub+pub) │ │
|
||||||
|
│ └─────────────────┘ └─────────────────┘ │
|
||||||
|
│ ┌─────────────────┐ ┌─────────────────┐ │
|
||||||
|
│ │ Feed Processor │ │ Health Monitor │ │
|
||||||
|
│ │ (sub+pub) │ │ (SURVEY queries)│ │
|
||||||
|
│ └─────────────────┘ └─────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ NOTE: Membrane Pipeline = Function calls, not sockets │
|
||||||
|
│ ┌──────────────────────────────────────────────────┐ │
|
||||||
|
│ │ fn process_frame() │ │
|
||||||
|
│ │ Stage 0: Triage (validate) │ │
|
||||||
|
│ │ Stage 1: Context (build_context) │ │
|
||||||
|
│ │ Stage 2: Decide (policy_engine) │ │
|
||||||
|
│ │ Stage 3: Commit (state_manager) │ │
|
||||||
|
│ └──────────────────────────────────────────────────┘ │
|
||||||
│ │
|
│ │
|
||||||
│ ENCRYPTION: LWF (RFC-0000) overlays both planes │
|
|
||||||
│ ┌────────────────────────────────────────────────────┐ │
|
|
||||||
│ │ Zenoh payload: XChaCha20-Poly1305 encrypted │ │
|
|
||||||
│ │ NNG payload: XChaCha20-Poly1305 encrypted │ │
|
|
||||||
│ └────────────────────────────────────────────────────┘ │
|
|
||||||
└─────────────────────────────────────────────────────────┘
|
└─────────────────────────────────────────────────────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4.2 Transport Selection Decision Tree
|
### 3.2 Transport Selection Decision Tree
|
||||||
```
|
```
|
||||||
Is the communication:
|
Is the communication:
|
||||||
├── Content-defined? (sensor/berlin/pm25)
|
├── Content-defined? (sensor/berlin/pm25)
|
||||||
│ └── Use ZENOH (key-expression routing)
|
│ └── Use ZENOH (key-expression routing)
|
||||||
├── Pattern-defined? (REQ/REP negotiation)
|
|
||||||
│ └── Use NNG (pattern semantics)
|
|
||||||
├── High-frequency + small payload?
|
├── High-frequency + small payload?
|
||||||
│ ├── >10k msgs/sec → ZENOH (lower overhead)
|
│ └── ZENOH (4-8 byte overhead)
|
||||||
│ └── <10k msgs/sec → Either
|
|
||||||
├── Must survive intermittent connectivity?
|
├── Must survive intermittent connectivity?
|
||||||
│ └── ZENOH (designed for this)
|
│ └── ZENOH (designed for this)
|
||||||
└── Must guarantee delivery ordering?
|
├── Must guarantee delivery ordering?
|
||||||
└── NNG PIPELINE or REQ/REP
|
│ └── ZENOH with reliability QoS
|
||||||
|
└── Is it internal pipeline stages?
|
||||||
|
└── Function calls, NOT message passing
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 5. SECURITY: LWF ENCRYPTION OVERLAY
|
## 4. SECURITY: LWF ENCRYPTION OVERLAY
|
||||||
|
|
||||||
Neither Zenoh nor NNG provides native encryption that satisfies Libertaria's sovereignty requirements. Both use **LWF encryption overlay** (RFC-0000).
|
Zenoh does not provide native encryption satisfying Libertaria's sovereignty requirements. Use **LWF encryption overlay** (RFC-0000).
|
||||||
|
|
||||||
### 5.1 Zenoh + LWF
|
### 4.1 Zenoh + LWF
|
||||||
```
|
```
|
||||||
Zenoh payload structure:
|
Zenoh payload structure:
|
||||||
┌─────────────────────────────────────────────────────┐
|
┌─────────────────────────────────────────────────────┐
|
||||||
|
|
@ -231,25 +236,13 @@ Zenoh payload structure:
|
||||||
└─────────────────────────────────────────────────────┘
|
└─────────────────────────────────────────────────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
### 5.2 NNG + LWF
|
|
||||||
```
|
|
||||||
NNG message structure:
|
|
||||||
┌─────────────────────────────────────────────────────┐
|
|
||||||
│ NNG Protocol Header (minimal) │
|
|
||||||
├─────────────────────────────────────────────────────┤
|
|
||||||
│ LWF Encrypted Body │
|
|
||||||
│ └── XChaCha20-Poly1305(SoulKey-derived nonce) │
|
|
||||||
│ └── Contains: Application message │
|
|
||||||
└─────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
**Key Derivation:** Per-session keys from X3DH handshake (RFC-0140), rotated per RFC-0010 epoch.
|
**Key Derivation:** Per-session keys from X3DH handshake (RFC-0140), rotated per RFC-0010 epoch.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6. IMPLEMENTATION
|
## 5. IMPLEMENTATION
|
||||||
|
|
||||||
### 6.1 Dependencies
|
### 5.1 Dependencies
|
||||||
```zig
|
```zig
|
||||||
// build.zig
|
// build.zig
|
||||||
const zenoh_pico = b.dependency("zenoh-pico", .{
|
const zenoh_pico = b.dependency("zenoh-pico", .{
|
||||||
|
|
@ -257,127 +250,122 @@ const zenoh_pico = b.dependency("zenoh-pico", .{
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
});
|
});
|
||||||
|
|
||||||
const nng = b.dependency("nng", .{
|
|
||||||
.target = target,
|
|
||||||
.optimize = optimize,
|
|
||||||
});
|
|
||||||
|
|
||||||
exe.addModule("zenoh", zenoh_pico.module("zenoh"));
|
exe.addModule("zenoh", zenoh_pico.module("zenoh"));
|
||||||
exe.linkLibrary(nng.artifact("nng"));
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 6.2 Zig API Example: Zenoh
|
### 5.2 Zig API Example
|
||||||
```zig
|
```zig
|
||||||
const zenoh = @import("zenoh");
|
const zenoh = @import("zenoh");
|
||||||
|
|
||||||
// Publisher
|
// Initialize session
|
||||||
var pub = try zenoh.open(b.allocator, .{.mode = .peer});
|
var session = try zenoh.open(allocator, .{.mode = .peer});
|
||||||
var publisher = try pub.declare_publisher("sensor/berlin/pm25");
|
|
||||||
try publisher.put("42.3");
|
|
||||||
|
|
||||||
// Subscriber
|
// PUB/SUB: Subscribe to sensors
|
||||||
var sub = try zenoh.open(b.allocator, .{.mode = .peer});
|
var sub = try session.declare_subscriber("sensor/+/pm25", .{
|
||||||
var subscriber = try sub.declare_subscriber("sensor/+/pm25", .{
|
|
||||||
.callback = onSensorReading,
|
.callback = onSensorReading,
|
||||||
});
|
});
|
||||||
|
|
||||||
fn onSensorReading(sample: zenoh.Sample) void {
|
fn onSensorReading(sample: zenoh.Sample) void {
|
||||||
const reading = sample.payload; // "42.3"
|
std.log.info("{s}: {s}", .{sample.key, sample.payload});
|
||||||
const key = sample.key; // "sensor/berlin/pm25"
|
|
||||||
std.log.info("{s}: {s}", .{key, reading});
|
|
||||||
}
|
}
|
||||||
```
|
|
||||||
|
|
||||||
### 6.3 Zig API Example: NNG
|
// PUB/SUB: Publish reading
|
||||||
```zig
|
var pub = try session.declare_publisher("sensor/berlin/pm25");
|
||||||
const nng = @cImport({
|
try pub.put("42.3");
|
||||||
@cInclude("nng/nng.h");
|
|
||||||
});
|
|
||||||
|
|
||||||
// REQ socket
|
// REQ/REP: Query trust distance
|
||||||
var req: nng.nng_socket = undefined;
|
var reply = try session.get("query/qvl/trust/did:libertaria:abc123");
|
||||||
_ = nng.nng_req0_open(&req);
|
std.log.info("Trust: {s}", .{reply.payload});
|
||||||
_ = nng.nng_dial(req, "ipc:///tmp/libertaria/governor.rep");
|
|
||||||
|
|
||||||
const msg = "AUTHORIZE: post-123";
|
// SURVEY: Health check all modules
|
||||||
_ = nng.nng_send(req, msg.ptr, msg.len, 0);
|
var replies = try session.get("health/**", .{.timeout_ms = 500});
|
||||||
|
var healthy: usize = 0;
|
||||||
var reply: ?*anyopaque = null;
|
while (replies.next()) |r| {
|
||||||
var reply_len: usize = 0;
|
if (std.mem.eql(u8, r.payload, "OK")) healthy += 1;
|
||||||
_ = nng.nng_recv(req, &reply, &reply_len, nng.NNG_FLAG_ALLOC);
|
}
|
||||||
std.log.info("Reply: {s}", .{@ptrCast([*]u8, reply)[0..reply_len]});
|
std.log.info("{d}/{d} modules healthy", .{healthy, replies.total});
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 7. KENYA COMPLIANCE
|
## 6. KENYA COMPLIANCE
|
||||||
|
|
||||||
| Library | Footprint | Kenya Status |
|
| Metric | Value | Status |
|
||||||
|---------|-----------|--------------|
|
|--------|-------|--------|
|
||||||
| Zenoh-Pico | ~50KB | ✅ Compliant |
|
| **Footprint** | ~50KB (zenoh-pico) | ✅ Compliant |
|
||||||
| NNG | ~200KB | ✅ Compliant |
|
| **No broker** | Peer-to-peer by default | ✅ Compliant |
|
||||||
| **Total** | **~250KB** | **✅ Compliant** |
|
| **Offline tolerance** | Designed for intermittent | ✅ Compliant |
|
||||||
|
| **C ABI** | Native `@cImport` | ✅ Compliant |
|
||||||
Both libraries:
|
|
||||||
- No mandatory broker (survives network partition)
|
|
||||||
- Low memory footprint (no JVM, no heavy runtime)
|
|
||||||
- C ABI (Zig interop without FFI complexity)
|
|
||||||
- Static linkable (single binary deployment)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 8. MIGRATION PATH
|
## 7. MIGRATION PATH
|
||||||
|
|
||||||
### Phase 1: Zenoh for Sensors (Week 1)
|
### Phase 1 (v0.5.0; ~15h): Zenoh-Pico Binding + First Channel
|
||||||
- Deploy Zenoh-Pico on sensor nodes
|
- Build zenoh-pico Zig binding
|
||||||
- Membrane Agent subscribes to `sensor/+/pm25`
|
- Implement `$MEMBRANE/defcon` as first live channel
|
||||||
- Verify: 4-8 byte overhead per message
|
- Proof: Membrane Agent publishes defcon changes
|
||||||
|
|
||||||
### Phase 2: NNG for Membrane (Week 2)
|
### Phase 2 (v0.6.0; ~15h): Sensor + Query Namespace
|
||||||
- Implement PIPELINE for processing stages
|
- `$SENSOR/` namespace for Sensor Oracle
|
||||||
- Replace ad-hoc channel communication
|
- `$QUERY/qvl/trust/{did}` for trust graph queries
|
||||||
- Verify: Zero-copy `inproc://` where possible
|
- Pattern Detection (RFC-0115) publishes to `$MEMBRANE/pattern/alert/*`
|
||||||
|
|
||||||
### Phase 3: Unified Health (Week 3)
|
### Phase 3 (v0.7.0; ~10h): Feed Integration
|
||||||
- NNG SURVEY for node health aggregation
|
- `$FEED/` namespace for Feed Social Protocol (RFC-0830)
|
||||||
- Zenoh pub for status broadcasts
|
- Gossip-Relay via Zenoh-Router
|
||||||
- Unified dashboard
|
|
||||||
|
|
||||||
### Phase 4: LWF Overlay (Week 4)
|
### Phase 4 (v0.8.0; ~10h): LWF Encryption Overlay
|
||||||
- Add XChaCha20-Poly1305 encryption to both transports
|
- Add XChaCha20-Poly1305 encryption to Zenoh payloads
|
||||||
- Key rotation per RFC-0010 epochs
|
- Key rotation per RFC-0010 epochs
|
||||||
- Security audit
|
- Security audit
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 9. CONCLUSION
|
## 8. CONCLUSION
|
||||||
|
|
||||||
> **Zenoh is the data plane. NNG is the control plane.**
|
> **Zenoh-only is the right knife.**
|
||||||
|
|
||||||
This dual-plane architecture respects Libertaria's core constraints:
|
The dual-plane approach (Zenoh + NNG) was architecturally valid but practically wrong for solo development. Two libraries = two failure modes = too much complexity.
|
||||||
- **Sovereignty:** No broker, no external dependency
|
|
||||||
- **Kenya:** 50-200KB footprint
|
The insight: **Zenoh's key-space IS the LWF Service-Type Registry, only readable.**
|
||||||
- **Security:** LWF encryption overlays both
|
|
||||||
- **Expressiveness:** Content routing (Zenoh) + Pattern semantics (NNG)
|
| Pattern | Old Approach | Zenoh-Only |
|
||||||
|
|---------|--------------|------------|
|
||||||
|
| PUB/SUB | `z_subscribe()` | `z_subscribe()` ✓ |
|
||||||
|
| REQ/REP | NNG REQ/REP | `z_get(specific_key)` ✓ |
|
||||||
|
| SURVEY | NNG SURVEY | `z_get(wildcard, timeout)` ✓ |
|
||||||
|
| PIPELINE | NNG PIPELINE | Function calls ✓ |
|
||||||
|
|
||||||
|
One library. One namespace. One mental model.
|
||||||
|
|
||||||
The submarine does not merely transport messages. It *thinks* through them.
|
The submarine does not merely transport messages. It *thinks* through them.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## APPENDIX: COMPARISON WITH v0.1.0
|
||||||
|
|
||||||
|
| Aspect | v0.1.0 (Dual-Plane) | v0.2.0 (Zenoh-Only) |
|
||||||
|
|--------|---------------------|---------------------|
|
||||||
|
| Libraries | 2 (Zenoh + NNG) | 1 (Zenoh) |
|
||||||
|
| Mental Models | 2 | 1 |
|
||||||
|
| Failure Modes | 2 | 1 |
|
||||||
|
| Update Cycles | 2 | 1 |
|
||||||
|
| Pipeline | NNG PIPELINE | Function calls |
|
||||||
|
| Complexity | Higher | Lower |
|
||||||
|
| Solo-Dev Fit | Poor | Excellent |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## APPENDIX: DEPENDENCIES
|
## APPENDIX: DEPENDENCIES
|
||||||
|
|
||||||
### Zenoh-Pico
|
### Zenoh-Pico
|
||||||
- **URL:** https://github.com/eclipse-zenoh/zenoh-pico
|
- **URL:** https://github.com/eclipse-zenoh/zenoh-pico
|
||||||
- **License:** EPL-2.0 OR Apache-2.0
|
- **License:** EPL-2.0 OR Apache-2.0
|
||||||
- **Zig binding:** Direct C API via `@cImport`
|
- **Zig binding:** Direct C API via `@cImport`
|
||||||
|
- **Footprint:** ~50KB
|
||||||
### NNG (nanomsg-next-generation)
|
|
||||||
- **URL:** https://github.com/nanomsg/nng
|
|
||||||
- **License:** MIT
|
|
||||||
- **Zig binding:** Direct C API via `@cImport`
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**End of RFC-0910 v0.1.0**
|
**End of RFC-0910 v0.2.0**
|
||||||
|
*The submarine's nervous system is unified.* 🜏
|
||||||
*The submarine's nervous system is waking up.* 🜏
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue