No description
Find a file
Markus Maiwald fa063a5ec5
Some checks failed
NexFS CI / Test Suite (push) Failing after 1m19s
NexFS CI / Security Scan (push) Failing after 4s
fix(nexfs): block_count u64 for >4GB filesystems, field order fix, gitignore superpowers
2026-03-31 10:13:08 +02:00
.forgejo/workflows ci: add CI workflow + bump minimum_zig_version to 0.15.0 2026-02-18 05:12:29 +01:00
docs plan: scattered superblock replicas + recompression implementation (v1.2.0) 2026-03-22 20:29:11 +01:00
src/nexfs fix(nexfs): block_count u64 for >4GB filesystems, field order fix, gitignore superpowers 2026-03-31 10:13:08 +02:00
tests feat(mount): recover from scattered superblock replicas when blocks 0+1 corrupt 2026-03-22 23:00:25 +01:00
.gitignore fix(nexfs): block_count u64 for >4GB filesystems, field order fix, gitignore superpowers 2026-03-31 10:13:08 +02:00
.grafignore Add comprehensive grafignore/gitignore for VCS hygiene 2026-03-21 20:06:14 +01:00
build.zig vendor: add minimal libzstd 1.5.7 C source for NexFS compression 2026-03-22 01:33:49 +01:00
build.zig.zon chore: bump version to 1.2.0 / 0.5.0 for feature flags release 2026-03-22 16:11:25 +01:00
LICENSE feat: NexFS sovereign flash filesystem, LSL-1.0 2026-02-15 22:26:16 +01:00
NEXFS-DEVELOPMENT-PLAN.md feat: NexFS v1.0.0 — Sprints 4-6 + hardening, 208 tests 2026-03-11 11:21:18 +01:00
NEXFS-STORAGE-STACK-OVERVIEW.md feat: NexFS v0.3.0 — multi-hash, volume profiles, path resolution, expanded tests 2026-02-17 22:08:13 +01:00
nexfs_size_check fix(nexfs): block_count u64 for >4GB filesystems, field order fix, gitignore superpowers 2026-03-31 10:13:08 +02:00
README.md feat: NexFS v0.3.0 — multi-hash, volume profiles, path resolution, expanded tests 2026-02-17 22:08:13 +01:00
test_zig fix(nexfs): block_count u64 for >4GB filesystems, field order fix, gitignore superpowers 2026-03-31 10:13:08 +02:00

NexFS — The Sovereign Flash Filesystem

Flash-native. Content-addressed. The BitTorrent killer built into your filesystem.

License: LSL-1.0 Zig Status: Alpha Tests: 51/51


Why NexFS Exists

Every filesystem you have used was designed for spinning rust, POSIX, and single machines. NexFS was designed for flash, sovereignty, and mesh networks.

Three profiles. One format. Zero POSIX baggage.

Profile What It Does Footprint Target
nexfs-core Power-safe flash storage with hash-verified metadata ~40KB IoT, Satellite, Sensor, MCU
nexfs-sovereign + Content-defined chunking, Merkle DAG, dedup, time travel ~120KB Phone, Laptop, Desktop
nexfs-mesh + UTCP cluster, gossip, chapter mesh, kinetic credits ~200KB Home Box, Chapter Node, Ground Station

An IoT sensor runs nexfs-core and never loads a byte of network code. A laptop runs nexfs-sovereign with full dedup and snapshots. A home box runs nexfs-mesh and becomes a node in the Chapter Network. Same filesystem format; different cognitive capacity.


The BitTorrent Killer

This is not a metaphor.

When nip install firefox runs on a Sovereign device, NexFS does not download a tarball from a central server. It resolves the package to a list of CAS chunk CIDs (BLAKE3 hashes), checks which chunks it already has locally, and requests the missing ones from Chapter Mesh peers over UTCP. If no peer has the chunk, it falls back to the Seed server.

The result: popular packages propagate through the mesh like a self-healing swarm. The Seed server serves each chunk once to the first node. After that, the mesh takes over.

Why This Beats BitTorrent

