README_ES.md
August 15, 2026 · View on GitHub
🌐 Otros idiomas: 中文 · English · 日本語 · 한국어 · Français · Deutsch · Русский
Una aplicación de mensajería instantánea cifrada de extremo a extremo, estilo WeChat, con cifrado ECDH + XSalsa20-Poly1305 sin estado por mensaje, videollamadas en tiempo real, almacenamiento de archivos Cloudflare R2, soporte multilingüe y despliegue PWA para iOS.
📸 Capturas de pantalla (haga clic para expandir)
Características
| Característica | Descripción |
|---|---|
| 🔐 Cifrado de extremo a extremo | ECDH sin estado + XSalsa20-Poly1305 — claves efímeras por mensaje, forward secrecy, verificación de número de seguridad estilo Signal |
| 🗝️ Cifrado de conocimiento cero | Para las conversaciones cifradas, el servidor almacena texto cifrado, aunque sigue procesando los metadatos esenciales de cuenta, contactos/grupos, enrutamiento y notificaciones push. Las claves privadas de identidad y las Sender Keys permanecen locales: Web usa IndexedDB protegido con AES-GCM, mientras que Android, iOS, Windows y macOS usan el almacenamiento seguro del sistema operativo |
| 🎭 Aspecto del texto y cifrado adicional | En Perfil > Privacidad de los mensajes, establece una contraseña adicional para todos los chats de este dispositivo y muestra el contenido con uno de ocho aspectos; admite bloqueo manual y automático |
| 📹 Llamadas de vídeo y voz | SFU LiveKit para llamadas 1:1 y reuniones (hasta 100 participantes), silenciar a todos y modo clase |
| 🎙️ Modificador de voz | Efectos de voz en tiempo real para mensajes de voz, llamadas 1:1 y llamadas grupales — 3 modos (0.8x grave / 1.0x normal / 1.2x agudo), basado en Web Audio API |
| 📱 Persistencia de sesión | Tokens de acceso de 30 minutos con sesiones de actualización de dispositivo de 90 días; recupera cambios de red, IP, VPN o proxy sin pedir la contraseña |
| 📨 Sincronización fiable de mensajes | Heartbeat bidireccional, detección de conexiones semimuertas, bandeja de salida persistente, IDs idempotentes y recuperación por cursor de secuencia del servidor |
| 📴 Acceso sin conexión | Caché aislada por cuenta para contactos, grupos, hasta 2.000 mensajes por conversación, Momentos, Línea de tiempo y multimedia; los envíos sin conexión se reintentan automáticamente |
| 🔎 Búsqueda Unicode de amigos | La protección de composición IME, normalización NFC y codificación UTF-8 permiten buscar de forma fiable nombres de usuario y apodos chinos |
| 👥 Chat grupal | Hasta 2000 miembros, modos "Cifrado" / "Sin cifrar" conmutables (solo propietario, al cambiar se borra el historial del chat). El modo cifrado usa el protocolo Sender Key estilo Signal (cifrado simétrico XSalsa20-Poly1305 + distribución de claves ECDH) — solo los miembros del grupo pueden descifrar los mensajes; los bots están desactivados en modo cifrado. Modo No Molestar, gestión de miembros |
| 👫 Sistema de amigos | Las solicitudes de amistad requieren aprobación con hasta 512 caracteres de mensaje; apodos personalizados; agrupación por etiquetas |
| ⏱️ Eliminación automática de mensajes | 5 niveles (nunca / 1 día / 3 días / 1 semana / 1 mes), configurable por ambas partes en DMs, solo por el propietario en grupos |
| 🔔 Notificaciones push | Web Push (VAPID) + FCM + OneSignal + ntfy + APNS cinco canales — alcanza usuarios incluso sin conexión (iOS nativo + Android chino sin Google Services) |
| 🌐 Multilingüe | Chino, inglés, japonés, coreano, francés, alemán, ruso, español — detección automática + cambio manual |
| 📱 iOS — Sin certificado empresarial | PWA vía Safari «Agregar a pantalla de inicio», funciona permanentemente sin firma de Apple |
| 📱 App nativa Android | Disponible en Google Play, con soporte de notificaciones push FCM |
| 📱 App nativa iOS | Disponible en App Store, con soporte de notificaciones push APNS |
| 🖥️ Cliente de escritorio Windows | Aplicación de escritorio Windows nativa, descargar aquí |
| 🍎 Cliente de escritorio Mac | Aplicación de escritorio Mac nativa, descargar aquí |
| 💬 Mensajería enriquecida | Texto, imágenes, video, archivos de documentos, mensajes de voz, 200+ emojis, paquetes de stickers de Telegram, confirmaciones de lectura, indicadores de escritura |
| 📤 Subida de archivos | Hasta 500 MB por archivo, Cloudflare R2 o almacenamiento local, con animación de progreso |
| 🌐 Momentos | Feed social estilo WeChat: texto + hasta 9 fotos o 1 video (≤ 10 min), likes, comentarios, visibilidad por etiquetas |
| 👤 Perfil de usuario | Página de perfil de contacto con controles de privacidad bidireccionales de Momentos |
| 📰 Línea de tiempo | Feed público estilo Xiaohongshu — diseño masonry de dos columnas, publicaciones anónimas, likes y comentarios |
| 🏷️ Etiquetas de amigos | Asignar múltiples etiquetas a amigos (paleta de 12 colores), filtrar contactos por etiqueta |
| 🗂️ Almacenamiento de objetos R2 | Cloudflare R2 para archivos de imagen/voz — URL CDN pública opcional |
| 🔑 Autenticación de dos factores (2FA) | TOTP compatible con Google Authenticator, 8 códigos de recuperación, obligatorio al iniciar sesión |
| 📷 Escanear y compartir código QR | Escanear códigos QR para agregar amigos o unirse a grupos con expiración configurable |
| 🏗️ Auto-hospedable | Docker Compose, Zeabur con un clic, o frontend en Vercel |
| 🌐 Configuración de proxy | Soporte de proxy SOCKS5 / HTTP / HTTPS — configurable en páginas de inicio de sesión y ajustes con dirección del servidor, puerto, usuario y contraseña para entornos de red restringidos |
| 🛡️ Moderación de contenido | Reportes de usuarios (6 categorías) + bloqueo de usuarios (oculta instantáneamente publicaciones/mensajes) + Términos de uso (EULA) |
| 🔧 Panel de administración | Dashboard de administración web integrado (/admin, ruta configurable), protegido por contraseña, revisar reportes, eliminar contenido infractor, banear usuarios — 8 idiomas |
Novedades de v2.4.4
-
Se corrigió el diálogo de cifrado adicional bloqueado que pedía configurar una contraseña; ahora solicita la contraseña de desbloqueo en los ocho idiomas.
-
Se corrigió un problema de seguridad que permitía desactivar el cifrado adicional de apariencia de texto sin verificar la contraseña; ahora es obligatorio volver a introducir la contraseña adicional correcta incluso si está desbloqueado.
-
La apariencia de texto ahora oculta prefijos de protocolo, sal e IV; la caché local ya no conserva el texto original.
-
El cifrado adicional se trasladó a Perfil > Privacidad de los mensajes y se aplica globalmente a todos los chats.
-
Los chats cifrados ahora fallan de forma segura: los errores de cifrado, distribución de claves o almacenamiento seguro nunca provocan un envío en texto claro. Cada mensaje muestra el protocolo realmente utilizado (
PQ v2,X25519 ↓oSK vN). -
Se añadió una contraseña opcional para el historial y ocho códecs de presentación: texto budista, chino aleatorio, símbolos del I Ching, coreano, jeroglíficos egipcios, cuneiforme, texto de valores fundamentales y alfanumérico.
-
Sin la contraseña adicional correcta solo se muestra el texto cifrado de presentación; puede bloquearse automáticamente tras 5/15/30/60 minutos en segundo plano. La contraseña permanece únicamente en memoria.
-
Se reforzó la protección local de claves privadas y Sender Keys con IndexedDB envuelto en AES-GCM en Web y almacenamiento seguro del sistema en clientes nativos; se completó la interfaz en los ocho idiomas.
Novedades de v2.3.9
- Se corrigieron registros antiguos de amistad unidireccionales que mostraban «Ya son amigos» aunque el contacto seguía invisible y no se podía chatear; al volver a añadirlo ahora se reparan ambas direcciones y se actualiza inmediatamente la lista de contactos.
Novedades de v2.3.8
- Se corrigió el botón Atrás que no respondía después de iniciar la cámara del escáner QR; al cerrar, la cámara se detiene y libera inmediatamente.
- Se corrigieron las solicitudes duplicadas a amigos existentes que dañaban la relación; los resultados ahora muestran «Ya son amigos».
- Los mensajes privados salientes guardan el texto cifrado en el objeto optimista justo después del cifrado de extremo a extremo, evitando persistir texto plano mientras llega la confirmación del servidor.
- Los mensajes de voz se detienen automáticamente a los 120 segundos; el audio con cambio de voz usa el mismo límite.
- La pantalla permanece activa durante grabaciones y llamadas, y al salir se liberan de forma fiable dispositivos y temporizadores.
- Los clientes Android, iOS, Windows y macOS protegen las claves privadas de identidad y las Sender Keys locales con el almacenamiento seguro del sistema operativo; Web usa IndexedDB protegido con AES-GCM. La caché de chats y el almacenamiento de claves privadas son límites de seguridad distintos, lo que reemplaza la antigua descripción de «persistencia en cuatro capas».
Recuperación de sesión y fiabilidad de mensajes
PaperPhonePlus separa el estado local de la cuenta, la conexión en tiempo real y la sincronización de mensajes. Un WebSocket abierto no se considera utilizable hasta recibir auth_ok. Los heartbeats bidireccionales ping/pong detectan conexiones semimuertas por cambios de VPN/IP, transiciones Wi-Fi/móvil o suspensión de la aplicación.
- Los tokens de acceso duran 30 minutos. Los tokens de actualización del dispositivo duran 90 días y se prolongan durante el uso activo, permitiendo renovar la sesión sin contraseña.
- Los dispositivos conectados con una versión anterior se actualizan automáticamente mientras su token siga vigente. Si ya caducó, se requiere un último inicio de sesión manual.
- Cada mensaje saliente tiene una
client_msg_idestable. Los mensajes sin ACK permanecen en la bandeja de salida local persistente y se reintentan con el mismo ID; una restricción única evita duplicados. - Cada mensaje almacenado tiene una
server_seqcreciente. El cliente recupera por cursor los mensajes faltantes tras autenticarse, reconectarse o volver al primer plano. - El cierre de sesión explícito y la revocación del dispositivo invalidan la sesión duradera. Los fallos de transporte y cambios de IP normales no la eliminan.
Important
En una actualización, despliegue primero el servidor y después los clientes. El servidor aplica y verifica automáticamente la migración de fiabilidad y no arranca si faltan columnas críticas. Haga una copia de seguridad de MySQL antes de actualizar producción.
Stack tecnológico
Backend (server/)
Rust (Axum 0.8) — Framework web asíncrono de alto rendimiento
sqlx + MySQL 8.0 — Persistencia de usuarios/mensajes
deadpool-redis + Redis 7 — Presencia en línea + enrutamiento entre nodos
aws-sdk-s3 — Almacenamiento de archivos Cloudflare R2 (API compatible con S3)
Autenticación argon2 + jsonwebtoken
Frontend (client/)
React 19 + TypeScript + Vite 6
Gestión de estado Zustand
libsodium-wrappers-sumo (WebAssembly — Curve25519 / XSalsa20-Poly1305)
WebRTC API — videollamadas / llamadas de voz
Web Audio API — modificador de voz en tiempo real (cadena de audio ScriptProcessorNode)
PWA: manifest.json + Service Worker
Capa criptográfica
ECDH sin estado + XSalsa20-Poly1305 — par de claves efímero por mensaje
Protección local de claves: IndexedDB protegido con AES-GCM en Web; almacenamiento seguro del sistema en Android/iOS/Windows/macOS
Las claves privadas de identidad y las Sender Keys permanecen locales y nunca se envían al servidor
📖 Guía de despliegue detallada (中文) | Deployment Guide (English) — Instrucciones paso a paso completas para el despliegue híbrido Zeabur + Vercel, despliegue local con Docker Compose + Nginx, y configuración de la dirección del servidor del cliente.
Opción 0: Despliegue en la nube con Zeabur en un clic
Limitación de red de Zeabur: la plantilla despliega LiveKit mediante WebSocket/API 7880 e ICE/TCP 7881. Zeabur actualmente no expone puertos UDP, por lo que las llamadas 1:1 y las reuniones usan TCP como alternativa. Para llamadas de producción, use LiveKit Cloud o una VM compatible con UDP.
Configuración Nginx del servidor
Use la configuración de producción de dos dominios deploy/nginx/paperphone-plus.conf. Sustituya api.example.com y meeting.example.com, obtenga certificados TLS, copie el archivo a /etc/nginx/sites-available/paperphone-plus, actívelo y ejecute sudo nginx -t && sudo systemctl reload nginx. Configure LIVEKIT_URL=wss://meeting.example.com en el backend. Nginx solo reenvía API y WebSocket; exponga TCP 7881 y UDP 7882 directamente en los cortafuegos.
Tip
Avanzado: Despliegue híbrido Zeabur + Vercel Después de desplegar en Zeabur, puede eliminar manualmente el servicio client y desplegar el frontend en Vercel en su lugar (ver Opción 2 a continuación). De esta forma, server/MySQL/Redis se alojan en Zeabur mientras el frontend se acelera mediante el CDN global de Vercel. El frontend no requiere variables de entorno en Vercel — los usuarios simplemente ingresan la dirección del servidor backend en la página de inicio de sesión.
Opción 1: Docker Compose (Recomendado)
git clone <repo-url> && cd paperphone-plus
cp server/.env.example server/.env
# Editar: DB_PASS / JWT_SECRET / LIVEKIT_URL etc.
docker compose up -d
open http://localhost
Opción 2: Frontend en Vercel
# 1. Hacer fork de este repositorio
# 2. Importar en Vercel: Root Directory = client/, Build = npm run build, Output = dist/
# No se necesitan variables de entorno
# 3. Desplegar el backend vía Docker o Zeabur
# 4. Abrir el frontend desplegado en Vercel, ingresar la dirección del servidor backend en la página de inicio de sesión
# ej. https://your-server.zeabur.app
Opción 3: Desarrollo local
# Backend (Rust)
cd server && cp .env.example .env && cargo run --release
# Frontend (React)
cd client && npm install && npm run dev
Modificador de voz
Los mensajes de voz, las llamadas 1:1 y las llamadas grupales admiten modificación de voz en tiempo real con 3 modos seleccionables:
| Modo | Velocidad | Efecto |
|---|---|---|
| 🐢 Lento | 0.8x | Voz más profunda y grave — ideal para anonimato |
| 🔊 Normal | 1.0x | Voz original, sin procesamiento |
| 🐇 Rápido | 1.2x | Voz más aguda — divertida y juguetona |
Cómo funciona: Utiliza la Web Audio API para construir una cadena de procesamiento de audio (AudioContext → MediaStreamSource → ScriptProcessorNode → MediaStreamDestination) que ajusta el tono/velocidad de la entrada del micrófono en tiempo real.
- Mensajes de voz: Seleccionar el modo de voz durante la grabación. El archivo
.webmexportado ya contiene el efecto de voz — los destinatarios no pueden restaurar la voz original, permitiendo mensajería verdaderamente anónima - Llamadas 1:1 / Grupales: Tocar el botón de modificador de voz durante una llamada para alternar entre modos. La pista de audio procesada reemplaza la original mediante
LiveKit LocalAudioTrack.replaceTrack()
No se requiere configuración del lado del servidor. El modificador de voz funciona completamente en el lado del cliente.
Aspecto del texto y cifrado adicional
En Perfil > Privacidad de los mensajes, puedes activar una contraseña adicional para todos los chats de este dispositivo. El contenido se cifra y se muestra como texto budista, chino aleatorio, símbolos del I Ching, hangul, jeroglíficos egipcios, cuneiforme, texto de valores fundamentales o letras y números.
- Ambas partes deben configurar la misma contraseña en sus dispositivos; no se sincroniza automáticamente.
- La contraseña debe tener al menos ocho caracteres y solo permanece en memoria mientras está desbloqueada. Localmente solo se guardan la sal y el verificador.
- Bloquea de inmediato o automáticamente 5 / 15 / 30 / 60 minutos después de pasar a segundo plano. Bloqueado, solo se muestra el texto cifrado con el aspecto elegido.
- Desactivar el cifrado adicional siempre exige volver a introducir la contraseña correcta, incluso si está desbloqueado.
- El aspecto del texto es una capa local adicional de privacidad; no sustituye el cifrado de extremo a extremo.
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 de Cloudflare | — |
R2_ACCESS_KEY_ID | Access Key del token API de R2 | — |
R2_SECRET_ACCESS_KEY | Secret Key del token API de R2 | — |
R2_BUCKET | Nombre del bucket R2 | — |
R2_PUBLIC_URL | URL base pública de R2 (opcional) | — |
LIVEKIT_URL | URL WebSocket pública de LiveKit para todas las llamadas | — |
LIVEKIT_API_KEY | Clave API compartida por el servidor y LiveKit | — |
LIVEKIT_API_SECRET | Secreto API compartido por el servidor y LiveKit | — |
VAPID_PUBLIC_KEY | Clave pública VAPID de Web Push (opcional) | — |
VAPID_PRIVATE_KEY | Clave privada VAPID de Web Push (opcional) | — |
VAPID_SUBJECT | Email de contacto VAPID (opcional) | mailto:admin@paperphoneplus.app |
FCM_PROJECT_ID | ID del proyecto Firebase (opcional, Capacitor Android) | — |
FCM_CLIENT_EMAIL | Email de la cuenta de servicio Firebase (opcional) | — |
FCM_PRIVATE_KEY | Clave privada de la cuenta de servicio Firebase (opcional, soporta tanto escape \n como saltos de línea reales; ver abajo) | — |
FCM_RELAY_SECRET | Secreto del relay push FCM (opcional, configurar en el host relay para habilitar endpoint) | — |
FCM_RELAY_URL | URL del relay push FCM (opcional, servidores auto-hospedados apuntan al host relay) | — |
FCM_RELAY_KEY | Clave de autenticación del relay push FCM (opcional, debe coincidir con FCM_RELAY_SECRET del host relay) | — |
ONESIGNAL_APP_ID | OneSignal App ID (opcional) | — |
ONESIGNAL_REST_KEY | OneSignal REST API Key (opcional) | — |
ONESIGNAL_RELAY_SECRET | Secreto del relay push OneSignal (opcional, configurar en host relay para habilitar endpoint) | — |
ONESIGNAL_RELAY_URL | URL del relay push OneSignal (opcional, servidores auto-hospedados apuntan al host relay) | — |
ONESIGNAL_RELAY_KEY | Clave de autenticación del relay push OneSignal (opcional, debe coincidir con ONESIGNAL_RELAY_SECRET del host relay) | — |
NTFY_BASE_URL | URL del servidor ntfy (opcional, usa ntfy.sh público por defecto) | https://ntfy.sh |
NTFY_TOKEN | Token de autenticación ntfy (opcional, para servidores auto-hospedados) | — |
APNS_TEAM_ID | Apple Developer Team ID (opcional, push nativo iOS) | — |
APNS_KEY_ID | ID de clave de autenticación APNS (opcional) | — |
APNS_PRIVATE_KEY | Contenido de clave privada .p8 de APNS (opcional, soporta escape \n) | — |
APNS_BUNDLE_ID | iOS App Bundle Identifier (opcional) | — |
APNS_SANDBOX | Modo sandbox APNS (opcional, true para desarrollo/TestFlight) | false |
APNS_RELAY_SECRET | Secreto del relay push (opcional, configurar en host relay para habilitar endpoint) | — |
APNS_RELAY_URL | URL del relay push (opcional, servidores auto-hospedados apuntan al host relay) | — |
APNS_RELAY_KEY | Clave de autenticación del relay push (opcional, debe coincidir con APNS_RELAY_SECRET del host relay) | — |
TELEGRAM_BOT_TOKEN | Telegram Bot Token (opcional) | — |
STICKER_PACKS | Paquetes de stickers personalizados (opcional, nombre:etiqueta) | 13 predeterminados integrados |
ADMIN_PATH | Ruta URL del panel de administración | /admin |
ADMIN_PASSWORD | Contraseña del panel de administración (cambiar en producción) | admin123 |
Manejo de saltos de línea en la clave privada FCM
El campo private_key en el JSON de la cuenta de servicio de Firebase contiene una clave privada RSA en formato PEM, que requiere saltos de línea reales (\n, ASCII 0x0A) entre cada línea de 64 caracteres. Sin embargo, muchas plataformas de despliegue (Zeabur, Vercel, Railway, Docker) almacenan las variables de entorno como cadenas de una sola línea, convirtiendo \n en la secuencia literal de dos caracteres \ + n.
Esta es la causa más común de fallo en las notificaciones push FCM — el parser PEM falla silenciosamente y no se envían notificaciones push, sin registros de error.
El servidor maneja esto automáticamente: fcm.rs normaliza las secuencias literales \n a saltos de línea reales antes del análisis. Ambos formatos funcionan:
-
Una línea (recomendado para plataformas en la nube): Pegar el valor bruto de
private_keydel archivo JSON con escapes\n:FCM_PRIVATE_KEY=-----BEGIN PRIVATE KEY-----\nMIIEvQ...\n-----END PRIVATE KEY-----\n -
Múltiples líneas (para archivos .env): Envolver el contenido PEM completo entre comillas con saltos de línea reales:
FCM_PRIVATE_KEY="-----BEGIN PRIVATE KEY----- MIIEvQ... -----END PRIVATE KEY-----"
| Plataforma | Formato recomendado | Notas |
|---|---|---|
| Zeabur | Una línea (\n escapado) | Pegar valor JSON directamente en el panel de Variables |
| Docker / docker-compose | Ambos | Usar YAML | para múltiples líneas; una línea en .env |
| Vercel / Railway | Una línea (\n escapado) | Los campos de entrada normalmente no soportan saltos de línea reales |
| Archivo .env en Linux | Múltiples líneas (con comillas) | Asegurarse de que las comillas estén correctamente cerradas |
Solución de problemas: Si las variables FCM están configuradas pero el push de Android no funciona, revisar los registros del servidor:
[FCM] No access token available→ Error de formato de clave privada (problema de salto de línea)[FCM] ✅ Push sent to user xxx→ El envío FCM funciona, el problema es del lado del cliente- Sin registros FCM →
FCM_PROJECT_IDno configurado o no hay token en la tablafcm_tokens
Push ntfy (Dispositivos Android chinos sin Google Services)
Para dispositivos Android sin Google Mobile Services (Huawei, Xiaomi, OPPO, vivo, etc.), PaperPhonePlus soporta notificaciones push vía ntfy.
Configuración predeterminada (sin configuración): Usa el servicio público ntfy.sh. No se necesita configuración adicional.
Configuración opcional (para servidores ntfy auto-hospedados):
NTFY_BASE_URL=https://your-ntfy-server.com
NTFY_TOKEN=your_optional_auth_token
Flujo de configuración del usuario:
- Instalar la app ntfy (Google Play / F-Droid / Descarga directa)
- Abrir Ajustes de PaperPhonePlus y encontrar la tarjeta «ntfy Push»
- Copiar el nombre del topic mostrado y suscribirse en la app ntfy
- Tocar «Registrar Push» para completar el registro
Nota de seguridad: Las notificaciones ntfy contienen títulos y resúmenes en texto plano (no el contenido real del mensaje). Para mayor seguridad, considere auto-hospedar un servidor ntfy.
Push APNS (App nativa iOS)
APNS (Apple Push Notification Service) envía notificaciones push a apps iOS nativas construidas con Capacitor. Hay dos opciones de configuración:
Opción A: Configuración directa (Servidor del desarrollador de la app)
- Iniciar sesión en Apple Developer → Certificates, Identifiers & Profiles → Keys
- Clic en + para crear una nueva Key → marcar Apple Push Notifications service (APNs) → Register
- Descargar el archivo
.p8(⚠️ ¡solo se puede descargar una vez!) y anotar el Key ID - Anotar su Team ID de la página de membresía de Apple Developer (10 caracteres alfanuméricos)
- Agregar a
server/.env:
APNS_TEAM_ID=AB12CD34EF
APNS_KEY_ID=LH4Z9YN3P7
APNS_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nMIGTAgEA...(contenido del archivo .p8)...\n-----END PRIVATE KEY-----"
APNS_BUNDLE_ID=com.yourcompany.paperphoneplus
APNS_SANDBOX=false
APNS_SANDBOX: Configurar comotruepara compilaciones de desarrollo/TestFlight,falsepara producción en App Store.
Opción B: Vía Push Relay (Servidores auto-hospedados)
Si está usando la app iOS de otra persona (ej. descargada del App Store), no tiene las credenciales de Apple del desarrollador y no puede enviar pushes APNS directamente. Use el Push Relay en su lugar.
Cómo funciona:
┌──────────────────────┐ ┌─────────────────────────┐ ┌─────────┐
│ Servidor │ HTTP │ Servidor del │ APNS │ Apple │
│ auto-hospedado │──────→│ desarrollador │──────→│ ──→ 📱 │
│ (sin creds Apple) │ │ (tiene .p8 Key + Relay) │ └─────────┘
│ APNS_RELAY_URL=... │ │ APNS_TEAM_ID=... │
│ APNS_RELAY_KEY=... │ │ APNS_RELAY_SECRET=... │
└──────────────────────┘ └─────────────────────────┘
Paso 1: El desarrollador de la app habilita el endpoint Relay
En el servidor del desarrollador de la app (que ya tiene credenciales APNS), configurar un secreto relay:
# .env del servidor del desarrollador (ya tiene APNS_TEAM_ID etc.)
APNS_RELAY_SECRET=un_secreto_compartido_largo_aleatorio
Esto habilita automáticamente el endpoint de relay push en POST /api/push-relay/apns.
Paso 2: El usuario auto-hospedado configura el Relay
Los servidores auto-hospedados solo necesitan dos variables — no se requieren credenciales de Apple:
# .env del servidor auto-hospedado
APNS_RELAY_URL=https://app-developer-server.com
APNS_RELAY_KEY=el_secreto_compartido_del_paso_1
Cómo funciona:
- El servidor auto-hospedado recibe un mensaje offline → consulta la tabla local
apns_tokenspara los tokens de dispositivos iOS del usuario - Envía tokens de dispositivo + título/contenido push vía HTTP POST al Relay
- El Relay valida la clave, luego envía a Apple usando sus propias credenciales APNS
- El Relay devuelve una lista de tokens obsoletos; el servidor auto-hospedado limpia automáticamente su base de datos local
Prioridad: Credenciales APNS locales → Push Relay → Omitir (silencioso). Si ambos están configurados, la conexión directa local tiene prioridad.
Nota de seguridad: El relay solo transmite títulos y resúmenes de notificaciones push (ej. «Alguien te envió un mensaje»), no el contenido real del mensaje. Los tokens de dispositivo no pueden usarse para leer datos del usuario.
Push Relay (Todos los canales)
Para operadores de servidores auto-hospedados que usan la app publicada de otra persona (ej. del App Store/Google Play), no tiene las credenciales push del desarrollador (Apple .p8 Key / cuenta de servicio Firebase / OneSignal API Key).
El sistema Push Relay proporciona capacidad de relay para los canales APNS, FCM y OneSignal:
El desarrollador de la app habilita los endpoints relay en su servidor:
# .env del servidor del desarrollador
APNS_RELAY_SECRET=una_cadena_larga_aleatoria
FCM_RELAY_SECRET=una_cadena_larga_aleatoria
ONESIGNAL_RELAY_SECRET=una_cadena_larga_aleatoria
Los usuarios auto-hospedados solo necesitan URL y clave del relay — no se requieren credenciales de servicios push:
# .env del servidor auto-hospedado
# APNS (push nativo iOS)
APNS_RELAY_URL=https://app-developer-server.com
APNS_RELAY_KEY=secreto_compartido
# FCM (push nativo Android)
FCM_RELAY_URL=https://app-developer-server.com
FCM_RELAY_KEY=secreto_compartido
# OneSignal (apps empaquetadas con Median.co)
ONESIGNAL_RELAY_URL=https://app-developer-server.com
ONESIGNAL_RELAY_KEY=secreto_compartido
Prioridad: Credenciales locales → Push Relay → Omitir (silencioso). Si ambos están configurados, la conexión directa local tiene prioridad.
Push Relay oficial
Los operadores de servidores auto-hospedados pueden usar el relay push oficial para habilitar notificaciones push iOS/Android sin configurar credenciales push:
# 2026-05-18
APNS_RELAY_URL=https://619.chat
APNS_RELAY_KEY=EzmpqftbsENaRUO6BTABxLV96q7RuEDyokXJr1DWdDjL54cLg7yXVUQqydCQvxrX
FCM_RELAY_URL=https://619.chat
FCM_RELAY_KEY=EzmpqftbsENaRUO6BTABxLV96q7RuEDyokXJr1DWdDjL54cLg7yXVUQqydCQvxrX
ONESIGNAL_RELAY_URL=https://619.chat
ONESIGNAL_RELAY_KEY=EzmpqftbsENaRUO6BTABxLV96q7RuEDyokXJr1DWdDjL54cLg7yXVUQqydCQvxrX
Agregue estas líneas al archivo .env de su servidor auto-hospedado.
Licencia
Este proyecto está licenciado bajo la GNU Affero General Public License v3.0 (AGPL-3.0).