Bêta précoce (0.1.0), pas encore pour un process critique. Voir la roadmap
PNeX logo

Sécurité

La sécurité par conception — les niveaux, ce qui est livré et ce qui est prévu

La sécurité fait partie de l'ADN de PNeX : chaque couche a été pensée chiffrée, cloisonnée et vérifiable dès sa conception — y compris sur un Raspberry Pi dans un atelier. Chaque niveau ci-dessous affiche son état réel ; rien n'est annoncé qui ne soit livré.

CLIENTSEDGE · TLSCŒURDONNÉESHTTPS · WSSHTTPSWSS · TLS 1.2WSScerts/auth/v1/ · /wsOIDCsuperviseSQL · jobsingest · querylast valuesliveartifacts · mediaApp webDioxus · WASMApps nativesDioxus · same codeAppareilsESP32 · ESP8266Votre codePublic APIEdge TLSnginx:443 · une seule origineCertificatsCA · Let’s EncryptIdentitéRauthyServeur PNeXRust · Loco + SeaORMMoteur de flowsEdgeLink · RustBase de donnéesPostgreSQLTélémétrieOpenObserveCache liveValkeyStockage objetS3-compatible
Chiffré TLS sur le réseau Réseau privé interneDocker Compose · Helm (à venir) · images Wolfi (Chainguard) · Raspberry Pi → cloud

Les huit niveaux

#NiveauCe qu'il garantitÉtat
1TransportTLS partout sur une seule origine, CA locale ou Let's Encrypt, CA épinglée dans le firmwareLivré
2Charge utile appareilTrames ChaCha20 par appareil dans le TLS, anti-clone, OTA vérifiée par SHA-256Livré
3IdentitéFournisseur OIDC dédié (Rauthy), PKCE, passkeysLivré
4AutorisationIsolation par organisation, rôles owner/admin/membre/lecteur, une seule source d'écriture par pin, IA en lecture seuleLivré
5ExécutionJavaScript en sandbox et Starlark hermétique, SQL des flows en lecture seule, builds de firmware custom encadrésLivré
6RéseauSeul nginx fait face au réseau (ports 80 et 443) ; bases et identité restent sur le réseau privéLivré — installeur de production ; la pile de développement publie encore les ports des bases
7Chaîne d'approvisionnementRust de bout en bout, images Wolfi (Chainguard), dépendances auditéesLivré
8Données au reposCoffre de secrets par organisation, XChaCha20-Poly1305, trousseau avec rotationLivré

TLS partout

nginx termine le TLS des navigateurs, des apps natives (desktop et Android) et des appareils sur une seule origine. TLS 1.2 reste activé car l'ESP8266 n'a pas TLS 1.3 ; de petits records TLS sont négociés pour son tampon de 2 Ko. Voir Auto-hébergement pour les modes de certificats local et cloud.

L'autorité de certification de votre edge (la CA locale, ou ISRG Root X1 en mode cloud) est injectée dans chaque build de firmware. Les appareils vérifient le certificat du serveur et son nom d'hôte — pour le canal WebSocket comme pour les téléchargements OTA. Renouveler le certificat serveur sous la même autorité est transparent ; changer d'autorité impose une recompilation et un reflash (USB ou OTA).

Chiffrement des trames appareil

À l'intérieur du TLS, chaque trame WebSocket entre un appareil et le serveur est chiffrée une seconde fois avec une clé propre à l'appareil :

trame = base64( nonce (12 octets) ‖ ChaCha20(clair) )
  • Clé : 32 octets aléatoires générés à l'enregistrement de l'appareil, stockés avec son token et intégrés à son firmware au build. Elle ne transite jamais sur le réseau.
  • Nonce : 12 octets aléatoires frais à chaque message, dans les deux sens (ChaCha20 selon la RFC 7539).
  • Périmètre : ingestion de télémétrie et canal appareil (annonce, commandes de pins, métriques et commandes custom, ordres OTA). Les agents edge utilisent le même encadrement. Les images OTA elles-mêmes sont téléchargées en HTTPS et vérifiées par SHA-256 (voir Firmware & OTA).
  • Limites de taille : une trame porte au plus 4 Ko de clair sur ESP32 et 1 Ko sur ESP8266 ; le firmware refuse d'envoyer plus gros plutôt que d'envoyer en clair.
  • Mauvaise clé : le serveur ne peut pas lire la trame et répond decryption_failed ; l'appareil ne passe jamais actif.

