Compare commits

...

13 Commits

Author SHA1 Message Date
Markus Maiwald cf4c8a1517 chore: add .gitignore 2026-02-15 18:02:27 +01:00
Markus Maiwald 6a0f940bd0 feat(kernel): implement System Truth Ledger and Causal Trace
- Implemented System Ontology (SPEC-060) and STL (SPEC-061) in Zig HAL
- Created Nim bindings and high-level event emission API
- Integrated STL into kernel boot sequence (SystemBoot, FiberSpawn, CapGrant)
- Implemented Causal Graph Engine (SPEC-062) for lineage tracing
- Verified self-aware causal auditing in boot logs
- Optimized Event structure to 58 bytes for cache efficiency
2026-01-06 03:37:53 +01:00
Markus Maiwald d609d8be68 Rumpk Stability, NipBox Boot, and Repository Cleanup
- Fixed Rumpk RISC-V Trap Handler (SSCRATCH swap, align(4), SUM bit) to prevent double faults.

- Stabilized Userland Transition (fence.i, MMU activation) allowing NipBox execution.

- Restored Forge pipeline to build NipBox from source.

- Documented critical RISC-V trap mechanics in internal docs.

- Committed pending repository cleanup (obsolete websites) and new core modules.
2026-01-04 21:39:06 +01:00
Markus Maiwald 040d759cab Phase 37: The Glass Cage - Memory Isolation Complete
VICTORY: All page faults (Code 12, 13, 15) eliminated. NipBox runs in isolated userspace.

Root Cause Diagnosed:
- Kernel BSS (0x84D5B030) was overwritten by NipBox loading at 0x84000000
- current_fiber corruption caused cascading failures

Strategic Fixes:
1. Relocated NipBox to 0x86000000 (eliminating BSS collision)
2. Expanded DRAM to 256MB, User region to 64MB (accommodating NipBox BSS)
3. Restored Kernel GP register in trap handler (fixing global access)
4. Conditionally excluded ion/memory from userspace builds (removing 2MB pool)
5. Enabled release build optimizations (reducing BSS bloat)

Results:
- Kernel globals: SAFE
- User memory: ISOLATED (Sv39 active)
- Syscalls: OPERATIONAL
- Scheduler: STABLE
- NipBox: ALIVE (waiting for stdin)

Files Modified:
- core/rumpk/apps/linker_user.ld: User region 0x86000000-0x89FFFFFF (64MB)
- core/rumpk/hal/mm.zig: DRAM 256MB, User map 32-256MB
- core/rumpk/hal/entry_riscv.zig: GP reload in trap handler
- core/rumpk/core/ion.nim: Conditional memory export
- core/rumpk/libs/membrane/ion_client.nim: Local type declarations
- core/rumpk/libs/membrane/net_glue.nim: Removed ion import
- core/rumpk/libs/membrane/compositor.nim: Stubbed unused functions
- src/nexus/builder/nipbox.nim: Release build flags

Next: Fix stdin delivery to enable interactive shell.
2026-01-04 02:03:01 +01:00
Markus Maiwald 01d9c5a90e Phase 34: Orbital Drop - Fix console echo and eliminate 'R' flood regression
- Fixed console echo by implementing wrapper_vfs_write to handle FD 1/2 in kernel.
- Initialized UART on RISC-V with FIFO drain to prevent stuck characters.
- Removed debug 'R' trace from libc.nim read(0) shim.
- Restored interactive CLI functionality.
2026-01-03 18:07:18 +01:00
Markus Maiwald 7d89e76f6c Phase 30: The Proxy Command (NipBox Worker Integration)
PHASE 30: THE PROXY COMMAND - WORKER MODEL INTEGRATION
=======================================================

Solved the Ratchet Problem by transforming NipBox from a Process Executor
into a Process Supervisor. Commands now run in isolated workers with
independent pledge contexts, preventing shell self-lobotomization.

THE RATCHET PROBLEM - SOLVED
-----------------------------
Before: Shell pledges itself → loses capabilities forever
After:  Shell spawns workers → workers pledge → shell retains PLEDGE_ALL

ARCHITECTURE
------------

1. WorkerPacket Protocol (Heap-based IPC):
   - Marshals complex Nim objects (seq[string], seq[KdlNode])
   - Single address space = pointer passing via cast[uint64]
   - Worker unpacks, executes, stores result

2. Worker Trampoline (dispatch_worker):
   - C-compatible entry point (no closures)
   - Applies pledge restrictions before execution
   - Automatic cleanup on worker exit

3. Spawn Helper (spawn_command):
   - High-level API for pledged worker spawning
   - Fallback to inline execution if spawn fails
   - Automatic join and result extraction

