Résumé

  • RFC 9996 enregistre application/protobuf et application/protobuf+json pour des objets sérialisés ; il n’enregistre ni fichiers .proto ni types de messages particuliers.
  • Le paramètre version concerne l’encodage sur le fil. Il ne dit rien, à lui seul, de Proto2, Proto3, des Editions, de la révision du schéma ou de la version métier.
  • La preuve opérationnelle doit relier une représentation à un type de message et à un descriptor authentifiés, puis conserver séparément la décision prise par le logiciel en fonctionnement.

Prenons un parc de capteurs médicaux. Un concentrateur reçoit une commande portant le type application/protobuf. Le filtre d’entrée accepte le corps. La bibliothèque le décode. Un champ numérique connu contient une valeur plausible. Rien ne ressemble à une attaque ni à une corruption.

Après une fusion, deux équipes ont pourtant conservé des messages différents sous des contrats d’API voisins. Dans l’un, le champ 7 fixe une limite d’alarme ; dans l’autre, il indique un délai de silence. Le concentrateur a déployé le mauvais descriptor. Les octets sont valides sous les deux définitions. La réussite du parseur ne protège donc pas le patient : elle masque l’erreur d’autorité qui a choisi le dictionnaire.

C’est la frontière éclairée par le RFC 9996, RFC informatif publié en juillet 2026. Le texte donne enfin deux noms publics aux représentations Protocol Buffers. Il ne prétend pas que ces noms puissent porter toute la sémantique de l’application.

Deux types de média, une mission volontairement étroite

application/protobuf désigne la représentation binaire. application/protobuf+json désigne la projection JSON. Les fiches IANA de la forme binaire et de la forme JSON deviennent des points de coordination communs pour les producteurs, mandataires, stockages et consommateurs.

Le périmètre porte sur l’objet sérialisé, et non sur le fichier d’interface ou la définition d’objet. Pour le premier type, l’encodage par défaut est binaire. Pour le second, l’encodage est JSON et le jeu de caractères requis est UTF-8. Un paramètre facultatif version annonce la version du wire encoding Protobuf ; sa valeur par défaut est 1. Un client qui ne prend pas en charge la version annoncée doit la refuser.

Cette précision met fin à une longue dépendance envers des noms privés tels que application/x-protobuf, application/x-protobuffer et application/x-protobuf+json, désormais signalés comme alias obsolètes. Elle répond à la logique du RFC 6838 : une étiquette de média est une convention publique, enregistrée avec ses paramètres et ses considérations de traitement.

Elle ne crée aucun magic number, suffixe de fichier standard ou identifiant de fragment. Elle n’ajoute ni chiffrement, ni intégrité, ni authentification, ni compression, ni défense contre l’épuisement de ressources. Le destinataire apprend quelle famille de représentation envisager. Il n’apprend pas qui a publié le message, quel contrat l’autorise ni quelle opération métier il contient.

Le mot « version » recouvre plusieurs calendriers

Le piège commence lorsque l’architecture stocke version=1 comme si elle avait enregistré « la version Protobuf ». Le paramètre du RFC 9996 ne suit que le wire encoding. Or Proto2, Proto3, Edition 2023 et Edition 2024 décrivent une évolution du langage de schéma et de ses comportements. Une équipe maintient encore la révision de son propre .proto, la version de son code généré, celle de sa runtime et celle du service déployé.

Ces calendriers ne se déplacent pas ensemble. Le RFC prend le traitement des enum inconnus comme exemple : les mêmes octets peuvent rester compatibles avec le fil alors que deux générations de schéma ou de runtime ne conservent ni n’exposent une valeur inconnue de la même manière. Une compatibilité binaire ne suffit donc pas à garantir une décision identique.

Un inventaire sérieux conserve séparément le wire version, le nom complet du message, l’empreinte du descriptor, la génération IDL et la build de runtime. Si l’organisation ne garde qu’un numéro, elle perd le moyen d’attribuer un changement de résultat au bon niveau.

Un numéro de champ n’explique pas son rôle

Le guide officiel de l’encodage montre ce que transporte le binaire : des numéros de champs et des wire types. Le nom source du champ n’est pas présent. Le wire type ne suffit pas non plus à retrouver le type déclaré. Un varint peut représenter un entier, un booléen ou une enum ; une valeur length-delimited peut être une chaîne, des octets, un message imbriqué ou une série compacte.

Le descriptor complète cette information. C’est pourquoi le choix du descriptor est une décision d’autorité, pas une commodité de bibliothèque. Sous la mauvaise définition, le parseur peut néanmoins produire un objet. Les champs inconnus sont sautés, des numéros compatibles prennent un autre nom et des valeurs par défaut remplissent les absences. L’échec bruyant aurait protégé l’opérateur ; l’objet plausible peut aller jusqu’à la facturation, l’autorisation ou la commande physique.

