nx_mgmt_client.nx
buildroot/runtime/nx_mgmt_client.nx
about
nx_mgmt_client.nx -- SOVEREIGN CLIENT for the live control-plane mgmt API (nx_mgmt_api.nx, https://<host>/api).
Operates the ecosystem OVER THE WIRE (sovereign TLS-1.3 + canonical Modern Auth: the X-Nishi-Session header,
NO cookies) instead of the shell/ssh dance. Two modes (parse argv):
nx_mgmt_client <base_url> login <handle> <passphrase_file> <token_out_file>
POST <base_url>/api/login body "handle=<enc>&passphrase=<enc>" (x-www-form-urlencoded, url-ENCODED so a
'&'/'='/space in a passphrase can't corrupt the form the daemon's sd_form_field parses) -> parse the
{"token":"<b64>"} value -> write the RAW b64 token to <token_out_file> (0600). Prints LOGIN OK | LOGIN FAIL status=<n>.
nx_mgmt_client <base_url> call <METHOD> <path> <token_file> [body_file]
<METHOD> <base_url><path> with header "X-Nishi-Session: <token>" (token read from file), optional
x-www-form-urlencoded body from body_file. Prints "status=<n>" then the response body.
nx_mgmt_client <base_url> upload <target> <artifact_file> <token_file>
PUBLISH an artifact over the authenticated API from ANYWHERE (retires the LAN-only nx_aw_send). Reads
<artifact_file>, splits it into <=48KB chunks (one request must fit under the daemon's 64KB read cap),
and POSTs each as POST /api/upload?target=<t>&seq=<i>&final=<0|1> with "X-Nishi-Session: <token>" and
the RAW chunk as the body. Aborts loudly on any non-200. On the final chunk prints the {"staged":...}
body + "UPLOAD OK <target>.new (<bytes> bytes)". The staged <target>.new is then promoted by /api/deploy.
REUSE (compose, don't rebuild TLS): the request BUILDERS (mcl_build_login_body / mcl_build_request) are pure
byte emitters; the TRANSPORT (mcl_send_drain) composes the SHIPPED TLS-1.3 record primitives -- exactly the
primitives nx_https_post_complete + nx_login_e2e_probe's e2e_send compose -- the ONLY difference is we send a
caller-built request so we can carry the X-Nishi-Session header the baked builders (nx_http_client_build_request*)
cannot. Token extraction reuses nx_http_response_parse + nx_acme_json_str (fail-closed: a well-formed closing
quote is required, else empty -- no garbage). The connect+handshake path reuses nx_https_url_for_fetch /
nx_https_url_connect / nx_tls13_client_session_run with fresh nx_csprng ephemerals (forward secrecy per run).
OPERATOR-DRIVEN by design: a human runs this; nothing acts autonomously. This file gate-proves its codec
in-process (nx_mgmt_client_gate.nx); the LIVE TLS round-trip is driven by the operator/parent. license_tier: ORIGINAL expect_exit: 0
dependencies 10 imports · 1 importers
imports: nx_acme_http.nxnx_https_url_for_fetch.nxnx_https_url_connect.nxnx_tls13_client_session_run.nxnx_tls13_client_session.nxnx_tls13.nxnx_tls13_record.nxnx_tls13_read_record_from_fd.nxnx_csprng.nxnx_sha256.nx
imported by: nx_mgmt_client_gate.nx
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 38 | const MCL_MAGIC_16384: i64 = 16384 |
| 39 | const MCL_MAGIC_16645: i64 = 16645 |
| 40 | const MCL_MAGIC_4194304: i64 = 4194304 |
| 41 | const MCL_MAGIC_4096: i64 = 4096 |
| 42 | const MCL_MAGIC_2097152: i64 = 2097152 |
| 43 | const MCL_MAGIC_2026: i64 = 2026 |
| 44 | const MCL_MAGIC_1048576: i64 = 1048576 |
| 45 | const MCL_MAGIC_4095: i64 = 4095 |
| 46 | const MCL_MAGIC_65536: i64 = 65536 |
| 47 | const MCL_MAGIC_2000: i64 = 2000 |
| 48 | const MCL_MAGIC_65535: i64 = 65535 |
| 50 | const MCL_TOKEN_OUT_MODE: i64 = 0x180 // 0600 -- session token is a secret; owner rw only |
| 51 | const MCL_CONNECT_FAIL: i64 = 0 - 11 |
| 52 | const MCL_HANDSHAKE_FAIL: i64 = 0 - 12 |
| 53 | const MCL_URL_FAIL: i64 = 0 - 10 |
| 54 | const MCL_UPLOAD_RETRIES: i64 = 8 // per-chunk retry budget: transport rc<0 + lost-response status=0 + 5xx (fresh connection+ephemerals + EXPONENTIAL BACKOFF each attempt; safe via the daemon's idempotent replay-ack). 8 attempts w/ backoff caps ~10s of edge-wedge ride-out per chunk. |
| 55 | const MCL_ARTIFACT_CAP: i64 = 134217728 // 128 MiB artifact read buffer (raised for build-over-API full-tree .pack ~80MB); a file that fills it is REFUSED (never silently truncate). Chunks still <=48KB (daemon 64KB read cap) so a big tree = many chunks; multi-part packs are the future optimization. |
functions
| 59 | func mcl_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } |
| 60 | func mcl_putn(v: i64) -> i64 called by 1: main |
| 70 | func mcl_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } called by 1: main |
| 71 | func mcl_streq(a: *u8, b: *u8) -> i64 called by 1: main |
| 78 | func mcl_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 } |
| 80 | func mcl_catb(d: *u8, o: i64, s: *u8, n: i64) -> i64 { var i: i64 = 0; while i < n { d[o + i] = s[i]; i = i + 1 } return o + i } |
| 82 | func mcl_catn(d: *u8, o: i64, v: i64) -> i64 |
| 90 | func mcl_hexdigit(v: i64) -> i64 { if v < 10 { return 48 + v } return 55 + v } // 0-9 then A-F (uppercase) called by 1: mcl_url_encode |
| 93 | func mcl_hex32(dig: *u8, out: *u8) -> i64 |
| 108 | func mcl_is_unreserved(c: i64) -> i64 called by 1: mcl_url_encode |
| 122 | func mcl_url_encode(src: *u8, n: i64, out: *u8) -> i64 |
| 140 | func mcl_build_login_body(handle: *u8, hn: i64, pass: *u8, pn: i64, out: *u8) -> i64 |
| 156 | func mcl_build_request( |
| 195 | func mcl_extract_token(resp: *u8, total: i64, out_status: *i64, out: *u8, out_cap: i64) -> i64 |
| 217 | func mcl_write_n(fd: i64, buf: *u8, n: i64) -> i64 called by 1: mcl_send_drain |
| 231 | func mcl_send_drain(s: *Tls13ClientSession, fd: i64, req: *u8, req_len: i64, out: *u8, out_cap: i64) -> i64 |
| 284 | func mcl_req(store: *TrustStore, full_url: *u8, target: *NxHttpsTarget, req: *u8, req_len: i64, out: *u8, out_cap: i64, vc_in: *TlsValidationContext) -> i64 |
| 315 | func mcl_read_file(path: *u8, out: *u8, cap: i64) -> i64 |
| 331 | func mcl_rtrim_nl(buf: *u8, n: i64) -> i64 called by 1: main |
| 345 | func mcl_join_url(base_url: *u8, suffix: *u8, out: *u8) -> i64 |
| 356 | func mcl_usage() -> i64 |
| 367 | func mcl_upload_qpath(base_path: *u8, base_plen: i64, target: *u8, seq: i64, final: i64, sha_hex: *u8, sha_len: i64, out: *u8) -> i64 |
| 388 | func main(argc: i64, argv: *i64) -> i64 |