Les trames utilisent ChaCha20 sans Poly1305 : ce niveau apporte la confidentialité par appareil, pas l'authentification. L'intégrité et l'authenticité du serveur viennent de la couche TLS sous-jacente, avec l'autorité de certification épinglée dans le firmware. Un passage versionné à un mode authentifié (AEAD) est prévu.

Identité appareil et anti-clone

Un appareil se connecte avec son identifiant et son token. Les tokens sont uniques par appareil, jamais réutilisés, et désactivables : le serveur revalide les sessions actives environ toutes les 10 secondes et ferme celle dont le token a été révoqué.

Comme l'identité est gravée dans le firmware, deux cartes flashées avec le même binaire sont indiscernables. Le serveur impose donc un bail « premier live gagne », tenu dans Valkey et partagé par toutes les instances du serveur :

  • tant que l'appareil d'origine est connecté, une seconde connexion avec la même identité est refusée (code de fermeture 4003) ;
  • une déconnexion propre libère le bail immédiatement ;
  • après un silence plus long que le bail (10 secondes par défaut, soit deux heartbeats manqués), le bail expire et une reconnexion est acceptée.

Ce dernier point est la limite assumée : une fois l'original silencieux, un clone peut prendre sa place. Sans identité matérielle par carte, PNeX détecte et refuse les clones simultanés, il ne peut pas prouver quelle carte physique est l'authentique.

Coffre de secrets

Chaque organisation dispose d'un coffre unique pour tous ses secrets : tokens et mots de passe des canaux de notification, identifiants des nœuds HTTP des flows, mots de passe Wi-Fi et clés des fournisseurs LLM.

  • Chiffrement : XChaCha20-Poly1305 avec un nonce aléatoire de 24 octets à chaque écriture. L'organisation et l'identifiant du secret sont liés en données associées : une ligne recopiée vers une autre organisation ou un autre secret ne se déchiffre plus.
  • Clé hors base : le trousseau de clés maîtresses vient de l'environnement (PNEX_SECRETS_KEYS, généré par l'installeur). Le serveur refuse de démarrer sans lui. Un dump de la base seul ne révèle aucun secret.
  • Des références, pas des valeurs : un champ de formulaire qui porte un secret stocke une référence typée vers une entrée du coffre. Aucune valeur secrète n'apparaît dans les flows déployés, dans la file de jobs ni entre instances du serveur.
  • Runtime des flows : il ne reçoit que des références et résout chacune au démarrage, uniquement pour les secrets utilisés par un flow déployé de cette organisation. La clé maîtresse n'atteint jamais le runtime, et les nœuds fonction (JavaScript, Starlark) n'ont pas accès aux secrets.
  • Écriture seule : la page Secrets liste chaque secret avec ce qui l'utilise, jamais sa valeur. Aucun rôle ne peut relire une valeur. Owners et admins créent et remplacent les secrets ; les membres ne peuvent que choisir un secret existant ; les lecteurs ne voient que les noms. Un secret encore utilisé ne peut pas être supprimé.
  • Rotation : placez une nouvelle clé en tête du trousseau et redémarrez, puis lancez le rechiffrement depuis la page d'état de la plateforme. Le rechiffrement ne tourne jamais au démarrage : des instances mises à jour une par une continuent toutes de lire le coffre. La page d'état compte les secrets encore sous une ancienne clé ; retirez cette clé quand le compteur est à zéro.

Changer la valeur d'un secret ne déclenche rien en cascade : les flows la prennent à leur prochain déploiement, et les appareils gardent le mot de passe Wi-Fi avec lequel ils ont été flashés jusqu'à leur recompilation.

Hors du coffre, volontairement : les secrets d'infrastructure (URL de base de données, clés de stockage, le trousseau lui-même) restent dans l'environnement, et les tokens et clés de trame des appareils sont des identifiants machine pas encore chiffrés au repos.

Frontières de moindre privilège

  • Le serveur n'écrit jamais un pin de sa propre initiative.
  • L'assistant IA lit, n'écrit jamais sur les appareils.
  • Les nœuds de flows exécutent du SQL en lecture seule, et les HTTP fetch sont option-gated.
  • Les builds de firmware custom partent d'un projet généré par le serveur, avec contrôle du source, catalogue de bibliothèques et sandbox sans réseau optionnel — et la fonction est désactivée par défaut. Voir Firmware & OTA.

Modèle de menaces

Les tokens appareil sont URL-safe et jamais réutilisés entre appareils. L'OTA vérifie le SHA-256 avant flash. Le fournisseur d'identité tourne comme composant séparé (Rauthy), donc l'identité reste gérable et auditable indépendamment du serveur de la plateforme.

On this page