Google ships a .proto file that describes .proto files. It's called descriptor.proto, a schema for schemas, written in the same language it describes. Descriptors are what you get when you run your own schema through it, and they power reflection, dynamic decoding, validation, and compiler plugin input.
Schemas Describing Schemas
When you compile a schema, the compiler doesn't just generate code. It can also output a binary representation of the schema itself, called a FileDescriptorSet. Both compilers will produce one: buf build -o descriptor.binpb or protoc --descriptor_set_out.
This FileDescriptorSet is itself a Protobuf message. Google already wrote the schema for it, descriptor.proto, which describes how to represent .proto files in the first place. Because of that, the same Protobuf tools you'd point at any other message can read and analyze a schema dynamically, at runtime. Buf's descriptors reference goes through the details.
Why is this useful?
Dynamic Decoding
Tools like this web explorer use descriptors to decode arbitrary binary data without generating static code.
Validation
Complex rule engines (like protovalidate) use descriptors to apply constraints dynamically.
Code Generation
Code generator plugins receive these descriptors as input. This is how every custom generator is built, and why the same plugin binary works under both buf generate and protoc.
Server Reflection
A server can hand out its own descriptors over the wire, so a client can call it knowing nothing in advance. buf curl uses this to invoke any reflection-enabled gRPC or Connect endpoint with no .proto files on your disk.
Try editing the schema below to see how the generated FileDescriptorSet changes in real-time.
edition = "2024"; package demo.v1; import "buf/validate/validate.proto"; message User { string id = 1 [json_name = "uid"]; string name = 2; uint32 age = 3 [(buf.validate.field).uint32.lt = 150]; Role role = 4; enum Role { ROLE_UNSPECIFIED = 0; ROLE_USER = 1; ROLE_ADMIN = 2; } }
Next
Extending
Plugins, extensions, and custom options. A plugin's input is the descriptors from this page.