2026-05-05 01:09:10 +08:00
2026-05-05 00:02:23 +08:00
2026-05-05 01:09:10 +08:00
2026-04-30 07:00:46 +08:00
2026-04-25 16:39:21 +08:00
2026-04-29 04:06:50 +08:00
2026-04-25 01:23:16 +08:00
2026-05-05 00:13:16 +08:00
2026-05-05 00:13:16 +08:00
2026-05-05 00:13:16 +08:00
2026-05-05 00:13:16 +08:00
2026-05-05 00:13:16 +08:00
2026-05-05 00:13:16 +08:00
2026-05-05 00:13:16 +08:00
2026-05-05 00:13:16 +08:00
2026-05-05 00:13:16 +08:00

🌐 Other Languages: 中文 · 日本語 · 한국어 · Français · Deutsch · Русский · Español

A WeChat-style end-to-end encrypted instant messaging app with stateless ECDH + XSalsa20-Poly1305 per-message encryption, real-time video calls, Cloudflare R2 file storage, multi-language support and iOS PWA deployment.

Rust React TypeScript MySQL Redis WebRTC

Deploy on Zeabur


📸 Screenshots (click to expand) ui1 ui2 ui3 ui4 ui5 ui6 ui7 ui8 ui9 ui10 ui11 ui12 ui13 ui14 ui15 ui16 ui17 ui18

Features

Feature Description
🔐 End-to-End Encryption Stateless ECDH + XSalsa20-Poly1305 — ephemeral keys per message, forward secrecy, Signal-style safety number verification
🗝️ Zero-Knowledge Server Server stores only ciphertext; private keys never leave the device
📹 Video & Voice Calls WebRTC P2P (1:1) + Mesh (group), Cloudflare TURN for NAT traversal
🎙️ Voice Changer Real-time voice effects for voice messages, 1:1 calls, and group calls — 3 modes (0.8x deep / 1.0x normal / 1.2x high-pitched), powered by Web Audio API
👥 Group Chat Up to 2000 members, plain-text messages (no encryption), Do Not Disturb mode, member management
👫 Friend System Friend requests require approval with up to 512-char message; custom nicknames; multi-tag grouping
⏱️ Auto-Delete Messages 5 tiers (never / 1 day / 3 days / 1 week / 1 month), settable by either party in DMs, owner-only in groups
🔔 Push Notifications Web Push (VAPID) + FCM + OneSignal + ntfy quad-channel — reach users even when offline (Chinese Android without Google Services supported)
🌐 Multi-Language Chinese, English, Japanese, Korean, French, German, Russian, Spanish — auto-detect + manual switch
📱 iOS — No Enterprise Cert PWA via Safari "Add to Home Screen", works permanently without Apple signing
💬 Rich Messaging Text, images, video, document files, voice messages, 200+ emoji, Telegram sticker packs, delivery receipts, typing indicators
📤 File Upload Up to 500MB per file, Cloudflare R2 or local storage, with progress animation
🌐 Moments WeChat-style social feed: text + up to 9 photos or 1 video (≤ 10 min), likes, comments, tag-based visibility
👤 User Profile Contact profile page with bidirectional Moments privacy controls
📰 Timeline Xiaohongshu-style public feed — dual-column masonry layout, anonymous posting, likes & comments
🏷️ Friend Tags Assign multiple tags to friends (12-color palette), filter contacts by tag
🗂️ R2 Object Storage Cloudflare R2 for image/voice files — optional public CDN URL
🔑 Two-Factor Auth (2FA) Google Authenticatorcompatible TOTP, 8 recovery codes, enforced at login
📷 QR Code Scan & Share Scan QR codes to add friends or join groups with configurable expiry
🏗️ Self-Hostable Docker Compose, Zeabur one-click, or frontend on Vercel
🌐 Proxy Settings SOCKS5 / HTTP / HTTPS proxy support — configurable on both login and settings pages with server address, port, username and password for restricted network environments

Tech Stack

