Decentralized Multimedia Layer for Nostr — Browser-as-a-Node P2P Network
"The Internet must be free and decentralized"
Share video, audio, and images across a peer-to-peer network without centralized servers. Every browser becomes a node. Every upload feeds the swarm.
Entropy is a Layer 2 protocol for decentralized social networks that enables the exchange of heavy multimedia content (video, 4K, audio, images) without relying on centralized servers or expensive storage relays. It runs entirely in the browser using a Browser-as-a-Node architecture and a bandwidth reciprocity incentive system.
Content is split into 5 MB chunks, hashed into a Merkle tree, and published as a Nostr kind:7001 Chunk Map event. Peers discover content through Nostr relays and exchange chunks directly via WebRTC DataChannels — encrypted, verifiable, and fully decentralized.
Download and install the extension from the releases folder.
- P2P Streaming — Progressive playback via MediaSource Extensions while chunks download from multiple peers in parallel
- Chunk Maps on Nostr — Content metadata lives on Nostr (kind:7001); binary data travels peer-to-peer
- Credit Economy — Bandwidth reciprocity with cryptographically signed Proof of Upstream receipts
- Background Seeding — Browser extension keeps serving chunks even when the tab is closed
- Cold Storage — High-ratio seeders earn premium credits by hosting unpopular content
- Plausible Deniability — Nodes only hold opaque binary fragments; no single node possesses recognizable content
- Keyframe-Aligned Chunking — Video chunks align to keyframes (IDR) for seamless MSE playback from any segment
- Credit Integrity — Tamper-resistant hash-chain ledger + peer-signed receipts prevent credit manipulation
┌─────────────────────────────────────────────────────────────────────┐
│ USER'S BROWSER │
│ │
│ ┌──────────────────────┐ ┌──────────────────────────────────┐ │
│ │ Extension Entropy │◄───►│ Web App Entropy │ │
│ │ (Manifest V3) │ │ (SPA — React + Vite) │ │
│ │ │ │ │ │
│ │ • Service Worker │ │ • Social Feed (Nostr) │ │
│ │ • Background Seeding │ │ • Media Player (MSE) │ │
│ │ • Node Dashboard │ │ • Upload Pipeline │ │
│ │ • Credit Ledger │ │ • Profile & Settings │ │
│ └──────────┬───────────┘ └───────────────┬──────────────────┘ │
│ └────────────┬────────────────────┘ │
│ ▼ │
│ ┌───────────────────────┐ │
│ │ @entropy/core │ │
│ │ (Shared Library) │ │
│ │ │ │
│ │ • Chunking + Merkle │ │
│ │ • WebRTC Transport │ │
│ │ • Nostr Protocol │ │
│ │ • Credit Ledger │ │
│ │ • IndexedDB Storage │ │
│ └───────────┬───────────┘ │
│ │ │
└──────────────────────────┼──────────────────────────────────────────┘
│
┌──────────────┼──────────────────┐
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌──────────────┐
│ Nostr │ │ WebRTC │ │ STUN / TURN │
│ Relays │ │ Peers │ │ Servers │
│ (Metadata) │ │ (Data P2P) │ │ (Signaling) │
└────────────┘ └────────────┘ └──────────────┘
Upload → Seed → Play in 5 stages:
- Chunk — File is split into ~5 MB pieces (keyframe-aligned for video)
- Hash — SHA-256 per chunk → Merkle root for integrity
- Store — Chunks persisted in IndexedDB via the browser extension
- Delegate — Extension activates WebRTC seeding in the background
- Publish — kind:7001 Chunk Map event signed and published to Nostr relays
Other users discover the content in their feed, connect via WebRTC signaling over Nostr ephemeral events (kind:20001), and download chunks in parallel from multiple peers with progressive streaming playback.
📖 Deep dives:
architecture.md·flow.md
A regular web page is ephemeral — the moment you close or navigate away from a tab, all JavaScript execution stops. For a P2P network that depends on nodes staying online to serve chunks to other peers, this is a fundamental problem.
The Entropy browser extension solves this by running a Manifest V3 Service Worker — a persistent background process that continues operating even when the web app tab is closed. This is the architectural reason the extension is not optional for uploaders and seeders.
| Capability | Without Extension | With Extension |
|---|---|---|
| Background seeding | ❌ Stops when tab closes | ✅ Service Worker keeps serving chunks 24/7 |
| Chunk storage | ✅ Persisted in IndexedDB, survives restarts | |
| Identity / Keypair | ✅ Managed in chrome.storage, always available |
|
| Credit ledger | ❌ Lost on page refresh | ✅ Persisted and hash-chain protected |
| Signaling listener | ❌ Offline when tab closes | ✅ Always online, ready to answer WebRTC offers |
Browser Tab (closed) Extension Service Worker (alive)
┌──────────────────────────────┐
│ • Listens for WebRTC offers │
│ via Nostr ephemeral events │
│ • Serves chunk requests from │
│ the IndexedDB chunk store │
│ • Earns credits for uploads │
│ • Verifies cold storage │
│ custody challenges │
│ • Prunes stale data (LRU) │
└──────────────────────────────┘
Without the extension, Entropy still works as a viewer and downloader — you can watch content and browse the feed. But to upload, seed, or earn credits, the extension is required. This is by design: it ensures that seeders are reliable long-term nodes, not ephemeral browser tabs.
🔌 Install the extension → see Build the Browser Extension below.
| Package | Path | Role |
|---|---|---|
@entropy/core |
packages/core/ |
Shared logic: chunking, hashing, Merkle tree, Nostr protocol, WebRTC transport, credit ledger, IndexedDB storage, peer reputation |
@entropy/web |
apps/web/ |
SPA — social feed, media player (MSE), upload pipeline, Nostr profiles, settings, credit panel |
@entropy/extension |
apps/extension/ |
Browser extension (Manifest V3) — background seeding, signaling listener, chunk server, node dashboard, credit integrity |
@entropy/core
▲ ▲
│ │
│ └──────────────┐
│ │
@entropy/web @entropy/extension
coreis pure and portable — no internal dependencies.webandextensiondepend oncorebut never on each other.- Communication between
webandextensionis exclusively via message passing (postMessage/chrome.runtime).
| Layer | Technology | Why |
|---|---|---|
| Language | TypeScript 5.x | Strict typing across the entire codebase |
| Monorepo | pnpm workspaces + Turborepo | Fast incremental builds, deduplicated dependencies |
| Web Framework | React 18 + Vite 5 | Lightweight SPA with fast HMR |
| Styling | Tailwind CSS 4 | Utility-first CSS with custom theme system (dark, light, ocean, forest, sunset) |
| State | Zustand | Minimal reactive state for P2P data |
| Routing | React Router 7 | SPA navigation |
| Nostr | nostr-tools | Event creation, signing, relay subscriptions |
| WebRTC | Native RTCPeerConnection | Direct peer connections with STUN/TURN support |
| Hashing | Web Crypto API (SHA-256) | Native, fast, zero dependencies |
| Storage | Dexie.js (IndexedDB) | Ergonomic API with migrations for gigabytes of binary data |
| Media | MediaSource Extensions (MSE) | Progressive streaming: feed chunks into <video> as they arrive |
| Transmuxing | mp4box 2.3.0 | Keyframe detection (stss), fMP4 remuxing for MSE compatibility |
| Extension | Manifest V3 + webextension-polyfill | Chrome + Firefox support via Service Worker |
| Testing | Vitest + fake-indexeddb | Unit and integration tests with in-memory IndexedDB |
pnpm installpnpm dev:webOpens the Entropy web app at http://localhost:5173. Requires a Nostr identity (NIP-07 extension like Alby or nos2x) or the Entropy extension for key management.
Pre-built packages are available in the releases/ folder while the extensions are pending store review. Download the .zip for your browser and follow the sideload instructions below.
Chrome / Chromium / Brave / Edge:
- Download
releases/entropy-extension-chrome-v*.zipand unzip it to a local folder. - Open
chrome://extensions→ enable Developer mode (top-right toggle). - Click Load unpacked → select the unzipped folder.
Firefox:
- Download
releases/entropy-extension-firefox-v*.zip. - Open
about:addons→ click the gear icon → Install Add-on From File… - Select the
.zipfile directly (Firefox accepts signed or temporary.zipinstalls).For persistent install (survives restart) without signing, open
about:config→ setxpinstall.signatures.requiredtofalse— for development/testing only.
Chrome:
pnpm --filter @entropy/extension buildLoad unpacked from apps/extension/dist/ in chrome://extensions (enable Developer mode).
Firefox:
pnpm --filter @entropy/extension build:firefoxLoad as temporary add-on from apps/extension/dist/ in about:debugging#/runtime/this-firefox.
To regenerate the distributable .zip files in releases/ (builds both targets in sequence):
# Both Chrome + Firefox in one command
pnpm package:extensions
# Or individually
pnpm --filter @entropy/extension package # → releases/entropy-extension-chrome-v*.zip
pnpm --filter @entropy/extension package:firefox # → releases/entropy-extension-firefox-v*.zip- Click the Entropy icon in the toolbar to see the popup with node status, credit ratio, and integrity info.
- Click Open Dashboard (or go to extension options) for the full node dashboard: chunk inventory, relay configuration, peer reputation, cold storage assignments, and network metrics.
# Run all tests across all packages
pnpm test
# TypeScript type checking
pnpm typecheck
# Per-package
pnpm --filter @entropy/core test
pnpm --filter @entropy/web test
pnpm --filter @entropy/extension testTests use Vitest with fake-indexeddb for storage tests. Core package has 190+ unit tests covering chunking, hashing, Merkle trees, Nostr events, credit ledger, proof of upstream, peer reputation, chunk transfer protocol, and credit integrity.
Content is not uploaded to Nostr relays. Instead, a Chunk Map event is published containing the Merkle root hash, individual chunk hashes, file size, MIME type, and a list of active seeders (gatekeepers). Peers use this map to discover and verify content.
{
"kind": 7001,
"tags": [
["x-hash", "<merkle_root_sha256>"],
["chunk", "<hash_0>", "0"],
["chunk", "<hash_1>", "1"],
["size", "157286400"],
["mime", "video/mp4"],
["title", "My Video"]
]
}Entropy uses bandwidth reciprocity to prevent free-riding:
- Earn credits by seeding chunks to other peers (upload)
- Spend credits by downloading chunks (download)
- Proof of Upstream (kind:7772) — cryptographic receipt signed by the receiver proving that a transfer happened
- Credit Gating — seeders verify requester's credit balance before serving chunks
- Onboarding — new users host "popular" content to earn their first credits
Users with high upload ratios (≥ 2.0) become eligible to host unpopular ("cold") chunks. In exchange, they earn premium credits that unlock priority bandwidth and faster downloads. The extension runs periodic cycles to assign, verify (via Custody Challenge/Proof), and prune cold storage assignments.
The credit ledger is protected by three layers:
- Hash Chain — each entry links to the previous via SHA-256, detecting any tampering
- Chunk-Backed Verification — entries are cross-referenced against the real IndexedDB chunk inventory
- Peer-Signed Receipts — every credit is backed by a Schnorr signature from the participating peer (kind:7772)
If integrity verification fails, the ledger resets to zero and cold storage eligibility is revoked.
📖 Full design:
credit-integrity.md
| Mechanism | Implementation |
|---|---|
| Plausible deniability | Chunks are raw binary fragments — no node holds recognizable content |
| Encrypted transport | WebRTC uses DTLS by default; all P2P traffic is encrypted |
| Encrypted signaling | SDP offers/answers encrypted with NIP-44 |
| No central servers | Neither relays nor STUN servers see content — only metadata and signaling |
| Hash verification | Every chunk verified against SHA-256 hash on receipt; invalid = peer banned |
| Rate limiting | 10 req/s per peer; 4 MB max per message; 60s DataChannel inactivity timeout |
| Optional Tor | Relay connections can be routed through SOCKS5/Tor |
| Document | Description |
|---|---|
architecture.md |
Full technical architecture — components, data models, protocols, ADRs |
flow.md |
Runtime flows — upload, discovery, download, streaming, credit lifecycle |
credit-integrity.md |
Credit integrity design — hash chain, chunk verification, peer-signed receipts |
tags.md |
Hidden tag system for organic content categorization |
| Command | Description |
|---|---|
pnpm install |
Install all workspace dependencies |
pnpm dev:web |
Start web app dev server (Vite) |
pnpm dev:extension |
Watch-build extension (Chrome) |
pnpm dev:core |
Watch-compile core package |
pnpm build |
Build all packages |
pnpm test |
Run all tests |
pnpm typecheck |
TypeScript verification across all packages |
pnpm --filter @entropy/extension build |
Production build for Chrome |
pnpm --filter @entropy/extension build:firefox |
Production build for Firefox |
pnpm package:extensions |
Build both targets and zip into releases/ |
pnpm --filter @entropy/extension package |
Chrome build → releases/entropy-extension-chrome-v*.zip |
pnpm --filter @entropy/extension package:firefox |
Firefox build → releases/entropy-extension-firefox-v*.zip |
This project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0).
Built with Nostr, WebRTC, and the belief that the Internet must be free and the future decentralized.
