Skip to content

schema

A stored message carries a dataschema. Three years later somebody has to answer what shape was this?, and the answer must be the same one a consumer would have got the day the message was written.

That is the whole job. Everything else here follows from it.

reg, err := schema.Embed(schema.Schema{
    URN:      urn.Schema{Name: "orders.created", Version: 3},
    Language: schema.JSONSchema,
    Bytes:    ordersCreated,   // //go:embed schemas/orders.created.v3.json
})

guard := schema.NewGuard(reg, []schema.Validator{jsonschema.New()})
err = guard.Validate(ctx, event.DataSchema, event.Data)

Three properties worth knowing before anything else

A published identifier cannot change

Republishing the same URN with different bytes is refused. Without that, a consumer that validated a message yesterday cannot reproduce the result today, and every stored dataschema becomes a claim about something that has since moved.

It resolves offline. A service embeds its schemas and validates without a network call. A service that must reach the registry to validate has taken a runtime dependency on it, which is what a registry should most avoid being.

It fails open, and says so in a number. A dataschema that cannot be resolved does not stop the message; it increments a counter. A registry outage is a degradation, not an outage of everything downstream — and the counter is what stops that being silent.

Start here

  • Getting started

    Embed a schema, validate a payload, and see the fail-open counter move.

  • Embed your schemas

    The offline path, which is the one nearly every service wants.

  • The HTTP API

    Four endpoints. Why they are not Confluent's.

  • Why it fails open

    The asymmetry: unresolvable fails open, non-conforming fails closed.

Not built yet, and honestly labelled

There is no compatibility checking. A version bump asserts only that the bytes differ; it is not a compatibility claim and nothing should read it as one.

The gap is shaped rather than left open: the publish API already takes a compatibility mode, a Checker interface already exists per language, and the store already keeps prior versions. When a checker arrives it is an implementation, not a refactor of every call site.