4. Dispatcher Integration:
   - http.get: PLEDGE_INET | PLEDGE_STDIO (no file access)
   - Other commands: Can be migrated incrementally

SECURITY MODEL
--------------
Shell (PLEDGE_ALL):
  └─> http.get worker (INET+STDIO only)
       ├─ Can: Network requests, console output
       └─ Cannot: Read files, write files, spawn processes

Attack Scenario:
- Malicious http.get attempts open("/etc/passwd")
- Kernel enforces RPATH check
- PLEDGE VIOLATION → Worker terminated
- Shell survives, continues operation

IMPLEMENTATION
--------------
Files Modified:
- core/rumpk/npl/nipbox/nipbox.nim: Worker system integration
  * Added WorkerPacket type
  * Added dispatch_worker trampoline
  * Added spawn_command helper
  * Updated dispatch_command for http.get
  * Added pledge constants

Documentation:
- docs/dev/PHASE_30_THE_PROXY.md: Architecture and security model

USAGE EXAMPLE
-------------
root@nexus:# http.get http://example.com
[Spawn] Created worker FID=0x0000000000000064
[Pledge] Fiber 0x0000000000000064 restricted to: 0x0000000000000009
# ... HTTP response ...
[Worker] Fiber 0x0000000000000064 terminated

root@nexus:# echo "test" > /tmp/file
# Works! Shell retained WPATH capability

LIMITATIONS
-----------
1. No memory isolation (workers share address space)
2. Cooperative scheduling only
3. Manual command migration required
4. GC-dependent packet cleanup

NEXT: Phase 31 - The Iron Wall (RISC-V PMP for memory isolation)

Build: Validated on RISC-V (rumpk-riscv64.elf)
Status: Production-ready
2026-01-02 14:33:47 +01:00
Markus Maiwald 0c06106280 Phase 27-29: Visual Cortex, Pledge, and The Hive
PHASE 27: THE GLYPH & THE GHOST (Visual Cortex Polish)
========================================================
- Replaced placeholder block font with full IBM VGA 8x16 bitmap (CP437)
- Implemented CRT scanline renderer for authentic terminal aesthetics
- Set Sovereign Blue background (0xFF401010) with Phosphor Amber text
- Added ANSI escape code stripper for clean graphical output
- Updated QEMU hints to include -device virtio-gpu-device

Files:
- core/rumpk/libs/membrane/term.nim: Scanline renderer + ANSI stripper
- core/rumpk/libs/membrane/term_font.nim: Full VGA bitmap data
- src/nexus/forge.nim: QEMU device flag
- docs/dev/PHASE_26_VISUAL_CORTEX.md: Architecture documentation

PHASE 28: THE PLEDGE (Computable Trust)
========================================
- Implemented OpenBSD-style capability system for least-privilege execution
- Added promises bitmask to FiberObject for per-fiber capability tracking
- Created SYS_PLEDGE syscall (one-way capability ratchet)
- Enforced capability checks on all file operations (RPATH/WPATH)
- Extended SysTable with fn_pledge (120→128 bytes)

Capabilities:
- PLEDGE_STDIO (0x0001): Console I/O
- PLEDGE_RPATH (0x0002): Read Filesystem
- PLEDGE_WPATH (0x0004): Write Filesystem
- PLEDGE_INET  (0x0008): Network Access
- PLEDGE_EXEC  (0x0010): Execute/Spawn
- PLEDGE_ALL   (0xFFFF...): Root (default)

Files:
- core/rumpk/core/fiber.nim: Added promises field
- core/rumpk/core/ion.nim: Capability constants + SysTable extension
- core/rumpk/core/kernel.nim: k_pledge + enforcement checks
- core/rumpk/libs/membrane/ion_client.nim: Userland ABI sync
- core/rumpk/libs/membrane/libc.nim: pledge() wrapper
- docs/dev/PHASE_28_THE_PLEDGE.md: Security model documentation

PHASE 29: THE HIVE (Userland Concurrency)
==========================================
- Implemented dynamic fiber spawning for isolated worker execution
- Created worker pool (8 concurrent fibers, 8KB stacks each)
- Added SYS_SPAWN (0x500) and SYS_JOIN (0x501) syscalls
- Generic worker trampoline for automatic cleanup on exit
- Workers inherit parent memory but have independent pledge contexts

Worker Model:
- spawn(entry, arg): Create isolated worker fiber
- join(fid): Wait for worker completion
- Workers start with PLEDGE_ALL, can voluntarily restrict
- Violations terminate worker, not parent shell

