Repository navigation
FR Bridge Securite
English · Type : explication · Public : administrateur · Contexte : Reality Bridge · Statut : prête · S'applique depuis : 4.2.5
Le Reality Bridge, profil
local(SQLite) · contrat : stable · vérifié par la CI (matrice de support de la 4.4.0, 2026-10-23) : Node 22.13+ avec node:sqlite, sous Ubuntu ; Chromium et WebKit de bout en bout · vérifications automatiques : jobsreality,reality-xcheck; plan de CIreality; 2 fichiers de tests · passes humaines : aucune · limites connues :bridge-rotation-resets-run-quotaLe Reality Bridge, profil
distributed(Postgres) · contrat : expérimental · vérifié par la CI (matrice de support de la 4.4.0, 2026-10-23) : Postgres 16 en CI ; Le profil de qualification : HTTPS, trois instances (ops/qualify/) · vérifications automatiques : jobsbridge-postgres,deployment-smoke; plan de CIreality; 1 fichier de tests · passes humaines :bridge-postgres-httpspas faite (#77) · limites connues :bridge-rotation-resets-run-quotaLes connecteurs (email, Telnet, SSH, Open Badges) · contrat : expérimental · vérifié par la CI (matrice de support de la 4.4.0, 2026-10-23) : Node 22 sous Ubuntu (Telnet et SSH pas sous Windows) · vérifications automatiques : jobs
connectors,connector-archive-e2e; plan de CIreality; 3 fichiers de tests · passes humaines :connectors-real-securitypas faite (#79) · limites connues :bridge-rotation-resets-run-quota
Comment le Reality Bridge décide qui peut proposer un signal, comment un joueur sait que le Bridge l’a accepté, quels secrets existent et où ils vivent, comment les clés tournent et les jetons sont révoqués, et que faire quand l’un d’eux fuit. Deux mécanismes, chacun pour une question : Biscuit dit qui peut proposer quoi, sur le Bridge seulement ; une signature Ed25519 dit ce que le Bridge a accepté, vérifiée par le joueur.
| Secret | Où | Qui le lit | S’il fuit |
|---|---|---|---|
| La moitié privée de la racine Biscuit |
root.key (mode 600) |
grant seulement ; serve ne la charge jamais |
N’importe qui peut fabriquer des jetons de connecteur : une nouvelle racine |
| La clé qui signe les événements (Ed25519) |
config.json (eventKey, mode 600) |
serve |
N’importe qui peut signer des signaux que les joueurs acceptent : la faire tourner |
| Le jeton de l’opérateur |
admin-token (seule copie en clair) ; son SHA-256 dans config.json
|
revoke, les routes /v1/admin/*
|
N’importe qui peut exporter, supprimer et révoquer. Aucune commande ne le remplace seul : en créer un avec init dans un dossier de travail, copier son adminTokenHash dans config.json et son admin-token à côté, redémarrer |
| Le Biscuit d’un connecteur | le tokenFile du connecteur |
ce connecteur | Le révoquer par son id, en donner un nouveau |
| Le secret et le jeton d’un webhook de démonstration |
config.json (webhooks) |
serve |
Refaire init, ou ne jamais les activer sur Internet (--no-demo-webhooks) |
| La capacité d’un joueur | le navigateur du joueur (localStorage) ; son SHA-256 sur le Bridge |
la page du joueur | Révoquer le lien du joueur |
| L’URL et le mot de passe de la base |
BRIDGE_STORE ou BRIDGE_STORE_FILE, jamais le fichier |
serve et les commandes |
Changer le mot de passe |
Gardez root.key là où le Bridge n’est pas, si vous le pouvez : un Bridge qui ne fait que servir n’en a pas besoin.
Le jeton d’un connecteur est fabriqué par grant à partir de la clé racine. Son bloc d’autorité nomme le jeu, les
sources, les signaux, les joueurs (ou n’importe quel joueur du jeu), l’audience du Bridge, le tenant (depuis la
4.1.10), s’il peut confirmer des codes d’appairage (--pair), et une expiration à la seconde (refusé à partir de
cette seconde). Le Bridge vérifie chaque proposition contre
bridge/policy/propose.datalog, et chaque confirmation d’appairage contre
pair.datalog, en 200 ms au plus de temps d’exécution Biscuit.
- Un connecteur peut atténuer son propre jeton (moins de joueurs, une expiration plus proche) avant de le transmettre ; il ne peut jamais l’élargir.
- Chaque jeton a des identifiants de révocation ; révoquer n’importe lequel fait refuser le jeton.
- L’ordre des vérifications ne trahit rien : le manifeste répond d’abord (il est public), puis le jeton, avant qu’aucun joueur ne soit nommé, si bien qu’un appelant sans jeton valide n’apprend rien des joueurs qui existent (un 401 ou un 403 avant toute 404).
- Une adresse qui rate 60 authentifications en une minute voit toutes ses requêtes refusées pendant un moment (429).
- Biscuit dit seulement qui peut proposer. Un connecteur vérifie lui-même ce qu’il affirme : la preuve d’un badge, l’expéditeur d’un e-mail.
Garantie ·
bridge-bad-token-before-player· référencée · portée vérifiée par son auteur (IA), pas relue Un mauvais jeton est refusé avant qu’un joueur inconnu ne soit nommé, et une adresse à court d’authentifications ratées attend.
Le Bridge signe chaque signal accepté sous forme de JWS compact (alg: EdDSA, un kid qui nomme la clé). Le joueur
vérifie la signature sur les octets exacts qu’il a reçus avant de les lire, puis la taille, la fenêtre de la clé, le
jeu, le joueur, le manifeste et l’expiration, avec cinq minutes de tolérance pour l’horloge de l’appareil. Le joueur
ne détient ni Biscuit ni clé : seulement une capacité qui lit et acquitte ses propres signaux.
Un WorldSignalV2 nomme aussi son tenant, son environnement, l’origine depuis laquelle le joueur s’est appairé, le
lien et la clé, si bien qu’un signal signé pour un contexte est refusé dans un autre (audience-mismatch), même quand
deux tenants ont reçu (à tort) la même clé. Un Bridge qui sert plusieurs tenants ne signe que du V2, et liste ses
clés avec leur tenant ; un Bridge à un seul tenant signe du V1 sauf si sa configuration dit signalVersion: 2
(init --signal-version=2). Le joueur accepte les deux par défaut. Chaque champ :
Signaux et manifeste.
Le webhook de démonstration (POST /v1/hooks/<source>) s’authentifie autrement : un HMAC-SHA256 du corps brut sous
le secret du webhook, dans X-Web-Scumm-Signature: sha256=<hex>, comparé en temps constant.
La clé d’événements signe chaque signal avec son kid ; le joueur fait confiance aux clés que liste GET /v1/keys,
chacune dans sa fenêtre.
# dans le dépôt du moteur (le dossier du Bridge)
npm run bridge -- rotate --keep-days=30rotate crée une nouvelle clé et garde l’actuelle dans previousKeys pendant --keep-days jours ; redémarrez
ensuite le Bridge. Dès lors, le Bridge livre chaque signal sous sa clé actuelle : ce qui attendait un joueur est
re-signé à la livraison (le stockage garde la charge telle qu’acceptée), et un joueur dont le lien était ouvert
redemande les clés une fois quand un signal en nomme une qu’il ne connaît pas. --keep-days n’a qu’à couvrir un
joueur qui a reçu un signal juste avant la rotation et le vérifie après.
Limite ·
bridge-rotation-resets-run-quotaLes compartiments de débit des classements de speedrun sont indexés par un HMAC dérivé de la clé d’événements : la faire tourner fait repartir chaque client sur un nouveau compartiment (au plus une minute de quota en plus).
La clé racine Biscuit fabrique les jetons des connecteurs : la faire tourner, c’est redonner un nouveau jeton à chaque connecteur.
# dans le dépôt du moteur, le Bridge en marche
npm run bridge -- revoke --url=<bridge> --player=<p-…> # le lien d'un joueur
npm run bridge -- revoke --url=<bridge> --token=<revocation id> # le jeton d'un connecteur (n'importe lequel de ses ids de révocation)Les flux d’un joueur révoqué se terminent aussitôt, et ses propositions sont refusées, y compris une qui chevauche la
révocation (le joueur et le jeton sont revérifiés dans l’étape par joueur). Un joueur peut révoquer son propre lien :
« Unlink » dans le menu pause (POST /v1/unlink). Une capacité finit aussi d’elle-même : 30 jours après son dernier
accusé de réception, 180 jours au plus après son appairage.
La clé d’événements d’un tenant compromise. rotate --dir=<tenant>, puis retirez la clé compromise de
previousKeys dans le config.json de ce tenant, et redémarrez ses instances. Les joueurs refusent dès lors ce
qu’elle a signé ; ce qui les attend est re-signé sous la nouvelle clé à la livraison. Les autres tenants ne sont pas
touchés.
Une racine Biscuit compromise. init --force en fait trop. Créez une nouvelle racine (init dans un dossier de
travail, copiez son biscuitRoot dans config.json et son root.key), redonnez un jeton à chaque connecteur du
tenant, redémarrez, puis révoquez les identifiants des anciens jetons (revoke --token=) pour la période d’avant le
redémarrage.
Une instance ou son hôte compromis. Elle détenait toutes les clés privées des tenants qu’elle servait : faites
tourner la clé d’événements de chaque tenant, créez à chaque tenant une nouvelle racine, changez le mot de passe de la
base et les jetons d’opérateur, et lisez la quarantaine du stockage (doctor) et les journaux depuis la
compromission.
La rotation, en routine. Par tenant, à son rythme : rotate --dir=<tenant> --keep-days=30, puis redémarrez ses
instances une à une (les autres continuent de servir ; le stockage est le même).
Un signal passe par trois mains : un connecteur, le Bridge, le navigateur du joueur. Chacune doit pouvoir vérifier la précédente, et un jeton volé à une étape ne doit pas ouvrir les autres. Le Biscuit d’un connecteur n’ouvre rien côté joueur ; la capacité d’un joueur ne propose rien ; la clé d’événements signe mais n’autorise aucun connecteur.
- COSE pour le signal signé : un décodeur CBOR dans le joueur, pour rien qu’un JWS n’offre déjà ici ; la solution de repli si un navigateur pris en charge abandonne Ed25519 (D15).
- Biscuit dans le joueur : le joueur détient plutôt une capacité étroite (D16).
- Des clés par tenant dérivées par construction : le joueur ne peut pas vérifier une dérivation dont il ne détient pas la racine ; un signal nomme plutôt son contexte (ADR 0010).
Deux formats à maintenir, des clés à faire tourner, et une vérification croisée de Biscuit en Rust dans la CI
(npm run reality:xcheck : la version WebAssembly utilisée en production et la crate Rust doivent s’accorder sur les
exemples officiels de Biscuit). Une capacité dans un navigateur est lisible par tout script de la page : elle est
étroite (lire et acquitter les signaux d’un joueur) et finit d’elle-même, mais ce n’est pas un secret que la page peut
se cacher à elle-même.
-
ADR 0007 : Biscuit autorise un connecteur ; une signature
atteste un événement (D15, D16 ; les mesures dans
docs/dev/reality-spike.md). - ADR 0010 : un signal signé nomme le contexte pour lequel il a été signé.
- Les modèles de menace :
docs/dev/THREAT-MODEL.md,docs/dev/threat-models/constellation.md. - Le code :
bridge/src/biscuit.ts,bridge/src/policy.ts,bridge/src/sign.ts,src/engine/reality/protocol.ts.
Generated publication · publication générée — current documentation · documentation courante: quest'n'click 4.4.0 r1, documentation revision · révision de la documentation 8dc717c69acb62c4742b69c54a7e7dfb620f75d8.
Earlier versions and permalinks · versions précédentes et liens permanents: https://wanoo.github.io/quest-n-click/docs/
An edit made in this wiki is overwritten at the next publication · une modification faite dans ce wiki est écrasée à la publication suivante.
Edit the source · Modifier la source · Report an error · Signaler une erreur
Index (6)
Démarrer (6)
Jouer (9)
Installer (17)
- Prérequis
- Installer depuis l'archive de la release
- Installer depuis npm, une fois publié
- Installer depuis le dépôt pour travailler sur le moteur
- doctor : vérifier sa machine
- Outils pour les images
- Outils audio : ffmpeg, fluidsynth, Furnace, uv
- Mettre à jour
- Notes de mise à jour : 4.4
- Notes de mise à jour : 4.3
- Notes de mise à jour : 4.2
- Notes de mise à jour : 4.1
- Notes de mise à jour : v3
- Notes de mise à jour : v2 vers v3
- Venir de web-scumm
- Référence du paquet
- Dépannage de l'installation
Créer un jeu (55)
- Votre premier jeu
- Organisation d'un projet créé et du dépôt du moteur
- La boucle de travail
- Principes du contenu et contrat d'identité
- Salles
- Géométrie : placement, zones de marche, masques, calques, caméra
- Un objet est visible mais inaccessible
- Réactions
- Commandes du jeu
- Conditions
- Drapeaux, variables et événements : lequel choisir
- Code sur mesure
- Personnages
- Objets et inventaire
- Carte du monde
- Dialogues
- Scripts et événements
- Faire bouger les personnages : patrouilles et marches
- Sorties
- Chapitres et objectifs
- État et sauvegardes : persistant ou temporaire, renommer un id, migrations
- Mise en scène : salles larges et animations
- La scène : lumières, particules, sols
- Habillage et polices
- Titre et générique
- Héros : plusieurs personnages et choix du héros
- Mini-jeux intégrés
- Un mini-jeu sur mesure
- Traductions
- Musique en stems
- Bruitages
- Voix : par langue et par héros
- Images : prompts, découpe des planches, transparence, échelle
- Provenance des assets
- Le storyboard, source de vérité
- Concevoir des énigmes
- Mécaniques classiques
- Pages de relecture
- Référence du DSL
- Vérifier son jeu
- Ce que prouve le solveur
- Options du solveur
- Playtests
- Remix
- Speedrun pour les auteurs
- La fin scellée
- Modèle de production
- Jeux d'exemple
- Travailler avec une IA
- Construire et publier un jeu
- Publier sur GitHub Pages
- Le jeu statique sur Clever Cloud
- Autres hébergeurs statiques
- Sous-chemin, cache et service worker, déploiement incomplet, retour arrière
- Tester sur un téléphone en réseau local
Studio local (24)
- Le Studio local : présentation et lancement
- Modifier une salle
- Placement et scène
- Aperçu, application, annulation
- Storyboard
- Assets et découpe
- Vérifier : validateur, solveur, chemin critique, chaleur, santé, couverture
- Captures avant et après
- Diff sémantique
- Jouer et rejouer une session
- Explication des règles
- Langue et objectifs
- Voix et matrice héros × langues
- Carte
- Notes
- Remix dans le Studio
- Musique
- L'Assistant : votre propre clé
- Mode démo et studio-apply
- Sécurité en local et en réseau local
- API du Studio
- MCP local : configurer chaque client
- Outils MCP
- Dépannage du Studio
Studio d'équipe (21)
- Ce qui tourne où
- Concepts : révision, transaction, pan, bail, aperçu, build
- Débuter comme dev invité
- Travailler ensemble au quotidien
- Rôles et actions : la matrice complète
- Invitations
- Gérer les membres
- Écriture refusée sur le Studio hébergé : révision de base périmée (409)
- Écriture refusée sur le Studio hébergé : pan verrouillé (423)
- Reprendre après un bail perdu, une déconnexion ou un aperçu périmé
- Commentaires
- Carte et diff sémantique
- Publier des builds
- MCP distant
- Le jeu dans Git
- Mettre à jour le moteur d'un jeu hébergé
- Assets hébergés
- La ligne de commande du serveur
- Routes de santé et de métriques
- L'essayer en local avec compose.yml
- Limites connues
Clever Cloud (23)
- Clever Cloud : vue d'ensemble, coût et prérequis
- Applications et add-ons
- Domaines et TLS
- Keycloak
- OIDC générique et GitLab
- Configuration
- Premier déploiement
- Sauvegardes
- RPO et RTO
- Exercice de restauration sur une installation vierge
- Redéploiements
- Mettre à jour le serveur et vérifier l'état après une migration
- Limites du retour arrière
- Supervision
- Sécurité
- Renouveler les clés
- Récupérer l'accès administrateur
- Changer de domaine ou de fournisseur OIDC
- Worker, PostgreSQL ou Cellar en panne
- Exporter le jeu et quitter l'installation
- Rétention
- Dépannage sur Clever Cloud
- Coûts et démontage
Reality Bridge et connecteurs (16)
- Le Reality Bridge : ce que c'est
- Démarrage rapide
- Garanties
- Installer et lancer le Bridge
- Sauvegarde et restauration
- Plusieurs tenants
- Signaux et manifeste
- La ligne de commande du Bridge
- Vue d'ensemble des connecteurs
- Connecteur email
- Connecteur Telnet
- Connecteur SSH
- Connecteur Open Badges
- Le SDK des connecteurs
- Sécurité : Biscuit et signatures
- Dépannage du Bridge et des connecteurs
Contribuer (25)
- Bienvenue
- Préparer son poste de développement
- La boucle de développement
- Avant une pull request
- Conventions
- Écrire la documentation
- Architecture
- Visite du code
- Frontières d'import
- Quels tests tournent quand
- Tests de mutation
- Le cliquet de couverture
- La référence de qualité
- La CI expliquée
- Passes de terrain
- Mesures de performance
- Publier une release
- Paquets npm
- Décisions et ADR
- Gouvernance
- Pull requests des bots
- Contribuer avec des agents IA
- Politique de sécurité
- Code de conduite
- Licences et crédits