README_FR.md

August 11, 2026 · View on GitHub

🌐 Autres langues : äž­æ–‡ · English · æ—„æœŹèȘž · 한ꔭ얎 · Deutsch · РуссĐșĐžĐč · Español

Une application de messagerie instantanée chiffrée de bout en bout, style WeChat, avec chiffrement ECDH + XSalsa20-Poly1305 sans état par message, appels vidéo en temps réel, stockage de fichiers Cloudflare R2, support multilingue et déploiement PWA iOS.

Rust React TypeScript MySQL Redis WebRTC License: AGPL v3

Deploy on Zeabur

Version

Google Play App Store Windows Mac


📾 Captures d'Ă©cran (cliquez pour agrandir) ui1 ui2 ui3 ui4 ui5 ui6 ui7 ui8 ui9 ui10 ui11 ui12 ui13 ui14 ui15 ui16 ui17 ui18

Fonctionnalités

FonctionnalitéDescription
🔐 Chiffrement de bout en boutECDH sans Ă©tat + XSalsa20-Poly1305 — clĂ©s Ă©phĂ©mĂšres par message, forward secrecy, vĂ©rification du numĂ©ro de sĂ©curitĂ© style Signal
đŸ—ïž Serveur Ă  connaissance nulleLe serveur ne stocke que le texte chiffrĂ© ; les clĂ©s privĂ©es ne quittent jamais l'appareil
đŸ“č Appels vidĂ©o et audioSFU LiveKit pour les appels 1:1 et les rĂ©unions (jusqu’à 100 participants), mise en sourdine globale et mode cours
đŸŽ™ïž Modificateur de voixEffets vocaux en temps rĂ©el pour les messages vocaux, appels 1:1 et appels de groupe — 3 modes (0.8x grave / 1.0x normal / 1.2x aigu), basĂ© sur Web Audio API
đŸ“± Persistance de sessionJetons d’accĂšs de 30 minutes avec sessions de rafraĂźchissement d’appareil de 90 jours ; rĂ©cupĂ©ration automatique aprĂšs changement de rĂ©seau, IP, VPN ou proxy sans mot de passe
📹 Synchronisation fiableHeartbeat bidirectionnel, dĂ©tection des connexions semi-ouvertes, boĂźte d’envoi persistante, identifiants idempotents et rattrapage par curseur de sĂ©quence serveur
📮 AccĂšs hors ligneCache isolĂ© par compte pour contacts, groupes, jusqu’à 2 000 messages par conversation, Moments, Timeline et mĂ©dias ; les envois hors ligne sont retentĂ©s automatiquement
🔎 Recherche Unicode d’amisProtection de composition IME, normalisation NFC et encodage UTF-8 pour une recherche fiable des noms d’utilisateur et pseudonymes chinois
đŸ‘„ Chat de groupeJusqu'Ă  2000 membres, modes « ChiffrĂ© » / « Non chiffrĂ© » commutables (propriĂ©taire uniquement, le changement efface l'historique). Le mode chiffrĂ© utilise le protocole Sender Key de type Signal (chiffrement symĂ©trique XSalsa20-Poly1305 + distribution de clĂ©s ECDH) — seuls les membres du groupe peuvent dĂ©chiffrer les messages ; les bots sont dĂ©sactivĂ©s en mode chiffrĂ©. Mode Ne pas dĂ©ranger, gestion des membres
đŸ‘« SystĂšme d'amisLes demandes d'amitiĂ© nĂ©cessitent une approbation avec jusqu'Ă  512 caractĂšres de message ; surnoms personnalisĂ©s ; regroupement par Ă©tiquettes
⏱ Suppression automatique des messages5 niveaux (jamais / 1 jour / 3 jours / 1 semaine / 1 mois), configurable par les deux parties en DM, uniquement par le propriĂ©taire dans les groupes
🔔 Notifications pushWeb Push (VAPID) + FCM + OneSignal + ntfy + APNS cinq canaux — atteint les utilisateurs mĂȘme hors ligne (iOS natif + Android chinois sans Google Services)
🌐 MultilingueChinois, anglais, japonais, corĂ©en, français, allemand, russe, espagnol — dĂ©tection automatique + changement manuel
đŸ“± iOS — Sans certificat d'entreprisePWA via Safari « Ajouter Ă  l'Ă©cran d'accueil », fonctionne en permanence sans signature Apple
đŸ“± App native AndroidDisponible sur Google Play, avec support des notifications push FCM
đŸ“± App native iOSDisponible sur l'App Store, avec support des notifications push APNS
đŸ–„ïž Client bureau WindowsApplication de bureau Windows native, tĂ©lĂ©charger ici
🍎 Client bureau MacApplication de bureau Mac native, tĂ©lĂ©charger ici
💬 Messagerie enrichieTexte, images, vidĂ©o, fichiers documents, messages vocaux, 200+ emojis, packs de stickers Telegram, accusĂ©s de rĂ©ception, indicateurs de saisie
đŸ“€ TĂ©lĂ©versement de fichiersJusqu'Ă  500 Mo par fichier, Cloudflare R2 ou stockage local, avec animation de progression
🌐 MomentsFil social style WeChat : texte + jusqu'Ă  9 photos ou 1 vidĂ©o (≀ 10 min), likes, commentaires, visibilitĂ© par Ă©tiquettes
đŸ‘€ Profil utilisateurPage de profil de contact avec contrĂŽles de confidentialitĂ© bidirectionnels des Moments
📰 Fil d'actualitĂ©Fil public style Xiaohongshu — mise en page masonry Ă  deux colonnes, publications anonymes, likes et commentaires
đŸ·ïž Étiquettes d'amisAttribuer plusieurs Ă©tiquettes par ami (palette de 12 couleurs), filtrer les contacts par Ă©tiquette
đŸ—‚ïž Stockage d'objets R2Cloudflare R2 pour les fichiers image/audio — URL CDN publique optionnelle
🔑 Authentification Ă  deux facteurs (2FA)TOTP compatible Google Authenticator, 8 codes de rĂ©cupĂ©ration, obligatoire Ă  la connexion
đŸ“· Scanner et partager des QR codesScanner des QR codes pour ajouter des amis ou rejoindre des groupes avec expiration configurable
đŸ—ïž Auto-hĂ©bergeableDocker Compose, Zeabur en un clic, ou frontend sur Vercel
🌐 ParamĂštres de proxySupport proxy SOCKS5 / HTTP / HTTPS — configurable sur les pages de connexion et de paramĂštres avec adresse serveur, port, identifiant et mot de passe pour les environnements rĂ©seau restreints
đŸ›Ąïž ModĂ©ration de contenuSignalements utilisateurs (6 catĂ©gories) + blocage d'utilisateurs (masquage instantanĂ© des publications/messages) + Conditions d'utilisation (EULA)
🔧 Panneau d'administrationDashboard web d'administration intĂ©grĂ© (/admin, chemin configurable), protĂ©gĂ© par mot de passe, examiner les signalements, supprimer le contenu problĂ©matique, bannir des utilisateurs — 8 langues