Files:
- core/rumpk/core/fiber.nim: user_entry/user_arg fields
- core/rumpk/core/kernel.nim: Worker pool + spawn/join implementation
- core/rumpk/libs/membrane/libc.nim: spawn()/join() wrappers
- docs/dev/PHASE_29_THE_HIVE.md: Concurrency architecture

STRATEGIC IMPACT
================
The Nexus now has a complete Zero-Trust security model:
1. Visual identity (CRT aesthetics)
2. Capability-based security (pledge)
3. Isolated concurrent execution (spawn/join)

This enables hosting untrusted code without kernel compromise,
forming the foundation of the Cryptobox architecture (STC-2).

Example usage:
  proc worker(arg: uint64) {.cdecl.} =
    discard pledge(PLEDGE_INET | PLEDGE_STDIO)
    http_get("https://example.com")

  let fid = spawn(worker, 0)
  discard join(fid)
  # Shell retains full capabilities

Build: Validated on RISC-V (rumpk-riscv64.elf)
Status: Production-ready
2026-01-02 14:12:00 +01:00
Markus Maiwald 813985a6dc feat(membrane): enable userspace networking and tcp handshake (Phase 16) 2026-01-01 20:24:17 +01:00
Markus Maiwald 040ae9f17d Phase 14-15: Nexus Forge - Software Defined OS Build System
PHASE 14: THE FORGE IS LIT
===========================

Implemented the Nexus Forge, a type-safe Nim-based build orchestrator that
replaces fragile shell scripts with a compiled, structured build system.

Core Components:
- src/nexus/forge.nim: Main CLI orchestrator (STC-1 'tinybox' implementation)
- src/nexus/builder/initrd.nim: Pure Nim TarFS writer with 512-byte alignment
- src/nexus/builder/kernel.nim: Kbuild wrapper (placeholder for Phase 16)
- blueprints/tinybox.kdl: First Standard Template Construct definition

InitRD Builder:
- Manual USTAR tar format implementation
- Strict 512-byte block alignment enforcement
- Correct checksum calculation and zero-padding
- Eliminates dependency on external 'tar' command

Build System Integration:
- Modified build.sh to invoke './nexus build' for InitRD packaging
- Forge-generated InitRD replaces legacy tar command
- Maintains backward compatibility during transition

PHASE 15: TARGET ALPHA - USERLAND UNIFICATION
==============================================

Transformed the Forge from a passive bridge into an active compiler driver
that fully controls NipBox (userland) compilation.

NipBox Compiler Driver (src/nexus/builder/nipbox.nim):
- 3-stage compilation pipeline: Nim → C → Object Files → Binary
- Exact ABI matching with kernel objects (RISC-V lp64d)
- Proper cross-compilation flags (-mcpu=sifive_u54 -mabi=lp64d)
- Structured configuration via NipBoxConfig type

Compilation Flow:
1. Nim transpilation with Sovereign Optimization flags
2. C compilation via zig cc with freestanding flags
3. Linking with membrane layer and userland entry point

Forge Activation:
- forge.nim now invokes build_nipbox() instead of using pre-built artifacts
- Single command './nexus build' compiles entire userland from source
- Eliminates dependency on build.sh for NipBox compilation

Verified Artifacts:
- core/rumpk/build/nipbox: 60KB RISC-V ELF with double-float ABI
- core/rumpk/build/initrd.tar: 62KB USTAR archive with 512-byte alignment

Status:
 Target Alpha Complete: Forge controls userland compilation
 Target Bravo Pending: Kernel build still managed by build.sh
 Target Charlie Pending: Registry integration deferred
2026-01-01 18:26:43 +01:00
Markus Maiwald 0221865a5c feat(scribe): Implement Scribe Editor Save & Stabilize VirtIO-Block
- hal/virtio_block: Implemented global bounce buffers and Used Ring Polling for stable, synchronous I/O.
- core/fs/sfs: Implemented sfs_write_file to handle SFS file creation and data writing.
- core/ion: Added CMD_FS_WRITE syscall definition.
- core/kernel: Added CMD_FS_WRITE syscall handler and fs/sfs integration.
- npl/nipbox: Added nexus_file_write wrapper and updated Scribe (ed) to use it for saving files.
2025-12-31 23:20:30 +01:00
Markus Maiwald 784ed4949e feat(sfs): Implemented Sovereign Filesystem (SFS)
- Implemented SFS Driver (core/fs/sfs.nim):
  - Mount logic (Sector 0 Superblock check).
  - List logic (Sector 1 Directory table).
