Run the registry¶
This module does not own a server. go/transport already
provides one with TLS, body limits, security headers, health endpoints and go/controls lifecycle,
and a registry that reimplemented those would be a second answer to a question the estate has
answered once.
Register it with the controller¶
srv, err := serve.Register(ctx, "schema-registry", controller, logger,
registry, // the read half
registry, // the write half, or nil for read-only
transporthttp.ServerSettings{Host: "0.0.0.0", Port: 8080},
serve.Authenticate(requireToken),
)
Register does not start anything — go/controls does, when it starts the service. A
misconfiguration therefore fails at wiring, where an operator is watching, rather than at the first
request.
The write path needs authentication¶
Mounting a publisher with nothing in front of it is refused
Publishing is privileged and reading is not, and the two handlers are only separate so that difference survives wiring. A mount that produced an open write path by omission would undo that in the one place nobody looks.
If it is genuinely wanted — a test, or a network whose boundary is somewhere else entirely —
serve.AllowAnonymousPublishing() says so out loud and leaves a grep-able string in the source.
The read handler is mounted at the root and the write handler under /publish, so a reverse proxy
or a policy engine has one prefix to protect rather than a method to discriminate on.
Your middleware sees the path as it arrived
PUT /publish/schemas/orders.created/3, prefix intact. The prefix is stripped inside the
middleware, not outside it.
That ordering is load bearing. go/transport's AuthMiddleware keys on r.URL.Path, and
WithAuthSkipper predicates on the request — so if the prefix were stripped first, the natural
policy "reads are public, skip auth under /schemas" would skip authentication for every
publish, and the audit log would record each one as a read.
Read-only is the common deployment¶
Schemas published by CI, read by everything:
srv, err := serve.Register(ctx, "schema-registry", controller, logger,
registry, nil,
transporthttp.ServerSettings{Host: "0.0.0.0", Port: 8080},
)
No authentication is needed and none is demanded. /publish answers nothing.
Point a service at it¶
resolver, err := client.New("https://schema.internal.example")
if err != nil {
return err
}
guard := schema.NewGuard(resolver, []schema.Validator{jsonschema.New()})
The client bounds itself to two seconds by default. A consumer is holding a message and deciding whether to validate it; waiting thirty seconds to learn the registry is down is worse than failing open in two, which is what the guard does anyway.
Storage¶
MemoryStore is what ships. It is a perfectly good backing store for a single instance whose
schemas are published by CI on every deploy, and it is not durable.
Anything else implements the four-method Store
interface. One requirement is not negotiable: prior versions must be retained. A store that kept
only the latest would make the compatibility retrofit require a backfill nobody has the data for.