# opentelemetry-proto: the wire contract every OpenTelemetry SDK and collector agrees on

> opentelemetry-proto holds the Protocol Buffer definitions for OTLP, the OpenTelemetry data delivery protocol used to export traces, metrics, logs and profiles over gRPC and HTTP, along with a separate process context protocol for sharing resource attributes through memory mapped regions. The repository is mostly a Makefile, a set of proto files and a stability contract.

**open-telemetry/opentelemetry-proto** — OpenTelemetry protocol (OTLP) specification and Protobuf definitions

- Repository: https://github.com/open-telemetry/opentelemetry-proto
- Website: https://opentelemetry.io/docs/specs/otlp/
- Stars: 824 · Forks: 326
- Language: Makefile
- License: Apache-2.0
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/open-telemetry-opentelemetry-proto

## What the repository actually contains

This repository contains the Protocol Buffer definitions for OTLP, the primary OpenTelemetry data delivery protocol, and other protocols that share its common types and release machinery. The primary language listed for the repository is a Makefile, which is an accurate signal of what the project is: a build system wrapped around schema files, not an implementation.

The proto files live under opentelemetry/proto/ and cover everything except the processcontext/ directory, which belongs to a separate protocol. The documentation points at the specification at docs/specification.md and the OTLP specification page on opentelemetry.io.

There are two protocol families in the repository. OTLP is the one everybody uses, for exporting traces, metrics, logs and profiles via gRPC and HTTP. Process Context Sharing is a non-OTLP protocol for sharing process-level resource attributes with external readers, such as the OpenTelemetry eBPF Profiler, and it works through memory-mapped regions rather than being exchanged over gRPC, HTTP or the OpenTelemetry Collector.

The process context work is the newest part and is still in the experimental phase, with its own documentation page, its own proto file under a v1development package, and a design document tracked as OTEP 4719 in the specification repository.

The repository is Apache-2.0 licensed, the last push was on 2026-10-19, and the release history shows v1.11.1 on 2026-09-29, v1.11.0 on 2026-07-21 and v1.10.0 on 2026-03-09.

## Generating client libraries from the proto files

The README gives one instruction for generating the raw gRPC client libraries: use make gen-${LANGUAGE}. The supported languages are listed as cpp, csharp, go, java, objc, openapi for swagger, php, python and ruby.

The Makefile shows what that target actually runs. The full generation set covers a slightly different and larger list, since the all target chains the per-language ones together:

```make
gen-all: gen-cpp gen-csharp gen-go gen-java gen-kotlin gen-objc gen-openapi gen-php gen-python gen-ruby
```

The tooling is containerised. The protoc invocation runs as a Docker container against a protobuf image version read out of the dependencies Dockerfile, with the working directory mounted and the proto path set to the repository root. The buf invocation is similar, mounting the workspace into a bufbuild image. The Makefile computes those image versions with an awk pattern over the dependencies file, so the toolchain version is pinned in one place rather than in the build scripts.

There is a second use of buf that matters for a protocol repository. The breaking change check compares the current definitions against the latest release tag, with the tag discovered by sorting git tags by committer date and taking the last one. The buf configuration file at the root, buf.yaml, carries the lint and breaking rules, and the dependency for the comparison is expressed as a git URL with a tag reference.

So generating code from these definitions is a supported path, and checking that the definitions have not broken compatibility is an automated one.

## The maturity table tells you what you can rely on

The README states that 1.0.0 and newer releases may contain unstable components as indicated by a maturity table, and the table is more granular than most people expect.