Nouveautés de la v2.3.9

  • Correction des anciennes relations d’amitiĂ© Ă  sens unique qui affichaient « DĂ©jĂ  amis » alors que le contact restait invisible et indisponible pour discuter ; un nouvel ajout rĂ©pare dĂ©sormais les deux sens et actualise immĂ©diatement la liste des contacts.

Nouveautés de la v2.3.8

  • Correction du bouton Retour inactif aprĂšs le dĂ©marrage de la camĂ©ra du scanner QR ; la fermeture arrĂȘte et libĂšre dĂ©sormais immĂ©diatement la camĂ©ra.
  • Correction des demandes d’ami rĂ©pĂ©tĂ©es Ă  un ami existant qui endommageaient la relation ; les rĂ©sultats indiquent dĂ©sormais « DĂ©jĂ  amis ».
  • Les messages privĂ©s sortants placent le texte chiffrĂ© dans l’objet optimiste dĂšs la fin du chiffrement de bout en bout, sans persister temporairement le texte en clair avant l’accusĂ© du serveur.
  • Les messages vocaux s’arrĂȘtent automatiquement Ă  120 secondes ; la sortie avec changement de voix suit la mĂȘme limite.
  • L’écran reste actif pendant les enregistrements et appels, et les pĂ©riphĂ©riques ainsi que les minuteurs sont libĂ©rĂ©s en quittant la page.
  • Android protĂšge aussi les clĂ©s et caches avec Android Keystore et AES-256-GCM ; le client Web conserve le stockage du navigateur.

