Self-hostable E2EE forward-secret chat application hosted via CloudFlare built to democratize access to E2EE communication without accounts or installing apps.
  • Rust 72.7%
  • TypeScript 24.6%
  • HTML 1.5%
  • CSS 0.8%
  • JavaScript 0.4%
Find a file
AWildBeard bf7cb1253e Add voice calls, /dcc file transfer, native tabs, and settings
Builds Phase 3 (voice calls) and Phase 4 (peer-to-peer file transfer) on
the initial codebase, plus a native multi-tab shell, a Settings/Help
surface, and the relay-side work all of it needs.

Voice calls (Phase 3): a relay-promoted call-leader mirrors the room
leader, with call membership riding a `call` flag on the room roster (no
separate call-roster kind). Adds the call-* wire kinds, the sans-IO
protocol layer (engine/src/call.rs `plan_call_mode` + engine.rs), and the
browser P2P audio-mesh transport (ui/src/call.rs, call_bar.rs): DTLS,
forward-secret, DM-encrypted SDP/ICE, per-peer WebAudio volume, Opus-only
<=16kbps. Leader-validated admission (S2) gates joiners on a WebRTC probe.
SFU (Cloudflare Realtime) is wired through the protocol but stubbed.

/dcc file transfer (Phase 4): IRC-style P2P send over a WebRTC
DataChannel, streamed and inline in the chat log. Adds the dcc-* wire
kinds, the pure engine layer (engine/src/dcc.rs — mime whitelist,
magic-byte sniff, batch planning), and the transport + inline render
(ui/src/dcc.rs, dcc_panel.rs). Offer/accept/reject bodies and all signaling
are 1-1 DM-encrypted (relay-blind: only routing metadata is plaintext).
Media within the size limit auto-downloads; everything else prompts.
Send to a user or multicast to the room via one fanned-out dcc-offers map.

Native tabs: up to 9 numbered room tabs plus a pinned menu tab, each
running its own irc-engine::Engine in the worker, multiplexed by a tab_id
envelope (ui/src/tabmsg.rs) at the host boundary only — the engine stays
tab-agnostic. Open tabs persist and reconnect on refresh.

Settings + Help: replaces key_manager.rs with components/settings.rs
(General/Calls/Keys subtabs — connection pref, DCC auto-download, message
font size, contacts, key import/export) and adds components/help.rs
(Overview/Usage/FAQ for non-technical readers).

meta join ack: every join now gets a per-client `meta` frame carrying
room facts (KV-authoritative public, colo, count, sfu capability) that
drive the public/private tab glyph, plus per-client ray/eyeball debug
facts.

Relay: liveness sweep is now write-failure-only reaping (an
unanswered-but-writable probe is never reaped — the previous guess flapped
healthy clients); the public-room directory is KV-authoritative with room
colo and operator "stapled" (pinned) entries; call leadership/keying and
failure-aware DCC routing are added. Updates Specification.md, the docs/
set, and CLAUDE.md; extends the worker test suites.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 07:43:34 -06:00
docs Add voice calls, /dcc file transfer, native tabs, and settings 2026-07-12 07:43:34 -06:00
engine Add voice calls, /dcc file transfer, native tabs, and settings 2026-07-12 07:43:34 -06:00
ui Add voice calls, /dcc file transfer, native tabs, and settings 2026-07-12 07:43:34 -06:00
web Add voice calls, /dcc file transfer, native tabs, and settings 2026-07-12 07:43:34 -06:00
worker Add voice calls, /dcc file transfer, native tabs, and settings 2026-07-12 07:43:34 -06:00
.gitignore Initial commit: ++IRC web client, engine, worker, and docs 2026-06-16 18:55:02 -05:00
Cargo.lock Initial commit: ++IRC web client, engine, worker, and docs 2026-06-16 18:55:02 -05:00
Cargo.toml Initial commit: ++IRC web client, engine, worker, and docs 2026-06-16 18:55:02 -05:00
CLAUDE.md Add voice calls, /dcc file transfer, native tabs, and settings 2026-07-12 07:43:34 -06:00
README.md Initial commit: ++IRC web client, engine, worker, and docs 2026-06-16 18:55:02 -05:00
Specification.md Add voice calls, /dcc file transfer, native tabs, and settings 2026-07-12 07:43:34 -06:00
wrangler.toml Initial commit: ++IRC web client, engine, worker, and docs 2026-06-16 18:55:02 -05:00

++IRC

End-to-end encrypted group chat in the style of IRC.

Clients are Rust/Dioxus apps — web (PWA), desktop, and mobile — sharing one UI crate and one sans-IO protocol engine. Rooms run as Cloudflare Durable Objects. All encryption is client-side using pure-Rust RustCrypto crates (ED25519 identity keys, ephemeral X25519 ECDHE session keys for forward secrecy, ChaCha20-Poly1305, BLAKE3).


Repository layout