BitTorrent NexFS Chapter Mesh
Integration Separate torrent client Native filesystem; nip install just works
Delta Updates Re-download entire file CAS dedup; Firefox 120→121 transfers ~5% delta
Encryption Optional, bolt-on Encrypted at rest by default (Monolith key)
NAT Traversal Tracker/DHT, NAT-punching hell UTCP CellID routing; no NAT problem
Discovery Central tracker or DHT Gossip-based peer discovery
Economy Goodwill (leechers win) Kinetic Credits; contribute more, get more
Dedup Per-file Per-chunk across all files, all nodes

Concrete example: Firefox 120.0.1 and 120.0.2 share 95% of their content-defined chunks. BitTorrent transfers the whole archive again. NexFS transfers 5%.

The Chapter Pool

Every Sovereign home installation dedicates a partition to the Chapter Network:

┌─ /dev/nvme0 ──────────────────────────────────┐
│                                                │
│  Partition 1: nexfs-sovereign (Root, Private)  │
│  ├── /Cas       (System, Immutable)            │
│  ├── /Nexus     (Config, Encrypted)            │
│  └── /Data      (User, Encrypted)              │
│                                                │
│  Partition 2: nexfs-mesh (Chapter Pool)        │
│  ├── Encrypted at rest (Monolith)              │
│  ├── Auto-joins Chapter via UTCP Discovery     │
│  ├── Contributes: Storage + Bandwidth          │
│  └── Receives: Redundancy + Content Access     │
│                                                │
└────────────────────────────────────────────────┘
# During Sovereign installation
nexus forge --chapter "frankfurt-01"

# User sees:
# "How much storage do you want to contribute to the Chapter Network?"
# [Slider: 10GB ... 500GB ... 2TB]
# Default: 10% of disk or 50GB, whichever is smaller

nexfs format /dev/nvme0p2 --mode sovereign-mesh \
  --chapter-id "frankfurt-01" \
  --quota 50G \
  --replication-factor 3

The node operator cannot inspect transit chunks stored on their disk. Sealed with Monolith-derived keys. Plausible deniability by architecture.

Kinetic Credits (SPEC-053)

No altruism. No charity. Engineered reciprocity.

Action Credits
Store 1GB for mesh (per day) +10
Serve 1GB to mesh peers +5
Download 1GB from mesh -2
Download 1GB from Seed (bypass mesh) -10 (penalty)
99.9% uptime (monthly) +100 bonus

Commonwealth packages are always free. Premium content and priority bandwidth cost credits.


Hash Configuration

NexFS supports configurable hash algorithms per volume. The hash algorithm is stored in the Superblock and determines integrity verification for all metadata and CAS operations.

nexfs format --hash blake3-256        # Default. Cryptographic. Future-proof.
nexfs format --hash blake3-128        # Compact sovereign. Same algorithm, truncated output.
nexfs format --hash xxh3-64 --force   # Ultra-constrained only. Birthday collision at ~2³².

The --force flag on XXH3-64 is not paternalism; it is an audit trail. When someone asks in three years why checksums collided, the format log shows it was a conscious decision.

Hash Decision Matrix

Hash ID Algorithm Output Use Case
0x00 BLAKE3 256-bit Default. CAS, Merkle DAG, ProvChain.
0x01 BLAKE3 128-bit Satellite with crypto requirements, tighter block pointers.
0x02 XXH3 64-bit ⚠️ MCU with extreme constraints. Requires --force.

BLAKE3 is an XOF (Extendable Output Function); 128-bit and 256-bit use the same algorithm with different output truncation.

Rule of thumb: 128-bit minimum for anything that lives longer than a reboot.


Architecture

On-Disk Layout

┌─────────────────────────────────────────────┐
│  Block 0: Primary Superblock (128 bytes)    │
├─────────────────────────────────────────────┤
│  Block 1: Backup Superblock (128 bytes)     │
├─────────────────────────────────────────────┤
│  Blocks 2-N: Block Allocation Map (BAM)     │
│  - Allocation status per block              │
│  - Erase counts (wear leveling)             │
│  - Bad block marking                        │
├─────────────────────────────────────────────┤
│  Blocks N+1-N+4: Inode Table                │
│  - File/directory metadata                  │
│  - Inode IDs 1-128                          │
├─────────────────────────────────────────────┤
│  Blocks N+5+: Data Blocks                   │
│  - File/directory contents                  │
│  - Wear-leveled allocation                  │
└─────────────────────────────────────────────┘

Superblock