Backend (server/)
  Rust (Axum 0.8) — High-performance async web framework
  sqlx + MySQL 8.0 — User/message persistence
  deadpool-redis + Redis 7 — Online presence + cross-node routing
  aws-sdk-s3 — Cloudflare R2 file storage (S3-compatible API)
  argon2 + jsonwebtoken authentication

Frontend (client/)
  React 19 + TypeScript + Vite 6
  Zustand state management
  libsodium-wrappers-sumo (WebAssembly — Curve25519 / XSalsa20-Poly1305)
  WebRTC API — video / voice calls
  Web Audio API — real-time voice changer (ScriptProcessorNode audio chain)
  PWA: manifest.json + Service Worker

Cryptographic Layer
  Stateless ECDH + XSalsa20-Poly1305 — ephemeral keypair per message
  Four-tier key persistence: memory → localStorage → sessionStorage → IndexedDB
  All private keys stored on-device only — never sent to the server

Option 0: Zeabur One-Click Cloud Deploy

Deploy on Zeabur

Note

One manual step is required after deploy:

  1. Go to Zeabur Console → server service → Environment Variables → copy ZEABUR_WEB_URL
  2. Go to client service → Environment Variables → add VITE_API_URL = the value copied above
  3. Restart the client service

Tip

Advanced: Zeabur + Vercel Hybrid Deployment After deploying on Zeabur, you can manually delete the client service and deploy the frontend on Vercel instead (see Option 2 below). This way server/MySQL/Redis are hosted on Zeabur while the frontend is accelerated by Vercel's global CDN. Set VITE_API_URL in Vercel to the public domain of your Zeabur server service.

git clone <repo-url> && cd paperphone-plus
cp server/.env.example server/.env
# Edit: DB_PASS / JWT_SECRET / CF_CALLS_APP_ID etc.
docker compose up -d
open http://localhost

Option 2: Frontend on Vercel

# 1. Fork this repo
# 2. Import in Vercel: Root Directory = client/, Build = npm run build, Output = dist/
# 3. Set env var: VITE_API_URL = https://your-server-domain.com
# 4. Deploy backend via Docker or Zeabur

Option 3: Local Development

# Backend (Rust)
cd server && cp .env.example .env && cargo run --release

# Frontend (React)
cd client && npm install && npm run dev

Voice Changer

Voice messages, 1:1 calls, and group calls all support real-time voice changing with 3 selectable modes:

Mode Speed Effect
🐢 Slow 0.8x Deeper, lower-pitched voice — ideal for anonymity
🔊 Normal 1.0x Original voice, no processing
🐇 Fast 1.2x Higher-pitched voice — fun and playful

How it works: Uses the Web Audio API to build an audio processing chain (AudioContext → MediaStreamSource → ScriptProcessorNode → MediaStreamDestination) that adjusts pitch/speed of the microphone input in real-time.

  • Voice messages: Select voice mode during recording. The exported .webm file already contains the voice effect — recipients cannot restore the original voice, enabling true anonymous messaging
  • 1:1 / Group calls: Tap the voice changer button during a call to cycle through modes. The processed audio track replaces the original via RTCRtpSender.replaceTrack()

No server-side configuration is required. The voice changer runs entirely on the client side.


Environment Variables

