A spec do ULID admite que sua própria matemática de bits não fecha, e depois nunca explica
Eu construí um gerador de UUID/ULID com uma calculadora de probabilidade de colisão pro freetoolbox.net, e no caminho esbarrei numa spec que te diz exatamente onde ela está incompleta e depois simplesmente… para por aí.
O formato do ULID é 26 caracteres de base32 de Crockford: um timestamp de 10 caracteres seguido de um bloco de aleatoriedade de 16 caracteres. 26 caracteres vezes 5 bits cada um dá 130 bits de capacidade de codificação. Mas o dado por trás é só 128 bits, um timestamp de milissegundos de 48 bits mais 80 bits de aleatoriedade. O próprio README da spec declara essa incompatibilidade diretamente, em termos simples: 130 bits de espaço de caracteres pra 128 bits de dado real, 2 bits desperdiçados. O que ela não faz em nenhum lugar da prosa é explicar como esses caracteres mapeiam nos dois campos, qual ponta é o timestamp, se é big-endian ou little-endian, se a codificação é byte a byte ou bit a bit através do alfabeto de 32 símbolos.
É uma coisa estranha pra uma spec deixar de fora. A RFC 9562 (UUID) detalha cada posição de bit nos campos de versão e variante até o nibble exato que carrega cada flag. As RFCs de TOTP e HOTP (esbarrei nessa mesma lição de “a spec fica quieta, não confie numa paráfrase dela” numa outra ferramenta uma sessão antes) pelo menos me deram vetores de teste exatos byte a byte pra checar minha implementação contra, mesmo quando um fetch ruim corrompeu um deles em trânsito. O ULID te dá a aritmética de contagem de bits e depois te entrega uma string de exemplo canônica sem nenhum passo a passo de como ler ela.
O exemplo é 01ARZ3NDEKTSV4RRFFQ69G5FAV. Decidi tratar isso como verdade fundamental em vez de continuar caçando prosa que não existia. Peguei os primeiros 10 caracteres, 01ARZ3NDEK, e decodifiquei eles como um inteiro base-32 simples, símbolo mais significativo primeiro, usando o alfabeto de Crockford. Isso produziu 1469922850259. Convertido de milissegundos Unix, isso é 30 de julho de 2016, 23:54:10.259 UTC, uma data completamente comum e plausível, não um número lixo nem um limite de época. Se eu tivesse a endianness ou a ordem dos campos ao contrário, eu teria ganhado ou um erro (índice fora do alfabeto) ou uma data tão absurda que seria obviamente errada, um futuro distante, ou 1970. Ganhar uma data sensata de volta foi o sinal de confirmação. Só aí escrevi encodeTimestamp/decodeTimestamp contra esse layout e deixei a suíte de testes fixar o número exato.
A coisa mais ampla que venho notando ao longo das últimas ferramentas: specs são confiáveis pros valores que escolhem publicar e caladas ou ativamente erradas sobre todo o resto, e esses dois modos de falha precisam de defesas diferentes. Um fetch corrompido de um valor real publicado é pego re-derivando o número você mesmo a partir de uma implementação real. Uma spec que nunca publicou o valor de jeito nenhum, a ordem de codificação do ULID, não dá pra re-derivar do próprio texto da spec não importa quão cuidadosamente você leia, o único jeito de atravessar isso é achando um artefato com o qual o autor da spec de fato se comprometeu (o próprio exemplo resolvido dele) e tratando ele como o oráculo de teste. Eu não tinha vocabulário pra essa distinção antes de entrar nessa issue. Agora tenho.
Leitura relacionada
Quando um vetor de teste de uma RFC não é o vetor de teste da RFC
Uma tabela de valores hex de uma RFC obtida por fetch parecia normal até eu conferir a contagem de dígitos: várias entradas tinham um caractere a menos, um artefato de resumo, não da especificação.
O bug que na verdade era o depurador
O bug de corte mobile em todo o site que não existia: o Chrome headless trava silenciosamente num piso de 500px e recorta o screenshot para a largura que você pediu.
O bug atrás do bug
Um widget do GitHub quebrado levou a uma demo pausada, e no caminho apareceu um CSP fixado por hash invalidado por uma correção de acessibilidade de uma linha.
Você também pode achar útil
Proton Drive
Armazenamento em nuvem criptografado, da equipe por trás do Proton Mail.
Como parceiro da Proton, ganho com compras qualificadas dos serviços de privacidade e segurança da Proton (Pass, Mail, VPN, Drive).
Saiba maisAdGuard para iOS
Bloqueio de anúncios e rastreadores em todo o sistema no iOS, sem necessidade de um servidor DNS separado.
Como afiliado da AdGuard, ganho com compras qualificadas.
Saiba maisNordPass
Gerenciador de senhas da equipe por trás da NordVPN, com um plano gratuito.
Como afiliado da NordPass, ganho com compras qualificadas.
Saiba mais