Il n’existe pas une seule bonne façon de lier le schéma. Un endpoint, une méthode RPC, un client généré, un profil d’API, un bundle signé, un manifeste de déploiement ou un registre contrôlé peuvent remplir ce rôle. La condition est double : la liaison doit être vérifiable plus tard, et un intermédiaire non autorisé ne doit pas pouvoir substituer un schéma simplement parce qu’il accepte les octets.

Le RFC 9205 aide à préserver cette répartition. Une application HTTP combine méthodes, statuts, en-têtes, relations de liens, comportement des ressources et types de médias. Le type de média participe au contrat sans devoir contenir le contrat entier. L’absence de registry universel des messages dans RFC 9996 laisse à l’application la place nécessaire pour exprimer son propre contexte.

La lisibilité de ProtoJSON ne vaut pas approbation

Dans application/protobuf+json, les noms de champs et d’enums apparaissent. Le suffixe structuré +json, défini par le RFC 6839, permet à un outil générique d’appliquer les règles JSON du RFC 8259 lorsqu’il n’a pas besoin de la sémantique exacte du média.

Cette visibilité entraîne toutefois un autre régime d’évolution. Le guide ProtoJSON avertit que cette représentation ne tolère pas les unknown fields aussi bien que le binaire. Un producteur peut ajouter un champ et faire échouer un ancien client. Renommer un champ ou une enum devient risqué parce que le nom se trouve sur le fil. Une conversion binaire → JSON → binaire peut aussi supprimer des informations inconnues que le dernier consommateur aurait su comprendre.

Le guide de mise à jour Proto3 demande en conséquence de réserver les anciens numéros et les anciens noms. Réutiliser un numéro donne potentiellement un nouveau sens aux archives binaires ; réutiliser un nom trouble les consommateurs JSON et le code généré. Aucune étiquette +json ne fait respecter cette discipline.

La stratégie de test doit suivre le trajet réel. Il faut injecter des données d’une version future dans les relais, observateurs et convertisseurs effectivement déployés. Un test direct entre deux services binaires ne révèle pas l’information perdue par une passerelle JSON intermédiaire.

Any facilite l’identification, pas la confiance

Le type Any associe des octets et une URL de type. À première vue, il semble régler la question : il suffirait de suivre l’URL pour obtenir le schéma. RFC 9996 relève toutefois que le mécanisme de déréférencement envisagé n’est pas pris en charge par des implémentations largement utilisées. De nombreux environnements interprètent l’URL comme une clé dans un catalogue local.

Même lorsqu’un resolver sait télécharger un descriptor, trouver n’est pas autoriser. DNS, TLS et une réponse correcte apportent des preuves sur le canal. Ils ne prouvent pas que le descriptor retourné est la version approuvée par le producteur et le propriétaire de l’API pour cette transaction. Le contenu d’une URL peut muter ; un domaine ou un dépôt peut changer de mains ; une résolution distante peut révéler les types traités par le client.

L’objet de contrôle devrait donc inclure l’URL ou le type, le digest exact du descriptor, l’éditeur ou la racine de confiance, le canal d’acquisition, l’approbation et la portée contractuelle. Un catalogue peut proposer un candidat. La promotion de ce candidat au rang d’autorité doit rester une décision distincte.

La sérialisation déterministe n’est pas une identité canonique

L’enjeu se prolonge dans les signatures et la déduplication. La documentation Serialization Is Not Canonical indique que deux messages ayant le même sens abstrait peuvent produire des octets différents. L’ordre des champs n’est pas une garantie universelle.

L’option deterministic rend une production plus répétable dans un contexte limité. Elle ne définit pas une forme canonique indépendante du langage, de la build, de la bibliothèque et du schéma. Une empreinte peut identifier exactement l’artefact produit. Elle ne peut représenter durablement l’objet métier sans un contrat supplémentaire de canonicalisation et sans le contexte du descriptor.

Une archive probante doit conserver les octets, le type de message, le digest du descriptor, la runtime et la règle de sérialisation. Une signature isolée peut établir que les octets n’ont pas changé tout en laissant indécidable ce qu’ils signifiaient.

Une couche exacte ne rend pas les suivantes exactes

La doctrine de Heng Lu sur la spécification initiale minimale décrit bien l’apport de RFC 9996. Le socle commun contient le nom du format et son wire version, parce que ces éléments doivent être partagés. Les taxonomies de messages, les autorités éditrices et la validation métier peuvent rester localisées tant qu’aucun besoin commun plus large n’est démontré.

Le texte sur les couches de réalité interdit ensuite de fusionner les preuves. Le média déclaré, le descriptor choisi, l’objet décodé et la transition d’état acceptée sont quatre faits liés mais différents. Le premier peut être vrai alors que le deuxième est illégitime ; les trois premiers peuvent être exacts alors que la politique refuse le quatrième.

Enfin, la primauté du code en fonctionnement place l’observation finale dans le service qui parse, valide, autorise et modifie l’état. Le registre dit ce qui devrait être compris. Le système actif révèle ce qui a obtenu un effet.