Introducción a Glyph Protocol para terminales
(rapha.land)- Surge un nuevo protocolo para terminales para resolver el problema tradicional de que, para renderizar íconos personalizados en aplicaciones de terminal, había que instalar fuentes parcheadas (como Nerd Font)
- Glyph Protocol permite que las aplicaciones registren glifos vectoriales directamente en la terminal en tiempo de ejecución y consulten si se puede renderizar un codepoint específico
- Los datos de glifos usan el formato
glyfde TrueType, por lo que se puede implementar aprovechando el rasterizador que la terminal ya tiene, sin nuevas dependencias - Restringe los codepoints registrables al Unicode Private Use Area (PUA) para bloquear de raíz ataques de phishing y suplantación visual
- La primera implementación está en marcha en Rio terminal, y ya se publicaron ejemplos de código para frameworks TUI importantes como Bubble Tea, Ratatui e Ink
Problema actual: dependencia de fuentes parcheadas
- Para que editores de terminal, prompts y TUIs muestren íconos correctamente, el usuario debe instalar manualmente fuentes parcheadas como Nerd Font o Powerline
- Si no instala la fuente, en lugar del ícono aparece tofu (□), y las fuentes parcheadas son bastante pesadas, de alrededor de 6 a 12 MB cada una
- JetBrainsMono Nerd Font Regular pesa unos 7.8 MB, FiraCode Nerd Font Regular unos 10.4 MB, y el archivo completo de símbolos ronda los 60 MB
- Los desarrolladores de aplicaciones no tienen una forma de distribuir directamente los glifos que quieren, así que solo pueden asumir que el usuario tiene la fuente correcta, la versión correcta y el mapeo correcto de codepoints
Funciones clave de Glyph Protocol
- Soporta dos operaciones principales
- Registro de glifos personalizados: la aplicación elige un codepoint Unicode PUA y envía directamente el contorno vectorial a la terminal para registrarlo en tiempo de ejecución
- Consulta de codepoints: permite consultar si un codepoint específico está cubierto por la fuente del sistema, por un registro dentro de la sesión, por ambos o por ninguno
- Si el usuario ya tiene Nerd Font instalada, la aplicación puede omitir el envío del glifo mediante una consulta; si no la tiene, puede enviar directamente el contorno para que el ícono se muestre correctamente
Estructura del protocolo
Método de transporte (Transport)
- Usa APC (Application Program Command) en lugar de OSC
- APC fue diseñado para comandos definidos por aplicaciones, y las terminales que no lo implementen pueden ignorar con seguridad esa secuencia
- OSC comparte un namespace global usando un solo entero decimal como identificador de comando, por lo que hay riesgo de colisiones; APC evita este problema gracias a su propia estructura de identificación
Identificador (Identifier)
- Todos los mensajes de Glyph Protocol llevan como prefijo el codepoint
25a1(U+25A1, WHITE SQUARE)- Ese carácter es el símbolo estándar de tofu que dibuja la terminal cuando falta un glifo
- Formato de framing:
ESC _ 25a1 ; <verb> [ ; key=value ]* [ ; <payload> ] ESC \\ - Hay 4 verbos:
s(support),q(query),r(register),c(clear)
Support (s): verificar soporte de la terminal
- Sirve para comprobar qué formatos de payload y versiones del protocolo soporta la terminal
- También es la forma estándar de detectar la existencia misma de Glyph Protocol
- El
fmtde la respuesta es un bitfield, donde cada bit representa un formato de payload1=glyf: glifo simple de TrueType, obligatorio en v12=colrv0: glifo en color plano por capas (OpenType COLR v0), añadido en v1.24=colrv1: grafo completo de pintura con gradientes y transformaciones (OpenType COLR v1), añadido en v1.2
- Si llega respuesta, se confirma soporte del protocolo; si hay timeout, no hay soporte; si
fmt=0, el protocolo está implementado pero sin soporte de formatos (definido por completitud)
Query (q): consultar si un codepoint puede renderizarse
- Consulta si un codepoint específico puede renderizarse y responde con un valor
status0(free): no renderiza nada, muestra tofu1(system): lo cubre la fuente del sistema2(glossary): lo cubre un registro de la sesión3(both): ambos lo cubren, y el registro sobrescribe al renderizar la fuente del sistema
- Si la TUI detecta que el sistema ya tiene ese ícono, omite el registro; si no, puede registrar un codepoint personalizado para tener un fallback elegante
Register (r): registro de glifos
- La aplicación elige un codepoint PUA y lo registra enviando un contorno
glyfcodificado en base64 - Parámetros principales
cp: codepoint objetivo (hex), debe estar dentro de uno de los 3 rangos Unicode PUA (U+E000–U+F8FF,U+F0000–U+FFFFD,U+100000–U+10FFFD); si está fuera de rango, se rechaza conreason=out_of_namespacefmt: formato del payload; en v1 solo se defineglyfy además es el valor por defecto, así que normalmente se puede omitirupm: units per em, define el espacio de coordenadas del contorno; el valor por defecto es 1000
- Si se envía un segundo
rpara el mismocp, sobrescribe el registro anterior - En caso de error (codepoint no PUA, payload inválido, glifo compuesto, etc.), responde con
status=<nonzero>; reason=<code>
Por qué se eligió el formato glyf
Por qué vectorial
- Un glifo no es una imagen, así que no tiene una resolución fija: el mismo ícono debe poder renderizarse tanto en una TUI densa a 12 px como en una pantalla HiDPI a 24 px
- Un glifo raster queda fijado a una resolución concreta y puede verse borroso en HiDPI o ilegible en celdas pequeñas
Razones concretas para elegir glyf
- Todas las terminales que renderizan texto ya tienen enlazado un rasterizador
glyf(FreeType, swash, ttf-parser, fontdue, allsorts, etc.) - Adoptar Glyph Protocol no añade ninguna dependencia nueva del lado de la terminal
- Si se adoptara SVG, habría que incorporar
resvgo escribir un nuevo parser de XML+path - El tamaño en el wire también es pequeño: un ícono común ocupa entre 150 y 400 bytes de datos
glyf, lo que es 2 a 3 veces menor que un SVG equivalente (incluyendo el overhead de base64)- Al registrar 50 íconos, la diferencia es de unos 13 KB vs 35 KB, algo perceptible en pipes de tmux o enlaces SSH móviles
Explicación breve de glyf
- Un registro
glyfguarda el glifo como un conjunto de contornos cerrados (contours) - Cada punto tiene 1 bit de metadata: on-curve u off-curve
- dos puntos on-curve seguidos → línea recta
- un punto off-curve entre dos on-curve → curva Bézier cuadrática
- dos puntos off-curve seguidos → existe un punto on-curve implícito en el punto medio (truco de compresión)
- Las coordenadas son posiciones enteras en una cuadrícula dentro del EM square; con
upm=1000,(500, 900)significa mitad del ancho y 90% de la altura - Un triángulo cerrado ocupa unos 30 bytes, y un ícono de 30 puntos unos 200 bytes
Subconjunto glyf definido por el protocolo
- Solo se permiten glifos simples: no se admiten glifos compuestos, referencias a otros glifos ni contexto a nivel de fuente
- Usa la codificación estándar de flags definida en la especificación OpenType
- No hay instrucciones de hinting: el hinting asume un conjunto global de valores de control de la fuente, que aquí no existe
- El espacio de coordenadas se define con
upm, cuyo valor por defecto es 1000, y puede sobrescribirse por registro
Color, escalado y autoría
- Los contornos
glyfno contienen información de color y se renderizan con el color de primer plano actual, igual que en el caso heredado de Nerd Font - Los glifos en color se soportan con formatos de payload separados:
fmt=colrv0/fmt=colrv1 - El valor
upmdefine el espacio de coordenadas del glifo y la terminal lo mapea a la celda al renderizar, así que no hace falta volver a registrar al cambiar el tamaño de fuente - La mayoría de desarrolladores no escribirán a mano bytes
glyf, sino que convertirán desde SVG en build time: se puede usar la interfazttx/pensdefonttools, y también está previsto publicar un helpersvg2glyfjunto con la implementación de referencia de Rio
Vida útil y capacidad
- Cada sesión de terminal tiene un glossary con hasta 1024 registros simultáneos, indexados por codepoints dentro de los 3 rangos PUA
- Los registros son válidos durante toda la duración de la sesión
- Al registrar el glifo número 1025, se expulsa el más antiguo en orden FIFO → no existe un error tipo "glossary full"
- Las aplicaciones que no puedan tolerar expulsiones silenciosas deben consultar ese codepoint antes de imprimirlo
Ejemplo real: registrar un ícono en un PUA vacío
- Se muestra un pipeline completo para registrar un contorno estilizado en
U+100000(el primer codepoint de Supplementary PUA-B) - Usa
fontToolscomo convertidor de SVG aglyf - Tras dibujar el contorno con
TTGlyphPen, se codifica enbase64, se envía en una secuencia APC y luego se imprime ese codepoint - El payload
glyfde un ícono típico de 20 puntos ocupa alrededor de 150 bytes, y con el wrapping APC y base64 llega a unos 250 bytes - Para desarrolladores con recursos SVG, está previsto ofrecer el helper
svg2glyf, con lo que el registro quedaría resuelto en 2 líneas
Opción para registros masivos: reply=
- Por defecto, la terminal envía una respuesta ACK por cada
r, pero en hooks de inicio que registran 100 glifos aparece el problema de que 100 ACKs encolados fluyen por el PTY y se muestran como basura en el shell - Hay 3 niveles de control
reply=1(por defecto): responde tanto en éxito como en error; útil para registros interactivos individualesreply=2: responde solo en caso de error; el éxito es silencioso, útil cuando en un registro masivo solo se quiere detectar erroresreply=0: no responde nunca; modo fire-and-forget, útil cuando no hay nadie que lea respuestas, como en hooks de arranque
- Los valores desconocidos hacen fallback automático a
reply=1, conservando compatibilidad hacia atrás para futuras extensiones
Clear (c): desregistrar
- Se usa al salir del editor para restaurar los valores predeterminados de la terminal, al cambiar el tema de una TUI o al depurar
- Liberar un solo slot: especificar un codepoint con el parámetro
cp - Limpiar todo el glossary: omitir
cp - Limpiar un slot vacío no es error sino un no-op, con respuesta
status=0 cpdebe estar dentro del rango PUA; si no lo está, devuelvereason=out_of_namespace
Funciones excluidas intencionalmente de v1
- No se puede registrar en codepoints no PUA: queda limitado a los 3 rangos Unicode PUA
- Sin ligaduras: el registro solo aplica a codepoints individuales; las sustituciones por secuencia de teclas quedan fuera del alcance de v1, y las ligaduras de programación (
->→⟶) ya las manejan las fuentes OpenType - Sin persistencia entre sesiones: hay que volver a enviar los glifos en cada ejecución, para evitar convertir la terminal en una caché de fuentes
- Sin compartición entre aplicaciones: cada sesión de terminal tiene su propio glossary; no hay IPC ni daemon
- Sin glifos en color en payloads
glyfde v1: se renderizan con el color de primer plano; el color se separa encolrv0/colrv1de v1.2 - Estas funciones podrían añadirse después si hacen falta, pero una vez agregadas sería difícil retirarlas, así que se excluyeron de forma deliberada
Base de seguridad de la restricción PUA
- La restricción a PUA no es una cuestión estética del API, sino una propiedad que hace seguro activar el protocolo por defecto
- Si se permitiera registrar codepoints arbitrarios, se podría registrar un glifo con forma de
oenU+0061(a), haciendo quebad.comse vea comobod.com- El buffer de celdas seguiría conteniendo
bad.com, así que al copiar y pegar los bytes serían correctos, pero lo que el usuario lee sería falso - Eso introduciría un primitivo de phishing en todos los programas de terminal, con efectos persistentes incluso para programas ejecutados después en la misma sesión
- El buffer de celdas seguiría conteniendo
- Al limitarlo a PUA, este tipo de ataque se vuelve mecánicamente imposible: los usuarios no teclean codepoints PUA, y nombres de archivo, URLs, comandos, nombres de variables y logs no los contienen
- Refuerza a nivel de protocolo el modelo de confianza que ya estableció Nerd Font por convención (los glifos personalizados solo existen en rangos reservados y no pueden superponerse al texto real)
- Propiedades de seguridad adicionales
- El buffer de celdas es la fuente de verdad: selección, copia, búsqueda, detección de hyperlinks, historial del shell, etc. deben devolver los codepoints emitidos por la aplicación, impidiendo trampas donde “lo que ves” y “lo que copias” difieren
- Aislamiento por sesión: dos pestañas pueden registrar de forma independiente íconos distintos para
U+E0A0, y el registro de una no afecta el renderizado de la otra
Comparación con métodos existentes
Kitty Image Protocol (KIP) + Unicode Placeholders
- Con los Unicode placeholders de KIP se puede aproximar Glyph Protocol, pero la integración es compleja y las terminales que implementan placeholders son solo Kitty, Ghostty y Rio
- KIP es un protocolo de imágenes, y un glifo no es una imagen
- Costo por uso: si un glifo se reutiliza 200 veces en pantalla (bordes de tablas, viñetas, etc.), hay que colocar 200 referencias a imagen, con costo de layout y composición. En Glyph Protocol, tras registrar el codepoint, se renderiza a velocidad de fuente
- Sin resolución nativa: un contorno
glyfno tiene tamaño en píxeles, así que se adapta automáticamente al cambiar el tamaño de fuente. KIP envía bitmaps a un tamaño específico, de modo que al cambiar el tamaño de fuente se vuelven borrosos o hay que re-subirlos, y además no hay forma de detectar ese cambio de tamaño - Herencia del color de primer plano: un contorno
glyfmonocromático se renderiza con el color actual de la celda, así que el tema se aplica automáticamente. Una imagen usa sus propios píxeles y no participa en el coloreado del texto
DEC DECDLD / DRCS
- Los Dynamically Redefinable Character Sets introducidos por VT220 en 1983 son formalmente parecidos a Glyph Protocol
- Tienen dos problemas fundamentales
- Enfoque bitmap: se sube una cuadrícula de píxeles ajustada al tamaño de celda actual de la terminal, por lo que al cambiar el tamaño de fuente, pasar a HiDPI o mover a un monitor 4K, los píxeles en bloque se escalan hacia arriba o hacia abajo. Es un enfoque propio de la era CRT fija de 10×20 e inadecuado para la variedad actual de tamaños de celda
- Sin límites de namespace: DECDLD puede sobrescribir conjuntos de caracteres que se mapean al rango GL (donde están
a,b,c, etc.), por lo que un programa no confiable puede redefinir cómo se renderizaa→ esta es la principal razón por la que las terminales modernas dudan en activar DECDLD
Estado de implementación en Rio terminal
- Glyph Protocol ya puede usarse en la rama main de Rio terminal, y se espera su llegada oficial durante mayo → primera implementación
- La especificación completa se publicará junto con el release, incluyendo ejemplos de código para registrar glifos y consultar la terminal
- Los ejemplos funcionales están disponibles en el repositorio raphamorim/glyph-protocol-examples, con integraciones de muestra para Bubble Tea, Ratatui e Ink
- El protocolo todavía puede actualizarse, y conforme más aplicaciones y terminales participen podrían cambiar el formato de mensajes, las respuestas a consultas y algunos edge cases → por ahora se recomienda tratarlo como moving target y fijar la versión de implementación al compilar
- Se espera la adopción por otros emuladores de terminal, ya que los beneficios para todo el ecosistema son grandes y el alcance de implementación se mantuvo intencionalmente pequeño
Preguntas abiertas para la comunidad
- ¿Las notificaciones de cambio de tamaño de fuente deberían formar parte del alcance del protocolo?: el propio Glyph Protocol evita este problema porque los contornos son independientes de la resolución, pero las TUIs que combinan imágenes y glifos no tienen forma de enterarse de cambios en las métricas de celdas fuera del polling → se debate si una notificación
resizeometrics-changeddebe entrar o no en el alcance - ¿Existe una forma responsable de permitir registros fuera de PUA?: la regla de solo PUA garantiza seguridad por defecto, pero bloquea casos de uso como IMEs CJK que quieran enviar glifos para hanzi no cubiertos o herramientas por idioma que quieran sobrescribir glifos → se piden ideas sobre cómo habilitar estos casos sin reintroducir phishing, por ejemplo con opt-in explícito a nivel de usuario, capacidades firmadas o flags de origen confiable
Aún no hay comentarios.