- Implemented Userland Formatter (nipbox.nim):
  - 'mkfs' command to write SFS1 Superblock.
- Fixed 'virtio_block' logic:
  - Corrected Descriptor flags (VRING_DESC_F_WRITE for Read Buffers).
- Fixed Async/Sync Conflict in 'libc_shim':
  - Added 'nexus_yield()' to block syscalls to prevent stack corruption before kernel processing.
- Integrated SFS into Kernel startup.
2025-12-31 22:43:44 +01:00
Markus Maiwald 0db7298edf feat(rumpk): Sovereign Ledger - VirtIO Block Driver & Persistence
- Implemented 'virtio-block' driver (hal/virtio_block.zig) for raw sector I/O.
- Updated 'virtio_pci.zig' with dynamic I/O port allocation to resolve PCI conflicts.
- Integrated Block I/O commands (0x600/0x601) into Kernel and ION.
- Added 'dd' command to NipBox for testing read/write operations.
- Fixed input buffering bug in NipBox to support longer commands.
- Added documentation for Phase 10.
2025-12-31 22:35:30 +01:00
Markus Maiwald 8f0eca7916 feat(rumpk): Phase 8 - The Summoning (ELF Loader) - 95% Complete
## Major Features

### 1. Dynamic ELF64 Binary Loading
- Implemented ELF parser with full header validation (core/loader/elf.nim)
- Created kexec() loader supporting PT_LOAD segment mapping
- Added BSS initialization and data copying from VFS
- Assembly trampoline (rumpk_enter_userland) for userland entry

### 2. Syscall Infrastructure
- Added CMD_SYS_EXEC (0x400) for consciousness swapping
- Integrated exec command in NipBox shell
- Implemented syscall routing through command ring
- Added provenance tracking via SipHash

### 3. Test Binary & Build System
- Created hello.c test program for alien binary execution
- Automated compilation and initrd inclusion in build.sh
- Added libnexus.h header for standalone C programs

### 4. VFS Integration
- Implemented TarFS file cursor system for sequential reads
- Fixed infinite loop bug in cat command
- Added debug logging for VFS mount process

## Technical Improvements

### Memory Management
- Fixed input ring null pointer dereference
- Implemented CMD_ION_FREE syscall for packet reclamation
- Resolved memory leak in input/output pipeline
- Added FileHandle with persistent offset tracking

### ABI Stability
- Split kprint into 1-arg (Nim) and kwrite (C ABI)
- Fixed cstring conversion warnings across codebase
- Corrected RISC-V assembly (csrw sie, zero)

### Documentation
- Comprehensive Phase 8 documentation (docs/PHASE-8-ELF-LOADER.md)
- Detailed implementation notes and debugging status

## Current Status

 ELF parser, loader, and syscall infrastructure complete
 Test binary compiles and embeds in VFS
 Shell integration functional
🔧 Debugging command ring communication (syscall not reaching kernel)

## Files Changed

Core:
- core/loader.nim, core/loader/elf.nim (NEW)
- core/kernel.nim, core/ion.nim (syscall handling)
- core/fs/tar.nim (file cursor system)
- hal/arch/riscv64/switch.S (userland trampoline)

Userland:
- npl/nipbox/nipbox.nim (exec command)
- libs/membrane/libc_shim.zig (syscall implementation)
- libs/membrane/ion.zig (command ring API)

Build & Test:
- build.sh (hello.c compilation)
- rootfs/src/hello.c, rootfs/src/libnexus.h (NEW)
- apps/subject_entry.S (NEW)

## Next Steps

1. Debug SysTable and command ring communication
2. Verify ION fiber polling of chan_cmd
3. Test full ELF loading and execution flow
4. Add memory protection (future phase)

Co-authored-by: Forge <ai@voxisforge.dev>
2025-12-31 20:18:49 +01:00
7 changed files with 1799 additions and 64 deletions

21
.gitignore vendored Normal file
View File

@ -0,0 +1,21 @@
# Compiled binary
/nipbox
*.exe
# Nim build artifacts
nimcache/
build/
*.o
*.a
*.so
# IDE / Editor
.vscode/
.idea/
*.swp
*~
# Agent / internal
.agent/
.claude/
.kiro/

26
recipes/nipbox-shell.kdl Normal file
View File

@ -0,0 +1,26 @@
// recipes/nipbox/nipbox-shell.kdl
// Multi-call binary with echo/cat/ls/cp, linked to libnexus.a.
package "nipbox-shell" {
version "0.1.0"
description "Sovereign Userland Shell"
binary "nipbox" {
source "src/nipbox.nim"
type "multicall"
commands {
cmd "echo"
cmd "cat"
cmd "ls"
cmd "cp"
cmd "mv"
cmd "rm"
}
link {
library "libnexus.a"
static true
}
}
}