Récupération de session et fiabilité des messages

PaperPhonePlus sĂ©pare l’état local du compte, la connexion temps rĂ©el et la synchronisation des messages. Un WebSocket ouvert n’est utilisable qu’aprĂšs rĂ©ception de auth_ok. Les heartbeats bidirectionnels ping/pong dĂ©tectent les connexions semi-ouvertes causĂ©es par un changement de VPN/IP, un basculement Wi-Fi/mobile ou la suspension de l’application.

  • Les jetons d’accĂšs durent 30 minutes. Les jetons de rafraĂźchissement d’appareil durent 90 jours et sont prolongĂ©s pendant l’utilisation active, sans redemander le mot de passe.
  • Les appareils dĂ©jĂ  connectĂ©s avec une ancienne version sont mis Ă  niveau automatiquement tant que leur jeton reste valide. S’il a expirĂ©, une derniĂšre connexion manuelle est nĂ©cessaire.
  • Chaque message sortant possĂšde une client_msg_id stable. Sans ACK serveur, il reste dans la boĂźte d’envoi locale persistante et est renvoyĂ© avec le mĂȘme ID ; une contrainte d’unicitĂ© empĂȘche les doublons.
  • Chaque message stockĂ© possĂšde une server_seq croissante. Le client rattrape les messages manquants par curseur aprĂšs authentification, reconnexion et retour au premier plan.
  • La dĂ©connexion explicite et la rĂ©vocation d’un appareil invalident la session durable. Les erreurs rĂ©seau ordinaires et changements d’IP la conservent.

Important

Lors d’une mise Ă  niveau, dĂ©ployez d’abord le serveur, puis les clients. Le serveur applique et vĂ©rifie automatiquement la migration de fiabilitĂ© et refuse de dĂ©marrer si des colonnes critiques manquent. Sauvegardez MySQL avant toute mise Ă  niveau de production.


Stack technique

Backend (server/)
  Rust (Axum 0.8) — Framework web asynchrone haute performance
  sqlx + MySQL 8.0 — Persistance des utilisateurs/messages
  deadpool-redis + Redis 7 — PrĂ©sence en ligne + routage inter-nƓuds
  aws-sdk-s3 — Stockage fichiers Cloudflare R2 (API compatible S3)
  Authentification argon2 + jsonwebtoken

Frontend (client/)
  React 19 + TypeScript + Vite 6
  Gestion d'état Zustand
  libsodium-wrappers-sumo (WebAssembly — Curve25519 / XSalsa20-Poly1305)
  WebRTC API — appels vidĂ©o / audio
  Web Audio API — modificateur de voix en temps rĂ©el (chaĂźne audio ScriptProcessorNode)
  PWA : manifest.json + Service Worker

Couche cryptographique
  ECDH sans Ă©tat + XSalsa20-Poly1305 — paire de clĂ©s Ă©phĂ©mĂšre par message
  Persistance des clĂ©s Ă  quatre niveaux : mĂ©moire → localStorage → sessionStorage → IndexedDB
  Toutes les clĂ©s privĂ©es stockĂ©es uniquement sur l'appareil — jamais envoyĂ©es au serveur

📖 Guide de dĂ©ploiement dĂ©taillĂ© (äž­æ–‡) | Deployment Guide (English) — Instructions Ă©tape par Ă©tape complĂštes pour le dĂ©ploiement hybride Zeabur + Vercel, le dĂ©ploiement local avec Docker Compose + Nginx, et la configuration de l'adresse du serveur client.

Option 0 : Déploiement cloud Zeabur en un clic

Deploy on Zeabur

Limitation rĂ©seau des appels sur Zeabur : le modĂšle dĂ©ploie LiveKit avec WebSocket/API 7880 et ICE/TCP 7881. Zeabur n’expose actuellement pas de ports UDP ; les appels 1:1 et les rĂ©unions utilisent donc le repli TCP. Pour les appels en production, utilisez LiveKit Cloud ou une VM prenant en charge UDP.

Configuration Nginx cÎté serveur

