DECENTRALIZED EDGE NETWORK · ONLINE

☁️ WEB25.cloud

01 BROWSER NODEE2E ENCRYPTEDP2P DELIVERY

🔐 Welcome back

Choose how you want to continue.

No external wallet required.

Signing happens locally in your browser before deployment.

WEB25 gateway

Open any peer-hosted site by its hash, mirror locator or full WEB25 link.

Gateway ready Signature checked before render Sandboxed, opaque-origin frame Local cache first

Resolved by your browser

Your own node joins the swarm and pulls the bundle from whoever is seeding it.

Verified before it renders

The signed .torrentchain manifest is checked against the publisher identity.

Mirror as a fallback

If no peer answers, a temporary GoFile locator in the link can serve the same bytes.

🐛 Debug mode

Add &debug=true to any URL for detailed progress logs:

https://WEB25.cloud?orc=ABC123DEF456...&debug=true
☁️ WEB25.cloud · a PeerWeb fork

The web, hosted by the people reading it

Sign a static site with a key that never leaves your browser, seed it to the swarm, and message anyone by their Nostr address — no backend, no signalling server, no mandatory wallet extension.

0servers to rent
1local key, three addresses
E2Eencrypted direct messages
P2Pbrowser-to-browser delivery

Built on

How it works

Three steps, all of them in your browser

1

Bundle and sign

Your folder is bundled in memory (gzip by default) and signed with your local EVM identity. A .torrentchain manifest carries the publisher identity and integrity metadata.

2

Seed to the swarm

WebTorrent seeds the signed bundle straight from this tab. An optional GoFile mirror can stand in as temporary fallback transport.

3

Share one link

Anyone who opens your WEB25 link resolves it peer-first, verifies the signature, and renders the site in a sandboxed, opaque-origin frame.

Under the hood

What the platform actually does

Three pieces carry the whole thing: who you are, how a site ships, and how two browsers talk.

One key, three addresses

A single local secp256k1 key signs your deploys, encrypts your messages and publishes your handle — no second seed anywhere.

  • 0x… signs deploys
  • 04… encrypts messages
  • npub1… is how people reach you

Sites ship signed

Your folder is bundled in memory, signed, and seeded straight from the tab. The signature travels with the bytes.

  • gzip bundle, built in the browser
  • Signed .torrentchain manifest
  • Verified before anything renders

Chats find each other

Type someone's Nostr address and the encrypted invitation is delivered for you — no magnet links, no key pasting.

  • WebRTC first, relay fallback
  • NIP-44 / NIP-59 gift wrap
  • Unknown senders wait for your Accept

“Cryptographic validity is not consent.”

A valid invitation proves who sent it — never that you want to answer. Anything from someone you do not already trust simply waits, and declining sends nothing back at all.

🪪 Identity: one local key, three addresses

The identity model is local-first and is inspired by mylofi/local-data-lock: key material stays in-browser and WebAuthn passkeys gate unlock/signing sessions. A single secp256k1 key backs all three identities shown on the Account page — there is no second seed and no second private key.

🏷️ EVM address for signing deploys — 0x…
🔑 ECIES public key for encrypted messaging — 04…
🪐 Nostr address for being reached by others — npub1…
🔐 Local wallet, no mandatory external wallet extension
🧬 WebAuthn PRF unlock with Face ID / Touch ID / device PIN
🗝️ Several passkeys, one wallet — each wraps the same key
🧹 Session key lifecycle with explicit lock/clear behavior

The passkey does more than gate the unlock — it is what the key is wrapped with. Web25 requires the WebAuthn PRF extension, and the secret the authenticator returns during a user-verified assertion is stretched through HKDF-SHA256 into a non-extractable AES-GCM key that wraps the vault. That secret never leaves the call stack and is never written to storage, so nothing persisted on the device is enough on its own to derive the wallet. A browser or security key without PRF is refused rather than quietly handed a weaker path.

The Account page shows all three side by side, each with its own copy button. The Nostr address can be added or removed there: removing it takes you off the relays and hides the address, and adding it back restores the same npub — because it was never a separate key to begin with.

📦 Publishing: Bundle pipeline + .torrentchain

Static-site deploys run through a bundle pipeline (default: gzip), then attach a signed .torrentchain manifest carrying publisher identity and integrity metadata.

🗜️ Default bundle mode: gzip
🧾 Signed .torrentchain generated at publish time
✍️ Publisher EVM identity embedded in deploy artifact
✅ Identity/integrity checks applied during load

