code wiki / _hdl_build / nx_invite_token.nx
nx_invite_token.nx
buildroot/runtime/_hdl_build/nx_invite_token.nx
about
nx_invite_token.nx -- SECRET single-use, expiring, realm+level-scoped INVITE TOKEN store.
Purpose: let a remote party (e.g. a law firm) self-register their OWN credential on an
admin.<domain> portal WITHOUT a guessable handle being the only gate -- they present a
secret single-use invite token that an owner minted out-of-band. The token is the bearer
secret; the server stores ONLY sha256(token) so a stolen store cannot reconstruct any
live token (rule: secrets never at rest in the clear).
Storage doctrine (ADDITIVE-ONLY, per global rule 13), modeled on hub/nx_user_account_store.nx
(nx_uas_append): append-only text log, one row per line, a single sys_write of < 512 bytes =>
atomic on the substrate (O_APPEND). LATEST row for a token-hash WINS. Single-use = consume
appends a superseding "consumed" row; history is never deleted.
THE TOKEN: inv_issue mints 32 CSPRNG bytes -> 64 lowercase-hex chars (out_token_hex) = the
bearer secret returned to the caller. The STORED identity is sha256(out_token_hex) hex-encoded.
inv_check/inv_consume re-hash the presented hex token the SAME way (sha256 of the 64-hex bytes),
so issue and check agree by construction. (Hashing the printable hex token -- not re-decoding to
raw -- keeps the wire contract one opaque string and avoids a decode step on the hot path.)
Row formats (space-delimited, '\n'-terminated; realm is a space/newline-free realm id):
issued: INV issued <hash:64hex> <realm> <level:dec> <expiry:hex16>\n
consumed: INV consumed <hash:64hex> <ts:hex16>\n
COMPOSES: nx_syscalls (openat_append/read_file/fsync/write), nx_csprng (nx_csprng_fill),
nx_sha256 (sha256_digest -- the canonical substrate SHA-256 one-shot).
COMPOSED BY: _hdl_build/nx_docportal_admin_daemon (POST /admin/register invite gate).
license_tier: ORIGINAL
dependencies 3 imports · 6 importers
imports: nx_syscalls.nxnx_csprng.nxnx_sha256.nx
imported by: nx_docportal_admin_daemon.nxnx_email_invite.nxnx_email_invite_gate.nxnx_email_portal_daemon.nxnx_invite_issue_cli.nxnx_invite_token_gate.nx
structs
| none |
consts
| 33 | const NX_INV_OK: i64 = 0 |
| 34 | const NX_INV_BAD_INPUT: i64 = 1480 |
| 35 | const NX_INV_IO_FAILED: i64 = 1481 |
| 36 | const NX_INV_RNG_FAILED: i64 = 1482 |
| 39 | const NX_INV_TOKEN_BYTES: i64 = 32 // CSPRNG entropy per token |
| 40 | const NX_INV_TOKEN_HEX: i64 = 64 // = 2 * NX_INV_TOKEN_BYTES (the wire token width) |
| 41 | const NX_INV_HASH_HEX: i64 = 64 // = 2 * 32 (hex of sha256(token)) |
| 42 | const NX_INV_ISSUED_HASH_OFF: i64 = 11 // strlen("INV issued ") |
| 43 | const NX_INV_CONSUMED_HASH_OFF: i64 = 13 // strlen("INV consumed ") |
| 44 | const NX_INV_LINE_CAP: i64 = 512 // one row, comfortably < 512 (atomic append) |
| 45 | const NX_INV_MAX_REALM_BYTES: i64 = 128 // realm-id upper bound (stays inside one row) |
| 46 | const NX_INV_EXPIRY_HEX: i64 = 16 // 64-bit unix expiry, fixed-width hex |
| 47 | const NX_INV_MAX_STORE_BYTES: i64 = 16777216 // 16 MiB scan cap |
functions
| 51 | func _inv_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 } |
| 52 | func _inv_catn(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 + n } |
| 54 | func _inv_hex_enc(src: *u8, n: i64, out: *u8) -> i64 |
| 65 | func _inv_i64_hex16(v: i64, out: *u8) -> i64 |
| 76 | func _inv_i64_dec(v: i64, out: *u8) -> i64 |
| 87 | func _inv_nib(c: i64) -> i64 called by 1: _inv_parse_hex16 |
| 95 | func _inv_parse_hex16(data: *u8, start: i64) -> i64 |
| 108 | func _inv_parse_dec(data: *u8, start: i64, end: i64) -> i64 called by 1: _inv_parse_issued |
| 125 | func _inv_match_hash(data: *u8, off: i64, want_hex: *u8) -> i64 called by 1: inv_check |
| 137 | func _inv_parse_issued(data: *u8, after_hash: i64, eol: i64, realm: *u8, realm_n: i64, |
| 186 | func inv_issue(store_path: *u8, realm: *u8, realm_n: i64, level: i64, ttl_s: i64, now_s: i64, called by 6: mainmainmainmainmainmain calls 12: sys_mmapnx_csprng_fill_inv_hex_encsha256_digest_inv_cat_inv_catn+6 |
| 243 | func inv_check(store_path: *u8, token_hex: *u8, token_n: i64, realm: *u8, realm_n: i64, now_s: i64) -> i64 |
| 321 | func inv_consume(store_path: *u8, token_hex: *u8, token_n: i64, now_s: i64) -> i64 called by 4: dad_handlemainepd_handlemain calls 10: sys_mmapsha256_digest_inv_hex_enc_inv_cat_inv_catn_inv_i64_hex16+4 |