code wiki / (root) / nx_gpu_ctl.nx

nx_gpu_ctl.nx source

↩ module page · 102 lines · 6457 B

1// nx_gpu_ctl.nx -- SOVEREIGN GPU-provider CONTROL PLANE (Vast.ai) over our OWN TLS-1.2 stack (console.vast.ai is 2// TLS-1.2-only; the shipped stack is 1.3-only). Retires the curl+ssh provisioning dance: every verb is one 3// authenticated request via t12_request (nx_tls12_req.nx) -- Bearer key read from a vault FILE, never argv. 4// The MCP-ready capability the operator asked for: turn a GPU ON/OFF, provision, check, destroy -- all sovereign. 5// nx_gpu_ctl me <keyfile> GET /api/v0/users/current/ -> account (auth proof) 6// nx_gpu_ctl find <keyfile> GET /api/v0/bundles/?q=<vm 5090> -> offer list 7// nx_gpu_ctl up <offer_id> <keyfile> PUT /api/v0/asks/<id>/ {vm KVM} -> {new_contract:<inst>} 8// nx_gpu_ctl status <instance_id> <keyfile> GET /api/v0/instances/<id>/ -> state + ssh + $/hr 9// nx_gpu_ctl off <instance_id> <keyfile> PUT /api/v0/instances/<id>/ {stopped}-> stop billing, keep disk 10// nx_gpu_ctl on <instance_id> <keyfile> PUT /api/v0/instances/<id>/ {running}-> resume 11// nx_gpu_ctl destroy <instance_id> <keyfile> DELETE /api/v0/instances/<id>/ -> tear down (billing ends) 12// OPERATOR-DRIVEN by design (nothing acts autonomously). license_tier: ORIGINAL expect_exit: 0 13import "nx_tls12_req.nx" 14const GC_MAGIC_262144: i64 = 262144 15const GC_MAGIC_4096: i64 = 4096 16const GC_MAGIC_4095: i64 = 4095 17const GC_MAGIC_2048: i64 = 2048 18 19const GC_VM_BODY: *u8 = "{\"image\":\"docker.io/vastai/kvm:ubuntu_terminal\",\"disk\":50,\"runtype\":\"ssh_direct\",\"vm\":true,\"label\":\"nishi-sovereign-gpu\",\"target_state\":\"running\"}" as *u8 20const GC_FIND_URL: *u8 = "https://console.vast.ai/api/v0/bundles/?q=%7B%22gpu_name%22%3A%7B%22eq%22%3A%22RTX%205090%22%7D%2C%22num_gpus%22%3A%7B%22eq%22%3A1%7D%2C%22rentable%22%3A%7B%22eq%22%3Atrue%7D%2C%22vms_enabled%22%3A%7B%22eq%22%3Atrue%7D%2C%22order%22%3A%5B%5B%22dph_total%22%2C%22asc%22%5D%5D%2C%22limit%22%3A40%7D" as *u8 21 22func gc_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 23func gc_putn(v: i64) -> i64 { let b: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m;sys_write(1,"-" as *u8,1)} let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} var i: i64=0; while i<k{b[i]=t[k-1-i];i=i+1} sys_write(1,b,k); return 0 } 24func gc_streq(a: *u8, b: *u8) -> i64 { var i: i64=0; while a[i]!=(0 as u8){ if a[i]!=b[i]{return 0} i=i+1 } if b[i]!=(0 as u8){return 0} return 1 } 25func gc_cat(d: *u8, o: i64, s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8){ d[o+i]=s[i]; i=i+1 } return o+i } 26func gc_readfile(path: *u8, out: *u8, cap: i64) -> i64 { 27 let fd: i64 = sys_openat_rd(path); if fd <= 0 { return 0 } 28 var off: i64 = 0; var go: i64 = 1 29 while go == 1 { let r: i64 = sys_read(fd, ((out as i64)+off) as *u8, cap-off); if r <= 0 { go = 0 } else { off = off + r } } 30 sys_close(fd) 31 var trimming: i64 = 1 32 while trimming == 1 { if off <= 0 { trimming = 0 } else { let c: i64 = out[off-1] as i64; if c == 10 { off = off - 1 } else { if c == 13 { off = off - 1 } else { trimming = 0 } } } } 33 return off 34} 35// build "https://console.vast.ai" + prefix + id + "/" into out (NUL-terminated); returns length. id required. 36func gc_url(prefix: *u8, id: *u8, out: *u8) -> i64 { 37 var o: i64 = gc_cat(out, 0, "https://console.vast.ai" as *u8) 38 o = gc_cat(out, o, prefix) 39 var i: i64=0; while id[i]!=(0 as u8){ out[o]=id[i]; o=o+1; i=i+1 } 40 out[o]=47 as u8; o=o+1 41 out[o] = 0 as u8 42 return o 43} 44func gc_bodylen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } 45 46func gc_usage() -> i64 { 47 gc_puts("usage: nx_gpu_ctl {me <key>|find <key>|up <offer> <key>|status <id> <key>|off <id> <key>|on <id> <key>|destroy <id> <key>}\n" as *u8) 48 return 0 49} 50 51// run one request, print the response, return 0 if HTTP 2xx else 1 52func gc_run(url: *u8, method: *u8, mlen: i64, tok: *u8, tn: i64, body: *u8, blen: i64) -> i64 { 53 let out: *u8 = sys_mmap(GC_MAGIC_262144) 54 let n: i64 = t12_request(url, method, mlen, tok, tn, body, blen, out, GC_MAGIC_262144) 55 if n < 0 { gc_puts("REQUEST FAILED rc=" as *u8); gc_putn(n); gc_puts(" (-2=connect -3=handshake -5=write -6=no-cert -7=truststore-load -8=cert-INVALID)\n" as *u8); return 1 } 56 sys_write(1, out, n) 57 gc_puts("\n" as *u8) 58 // crude 2xx check: response starts "HTTP/1.1 2" 59 if n > 11 { if out[9]==(50 as u8) { return 0 } } 60 return 1 61} 62 63func main(argc: i64, argv: *i64) -> i64 { 64 if argc < 3 { gc_usage(); sys_exit(1); return 1 } 65 let verb: *u8 = argv[1] as *u8 66 let keyfile: *u8 = argv[argc-1] as *u8 // keyfile is always the LAST argument 67 let kbuf: *u8 = sys_mmap(GC_MAGIC_4096) 68 let tn: i64 = gc_readfile(keyfile, kbuf, GC_MAGIC_4095) 69 if tn <= 0 { gc_puts("cannot read keyfile\n" as *u8); sys_exit(1); return 1 } 70 let url: *u8 = sys_mmap(GC_MAGIC_2048) 71 72 // no-id verbs (full literal URL) 73 if gc_streq(verb, "me" as *u8) == 1 { sys_exit(gc_run("https://console.vast.ai/api/v0/users/current/" as *u8, "GET" as *u8, 3, kbuf, tn, 0 as *u8, 0)); return 0 } 74 if gc_streq(verb, "find" as *u8) == 1 { sys_exit(gc_run(GC_FIND_URL, "GET" as *u8, 3, kbuf, tn, 0 as *u8, 0)); return 0 } 75 76 // id verbs need <verb> <id> <keyfile> 77 if argc < 4 { gc_usage(); sys_exit(1); return 1 } 78 let id: *u8 = argv[2] as *u8 79 80 if gc_streq(verb, "up" as *u8) == 1 { 81 gc_url("/api/v0/asks/" as *u8, id, url) 82 sys_exit(gc_run(url, "PUT" as *u8, 3, kbuf, tn, GC_VM_BODY, gc_bodylen(GC_VM_BODY))); return 0 83 } 84 if gc_streq(verb, "status" as *u8) == 1 { 85 gc_url("/api/v0/instances/" as *u8, id, url) 86 sys_exit(gc_run(url, "GET" as *u8, 3, kbuf, tn, 0 as *u8, 0)); return 0 87 } 88 if gc_streq(verb, "off" as *u8) == 1 { 89 gc_url("/api/v0/instances/" as *u8, id, url) 90 sys_exit(gc_run(url, "PUT" as *u8, 3, kbuf, tn, "{\"state\":\"stopped\"}" as *u8, 19)); return 0 91 } 92 if gc_streq(verb, "on" as *u8) == 1 { 93 gc_url("/api/v0/instances/" as *u8, id, url) 94 sys_exit(gc_run(url, "PUT" as *u8, 3, kbuf, tn, "{\"state\":\"running\"}" as *u8, 19)); return 0 95 } 96 if gc_streq(verb, "destroy" as *u8) == 1 { 97 gc_url("/api/v0/instances/" as *u8, id, url) 98 sys_exit(gc_run(url, "DELETE" as *u8, 6, kbuf, tn, 0 as *u8, 0)); return 0 99 } 100 gc_usage(); sys_exit(1) 101 return 1 102}