nx_abi_lock.nx
buildroot/runtime/nx_abi_lock.nx
about
nx_abi_lock.nx -- sealed signature of a module's public ABI surface.
LAYER 2 stability primitive paired with nx_module_cas. The IDE at
nishifamily.com/ide pins each user program to BOTH the module CAS
root (which proves bytes-equal) AND the upstream-substrate ABI lock
(which proves "the public surface I depend on still exists and has
the same shape"). An update that bumps internal bytes but doesn't
alter the ABI returns NX_ABI_UNCHANGED -- safe to auto-rebind. A
pure addition returns NX_ABI_ADDITIVE_OK. Anything else BREAKING_*
and the IDE refuses to silently upgrade the user's pinned version.
ABI signature shape (canonical bytes):
for each public symbol, sorted by name lex-ascending:
uvarint(name_len) || name_utf8 ||
uvarint(n_params) ||
uvarint(return_type_id) ||
for each param i in 0..n_params-1:
uvarint(param_type_id_i)
abi_signature = sha256(concat above)
Type IDs are NishiLang TypeKind values (TY_VOID=0, TY_I64=4, etc.
from ir.h). Substrate type-table is stable; type IDs do not change
release to release. When adding a new TypeKind we add it at the end
of the enum so old IDs stay valid -- cardinal global rule 19.
Sealed verdict for ABI compat check (NX_ABI_*):
0 INCONCLUSIVE missing/empty symbol table on one side
1 BREAKING_REMOVED old symbol absent from new manifest
2 BREAKING_RETYPED same name but signature bytes differ
3 BREAKING_RESERIALIZED same name + arity but ret/param type id changed
4 ADDITIVE_OK every old symbol present, identical, plus new ones
5 UNCHANGED identical signature hashes both sides
Cardinal global rule 19 (API Contract Stability) is enforced at the
IDE bind point: the loader compares the user's pinned NX_ABI hash
to the live substrate's hash. UNCHANGED or ADDITIVE_OK proceed
silently; anything else paginates the user with a named diff and
refuses to load until they re-pin.
genealogy_id: semver_2_0_0 + abi_compat_lessons_glibc + rust_semver_trick +
dependencies 3 imports · 1 importers
imports: nx_syscalls.nxnx_tier.nxnx_sha256.nx
imported by: nx_abi_lock_test.nx
structs
| 62 | struct AbiSymbol { |
| 73 | struct AbiManifest { |
consts
| 48 | const NX_ABI_HASH_BYTES: nx_int = 32 |
| 51 | const NX_ABI_INCONCLUSIVE: nx_int = 0 |
| 52 | const NX_ABI_BREAKING_REMOVED: nx_int = 1 |
| 53 | const NX_ABI_BREAKING_RETYPED: nx_int = 2 |
| 54 | const NX_ABI_BREAKING_RESERIALIZED: nx_int = 3 |
| 55 | const NX_ABI_ADDITIVE_OK: nx_int = 4 |
| 56 | const NX_ABI_UNCHANGED: nx_int = 5 |
| 57 | const NX_ABI_N_VERDICTS: nx_int = 6 |
functions
| 82 | func nx_abi_lock_seal(am: *AbiManifest) -> nx_int { |
| 116 | func nx_abi_lock_compare(old: *AbiManifest, new_m: *AbiManifest) -> nx_int {
called by 1: main |
| 173 | func nx_abi_verdict_is_valid(v: nx_int) -> nx_int {
called by 1: main |
| 182 | func nx_abi_upgrade_is_safe(v: nx_int) -> nx_int {
called by 1: main |