const Superblock = struct {
    magic: u32,              // 0x4E455846 ("NEXF")
    version: u16,            // Format version
    hash_algo: u8,           // 0x00=BLAKE3-256, 0x01=BLAKE3-128, 0x02=XXH3-64
    profile: u8,             // 0x00=core, 0x01=sovereign, 0x02=mesh
    generation: u64,         // Monotonic; crash recovery
    mount_count: u32,        // Health monitoring
    block_size: u32,
    block_count: u32,
    chapter_id: [16]u8,      // 128-bit CellID (mesh only; zero otherwise)
    mesh_quota: u64,         // Bytes for mesh (mesh only; zero otherwise)
    replication_target: u8,  // Desired copies (default 3)
    checksum: [32]u8,        // Self-checksum (algorithm per hash_algo)
};

Flash Interface

Zero allocation. All buffers provided by caller.

pub const FlashInterface = struct {
    read: *const fn (ctx: *anyopaque, addr: u64, buffer: []u8) NexFSError!usize,
    write: *const fn (ctx: *anyopaque, addr: u64, buffer: []const u8) NexFSError!void,
    erase: *const fn (ctx: *anyopaque, block_addr: BlockAddr) NexFSError!void,
    sync: *const fn (ctx: *anyopaque) NexFSError!void,
};

Key Components

Block Allocation Map (BAM): Per-block metadata tracking allocation status, erase counts for wear leveling, and bad block flags. Generation counter for block age.

Inode Table: File and directory metadata. Supports Regular, Directory, Symlink, and Device node types. Max filename: 255 characters (UTF-8).

Dual Superblock: Primary + backup. If one corrupts, the other survives. No journal to replay; metadata writes are atomic at the block level.


Build System

Core Layer (Zig)

NexFS Core is pure Zig with zero dependencies. Build profiles are controlled via comptime flags:

// nexfs/config.zig
pub const Profile = enum {
    core,       // Block Valve + Hash verification only
    sovereign,  // + CAS, CDC, Merkle DAG, TimeWarp
    mesh,       // + UTCP Storage, Gossip, Replication
};

pub const build_profile: Profile = if (@hasDecl(root, "nexfs_profile"))
    root.nexfs_profile
else
    .core;  // Default: minimal

pub const has_cdc = build_profile == .sovereign or build_profile == .mesh;
pub const has_dag = build_profile == .sovereign or build_profile == .mesh;
pub const has_mesh = build_profile == .mesh;
# Build core (IoT, satellite)
zig build -Dnexfs_profile=core

# Build sovereign (laptop, phone)
zig build -Dnexfs_profile=sovereign

# Build mesh (chapter node, home box)
zig build -Dnexfs_profile=mesh

Sovereign & Mesh Layers (Nim)

The Sovereign extensions (SPEC-085) and Mesh protocol (SPEC-704) run in the Membrane (userland) as NPL services, written in Nim. They consume NexFS Core via the Block Valve (SPEC-083).

# libs/membrane/nexfs/sovereign/config.nim
const
  nexfs_cdc*      = defined(nexfs_cdc)       # Content-Defined Chunking
  nexfs_dag*      = defined(nexfs_dag)       # Merkle DAG Directories
  nexfs_dedup*    = defined(nexfs_dedup)     # Local Deduplication
  nexfs_timewarp* = defined(nexfs_timewarp)  # Snapshot Time Travel
  nexfs_mesh*     = defined(nexfs_mesh)      # UTCP Cluster + Gossip
# Sovereign membrane module
nim c -d:nexfs_cdc -d:nexfs_dag -d:nexfs_dedup -d:nexfs_timewarp nexfs_sovereign.nim

# Mesh membrane module (includes sovereign)
nim c -d:nexfs_cdc -d:nexfs_dag -d:nexfs_dedup -d:nexfs_timewarp -d:nexfs_mesh nexfs_mesh.nim

The split is architectural, not accidental:

  • Zig handles the physics: raw flash I/O, sector checksums, wear leveling, zero-allocation constraints. Kernel-adjacent; runs in the Block Valve.
  • Nim handles the logic: CAS indexing, CDC chunking, Merkle DAG operations, UTCP gossip, replication policy. Userland; runs in the Membrane.