++IRC/
├── engine/         irc-engine  — sans-IO protocol core: crypto, keying/leadership,
│                                 connection supervisor, UI⇄engine protocol
├── ui/             irc-ui      — ALL shared Dioxus components, display state,
│                                 platform façade, theming assets (tailwind.css,
│                                 Twemoji) + the npm tooling that generates them
├── web/            irc-web     — browser app (PWA; engine in a Web Worker)
├── desktop/        irc-desktop — macOS/Windows/Linux app (engine on a tokio thread)
├── mobile/         irc-mobile  — Android + iOS app (same native engine host)
├── worker/         Cloudflare Worker + Durable Object Relay (TypeScript)
├── wrangler.toml   Cloudflare deployment configuration (root-level)
└── Cargo.toml      Rust workspace

A single Cloudflare Worker serves both halves of the web app in production: the static WASM frontend (via Cloudflare Static Assets) and the realtime backend (Durable Object rooms + the KV public-room directory). One deploy, one origin. The desktop and mobile apps talk to the same backend (set IRCPP_RELAY).


Prerequisites

Tool Purpose Install
Rust stable Client compilation rustup update stable
wasm32-unknown-unknown target Web build rustup target add wasm32-unknown-unknown
dx (Dioxus CLI) 0.7+ Dev server & builds, all platforms cargo install dioxus-cli (if another tool's dx shadows it, call ~/.cargo/bin/dx)
Node.js 18+ Worker tooling + CSS/emoji generation nodejs.org
wrangler 3.78+ Cloudflare deploy / local dev npm install in worker/

Mobile additionally needs the Android SDK/NDK or Xcode — see mobile/README.md.


Local development (web)

Two processes run side-by-side: the Worker on :8787 and the Dioxus dev server on :8080. They are different origins in dev, so the client hard-codes ws://localhost:8787 / http://localhost:8787 for local connections (see ui/src/platform/web.rs) and the Worker sends Access-Control-Allow-Origin: * on /rooms. In production everything is one origin, so neither special case applies.

1 — Start the backend

cd worker
npm install          # first time only
npx wrangler dev     # Worker + DO emulation + KV on :8787

2 — Start the web app

cd web
dx serve             # hot-reload dev server on :8080

Open http://localhost:8080. To test room sharing, open a second tab at http://localhost:8080/#my-room.

Desktop / mobile dev

cd desktop && dx serve --platform desktop          # IRCPP_RELAY to pick a relay
cd mobile  && dx serve --platform android          # or --platform ios

Details and platform prerequisites: desktop/README.md, mobile/README.md.


Production deployment (one Worker serves everything)

In production the Worker serves the WASM frontend and the WebSocket rooms from the same origin. Because of that, no frontend URL needs to change: the client derives its endpoints from window.location, so when the page is served from https://chat.example.com, room sockets go to wss://chat.example.com/<room> and the directory to https://chat.example.com/rooms automatically. The localhost:8787 hard-coding only triggers when the page host is localhost/127.0.0.1.

Quick version:

# 1. Build the web bundle (output: target/dx/irc-web/release/web/public).
#    No npm step needed: the generated assets (tailwind.css, emojis.json,
#    twemoji/) are committed in ui/assets. (Only run `npm run build:css` /
#    `npm run build:emoji` — both from ui/ — if you changed styles or the
#    emoji set; see ui/README.md.)
cd web
dx build --release
cd ..

# 2. One-time: authenticate + create the KV namespace, paste ids into wrangler.toml
cd worker && npx wrangler login
npx wrangler kv namespace create ROOMS            # paste id        → wrangler.toml
npx wrangler kv namespace create ROOMS --preview  # paste preview_id → wrangler.toml
cd ..

# 3. Deploy from the repo root (where wrangler.toml lives)
npx wrangler deploy

The full, annotated walkthrough — including the asset/Worker routing model and the config pitfalls to avoid — is in worker/README.md. Web build details are in web/README.md; shared UI and theming in ui/README.md.

Desktop and mobile releases are built with dx build --release --platform desktop|android|ios from their crate directories (dx bundle produces installers with the ++IRC icon); point them at your deployed relay with IRCPP_RELAY=wss://chat.example.com.

Production caveats (please read)

  • KV ids are placeholders. wrangler.toml ships with REPLACE_WITH_YOUR_KV_NAMESPACE_ID. Local wrangler dev emulates KV so the public-room directory works locally, but you must paste real ids before wrangler deploy or public rooms silently won't register.
  • Self-hosted assets, no CDNs. Styling (Tailwind, compiled to ui/assets/tailwind.css), icons (inline SVGs), and emoji (Twemoji SVGs in ui/assets/twemoji/ + the assets/emojis.json shortcode table) are fully self-hosted, so no third-party request leaks a visitor's IP/referrer. Every platform app bundles these same committed assets — one brand everywhere. Regenerate only when their inputs change — cd ui && npm run build:css after editing utility classes (a CI/production build should run it before dx build --release), npm run build:emoji only when changing the emoji set or upgrading Twemoji.

Running tests

# All native unit tests: the engine's protocol/crypto/supervisor/leadership
# suite + the shared UI's display logic (no WASM toolchain needed)
cargo test --workspace                 # 128 tests (76 engine + 52 ui)

# Worker: TypeScript type-check + test suites (mock harness, pure logic,
# and a real-workerd Durable Object integration suite via vitest-pool-workers)
cd worker
npx tsc --noEmit
npx vitest run                         # 100 tests