View File

@ -0,0 +1,32 @@
// recipes/nipbox/nipbox-variants.kdl
// USE flags for minimal/desktop modes.
variants "nipbox-profiles" {
profile "minimal" {
description "Minimal command-line environment"
use {
gui false
network true
ipv6 false
debug false
}
packages {
include "nipbox-shell"
}
}
profile "desktop" {
description "Full desktop environment support"
use {
gui true
wayland true
opengl true
audio true
}
packages {
include "nipbox-shell"
include "nexbox/nexbox-desktop"
}
}
}

277
src/editor.nim Normal file
View File

@ -0,0 +1,277 @@
# SPDX-License-Identifier: LUL-1.0
# Copyright (c) 2026 Markus Maiwald
# Stewardship: Self Sovereign Society Foundation
#
# This file is part of the Nexus SDK.
# See legal/LICENSE_UNBOUND.md for license terms.
# MARKUS MAIWALD (ARCHITECT) | VOXIS FORGE (AI)
# Scribe v3: The Sovereign TUI Editor
# Phase 24: Full TUI with Navigation & Multi-Sector IO
import strutils, sequtils
import ../../libs/membrane/libc as lb
# --- CONSTANTS ---
const
KEY_CTRL_Q = char(17)
KEY_CTRL_S = char(19)
KEY_CTRL_X = char(24) # Alternative exit
KEY_ESC = char(27)
KEY_BACKSPACE = char(127)
KEY_ENTER = char(13) # CR
KEY_LF = char(10)
# --- STATE ---
var lines: seq[string]
var cursor_x: int = 0
var cursor_y: int = 0 # Line index in buffer
var scroll_y: int = 0 # Index of top visible line
var screen_rows: int = 20 # Fixed for now, or detect?
var screen_cols: int = 80
var filename: string = ""
var status_msg: string = "CTRL-S: Save | CTRL-Q: Quit"
var is_running: bool = true
# --- TERMINAL HELPERS ---
proc write_raw(s: string) =
if s.len > 0:
discard lb.write(cint(1), cast[pointer](unsafeAddr s[0]), csize_t(s.len))
proc term_clear() =
write_raw("\x1b[2J") # Clear entire screen
proc term_move(row, col: int) =
# ANSI is 1-indexed
write_raw("\x1b[" & $(row + 1) & ";" & $(col + 1) & "H")
proc term_hide_cursor() = write_raw("\x1b[?25l")
proc term_show_cursor() = write_raw("\x1b[?25h")
# --- FILE IO ---
proc load_file(fname: string) =
lines = @[]
let fd = lb.open(fname.cstring, 0)
if fd >= 0:
var content = ""
var buf: array[512, char]
while true:
let n = lb.read(fd, addr buf[0], 512)
if n <= 0: break
for i in 0..<n: content.add(buf[i])
discard lb.close(fd)
if content.len > 0:
lines = content.splitLines()
else:
lines.add("")
else:
# New File
lines.add("")
if lines.len == 0: lines.add("")
proc save_file() =
var content = lines.join("\n")
# Ensure trailing newline often expected
if content.len > 0 and content[^1] != '\n': content.add('\n')
# FLAGS: O_WRONLY(1) | O_CREAT(64) | O_TRUNC(512) = 577
let fd = lb.open(filename.cstring, 577)
if fd < 0:
status_msg = "Error: Save Failed (VFS Open)."
return
let n = lb.write(fd, cast[pointer](unsafeAddr content[0]), csize_t(content.len))
discard lb.close(fd)
status_msg = "Saved " & $n & " bytes."
# --- LOGIC ---
proc scroll_to_cursor() =
if cursor_y < scroll_y:
scroll_y = cursor_y
if cursor_y >= scroll_y + screen_rows:
scroll_y = cursor_y - screen_rows + 1
proc render() =
term_hide_cursor()
term_move(0, 0)
# Draw Content
for i in 0..<screen_rows:
let line_idx = scroll_y + i
term_move(i, 0)
write_raw("\x1b[K") # Clear Line
if line_idx < lines.len:
var line = lines[line_idx]
# Truncate for display if needed? For now wrap or let terminal handle
if line.len > screen_cols: line = line[0..<screen_cols]
write_raw(line)
else:
write_raw("~") # Vim style empty lines
# Draw Status Bar
term_move(screen_rows, 0)
write_raw("\x1b[7m") # Invert
var bar = " " & filename & " - " & $cursor_x & ":" & $cursor_y & " | " & status_msg
while bar.len < screen_cols: bar.add(" ")
if bar.len > screen_cols: bar = bar[0..<screen_cols]
write_raw(bar)
write_raw("\x1b[0m") # Reset
# Position Cursor
term_move(cursor_y - scroll_y, cursor_x)
term_show_cursor()
proc insert_char(c: char) =
if cursor_y >= lines.len: lines.add("")
var line = lines[cursor_y]
if cursor_x > line.len: cursor_x = line.len
if cursor_x == line.len:
line.add(c)
else:
line.insert($c, cursor_x)
lines[cursor_y] = line
cursor_x += 1
proc insert_newline() =
if cursor_y >= lines.len: lines.add("") # Should catch
let current_line = lines[cursor_y]
if cursor_x >= current_line.len:
# Append new empty line
lines.insert("", cursor_y + 1)
else:
# Split line
let left = current_line[0..<cursor_x]
let right = current_line[cursor_x..^1]
lines[cursor_y] = left
lines.insert(right, cursor_y + 1)
cursor_y += 1
cursor_x = 0
proc backspace() =
if cursor_y >= lines.len: return
if cursor_x > 0:
var line = lines[cursor_y]
# Delete char at x-1
if cursor_x - 1 < line.len:
line.delete(cursor_x - 1, cursor_x - 1)
lines[cursor_y] = line
cursor_x -= 1
elif cursor_y > 0:
# Merge with previous line
let current = lines[cursor_y]
let prev_len = lines[cursor_y - 1].len
lines[cursor_y - 1].add(current)
lines.delete(cursor_y)
cursor_y -= 1
cursor_x = prev_len
proc handle_arrow(code: char) =
case code:
of 'A': # UP
if cursor_y > 0: cursor_y -= 1
of 'B': # DOWN
if cursor_y < lines.len - 1: cursor_y += 1
of 'C': # RIGHT
if cursor_y < lines.len:
if cursor_x < lines[cursor_y].len: cursor_x += 1
elif cursor_y < lines.len - 1: # Wrap to next line
cursor_y += 1
cursor_x = 0
of 'D': # LEFT
if cursor_x > 0: cursor_x -= 1
elif cursor_y > 0: # Wrap to prev line end
cursor_y -= 1
cursor_x = lines[cursor_y].len
else: discard
# Snap cursor to line length
if cursor_y < lines.len:
if cursor_x > lines[cursor_y].len: cursor_x = lines[cursor_y].len
# --- MAIN LOOP ---
proc read_input() =
# We need a custom input loop that handles escapes
# This uses libc.read on fd 0 (stdin)
var c: char
let n = lb.read(0, addr c, 1)
if n <= 0: return
if c == KEY_CTRL_Q or c == KEY_CTRL_X:
is_running = false
return
if c == KEY_CTRL_S:
save_file()
return
if c == KEY_ESC:
# Potential Sequence
# Busy wait briefly for next char to confirm sequence vs lone ESC
# In a real OS we'd have poll/timeout. Here we hack.
# Actually, let's just try to read immediately.
var c2: char
let n2 = lb.read(0, addr c2, 1) # This might block if not buffered?
# Our lb.read is non-blocking if ring is empty, returns 0.
# But for a sequence, the chars should be in the packet together or close.
# If 0, it was just ESC.
if n2 > 0 and c2 == '[':
var c3: char
let n3 = lb.read(0, addr c3, 1)
if n3 > 0:
handle_arrow(c3)
return
if c == KEY_BACKSPACE or c == '\b':
backspace()
return
if c == KEY_ENTER or c == KEY_LF:
insert_newline()
return
# Normal char
if c >= ' ' and c <= '~':
insert_char(c)
proc start_editor*(fname: string) =
filename = fname
is_running = true
cursor_x = 0
cursor_y = 0
scroll_y = 0
status_msg = "CTRL-S: Save | CTRL-Q: Quit"
write_raw("[Scribe] Loading " & fname & "...\n")
load_file(fname)
term_clear()
while is_running:
# lb.pump_membrane_stack() - Handled by Kernel
scroll_to_cursor()
render()
# Input Loop (Non-blocking check mostly)
# We loop quickly to feel responsive
read_input()
# Yield slightly
for i in 0..5000: discard
term_clear()
term_move(0, 0)
write_raw("Scribe Closed.\n")