Utilisez la configuration de production Ă  deux domaines deploy/nginx/paperphone-plus.conf. Remplacez api.example.com et meeting.example.com, obtenez les certificats TLS, copiez le fichier dans /etc/nginx/sites-available/paperphone-plus, activez-le, puis exĂ©cutez sudo nginx -t && sudo systemctl reload nginx. DĂ©finissez LIVEKIT_URL=wss://meeting.example.com. Nginx ne relaie que l’API et WebSocket ; exposez directement TCP 7881 et UDP 7882 dans les pare-feu.

Tip

AvancĂ© : DĂ©ploiement hybride Zeabur + Vercel AprĂšs le dĂ©ploiement sur Zeabur, vous pouvez supprimer manuellement le service client et dĂ©ployer le frontend sur Vercel Ă  la place (voir Option 2 ci-dessous). Ainsi, server/MySQL/Redis sont hĂ©bergĂ©s sur Zeabur tandis que le frontend est accĂ©lĂ©rĂ© par le CDN mondial de Vercel. Le frontend ne nĂ©cessite aucune variable d'environnement sur Vercel — les utilisateurs saisissent simplement l'adresse du serveur backend sur la page de connexion.

Option 1 : Docker Compose (Recommandé)

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

Option 2 : Frontend sur Vercel

# 1. Forker ce dépÎt
# 2. Importer dans Vercel : Root Directory = client/, Build = npm run build, Output = dist/
#    Aucune variable d'environnement requise
# 3. Déployer le backend via Docker ou Zeabur
# 4. Ouvrir le frontend déployé sur Vercel, saisir l'adresse du serveur backend sur la page de connexion
#    ex. https://your-server.zeabur.app

Option 3 : Développement local

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

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

Modificateur de voix

Les messages vocaux, les appels 1:1 et les appels de groupe supportent la modification de voix en temps réel avec 3 modes sélectionnables :

ModeVitesseEffet
🐱 Lent0.8xVoix plus profonde et grave — idĂ©al pour l'anonymat
🔊 Normal1.0xVoix originale, aucun traitement
🐇 Rapide1.2xVoix plus aiguĂ« — amusant et ludique

Fonctionnement : Utilise la Web Audio API pour construire une chaĂźne de traitement audio (AudioContext → MediaStreamSource → ScriptProcessorNode → MediaStreamDestination) qui ajuste la hauteur/vitesse de l'entrĂ©e microphone en temps rĂ©el.

  • Messages vocaux : SĂ©lectionner le mode vocal pendant l'enregistrement. Le fichier .webm exportĂ© contient dĂ©jĂ  l'effet vocal — les destinataires ne peuvent pas restaurer la voix originale, permettant une messagerie vĂ©ritablement anonyme
  • Appels 1:1 / Groupe : Appuyer sur le bouton modificateur de voix pendant un appel pour alterner entre les modes. La piste audio traitĂ©e remplace l'originale via LiveKit LocalAudioTrack.replaceTrack()

Aucune configuration cÎté serveur requise. Le modificateur de voix fonctionne entiÚrement cÎté client.


Variables d'environnement

