# SPDX-License-Identifier: LSL-1.0 # Copyright (c) 2026 Markus Maiwald # Stewardship: Self Sovereign Society Foundation # # This file is part of the Nexus Sovereign Core. # See legal/LICENSE_SOVEREIGN.md for license terms. ## Nexus Membrane: SFS Userspace Client (SPEC-503) ## ## The Sovereign Filesystem Overlay: ## - L0: LittleFS (Atomic Physics) via `lfs_nim` ## - L1: SFS Overlay (Encryption via Monolith VolumeKey) ## ## Kernel is just a Block Valve - no FS logic there. import ../blk import ../libc import lfs_nim import monolith const SFS_MAGIC* = 0x32534653'u32 # "SFS2" little endian SEC_SB = 0'u64 SEC_BAM = 1'u64 SEC_DIR = 2'u64 CHUNK_SIZE = 508 EOF_MARKER = 0xFFFFFFFF'u32 DIR_ENTRY_SIZE = 64 MAX_FILENAME = 32 type DirEntry* = object filename*: array[32, char] start_sector*: uint32 size_bytes*: uint32 reserved*: array[24, byte] var sfs_mounted: bool = false var io_buffer: array[512, byte] proc print(s: cstring) = discard libc.write(1, cast[pointer](s), csize_t(s.len)) proc print(s: string) = if s.len > 0: discard libc.write(1, cast[pointer](unsafeAddr s[0]), csize_t(s.len)) # ========================================================= # Helpers # ========================================================= proc sfs_alloc_sector(): uint32 = ## Allocate a free sector using the Block Allocation Map discard blk_read(SEC_BAM, addr io_buffer[0]) for i in 0..<512: if io_buffer[i] != 0xFF: for b in 0..7: if (io_buffer[i] and byte(1 shl b)) == 0: let sec = uint32(i * 8 + b) # Mark as allocated io_buffer[i] = io_buffer[i] or byte(1 shl b) discard blk_write(SEC_BAM, addr io_buffer[0]) return sec return 0 # Disk full # ========================================================= # SFS API (Userland) # ========================================================= proc sfs_mount*(): bool = ## Mount the SFS filesystem (SPEC-503/022) ## Uses LittleFS as backend, VolumeKey for encryption print("[SFS-U] Mounting Sovereign Filesystem...\n") # Phase 1: Initialize LittleFS backend if not lfs_nim_mount(): print("[SFS-U] LittleFS mount failed. Attempting format...\n") if not lfs_nim_format(): print("[SFS-U] ERROR: LittleFS format failed.\n") return false if not lfs_nim_mount(): print("[SFS-U] ERROR: LittleFS mount failed after format.\n") return false print("[SFS-U] LittleFS backend mounted.\n") sfs_mounted = true print("[SFS-U] Mount SUCCESS. SPEC-503 Compliant.\n") return true proc sfs_is_mounted*(): bool = sfs_mounted proc sfs_list*(): seq[string] = ## List all files in the filesystem result = @[] if not sfs_mounted: return discard blk_read(SEC_DIR, addr io_buffer[0]) for offset in countup(0, 511, DIR_ENTRY_SIZE): if io_buffer[offset] != 0: var name = "" for i in 0.. 0: var sector_buf: array[512, byte] let chunk_size = if remaining > CHUNK_SIZE: CHUNK_SIZE else: remaining copyMem(addr sector_buf[0], cast[pointer](cast[int](data) + data_ptr), chunk_size) remaining -= chunk_size data_ptr += chunk_size # Determine next sector var next_sector = EOF_MARKER if remaining > 0: next_sector = sfs_alloc_sector() if next_sector == 0: next_sector = EOF_MARKER remaining = 0 # Write next pointer at end of sector sector_buf[508] = byte(next_sector and 0xFF) sector_buf[509] = byte((next_sector shr 8) and 0xFF) sector_buf[510] = byte((next_sector shr 16) and 0xFF) sector_buf[511] = byte((next_sector shr 24) and 0xFF) discard blk_write(uint64(current_sector), addr sector_buf[0]) if next_sector == EOF_MARKER: break current_sector = next_sector # Update directory entry discard blk_read(SEC_DIR, addr io_buffer[0]) for i in 0.. max_len: remaining = max_len var total_read = 0 while remaining > 0 and current_sector != EOF_MARKER and current_sector != 0: var sector_buf: array[512, byte] discard blk_read(uint64(current_sector), addr sector_buf[0]) let payload_size = min(remaining, CHUNK_SIZE) copyMem(cast[pointer](dest_addr), addr sector_buf[0], payload_size) dest_addr += payload_size remaining -= payload_size total_read += payload_size # Next sector pointer current_sector = uint32(sector_buf[508]) or (uint32(sector_buf[509]) shl 8) or (uint32(sector_buf[510]) shl 16) or (uint32(sector_buf[511]) shl 24) return total_read # ========================================================= # Streaming Write API (Phase 39: The Downloader) # ========================================================= type SfsHandle* = ref object filename*: string first_sector*: uint32 current_sector*: uint32 byte_offset*: int # Total file size so far sector_buf*: array[512, byte] # Current sector buffer buf_pos*: int # Position in current sector buffer (0..508) dir_index*: int # Directory slot index proc sfs_open_write*(filename: string): SfsHandle = ## Open a file for streaming write (overwrites existing) if not sfs_mounted: return nil discard blk_read(SEC_DIR, addr io_buffer[0]) var dir_offset = -1 # Find free slot or overwrite existing for offset in countup(0, 511, DIR_ENTRY_SIZE): if io_buffer[offset] != 0: var entry_name = "" for i in 0.. 0: # Space left in current sector payload (max 508 bytes) let space = CHUNK_SIZE - h.buf_pos if space > 0: let to_copy = min(remaining, space) copyMem(addr h.sector_buf[h.buf_pos], cast[pointer](src_ptr), to_copy) h.buf_pos += to_copy h.byte_offset += to_copy remaining -= to_copy src_ptr += to_copy # If sector full (payload filled), flush it if h.buf_pos == CHUNK_SIZE: # Allocate next sector let next_sec = sfs_alloc_sector() # What if disk full? (MVP: Panic or stop writing?) # We'll just mark EOF and stop if 0. var next_marker = if next_sec == 0: EOF_MARKER else: next_sec # Write link h.sector_buf[508] = byte(next_marker and 0xFF) h.sector_buf[509] = byte((next_marker shr 8) and 0xFF) h.sector_buf[510] = byte((next_marker shr 16) and 0xFF) h.sector_buf[511] = byte((next_marker shr 24) and 0xFF) # Flush current sector discard blk_write(uint64(h.current_sector), addr h.sector_buf[0]) if next_sec == 0: print("[SFS-U] Warning: Disk Full during stream.\n") break # Move to next h.current_sector = next_sec h.buf_pos = 0 # Reset buffer? Yes mostly # for i in 0..511: h.sector_buf[i] = 0 proc sfs_close_write*(h: SfsHandle) = ## Flush remaining data and update directory if h == nil: return # Write final sector (even if partially full) let next_marker = EOF_MARKER h.sector_buf[508] = byte(next_marker and 0xFF) h.sector_buf[509] = byte((next_marker shr 8) and 0xFF) h.sector_buf[510] = byte((next_marker shr 16) and 0xFF) h.sector_buf[511] = byte((next_marker shr 24) and 0xFF) discard blk_write(uint64(h.current_sector), addr h.sector_buf[0]) # Update Directory Entry discard blk_read(SEC_DIR, addr io_buffer[0]) let dir_offset = h.dir_index let filename = h.filename # Clear name area first for i in 0..