A crash in the Nim Membrane layer does not take down the kernel. The Block Valve keeps serving raw sectors. The Membrane restarts clean. This is the Blink recovery pattern (SPEC-021).


Sovereign Extensions (SPEC-085)

Content-Defined Chunking (FastCDC)

Traditional filesystems split files by fixed offset. Insert one byte at the start of a 1GB file and every block shifts. Dedup dies. Sync re-transfers everything.

NexFS splits by content boundaries using a Gear-hash rolling window:

Parameter Default Configurable
Min chunk 16 KB Per-volume in Superblock
Avg chunk 64 KB Per-volume in Superblock
Max chunk 256 KB Per-volume in Superblock

Identical byte sequences across different files produce identical chunks automatically. VM images sharing a base OS: ~90% dedup. Source repos with branches: ~95%. Software updates: ~95%.

Content-Addressable Storage (CAS)

Every chunk is stored exactly once, addressed by its BLAKE3 hash. Dedup is inline at write time; no background processing, no dedup tables eating RAM.

$ nexfs status /Data
NexFS Sovereign Volume: /Data
  Logical Size:   847.3 GB
  Physical Size:  612.1 GB
  Dedup Ratio:    1.38x
  Chunks:         9,847,221
  Unique Chunks:  7,104,887
  Hash Algorithm: BLAKE3-256

Merkle DAG (The Inode Killer)

POSIX inodes are mutable, numbered, tied to a single filesystem, and impossible to verify. NexFS replaces them with a Merkle DAG: every file is a list of chunk CIDs, every directory is a cryptographic commitment to its children.

Updating a file creates new versions of ancestor nodes up to the root. The unchanged chunks remain shared via CAS refcounting. Cost of an update: one new chunk + O(depth) new DAG nodes.

The DAG root is the only mutable structure in the Sovereign layer. Everything it points to is immutable.

TimeWarp (Snapshots)

Snapshots cost zero additional storage; a snapshot is a 128-byte DAG node pointing to an existing root. Storage grows only when data changes after the snapshot.

# Create
$ nexfs snap create "before-upgrade"

# List
$ nexfs snap list
  2026-02-16 14:30  before-upgrade     blake3:d4e2...
  2026-02-15 03:00  daily-2026-02-15   blake3:a1b2...

# Browse (read-only mount)
$ nexfs snap mount "before-upgrade" /Data/Volume/Local/timewarp

# Rollback (atomic; one Root Register swap)
$ nexfs snap rollback "before-upgrade"

# Diff
$ nexfs snap diff "daily-2026-02-14" "daily-2026-02-15"
  + /Data/User/markus/Documents/contract-final.pdf  (247 KB, 4 chunks)
  ~ /Data/User/markus/.config/nexshell.kdl          (2 chunks changed)
  - /Data/Cache/nip/build-artifacts/stale.o         (removed)

Automatic snapshots via KDL config:

nexfs {
  timewarp {
    auto-snap-interval "6h"
    retention-daily 7
    retention-weekly 4
    retention-monthly 12
    gc-orphan-delay "24h"
  }
}

UTCP Storage Protocol (SPEC-704)

The wire protocol for distributed block exchange between Mesh nodes. 16-byte extension header on existing UTCP frames (SPEC-093).

Operations

Category Ops Purpose
Block HAVE, WANT, PUT, DROP, DENY Chunk exchange and announcements
DAG SYNC, DIFF, WANT_TREE Merkle root comparison and tree sync
Peer ANNOUNCE, HEARTBEAT, LEAVING Discovery, health, graceful departure

Flow

Node A                          Node B
  │                               │
  ├──BLOCK_WANT(cid:a7f2...)────►│
  │                               ├── CAS lookup + verify
  │◄──BLOCK_PUT(cid:a7f2...)─────┤
  ├── BLAKE3 verify               │
  ├── CAS store                   │

Latency target: < 200µs on LAN. Credit-based flow control prevents fast nodes from overwhelming slow ones.

Security

Every BLOCK_PUT is hash-verified by the receiver. A malicious node that sends garbage fails immediately; BLAKE3 does not lie. Peers authenticate via Ed25519 CellID signatures (SPEC-051). Poisoning is architecturally impossible: the CID is the hash. You cannot forge a valid CID for garbage data without breaking BLAKE3.


Quick Start