💬 Direct Messenger: search an address, start a chat

Messaging UX started from michal-wrzosek/p2p-chat and replaced its manual offer/answer step entirely. You enter someone's Nostr address, and the encrypted WebRTC invitation is delivered for you through public Nostr relays — no magnet links or key pasting.

🔍 Search by npub, then request a chat
🎁 Invitations sealed and gift-wrapped (NIP-44 / NIP-59)
🙈 SDP, ICE, EVM address and keys never publicly readable
🛑 An unknown peer is never auto-answered — you decide
🪪 Peer verification from public key to EVM address
✍️ Per-message signatures verified on receive
🧊 STUN used: stun:stun.l.google.com:19302

WebRTC stays the preferred transport. If a direct connection cannot be established, messages fall back to the relays — still signed and ECIES-encrypted exactly as before, with NIP-44 on top. The chat header says which path is in use: Connected · WebRTC or Connected · Nostr — one indicator, green either way.

🤝 Cryptographic validity is not consent

Verifying an invitation proves the sender is who they claim. It does not mean you want to talk to them — and anyone who knows your npub can produce a perfectly valid offer. Answering one is not free: it reveals your full ECIES public key, your EVM address, and, through ICE gathering, your machine's network addresses.

So an invitation from anyone who is not already a trusted contact simply waits. No answer is created, no ICE is gathered, and nothing is sent back until you press Accept. Declining discards it silently — the sender is not told, because telling them would confirm the address is live — and their retries are dropped, so a refusal cannot be worn down by repetition.

🔔 Pending invitations show npub, name, EVM address and time
📨 A stranger's chat request arrives there too — one ask is enough
✅ Accept re-checks expiry and identity, then answers
🚫 Decline sends nothing at all
🤝 Trusted friends reconnect directly, crypto checks and all
🔐 Contacts are encrypted with your wallet identity
🔒 Lock the wallet and the contact list is unreadable

Trust is authorization layered on top of authentication — it replaces none of the existing checks. A trusted friend's invitation is still verified end to end, and its identity tuple (Nostr key, ECIES key, EVM address) must both validate and match what was stored, so taking over an npub does not inherit the trust attached to it. Removing a contact is a local change only: they become unknown again, and no key is deleted or rotated on either side.

🛰️ Nostr relay: browser to relay, directly

The app talks straight to one public relay over a WebSocket. There is no Web25 relay, no proxy and no signalling server — just a rendezvous both sides agree on.

Two browsers can only meet on a relay they both use. Spreading over several relays looks sturdier and behaves worse: a message accepted by one relay and a subscription healthy on another never meet, and the invitation is lost while every relay involved reports success. One relay makes finding each other predictable, at an honest cost: while that relay is down, the messenger is down with it.

🌐 Rendezvous: wss://nos.lol
⚙️ One array in the config — point it at your own relay
🧯 Reconnects with backoff; a dropped socket is not a lost session
🧬 Every event is re-verified locally; duplicates collapse

🛡️ Security profile

WEB25.cloud validates signed publisher metadata and sanitizes rendered HTML with DOMPurify at load time. The sanitizer is intentionally configured in a compatibility-oriented profile (more permissive than strict defaults) to support richer static websites.

🧾 Signed-manifest verification before render gates
🧼 DOMPurify sanitization applied to loaded HTML
🧩 Extended tag/attribute/protocol allowance for compatibility
⚖️ Explicit trade-off: broader compatibility vs stricter hardening
Status

Shipped today, and what comes next

📋 What is available now

✅ Signed static-site deploys with publisher identity in .torrentchain
✅ Load-time verification path for identity + bundle integrity
✅ Local EVM identity protected by WebAuthn passkeys
✅ One key, three identities: EVM, ECIES and Nostr
✅ Direct P2P messaging with asymmetric cryptography
✅ Address a chat by npub, with encrypted Nostr signalling
✅ Encrypted Nostr relay fallback when WebRTC cannot connect
✅ Browser-first workflow with no mandatory centralized backend

🛣️ Future goals

🛰️ Own WebTorrent tracker
🪐 Own Nostr infrastructure
📦 Migrating from GoFile to Sia Network
🔒 Encrypted static-site content delivery
💱 Decryption-key unlock via atomic-swap payment flow

Publish something nobody can take down for you

Create a local wallet, drop a folder, sign it, and share the link. It runs entirely in this tab.

⭐ View the source