From 668e79504db502a2f2eed1592c37b9158574a03a Mon Sep 17 00:00:00 2001 From: Markus Maiwald Date: Sun, 4 Jan 2026 21:39:06 +0100 Subject: [PATCH] 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 .agent/tips. - Committed pending repository cleanup (obsolete websites) and new core modules. --- editor.nim | 9 +- kdl.nim | 7 ++ nipbox.nim | 356 +++++++++++++++++++++++++++++++++++++++++++---------- std.nim | 7 ++ 4 files changed, 310 insertions(+), 69 deletions(-) diff --git a/editor.nim b/editor.nim index 719a9c4..1370b53 100644 --- a/editor.nim +++ b/editor.nim @@ -1,3 +1,10 @@ +# 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 @@ -254,7 +261,7 @@ proc start_editor*(fname: string) = term_clear() while is_running: - lb.pump_membrane_stack() # Keep net alive if needed + # lb.pump_membrane_stack() - Handled by Kernel scroll_to_cursor() render() diff --git a/kdl.nim b/kdl.nim index a5e351c..f49d30a 100644 --- a/kdl.nim +++ b/kdl.nim @@ -1,3 +1,10 @@ +# 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. diff --git a/nipbox.nim b/nipbox.nim index 1296984..bf172c9 100644 --- a/nipbox.nim +++ b/nipbox.nim @@ -1,10 +1,17 @@ +# 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/nipbox.nim # Phase 21: The Teleporter - Networked Object Pipelines import strutils, parseutils, tables, sequtils, json import kdl import ../../libs/membrane/libc as lb -import ../../libs/membrane/libc_net as net +import ../../libs/membrane/libc_net as lnet import ../../libs/membrane/fs/sfs_user as sfs import editor import ../../libs/membrane/term # Phase 26: Visual Cortex @@ -143,8 +150,7 @@ proc spawn_command(cmd_fn: proc(args: seq[string], input: PipelineData): Pipelin discard lb.join(fid) return packet.output - discard lb.join(fid) - return packet.output + proc cmd_crash*(args: seq[string], input: PipelineData): PipelineData = print("[NipBox] PREPARING TO CRASH...\n") @@ -160,9 +166,6 @@ proc cmd_crash*(args: seq[string], input: PipelineData): PipelineData = # Spawn the suicider return spawn_command(worker_crash, args, input, PLEDGE_ALL) - # Spawn the suicider - return spawn_command(worker_crash, args, input, PLEDGE_ALL) - proc cmd_upgrade*(args: seq[string], input: PipelineData): PipelineData = if args.len < 1: print("Usage: sys.upgrade \n") @@ -204,6 +207,67 @@ proc cmd_mount*(args: seq[string], input: PipelineData): PipelineData = print("[mount] System Disk Engaged.\n") return @[] +proc cmd_cp*(args: seq[string], input: PipelineData): PipelineData = + if args.len < 2: + print("Usage: cp \n") + return @[] + + let src = args[0] + let dest = args[1] + + let fd_src = lb.open(src.cstring, 0) # O_RDONLY + if fd_src < 0: + print("cp: cannot stat '" & src & "': No such file\n") + return @[] + + # O_WRONLY(1) | O_CREAT(64) | O_TRUNC(512) = 577 + let fd_dest = lb.open(dest.cstring, 577) + if fd_dest < 0: + print("cp: cannot create '" & dest & "'\n") + discard lb.close(fd_src) + return @[] + + var buf: array[4096, char] + var total = 0 + while true: + let n = lb.read(fd_src, addr buf[0], 4096) + if n <= 0: break + let written = lb.write(fd_dest, addr buf[0], csize_t(n)) + if written < 0: + print("cp: write error\n") + break + total += int(written) + + discard lb.close(fd_src) + discard lb.close(fd_dest) + print("[OK] Copied " & $total & " bytes.\n") + return @[] + +proc cmd_mv*(args: seq[string], input: PipelineData): PipelineData = + if args.len < 2: + print("Usage: mv \n") + return @[] + + # Step 1: Copy + print("[mv] Copying...\n") + discard cmd_cp(args, input) + + # Step 2: Unlink (Not yet supported by Kernel) + print("[mv] Warning: Original file '" & args[0] & "' retained (SFS unlink not implemented).\n") + return @[] + +proc cmd_touch*(args: seq[string], input: PipelineData): PipelineData = + if args.len < 1: + print("Usage: touch \n") + return @[] + + let fd = lb.open(args[0].cstring, 577) # O_CREAT | O_TRUNC + if fd >= 0: + discard lb.close(fd) + else: + print("touch: cannot touch '" & args[0] & "'\n") + return @[] + proc cmd_matrix*(args: seq[string], input: PipelineData): PipelineData = let state = if args.len > 0: args[0].toUpperAscii() else: "STATUS: NOMINAL" print("[matrix] " & state & "\n") @@ -372,7 +436,7 @@ proc cmd_http_get*(args: seq[string], input: PipelineData): PipelineData = # Wait for establishment (pumping the stack) var timeout = 0 while timeout < 1000: - lb.pump_membrane_stack() + # lb.pump_membrane_stack() - Handled by Kernel # Check if connected (we need a way to check socket state) # For now, let's assume if we can send, we are connected or it will buffer. # In our net_glue, glue_write returns -1 if not established. @@ -395,7 +459,7 @@ proc cmd_http_get*(args: seq[string], input: PipelineData): PipelineData = var buf: array[2048, char] timeout = 0 while timeout < 5000: - lb.pump_membrane_stack() + # lb.pump_membrane_stack() let n = lb.recv(cint(fd), addr buf[0], 2048, 0) if n > 0: for i in 0.. \n") return @[] @@ -447,50 +505,49 @@ proc cmd_http_download*(args: seq[string], input: PipelineData): PipelineData = let ip_str = parts[0] let port = uint16(parseInt(parts[1])) - let ip_parts = ip_str.split('.') - if ip_parts.len != 4: return @[] - let ip_val = (uint32(parseInt(ip_parts[0])) shl 0) or - (uint32(parseInt(ip_parts[1])) shl 8) or - (uint32(parseInt(ip_parts[2])) shl 16) or - (uint32(parseInt(ip_parts[3])) shl 24) + # Parse IP + var ip_val: uint32 = 0 + try: + let p = ip_str.split('.') + ip_val = (uint32(parseInt(p[0])) and 0xFF) or + ((uint32(parseInt(p[1])) and 0xFF) shl 8) or + ((uint32(parseInt(p[2])) and 0xFF) shl 16) or + ((uint32(parseInt(p[3])) and 0xFF) shl 24) + except: + print("Error: Invalid IP\n") + return @[] print("[Download] Connecting to " & host_part & "...\n") let fd = lb.socket(2, 1, 0) - if fd < 100: return @[] + if fd < 0: return @[] - type SockAddrIn = object - sin_family: uint16 - sin_port: uint16 - sin_addr: uint32 - sin_zero: array[8, char] + # SockAddr setup + var addr_buf: array[16, byte] + copyMem(addr addr_buf[2], unsafeAddr port, 2) + copyMem(addr addr_buf[4], unsafeAddr ip_val, 4) - var addr_in: SockAddrIn - addr_in.sin_family = 2 - addr_in.sin_port = ((port and 0xFF) shl 8) or (port shr 8) - addr_in.sin_addr = ip_val - - if lb.connect(fd, addr addr_in, sizeof(addr_in)) < 0: + if lb.connect(fd, addr addr_buf[0], 16) < 0: print("Error: Connection Failed.\n") return @[] - # Wait for connection - var timeout = 0 - while timeout < 1000: - lb.pump_membrane_stack() - timeout += 1 - for i in 0..1000: discard - # Request - let req = "GET " & path_str & " HTTP/1.1\r\nHost: " & ip_str & "\r\nConnection: close\r\n\r\n" + let req = "GET " & path_str & " HTTP/1.0\r\nHost: " & ip_str & "\r\nConnection: close\r\n\r\n" if lb.send(cint(fd), cast[pointer](unsafeAddr req[0]), csize_t(req.len), 0) <= 0: print("Error: Send Failed.\n") discard lb.close(cint(fd)) return @[] - # Open File - let fd_file = lb.open(outfile.cstring, 577) # O_WRONLY|O_CREAT|O_TRUNC - if fd_file < 0: - print("Error: Cannot open output file " & outfile & "\n") + # Mount SFS if needed + if not sfs.sfs_is_mounted(): + if not sfs.sfs_mount(): + print("Error: Could not mount SFS.\n") + discard lb.close(cint(fd)) + return @[] + + # Open SFS Stream + let sfs_h = sfs.sfs_open_write(outfile) + if sfs_h == nil: + print("Error: Could not create file " & outfile & "\n") discard lb.close(cint(fd)) return @[] @@ -500,10 +557,11 @@ proc cmd_http_download*(args: seq[string], input: PipelineData): PipelineData = var header_acc = "" var header_parsed = false var total_bytes = 0 - timeout = 0 + var content_len = -1 + var timeout = 0 while timeout < 10000: - lb.pump_membrane_stack() + # Use libc shim which pumps stack let n = lb.recv(cint(fd), addr buf[0], 4096, 0) if n > 0: @@ -513,11 +571,23 @@ proc cmd_http_download*(args: seq[string], input: PipelineData): PipelineData = let sep = header_acc.find("\r\n\r\n") if sep != -1: header_parsed = true + + # Try to find Content-Length + # Quick hacky parse + let lower_head = header_acc.toLowerAscii() + let cl_idx = lower_head.find("content-length:") + if cl_idx != -1: + let end_line = lower_head.find("\r\n", cl_idx) + if end_line != -1: + try: + content_len = parseInt(lower_head[cl_idx+15.. 0: + # let pct = (total_bytes * 100) div content_len + if total_bytes mod 10240 < int(n): print(".") + else: + if total_bytes mod 10240 < int(n): print(".") - - total_bytes += n - if total_bytes mod 50000 == 0: discard # print(".") elif n == 0: break else: timeout += 1 - for i in 0..1000: discard + # Busy wait / pump handled in recv? + # Recv calls pump_membrane_stack loop + # But if we return -1 (EAGAIN), we need to retry. + # My libc.libc_recv returns 0 on closed? + # Actually libc_recv in step 945 waits until data or closed. + # So n==0 means closed. + # Wait, libc.nim recv implementation: + # while true: pump; if data return n; if closed return 0. + # So it blocks until data. + # Thus n > 0 always unless closed. + break - discard lb.close(fd_file) + sfs.sfs_close_write(sfs_h) discard lb.close(cint(fd)) - print("\n[Download] Complete. " & $total_bytes & " bytes.\n") + print("\n[Download] Complete. " & $total_bytes & " bytes written to " & outfile & " (Glass Vault).\n") return @[] # Phase 37: HTTP Verification Tool @@ -550,21 +636,21 @@ proc cmd_http_test*(args: seq[string], input: PipelineData): PipelineData = let host = args[0] print("Dialing " & host & ":80...\n") - let fd = net.net_dial_tcp(host, 80) + let fd = lnet.net_dial_tcp(host, 80) if fd < 0: print("Connection Failed! Error: " & $fd & "\n") return @[] print("Connected! Sending GET request...\n") - discard net.net_send(fd, "GET / HTTP/1.0\r\nHost: " & host & "\r\nConnection: close\r\n\r\n") + discard lnet.net_send(fd, "GET / HTTP/1.0\r\nHost: " & host & "\r\nConnection: close\r\n\r\n") print("Waiting for response...\n") # Simple read loop var total = 0 while true: - lb.pump_membrane_stack() - let resp = net.net_recv(fd, 512) + # lb.pump_membrane_stack() + let resp = lnet.net_recv(fd, 512) if resp.len > 0: print(resp) total += resp.len @@ -573,7 +659,62 @@ proc cmd_http_test*(args: seq[string], input: PipelineData): PipelineData = break print("\n[HTTP] Closed. Total bytes: " & $total & "\n") - net.net_close(fd) + lnet.net_close(fd) + return @[] + +proc cmd_http_serve*(args: seq[string], input: PipelineData): PipelineData = + print("[Server] Starting Nexus Web/1.0...\n") + + let port: uint16 = if args.len > 0: uint16(parseInt(args[0])) else: 80 + + let s = lb.socket(2, 1, 0) + if s < 0: + print("Error: Socket creation failed.\n") + return @[] + + # Bind 0.0.0.0:port + var addr_buf: array[16, byte] + addr_buf[0] = 2 # AF_INET + copyMem(addr addr_buf[2], unsafeAddr port, 2) + # IP 0.0.0.0 is default 0s + + if lb.bind_socket(s, addr addr_buf[0], 16) < 0: + print("Error: Bind failed.\n") + return @[] + + if lb.listen(s, 1) < 0: + print("Error: Listen failed.\n") + return @[] + + print("[Server] Listening on port " & $port & "...\n") + + while true: + # Accept blocks and pumps stack + let client = lb.accept(s, nil, nil) + if client < 0: + print("Error: Accept failed.\n") + continue + + print("[Server] Client Connected (FD " & $client & ")\n") + + var buf: array[1024, char] + let n = lb.recv(client, addr buf[0], 1024, 0) + + if n > 0: + var req = "" + for i in 0..\n") + return @[] + + let host = args[0] + print("[NipBox] Dialing " & host & ":80...\n") + + # Use libc.socket/connect (Phase 38 Shim) + let fd = lb.socket(2, 1, 0) + if fd < 0: + print("Socket Error\n") + return @[] + + # Parse IP (Quick hack for 10.0.2.2) + # We need proper parsing but let's assume raw IP for MVP + var ip_val: uint32 = 0 + try: + let p = host.split('.') + ip_val = (uint32(parseInt(p[0])) and 0xFF) or + ((uint32(parseInt(p[1])) and 0xFF) shl 8) or + ((uint32(parseInt(p[2])) and 0xFF) shl 16) or + ((uint32(parseInt(p[3])) and 0xFF) shl 24) + except: + print("Error: Invalid IP format (use A.B.C.D)\n") + return @[] + + # Construct SockAddrIn (Layout must match libc.connect hack) + var addr_buf: array[16, byte] + # Port 80 (0x0050) -> Big Endian 0x0050? No, htons(80) = 0x5000 on LE? + # 80 = 0x0050. LE in mem: 50 00. + # LwIP wants host byte order or network? + # connect() shim expects us to pass port as uint16. + # But the shim casts addr_ptr+2 to uint16*. + # If we write 80 there, it reads 80. + + let port: uint16 = 80 + copyMem(addr addr_buf[2], unsafeAddr port, 2) + copyMem(addr addr_buf[4], unsafeAddr ip_val, 4) + + if lb.connect(fd, addr addr_buf[0], 16) < 0: + print("Connect Failed\n") + return @[] + + print("[NipBox] Connected! Sending Payload...\n") + let req = "GET / HTTP/1.0\r\n\r\n" + discard lb.send(fd, unsafeAddr req[0], uint64(req.len), 0) + + print("[NipBox] Waiting for Data...\n") + var buf: array[1024, char] + while true: + let n = lb.recv(fd, addr buf[0], 1024, 0) + if n > 0: + var s = "" + for i in 0.. New LibC