Installation

Add NexFS to your build.zig.zon:

.{
    .name = "your-project",
    .version = "0.1.0",
    .dependencies = .{
        .nexfs = .{
            .url = "https://git.sovereign-society.org/nexus/nexfs/archive/main.tar.gz",
            .hash = "...",
        },
    },
}

Basic Usage

const std = @import("std");
const nexfs = @import("nexfs");

pub fn main() !void {
    // 1. Provide your flash HAL
    var flash = MyFlash{ .flash_data = try allocator.alloc(u8, 1024 * 1024) };

    // 2. Provide buffers (zero dynamic allocation)
    var read_buf: [4096]u8 = undefined;
    var write_buf: [4096]u8 = undefined;
    var workspace: [256]u8 = undefined;

    const config = nexfs.Config{
        .flash = .{
            .ctx = &flash,
            .read = MyFlash.read,
            .write = MyFlash.write,
            .erase = MyFlash.erase,
            .sync = MyFlash.sync,
        },
        .device_size = 1024 * 1024,
        .block_size = 4096,
        .block_count = 256,
        .page_size = 256,
        .hash_algo = .blake3_256,   // Configurable per volume
        .read_buffer = &read_buf,
        .write_buffer = &write_buf,
        .workspace = &workspace,
        .time_source = null,
        .verbose = true,
    };

    // 3. Format
    try nexfs.format(&config.flash, &config, &write_buf);

    // 4. Write
    var fs = try nexfs.NexFS.init(allocator, config);
    const fd = try fs.create("/config.txt");
    try fs.write(fd, "hello nexfs");
    try fs.close(fd);

    // 5. Read
    var buf: [64]u8 = undefined;
    const fd2 = try fs.open("/config.txt");
    const len = try fs.read(fd2, &buf);
    try std.io.getStdOut().writeAll(buf[0..len]);
    try fs.close(fd2);
}

Raspberry Pi + SPI Flash (1MB):

.block_size = 4096, .page_size = 256, .block_count = 256,
.hash_algo = .blake3_128,  // Crypto integrity, compact pointers

ESP32 + Flash (4MB):

.block_size = 4096, .page_size = 256, .block_count = 1024,
.hash_algo = .xxh3_128,    // Speed priority, local trust

Microcontroller + NOR Flash (512KB):

.block_size = 2048, .page_size = 256, .block_count = 256,
.hash_algo = .xxh3_128,    // Minimal overhead

Use Cases

Libertaria Mesh Nodes

Primary use case. NexFS provides the persistence layer for SoulKeys, peer tables, trust graphs, and message queues.

┌─────────────────────────────────────────┐
│        Libertaria Capsule Node          │
├─────────────────────────────────────────┤
│  L3 Gossip (QVL Trust Edges)            │
│  L2 Session (Noise Handshakes)          │
│  L1 Identity (SoulKeys)                 │
│  NexFS (Persistent Storage)         ◄───┤
│  Raw Flash (NAND/NOR/SPI)               │
└─────────────────────────────────────────┘

Embedded Sovereign Devices

Solar monitors, weather networks, pager devices, home automation. Anywhere you need persistence on raw flash without an FTL, an OS, or an allocator.

Sovereign Desktops and Laptops

Full dedup, Merkle DAG directories, TimeWarp snapshots. Replaces EXT4/Btrfs/APFS with a filesystem designed for content-addressing from day one.

Chapter Network Nodes

Home boxes that contribute storage to the decentralized mesh. Package distribution, content sharing, redundancy; all native to the filesystem.


Design Philosophy

Five Principles

  1. No Secrets. All code is open source and auditable (LSL-1.0).
  2. No Dependencies. Zero external libraries in Core. Pure Zig.
  3. No Vendor Lock-in. Standard flash interface. Portable anywhere.
  4. No Hidden Allocation. Explicit memory management. Caller provides all buffers.
  5. No Trust Required. Every metadata structure is hash-verified. Every chunk is content-addressed.

Flash-Aware Design

Raw flash, not FTL. Predictable performance without FTL latency spikes. Full control over wear leveling. No consumer-grade write amplification. No background garbage collection burning your battery.

Wear Leveling Strategy: Track erase counts per block in the BAM. Prefer lowest-count blocks for writes. Reserve high-count blocks for cold data. Target: even distribution across flash lifetime.


