feat(userland): NipBox LITE subject binary, ARM64 init support

This commit is contained in:
Markus Maiwald 2026-02-15 19:59:30 +01:00
parent a38bc523a8
commit 84c3345595
2 changed files with 344 additions and 239 deletions

View File

@ -9,6 +9,62 @@
import ../../libs/membrane/libc import ../../libs/membrane/libc
# --- M4.4: BKDL Capability Manifest (SPEC-071) ---
# Declares what capabilities this binary needs. The kernel reads this
# from the .nexus.manifest ELF section during loading and grants only
# what is declared here. No manifest = PLEDGE_STDIO only.
#
# Capabilities requested:
# - Channel 0x1001 (console.output) WRITE
# - Channel 0x2000 (VFS) READ
# - Channel 0x0500 (NET_TX) WRITE
# - Channel 0x0501 (NET_RX) READ
{.emit: """
__attribute__((section(".nexus.manifest"), used))
static const unsigned char nexus_manifest[166] = {
/* BkdlHeader (118 bytes) */
0x53, 0x55, 0x58, 0x4E, /* magic: "NXUS" (LE) */
0x01, 0x00, /* version: 1 */
0x00, 0x00, /* flags: 0 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* signature[0..63]: zeros */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* pubkey_hash[0..31]: zeros */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x00, /* cap_count: 4 */
0x00, 0x00, 0x00, 0x00, /* blob_size: 0 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* entry_point: 0 */
/* CapDescriptor[0]: console.output (0x1001) WRITE */
0x02, /* cap_type: Channel */
0x02, /* perms: PERM_WRITE */
0x00, 0x00, /* reserved */
0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* resource_id: 0x1001 (LE) */
/* CapDescriptor[1]: VFS (0x2000) READ */
0x02, /* cap_type: Channel */
0x01, /* perms: PERM_READ */
0x00, 0x00, /* reserved */
0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* resource_id: 0x2000 (LE) */
/* CapDescriptor[2]: NET_TX (0x0500) WRITE */
0x02, /* cap_type: Channel */
0x02, /* perms: PERM_WRITE */
0x00, 0x00, /* reserved */
0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* resource_id: 0x0500 (LE) */
/* CapDescriptor[3]: NET_RX (0x0501) READ */
0x02, /* cap_type: Channel */
0x01, /* perms: PERM_READ */
0x00, 0x00, /* reserved */
0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* resource_id: 0x0501 (LE) */
};
""".}
proc main() = proc main() =
# 1. Pledge Sovereignty # 1. Pledge Sovereignty
discard pledge(0xFFFFFFFFFFFFFFFF'u64) # PLEDGE_ALL discard pledge(0xFFFFFFFFFFFFFFFF'u64) # PLEDGE_ALL
@ -18,60 +74,67 @@ proc main() =
print(cstring("\x1b[1;35m║ SOVEREIGN INIT (NexInit v1.0) ║\x1b[0m\n")) print(cstring("\x1b[1;35m║ SOVEREIGN INIT (NexInit v1.0) ║\x1b[0m\n"))
print(cstring("\x1b[1;35m╚═══════════════════════════════════════╝\x1b[0m\n\n")) print(cstring("\x1b[1;35m╚═══════════════════════════════════════╝\x1b[0m\n\n"))
print(cstring("[INIT] Initializing Membrane Network Stack...\n")) print(cstring("[INIT] PHASE_42_VERIFY: Membrane Network Stack...\\n"))
membrane_init() # DISABLED: Network stack requires LwIP
# membrane_init()
proc glue_get_ip(): uint32 {.importc: "glue_get_ip", cdecl.} # proc glue_get_ip(): uint32 {.importc: "glue_get_ip", cdecl.}
# --- DHCP PHASE --- # # --- DHCP PHASE ---
print(cstring("[INIT] Waiting for DHCP IP Address...\n")) # print(cstring("[INIT] Waiting for DHCP IP Address...\n"))
var ip: uint32 = 0 # var ip: uint32 = 0
for i in 0 ..< 600: # 60 seconds # for i in 0 ..< 600: # 60 seconds
pump_membrane_stack() # pump_membrane_stack()
ip = glue_get_ip() # ip = glue_get_ip()
if ip != 0: break # if ip != 0: break
discard syscall(0x65, 100000000'u64) # 100ms # discard syscall(0x65, 100000000'u64) # 100ms
if ip == 0: # if ip == 0:
print(cstring("[INIT] WARNING: DHCP Discovery timed out. Proceeding...\n")) # print(cstring("[INIT] WARNING: DHCP Discovery timed out. Proceeding...\n"))
else: # else:
print(cstring("[INIT] Network ONLINE (10.0.2.15)\n")) # print(cstring("[INIT] Network ONLINE (10.0.2.15)\n"))
# # --- DNS PHASE ---
# print(cstring("\n[TEST] ══════════════════════════════════════\n"))
# print(cstring("[TEST] DNS Resolution: google.com\n"))
# print(cstring("[TEST] ══════════════════════════════════════\n\n"))
# type
# AddrInfo {.importc: "struct addrinfo", header: "<netdb.h>".} = object
# proc getaddrinfo(node: cstring, service: cstring, hints: pointer, res: ptr ptr AddrInfo): cint {.importc, header: "<netdb.h>".}
# proc freeaddrinfo(res: ptr AddrInfo) {.importc, header: "<netdb.h>".}
# var res: ptr AddrInfo
# for attempt in 1..5:
# print(cstring("[TEST] Resolving google.com (Attempt "))
# # (Simplified number printing not available, just loop)
# if getaddrinfo("google.com", nil, nil, addr res) == 0:
# print(cstring(") -> SUCCESS!\n"))
# freeaddrinfo(res)
# break
# else:
# print(cstring(") -> FAILED. Waiting 5s...\n"))
# for j in 1..50:
# pump_membrane_stack()
# discard syscall(0x65, 100000000'u64) # 100ms
# --- DNS PHASE ---
print(cstring("\n[TEST] ══════════════════════════════════════\n"))
print(cstring("[TEST] DNS Resolution: google.com\n"))
print(cstring("[TEST] ══════════════════════════════════════\n\n"))
var res: ptr AddrInfo
for attempt in 1..5:
print(cstring("[TEST] Resolving google.com (Attempt "))
# (Simplified number printing not available, just loop)
if getaddrinfo("google.com", nil, nil, addr res) == 0:
print(cstring(") -> SUCCESS!\n"))
freeaddrinfo(res)
break
else:
print(cstring(") -> FAILED. Waiting 5s...\n"))
for j in 1..50:
pump_membrane_stack()
discard syscall(0x65, 100000000'u64) # 100ms
# --- SHELL PHASE --- # --- SHELL PHASE ---
proc spawn_fiber(path: cstring): int = proc spawn_fiber(path: cstring): int =
return int(syscall(0x300, cast[uint64](path), 0, 0)) return int(syscall(0x300, cast[uint64](path), 0, 0))
print(cstring("[INIT] Spawning mksh...\n")) print(cstring("[INIT] Spawning mksh...\n"))
discard spawn_fiber(cstring("/bin/mksh")) discard spawn_fiber(cstring("/bin/mksh"))
# --- SUPERVISOR PHASE --- # --- SUPERVISOR PHASE ---
print(cstring("[INIT] Entering Supervisor Loop...\n")) print(cstring("[INIT] Entering Supervisor Loop...\n"))
var loop_count = 0 var loop_count = 0
while true: while true:
pump_membrane_stack() # pump_membrane_stack() # DISABLED: Requires LwIP
loop_count += 1 loop_count += 1
if loop_count mod 100 == 0: if loop_count mod 0x100000 == 0: # Every ~1M iterations
print(cstring("[INIT] Heartbeat\n")) discard syscall(0x65, 1000000000'u64) # 1s yield
discard syscall(0x65, 100000000'u64) # 100ms discard syscall(0x65, 100000000'u64) # 100ms
when isMainModule: when isMainModule:

View File

@ -12,9 +12,49 @@ import strutils, parseutils, tables, sequtils, json
import kdl import kdl
import ../../libs/membrane/libc as lb import ../../libs/membrane/libc as lb
import ../../libs/membrane/libc_net as lnet import ../../libs/membrane/libc_net as lnet
import ../../libs/membrane/fs/sfs_user as sfs when not defined(NIPBOX_LITE):
import ../../libs/membrane/fs/sfs_user as sfs
import editor import editor
import ../../libs/membrane/term # Phase 26: Visual Cortex when not defined(NIPBOX_LITE):
import ../../libs/membrane/term # Phase 26: Visual Cortex
# --- M4.4: BKDL Capability Manifest (SPEC-071) ---
{.emit: """
__attribute__((section(".nexus.manifest"), used))
static const unsigned char nexus_manifest[166] = {
/* BkdlHeader (118 bytes) */
0x53, 0x55, 0x58, 0x4E, /* magic: "NXUS" (LE) */
0x01, 0x00, /* version: 1 */
0x00, 0x00, /* flags: 0 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* signature[0..63]: zeros */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* pubkey_hash[0..31]: zeros */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x00, /* cap_count: 4 */
0x00, 0x00, 0x00, 0x00, /* blob_size: 0 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* entry_point: 0 */
/* CapDescriptor[0]: console.output (0x1001) WRITE */
0x02, 0x02, 0x00, 0x00,
0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* CapDescriptor[1]: VFS (0x2000) READ */
0x02, 0x01, 0x00, 0x00,
0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* CapDescriptor[2]: NET_TX (0x0500) WRITE */
0x02, 0x02, 0x00, 0x00,
0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* CapDescriptor[3]: NET_RX (0x0501) READ */
0x02, 0x01, 0x00, 0x00,
0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
""".}
# Phase 30: Pledge Constants # Phase 30: Pledge Constants
const const
@ -221,7 +261,7 @@ proc cmd_cp*(args: seq[string], input: PipelineData): PipelineData =
return @[] return @[]
# O_WRONLY(1) | O_CREAT(64) | O_TRUNC(512) = 577 # O_WRONLY(1) | O_CREAT(64) | O_TRUNC(512) = 577
let fd_dest = lb.open(dest.cstring, 577) let fd_dest = lb.open(dest.cstring, 577)
if fd_dest < 0: if fd_dest < 0:
print("cp: cannot create '" & dest & "'\n") print("cp: cannot create '" & dest & "'\n")
discard lb.close(fd_src) discard lb.close(fd_src)
@ -247,11 +287,11 @@ proc cmd_mv*(args: seq[string], input: PipelineData): PipelineData =
if args.len < 2: if args.len < 2:
print("Usage: mv <source> <dest>\n") print("Usage: mv <source> <dest>\n")
return @[] return @[]
# Step 1: Copy # Step 1: Copy
print("[mv] Copying...\n") print("[mv] Copying...\n")
discard cmd_cp(args, input) discard cmd_cp(args, input)
# Step 2: Unlink (Not yet supported by Kernel) # Step 2: Unlink (Not yet supported by Kernel)
print("[mv] Warning: Original file '" & args[0] & "' retained (SFS unlink not implemented).\n") print("[mv] Warning: Original file '" & args[0] & "' retained (SFS unlink not implemented).\n")
return @[] return @[]
@ -260,7 +300,7 @@ proc cmd_touch*(args: seq[string], input: PipelineData): PipelineData =
if args.len < 1: if args.len < 1:
print("Usage: touch <filename>\n") print("Usage: touch <filename>\n")
return @[] return @[]
let fd = lb.open(args[0].cstring, 577) # O_CREAT | O_TRUNC let fd = lb.open(args[0].cstring, 577) # O_CREAT | O_TRUNC
if fd >= 0: if fd >= 0:
discard lb.close(fd) discard lb.close(fd)
@ -291,45 +331,53 @@ proc cmd_cat*(args: seq[string], input: PipelineData): PipelineData =
proc cmd_write*(args: seq[string], input: PipelineData): PipelineData = proc cmd_write*(args: seq[string], input: PipelineData): PipelineData =
## write <filename> <content> ## write <filename> <content>
## Uses USERLAND SFS (Block Valve architecture) ## Uses USERLAND SFS (Block Valve architecture)
if args.len < 2: when not defined(NIPBOX_LITE):
print("Usage: write <filename> <content>\n") if args.len < 2:
print("Usage: write <filename> <content>\n")
return @[]
let filename = args[0]
let content = args[1..^1].join(" ")
# Mount userland FS if not already done
if not sfs.sfs_is_mounted():
discard sfs.sfs_mount()
let bytes_written = sfs.sfs_write(filename, cast[pointer](unsafeAddr content[0]), content.len)
if bytes_written > 0:
print("[Glass Vault] Written " & $bytes_written & " bytes to: " & filename & " (Userland SFS)\n")
else:
print("Error: Could not write to " & filename & "\n")
return @[] return @[]
let filename = args[0]
let content = args[1..^1].join(" ")
# Mount userland FS if not already done
if not sfs.sfs_is_mounted():
discard sfs.sfs_mount()
let bytes_written = sfs.sfs_write(filename, cast[pointer](unsafeAddr content[0]), content.len)
if bytes_written > 0:
print("[Glass Vault] Written " & $bytes_written & " bytes to: " & filename & " (Userland SFS)\n")
else: else:
print("Error: Could not write to " & filename & "\n") print("[nipbox] 'write' requires SFS (not available in lite mode)\n")
return @[] return @[]
proc cmd_read*(args: seq[string], input: PipelineData): PipelineData = proc cmd_read*(args: seq[string], input: PipelineData): PipelineData =
## read <filename> ## read <filename>
## Uses USERLAND SFS (Block Valve architecture) ## Uses USERLAND SFS (Block Valve architecture)
if args.len == 0: when not defined(NIPBOX_LITE):
print("Usage: read <filename>\n") if args.len == 0:
print("Usage: read <filename>\n")
return @[]
let filename = args[0]
# Mount userland FS if not already done
if not sfs.sfs_is_mounted():
discard sfs.sfs_mount()
var buf: array[4096, char]
let bytes_read = sfs.sfs_read(filename, addr buf[0], 4096)
if bytes_read > 0:
discard lb.write(cint(1), addr buf[0], csize_t(bytes_read))
print("\n[Glass Vault] Read " & $bytes_read & " bytes from: " & filename & " (Userland SFS)\n")
else:
print("Error: Could not open " & filename & "\n")
return @[] return @[]
let filename = args[0]
# Mount userland FS if not already done
if not sfs.sfs_is_mounted():
discard sfs.sfs_mount()
var buf: array[4096, char]
let bytes_read = sfs.sfs_read(filename, addr buf[0], 4096)
if bytes_read > 0:
discard lb.write(cint(1), addr buf[0], csize_t(bytes_read))
print("\n[Glass Vault] Read " & $bytes_read & " bytes from: " & filename & " (Userland SFS)\n")
else: else:
print("Error: Could not open " & filename & "\n") print("[nipbox] 'read' requires SFS (not available in lite mode)\n")
return @[] return @[]
proc cmd_edit*(args: seq[string], input: PipelineData): PipelineData = proc cmd_edit*(args: seq[string], input: PipelineData): PipelineData =
if args.len == 0: if args.len == 0:
@ -482,239 +530,223 @@ proc cmd_http_get*(args: seq[string], input: PipelineData): PipelineData =
return @[node] return @[node]
proc cmd_http_download*(args: seq[string], input: PipelineData): PipelineData = proc cmd_http_download*(args: seq[string], input: PipelineData): PipelineData =
if args.len < 2: when not defined(NIPBOX_LITE):
print("Usage: http.download <ip:port/path> <outfile>\n") if args.len < 2:
return @[] print("Usage: http.download <ip:port/path> <outfile>\n")
return @[]
let url_arg = args[0] let url_arg = args[0]
let outfile = args[1] let outfile = args[1]
var host_part = url_arg var host_part = url_arg
var path_str = "/" var path_str = "/"
let slash_pos = url_arg.find('/') let slash_pos = url_arg.find('/')
if slash_pos != -1: if slash_pos != -1:
host_part = url_arg[0..<slash_pos] host_part = url_arg[0..<slash_pos]
path_str = url_arg[slash_pos..^1] path_str = url_arg[slash_pos..^1]
let parts = host_part.split(':') let parts = host_part.split(':')
if parts.len != 2: if parts.len != 2:
print("Error: Target must be IP:PORT (e.g. 10.0.2.2:8000)\n") print("Error: Target must be IP:PORT (e.g. 10.0.2.2:8000)\n")
return @[] return @[]
let ip_str = parts[0] let ip_str = parts[0]
let port = uint16(parseInt(parts[1])) let port = uint16(parseInt(parts[1]))
# Parse IP # Parse IP
var ip_val: uint32 = 0 var ip_val: uint32 = 0
try: try:
let p = ip_str.split('.') let p = ip_str.split('.')
ip_val = (uint32(parseInt(p[0])) and 0xFF) or ip_val = (uint32(parseInt(p[0])) and 0xFF) or
((uint32(parseInt(p[1])) and 0xFF) shl 8) or ((uint32(parseInt(p[1])) and 0xFF) shl 8) or
((uint32(parseInt(p[2])) and 0xFF) shl 16) or ((uint32(parseInt(p[2])) and 0xFF) shl 16) or
((uint32(parseInt(p[3])) and 0xFF) shl 24) ((uint32(parseInt(p[3])) and 0xFF) shl 24)
except: except:
print("Error: Invalid IP\n") print("Error: Invalid IP\n")
return @[] return @[]
print("[Download] Connecting to " & host_part & "...\n") print("[Download] Connecting to " & host_part & "...\n")
let fd = lb.socket(2, 1, 0) let fd = lb.socket(2, 1, 0)
if fd < 0: return @[] if fd < 0: return @[]
# SockAddr setup # SockAddr setup
var addr_buf: array[16, byte] var addr_buf: array[16, byte]
copyMem(addr addr_buf[2], unsafeAddr port, 2) copyMem(addr addr_buf[2], unsafeAddr port, 2)
copyMem(addr addr_buf[4], unsafeAddr ip_val, 4) copyMem(addr addr_buf[4], unsafeAddr ip_val, 4)
if lb.connect(fd, addr addr_buf[0], 16) < 0: if lb.connect(fd, addr addr_buf[0], 16) < 0:
print("Error: Connection Failed.\n") print("Error: Connection Failed.\n")
return @[] return @[]
# Request # Request
let req = "GET " & path_str & " HTTP/1.0\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: if lb.send(cint(fd), cast[pointer](unsafeAddr req[0]), csize_t(req.len), 0) <= 0:
print("Error: Send Failed.\n") print("Error: Send Failed.\n")
discard lb.close(cint(fd))
return @[]
# 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)) discard lb.close(cint(fd))
return @[] return @[]
# Open SFS Stream # Mount SFS if needed
let sfs_h = sfs.sfs_open_write(outfile) if not sfs.sfs_is_mounted():
if sfs_h == nil: if not sfs.sfs_mount():
print("Error: Could not create file " & outfile & "\n") print("Error: Could not mount SFS.\n")
discard lb.close(cint(fd)) discard lb.close(cint(fd))
return @[] return @[]
print("[Download] Downloading...\n") # 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 @[]
var buf: array[4096, char] print("[Download] Downloading...\n")
var header_acc = ""
var header_parsed = false
var total_bytes = 0
var content_len = -1
var timeout = 0
while timeout < 10000: var buf: array[4096, char]
# Use libc shim which pumps stack var header_acc = ""
let n = lb.recv(cint(fd), addr buf[0], 4096, 0) var header_parsed = false
var total_bytes = 0
var content_len = -1
var timeout = 0
if n > 0: while timeout < 10000:
timeout = 0 let n = lb.recv(cint(fd), addr buf[0], 4096, 0)
if not header_parsed:
for i in 0..<n: header_acc.add(buf[i]) if n > 0:
let sep = header_acc.find("\r\n\r\n") timeout = 0
if sep != -1: if not header_parsed:
header_parsed = true for i in 0..<n: header_acc.add(buf[i])
let sep = header_acc.find("\r\n\r\n")
# Try to find Content-Length if sep != -1:
# Quick hacky parse header_parsed = true
let lower_head = header_acc.toLowerAscii()
let cl_idx = lower_head.find("content-length:") let lower_head = header_acc.toLowerAscii()
if cl_idx != -1: let cl_idx = lower_head.find("content-length:")
let end_line = lower_head.find("\r\n", cl_idx) if cl_idx != -1:
if end_line != -1: let end_line = lower_head.find("\r\n", cl_idx)
try: if end_line != -1:
content_len = parseInt(lower_head[cl_idx+15..<end_line].strip()) try:
except: content_len = parseInt(lower_head[cl_idx+15..<end_line].strip())
content_len = -1 except:
content_len = -1
let body_start = sep + 4
if body_start < header_acc.len: let body_start = sep + 4
let chunk = header_acc[body_start..^1] if body_start < header_acc.len:
sfs.sfs_write_chunk(sfs_h, cast[pointer](unsafeAddr chunk[0]), chunk.len) let chunk = header_acc[body_start..^1]
total_bytes += chunk.len sfs.sfs_write_chunk(sfs_h, cast[pointer](unsafeAddr chunk[0]), chunk.len)
header_acc = "" total_bytes += chunk.len
header_acc = ""
else:
if header_acc.len > 8192:
print("Error: Headers too large.\n")
break
else: else:
if header_acc.len > 8192: sfs.sfs_write_chunk(sfs_h, addr buf[0], int(n))
print("Error: Headers too large.\n") total_bytes += int(n)
break
if content_len > 0:
if total_bytes mod 10240 < int(n): print(".")
else:
if total_bytes mod 10240 < int(n): print(".")
elif n == 0:
break
else: else:
# Stream directly to SFS break
sfs.sfs_write_chunk(sfs_h, addr buf[0], int(n))
total_bytes += int(n)
# Progress Bar
if content_len > 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(".")
elif n == 0: sfs.sfs_close_write(sfs_h)
break discard lb.close(cint(fd))
else: print("\n[Download] Complete. " & $total_bytes & " bytes written to " & outfile & " (Glass Vault).\n")
timeout += 1 return @[]
# Busy wait / pump handled in recv? else:
# Recv calls pump_membrane_stack loop print("[nipbox] 'http.download' requires SFS (not available in lite mode)\n")
# But if we return -1 (EAGAIN), we need to retry. return @[]
# 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
sfs.sfs_close_write(sfs_h)
discard lb.close(cint(fd))
print("\n[Download] Complete. " & $total_bytes & " bytes written to " & outfile & " (Glass Vault).\n")
return @[]
# Phase 37: HTTP Verification Tool # Phase 37: HTTP Verification Tool
proc cmd_http_test*(args: seq[string], input: PipelineData): PipelineData = proc cmd_http_test*(args: seq[string], input: PipelineData): PipelineData =
if args.len < 1: if args.len < 1:
print("Usage: http <host>\n") print("Usage: http.test <host>\n")
return @[] return @[]
let host = args[0] let host = args[0]
print("Dialing " & host & ":80...\n") print("Dialing " & host & ":80...\n")
let fd = lnet.net_dial_tcp(host, 80) let fd = lnet.net_dial_tcp(host, 80)
if fd < 0: if fd < 0:
print("Connection Failed! Error: " & $fd & "\n") print("Connection Failed! Error: " & $fd & "\n")
return @[] return @[]
print("Connected! Sending GET request...\n") print("Connected! Sending GET request...\n")
discard lnet.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") print("Waiting for response...\n")
# Simple read loop
var total = 0 var total = 0
while true: while true:
# lb.pump_membrane_stack()
let resp = lnet.net_recv(fd, 512) let resp = lnet.net_recv(fd, 512)
if resp.len > 0: if resp.len > 0:
print(resp) print(resp)
total += resp.len total += resp.len
else: else:
# End of stream or empty poll
break break
print("\n[HTTP] Closed. Total bytes: " & $total & "\n") print("\n[HTTP] Closed. Total bytes: " & $total & "\n")
lnet.net_close(fd) lnet.net_close(fd)
return @[] return @[]
proc cmd_http_serve*(args: seq[string], input: PipelineData): PipelineData = proc cmd_http_serve*(args: seq[string], input: PipelineData): PipelineData =
print("[Server] Starting Nexus Web/1.0...\n") print("[Server] Starting Nexus Web/1.0...\n")
let port: uint16 = if args.len > 0: uint16(parseInt(args[0])) else: 80 let port: uint16 = if args.len > 0: uint16(parseInt(args[0])) else: 80
let s = lb.socket(2, 1, 0) let s = lb.socket(2, 1, 0)
if s < 0: if s < 0:
print("Error: Socket creation failed.\n") print("Error: Socket creation failed.\n")
return @[] return @[]
# Bind 0.0.0.0:port # Bind 0.0.0.0:port
var addr_buf: array[16, byte] var addr_buf: array[16, byte]
addr_buf[0] = 2 # AF_INET addr_buf[0] = 2 # AF_INET
copyMem(addr addr_buf[2], unsafeAddr port, 2) copyMem(addr addr_buf[2], unsafeAddr port, 2)
# IP 0.0.0.0 is default 0s # IP 0.0.0.0 is default 0s
if lb.bind_socket(s, addr addr_buf[0], 16) < 0: if lb.bind_socket(s, addr addr_buf[0], 16) < 0:
print("Error: Bind failed.\n") print("Error: Bind failed.\n")
return @[] return @[]
if lb.listen(s, 1) < 0: if lb.listen(s, 1) < 0:
print("Error: Listen failed.\n") print("Error: Listen failed.\n")
return @[] return @[]
print("[Server] Listening on port " & $port & "...\n") print("[Server] Listening on port " & $port & "...\n")
while true: while true:
# Accept blocks and pumps stack # Accept blocks and pumps stack
let client = lb.accept(s, nil, nil) let client = lb.accept(s, nil, nil)
if client < 0: if client < 0:
print("Error: Accept failed.\n") print("Error: Accept failed.\n")
continue continue
print("[Server] Client Connected (FD " & $client & ")\n") print("[Server] Client Connected (FD " & $client & ")\n")
var buf: array[1024, char] var buf: array[1024, char]
let n = lb.recv(client, addr buf[0], 1024, 0) let n = lb.recv(client, addr buf[0], 1024, 0)
if n > 0: if n > 0:
var req = "" var req = ""
for i in 0..<n: req.add(buf[i]) for i in 0..<n: req.add(buf[i])
print("[Server] Request:\n" & req & "\n") print("[Server] Request:\n" & req & "\n")
let resp = "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nHello from Nexus Unikernel!\n" let resp = "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nHello from Nexus Unikernel!\n"
discard lb.send(client, unsafeAddr resp[0], uint64(resp.len), 0) discard lb.send(client, unsafeAddr resp[0], uint64(resp.len), 0)
discard lb.close(client) discard lb.close(client)
print("[Server] Client Closed.\n") print("[Server] Client Closed.\n")
# Just handle one for testing? No, loop forever. # Just handle one for testing? No, loop forever.
# But how to exit? Ctrl-C not implemented in NipBox yet? # But how to exit? Ctrl-C not implemented in NipBox yet?
# We'll just run forever for MVP. # We'll just run forever for MVP.
return @[] return @[]
proc cmd_from_json*(args: seq[string], input: PipelineData): PipelineData = proc cmd_from_json*(args: seq[string], input: PipelineData): PipelineData =
@ -774,8 +806,18 @@ proc cmd_set*(args: seq[string], input: PipelineData): PipelineData =
proc cmd_help*(args: seq[string], input: PipelineData): PipelineData = proc cmd_help*(args: seq[string], input: PipelineData): PipelineData =
print("NipBox " & NIPBOX_VERSION & " (Phase 34: Orbital Drop)\n") when defined(NIPBOX_LITE):
print("Commands: ls, cat, echo, where, http, http.get, http.download, from_json, mount, matrix, set, if, while, help, exit\n") print("NipBox " & NIPBOX_VERSION & " [LITE] (Phase 34: Orbital Drop)\n")
print("Commands: ls, cat, cp, mv, touch, edit, echo, where, http, http.get,\n")
print(" http.test, http.serve, from_json, mount, matrix, crash,\n")
print(" sys.upgrade, set, if, while, help, exit\n")
print("Disabled: write, read, http.download (requires SFS)\n")
else:
print("NipBox " & NIPBOX_VERSION & " (Phase 34: Orbital Drop)\n")
print("Commands: ls, cat, cp, mv, touch, edit, echo, where, write, read,\n")
print(" http, http.get, http.download, http.test, http.serve,\n")
print(" from_json, mount, matrix, crash, sys.upgrade,\n")
print(" set, if, while, help, exit\n")
return @[] return @[]
# --- DISPATCHER --- # --- DISPATCHER ---
@ -802,16 +844,16 @@ proc dispatch_command(name: string, args: seq[string],
if args.len < 1: if args.len < 1:
print("Usage: http <ip>\n") print("Usage: http <ip>\n")
return @[] return @[]
let host = args[0] let host = args[0]
print("[NipBox] Dialing " & host & ":80...\n") print("[NipBox] Dialing " & host & ":80...\n")
# Use libc.socket/connect (Phase 38 Shim) # Use libc.socket/connect (Phase 38 Shim)
let fd = lb.socket(2, 1, 0) let fd = lb.socket(2, 1, 0)
if fd < 0: if fd < 0:
print("Socket Error\n") print("Socket Error\n")
return @[] return @[]
# Parse IP (Quick hack for 10.0.2.2) # Parse IP (Quick hack for 10.0.2.2)
# We need proper parsing but let's assume raw IP for MVP # We need proper parsing but let's assume raw IP for MVP
var ip_val: uint32 = 0 var ip_val: uint32 = 0
@ -824,28 +866,28 @@ proc dispatch_command(name: string, args: seq[string],
except: except:
print("Error: Invalid IP format (use A.B.C.D)\n") print("Error: Invalid IP format (use A.B.C.D)\n")
return @[] return @[]
# Construct SockAddrIn (Layout must match libc.connect hack) # Construct SockAddrIn (Layout must match libc.connect hack)
var addr_buf: array[16, byte] var addr_buf: array[16, byte]
# Port 80 (0x0050) -> Big Endian 0x0050? No, htons(80) = 0x5000 on LE? # Port 80 (0x0050) -> Big Endian 0x0050? No, htons(80) = 0x5000 on LE?
# 80 = 0x0050. LE in mem: 50 00. # 80 = 0x0050. LE in mem: 50 00.
# LwIP wants host byte order or network? # LwIP wants host byte order or network?
# connect() shim expects us to pass port as uint16. # connect() shim expects us to pass port as uint16.
# But the shim casts addr_ptr+2 to uint16*. # But the shim casts addr_ptr+2 to uint16*.
# If we write 80 there, it reads 80. # If we write 80 there, it reads 80.
let port: uint16 = 80 let port: uint16 = 80
copyMem(addr addr_buf[2], unsafeAddr port, 2) copyMem(addr addr_buf[2], unsafeAddr port, 2)
copyMem(addr addr_buf[4], unsafeAddr ip_val, 4) copyMem(addr addr_buf[4], unsafeAddr ip_val, 4)
if lb.connect(fd, addr addr_buf[0], 16) < 0: if lb.connect(fd, addr addr_buf[0], 16) < 0:
print("Connect Failed\n") print("Connect Failed\n")
return @[] return @[]
print("[NipBox] Connected! Sending Payload...\n") print("[NipBox] Connected! Sending Payload...\n")
let req = "GET / HTTP/1.0\r\n\r\n" let req = "GET / HTTP/1.0\r\n\r\n"
discard lb.send(fd, unsafeAddr req[0], uint64(req.len), 0) discard lb.send(fd, unsafeAddr req[0], uint64(req.len), 0)
print("[NipBox] Waiting for Data...\n") print("[NipBox] Waiting for Data...\n")
var buf: array[1024, char] var buf: array[1024, char]
while true: while true:
@ -856,7 +898,7 @@ proc dispatch_command(name: string, args: seq[string],
print(s) print(s)
else: else:
break break
print("\n[NipBox] Done.\n") print("\n[NipBox] Done.\n")
return @[] return @[]
of "http.test": return cmd_http_test(args, input) of "http.test": return cmd_http_test(args, input)
@ -1067,7 +1109,7 @@ proc run_script(path: string) =
proc nipbox_main*() = proc nipbox_main*() =
# DIAGNOSTIC: Very first thing - prove we're executing # DIAGNOSTIC: Very first thing - prove we're executing
print("[NIPBOX] Entry point reached!\n") print("[NIPBOX] Entry point reached!\n")
# Phase 30: Pledge Safety # Phase 30: Pledge Safety
# NipBox is the Shell, so it needs broad permissions, but we can restrict RPATH/WPATH to specific zones # NipBox is the Shell, so it needs broad permissions, but we can restrict RPATH/WPATH to specific zones
# For now, we PLEDGE_ALL because the shell needs to explore # For now, we PLEDGE_ALL because the shell needs to explore
@ -1087,7 +1129,7 @@ proc nipbox_main*() =
# Phase 38: Boot Script # Phase 38: Boot Script
run_script("/init.nsh") run_script("/init.nsh")
print("\x1b[1;32m╚═══════════════════════════════════════╝\x1b[0m\n\n") print("\x1b[1;32m╚═══════════════════════════════════════╝\x1b[0m\n\n")
print("\x1b[1;33mroot@nexus:# \x1b[0m") print("\x1b[1;33mroot@nexus:# \x1b[0m")
var inputBuffer: string = "" var inputBuffer: string = ""