diff --git a/apps/init/init.nim b/apps/init/init.nim index 569b4d7..81c7e43 100644 --- a/apps/init/init.nim +++ b/apps/init/init.nim @@ -9,6 +9,62 @@ 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() = # 1. Pledge Sovereignty 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╚═══════════════════════════════════════╝\x1b[0m\n\n")) - print(cstring("[INIT] Initializing Membrane Network Stack...\n")) - membrane_init() + print(cstring("[INIT] PHASE_42_VERIFY: Membrane Network Stack...\\n")) + # 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 --- - print(cstring("[INIT] Waiting for DHCP IP Address...\n")) - var ip: uint32 = 0 - for i in 0 ..< 600: # 60 seconds - pump_membrane_stack() - ip = glue_get_ip() - if ip != 0: break - discard syscall(0x65, 100000000'u64) # 100ms - - if ip == 0: - print(cstring("[INIT] WARNING: DHCP Discovery timed out. Proceeding...\n")) - else: - print(cstring("[INIT] Network ONLINE (10.0.2.15)\n")) + # # --- DHCP PHASE --- + # print(cstring("[INIT] Waiting for DHCP IP Address...\n")) + # var ip: uint32 = 0 + # for i in 0 ..< 600: # 60 seconds + # pump_membrane_stack() + # ip = glue_get_ip() + # if ip != 0: break + # discard syscall(0x65, 100000000'u64) # 100ms + + # if ip == 0: + # print(cstring("[INIT] WARNING: DHCP Discovery timed out. Proceeding...\n")) + # else: + # 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: "".} = object + + # proc getaddrinfo(node: cstring, service: cstring, hints: pointer, res: ptr ptr AddrInfo): cint {.importc, header: "".} + # proc freeaddrinfo(res: ptr AddrInfo) {.importc, header: "".} + + # 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 --- proc spawn_fiber(path: cstring): int = return int(syscall(0x300, cast[uint64](path), 0, 0)) - + print(cstring("[INIT] Spawning mksh...\n")) discard spawn_fiber(cstring("/bin/mksh")) - + # --- SUPERVISOR PHASE --- print(cstring("[INIT] Entering Supervisor Loop...\n")) var loop_count = 0 while true: - pump_membrane_stack() + # pump_membrane_stack() # DISABLED: Requires LwIP loop_count += 1 - if loop_count mod 100 == 0: - print(cstring("[INIT] Heartbeat\n")) + if loop_count mod 0x100000 == 0: # Every ~1M iterations + discard syscall(0x65, 1000000000'u64) # 1s yield discard syscall(0x65, 100000000'u64) # 100ms when isMainModule: diff --git a/npl/nipbox/nipbox.nim b/npl/nipbox/nipbox.nim index 3cc2cf6..a513c1b 100644 --- a/npl/nipbox/nipbox.nim +++ b/npl/nipbox/nipbox.nim @@ -12,9 +12,49 @@ import strutils, parseutils, tables, sequtils, json import kdl import ../../libs/membrane/libc as lb 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 ../../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 const @@ -221,7 +261,7 @@ proc cmd_cp*(args: seq[string], input: PipelineData): PipelineData = return @[] # 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: print("cp: cannot create '" & dest & "'\n") discard lb.close(fd_src) @@ -247,11 +287,11 @@ 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 @[] @@ -260,7 +300,7 @@ 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) @@ -291,45 +331,53 @@ proc cmd_cat*(args: seq[string], input: PipelineData): PipelineData = proc cmd_write*(args: seq[string], input: PipelineData): PipelineData = ## write ## Uses USERLAND SFS (Block Valve architecture) - if args.len < 2: - print("Usage: write \n") + when not defined(NIPBOX_LITE): + if args.len < 2: + print("Usage: write \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 @[] - - 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 @[] + print("[nipbox] 'write' requires SFS (not available in lite mode)\n") + return @[] proc cmd_read*(args: seq[string], input: PipelineData): PipelineData = ## read ## Uses USERLAND SFS (Block Valve architecture) - if args.len == 0: - print("Usage: read \n") + when not defined(NIPBOX_LITE): + if args.len == 0: + print("Usage: read \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 @[] - - 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 @[] + print("[nipbox] 'read' requires SFS (not available in lite mode)\n") + return @[] proc cmd_edit*(args: seq[string], input: PipelineData): PipelineData = if args.len == 0: @@ -482,239 +530,223 @@ proc cmd_http_get*(args: seq[string], input: PipelineData): PipelineData = return @[node] proc cmd_http_download*(args: seq[string], input: PipelineData): PipelineData = - if args.len < 2: - print("Usage: http.download \n") - return @[] + when not defined(NIPBOX_LITE): + if args.len < 2: + print("Usage: http.download \n") + return @[] - let url_arg = args[0] - let outfile = args[1] + let url_arg = args[0] + let outfile = args[1] - var host_part = url_arg - var path_str = "/" + var host_part = url_arg + var path_str = "/" - let slash_pos = url_arg.find('/') - if slash_pos != -1: - host_part = url_arg[0.. 0: - timeout = 0 - if not header_parsed: - for i in 0.. 0: + timeout = 0 + if not header_parsed: + for i in 0.. 8192: + print("Error: Headers too large.\n") + break else: - if header_acc.len > 8192: - print("Error: Headers too large.\n") - break + sfs.sfs_write_chunk(sfs_h, addr buf[0], int(n)) + total_bytes += int(n) + + 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: - # Stream directly to SFS - 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(".") + break - elif n == 0: - break - else: - timeout += 1 - # 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 - - 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 @[] + 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 @[] + else: + print("[nipbox] 'http.download' requires SFS (not available in lite mode)\n") + return @[] # Phase 37: HTTP Verification Tool proc cmd_http_test*(args: seq[string], input: PipelineData): PipelineData = if args.len < 1: - print("Usage: http \n") + print("Usage: http.test \n") return @[] - + let host = args[0] print("Dialing " & host & ":80...\n") - + 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 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 = lnet.net_recv(fd, 512) if resp.len > 0: print(resp) total += resp.len else: - # End of stream or empty poll break - + print("\n[HTTP] Closed. Total bytes: " & $total & "\n") 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 @@ -824,28 +866,28 @@ proc dispatch_command(name: string, args: seq[string], 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? + # 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: @@ -856,7 +898,7 @@ proc dispatch_command(name: string, args: seq[string], print(s) else: break - + print("\n[NipBox] Done.\n") return @[] of "http.test": return cmd_http_test(args, input) @@ -1067,7 +1109,7 @@ proc run_script(path: string) = proc nipbox_main*() = # DIAGNOSTIC: Very first thing - prove we're executing print("[NIPBOX] Entry point reached!\n") - + # Phase 30: Pledge Safety # 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 @@ -1087,7 +1129,7 @@ proc nipbox_main*() = # Phase 38: Boot Script run_script("/init.nsh") print("\x1b[1;32m╚═══════════════════════════════════════╝\x1b[0m\n\n") - + print("\x1b[1;33mroot@nexus:# \x1b[0m") var inputBuffer: string = ""