Performance

Operation Typical Latency Notes
Mount < 10ms Read superblock, validate hash
Format 100-500ms Initialize all metadata blocks
File Create 5-20ms Allocate inode, write metadata
File Read (4KB) 1-5ms Single block read + hash verify
File Write (4KB) 10-30ms Erase + write cycle
CDC Chunking > 2 GB/s FastCDC with SIMD gear-hash (sovereign)
CAS Put (dedup hit) < 5µs Hash lookup + refcount increment (sovereign)
Snapshot Create < 10µs One DAG node + Root Register swap (sovereign)
BLOCK_WANT→PUT < 200µs LAN round-trip (mesh)

Memory: Minimum 2 × block_size + page_size (~8.5KB for 4KB blocks). No allocator required.


Testing

Current: 51/51 tests passing (100%)

zig build test            # Run all tests
zig build test -Dverbose  # Verbose output

Coverage: Superblock validation, hash algorithms (BLAKE3-256, BLAKE3-128, XXH3-64), block allocation/deallocation, inode CRUD, directory operations (create/delete/lookup/iterate), file I/O (read/write/seek), path resolution, format round-trip, createFile/createDir, deleteFile/deleteDir, error conditions.


Specifications

NexFS is defined by three SPECs that build on each other:

SPEC Name Scope
SPEC-084 NexFS Core Format Superblock, block pointers, hash-ID, profile byte. The foundation.
SPEC-085 NexFS Sovereign Extensions FastCDC, CAS, Merkle DAG, local dedup, TimeWarp. The mind.
SPEC-704 UTCP Storage Extensions Wire protocol for mesh block exchange, gossip, replication. The network.
SPEC-084 (Core) ──► SPEC-085 (Sovereign) ──► SPEC-704 (Mesh)
     │                      │                       │
   ~40KB                 +80KB                   +80KB
    IoT                  Laptop                 Home Box

Each SPEC is independently implementable. core compiles without the others.


Roadmap

v0.2.0 (Q2 2026) — Sovereign Profile

SPEC-085 implementation: FastCDC chunker, CAS store with inline dedup, Merkle DAG directory structure, TimeWarp snapshots, active wear leveling, bad block management, hash algorithm selection.

v0.3.0 (Q3 2026) — Mesh Profile

SPEC-704 implementation: UTCP storage protocol, gossip-based peer discovery, cross-node dedup, Chapter Mesh integration, Kinetic Credits, credit-based flow control, LZ4 compression, nexfs fsck.

v1.0.0 (Q4 2026) — Production

XChaCha20-Poly1305 encryption, CRDT for concurrent DAG edits, multi-chapter federation, production hardening, full Libertaria stack integration.


Security

Data Integrity: Configurable hash algorithm (BLAKE3-256 default) protects all metadata. Dual superblock survives single-block corruption. CAS verification on every read; silent corruption is impossible.

Power-Loss Resilience: Primary + backup superblock. Atomic metadata writes. No journal to replay. Sovereign layer: immutable DAG with single mutable Root Register; odd generation = incomplete write = automatic rollback.

Mesh Security: Every chunk is hash-verified on receipt. Peer authentication via Ed25519 CellID signatures. Transit chunks encrypted at rest with Monolith-derived keys. Poisoning is architecturally impossible.

Planned: XChaCha20-Poly1305 encryption at rest, AEAD, SoulKey-derived key integration.


Contributing

Status: Alpha (v0.3.0)

Areas: Wear leveling algorithm, bad block detection, performance optimization, test coverage, documentation.

Code Style: Zig style guidelines. SPDX license headers required. BDD-style tests preferred. Panopticum architecture compliance.


License

LSL-1.0 (Libertaria Sovereign License 1.0)

Open source. Auditable. Free for sovereign applications. Modifications must be contributed back. No commercial restrictions for sovereign use.

See LICENSE for full text.


Community

Repository: git.sovereign-society.org/nexus/nexfs

Nexus Ecosystem: rumpk · nip · nipbox · nexfs

Related: Libertaria Stack · Janus Language

Inspired by: LittleFS (flash resilience), JFFS2 (journaling flash), IPFS (content addressing). Built to surpass all three.


"The past is not deleted. It is addressed."