Variable Description Default
PORT Server port 3000
JWT_SECRET JWT signing key (change in production) dev_secret
DB_HOST / DB_PASS / DB_NAME MySQL connection
REDIS_HOST / REDIS_PASS Redis connection
R2_ACCOUNT_ID Cloudflare account ID
R2_ACCESS_KEY_ID R2 API token access key
R2_SECRET_ACCESS_KEY R2 API token secret key
R2_BUCKET R2 bucket name
R2_PUBLIC_URL R2 public base URL (optional)
CF_CALLS_APP_ID Cloudflare Calls App ID (optional)
CF_CALLS_APP_SECRET Cloudflare Calls App Secret (optional)
METERED_TURN_API_KEY Metered.ca TURN API Key (optional, free alternative)
VAPID_PUBLIC_KEY Web Push VAPID public key (optional)
VAPID_PRIVATE_KEY Web Push VAPID private key (optional)
VAPID_SUBJECT VAPID contact email (optional) mailto:admin@paperphone.app
FCM_PROJECT_ID Firebase project ID (optional, Capacitor Android)
FCM_CLIENT_EMAIL Firebase service account email (optional)
FCM_PRIVATE_KEY Firebase service account private key (optional, supports both \n escape and real newlines; see below)
ONESIGNAL_APP_ID OneSignal App ID (optional)
ONESIGNAL_REST_KEY OneSignal REST API Key (optional)
NTFY_BASE_URL ntfy server URL (optional, uses public ntfy.sh by default) https://ntfy.sh
NTFY_TOKEN ntfy auth token (optional, for self-hosted servers)
TELEGRAM_BOT_TOKEN Telegram Bot Token (optional)
STICKER_PACKS Custom sticker packs (optional, name:label) 8 built-in defaults

FCM Private Key Newline Handling

The private_key field in Firebase service account JSON contains an RSA private key in PEM format, which requires real newline characters (\n, ASCII 0x0A) between each 64-character line. However, many deployment platforms (Zeabur, Vercel, Railway, Docker) store environment variables as single-line strings, converting \n into the literal two-character sequence \ + n.

This is the most common cause of FCM push notification failure — the PEM parser silently fails and no push notifications are sent, with no error logs.

The server handles this automatically: fcm.rs normalizes literal \n sequences back to real newlines before parsing. Both formats work:

  • Single-line (recommended for cloud platforms): Paste the raw private_key value from the JSON file as-is, with \n escapes:

    FCM_PRIVATE_KEY=-----BEGIN PRIVATE KEY-----\nMIIEvQ...\n-----END PRIVATE KEY-----\n
    
  • Multi-line (for .env files): Wrap the full PEM content in quotes with real newlines:

    FCM_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----
    MIIEvQ...
    -----END PRIVATE KEY-----"
    
Platform Recommended Format Notes
Zeabur Single-line (\n escaped) Paste JSON value directly in Variables panel
Docker / docker-compose Either Use YAML | for multi-line; single-line in .env
Vercel / Railway Single-line (\n escaped) Input fields typically don't support real newlines
Linux .env file Multi-line (quoted) Ensure quotes are properly closed

Troubleshooting: If FCM variables are set but Android push isn't working, check server logs:

  • [FCM] No access token available → Private key format error (newline issue)
  • [FCM] ✅ Push sent to user xxx → FCM sending works, issue is client-side
  • No FCM logs at all → FCM_PROJECT_ID not set or no token in fcm_tokens table

ntfy Push (Chinese Android Devices without Google Services)

For Android devices without Google Mobile Services (Huawei, Xiaomi, OPPO, vivo, etc.), PaperPhone supports push notifications via ntfy.

Default setup (zero configuration): Uses the public ntfy.sh service. No additional configuration needed.

Optional configuration (for self-hosted ntfy servers):

NTFY_BASE_URL=https://your-ntfy-server.com
NTFY_TOKEN=your_optional_auth_token

User setup flow:

  1. Install the ntfy app (Google Play / F-Droid / Direct Download)
  2. Open PaperPhone Settings and find the "ntfy Push" card
  3. Copy the displayed topic name and subscribe to it in the ntfy app
  4. Tap "Register Push" to complete registration

Security note: ntfy notifications contain notification titles and summaries in plaintext (not the actual message content). For higher security, consider self-hosting an ntfy server.


License

MIT © PaperPhone Contributors

Description
仿微信ui的端对端加密+前向加密+抗量子加密的即时通讯软件,PaperPhone原版基础上使用react和rust重构的版本。前端可以部署在Vercel
Readme 2.6 MiB
Languages
TypeScript 67.9%
Rust 23.2%
CSS 7.1%
Shell 0.8%
Dockerfile 0.4%
Other 0.6%