VariableDescriptionDéfaut
PORTPort du serveur3000
JWT_SECRETClé de signature JWT (changer en production)dev_secret
DB_HOST / DB_PASS / DB_NAMEConnexion MySQL—
REDIS_HOST / REDIS_PASSConnexion Redis—
R2_ACCOUNT_IDID de compte Cloudflare—
R2_ACCESS_KEY_IDAccess Key du jeton API R2—
R2_SECRET_ACCESS_KEYSecret Key du jeton API R2—
R2_BUCKETNom du bucket R2—
R2_PUBLIC_URLURL de base publique R2 (optionnel)—
LIVEKIT_URLURL WebSocket publique LiveKit pour tous les appels—
LIVEKIT_API_KEYClĂ© API partagĂ©e par le serveur et LiveKit—
LIVEKIT_API_SECRETSecret API partagĂ© par le serveur et LiveKit—
VAPID_PUBLIC_KEYClĂ© publique VAPID Web Push (optionnel)—
VAPID_PRIVATE_KEYClĂ© privĂ©e VAPID Web Push (optionnel)—
VAPID_SUBJECTEmail de contact VAPID (optionnel)mailto:admin@paperphoneplus.app
FCM_PROJECT_IDID du projet Firebase (optionnel, Capacitor Android)—
FCM_CLIENT_EMAILEmail du compte de service Firebase (optionnel)—
FCM_PRIVATE_KEYClĂ© privĂ©e du compte de service Firebase (optionnel, supporte l'Ă©chappement \n et les sauts de ligne rĂ©els ; voir ci-dessous)—
FCM_RELAY_SECRETSecret du relay push FCM (optionnel, configurer sur l'hîte relay pour activer le endpoint)—
FCM_RELAY_URLURL du relay push FCM (optionnel, les serveurs auto-hĂ©bergĂ©s pointent vers l'hĂŽte relay)—
FCM_RELAY_KEYClĂ© d'authentification du relay push FCM (optionnel, doit correspondre au FCM_RELAY_SECRET de l'hĂŽte relay)—
ONESIGNAL_APP_IDOneSignal App ID (optionnel)—
ONESIGNAL_REST_KEYOneSignal REST API Key (optionnel)—
ONESIGNAL_RELAY_SECRETSecret du relay push OneSignal (optionnel, configurer sur l'hîte relay pour activer le endpoint)—
ONESIGNAL_RELAY_URLURL du relay push OneSignal (optionnel, les serveurs auto-hĂ©bergĂ©s pointent vers l'hĂŽte relay)—
ONESIGNAL_RELAY_KEYClĂ© d'authentification du relay push OneSignal (optionnel, doit correspondre au ONESIGNAL_RELAY_SECRET de l'hĂŽte relay)—
NTFY_BASE_URLURL du serveur ntfy (optionnel, utilise le service public ntfy.sh par défaut)https://ntfy.sh
NTFY_TOKENJeton d'authentification ntfy (optionnel, pour les serveurs auto-hĂ©bergĂ©s)—
APNS_TEAM_IDApple Developer Team ID (optionnel, push natif iOS)—
APNS_KEY_IDID de clĂ© d'authentification APNS (optionnel)—
APNS_PRIVATE_KEYContenu de la clĂ© privĂ©e .p8 APNS (optionnel, supporte l'Ă©chappement \n)—
APNS_BUNDLE_IDiOS App Bundle Identifier (optionnel)—
APNS_SANDBOXMode sandbox APNS (optionnel, true pour développement/TestFlight)false
APNS_RELAY_SECRETSecret du relay push (optionnel, configurer sur l'hîte relay pour activer le endpoint)—
APNS_RELAY_URLURL du relay push (optionnel, les serveurs auto-hĂ©bergĂ©s pointent vers l'hĂŽte relay)—
APNS_RELAY_KEYClĂ© d'authentification du relay push (optionnel, doit correspondre au APNS_RELAY_SECRET de l'hĂŽte relay)—
TELEGRAM_BOT_TOKENTelegram Bot Token (optionnel)—
STICKER_PACKSPacks de stickers personnalisés (optionnel, nom:label)13 packs intégrés par défaut
ADMIN_PATHChemin URL du panneau d'administration/admin
ADMIN_PASSWORDMot de passe du panneau d'administration (changer en production)admin123

Gestion des sauts de ligne de la clé privée FCM

Le champ private_key dans le JSON du compte de service Firebase contient une clé privée RSA au format PEM, qui nécessite des sauts de ligne réels (\n, ASCII 0x0A) entre chaque ligne de 64 caractÚres. Cependant, de nombreuses plateformes de déploiement (Zeabur, Vercel, Railway, Docker) stockent les variables d'environnement sous forme de chaßnes sur une seule ligne, convertissant \n en la séquence littérale de deux caractÚres \ + n.

C'est la cause la plus frĂ©quente d'Ă©chec des notifications push FCM — le parseur PEM Ă©choue silencieusement et aucune notification push n'est envoyĂ©e, sans journaux d'erreur.

Le serveur gÚre cela automatiquement : fcm.rs normalise les séquences littérales \n en sauts de ligne réels avant l'analyse. Les deux formats fonctionnent :

  • Ligne unique (recommandĂ© pour les plateformes cloud) : Coller la valeur brute de private_key du fichier JSON avec les Ă©chappements \n :

    FCM_PRIVATE_KEY=-----BEGIN PRIVATE KEY-----\nMIIEvQ...\n-----END PRIVATE KEY-----\n
    
  • Multi-lignes (pour les fichiers .env) : Envelopper le contenu PEM complet entre guillemets avec des sauts de ligne rĂ©els :

    FCM_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----
    MIIEvQ...
    -----END PRIVATE KEY-----"
    
PlateformeFormat recommandéNotes
ZeaburLigne unique (\n échappé)Coller la valeur JSON directement dans le panneau Variables
Docker / docker-composeLes deuxUtiliser la syntaxe YAML | pour multi-lignes ; ligne unique dans .env
Vercel / RailwayLigne unique (\n échappé)Les champs de saisie ne supportent généralement pas les sauts de ligne réels
Fichier .env LinuxMulti-lignes (entre guillemets)S'assurer que les guillemets sont correctement fermés

Dépannage : Si les variables FCM sont configurées mais que le push Android ne fonctionne pas, vérifier les journaux du serveur :

  • [FCM] No access token available → Erreur de format de clĂ© privĂ©e (problĂšme de saut de ligne)
  • [FCM] ✅ Push sent to user xxx → L'envoi FCM fonctionne, le problĂšme est cĂŽtĂ© client
  • Aucun journal FCM → FCM_PROJECT_ID non configurĂ© ou aucun token dans la table fcm_tokens

Push ntfy (Appareils Android chinois sans Google Services)

Pour les appareils Android sans Google Mobile Services (Huawei, Xiaomi, OPPO, vivo, etc.), PaperPhonePlus supporte les notifications push via ntfy.

Configuration par défaut (zéro configuration) : Utilise le service public ntfy.sh. Aucune configuration supplémentaire nécessaire.

Configuration optionnelle (pour les serveurs ntfy auto-hébergés) :

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

Procédure de configuration utilisateur :

  1. Installer l'application ntfy (Google Play / F-Droid / Téléchargement direct)
  2. Ouvrir les ParamÚtres de PaperPhonePlus et trouver la carte « ntfy Push »
  3. Copier le nom du topic affiché et s'y abonner dans l'application ntfy
  4. Appuyer sur « Enregistrer Push » pour terminer l'inscription

Note de sécurité : Les notifications ntfy contiennent les titres et résumés en texte brut (pas le contenu réel du message). Pour une sécurité renforcée, envisagez d'auto-héberger un serveur ntfy.

Push APNS (App native iOS)

APNS (Apple Push Notification Service) envoie des notifications push aux apps iOS natives construites avec Capacitor. Deux options de configuration sont disponibles :

Option A : Configuration directe (Serveur du développeur de l'app)

  1. Se connecter à Apple Developer → Certificates, Identifiers & Profiles → Keys
  2. Cliquer sur + pour crĂ©er une nouvelle Key → cocher Apple Push Notifications service (APNs) → Register
  3. TĂ©lĂ©charger le fichier .p8 (⚠ ne peut ĂȘtre tĂ©lĂ©chargĂ© qu'une seule fois !) et noter le Key ID
  4. Noter votre Team ID depuis la page de membre Apple Developer (10 caractÚres alphanumériques)
  5. Ajouter Ă  server/.env :
APNS_TEAM_ID=AB12CD34EF
APNS_KEY_ID=LH4Z9YN3P7
APNS_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nMIGTAgEA...(contenu du fichier .p8)...\n-----END PRIVATE KEY-----"
APNS_BUNDLE_ID=com.yourcompany.paperphoneplus
APNS_SANDBOX=false

APNS_SANDBOX : Définir à true pour les builds de développement/TestFlight, false pour la production App Store.

Option B : Via Push Relay (Serveurs auto-hébergés)

Si vous utilisez l'app iOS de quelqu'un d'autre (ex. téléchargée depuis l'App Store), vous n'avez pas les identifiants Apple du développeur et ne pouvez pas envoyer de pushes APNS directement. Utilisez le Push Relay à la place.

Fonctionnement :

┌──────────────────────┐       ┌─────────────────────────┐       ┌─────────┐
│  Serveur              │  HTTP  │  Serveur du              │  APNS  │  Apple  │
│  auto-hĂ©bergĂ©         │──────→│  dĂ©veloppeur             │──────→│  ──→ đŸ“± │
│  (sans creds Apple)   │       │  (a .p8 Key + Relay)     │       └─────────┘
│  APNS_RELAY_URL=...   │       │  APNS_TEAM_ID=...        │
│  APNS_RELAY_KEY=...   │       │  APNS_RELAY_SECRET=...   │
└──────────────────────┘       └─────────────────────────┘

Étape 1 : Le dĂ©veloppeur de l'app active le endpoint Relay

Sur le serveur du développeur de l'app (qui possÚde déjà les identifiants APNS), configurer un secret relay :

# .env du serveur du développeur (a déjà APNS_TEAM_ID etc.)
APNS_RELAY_SECRET=un_long_secret_partagé_aléatoire

Cela active automatiquement le endpoint de relay push Ă  POST /api/push-relay/apns.

Étape 2 : L'utilisateur auto-hĂ©bergĂ© configure le Relay

Les serveurs auto-hĂ©bergĂ©s n'ont besoin que de deux variables — aucun identifiant Apple requis :

# .env du serveur auto-hébergé
APNS_RELAY_URL=https://app-developer-server.com
APNS_RELAY_KEY=le_secret_partagé_de_l_étape_1

Fonctionnement :

  1. Le serveur auto-hĂ©bergĂ© reçoit un message hors ligne → interroge la table locale apns_tokens pour les tokens d'appareils iOS de l'utilisateur
  2. Envoie les tokens d'appareils + titre/contenu push via HTTP POST au Relay
  3. Le Relay valide la clé, puis envoie à Apple en utilisant ses propres identifiants APNS
  4. Le Relay retourne une liste de tokens obsolÚtes ; le serveur auto-hébergé nettoie automatiquement sa base de données locale

PrioritĂ© : Identifiants APNS locaux → Push Relay → Ignorer (silencieux). Si les deux sont configurĂ©s, la connexion directe locale a la prioritĂ©.

Note de sĂ©curitĂ© : Le relay ne transmet que les titres et rĂ©sumĂ©s des notifications push (ex. « Quelqu'un vous a envoyĂ© un message »), pas le contenu rĂ©el du message. Les tokens d'appareils ne peuvent pas ĂȘtre utilisĂ©s pour lire les donnĂ©es des utilisateurs.

Push Relay (Tous les canaux)

Pour les opérateurs de serveurs auto-hébergés utilisant l'app publiée par quelqu'un d'autre (ex. depuis l'App Store/Google Play), vous n'avez pas les identifiants push du développeur (Apple .p8 Key / compte de service Firebase / OneSignal API Key).

Le systÚme Push Relay fournit une capacité de relais pour les canaux APNS, FCM et OneSignal :

Le développeur de l'app active les endpoints relay sur son serveur :

# .env du serveur du développeur
APNS_RELAY_SECRET=une_longue_chaßne_aléatoire
FCM_RELAY_SECRET=une_longue_chaßne_aléatoire
ONESIGNAL_RELAY_SECRET=une_longue_chaßne_aléatoire

Les utilisateurs auto-hĂ©bergĂ©s n'ont besoin que de l'URL et la clĂ© du relay — aucun identifiant de service push requis :

# .env du serveur auto-hébergé
# APNS (push natif iOS)
APNS_RELAY_URL=https://app-developer-server.com
APNS_RELAY_KEY=secret_partagé

# FCM (push natif Android)
FCM_RELAY_URL=https://app-developer-server.com
FCM_RELAY_KEY=secret_partagé

# OneSignal (apps empaquetées avec Median.co)
ONESIGNAL_RELAY_URL=https://app-developer-server.com
ONESIGNAL_RELAY_KEY=secret_partagé

PrioritĂ© : Identifiants locaux → Push Relay → Ignorer (silencieux). Si les deux sont configurĂ©s, la connexion directe locale a la prioritĂ©.


Push Relay officiel

Les opérateurs de serveurs auto-hébergés peuvent utiliser le relay push officiel pour activer les notifications push iOS/Android sans configurer d'identifiants 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

Ajoutez ces lignes au fichier .env de votre serveur auto-hébergé.


Licence

Ce projet est distribué sous la GNU Affero General Public License v3.0 (AGPL-3.0).