Skip to content

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:

janus
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:

janus
@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:

  1. Compile new Janus script to bytecode (Gravity fiber – costs nothing)
  2. Validate bytecode against capability schema (can this script reference these match fields?)
  3. Stage new table in shadow slot
  4. Atomic swap – flip pointer in ION Ring dispatch table
  5. Drain – old table remains valid for in-flight packets until ring slots consumed
  6. 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:

  1. LPF Ring 2 detects sustained high load (packets/sec exceeding threshold)
  2. Membrane Agent sends LPF_PEER_OVERFLOW over UTCP to mesh peers
  3. Peers respond with capacity bids (available filter budget from Kinetic Economy)
  4. Originating router forwards flows to peers using consistent hashing on CellID – same flow always goes to same peer, so stateful DPI doesn't break
  5. 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-Net

IPv4/ARP/IPv6 still routes through LwIP for legacy compatibility. LWF frames never touch the IP stack.


Crypto Stack

PrimitiveLibraryUsed For
X25519MonocypherNoise XX DH
ChaCha20-Poly1305MonocypherAEAD (Noise transport + Mode B sealed)
BLAKE2bMonocypherNoise KDF (keyed BLAKE2b as HMAC)
BLAKE3C reference v1.5.1Identity (QVL key IDs), content addressing
Ed25519Zig stdLWF trailer signatures
SipHash-2-4Zig stdPacket 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

ComponentStatusDetail
EtherType demuxShippedNetSwitch routes LWF/UTCP/IP at L2
CellID registrationShippedSource hint -> MAC table, per-frame update
Noise XX handshakeShipped3-way, bidirectional, session table
AEAD seal/unsealShippedKernel syscalls (0x800/0x801)
QVL trust bindingShippedBLAKE3(remote_static), 16-peer table
Sovereign pathShippedLWF bypasses LwIP entirely
3-node meshShippedQEMU multicast, all nodes boot + LWF
Ring 0 bitmapPlannedCellID allowlist in NetSwitch
Ring 1 Janus DSLPlannedFirst Janus compiler target
Ring 2 DPIPlannedMembrane agent, Gravity spectrum
VerteilenPlannedMesh overflow distribution