# PaperPhone IM 🌐 **Otros idiomas / Other Languages:** [中文](README.md) · [English](README_EN.md) · [日本語](README_JA.md) · [한국어](README_KO.md) · [Français](README_FR.md) · [Deutsch](README_DE.md) · [Русский](README_RU.md) Una aplicación de mensajería instantánea cifrada de extremo a extremo estilo WeChat, con cifrado ECDH sin estado + XSalsa20-Poly1305 por mensaje, videollamadas en tiempo real, almacenamiento Cloudflare R2, soporte multilingüe y despliegue iOS PWA. [![Node.js](https://img.shields.io/badge/Node.js-20+-green)](#) [![MySQL](https://img.shields.io/badge/MySQL-8.0-blue)](#) [![Redis](https://img.shields.io/badge/Redis-7.x-red)](#) [![WebRTC](https://img.shields.io/badge/WebRTC-P2P%20%2B%20Mesh-orange)](#) [![Deploy on Zeabur](https://zeabur.com/button.svg)](https://zeabur.com/templates/P2J7Y3?referralCode=619dev) --- ui ## Características | Función | Descripción | |---------|-------------| | 🔐 Cifrado E2E | ECDH sin estado + XSalsa20-Poly1305 — claves efímeras por mensaje, secreto hacia adelante | | 🗝️ Servidor de conocimiento cero | El servidor solo almacena texto cifrado; las claves privadas nunca salen del dispositivo | | 📹 Videollamadas/voz | WebRTC P2P (1:1) + Mesh (grupo), Cloudflare TURN para atravesar NAT | | 👥 Chat grupal | Hasta 2000 miembros, mensajes en texto plano, modo No molestar, gestión de miembros | | ⏱️ Eliminación automática | 5 niveles (nunca/1d/3d/1sem/1mes), en DMs ambas partes, en grupos solo propietario | | 🔔 Notificaciones push | Web Push (VAPID) + OneSignal canal doble | | 🌐 Multilingüe | ZH/EN/JA/KO/FR/DE/RU/ES — detección automática + selección manual | | 📱 iOS sin certificado empresarial | PWA vía Safari "Añadir a inicio", sin firma de Apple | | 💬 Mensajería rica | Texto, imágenes, mensajes de voz, 64 emojis, confirmaciones de lectura | | 🌐 Momentos | Feed social: texto + hasta 9 fotos, likes (avatares de amigos), comentarios, visibilidad por etiquetas | | 🏷️ Etiquetas de amigos | Múltiples etiquetas por amigo (paleta de 12 colores), filtrar contactos por etiqueta | | 🗂️ Almacenamiento R2 | Cloudflare R2 para imágenes/audio — URL CDN opcional | | 🏗️ Auto-alojable | Despliegue Docker Compose en un comando | --- ## Stack tecnológico ``` Backend (server/) Node.js 20 + Express + ws MySQL 8.0 — usuarios, mensajes (texto cifrado) Redis — presencia en línea + enrutamiento entre nodos Cloudflare R2 — almacenamiento de archivos (API compatible S3) Autenticación JWT + bcrypt Frontend (client/) HTML nativo + Vanilla JS (ESM, sin bundler) libsodium-wrappers (WebAssembly — Curve25519 / XSalsa20-Poly1305) API WebRTC — videollamadas / llamadas de voz PWA: manifest.json + Service Worker Capa criptográfica ECDH sin estado + XSalsa20-Poly1305 — par de claves efímeras por mensaje Persistencia de claves en 4 niveles: memoria → localStorage → sessionStorage → IndexedDB Todas las claves privadas solo en el dispositivo — nunca se envían al servidor ``` --- ## Inicio rápido ### Opción 0: Zeabur — Despliegue en la nube con un clic [![Deploy on Zeabur](https://zeabur.com/button.svg)](https://zeabur.com/templates/P2J7Y3?referralCode=619dev) > [!NOTE] > Se requiere un paso manual después del despliegue: > 1. Consola Zeabur → servicio **server** → Variables de entorno → copiar el valor de `ZEABUR_WEB_URL` > 2. Servicio **client** → Variables de entorno → agregar `SERVER_URL` = valor copiado > 3. Reiniciar el servicio client ### Opción 1: Docker Compose (recomendado) ```bash git clone && cd paperphone cp server/.env.example server/.env # Editar: DB_PASS / JWT_SECRET / R2_* etc. docker compose up -d open http://localhost ``` > Imágenes Docker Hub: `facilisvelox/paperphone-client:latest` y `facilisvelox/paperphone-server:latest` ### Opción 2: Inicio local manual ```bash # Backend cd server && npm install && npm run dev # → http://localhost:3000 # Frontend npx serve client -p 8080 # → http://localhost:8080 ``` --- ## Configuración de videollamadas — Cloudflare TURN ```env CF_CALLS_APP_ID=your_app_id_here CF_CALLS_APP_SECRET=your_app_secret_here ``` | Tipo | Transporte | Recomendado para | |------|-----------|------------------| | Video 1:1 | WebRTC P2P + TURN | Todos los escenarios | | Voz 1:1 | WebRTC P2P + TURN | Todos los escenarios | | Llamada grupal | WebRTC Mesh | Hasta 6 participantes | > **Sin configuración**: solo STUN. Las llamadas en LAN funcionan sin configuración adicional. --- ## Notificaciones push | Canal | Plataformas | Configuración | |-------|-------------|---------------| | Web Push | Navegadores + iOS PWA (Safari 16.4+) | Claves VAPID | | OneSignal | Apps nativas Median.co | App ID + REST Key | ```bash npx web-push generate-vapid-keys ``` --- ## iOS — Instalación permanente sin certificado 1. Desplegar en servidor HTTPS → 2. Abrir en Safari → 3. Compartir ⬆️ → 4. «Añadir a pantalla de inicio» --- ## Modelo de seguridad ``` Registro: IK + SPK + 10×OPK generados localmente, claves públicas subidas Mensaje: ECDH efímero → X25519 → XSalsa20-Poly1305 El servidor ve: ✅ texto cifrado + metadatos de enrutamiento ❌ texto plano / claves privadas ``` --- ## Esquema de base de datos 11 tablas, creadas automáticamente en el primer inicio: | Tabla | Propósito | |-------|-----------| | `users` | Perfiles + claves públicas ECDH/OPK | | `prekeys` | Pre-claves X3DH de un solo uso | | `friends` | Relaciones de amistad | | `groups` / `group_members` | Grupos + miembros | | `messages` | Mensajes cifrados | | `moments` / `moment_images` | Publicaciones + imágenes | | `moment_likes` / `moment_comments` | Likes + comentarios | | `push_subscriptions` | Web Push (VAPID) | | `onesignal_players` | Dispositivos OneSignal | --- ## Variables de entorno | Variable | Descripción | Predeterminado | |----------|-------------|----------------| | `PORT` | Puerto del servidor | `3000` | | `JWT_SECRET` | Clave de firma JWT (**cambiar en producción**) | dev_secret | | `DB_HOST`/`DB_PASS`/`DB_NAME` | Conexión MySQL | — | | `REDIS_HOST`/`REDIS_PASS` | Conexión Redis | — | | `R2_ACCOUNT_ID` | ID de cuenta Cloudflare | — | | `R2_ACCESS_KEY_ID` | Clave de acceso R2 API | — | | `R2_SECRET_ACCESS_KEY` | Clave secreta R2 API | — | | `R2_BUCKET` | Nombre del bucket R2 | — | | `R2_PUBLIC_URL` | URL pública R2 (opc.) | — | | `CF_CALLS_APP_ID` | Calls App ID (opc.) | — | | `CF_CALLS_APP_SECRET` | Calls Secret (opc.) | — | | `VAPID_PUBLIC_KEY` | Clave pública VAPID (opc.) | — | | `VAPID_PRIVATE_KEY` | Clave privada VAPID (opc.) | — | | `ONESIGNAL_APP_ID` | OneSignal App ID (opc.) | — | | `ONESIGNAL_REST_KEY` | OneSignal REST Key (opc.) | — | --- ## Licencia MIT © PaperPhone Contributors