Introdução ao Glyph Protocol para terminais
(rapha.land)- Surgiu um novo protocolo para terminais para resolver o problema antigo de ser preciso instalar fontes modificadas (como Nerd Font) para renderizar ícones personalizados em aplicações de terminal
- O Glyph Protocol permite que aplicações registrem glifos vetoriais diretamente no terminal em tempo de execução e consultem se a renderização de um codepoint específico é possível
- Os dados de glifo usam o formato
glyfdo TrueType, permitindo implementação sem novas dependências ao aproveitar o rasterizador que o terminal já possui - Os codepoints permitidos para registro são restritos à Unicode Private Use Area (PUA), bloqueando por princípio ataques de phishing e falsificação visual
- A Rio terminal já está recebendo a primeira implementação, e códigos de exemplo para frameworks TUI importantes como Bubble Tea, Ratatui e Ink já foram publicados
Problema existente: dependência de fontes modificadas
- Para que editores de terminal, prompts e TUIs exibam ícones corretamente, o usuário precisa instalar manualmente fontes modificadas como Nerd Font ou Powerline
- Sem a fonte instalada, aparece tofu (□) no lugar do ícone, e essas fontes modificadas costumam ser pesadas, com 6–12 MB cada
- JetBrainsMono Nerd Font Regular cerca de 7,8 MB, FiraCode Nerd Font Regular cerca de 10,4 MB, e o arquivo completo de símbolos cerca de 60 MB
- Desenvolvedores de aplicações não têm como distribuir diretamente os glifos que desejam e acabam dependendo de que o usuário tenha a fonte, a versão e o mapeamento de codepoints corretos
Recursos principais do Glyph Protocol
- Há suporte para duas operações centrais
- Registro de glifos personalizados: a aplicação escolhe um codepoint na Unicode PUA e envia diretamente ao terminal o contorno vetorial para registrá-lo em tempo de execução
- Consulta de codepoint: permite verificar se um codepoint específico é coberto pela fonte do sistema, por um registro feito na sessão, por ambos ou por nenhum
- Se o usuário já tiver Nerd Font instalada, a aplicação pode usar a consulta para pular o envio do glifo; se não tiver, pode enviar o contorno diretamente e o ícone ainda assim será exibido corretamente
Estrutura do protocolo
Modo de transporte (Transport)
- Usa APC (Application Program Command) em vez de OSC
- APC foi projetado para comandos definidos por aplicações, e terminais que não o implementam podem ignorar com segurança a sequência
- OSC compartilha um namespace global usando um único inteiro decimal como identificador de comando, o que traz risco de conflito; APC não tem esse problema porque possui sua própria estrutura de identificação
Identificador (Identifier)
- Todas as mensagens do Glyph Protocol recebem como prefixo o codepoint
25a1(U+25A1, WHITE SQUARE)- Esse caractere é o símbolo padrão de tofu que o terminal desenha quando não há glifo
- Formato de framing:
ESC _ 25a1 ; <verb> [ ; key=value ]* [ ; <payload> ] ESC \\ - Existem 4 verbos:
s(support),q(query),r(register),c(clear)
Support (s): verificar suporte do terminal
- Serve para verificar quais formatos de payload e versões do protocolo o terminal suporta
- Também é a forma padronizada de detectar a própria existência do Glyph Protocol
- O
fmtda resposta é um bitfield, em que cada bit representa um formato de payload1=glyf: glifo simples TrueType, obrigatório na v12=colrv0: glifo colorido em camadas planas (OpenType COLR v0), adicionado na v1.24=colrv1: grafo completo de pintura com gradientes e transformações (OpenType COLR v1), adicionado na v1.2
- Se houver resposta, o suporte ao protocolo foi confirmado; se houver timeout, não há suporte; se
fmt=0, o protocolo existe, mas não há suporte a formatos de payload (definido por completude)
Query (q): consultar se um codepoint pode ser renderizado
- Consulta se um codepoint específico pode ser renderizado e recebe resposta no valor
status0(free): nada é renderizado, aparece tofu1(system): coberto pela fonte do sistema2(glossary): coberto por registro na sessão3(both): coberto por ambos; o registro sobrescreve a renderização da fonte do sistema
- Se a TUI detectar que o ícone já existe no sistema, pode pular o registro; caso contrário, pode registrar um codepoint personalizado e fazer fallback elegante
Register (r): registrar glifo
- A aplicação escolhe um codepoint PUA e o registra enviando um contorno
glyfcodificado em base64 - Parâmetros principais
cp: codepoint de destino (hex), que deve obrigatoriamente estar em uma das 3 faixas Unicode PUA (U+E000–U+F8FF,U+F0000–U+FFFFD,U+100000–U+10FFFD); fora dessas faixas, a resposta seráreason=out_of_namespacefmt: formato do payload; na v1, sóglyfé definido e é o padrão, então geralmente pode ser omitidoupm: units per em, define o espaço de coordenadas do contorno; valor padrão 1000
- Se um segundo
rfor enviado para o mesmocp, ele sobrescreve o registro anterior - Em caso de erro (codepoint fora da PUA, payload inválido, glifo composto etc.), a resposta vem como
status=<nonzero>; reason=<code>
Por que escolher o formato glyf
Por que vetor
- Glifos não são fotos, então não têm resolução fixa: o mesmo ícone precisa ser renderizado tanto em uma TUI densa de 12 px quanto em uma tela HiDPI de 24 px
- Glifos raster ficam presos a uma resolução específica e podem ficar borrados em HiDPI ou ilegíveis em células pequenas
Motivos concretos para escolher glyf
- Todo terminal que renderiza texto já traz linkado um rasterizador
glyf(FreeType, swash, ttf-parser, fontdue, allsorts etc.) - Adotar o Glyph Protocol não adiciona nenhuma dependência nova ao lado do terminal
- Adotar SVG exigiria trazer
resvgou escrever um novo parser de XML+path - O tamanho no wire também é menor: ícones comuns ficam entre 150 e 400 bytes de dados
glyf, cerca de 2 a 3 vezes menores que SVG equivalente, mesmo contando o overhead de base64- Ao registrar 50 ícones, a diferença é de cerca de 13 KB contra 35 KB, algo perceptível em pipes do tmux ou links SSH móveis
Explicação rápida de glyf
- Um registro
glyfarmazena o glifo como um conjunto de contornos fechados (contours) - Cada ponto tem 1 bit de metadado: on-curve ou off-curve
- dois pontos on-curve consecutivos → linha reta
- um ponto off-curve entre dois on-curve → curva Bézier quadrática
- dois pontos off-curve consecutivos → existe um ponto on-curve implícito entre eles (truque de compressão)
- As coordenadas ficam em posições inteiras dentro do quadrado EM; com
upm=1000,(500, 900)significa metade da largura e 90% da altura - Um triângulo fechado ocupa cerca de 30 bytes; um ícone com 30 pontos, cerca de 200 bytes
Subconjunto glyf definido pelo protocolo
- Só glifos simples são permitidos: glifos compostos, referências a outros glifos e contexto em nível de fonte não são aceitos
- Usa a codificação padrão de flags definida na especificação OpenType
- Sem instruções de hinting: hinting pressupõe um conjunto completo de valores de controle da fonte, o que não existe aqui
- O espaço de coordenadas é definido por
upm, com padrão 1000, podendo ser sobrescrito por registro
Cor, escalonamento e autoria
- Contornos
glyfnão carregam informação de cor e são renderizados com a cor atual de primeiro plano, igual ao caso de herança da Nerd Font - Glifos coloridos são suportados em formatos de payload separados:
fmt=colrv0efmt=colrv1 - O valor
upmdefine o espaço de coordenadas do glifo, e o terminal mapeia isso para a célula na renderização → não é preciso registrar novamente ao mudar o tamanho da fonte - A maioria dos desenvolvedores não vai escrever bytes
glyfmanualmente, e sim converter a partir de SVG em build time: é possível usar as interfacesttx/pensdofonttools, e o helpersvg2glyfdeve ser distribuído junto com a implementação de referência do Rio
Vida útil e capacidade
- Cada sessão de terminal mantém um glossary com no máximo 1024 registros simultâneos, indexados por codepoints dentro das 3 faixas PUA
- Os registros permanecem válidos durante toda a sessão
- Ao registrar o 1025º glifo, o registro mais antigo é expulso em ordem FIFO → não existe erro de “glossary full”
- Aplicações que não podem tolerar expulsão silenciosa devem consultar o codepoint antes de exibi-lo
Exemplo real: registrar um ícone em uma PUA vazia
- Há um exemplo do pipeline completo para registrar um contorno estilizado em
U+100000(o primeiro codepoint da Supplementary PUA-B) - Usa
fontToolscomo conversor de SVG paraglyf - Após desenhar o contorno com
TTGlyphPen, ele é codificado embase64, enviado em uma sequência APC e depois o codepoint correspondente é exibido - Um payload
glyfde um ícone comum de 20 pontos tem cerca de 150 bytes; com encapsulamento APC e base64, fica em torno de 250 bytes - Para desenvolvedores com assets em SVG, está previsto um helper
svg2glyf→ registro concluído em 2 linhas
Opção para registro em massa: reply=
- Por padrão, o terminal envia uma resposta ACK para todo
r, mas em hooks de inicialização que registram 100 glifos isso causa o problema de 100 ACKs enfileirados saindo pelo PTY e aparecendo como lixo no shell - Há 3 níveis de controle
reply=1(padrão): responde em sucesso e falha; indicado para registro interativo individualreply=2: responde apenas em caso de falha; sucesso é silencioso; usado quando se quer detectar só erros em registro em massareply=0: sem resposta alguma; fire-and-forget; útil quando não existe um processo para ler a resposta, como em hooks de inicialização
- Valores desconhecidos fazem fallback automático para
reply=1, preservando compatibilidade retroativa em futuras extensões
Clear (c): remover registro
- Pode ser usado para restaurar o padrão do terminal ao fechar o editor, trocar o tema da TUI ou depurar
- Remoção de um único slot: especificar um codepoint com o parâmetro
cp - Limpar todo o glossary: omitir
cp - Limpar um slot vazio não gera erro, é um no-op, com resposta
status=0 cpprecisa estar dentro da faixa PUA; fora dela, retornareason=out_of_namespace
Recursos deliberadamente fora da v1
- Não é possível registrar codepoints fora da PUA: a v1 é limitada às 3 faixas Unicode PUA
- Sem ligaturas: o registro vale apenas para um único codepoint; substituição por sequência de teclas está fora do escopo da v1, e ligaturas de programação (
->→⟶) já são tratadas por fontes OpenType - Sem persistência entre sessões: os glifos precisam ser enviados novamente a cada execução, evitando transformar o terminal em um cache de fontes
- Sem compartilhamento entre aplicações: cada sessão de terminal possui seu próprio glossary, sem IPC nem daemon
- Sem glifos coloridos no payload
glyfda v1: a renderização é feita na cor de primeiro plano; cor fica separada emcolrv0/colrv1na v1.2 - Esses recursos podem ser adicionados depois, se necessário, mas foram excluídos de propósito porque depois de adicionados não é fácil removê-los
Base de segurança da restrição à PUA
- A restrição à PUA não é uma escolha estética de API, e sim uma propriedade que torna seguro ativar o protocolo por padrão
- Se fosse permitido registrar codepoints arbitrários: seria possível registrar um glifo com forma de
oemU+0061(a) e fazerbad.comparecerbod.com- O buffer de células ainda conteria
bad.com, então copiar e colar produziria os bytes corretos, mas o que o usuário lê seria falso - Isso criaria um primitivo de phishing para qualquer programa de terminal, com efeito persistente inclusive sobre programas executados depois na mesma sessão
- O buffer de células ainda conteria
- Ao restringir à PUA, esse tipo de ataque se torna mecanicamente impossível: usuários não digitam codepoints PUA, e nomes de arquivo, URLs, comandos, nomes de variáveis e logs não contêm codepoints PUA
- O protocolo reforça no nível da implementação o modelo de confiança estabelecido por convenção na Nerd Font: glifos personalizados só existem em faixas reservadas e não podem se sobrepor a texto real
- Propriedades extras de segurança
- O buffer de células é a autoridade: seleção, cópia, busca, detecção de hyperlink, histórico do shell etc. devem retornar os codepoints que a aplicação realmente imprimiu, sem permitir armadilhas de “o que você vê não é o que copia”
- Isolamento por sessão: duas abas podem registrar ícones diferentes para
U+E0A0de forma independente, e o registro de uma aba não afeta a renderização na outra
Comparação com abordagens existentes
Kitty Image Protocol (KIP) + Unicode Placeholders
- Dá para aproximar o Glyph Protocol com placeholders Unicode do KIP, mas a integração é complicada e só Kitty, Ghostty e Rio implementam esse tipo de placeholder
- KIP é um protocolo de imagem, e glifos não são imagens
- Custo por uso: um glifo reutilizado 200 vezes na tela (bordas de tabela, marcadores de lista etc.) exige 200 referências de imagem, com custo de layout e composição. Com o Glyph Protocol, depois de registrar o codepoint, ele é renderizado na velocidade de fonte
- Sem resolução nativa: contornos
glyfnão têm tamanho em pixels, então acompanham automaticamente mudanças no tamanho da fonte. KIP envia bitmaps em um tamanho específico, então ao mudar a fonte a imagem fica borrada ou precisa ser reenviada, e o protocolo nem tem como detectar essa mudança - Herança da cor de primeiro plano: um contorno monocromático
glyfé renderizado com a cor atual da célula, então o tema se aplica automaticamente. Imagens têm seus próprios pixels e não participam da coloração do texto
DEC DECDLD / DRCS
- O VT220 introduziu em 1983 os Dynamically Redefinable Character Sets, que se parecem conceitualmente com o Glyph Protocol
- Há dois problemas centrais
- Abordagem bitmap: o terminal faz upload de uma grade de pixels ajustada ao tamanho atual da célula, então ao mudar o tamanho da fonte, usar HiDPI ou trocar para um monitor 4K, os pixels em bloco são ampliados ou reduzidos. Era uma solução da época dos CRTs fixos de 10×20 e não combina com a variedade moderna de tamanhos de célula
- Sem limitação de namespace: o DECDLD pode sobrescrever conjuntos de caracteres mapeáveis para a faixa GL (onde ficam a, b, c etc.), então um programa não confiável poderia redefinir a renderização de
a→ esse é o principal motivo pelo qual terminais modernos evitam habilitar DECDLD
Estado da implementação no terminal Rio
- O Glyph Protocol já pode ser usado na branch main do terminal Rio, com landing oficial previsto para maio → primeira implementação
- A especificação completa será publicada junto com o release, incluindo código de exemplo para registro de glifos e consulta ao terminal
- Exemplos funcionais podem ser vistos no repositório raphamorim/glyph-protocol-examples, incluindo integrações de exemplo para Bubble Tea, Ratatui e Ink
- O protocolo ainda pode ser atualizado, e o formato das mensagens, as respostas a consultas e alguns edge cases podem mudar à medida que mais aplicações e terminais participarem → por enquanto, trate compilações atuais como alvo móvel e fixe a versão implementada
- Há expectativa de adoção por outros emuladores de terminal, e o benefício para o ecossistema como um todo é grande, enquanto o escopo de implementação foi mantido intencionalmente pequeno
Perguntas em aberto para a comunidade
- A notificação de mudança do tamanho da fonte deveria fazer parte do escopo do protocolo?: o Glyph Protocol em si evita esse problema porque os contornos são independentes de resolução, mas TUIs que combinam imagem e glifo não têm como saber de mudanças nas métricas da célula sem polling → discute-se se um aviso como
resizeoumetrics-changedpertence ao escopo ou está além dele - Existe uma forma responsável de permitir registros fora da PUA?: a regra de PUA-only garante segurança por padrão, mas bloqueia casos como IMEs CJK que queiram enviar glifos para hanzi não cobertos ou ferramentas linguísticas que desejem sobrescrever glifos → pede-se opinião sobre formas de abrir esses casos sem reintroduzir phishing, como opt-in explícito do usuário, recursos assinados ou flags de origem confiável
Ainda não há comentários.