nx_emitted_substrate.nx
buildroot/runtime/nx_emitted_substrate.nx
about
nx_emitted_substrate.nx -- Phase 5 (Reproduce) substrate emission.
Closes the ecosystem evolution lifecycle: a mature substrate
(Phase 4 seed-ready) emits a SIGNED expected-blob-set that
downstream devices consume in Phase 2 (Network) via
nx_substrate_manifest_ingest.
Per NISHI_ECOSYSTEM_EVOLUTION_ROADMAP.md Phase 5:
Mature -> nx_emitted_substrate_sign(record, operator_priv)
-> transport (sneakernet / USB / serial / HTTPS / spore)
-> Downstream receiver verifies sig + populates its
nx_substrate_manifest from the record
-> Ingests blobs via nx_substrate_manifest_ingest until
is_complete
The record is the DECLARATION; the bytes-on-wire transport is
transport-agnostic (the same record content goes over any
medium). Bytes-identical canonical serialization means the
signature works across any transport.
Defends against:
- Adversary in the middle who replaces an expected hash ->
signature verification fails (downstream refuses ingest)
- Adversary who substitutes the emitter's identity ->
install_hash mismatch caught when downstream cross-checks
against the operator's known good install_hashes
- Stale spore replay -> ts_emit_us in canonical bytes; if
paired with a freshness window, replays caught
V1 scope:
- Up to NX_EMIT_MAX_EXPECTED = 16 expected hashes inline
- Single Ed25519 signature (threshold queued for SA-7)
- Canonical serialization same shape as nx_install_plan
(magic tag + LE i64 fields + length-prefixed hash array)
- Round-trip from record into a fresh NxSubstrateManifest
Deferred:
- Larger N>16 (composes with Merkle root rather than flat set)
- Multi-signature (threshold / N-of-M peers must co-sign)
- Post-quantum migration (Ed25519 -> ML-DSA queued)
dependencies 6 imports · 2 importers
imports: nx_syscalls.nxnx_sha256.nxnx_ed25519_signature.nxnx_blob_store.nxnx_install_hash.nxnx_substrate_manifest.nx
imported by: nx_emitted_substrate_test.nxnx_spore_up_lifecycle_test.nx
structs
| 123 | struct NxEmittedSubstrate |
consts
| 79 | const NX_MAGIC_1000000: i64 = 1000000 |
| 80 | const NX_MAGIC_4096: i64 = 4096 |
| 83 | const NX_EMIT_MAX_EXPECTED: i64 = 16 |
| 84 | const NX_EMIT_SCHEMA_VERSION: i64 = 1 |
| 85 | const NX_EMIT_HEADER_BYTES: i64 = 80 // pre-hashes header (10 i64s) |
| 86 | const NX_EMIT_SIG_BYTES: i64 = 64 |
| 87 | const NX_EMIT_PRIV_BYTES: i64 = 32 |
| 88 | const NX_EMIT_PUB_BYTES: i64 = 32 |
| 91 | const NX_EMIT_OK: i64 = 0 |
| 92 | const NX_EMIT_BAD_INPUT: i64 = 1 |
| 93 | const NX_EMIT_FULL: i64 = 2 |
| 94 | const NX_EMIT_TAMPER: i64 = 3 |
| 95 | const NX_EMIT_BAD_SIG: i64 = 4 |
| 96 | const NX_EMIT_DUPLICATE: i64 = 5 |
| 97 | const NX_EMIT_N: i64 = 6 |
| 106 | const NX_EMIT_CANARY_PRE: i64 = 0x4E58454D49545000 // "NXEMITP\0" |
| 107 | const NX_EMIT_CANARY_POST: i64 = 0x4E58454D4954454E // "NXEMITEN" |
functions
| 99 | func nx_emit_verdict_is_valid(v: i64) -> i64 called by 1: main |
| 149 | func _emit_store_i64_le(buf: *u8, off: i64, v: i64) -> i64 called by 1: nx_emitted_substrate_canonicalize |
| 162 | func _emit_store_magic(buf: *u8, off: i64) -> i64 called by 1: nx_emitted_substrate_canonicalize |
| 175 | func nx_emitted_substrate_new( |
| 257 | func nx_emitted_substrate_is_valid(r: *NxEmittedSubstrate) -> i64 |
| 268 | func nx_emitted_substrate_add_expected( |
| 313 | func nx_emitted_substrate_canonicalize( |
| 349 | func nx_emitted_substrate_sign( |
| 395 | func nx_emitted_substrate_verify_sig( |
| 442 | func nx_emitted_substrate_to_manifest( |
| 464 | func nx_emitted_substrate_n_expected(r: *NxEmittedSubstrate) -> i64 |
| 469 | func nx_emitted_substrate_is_signed(r: *NxEmittedSubstrate) -> i64 |