nullroomio/nullroom.io
nullroomio/nullroom.io has 29 GitHub stars and 0 forks. GitHot measured 0 stars in 24 hours and 0 in 7 days. Last checked 2026-11-12 10:49 UTC. Last push 2026-10-23.
zero-trace, post-quantum safe, ephemeral peer-to-peer messaging web app
できること
- E2EE Messaging - AES-GCM-256 via Web Crypto API, per-message random IV
- Post-Quantum Key Exchange - ML-KEM-768 (NIST FIPS 203, Security Level 3) with hybrid HKDF derivation
- P2P File Transfer - Chunked, per-chunk encrypted, dedicated data channel; connection-aware limits (1 GB direct / 100 MB relay)
- 4-Word Handshake - BIP-39 wordlist, PBKDF2 (100K iterations), one-time encrypted Redis blob (180s TTL)
計測したスター増加
- 24時間
- 0
- 7日間
- 0
- 15日間
- 更新が遅れています
- 1か月
- 更新が遅れています
期間ごとの純増数です。期間は重複しており、日別の値ではありません。未計測の値は空欄です。スター数は注目度の指標であり、品質の保証ではありません。
README.md
オリジナルPeer-to-peer encrypted messaging that leaves no trace. No accounts. No logs. No server memory. Messages travel directly between browsers over WebRTC, encrypted with a hybrid post-quantum + classical key that the server never possesses. Rooms self-destruct after 15 minutes. Close the tab and it's gone.
Why nullroom?
Most encrypted messengers still require a phone number, an app install, and an account — creating a permanent identity tied to your conversations. nullroom takes a different approach:
| Traditional E2EE Apps | nullroom | |
|---|---|---|
| Identity | Phone number or email required | None — no accounts, ever |
| Installation | App store download | Browser tab — nothing to install |
| Persistence | Messages stored (encrypted) on servers | Messages exist only in browser memory |
| Metadata | Server logs connections, contacts, timestamps | Server logs disabled; Redis-only ephemeral state |
| Post-quantum | Rarely | ML-KEM-768 hybrid key exchange by default |
| After the conversation | Account persists, contact graph remains | Close the tab. No trace on the device or server |
nullroom is built for conversations that shouldn't leave a footprint: sharing production secrets, sensitive legal consultations, journalist-source communication, or any exchange where the existence of the conversation is as sensitive as its content.
Security Architecture
Hybrid Post-Quantum Encryption
Every session is protected by two independent layers of encryption, fused into a single hybrid key:
Classical Layer Quantum Layer
───────────── ─────────────
AES-GCM-256 key ML-KEM-768 (FIPS 203)
generated in browser key exchange over DataChannel
lives in URL fragment (#) 1184-byte public key
never sent to server 1088-byte ciphertext
32-byte shared secret
│ │
└──────────┐ ┌────────────────┘
▼ ▼
HKDF-SHA-256 derivation
salt = classical key
ikm = quantum secret
info = "nullroom-hybrid-v1"
│
▼
Hybrid Session Key (AES-GCM-256)
══════════════════════════════════
Used for all message encryption
Neither key alone is sufficient. An attacker would need to compromise both the URL fragment and break the lattice-based ML-KEM scheme — which is designed to resist quantum computers (NIST Security Level 3).
The post-quantum upgrade follows a 3-message protocol over the WebRTC data channel with mutual HMAC-SHA-256 confirmation using role-specific labels (nullroom-pq-confirm-initiator / nullroom-pq-confirm-responder), preventing reflection and man-in-the-middle attacks.
Zero-Knowledge Signaling
The server is a dumb relay. It never possesses encryption keys and cannot decrypt any messages:
- The encryption key is generated client-side via the Web Crypto API and placed in the URL fragment (
#), which browsers never send to servers. - ActionCable relays WebRTC signaling payloads (SDP offers/answers, ICE candidates) without parsing or storing them.
- The quantum key exchange happens directly over the peer-to-peer data channel — the server is not involved.
- Production logging is disabled (
Logger.new(nil)+:fatallog level). Theno-referrermeta tag prevents URL leakage to third parties.
Ephemeral State
No database writes for room or message data. The only server-side state is volatile Redis:
| Key | TTL | Purpose |
|---|---|---|
room:<uuid> |
15 min | Room existence marker |
room:<uuid>:count |
16 min | Participant counter (max 2) |
Both keys auto-expire. When a peer disconnects, the other peer's UI is immediately scrubbed and the room can optionally be destroyed in Redis on the spot.
Verifiability
nullroom is built deterministically. Every push to main produces a cryptographically signed attestation via GitHub Actions — proving the code you receive matches the code in this repository.
In-browser audit: Look for the small dot in the bottom-left corner of any page. Click it to see how many loaded modules match the build manifest, along with a direct link to the GitHub Attestation for the running commit. This check uses zero network calls — it compares data already in your browser.
Independent verification (CLI):
gh run download --repo nullroomio/nullroom.io -n asset-checksums
gh attestation verify asset-checksums/checksums.sha256 --repo nullroomio/nullroom.io
For the full technical explanation — why the build is deterministic, how to reproduce it locally, and what's attested — see VERIFICATION.md.
How It Works
1. Create a room
Click "Create Secure Room" → browser sends POST /rooms → server creates a UUID in Redis and returns TURN credentials → browser generates an AES-GCM-256 key locally → redirects to /rooms/<uuid>#<key>.
The server knows the room exists. It never knows the key.
2. Invite your peer
Three ways to share the room — pick the one that fits your threat model:
| Method | How | Security |
|---|---|---|
| Direct Link | Copy the full URL (with #key) and send via a trusted channel |
Key travels with the link |
| QR Code | Peer scans the on-screen QR code in person | Air-gapped — key never touches the network |
| 4-Word Phrase | Read a phrase like neon-zebra-piano-rocket over a voice call |
PBKDF2-derived AES key (100K iterations) encrypts the room URL; SHA-256 of the phrase serves as a one-time Redis lookup key; blob auto-deletes after retrieval or 3 minutes |
3. Connect and upgrade
Once both peers are in the room:
- WebRTC signaling — SDP offer/answer and ICE candidates relay through ActionCable
- Data channel opens — direct peer-to-peer connection established
- Post-quantum upgrade — ML-KEM-768 key exchange runs automatically over the data channel
- Hybrid key derived — classical URL key + quantum secret fused via HKDF-SHA-256
- Mutual confirmation — both peers verify role-specific HMACs before activating the hybrid key
Status transitions: Signaling… → Upgrading… → Secure P2P
4. Chat and transfer files
- Every message is encrypted with the hybrid key (AES-GCM-256, random 12-byte IV per message) before leaving the browser.
- File transfers run over a dedicated
nullroom-filesdata channel with per-chunk encryption (64 KB chunks, backpressure control at 16 MB buffer). Size limits are connection-aware: 1 GB for direct P2P, 100 MB when relayed through TURN. The authorization gate sees only the file size — never the filename, connection type, or content. - Messages and files travel directly between browsers — the server is not in the data path.
5. Room termination
When either peer closes the tab:
- The remaining peer's message history is immediately scrubbed from the DOM.
- Input is disabled and a termination modal is shown.
- The Redis room key auto-expires (or is deleted immediately if
DESTROY_ROOM_ON_PEER_LEAVEis enabled). - Refreshing the URL shows "Invalid room link." There is nothing to recover.
Features
| Feature | Details |
|---|---|
| E2EE Messaging | AES-GCM-256 via Web Crypto API, per-message random IV |
| Post-Quantum Key Exchange | ML-KEM-768 (NIST FIPS 203, Security Level 3) with hybrid HKDF derivation |
| P2P File Transfer | Chunked, per-chunk encrypted, dedicated data channel; connection-aware limits (1 GB direct / 100 MB relay) |
| 4-Word Handshake | BIP-39 wordlist, PBKDF2 (100K iterations), one-time encrypted Redis blob (180s TTL) |
| QR Code Sharing | In-person room joining without transmitting the key over the network |
| Anonymous Donations | Monero (XMR) payments with RSA blind signatures — payment identity is cryptographically decoupled from chat sessions |
| Zero-Trace Termination | Peer disconnect triggers immediate UI scrub + optional Redis room destruction |
| NAT Traversal | Self-hosted TURN relay (coturn) with short-lived HMAC credentials over TLS |
| 2-Person Room Lock | Server-enforced capacity — third connection attempts are rejected |
| No-Log Production | Logger disabled, no-referrer policy, filtered parameters |
| CSP Hardened | Per-request nonces, frame-ancestors: none, no unsafe-inline |
Tech Stack
| Layer | Technology |
|---|---|
| Backend | Ruby on Rails 8, ActionCable, Redis |
| Frontend | Stimulus, Turbo, Importmap, Tailwind CSS |
| Transport | WebRTC (RTCPeerConnection + DataChannel) |
| Classical Crypto | Web Crypto API — AES-GCM-256, HKDF-SHA-256, HMAC-SHA-256, PBKDF2 |
| Post-Quantum Crypto | ML-KEM-768 (NIST FIPS 203) |
| NAT Traversal | Self-hosted coturn (STUN + TURNS over TLS) |
| Payments | Monero RPC + RSA blind signatures |
| Database | SQLite (app metadata only — no chat data) |
Configuration
All settings live in config/initializers/nullroom.rb and can be overridden via environment variables:
| Variable | Default | Description |
|---|---|---|
NULLROOM_ROOM_TTL_SECONDS |
900 (15 min) |
Room lifetime before auto-expiry |
NULLROOM_FILE_TRANSFER_SIZE_LIMIT_RELAY_BYTES |
104857600 (100 MB) |
Max relayed (TURN) file transfer size |
NULLROOM_FILE_TRANSFER_SIZE_LIMIT_DIRECT_BYTES |
1073741824 (1 GB) |
Max direct-P2P file transfer size |
NULLROOM_DONATION_AMOUNT_PICONERO |
4000000000 (0.004 XMR) |
Donation amount for blind token |
NULLROOM_DONATION_CONFIRMATIONS_REQUIRED |
10 |
Monero confirmations before signing |
NULLROOM_DONATION_PAYMENT_SESSION_TTL_SECONDS |
1800 (30 min) |
Payment session lifetime |
NULLROOM_DONATION_SPENT_TOKEN_TTL_SECONDS |
86400 (24 hr) |
Spent token replay prevention window |
NULLROOM_DONATION_SESSION_UNLOCK_TTL_SECONDS |
43200 (12 hr) |
Browser session unlock duration |
Room lifecycle
By default, rooms remain active until their TTL expires, allowing peers to reconnect via browser history. To destroy rooms immediately when any peer leaves:
# config/initializers/nullroom.rb
DESTROY_ROOM_ON_PEER_LEAVE = true
Routes
| Method | Path | Description |
|---|---|---|
GET |
/ |
Landing page |
POST |
/rooms |
Create room (returns JSON) |
GET |
/rooms/:id |
Room UI |
GET |
/privacy |
Privacy policy & FAQ |
GET |
/up |
Health check (app + Redis) |
/cable |
ActionCable WebSocket |
Contributing
See CONTRIBUTING.md.
Roadmap
See ROADMAP.md for planned improvements.
Security
Please read our security policy and disclosure process in SECURITY.md.
License
関連プロジェクトを探す
trimstray/the-book-of-secret-knowledge
インスピレーションを与えるリスト、マニュアル、チートシート、ブログ、ハック、ワンライナー、CLI/Web ツールなどを集めたコレクション。
258,008 スターHack-with-Github/Awesome-Hacking
ハッカー、ペンテスター、セキュリティ研究者向けのさまざまな Awesome リスト集。
125,360 スターswisskyrepo/PayloadsAllTheThings
Web アプリケーションセキュリティおよびペネトレーションテスト/CTF 向けの、実用的なペイロードとバイパスの一覧。
82,623 スターcaddyserver/caddy
高速で拡張しやすい、マルチプラットフォーム対応の HTTP/1-2-3 Web サーバー。自動 HTTPS に対応
79,020 スター