Captures the verified 2026-06-16 design: swarm-assist/origin-always-wins, iroh-blobs as the swarm engine, BLAKE3 addressing, signed Nostr/release-root authenticity, and the Phase 0-4 plan. Foundation doc for the dht branch. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
12 KiB
DHT / Peer-Distributed Content Design
Status: Design (no code yet) · Date: 2026-06-16 · Author: archipelago + Claude
1. Purpose
Make Archipelago's large-file movement peer-distributed: a node should be able to fetch content (OTA updates, app/OCI images, IndeeHub films) from any other node that already has it, falling back to the central origin only when no peer can serve it.
This document covers three use-cases that are the same problem — "fetch content-addressed bytes from whatever node already has them, verify, fall back to origin":
- OTA releases — node binaries + frontend tarballs.
- App installs — container/OCI images.
- IndeeHub streaming — films created in "backstage" on one node, streamable from any node that has them stored or cached.
Guiding principle (decided 2026-06-16)
Swarm-assist, origin always wins. The peer swarm is an optimization. The central origin (OVH HTTP release assets / MinIO) remains the guaranteed fallback and the source of truth for reliability. We never bet correctness or availability on the P2P layer. This is what keeps the system bulletproof while the P2P stack matures.
2. Current state (verified 2026-06-16)
OTA (core/archipelago/src/update.rs)
- Manifest at
DEFAULT_UPDATE_MANIFEST_URL(update.rs:67) = vps2 OVH (146.59.87.168:3000/lfg2025/archy/raw/branch/main/releases/manifest.json). check_for_updates()(:565) walks an operator mirror list (default_mirrors():105,load_mirrors():123), origin-rewrites component URLs to the chosen mirror (rewrite_manifest_origins():227).download_component_resumable()(:821) — resumable HTTP Range download, 6 retries, exponential backoff.- Integrity: SHA-256 only (
:984), compared againstComponentUpdate.sha256. - No authenticity: manifests are unsigned. A compromised mirror can serve a malicious
but hash-consistent binary. Post-apply health probe + auto-rollback exist
(
verify_pending_update():389,rollback_update():1423) but that is not a substitute for signature verification. - Manifest schema:
{version, release_date, changelog[], components[{name, current_version, new_version, download_url, sha256, size_bytes}]}.
App installs (core/archipelago/src/api/rpc/package/install.rs)
handle_package_install()(:195) →do_pull_image()(:1062) tries each registry fromcontainer/registry.rsin priority order (OVH primary),rewrite_image()rewrites the origin,podman pull. Same centralized-mirror shape as OTA.
Transport & identity (already P2P-capable)
transport/mod.rs—NodeTransporttrait (:74),TransportRouter(:336), priority stack Mesh→LAN→FIPS→Tor.PeerRegistry(:199) tracks per-peer addresses (mesh id, LAN ip:port,fips_npub, onion).- Seed-derived identity (
seed.rs): node Ed25519 (archipelago/node/ed25519/v1), node Nostr secp256k1 (archipelago/nostr-node/secp256k1/v1), FIPS secp256k1 (archipelago/fips/secp256k1/v1). DID + npub per node. - Already content-addressed:
blobs.rsstoresblobs/<cid>keyed by SHA-256 hex, with HMAC-SHA256 capability tokens (BlobMeta, 64 MiB cap).transport/chunking.rsdoes Reed-Solomon chunking for LoRa.
Trust scaffolding — NOT built yet
- No
core/src/trust/, noROOT_PUBKEY, noderive_release_root_*, noarchipelago/release/root/*HKDF strings, no JCS/canonical JSON, no signing ceremony scripts, nomanifest-v2.json. The "Phase 0 signed manifest" design exists only as notes.
IndeeHub (the streaming target)
- Original platform (not a fork). Working source:
~/Projects/Indeedhub Prototype/(Vue 3 + NestJS). Submodulegit.tx1138.com/lfg2025/indeehub.git(host retired — needs a live remote). Inarchy: image-only,apps/indeedhub/manifest.ymlpulls146.59.87.168:3000/lfg2025/indeedhub:1.0.0(+-api,-ffmpeg, postgres, redis, minio, nostr-rs-relay). - Streaming today: FFmpeg → HLS (.m3u8 + AES-128 .ts segments) in MinIO
(
indeedhub-private/-public), metadata in Postgres, transcode queue in Redis, auth via Nostr (NIP-98). Glue:install.rs:68patch_indeedhub_nostr_provider()injects the NIP-07 provider into the nginx-wrapped frontend. - No "backstage" code yet — it's the creator/upload side we're introducing.
3. Protocol evaluation (verified maintenance status, 2026-06-16)
| Option | Verdict | Why |
|---|---|---|
| Web5 / TBD / DWN | ❌ Reject | Block wound TBD down, handed components to DIF (TBD54566975→decentralized-identity). web5-js latest release 0.12.0, Oct 2024 (~20 mo stale). DWN spec still Draft. DWNs are DID-scoped record stores, not a blob-streaming swarm. Fails the "well-maintained + bulletproof" bar. |
| iroh / iroh-blobs | ✅ Swarm engine | v1.0.0 shipped 2026-06-15. Rust (matches core), BLAKE3 verified streaming over QUIC + hole-punching + relays, content-addressed, KB→TB, native byte-range support (ideal for HLS). n0 team, production relays. |
| Nostr Blossom | ✅ Index/catalog layer | SHA-256-addressed blobs over HTTP, modular BUD specs (BUD-01/02/04/05/06/08), actively developed, already aligned (Nostr identity everywhere; blobs.rs already SHA-256). Server-centric (not a peer swarm) → use as discovery + IndeeHub catalog + HTTP fallback, not the distribution engine. |
| libp2p-kad (hand-rolled DHT) | ⚠️ De-prioritize | Was the old "Phase 4 build a Kademlia" plan. iroh 1.0 supersedes the need to hand-roll discovery + swarm. Revisit only if iroh proves unworkable. |
Note vs. prior plan: the saved DHT design said "no iroh as a Phase 0–5 dep (revisit post-Phase 3)." iroh hitting 1.0 removes the main reason for that deferral — this design reverses that non-choice and adopts iroh as the swarm layer, collapsing the from-scratch Kademlia work.
4. Recommended architecture — three layers, one engine
Build one peer-distribution layer; use it for all three use-cases.
┌─────────────────────────────────────────────┐
Authenticity │ Signed Nostr events (per-node npub) + │ "who published this,
& Discovery │ seed-derived RELEASE ROOT key for OTA + │ who has it"
│ Blossom BUD catalog for IndeeHub │
└─────────────────────────────────────────────┘
┌─────────────────────────────────────────────┐
Integrity & │ BLAKE3 content addressing (iroh-native, │ "name bytes by hash,
Addressing │ range-verifiable). SHA-256 kept in manifest │ verify on arrival"
│ during migration window. │
└─────────────────────────────────────────────┘
┌─────────────────────────────────────────────┐
Transport & │ iroh-blobs swarm (peers that already have │ "move the bytes"
Swarm │ it) ─── fallback ───▶ OVH HTTP / MinIO │
│ origin (ALWAYS wins) │
└─────────────────────────────────────────────┘
- Integrity/addressing — BLAKE3. iroh-native, supports verified range streaming
(essential for HLS + resumable). Keep SHA-256 in the manifest for back-compat through the
migration window; add a
blake3field alongside. - Discovery/authenticity — signed Nostr events + release root key.
- OTA: the Phase 0 seed-derived release root key signs the manifest (BLAKE3 root hash
- version). Integrity ≠ authenticity — content addressing proves bytes are intact, the signature proves we authorized them. Both are required.
- "Who has blob X" advertised via signed Nostr events
{content-hash, provider-npub, ts}, so nodes find seeds without a central tracker. - IndeeHub: Blossom BUDs for the film catalog + provider/mirror lists.
- OTA: the Phase 0 seed-derived release root key signs the manifest (BLAKE3 root hash
- Transport/swarm — iroh-blobs, origin fallback. Node asks the swarm for a hash; peers
that have it serve range-verified BLAKE3 streams; if the swarm yields nothing, fall back to
the existing resumable HTTP path (
update.rs:821) against OVH/MinIO. A node that finishes a download automatically becomes a seed.
Bulletproof posture
The swarm sits above a proven HTTP path, never in place of it. Worst case (every peer offline, iroh bug, NAT failure) the node downloads exactly as it does today. iroh 1.0 is new; this containment is deliberate.
5. Use-case flows
OTA / app installs
- Node reads the signed manifest (via signed Nostr event or HTTP), gets BLAKE3 root hash
- release-root signature; verify signature → reject on failure.
- Query swarm (signed provider events) for peers holding that hash.
- Download range-verified BLAKE3 stream from peers; verify full BLAKE3 (+ SHA-256 during migration).
- No peers / failure → resumable HTTP from OVH (current path).
- Apply + health-probe + auto-rollback (unchanged). Updated node becomes a seed.
- OCI images: content-address image layers the same way; OVH registry stays the origin.
IndeeHub streaming ("backstage → any node")
- Creator publishes a film in backstage → FFmpeg → HLS; each .ts segment is a content-addressed (BLAKE3) blob, immutable and small → ideal swarm objects.
- Publish a signed Nostr event advertising title + segment hashes (Blossom catalog).
- Any node running IndeeHub resolves the content address and streams from the nearest node(s) that have it stored/cached via iroh range streaming; MinIO/OVH is origin.
- AES-128 key delivery + NIP-98 auth unchanged (keys gate decryption; swarm only moves encrypted segments — so untrusted seeds can cache without seeing plaintext).
6. Phasing (folds into the existing Phase 0–6 plan)
- Signed manifests (required first, unbuilt).
derive_release_root_ed25519/derive_release_root_nostrinseed.rs(HKDFarchipelago/release/root/ed25519/v1,.../secp256k1/v1);core/src/trust/(anchor/bundle/manifest/timestamp/nostr); JCS canonical JSON; ceremony scripts;manifest-v2.jsonwith signature. Gives authenticity, which content-addressing does not. - BLAKE3 alongside SHA-256 in the manifest +
blobs.rs. - iroh-blobs PoC behind a feature flag: serve OTA blobs from the swarm with HTTP fallback; measure on a scratch/test node, then the fleet.
- Signed Nostr advertisement events for releases (publisher identity + provider lists).
- IndeeHub on the same blob layer (Blossom catalog + iroh swarm; MinIO origin).
This collapses the old "Phase 4: build S/Kademlia from scratch" into "adopt iroh," a large de-risking.
7. Open decisions
- BLAKE3 migration scope: dual-hash window length; whether to re-hash historical releases or only BLAKE3 going forward.
- iroh ↔ existing transports: iroh brings its own QUIC + hole-punching + relays; decide
how it coexists with FIPS/Tor (run iroh standalone first; integrate with
TransportRouterlater if useful). - Seed retention policy: how long nodes keep blobs to seed others (disk pressure on small nodes); pinning rules for IndeeHub films vs. transient OTA blobs.
- Privacy: iroh dial-by-key vs. Tor's anonymity; default transport per content type.
References
- iroh: https://github.com/n0-computer/iroh · iroh-blobs: https://github.com/n0-computer/iroh-blobs · docs: https://docs.iroh.computer/protocols/blobs
- Blossom: https://github.com/hzrd149/blossom · NIP-B7: https://nips.nostr.com/B7 · nostr-blossom (Rust): https://docs.rs/nostr-blossom
- Web5/DWN (rejected): https://github.com/decentralized-identity/web5-js · https://identity.foundation/decentralized-web-node/spec/ · https://block.xyz/inside/block-contributes-digital-identity-components-to-the-decentralized-identity-foundation