Présentation de Glyph Protocol pour les terminaux
(rapha.land)- Un nouveau protocole de terminal apparaît pour résoudre le problème historique qui imposait d’installer des polices patchées (comme Nerd Font) afin de rendre des icônes personnalisées dans les applications terminales
- Glyph Protocol permet à une application d’enregistrer directement des glyphes vectoriels dans le terminal à l’exécution et d’interroger la possibilité de rendu d’un point de code donné
- Les données de glyphe utilisent le format
glyfde TrueType, ce qui permet d’exploiter tel quel le rasterizer déjà présent dans le terminal, sans nouvelle dépendance - Les points de code enregistrables sont limités à la Unicode Private Use Area (PUA), ce qui bloque à la racine les attaques de phishing et d’usurpation visuelle
- Une première implémentation est en cours dans Rio terminal, et des exemples de code pour les principaux frameworks TUI comme Bubble Tea, Ratatui et Ink sont déjà publiés
Problème initial : la dépendance aux polices patchées
- Pour que les éditeurs de terminal, prompts et TUI affichent correctement des icônes, l’utilisateur doit installer lui-même des polices patchées comme Nerd Font ou Powerline
- Sans ces polices, des tofu (□) s’affichent à la place des icônes, et ces polices patchées pèsent lourd, entre 6 et 12 Mo chacune
- JetBrainsMono Nerd Font Regular fait environ 7,8 Mo, FiraCode Nerd Font Regular environ 10,4 Mo, et l’archive complète de symboles environ 60 Mo
- Les développeurs d’applications n’ont aucun moyen de distribuer directement les glyphes souhaités et doivent supposer que l’utilisateur dispose de la bonne police, de la bonne version et du bon mapping de points de code
Fonctions clés de Glyph Protocol
- Deux opérations centrales sont prises en charge
- Enregistrement de glyphes personnalisés : l’application choisit un point de code Unicode PUA et envoie directement au terminal son contour vectoriel pour l’enregistrer à l’exécution
- Interrogation de point de code : permet de savoir si un point de code donné est couvert par la police système, par un enregistrement de session, par les deux, ou par aucun des deux
- Si l’utilisateur a déjà installé Nerd Font, l’application peut éviter d’envoyer le glyphe après interrogation ; sinon, elle peut envoyer directement le contour pour que l’icône s’affiche correctement
Structure du protocole
Mode de transport (Transport)
- Utilise APC (Application Program Command) au lieu d’OSC
- APC a été conçu pour les commandes définies par les applications, et les terminaux qui ne l’implémentent pas peuvent ignorer la séquence sans risque
- OSC partage un espace de noms global utilisant un entier décimal unique comme identifiant de commande, avec un risque de collision ; APC n’a pas ce problème grâce à sa propre structure d’identification
Identifiant (Identifier)
- Tous les messages de Glyph Protocol sont préfixés par le point de code
25a1(U+25A1, WHITE SQUARE)- Ce caractère est le symbole standard du tofu affiché par les terminaux lorsqu’un glyphe manque
- Format de framing :
ESC _ 25a1 ; <verb> [ ; key=value ]* [ ; <payload> ] ESC \\ - Quatre verbes :
s(support),q(query),r(register),c(clear)
Support (s) : vérifier la prise en charge par le terminal
- Sert à vérifier quels formats de payload et quelles versions du protocole le terminal prend en charge
- C’est aussi la méthode standard pour détecter la présence même de Glyph Protocol
- Le
fmtde la réponse est un bitfield, chaque bit représentant un format de payload1=glyf: glyphe simple TrueType, obligatoire en v12=colrv0: glyphe couleur plat à couches (OpenType COLR v0), ajouté en v1.24=colrv1: graphe de peinture complet avec dégradés et transformations (OpenType COLR v1), ajouté en v1.2
- Une réponse confirme la prise en charge du protocole ; un timeout signifie absence de prise en charge ;
fmt=0signifie que le protocole est implémenté mais sans format pris en charge (défini pour l’exhaustivité)
Query (q) : interroger la possibilité de rendu d’un point de code
- Permet de demander si un point de code donné peut être rendu, avec une réponse dans
status0(free) : rien n’est rendu, affichage tofu1(system) : couvert par la police système2(glossary) : couvert par un enregistrement de la session3(both) : couvert par les deux, l’enregistrement remplaçant le rendu de la police système
- Si l’icône est déjà présente dans le système, le TUI peut sauter l’enregistrement ; sinon, il peut enregistrer un point de code personnalisé pour obtenir un fallback élégant
Register (r) : enregistrer un glyphe
- L’application choisit un point de code PUA et l’enregistre en envoyant un contour
glyfencodé en base64 - Principaux paramètres
cp: point de code cible (hex), qui doit impérativement appartenir à l’une des 3 plages Unicode PUA (U+E000–U+F8FF,U+F0000–U+FFFFD,U+100000–U+10FFFD) ; hors plage, la requête est rejetée avecreason=out_of_namespacefmt: format de payload ; en v1, seulglyfest défini et c’est la valeur par défaut, donc il peut généralement être omisupm: units per em, qui définit l’espace de coordonnées du contour ; valeur par défaut 1000
- Envoyer un second
rpour le mêmecpécrase l’enregistrement précédent - En cas d’erreur (point de code non PUA, payload invalide, glyphe composite, etc.), la réponse est
status=<nonzero>; reason=<code>
Pourquoi avoir choisi le format glyf
Pourquoi du vectoriel
- Un glyphe n’est pas une image, il n’a donc pas de résolution fixe : une même icône doit pouvoir être rendue aussi bien dans un TUI dense en 12 px que sur un écran HiDPI en 24 px
- Un glyphe raster est figé à une résolution donnée et sera flou sur un écran HiDPI ou illisible dans une petite cellule
Pourquoi précisément glyf
- Tous les terminaux capables de rendre du texte embarquent déjà un rasterizer
glyf(FreeType, swash, ttf-parser, fontdue, allsorts, etc.) - Adopter Glyph Protocol n’ajoute donc absolument aucune nouvelle dépendance côté terminal
- Adopter SVG imposerait d’ajouter
resvgou d’écrire un nouveau parseur XML + path - La taille sur le fil est aussi plus faible : une icône standard tient dans 150 à 400 octets de données
glyf, soit 2 à 3 fois moins qu’un SVG équivalent (même en comptant le surcoût du base64)- Pour 50 icônes enregistrées, cela représente environ 13 Ko contre 35 Ko, une différence sensible via un pipe tmux ou une liaison SSH mobile
Brève explication de glyf
- Un enregistrement
glyfstocke un glyphe comme un ensemble de contours fermés (contours) - Chaque point porte 1 bit de métadonnée : on-curve ou off-curve
- deux points on-curve consécutifs → ligne droite
- un point off-curve entre deux points on-curve → courbe de Bézier quadratique
- deux points off-curve consécutifs → présence implicite d’un point on-curve intermédiaire (astuce de compression)
- Les coordonnées sont des positions entières dans la grille du carré EM ; avec
upm=1000,(500, 900)signifie demi-largeur et 90 % de hauteur - Un triangle fermé prend environ 30 octets, une icône à 30 points environ 200 octets
Le sous-ensemble glyf défini par le protocole
- Seuls les glyphes simples sont autorisés : pas de glyphes composites, pas de référence à d’autres glyphes, pas de contexte au niveau de la police
- Utilise l’encodage standard des flags défini dans la spécification OpenType
- Pas d’instructions de hinting : le hinting suppose un ensemble global de valeurs de contrôle pour la police, ce qui n’existe pas ici
- L’espace de coordonnées est défini par
upm, avec une valeur par défaut de 1000, modifiable par enregistrement
Couleur, mise à l’échelle et création
- Les contours
glyfne contiennent pas d’information de couleur et sont rendus avec la couleur de premier plan courante, comme dans le cas d’héritage de Nerd Font - Les glyphes couleur sont pris en charge via des formats de payload séparés :
fmt=colrv0/fmt=colrv1 - La valeur
upmdéfinit l’espace de coordonnées du glyphe, que le terminal mappe ensuite à la cellule au moment du rendu → inutile de réenregistrer lors d’un changement de taille de police - Dans la pratique, la plupart des développeurs n’écriront pas eux-mêmes les octets
glyfmais les convertiront depuis du SVG au build : l’interfacettx/pensdefonttoolspeut être utilisée, et un helpersvg2glyfsera distribué avec l’implémentation de référence de Rio
Durée de vie et capacité
- Chaque session de terminal possède un glossary pouvant contenir jusqu’à 1024 enregistrements simultanés, indexés par point de code dans les 3 plages PUA
- Les enregistrements restent valides pendant toute la durée de la session
- Lors de l’enregistrement du 1025e glyphe, le plus ancien enregistrement est expulsé selon un ordre FIFO → pas d’erreur « glossary full »
- Les applications qui ne peuvent tolérer une éviction silencieuse doivent interroger le point de code avant l’affichage
Exemple concret : enregistrer une icône dans un PUA vide
- Exemple de pipeline complet pour enregistrer un contour stylisé dans
U+100000(premier point de code de Supplementary PUA-B) - Utilise
fontToolscomme convertisseur SVG→glyf - Après dessin du contour avec
TTGlyphPen, le résultat est encodé enbase64, envoyé dans une séquence APC, puis le point de code correspondant est affiché - Pour une icône standard de 20 points, le payload
glyffait environ 150 octets, soit environ 250 octets avec l’enveloppe APC et le base64 - Un helper
svg2glyfsera fourni pour les développeurs disposant déjà d’assets SVG → enregistrement en 2 lignes
Option pour les enregistrements en masse : reply=
- Par défaut, le terminal envoie un ACK pour chaque
r, mais dans un hook de démarrage qui enregistre 100 glyphes, cela produit 100 ACK mis en file dans le PTY et affichés comme du bruit dans le shell - Contrôle en 3 niveaux
reply=1(par défaut) : réponse sur succès comme sur échec, pour les enregistrements unitaires interactifsreply=2: réponse uniquement en cas d’échec, succès silencieux, utile lorsqu’on veut détecter seulement les erreurs lors d’un enregistrement massifreply=0: aucune réponse, mode fire-and-forget, utile quand aucun processus ne lira les réponses, comme dans un hook de démarrage
- Les valeurs inconnues retombent automatiquement sur
reply=1, ce qui préserve la compatibilité descendante pour les extensions futures
Clear (c) : désenregistrer
- Utile pour restaurer le comportement par défaut du terminal à la fermeture d’un éditeur, lors d’un changement de thème TUI, ou pour le débogage
- Libérer un seul slot : spécifier un point de code via le paramètre
cp - Libérer tout le glossary : omettre
cp - Libérer un slot vide n’est pas une erreur mais un no-op, avec réponse
status=0 cpdoit appartenir à une plage PUA ; hors plage, le terminal renvoiereason=out_of_namespace
Fonctions volontairement exclues de la v1
- Impossible d’enregistrer des points de code hors PUA : limitation stricte aux 3 plages Unicode PUA
- Pas de ligatures : l’enregistrement ne s’applique qu’à un seul point de code ; le remplacement de séquences de touches est hors du périmètre de la v1 ; les ligatures de programmation (
->→⟶) sont déjà gérées par les polices OpenType - Pas de persistance entre sessions : les glyphes sont renvoyés à chaque exécution, afin d’éviter que le terminal ne devienne un cache de polices
- Pas de partage inter-applications : chaque session de terminal possède son propre glossary, sans IPC ni démon
- Pas de glyphes couleur dans le payload
glyfde la v1 : rendu en couleur de premier plan, la couleur étant séparée danscolrv0/colrv1à partir de la v1.2 - Ces fonctions pourront être ajoutées plus tard si nécessaire, mais elles sont volontairement exclues car une fois ajoutées, elles sont difficiles à retirer
Justification de sécurité de la limitation au PUA
- La restriction au PUA n’est pas une question d’esthétique d’API, mais une propriété qui rend le protocole sûr même s’il est activé par défaut
- Si l’enregistrement de points de code arbitraires était autorisé, on pourrait enregistrer un glyphe en forme de
osurU+0061(a) pour faire apparaîtrebad.comcommebod.com- Le buffer de cellules contiendrait toujours
bad.com, donc le copier-coller resterait fidèle aux octets, mais ce que l’utilisateur lit serait faux - Cela créerait une primitive de phishing dans tous les programmes de terminal, avec effet persistant sur les programmes lancés ensuite dans la même session
- Le buffer de cellules contiendrait toujours
- En limitant au PUA, cette attaque devient mécaniquement impossible : les utilisateurs ne saisissent pas de points de code PUA, et les noms de fichiers, URL, commandes, noms de variables ou logs n’en contiennent pas
- Le protocole impose au niveau protocolaire le modèle de confiance déjà établi par l’usage de Nerd Font (les glyphes personnalisés n’existent que dans des plages réservées et ne peuvent pas se superposer au texte réel)
- Autres propriétés de sécurité
- Le buffer de cellules fait autorité : sélection, copie, recherche, détection de liens, historique du shell, etc. doivent renvoyer les points de code réellement émis par l’application, empêchant les pièges du type « ce que vous voyez n’est pas ce que vous copiez »
- Isolation par session : deux onglets peuvent enregistrer indépendamment des icônes de branche différentes sur
U+E0A0, sans qu’un enregistrement dans un onglet n’affecte le rendu dans l’autre
Comparaison avec les approches existantes
Kitty Image Protocol (KIP) + Unicode Placeholders
- Il est possible d’approcher Glyph Protocol via les Unicode placeholders de KIP, mais l’intégration est complexe et les terminaux qui implémentent ces placeholders se limitent à Kitty, Ghostty et Rio
- KIP est un protocole d’image, alors qu’un glyphe n’est pas une image
- Coût à l’usage : un glyphe réutilisé 200 fois à l’écran (bordures de tableau, puces, etc.) nécessite 200 références d’image, avec un coût de layout et de composition. Avec Glyph Protocol, une fois le point de code enregistré, le rendu se fait à la vitesse d’une police
- Pas de résolution native : un contour
glyfn’a pas de taille en pixels et s’adapte automatiquement aux changements de taille de police. KIP envoie un bitmap à une taille donnée, qui devient flou ou doit être renvoyé en cas de redimensionnement, sans mécanisme de détection du changement de taille de police - Héritage de la couleur de premier plan : un contour
glyfmonochrome est rendu avec la couleur de premier plan de la cellule, donc le thème s’applique automatiquement. Une image garde ses propres pixels et ne participe pas à la coloration du texte
DEC DECDLD / DRCS
- Introduit sur le VT220 en 1983 sous le nom de Dynamically Redefinable Character Sets, ce mécanisme ressemble formellement à Glyph Protocol
- Deux problèmes majeurs
- Approche bitmap : on téléverse une grille de pixels adaptée à la taille actuelle de cellule du terminal ; en cas de changement de taille de police, de passage au HiDPI ou à un écran 4K, les pixels en blocs sont agrandis ou réduits. Une approche adaptée à l’ère des CRT fixes en 10×20, mais inadaptée aux tailles de cellules variées d’aujourd’hui
- Aucune restriction d’espace de noms : DECDLD peut écraser des jeux de caractères mappables dans la plage GL (celle où se trouvent a, b, c), permettant à un programme non fiable de redéfinir le rendu de
a→ c’est la principale raison pour laquelle les terminaux modernes hésitent à activer DECDLD
État de l’implémentation dans Rio terminal
- Glyph Protocol est déjà utilisable sur la branche main de Rio terminal, avec une intégration officielle prévue en mai → première implémentation
- La spécification complète sera publiée avec la release, avec du code d’exemple pour l’enregistrement de glyphes et l’interrogation du terminal
- Des exemples fonctionnels sont disponibles dans le dépôt raphamorim/glyph-protocol-examples, avec des intégrations de démonstration pour Bubble Tea, Ratatui et Ink
- Le protocole peut encore évoluer ; à mesure que davantage d’applications et de terminaux l’adopteront, la forme des messages, les réponses aux requêtes et certains edge cases pourront changer → il faut actuellement le considérer comme une cible mouvante et verrouiller la version implémentée
- Les auteurs espèrent une adoption par d’autres émulateurs de terminal, les bénéfices pour tout l’écosystème étant importants tandis que le périmètre d’implémentation reste volontairement réduit
Questions ouvertes pour la communauté
- La notification de changement de taille de police doit-elle faire partie du protocole ? : Glyph Protocol lui-même évite ce problème grâce à ses contours indépendants de la résolution, mais un TUI qui combine images et glyphes n’a aucun moyen de détecter un changement de métriques de cellule autrement qu’en pollant → discussion ouverte sur l’inclusion éventuelle d’une notification
resizeoumetrics-changed - Existe-t-il une manière responsable d’autoriser l’enregistrement hors PUA ? : la règle PUA-only garantit la sécurité par défaut, mais elle bloque des cas d’usage comme un IME CJK envoyant des glyphes pour des sinogrammes non couverts, ou des outils linguistiques qui veulent surcharger certains glyphes → appel à idées sur une solution possible via opt-in explicite au niveau utilisateur, fonctionnalité signée, drapeau de source de confiance, etc., sans rouvrir la porte au phishing
Aucun commentaire pour le moment.