One interface, every protocol
openbindings.com
openbindings.com
The short version of why: programming languages have had interfaces and duck typing forever. You code to a shape, not an implementation. The web never got a successful equivalent at the network boundary. OpenBindings is an attempt at that.
What's here today: - The spec (v0.1.0): https://openbindings.com/spec - ob CLI: https://github.com/openbindings/ob - Go SDK: https://github.com/openbindings/openbindings-go - TypeScript SDK: https://github.com/openbindings/openbindings-ts - Binding executors for different protocols
Fastest way to try it: brew install openbindings/tap/ob ob demo
That starts a coffee shop service on six protocols. `ob op exec localhost:8080 getMenu` calls it. The CLI discovers the OBI (OpenBindings Interface) at /.well-known/openbindings and handles the rest.
Would love feedback on the spec design.
Claude wrote this
Except for the central registry of "stripe" and "payment-processor"?
Even purely on the technical level, this seemingly hasn't internalized the lessons of https://xkcd.com/927/
OBI operates at the contract level instead. Two services with different wire formats can satisfy the same interface as long as their operation shapes match. The binding executors handle the wire differences. That's the duck typing analogy. Match the shape, not the implementation.
You're right that the drivers are partly economic. But technical standards and economics aren't isolated from each other. Terraform, Kubernetes, and OpenAPI itself are technical solutions that enabled economic behavior that wasn't viable before them. Lowering the cost of interop changes what's economically rational to pursue.
On the xkcd, the post addresses this. OBI structurally can't replace OpenAPI, gRPC, or MCP. An OBI without sources and bindings pointing to them is an unbound contract, not an actionable interface. The dependency runs one way. Those specs are inputs, not competitors.
The flip side is worth noting too. If you own the API and want to preserve contract compatibility, OBI lets you change your implementation (paths, payload shapes, even protocols) behind the same contract. Transforms bridge shape differences. Binding swaps change protocols. Consumers targeting the interface don't need to care.
OBI is a boilerplate generator for the adapter pattern for service communication; its contracts are just another set of their own paths, payload shapes, and protocols. The distinction between “protocol” and “contract” in this context is nonsense.
Duck typing also requires being able to see a duck. Go has methods you can introspect. Raw REST doesn't. OpenAPI and gRPC reflection give you a structural surface, and OBI unifies those into one comparable form above the transport. What you're calling ad-hoc duck typing at the network boundary is humans reading docs and guessing whether the shapes match. That's a weaker version of the same idea, without the mechanical verification that makes duck typing useful. On "just another set of paths and protocols," OBI operation keys are transport-neutral. `tasks.create` has no route baked in. Bindings say how to reach it: REST at POST /tasks, gRPC at TaskService.Create, MCP as a tool. Same operation, multiple bindings, one identifier.
Thanks for the sharpening!
Also “tasks.create” is morally a route. Eg grpc has web transports and autogenerated CLIs too; even without those, there’s no particular reason why the OBI “client layer” couldn’t just be, say, gRPC under the hood, with the OBI specs just being used to codegen adapters from the source protocol to gRPC. It would then immediately have a much more widely understood and supported client layer, with better performance, while remaining as simple to implement as the adapters to this new custom OBI runtime protocol.
On the gRPC-as-client-layer point, I want to make sure I understand. You're suggesting that between the OBI client SDK and the source service, there should be a gRPC layer, with generated gRPC adapters fronting each source? Or something else?
Worth noting either way: the spec doesn't dictate implementation. It defines operation contracts, binding sources, and the executor role. How an executor actually reaches a service is left open, so gRPC-based routing is a valid strategy within the spec regardless.
Thanks for sticking with this. This is exactly the kind of discussion I was hoping for. I really do appreciate it.
[1]: https://github.com/grpc/grpc-go/blob/06fc26a196350499dd0cf2d...
If you're open to it, I'd be interested in staying in touch as OBI evolves. I know you're not sold on the project, but critical voices willing to engage constructively for this long are rare and valuable regardless. If you ever want to poke at something or tell me where you think it's going wrong, I'm at https://x.com/clevengermatt, and there's the GitHub discussions.
Man I hate JSON so much.
That said, if it feels buried, I'll look at surfacing it more clearly.
You're right about the gRPC fidelity issues though. int64 precision, oneof vs oneOf semantics, enum value mapping, and well-known types all need careful handling when binding to Proto. The tradeoff is that JSON Schema is already the schema format inside OpenAPI, AsyncAPI, and MCP, so OBI can reference their schemas directly without translation. Proto would have given better fidelity for gRPC but required schema translation for every other binding. Picking JSON Schema prioritizes cross-binding reach over depth in any one protocol.
The fidelity limitations deserve clearer documentation. Adding that to the list. Thanks for pushing on this.
The programming language analogy is about structural compatibility between contracts, not about the wire format the contract is written in.
What are the limitations of json schema?
OBI needs its schema format to be universally parseable, deterministic to compare, and rich enough to describe the portable subset of any binding format's type system. JSON Schema meets those requirements without tying the spec to any one ecosystem. Proto-level fidelity would center gRPC. A custom IDL would fragment tooling. JSON Schema is the neutral ground.
The limitations are real, especially with proto. For example, our own proto interface creator implementation maps 32-bit integers to JSON Schema `integer` and 64-bit integers to `string` to avoid JSON precision loss beyond 2^53, following Proto3's canonical JSON mapping. Proto enums are int-valued with names; JSON Schema enums are typically strings. Proto's oneof differs semantically from JSON Schema's oneOf. Maps with non-string keys can't be expressed directly.
These affect cross-service structural comparison, not execution. Executors handle source types natively on the wire, so int64 stays int64 when you actually make a call. Transforms bridge shape differences between operation schemas and binding sources at runtime. The comparison layer works at a coarser grain in v0.1 because that's what makes it deterministic and universally implementable. Future profile versions can tighten comparison precision as the ecosystem converges on the tradeoffs worth making.
An OBI gives an AI agent typed operations and bindings it can use on the first try. No doc parsing, no guessing at endpoints. And AI can generate OBIs from existing specs or use the ob cli to do it for them.
AI and structured contracts aren't competing concepts. Good interface design matters as much for AI as it does for us. Maybe more.
The longer game is vendor-published OBIs and shared interface roles (e.g. a neutral object-storage interface that S3 and GCS both satisfy), but one OBI, generated from specs that already exist, is still useful on day one.