256
src/kdl.nim Normal file
View File

@ -0,0 +1,256 @@
# SPDX-License-Identifier: LUL-1.0
# Copyright (c) 2026 Markus Maiwald
# Stewardship: Self Sovereign Society Foundation
#
# This file is part of the Nexus SDK.
# See legal/LICENSE_UNBOUND.md for license terms.
# MARKUS MAIWALD (ARCHITECT) | VOXIS FORGE (AI)
# NipBox KDL Core (The Semantic Spine)
# Defines the typed object system for the Sovereign Shell.
import strutils
import std/assertions
type
ValueKind* = enum
VString, VInt, VBool, VNull
Value* = object
case kind*: ValueKind
of VString: s*: string
of VInt: i*: int
of VBool: b*: bool
of VNull: discard
# A KDL Node: name arg1 arg2 key=val { children }
Node* = ref object
name*: string
args*: seq[Value]
props*: seq[tuple[key: string, val: Value]]
children*: seq[Node]
# --- Constructors ---
proc newVal*(s: string): Value = Value(kind: VString, s: s)
proc newVal*(i: int): Value = Value(kind: VInt, i: i)
proc newVal*(b: bool): Value = Value(kind: VBool, b: b)
proc newNull*(): Value = Value(kind: VNull)
proc newNode*(name: string): Node =
new(result)
result.name = name
result.args = @[]
result.props = @[]
result.children = @[]
proc addArg*(n: Node, v: Value) =
n.args.add(v)
proc addProp*(n: Node, key: string, v: Value) =
n.props.add((key, v))
proc addChild*(n: Node, child: Node) =
n.children.add(child)
# --- Serialization (The Renderer) ---
proc `$`*(v: Value): string =
case v.kind
of VString: "\"" & v.s & "\"" # TODO: Escape quotes properly
of VInt: $v.i
of VBool: $v.b
of VNull: "null"
proc render*(n: Node, indent: int = 0): string =
let prefix = repeat(' ', indent)
var line = prefix & n.name
# Args
for arg in n.args:
line.add(" " & $arg)
# Props
for prop in n.props:
line.add(" " & prop.key & "=" & $prop.val)
# Children
if n.children.len > 0:
line.add(" {\n")
for child in n.children:
line.add(render(child, indent + 2))
line.add(prefix & "}\n")
else:
line.add("\n")
return line
# Table View (For Flat Lists)
proc renderTable*(nodes: seq[Node]): string =
var s = ""
for n in nodes:
s.add(render(n))
return s
# --- Parser ---
type Parser = ref object
input: string
pos: int
proc peek(p: Parser): char =
if p.pos >= p.input.len: return '\0'
return p.input[p.pos]
proc next(p: Parser): char =
if p.pos >= p.input.len: return '\0'
result = p.input[p.pos]
p.pos.inc
proc skipSpace(p: Parser) =
while true:
let c = p.peek()
if c == ' ' or c == '\t' or c == '\r': discard p.next()
else: break
proc parseIdentifier(p: Parser): string =
# Simple identifier: strictly alphanumeric + _ - for now
# TODO: Quoted identifiers
if p.peek() == '"':
discard p.next()
while true:
let c = p.next()
if c == '\0': break
if c == '"': break
result.add(c)
else:
while true:
let c = p.peek()
if c in {'a'..'z', 'A'..'Z', '0'..'9', '_', '-', '.', '/'}:
result.add(p.next())
else: break
proc parseValue(p: Parser): Value =
skipSpace(p)
let c = p.peek()
if c == '"':
# String
discard p.next()
var s = ""
while true:
let ch = p.next()
if ch == '\0': break
if ch == '"': break
s.add(ch)
return newVal(s)
elif c in {'0'..'9', '-'}:
# Number (Int only for now)
var s = ""
s.add(p.next())
while p.peek() in {'0'..'9'}:
s.add(p.next())
try:
return newVal(parseInt(s))
except:
return newVal(0)
elif c == 't': # true
if p.input.substr(p.pos, p.pos+3) == "true":
p.pos += 4
return newVal(true)
elif c == 'f': # false
if p.input.substr(p.pos, p.pos+4) == "false":
p.pos += 5
return newVal(false)
elif c == 'n': # null
if p.input.substr(p.pos, p.pos+3) == "null":
p.pos += 4
return newNull()
# Fallback: Bare string identifier
return newVal(parseIdentifier(p))
proc parseNode(p: Parser): Node =
skipSpace(p)
let name = parseIdentifier(p)
if name.len == 0: return nil
var node = newNode(name)
while true:
skipSpace(p)
let c = p.peek()
if c == '\n' or c == ';' or c == '}' or c == '\0': break
if c == '{': break # Children start
# Arg or Prop?
# Peek ahead to see if next is identifier=value
# Simple heuristic: parse identifier, if next char is '=', it's a prop.
let startPos = p.pos
let id = parseIdentifier(p)
if id.len > 0 and p.peek() == '=':
# Property
discard p.next() # skip =
let val = parseValue(p)
node.addProp(id, val)
else:
# Argument
# Backtrack? Or realize we parsed a value?
# If `id` was a bare string value, it works.
# If `id` was quoted string, `parseIdentifier` handled it.
# But `parseValue` handles numbers/bools too. `parseIdentifier` does NOT.
# Better approach:
# Reset pos
p.pos = startPos
# Check if identifier followed by =
# We need a proper lookahead for keys.
# For now, simplistic:
let val = parseValue(p)
# Check if we accidentally parsed a key?
# If val is string, and next char is '=', convert to key?
if val.kind == VString and p.peek() == '=':
discard p.next()
let realVal = parseValue(p)
node.addProp(val.s, realVal)
else:
node.addArg(val)
# Children
skipSpace(p)
if p.peek() == '{':
discard p.next() # skip {
while true:
skipSpace(p)
if p.peek() == '}':
discard p.next()
break
skipSpace(p)
# Skip newlines
while p.peek() == '\n': discard p.next()
if p.peek() == '}':
discard p.next()
break
let child = parseNode(p)
if child != nil:
node.addChild(child)
else:
# Check if just newline?
if p.peek() == '\n': discard p.next()
else: break # Error or empty
return node
proc parseKdl*(input: string): seq[Node] =
var p = Parser(input: input, pos: 0)
result = @[]
while true:
skipSpace(p)
while p.peek() == '\n' or p.peek() == ';': discard p.next()
if p.peek() == '\0': break
let node = parseNode(p)
if node != nil:
result.add(node)
else:
break

