libertaria-stack/RFC/RFC-0910-FIRST-CLASS-MESSAG...

15 KiB

RFC-0910: FIRST-CLASS MESSAGING SYSTEM

The Submarine's Nervous System

Version: 0.2.0
Status: DRAFT
Layer: L0-L1 (Transport — Internal Process Communication)
Class: ARCHITECTURAL
Author: Markus Maiwald
Date: 2026-02-09


0. ABSTRACT

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:

  • No-broker sovereignty: No Kafka, no RabbitMQ, no central daemon
  • Kenya compliance: ~50KB footprint (zenoh-pico)
  • Unified semantics: Everything is a key-space query
  • 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.1 The Gap

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:

  • Brokerless operation (sovereignty requirement)
  • Unified patterns (PUB/SUB, REQ/REP, SURVEY as one semantic)
  • Content-based routing (subscribe to sensor/berlin/pm25/**)
  • Kenya compliance (embedded-friendly footprint)

1.2 The Zenoh-Only Insight

After evaluating dual-plane (Zenoh + NNG), we reject it. Two libraries = two failure modes = too much complexity for solo development.

The key realization: Zenoh's key-space query unifies all messaging patterns.

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 UNIFIED MESSAGING LAYER

2.1 What is Zenoh?

Zero-overhead pub/sub with query semantics. Rust core, Eclipse Foundation lineage.

2.2 Why Zenoh-Only?

  • Single mental model: Everything is a key-space query
  • 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
  • Wire efficiency: 4-8 bytes overhead per message (binary protocol)
  • Storage alignment: Built-in persistence backends (RocksDB, memory)
  • zenoh-pico: C library, ~50KB footprint (Kenya-compliant)

2.3 Pattern Mapping: Zenoh Replaces All

PUB/SUB → z_subscribe()

// Subscribe to all PM2.5 sensors
var sub = try session.declare_subscriber("sensor/+/pm25", .{
    .callback = onSensorReading,
});

// Publisher
var pub = try session.declare_publisher("sensor/berlin/pm25");
try pub.put("42.3");

REQ/REP → z_get() on specific key

// Requester: Query specific trust distance
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

// 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
Feed Relay $FEED/{chapter}/+ Relayed posts
Economic Engine economy/scrap/** Velocity updates
Health Monitor health/** (SURVEY mode) Aggregated status

3. ARCHITECTURE

3.1 Node Interior Layout

┌─────────────────────────────────────────────────────────┐
│ LIBERTARIA NODE — Zenoh-Only Architecture                │
│                                                          │
│  ZENOH KEY-SPACE (Unified Messaging)                     │
│  ┌──────────────────────────────────────────────────┐   │
│  │ $MEMBRANE/                                       │   │
│  │   defcon/current                                 │   │
│  │   pattern/alert/*                                │   │
│  ├──────────────────────────────────────────────────┤   │
│  │ $SENSOR/                                         │   │
│  │   {geohash}/{metric}                             │   │
│  │   {geohash}/{metric}/history (queryable)         │   │
│  ├──────────────────────────────────────────────────┤   │
│  │ $FEED/                                           │   │
│  │   {chapter}/world/{post_id}                      │   │
│  │   {chapter}/channel/{chan_id}                    │   │
│  ├──────────────────────────────────────────────────┤   │
│  │ $QUERY/                                          │   │
│  │   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)                 │   │
│  └──────────────────────────────────────────────────┘   │
│                                                          │
└─────────────────────────────────────────────────────────┘

3.2 Transport Selection Decision Tree

Is the communication:
├── Content-defined? (sensor/berlin/pm25)
│   └── Use ZENOH (key-expression routing)
├── High-frequency + small payload?
│   └── ZENOH (4-8 byte overhead)
├── Must survive intermittent connectivity?
│   └── ZENOH (designed for this)
├── Must guarantee delivery ordering?
│   └── ZENOH with reliability QoS
└── Is it internal pipeline stages?
    └── Function calls, NOT message passing

4. SECURITY: LWF ENCRYPTION OVERLAY

Zenoh does not provide native encryption satisfying Libertaria's sovereignty requirements. Use LWF encryption overlay (RFC-0000).

4.1 Zenoh + LWF

Zenoh payload structure:
┌─────────────────────────────────────────────────────┐
│ LWF Header (72 bytes)                               │
│ ├── Version, Frame Type, Session ID                 │
│ ├── Sequence, Timestamp                             │
│ └── Payload Length                                  │
├─────────────────────────────────────────────────────┤
│ Encrypted Payload (XChaCha20-Poly1305)              │
│ └── Contains: Zenoh binary message                  │
├─────────────────────────────────────────────────────┤
│ MAC (16 bytes)                                      │
└─────────────────────────────────────────────────────┘

Key Derivation: Per-session keys from X3DH handshake (RFC-0140), rotated per RFC-0010 epoch.


5. IMPLEMENTATION

5.1 Dependencies

// build.zig
const zenoh_pico = b.dependency("zenoh-pico", .{
    .target = target,
    .optimize = optimize,
});

exe.addModule("zenoh", zenoh_pico.module("zenoh"));

5.2 Zig API Example

const zenoh = @import("zenoh");

// Initialize session
var session = try zenoh.open(allocator, .{.mode = .peer});

// PUB/SUB: Subscribe to sensors
var sub = try session.declare_subscriber("sensor/+/pm25", .{
    .callback = onSensorReading,
});

fn onSensorReading(sample: zenoh.Sample) void {
    std.log.info("{s}: {s}", .{sample.key, sample.payload});
}

// PUB/SUB: Publish reading
var pub = try session.declare_publisher("sensor/berlin/pm25");
try pub.put("42.3");

// REQ/REP: Query trust distance
var reply = try session.get("query/qvl/trust/did:libertaria:abc123");
std.log.info("Trust: {s}", .{reply.payload});

// SURVEY: Health check all modules
var replies = try session.get("health/**", .{.timeout_ms = 500});
var healthy: usize = 0;
while (replies.next()) |r| {
    if (std.mem.eql(u8, r.payload, "OK")) healthy += 1;
}
std.log.info("{d}/{d} modules healthy", .{healthy, replies.total});

6. KENYA COMPLIANCE

Metric Value Status
Footprint ~50KB (zenoh-pico) Compliant
No broker Peer-to-peer by default Compliant
Offline tolerance Designed for intermittent Compliant
C ABI Native @cImport Compliant

7. MIGRATION PATH

Phase 1 (v0.5.0; ~15h): Zenoh-Pico Binding + First Channel

  • Build zenoh-pico Zig binding
  • Implement $MEMBRANE/defcon as first live channel
  • Proof: Membrane Agent publishes defcon changes

Phase 2 (v0.6.0; ~15h): Sensor + Query Namespace

  • $SENSOR/ namespace for Sensor Oracle
  • $QUERY/qvl/trust/{did} for trust graph queries
  • Pattern Detection (RFC-0115) publishes to $MEMBRANE/pattern/alert/*

Phase 3 (v0.7.0; ~10h): Feed Integration

  • $FEED/ namespace for Feed Social Protocol (RFC-0830)
  • Gossip-Relay via Zenoh-Router

Phase 4 (v0.8.0; ~10h): LWF Encryption Overlay

  • Add XChaCha20-Poly1305 encryption to Zenoh payloads
  • Key rotation per RFC-0010 epochs
  • Security audit

8. CONCLUSION

Zenoh-only is the right knife.

The dual-plane approach (Zenoh + NNG) was architecturally valid but practically wrong for solo development. Two libraries = two failure modes = too much complexity.

The insight: Zenoh's key-space IS the LWF Service-Type Registry, only readable.

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.


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

Zenoh-Pico


End of RFC-0910 v0.2.0
The submarine's nervous system is unified. 🜏