LPF – Libertaria Packet Filter
Why LPF Kills Everything Before It
iptables, nftables, eBPF – they all share the same original sin: they filter packets that have no identity. They stare at IP headers, port numbers, byte patterns. Dumb matter. They are forensic pathologists performing autopsies on anonymous corpses.
LPF starts from a radically different axiom:
Every packet arriving on EtherType 0x88B5 already carries a cryptographic identity – SipHash CellID, Ed25519 verification, Noise XX session. The filter doesn't need to discover who's talking; it already knows.
This means 80% of what iptables does – source validation, connection tracking, SYN flood protection – is pre-solved by the protocol layer. The NetSwitch already drops unknown CellIDs. LPF only needs to handle policy, not identification.
Architecture: The Three Rings
+-----------------------------------------------------+
| RING 0: The Wire Gate (Zig, L0, in NetSwitch) |
| --------------------------------------------------- |
| - EtherType fork (existing) |
| - CellID allowlist/denylist (bitmap, O(1) lookup) |
| - Rate limiter per CellID (token bucket, atomic) |
| - NexFS Mesh fast-lane (StorageOp -> bypass Ring 1) |
| - ~50 lines of Zig. Runs at line rate. |
+-------------------------+---------------------------+
| packets that survive
v
+-----------------------------------------------------+
| RING 1: The Policy Engine (Janus DSL, Membrane) |
| --------------------------------------------------- |
| - Janus-compiled filter scripts |
| - Match on: CellID, StorageOp, UTCP flags, |
| payload type tags, Noise session age, reputation |
| - Actions: PASS, DROP, REDIRECT(dpi_ring), |
| THROTTLE(rate), TAG(label), MIRROR(audit_ring) |
| - Hot-reload via atomic table swap (zero drops) |
| - Kinetic Economy integration (budget per rule) |
+-------------------------+---------------------------+
| suspicious -> DPI ring
v
+-----------------------------------------------------+
| RING 2: The Deep Loop (Membrane Agent, async) |
| --------------------------------------------------- |
| - Full payload inspection (DPI) |
| - Pattern matching, anomaly detection |
| - Feeds threat intel back to Ring 1 (hot-patch) |
| - Can trigger PEER_OVERFLOW to cluster peers |
| - Runs at Gravity spectrum (never starves traffic) |
+-----------------------------------------------------+Ring 0 – The Wire Gate
Zero-cost filtering at the NetSwitch layer. The EtherType demux already exists (LWF 0x4C57, UTCP 0x88B5, IPv4/IPv6 to LwIP). Ring 0 adds a CellID bitmap – a simple bitfield where each known CellID gets a slot. Lookup is O(1), no hash table, no cache miss. Unknown CellIDs are dropped before they touch the ION ring.
Token bucket rate limiters per CellID prevent flood attacks even from known peers. The entire Ring 0 is ~50 lines of Zig in the existing NetSwitch hot path.
Ring 1 – The Policy Engine
This is where the Janus DSL shines. Filter rules are declarative pattern matches compiled to bytecode:
filter "known-peers" priority=10 {
match peer.verified == true
match session.noise_age < 24h
-> pass
}
filter "rate-limit-strangers" priority=20 {
match peer.verified == false
-> throttle 100/s per_peer
-> tag "stranger"
}
filter "suspicious-payload" priority=50 {
match payload.entropy > 0.95
match peer.reputation < 0.3
-> redirect dpi_ring
}
filter "catch-all" priority=999 {
-> drop
-> log stl
}Rules are loaded atomically via RCU (Read-Copy-Update) – compile new table in a Gravity fiber, validate against capability schema, stage in shadow slot, flip pointer, drain old table naturally. No daemon restart. No kernel module reload. No connection tracking flush.
Ring 2 – The Deep Loop
Full payload inspection for suspicious traffic. Runs asynchronously at Gravity spectrum – never starves the Photon/Matter traffic path. Feeds threat intelligence back to Ring 1 via hot-patch:
@agent "defense-escalation" {
on threat_level > HIGH {
patch "rate-limit-strangers" {
throttle 10/s per_peer
}
activate filter "lockdown" {
match peer.verified == false
-> drop
}
}
}Hot-Reload: Zero Connection Drops
NPF (NetBSD Packet Filter) pioneered atomic ruleset swap. LPF improves it:
- Compile new Janus script to bytecode (Gravity fiber – costs nothing)
- Validate bytecode against capability schema (can this script reference these match fields?)
- Stage new table in shadow slot
- Atomic swap – flip pointer in ION Ring dispatch table
- Drain – old table remains valid for in-flight packets until ring slots consumed
- Free – old table memory returned to slab
Because the filter runs in Membrane (userspace), not in the kernel, there is no bpf() syscall, no verifier gymnastics, no kernel module. The UTCP sessions don't even notice the swap.
Cluster Load Distribution: Verteilen
When a single router node gets hammered:
- LPF Ring 2 detects sustained high load (packets/sec exceeding threshold)
- Membrane Agent sends
LPF_PEER_OVERFLOWover UTCP to mesh peers - Peers respond with capacity bids (available filter budget from Kinetic Economy)
- Originating router forwards flows to peers using consistent hashing on CellID – same flow always goes to same peer, so stateful DPI doesn't break
- When load drops, flows migrate back (graceful drain, same RCU pattern)
This is anycast for packet filtering within the Libertaria mesh. No central load balancer. No single point of failure. The mesh is the firewall.
The Sovereign Networking Path (M5 – Delivered)
The full LWF path now bypasses LwIP entirely:
VirtIO-Net -> NetSwitch (EtherType 0x4C57)
|
+-> LWF validate (magic, version, flags)
+-> CellID register (source hint -> MAC)
+-> chan_lwf_rx (ION ring, zero-copy)
|
+-> Capsule (U-mode, freestanding):
| Noise XX handshake (BLAKE2b KDF, X25519 DH)
| Session table (8 slots, LRU eviction)
| AEAD seal/unseal (XChaCha20-Poly1305 via Monocypher)
| QVL trust binding (BLAKE3 key IDs)
|
+-> chan_lwf_tx -> NetSwitch -> VirtIO-NetIPv4/ARP/IPv6 still routes through LwIP for legacy compatibility. LWF frames never touch the IP stack.
Crypto Stack
| Primitive | Library | Used For |
|---|---|---|
| X25519 | Monocypher | Noise XX DH |
| ChaCha20-Poly1305 | Monocypher | AEAD (Noise transport + Mode B sealed) |
| BLAKE2b | Monocypher | Noise KDF (keyed BLAKE2b as HMAC) |
| BLAKE3 | C reference v1.5.1 | Identity (QVL key IDs), content addressing |
| Ed25519 | Zig std | LWF trailer signatures |
| SipHash-2-4 | Zig std | Packet IDs, CellID derivation |
All crypto in the capsule is freestanding (no std, no heap). Monocypher + BLAKE3 are compiled directly into the capsule ELF alongside the Noise XX state machine.
Implementation Status
| Component | Status | Detail |
|---|---|---|
| EtherType demux | Shipped | NetSwitch routes LWF/UTCP/IP at L2 |
| CellID registration | Shipped | Source hint -> MAC table, per-frame update |
| Noise XX handshake | Shipped | 3-way, bidirectional, session table |
| AEAD seal/unseal | Shipped | Kernel syscalls (0x800/0x801) |
| QVL trust binding | Shipped | BLAKE3(remote_static), 16-peer table |
| Sovereign path | Shipped | LWF bypasses LwIP entirely |
| 3-node mesh | Shipped | QEMU multicast, all nodes boot + LWF |
| Ring 0 bitmap | Planned | CellID allowlist in NetSwitch |
| Ring 1 Janus DSL | Planned | First Janus compiler target |
| Ring 2 DPI | Planned | Membrane agent, Gravity spectrum |
| Verteilen | Planned | Mesh overflow distribution |