nx_swhid_lib.nx
buildroot/runtime/nx_swhid_lib.nx
about
nx_swhid_lib.nx -- SWHID, the SoftWare Hash IDentifier. ISO/IEC 18670:2025 (spec V1.2).
WHY THE LIBRARIAN NEEDS THIS AND OUR OWN CID CANNOT SUBSTITUTE.
Our nxc1- identifier is a plain sha256 of the bytes: excellent, and MEANINGLESS TO ANYONE ELSE.
A SWHID is computed the same way by everyone, from the bytes alone, with NO central registry and NO
hosting forge involved -- so a third party can independently confirm that the file we hold is the
same object Software Heritage holds, without trusting us OR them. That is the anti-rot property the
whole library exists for: the pointer can die and the identity survives.
It is also the interop currency: SPDX 2.3 Annex F accepts `swh` as a Persistent-Id external
repository identifier, and DataCite Metadata Schema 4.7 (2026-03-03) added SWHID as a
relatedIdentifierType. We emit one identifier and two ecosystems can read it.
THE CONSTRUCTION, and it is deliberately git's:
swh:1:cnt:<sha1 hex of ( "blob " + decimal(length) + NUL + the file bytes )>
`cnt` hashes ONLY the file bytes -- no filename, no path, no mtime, no metadata -- which is exactly
why it is stable across forges, renames and re-hosting.
⚠SHA-1 IS THE STANDARD'S CHOICE, NOT OURS, AND IT IS NOT OUR INTEGRITY GUARANTEE.
SHAttered (2017) produced chosen-prefix collisions, so a SWHID is an INTEROPERABLE NAME, never a
tamper-evidence claim. Our integrity story stays sha256 + byte length (aq_fixity). Carrying both is
the point: the SWHID is what the world can check, the sha256+length is what we can trust. Emitting
only the SWHID would be a downgrade wearing a standard's number.
⚠NAMED BOUND, NOT HIDDEN: the git object hash requires the header PREPENDED to the content, and the
estate's sha1 has no streaming/update API -- it takes one contiguous buffer. So this materialises
header||data and its memory cost is O(n). That is fine for a licence file or a source tarball and it
is NOT fine for a multi-gigabyte model weight. A streaming variant needs a streaming SHA-1 the
estate does not have; that is a real gap and it is stated here rather than discovered later.
license_tier: ORIGINAL
dependencies 2 imports · 3 importers
imports: nx_syscalls.nxnx_sha1.nx
imported by: nx_swhid.nxnx_swhid_gate.nxnx_wpt_acquire_lib.nx
structs
| none |
consts
| 33 | const SW_DIGEST_BYTES: i64 = 20 // SHA-1 is 160 bits |
| 34 | const SW_HEXLEN: i64 = 40 // DERIVED shape: 2 hex chars per digest byte |
| 35 | const SW_PREFIX_LEN: i64 = 10 // "swh:1:cnt:" |
| 36 | const SW_IDLEN: i64 = 50 // DERIVED: SW_PREFIX_LEN + SW_HEXLEN |
| 38 | const SW_HDR_MAX: i64 = 32 |
| 39 | const SW_B10: i64 = 10 |
| 40 | const SW_ZERO: i64 = 48 |
| 41 | const SW_NINE: i64 = 57 |
| 42 | const SW_LC_A: i64 = 97 |
| 43 | const SW_LC_F: i64 = 102 |
functions
| 45 | func sw_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } |
| 47 | func sw_cat(d: *u8, at: i64, s: *u8) -> i64 called by 1: swhid_cnt |
| 55 | func sw_hex(inp: *u8, n: i64, out: *u8, at: i64) -> i64 called by 1: swhid_cnt |
| 70 | func sw_decimal(v: i64, d: *u8, at: i64) -> i64 |
| 85 | func swhid_cnt(data: *u8, n: i64, out: *u8) -> i64 |
| 111 | func swhid_is_wellformed(s: *u8) -> i64 |