nx_admin_login_flow.nx
buildroot/runtime/hub/nx_admin_login_flow.nx
about
nx_admin_login_flow.nx -- composable admin-login state machine.
COMPOSES (per NISHI_SMALL_SHARP_COMPOSABLE_STANDARD §4.1 M7):
nx_basic_auth challenge + verify (RFC 7617)
nx_http_session opaque-token + Set-Cookie / Cookie I/O
nx_kv_store session storage (caller-allocated; FNV-1a indexed)
nx_hash_facade password hash + compare (V1 non-cryptographic;
V2 upgrade per honest-stub discipline below)
COMPOSED BY (suggested wirings; each ~50-100 lines per site):
wiki/nx_wiki_admin_wiring.nx /wiki/admin route
(future) obd-config/nx_obd_admin_wiring.nx
(future) sprinkler-config/nx_spk_admin_wiring.nx
(future) any operator-built site needing admin login
Status: V1 SEED. 2026-05-27. SITE-AGNOSTIC HUB PRIMITIVE per
NISHI_SMALL_SHARP_COMPOSABLE_STANDARD §4.1 M3. No wiki / OBD /
sensor assumption baked in; the flow is reusable for any site
the operator wants to build admin login for.
WINNER-TIER: BASELINE-C provisional (V1 hash is non-cryptographic
by deliberate scope limit per honest-stub discipline;
V2 promotes to WINNER-S after argon2id NishiLang impl
ships)
INCUMBENTS: Devise (Ruby), Passport.js (Node), Spring Security
(Java), django.contrib.auth (Python), Flask-Login,
Authlib (Python OAuth-bias)
NUMBERS: V1 ships the flow + state machine; paired login-
latency bench vs Flask-Login + Devise pending real
user-flow fixture
GAP: all incumbents include password reset / 2FA /
role-based ACL / OAuth-callback handlers in V1;
this flow ships JUST the admin-login state machine
(V1 single-admin; V2 multi-user; V3 reset/2FA).
Trade-off: small + sharp + composable per the
standard vs framework-style bundle
PLAN: M-next: argon2id NishiLang impl (replaces FNV-1a
password hash); add session-cleanup-cron + 2FA
when operator needs them
EXEMPTION REASON: n/a; provisional pending measurement
dependencies 5 imports · 2 importers
imports: nx_syscalls.nxnx_basic_auth.nxnx_http_session.nxnx_kv_store.nxnx_hash_facade.nx
imported by: nx_wiki_https_daemon.nxnx_wiki_main.nx
structs
| 111 | struct NxAdminLoginConfig { |
| 157 | struct NxAdminLoginRequest { |
| 183 | struct NxAdminLoginResponse { |
consts
| 67 | const NX_ALOGIN_OK: i64 = 0 |
| 68 | const NX_ALOGIN_BAD_CONFIG: i64 = 1000 |
| 69 | const NX_ALOGIN_BAD_REQUEST: i64 = 1001 |
| 70 | const NX_ALOGIN_BUF_OVERFLOW: i64 = 1002 |
| 71 | const NX_ALOGIN_HASH_FAILED: i64 = 1003 |
| 72 | const NX_ALOGIN_KV_OOM: i64 = 1004 |
| 73 | const NX_ALOGIN_WEAK_HASH_WARNING: i64 = 1005 |
| 74 | const NX_ALOGIN_NOT_IMPLEMENTED: i64 = 1006 |
| 77 | const NX_ALOGIN_STATE_ALLOW: i64 = 0 // valid session; allow protected access |
| 78 | const NX_ALOGIN_STATE_CHALLENGE: i64 = 1 // no creds / no session; emit 401 + WWW-Authenticate |
| 79 | const NX_ALOGIN_STATE_DENY: i64 = 2 // bad creds; emit 401 (no challenge to avoid info leak) |
| 80 | const NX_ALOGIN_STATE_SESSION_NEW: i64 = 3 // creds verified; new session created (caller emits Set-Cookie + 200) |
| 81 | const NX_ALOGIN_STATE_LOGOUT_OK: i64 = 4 // session invalidated (caller emits Set-Cookie expire + 200) |
| 82 | const NX_ALOGIN_STATE_N_KINDS: i64 = 5 |
| 91 | const NX_ALOGIN_MAX_USERNAME_LEN: i64 = 64 |
| 92 | const NX_ALOGIN_MAX_PASSWORD_LEN: i64 = 256 |
| 93 | const NX_ALOGIN_MAX_REALM_LEN: i64 = 128 |
| 94 | const NX_ALOGIN_SESSION_TOKEN_LEN: i64 = 32 // hex chars per nx_http_session |
| 95 | const NX_ALOGIN_SESSION_TOKEN_RAW_BYTES: i64 = 16 // 128-bit entropy |
| 96 | const NX_ALOGIN_MAX_AUTH_HEADER_LEN: i64 = 1024 |
| 97 | const NX_ALOGIN_MAX_COOKIE_HEADER_LEN: i64 = 4096 |
| 98 | const NX_ALOGIN_DEFAULT_SESSION_MAX_AGE_S: i64 = 1800 // 30 min per nx_http_session default |
| 99 | const NX_ALOGIN_SESSION_VALUE_BYTES: i64 = 64 // session-record payload (timestamp + user-id + flags) |
| 102 | const NX_ALOGIN_ASCII_LF: i64 = 0x0A |
| 103 | const NX_ALOGIN_ASCII_CR: i64 = 0x0D |
| 104 | const NX_ALOGIN_ASCII_SP: i64 = 0x20 |
| 225 | const NX_ALOGIN_REC_OFF_CREATED: i64 = 0 |
| 226 | const NX_ALOGIN_REC_OFF_EXPIRES: i64 = 8 |
| 227 | const NX_ALOGIN_REC_OFF_USER_HASH: i64 = 16 |
| 228 | const NX_ALOGIN_REC_RESERVED_BYTES: i64 = 40 |
| 397 | const NX_ALOGIN_COOKIE_NAME: *u8 = "nishi_sess" as *u8 |
| 398 | const NX_ALOGIN_COOKIE_NAME_N: i64 = 10 |
functions
| 84 | func nx_alogin_state_is_valid(s: i64) -> i64 { |
| 129 | func nx_admin_login_config_init(cfg: *NxAdminLoginConfig, |
| 206 | func nx_alogin_bytes_eq(a: *u8, a_n: i64, b: *u8, b_n: i64) -> i64 {
called by 1: nx_alogin_verify_credentials |
| 230 | func nx_alogin_session_encode(out: *u8, out_cap: i64, |
| 246 | func nx_alogin_session_decode_expires(rec: *u8, rec_n: i64) -> i64 { |
| 255 | func nx_alogin_emit_challenge(cfg: *NxAdminLoginConfig,
called by 1: nx_admin_login_handle |
| 292 | func nx_alogin_verify_credentials(cfg: *NxAdminLoginConfig, |
| 334 | func nx_alogin_create_session(cfg: *NxAdminLoginConfig, |
| 400 | func nx_alogin_validate_session(cfg: *NxAdminLoginConfig, |
| 457 | func nx_alogin_emit_logout_cookie(cfg: *NxAdminLoginConfig, |
| 483 | func nx_admin_login_handle(cfg: *NxAdminLoginConfig, |