For OTLP, the common types and the resource types are marked Stable for both binary protobuf and JSON. So are metrics, with the metrics/* and collector/metrics/* paths, traces with trace/* and collector/trace/*, and logs with logs/* and collector/logs/*. Profiles, under profiles/* and collector/profiles/*, are marked Development in both columns.

Process Context Sharing, under processcontext/*, is Development for binary protobuf and not applicable for JSON, which follows from it not being an HTTP or gRPC protocol at all.

So a client that sends traces, metrics and logs is on solid ground, while a client that sends profiles is using a part of the protocol that is still moving. That distinction matters when you are pinning a version, and it explains why the repository describes itself as primary language being a Makefile rather than any particular language implementation.

The OTLP/JSON representation has its own additional requirements, which live in the specification repository rather than here, and the README links the section on JSON protobuf encoding for them.

## What Stable actually promises

The stability definition is a precise contract, and it is worth reading in full if you are implementing a receiver or generating long-lived bindings.

Field types, numbers and names will not change. Service names and service package names will not change. Service method names will not change, as will service method parameter names, service method parameter types and return types, and the method kind whether unary or streaming. The names of messages and enums will not change, the numbers assigned to enum choices will not change, the names of enum choices will not change, and the lexical location of a message or enum, whether declared at the top scope or nested inside another message, will not change. Package names and directory structure will not change, the optional and repeated declarators of existing fields will not change, and no existing symbol will be deleted.

Those guarantees marked from 1.0.0 go into effect when the repository is tagged with version 1.0.0, which the current 1.11.x releases are well past.

The additive changes that are allowed are the mirror image: adding new fields to existing messages, new messages or enums, new choices to existing enums, new choices to existing oneof fields, new services, and new methods on existing services. And there is one condition attached to all of them: each additive change must be accompanied by an explanation of how senders and receivers implementing the protocol before and after the change interoperate.

That last requirement is the interesting one. It is the protocol equivalent of requiring a test for a new field, and it is why the repository treats a schema change as a design problem rather than a mechanical one.

## How new signals get introduced without breaking things

The experiments section explains the mechanism for adding something genuinely new, such as a whole new signal, to OpenTelemetry.

The recommendation is to use a development package while the component is being designed. That package keeps the development suffix throughout, and when the component reaches release candidate the suffix is removed, for example v1 instead of v1development, which creates the stable release package. The profiles protocol follows exactly this pattern today, with its process context proto file sitting under a v1development directory.

Components in an isolated experimental package are excluded from the stability requirements described above, which is the point: they are free to change until they graduate.

The levels used to signal readiness are Development, Alpha, Beta and Release Candidate, taken from the maturity levels defined in the specification repository. The rule is that these levels must be communicated in the documentation of a message or a field whenever the level does not match the stability of the package it lives in. If a package is Stable but an individual field is not, the documentation must say so and give the level, for example Alpha or Beta.

The reverse case is also handled. Individual Status annotations are not required if a component is at the same maturity level as the package containing it. So the convention is a package-level statement by default, with per-field annotation only where the field is the exception.

## Where opentelemetry-proto is the wrong thing to reach for

The first misunderstanding to avoid is that this is a library. There is nothing to import, no client, no collector and no SDK. If you want to send telemetry from an application, you want an OpenTelemetry language SDK, and if you want to receive it, you want the Collector. The repository says this itself: the language libraries may publish generated bindings, and it tells you to check the documentation of the language library you are using.

The second is that the protocol is not the whole story. OTLP defines the payload, but the semantics of the signals, the conventions for naming resources and the attribute conventions live in the specification repository, and a receiver that implements the proto files but not the conventions will produce data that other systems cannot interpret correctly.

The third is a practical matter of who generates what. The repository says the proto files can be consumed as git submodules or copied and built directly in the consumer project, which is the escape hatch when you need a language with no official bindings. The cost of that path is that you now own the generation, you own the update cadence, and you have to check the breaking change tooling yourself. The buf-based check in the Makefile compares against the latest release tag, which is a good model to copy.

For most teams the right interaction is to consume the published bindings from the language library and treat this repository as the reference you read when a field is unclear, when a payload looks wrong, or when you need a language nobody has covered yet.

## Conclusion

opentelemetry-proto is the right thing to look at when you need to know exactly what OTLP puts on the wire, when you are generating client bindings in a language the official SDKs do not cover, or when you are implementing a collector or exporter yourself. It is not a library you link against, and it is not something you vendor into an application. Before depending on a specific field, check the maturity table in the README, because profiles and process context are marked Development while common, resource, metrics, traces and logs are Stable, and pin the release tag you build against, since the wire contract changes additively and your generated code has to match the receiver's version.

## FAQ

### What is opentelemetry-proto and what does it hold?

It holds the Protocol Buffer definitions for OTLP, the primary OpenTelemetry data delivery protocol used to export traces, metrics, logs and profiles via gRPC and HTTP, plus a separate process context protocol that shares process-level resource attributes through memory-mapped regions.

### How do I generate gRPC client libraries from the proto files?

You run `make gen-${LANGUAGE}`, with the supported languages listed as cpp, csharp, go, java, objc, openapi for swagger, php, python and ruby. The Makefile runs protoc and buf inside Docker containers, with the image versions read from the dependencies Dockerfile.

### Which parts of OTLP are stable?

The maturity table marks common, resource, metrics, traces and logs as Stable for both binary protobuf and JSON. Profiles are marked Development in both columns, and process context sharing is Development for binary protobuf and not applicable for JSON.

### What changes are allowed in a stable OTLP component?

Additive changes only: new fields on existing messages, new messages or enums, new enum choices, new choices on existing oneof fields, new services and new methods. Each must come with an explanation of how senders and receivers on either side of the change interoperate.

### How is a new OpenTelemetry signal introduced without breaking compatibility?

It is developed in a development package, with the development suffix kept until release candidate, at which point the suffix is removed to create the stable release package. Components in such a package are excluded from the stability requirements, and the Development, Alpha, Beta and Release Candidate levels must be documented where they differ from the package level.

## Sources

- [License: Apache-2.0](https://github.com/open-telemetry/opentelemetry-proto/blob/main/LICENSE)
- [open-telemetry/opentelemetry-proto on GitHub](https://github.com/open-telemetry/opentelemetry-proto)
- [Project website](https://opentelemetry.io/docs/specs/otlp/)
- [README](https://github.com/open-telemetry/opentelemetry-proto/blob/main/README.md)
- [Releases](https://github.com/open-telemetry/opentelemetry-proto/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/open-telemetry-opentelemetry-proto
