fix(license): replace the LGPL zbase32 crate with an in-tree implementation
`zbase32 0.1.2` is LGPL-3.0+ — the only hard copyleft dependency in the whole
Rust graph and the last remaining blocker for the MIT release
(docs/LICENSE-COMPLIANCE-AUDIT.md §2). Statically linking LGPL code into a Rust
binary obliges us to ship relinkable objects, which is impractical for a node
image.
The audit offered two routes: the MIT `z32` crate, or an original
implementation. Took the latter — z-base-32 is an alphabet substitution over a
bit stream, so ~60 lines removes the blocker while adding *zero* new
dependencies rather than trading one supply-chain entry for another.
**Byte-compatibility was the requirement, not a nice-to-have.** A `did:dht`
identifier IS this encoding of an Ed25519 public key, so any drift would
silently rotate every node's DID and orphan its already-published DHT records.
So the semantics were not guessed: I read the vendored zbase32-0.1.2 source to
extract exactly what `encode_full_bytes` and `decode_full_bytes_str` do —
including that decode truncates to the next lower byte boundary, which is why a
52-character string round-trips to 32 bytes while discarding 4 padding bits.
A model implementation was then validated against three independent sources
before any Rust was written, all five vectors agreeing:
encode(b"testdata", 64) -> qt1zg7drcf4gn (crate doctest)
encode_full_bytes("Just an…") -> jj4zg7bycfzn… (crate doctest)
decode_full_bytes("qb1ze3m1") -> b"peter" (crate doctest)
encode([f0,bf,c7]) -> 6n9hq (Zimmermann spec)
encode([d4,7a,04]) -> 4t7ye (Zimmermann spec)
The module pins all of those plus four known 32-byte keys, a 0..40-byte
round-trip sweep, a 52-char/round-trip check over 64 keys, rejection of the
characters z-base-32 deliberately omits (`l`, `v`, `2`, `0`) and of non-ASCII,
and an alphabet/decode-table consistency check so the compile-time reverse table
can't drift from the alphabet.
`did_dht.rs` gains `did_for_a_known_key_is_stable`, which pins the full
identifier string for a known key — the regression that would actually hurt,
asserted at the call site that gives the string its meaning.
Dropped from Cargo.toml and Cargo.lock (7 lines); no other user in the tree.
Verified: 28/28 network tests pass, zero copyleft crates remain in the lockfile.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
6d33fea157
commit
fe46c898d1
@@ -7,7 +7,8 @@ Audit date: 2026-07-22. Scope: entire repo (core Rust workspace, neode-ui, apps/
|
||||
> **Updated 2026-08-08.** §1 (no license) and §3 (non-redistributable committed
|
||||
> files) are now **closed** — root `LICENSE` (MIT) + `NOTICE` are in the tree, and
|
||||
> the proprietary fonts and unused packages have actually been deleted. **§2
|
||||
> (`zbase32`, LGPL-3.0+) is still open** and is the last hard blocker.
|
||||
> (`zbase32`, LGPL-3.0+) is now closed too** — replaced by an in-tree
|
||||
> implementation. No copyleft dependency remains in the Rust graph.
|
||||
|
||||
---
|
||||
|
||||
@@ -44,7 +45,7 @@ Audit date: 2026-07-22. Scope: entire repo (core Rust workspace, neode-ui, apps/
|
||||
- License inventories generated: `core/THIRD-PARTY-LICENSES.md` (649 crates) and `neode-ui/THIRD-PARTY-LICENSES.md` (runtime deps + fonts + vendored).
|
||||
|
||||
**REMAINING (code changes, awaiting review — see sections below for detail):**
|
||||
1. Replace `zbase32` (LGPL-3.0+) with `z32` or original impl — §2.
|
||||
1. ~~Replace `zbase32` (LGPL-3.0+) with `z32` or original impl~~ — **DONE 2026-08-08**, original impl (§2).
|
||||
2. Swap `redis:7.4.8` → Valkey in `scripts/image-versions.sh` and deploys — §3.
|
||||
3. Delete dead StartOS-derived crates `core/{js-engine,container-init,models,helpers}` — §4.
|
||||
4. Attribution build integration: cargo-about in CI → ship full license texts in ISO; vite/rollup license plugin (or UI licenses page) for the web bundle; Android OSS-licenses screen — §5.
|
||||
@@ -52,7 +53,7 @@ Audit date: 2026-07-22. Scope: entire repo (core Rust workspace, neode-ui, apps/
|
||||
6. ~~Before repo goes public: purge deleted fonts/APKs from git history (`git filter-repo`)~~ — **superseded**: the launch plan is a fresh-history publish, so there is no history to rewrite. What still applies is verifying the game-icons author credit, and actually deleting the files (see the correction above — they were never removed).
|
||||
|
||||
**Re-verified 2026-08-08:**
|
||||
- `zbase32 0.1.2` (LGPL-3.0+) is **still a direct dependency** (`core/archipelago/Cargo.toml:113`), still used at `network/did_dht.rs:40,49`. Item 1 remains open and is the only hard copyleft blocker.
|
||||
- ~~`zbase32 0.1.2` (LGPL-3.0+) is still a direct dependency.~~ **Removed 2026-08-08** — see §2.
|
||||
- `LICENSE` (MIT) and `NOTICE` are present ✅. `core/THIRD-PARTY-LICENSES.md` and `neode-ui/THIRD-PARTY-LICENSES.md` are present ✅.
|
||||
- The four StartOS-derived crates in item 3 (`core/{js-engine,container-init,models,helpers}`) **still exist** — note KEY-05 legitimately cites `core/models`, so that one needs a look before deletion rather than a blind `rm`.
|
||||
|
||||
@@ -70,9 +71,22 @@ There is no `LICENSE`/`COPYING` file anywhere in the repo. No crate in `core/` d
|
||||
- [ ] Add `license = "MIT"` to all five workspace member `Cargo.toml`s (archipelago, container, openwrt, performance, security) and `Android/rust/archy-fips-core` (declares MIT but ships no license file — add one).
|
||||
- [ ] Add `"license": "MIT"` to `neode-ui/package.json` and `apps/{morphos-server,router,did-wallet}/package.json`.
|
||||
|
||||
## 2. BLOCKER — copyleft dependency that must be replaced
|
||||
## 2. BLOCKER — copyleft dependency that must be replaced ✅ CLOSED 2026-08-08
|
||||
|
||||
- [ ] **`zbase32 0.1.2` — LGPL-3.0+** — the only hard copyleft blocker in all 649 resolved Rust crates. Direct dep of `archipelago`, used in `core/archipelago/src/network/did_dht.rs` for did:dht z-base-32 encoding. LGPL statically linked into a Rust binary requires shipping relinkable objects/source — impractical. **Replace with the MIT `z32` crate** or a ~30-line original alphabet-substitution implementation.
|
||||
- [x] **`zbase32 0.1.2` — LGPL-3.0+** — was the only hard copyleft blocker in all 649 resolved Rust crates. Direct dep of `archipelago`, used in `core/archipelago/src/network/did_dht.rs` for did:dht z-base-32 encoding. LGPL statically linked into a Rust binary requires shipping relinkable objects/source — impractical.
|
||||
|
||||
**DONE 2026-08-08.** Replaced with an original in-tree implementation at
|
||||
`core/archipelago/src/network/zbase32.rs` (~60 lines incl. docs) rather than
|
||||
the `z32` crate — the encoding is an alphabet substitution over a bit stream,
|
||||
so this removes the blocker without adding any dependency or new supply-chain
|
||||
surface. Dropped from `Cargo.toml` and `Cargo.lock`.
|
||||
|
||||
Byte-compatibility was the hard requirement: a `did:dht` identifier *is* this
|
||||
encoding of an Ed25519 public key, so any drift would silently rotate every
|
||||
node's DID and orphan its published DHT records. The replacement is pinned
|
||||
against the removed crate's own three doc-test vectors, the canonical vectors
|
||||
from Zimmermann's z-base-32 spec, and four known 32-byte keys — plus a
|
||||
`did_for_a_known_key_is_stable` test at the `did_dht.rs` call site.
|
||||
|
||||
No GPL, AGPL, SSPL, or unlicensed crates exist anywhere else in the Rust graph. (`r-efi` and `self_cell` list LGPL/GPL only as options in OR-expressions — elect MIT/Apache, no action.)
|
||||
|
||||
@@ -132,7 +146,7 @@ The ISO redistributes a full Debian (trixie) system plus ~29 container image tar
|
||||
|
||||
## Quick reference: what's already clean
|
||||
|
||||
- All 649 Rust crates except `zbase32`: permissive or dual-licensed.
|
||||
- All Rust crates: permissive or dual-licensed (`zbase32` was the sole exception and is gone as of 2026-08-08).
|
||||
- All 833 npm packages in neode-ui: no GPL/AGPL anywhere; only dev-tool LGPL (sharp's libvips, never distributed).
|
||||
- Android Gradle deps: 100 % Apache-2.0, all pinned, no Play Services/telemetry.
|
||||
- FIPS mesh: MIT (© 2026 Johnathan Corgan) — keep notice.
|
||||
|
||||
Reference in New Issue
Block a user