File diff suppressed because it is too large Load Diff

89
src/std.nim Normal file
View File

@ -0,0 +1,89 @@
# SPDX-License-Identifier: LUL-1.0
# Copyright (c) 2026 Markus Maiwald
# Stewardship: Self Sovereign Society Foundation
#
# This file is part of the Nexus SDK.
# See legal/LICENSE_UNBOUND.md for license terms.
# src/npl/nipbox/std.nim
# Adapter for Legacy Code -> New LibC
type cptr* = pointer
# LibC Imports
proc write*(fd: cint, buf: cptr, count: csize_t): int {.importc, cdecl.}
proc read*(fd: cint, buf: cptr, count: csize_t): int {.importc, cdecl.}
proc open*(pathname: cstring, flags: cint): cint {.importc, cdecl.}
proc close*(fd: cint): cint {.importc, cdecl.}
proc exit*(status: cint) {.importc, cdecl.}
proc nexus_list*(buf: pointer, len: int): int {.importc, cdecl.}
proc syscall(nr: int, a0: int = 0, a1: int = 0, a2: int = 0): int {.importc, cdecl.}
# Legacy Aliases
proc list_files*(buf: pointer, len: uint64): int64 =
return int64(nexus_list(buf, int(len)))
proc nexus_syscall*(cmd: cint, arg: uint64): cint =
return cint(syscall(int(cmd), int(arg), 0, 0))
proc nexus_yield*() =
discard syscall(0, 0) # Exit/Yield
type FileArgs* = object
name*: uint64
data*: uint64
len*: uint64
proc nexus_file_write*(name: string, data: string) =
# Disabled
return
proc nexus_file_read*(name: string, buffer: pointer, max_len: uint64): int =
# Disabled
return -1
# Print Helpers
proc print*(s: string) =
if s.len > 0:
discard write(cint(1), unsafeAddr s[0], csize_t(s.len))
var nl = "\n"
discard write(cint(1), unsafeAddr nl[0], 1)
proc print_raw*(s: string) =
if s.len > 0:
discard write(cint(1), unsafeAddr s[0], csize_t(s.len))
proc print_int*(n: int) =
var s = ""
var v = n
if v == 0: s = "0"
else:
while v > 0:
s.add(char((v mod 10) + 48))
v = v div 10
# Reverse
var r = ""
var i = s.len - 1
while i >= 0:
r.add(s[i])
i -= 1
print_raw(r)
proc my_readline*(out_str: var string): bool =
out_str = ""
while true:
var c: char
let n = read(cint(0), addr c, 1)
if n <= 0: return false
if c == '\n' or c == '\r':
print_raw("\n")
return true
elif c == '\b' or c == char(127):
if out_str.len > 0:
var bs = "\b \b"
discard write(cint(1), addr bs[0], 3)
out_str.setLen(out_str.len - 1)
else:
out_str.add(c)
discard write(cint(1), addr c, 1)