`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>
17 KiB
License Compliance Audit — Open-Source Release
Audit date: 2026-07-22. Scope: entire repo (core Rust workspace, neode-ui, apps/*, Android companion, image-recipe ISO, docker/, app-catalog, reticulum-daemon, demo/) plus the external FIPS source and registry-mirrored images.
Verdict (as of the 2026-07-22 audit): the dependency graph is almost entirely permissive (MIT/Apache/BSD) and compatible with a free open-source release. But the repo was not releasable as-is: it had no license of its own, one LGPL Rust dependency, several non-redistributable committed assets (proprietary fonts, unknown-rights media), and missing attribution machinery. Everything below is ordered by severity.
Updated 2026-08-08. §1 (no license) and §3 (non-redistributable committed files) are now closed — root
LICENSE(MIT) +NOTICEare in the tree, and the proprietary fonts and unused packages have actually been deleted. §2 (zbase32, LGPL-3.0+) is now closed too — replaced by an in-tree implementation. No copyleft dependency remains in the Rust graph.
STATUS UPDATE — 2026-07-23
DONE:
-
MIT adopted. Root
LICENSE+NOTICEadded;license = "MIT"in all 5 workspace crates (archy-fips-core already had it);"license": "MIT"(+"private": true) in all 4 package.json files. -
Deleted:
Courier_New/,Benton_Sans/,Redacted/fonts;wireguard.apk;atob.s9pk.History note (2026-08-08): this line originally claimed all of these plus
test-install.shwere "git-rm'd" on 2026-07-23. They were not — only theweb/distcopies had been removed, and all seven sources were still tracked at HEAD nearly three weeks later. The six listed above were actually deleted on 2026-08-08 (neode-ui/test-install.shwas left; it is not a licensing concern). Kept as a reminder that a DONE entry here is a claim, not evidence — re-verify with:git ls-tree -r HEAD --name-only | grep -iE 'Courier_New|Benton_Sans|Redacted/|wireguard.apk|atob.s9pk'Deletion was safe: no
@font-facerule ever referenced them (all four in the tree load Montserrat), theCourier Newhits intailwind.config.jsand two public HTML files arefont-familyfallbacks naming the system font, andwireguard.apk/atob.s9pkhad zero references anywhere. Montserrat (OFL.txt) and Open Sans (LICENSE.txt) remain, as does the actively-usedarchipelago-companion.apk. Removing the two packages also took ~40 MB off the frontend OTA tarball. -
Media provenance resolved: all demo music/photos/posters, UI sfx, backgrounds, and intro video are the author's original work — recorded in
demo/content/README.mdandNOTICE. -
Meshtastic device artwork attributed (
mesh-devices/ATTRIBUTION.md+ NOTICE); icon attribution added (assets/icon/ATTRIBUTION.md: game-icons.net CC BY 3.0, pixelarticons MIT). -
Reticulum decision: include + disclose (NOTICE states the Reticulum License restrictions and that it applies only to the optional daemon).
-
indeedhub: deferred — partnership in place; license the submodule before/at public release.
-
License inventories generated:
core/THIRD-PARTY-LICENSES.md(649 crates) andneode-ui/THIRD-PARTY-LICENSES.md(runtime deps + fonts + vendored).
REMAINING (code changes, awaiting review — see sections below for detail):
Replace— DONE 2026-08-08, original impl (§2).zbase32(LGPL-3.0+) withz32or original impl- Swap
redis:7.4.8→ Valkey inscripts/image-versions.shand deploys — §3. - Delete dead StartOS-derived crates
core/{js-engine,container-init,models,helpers}— §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.
- Release-checklist items: per-release Debian source pointer (snapshot.debian.org), catalog
license/sourceUrlfields, restrict ISO image bundling to the audited list — §6. Before repo goes public: purge deleted fonts/APKs from git history (— 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).git filter-repo)
Re-verified 2026-08-08:
Removed 2026-08-08 — see §2.zbase32 0.1.2(LGPL-3.0+) is still a direct dependency.LICENSE(MIT) andNOTICEare present ✅.core/THIRD-PARTY-LICENSES.mdandneode-ui/THIRD-PARTY-LICENSES.mdare present ✅.- The four StartOS-derived crates in item 3 (
core/{js-engine,container-init,models,helpers}) still exist — note KEY-05 legitimately citescore/models, so that one needs a look before deletion rather than a blindrm.
1. BLOCKER — the project has no license ✅ CLOSED
Resolved: MIT adopted, root LICENSE + NOTICE present. Original finding below.
There is no LICENSE/COPYING file anywhere in the repo. No crate in core/ declares a license field; none of the four package.json files do either (and the three apps/* packages aren't even private: true). Until fixed, the code is "all rights reserved" — publicly visible, but legally not open source and not usable by anyone.
Do:
- Choose a license. Recommendation: MIT — the Bitcoin-ecosystem norm (Bitcoin Core, LND are MIT), maximally compatible with everything found in the graph. (Alternatives: Apache-2.0 adds a patent grant; GPLv3 if copyleft is desired — nothing in the deps prevents any of these.)
- Add
LICENSEat repo root with the year and copyright holder. - Add
license = "MIT"to all five workspace memberCargo.tomls (archipelago, container, openwrt, performance, security) andAndroid/rust/archy-fips-core(declares MIT but ships no license file — add one). - Add
"license": "MIT"toneode-ui/package.jsonandapps/{morphos-server,router,did-wallet}/package.json.
2. BLOCKER — copyleft dependency that must be replaced ✅ CLOSED 2026-08-08
-
zbase32 0.1.2— LGPL-3.0+ — was the only hard copyleft blocker in all 649 resolved Rust crates. Direct dep ofarchipelago, used incore/archipelago/src/network/did_dht.rsfor 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 thez32crate — 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 fromCargo.tomlandCargo.lock.Byte-compatibility was the hard requirement: a
did:dhtidentifier 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 adid_for_a_known_key_is_stabletest at thedid_dht.rscall 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.)
3. BLOCKER — committed files we may not redistribute ◐ fonts/packages CLOSED 2026-08-08; media + redis items still open
Remove from git (history purge is moot — the launch plan is a fresh-history publish, so past commits are not carried over):
neode-ui/public/assets/fonts/Courier_New/— Monotype proprietary font, no license, unused in CSS.Delete.DELETED 2026-08-08.neode-ui/public/assets/fonts/Benton_Sans/BentonSans-Regular.otf— commercial Font Bureau typeface, no license, unused.Delete.DELETED 2026-08-08.neode-ui/public/packages/wireguard.apk(17 MB) — DELETED 2026-08-08. — official WireGuard Android APK containing GPL-2.0libwgcomponents; redistribution triggers GPL source-offer. Unreferenced since the FIPS migration — delete.neode-ui/public/packages/atob.s9pk(24 MB) — DELETED 2026-08-08. — Start9 service package, unknown license, referenced only by a test script. Delete.demo/content/music/(18 full tracks, ~150 MB) anddemo/peer-media/(17 photos/book covers/film posters) — no recorded rights. If they're your own/AI-generated work, document that in ademo/content/README; otherwise remove.neode-ui/public/assets/video/video-intro.mp4,Kratter.MP3, photographicbg-*.jpgbackgrounds, UI/arcade sound effects inassets/audio/— same: document provenance (user-made per project convention) or replace.welcome-noderunner.mp3is ElevenLabs TTS — their commercial-use terms allow this on paid plans; note it.- Registry:
redis:7.4.8(scripts/image-versions.shREDIS_IMAGE) — Redis ≥ 7.4 is RSALv2/SSPLv1, not open source; re-hosting it on your registry is redistribution under a restricted license. Switch to Valkey (BSD-3, already mirrored) everywhere.
4. VERIFY — unknown/third-party provenance
neode-ui/public/assets/img/mesh-devices/(36 SVGs) — almost certainly Meshtastic project device artwork (meshtastic/web is GPL-3.0). Confirm source; either replace with original art or comply with the upstream license + attribution.neode-ui/public/assets/icon/—barbarian.svg,batteries.svgmatch game-icons.net (CC BY 3.0 — visible attribution required); pixel-style icons match pixelarticons (MIT). Confirm and add attribution, or replace.Redacted/redacted.regular.ttf— upstream is SIL OFL 1.1 but no license file is shipped.AddDELETED 2026-08-08 (unused; deleting was cheaper than sourcing the OFL text).OFL.txtor delete (unused).- indeedhub — submodule (private gitea) not checked out; no known license, yet
indeedhub{,-api,-ffmpeg}:1.0.0images are distributed via registry/ISO.indeedhub-ffmpegimplies a bundled FFmpeg (LGPL/GPL → source-offer obligations). Must license the project and audit the ffmpeg build before public release. minmoto/fmcdv0.8.0 andark-bitcoin/bark(barkd) — binaries redistributed in your images; verify upstream licenses (bark claims Apache-2.0/MIT dual) and include their notices.- Start9/StartOS heritage —
core/{js-engine,container-init,models,helpers}are StartOS-derived (embassy paths, s9pk handling). start-os is MIT → attribution required if kept. Better: delete these four crates — they are not workspace members, cannot compile (broken../../patch-dbpath dep), and carry an unpinnedyajrc = "*"git dep on a moving branch. Deleting removes both the attribution question and dead code. - Reticulum (RNS 1.3.5 + LXMF) — verified: custom "Reticulum License" — MIT-style plus field-of-use restrictions (no systems designed to harm humans; no AI/ML training-dataset use). Redistribution is permitted, so shipping the PyInstaller
archy-reticulum-daemonbinary is fine if the license text is included with it — but the OS cannot claim to be 100 % OSI-open-source while bundling it. Options: include + disclose (recommended, matches "plan for decentralization" honesty), or make the daemon an optional download.
5. REQUIRED — attribution / notice machinery (currently absent)
Nearly every permissive license (MIT/BSD/ISC/Apache) requires reproducing copyright + license text in distributed binaries — and right now every distribution channel strips them:
- Rust binaries (649 crates, ~85 % MIT/Apache dual): generate
THIRD-PARTY-LICENSESwithcargo-about(orcargo-license) in CI; ship it in the ISO at e.g./usr/share/doc/archipelago/. Include ring's three license files (LICENSE, LICENSE-BoringSSL, LICENSE-other-bits) and note the system OpenSSL (Apache-2.0) linked viassh2. - Web bundle: Vite/esbuild strips all
@licensecomments fromweb/dist. Addrollup-plugin-license/vite-plugin-licenseto emit a third-party attribution file, or add an "Open-source licenses" page in the UI. Runtime deps needing notices: vue/vue-router/pinia/vue-i18n (MIT), d3 (ISC), leaflet (BSD-2), dompurify (elect Apache-2.0 of its MPL/Apache dual), fuse.js (Apache-2.0), qrcode/qr-scanner/qrloop/buffer/fast-json-patch (MIT). - Android APK:
packaging.excludesstripsMETA-INFlicense texts and there is no licenses screen. Add an OSS-licenses screen or bundledlicenses.txtcovering AndroidX/Compose/OkHttp/ZXing (Apache-2.0), fips © 2026 Johnathan Corgan (MIT — the core of the VPN feature), tokio/tracing (MIT), subtle (BSD-3), tun (WTFPL — permissive, just list it), secp256k1 family (CC0). Generate the Rust side from the committedCargo.lockwith cargo-about. - AIUI demo bundle (
demo/aiui/— committed minified build): bundles Mermaid, Cytoscape, KaTeX, D3, Lodash, Workbox (all MIT/BSD). Add aTHIRD-PARTY-LICENSESfile next to it (or rebuild with a license plugin). - Keep the intact MIT headers in the two vendored
qrcode.jscopies (docker/lnd-ui, docker/electrs-ui) — already compliant, don't minify them. - Fonts kept: Montserrat (OFL.txt present ✓), Open Sans (Apache LICENSE.txt present ✓) — keep license files adjacent to the font files in dist.
6. REQUIRED — distribution-level obligations (ISO & registry)
The ISO redistributes a full Debian (trixie) system plus ~29 container image tarballs; the private registry re-hosts upstream images. Re-hosting = redistribution, same obligations as bundling.
- GPL source offer for the ISO — kernel, GRUB, busybox/live-boot, coreutils, nftables, cryptsetup, wireguard-tools, SYSLINUX
isohdpfx.bin, etc. Easiest compliance: keep/usr/share/doc/*/copyright(the build already does ✓) and publish, per release, either a mirror of the exact Debian source packages (apt-get sourcesnapshot / snapshot.debian.org pointer) or a written offer in the docs. Add this to the release checklist. - AGPLv3 images redistributed (mempool, Grafana, Vaultwarden, SearXNG, PhotoPrism, Nextcloud, Immich, CryptPad, MinIO): AGPL compliance = make corresponding source available. You ship a modified mempool-frontend (
docker/mempool-frontendentrypoint patch) — the patch is in-repo, so compliance is met once the repo is public; state this in docs. For unmodified images, link upstream sources in the app catalog. - GPLv2/GPLv3 images (MariaDB, Jellyfin, AdGuard Home, strfry): unmodified redistribution → provide license text + upstream source links (a
license+sourceUrlfield perapp-catalog/catalog.jsonentry solves this catalog-wide). - Non-free firmware (firmware-realtek/iwlwifi/misc/linux-nonfree, intel/amd microcode): redistributable but proprietary — disclose in docs ("includes non-free firmware for hardware support"), like Debian's own non-free-firmware ISOs do.
- The ISO build's live-server image capture (
podman saveof whatever matches on the dev server) is a compliance hazard — bundle only from the audited image list. - FIPS daemon (jmcorgan/fips v0.4.1, MIT ✓) and nostr-rs-relay binary (MIT ✓): include their license texts in the notices bundle.
7. Housekeeping (supports compliance)
- Add lockfiles + pinned versions in
apps/*(currently floating^ranges, violating the project's own pinning rule) — reproducibility is also what makes license audits stay true. Androidfips dep is pinned to a personal fork rev (9qeklajc/fips-native@46494a74) — mirror or vendor it so outside contributors can build.- Move
@types/dompurifyto devDeps; refresh staleneode-ui/node_modules. - Add a
NOTICEfile at root naming: fips (Johnathan Corgan, MIT), Start9 start-os (if any derived code remains), Kazuhiko Arase qrcode.js, font licenses, icon attributions. - Consider CI license gating:
cargo-deny(Rust) +license-checker(npm) with an allowlist, so new copyleft deps are caught at PR time.
Quick reference: what's already clean
- All Rust crates: permissive or dual-licensed (
zbase32was 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.
- js-engine binds deno_core (MIT) as a crate, nothing vendored — moot if dead crates are deleted.
- reticulum-daemon Python is original code; obligations attach only to the PyInstaller binary (see §4).
- Bitcoin Core/Knots, LND, BTCPay, Electrs, Fedimint, core-lightning, Gitea, Home Assistant, Tailscale, Portainer, Uptime-Kuma, filebrowser, ollama, penpot: MIT/Apache/BSD/Zlib/MPL — link + notice is enough.