Who picks the registry?
A kaiv reference like hub/paylog names a schema; four
resolution layers decide where its bytes come from. On ranking
those layers by who authors them — and on the two flags and one
build profile that let a consumer refuse every hand but their
own.
A validation verdict is only as trustworthy as the schema it ran
against. pass — against what, exactly? A kaiv document names
its contract with a registry reference: .!schema:hub/paylog
says “this file satisfies the hub payment-log schema,” and the
schema registry binds
that name to bytes exactly once, forever. So far, so citable.
But a name is not bytes. Between the reference and the schema that actually loads sits resolution, and kaiv deliberately lets several parties influence it: the document itself, the consumer’s project configuration, environment variables, the registry’s own redirects. Each exists for a reason — vendoring, air-gapped builds, private registries, enterprise mirrors. Each is also a hand on the steering wheel. This article is about whose hands they are, and how a consumer takes any of them — or all of them — off.
Four layers, ranked by author
Resolution consults four layers, most specific first:
| Layer | Mechanism | Authored by |
|---|---|---|
| 1 | .!registry declarations in the document |
the producer |
| 2 | kaiv.kaiv / KAIV_REGISTRY_* environment |
the consumer |
| 3 | registry redirect aliasing (HTTP 301/302) | the namespace owner |
| 4 | the default registry hosts | the format |
The column that matters is the last one. Layer 2 is your own project file; if it lies, your build environment was already lost. Layers 3–4 are the registry authority, and what you trust there is immutability: a published artifact never changes, so a cached copy is valid forever. Layer 1 is different in kind: it is written by whoever wrote the document — and it outranks everything.
A document that picks its own registry
That priority is exactly right for the producer’s legitimate uses, and exactly wrong for one scenario: a document you did not write, making claims about what it satisfies. Consider:
$ cat suspect.kaiv
.!kaiv
.!registry hub=./mirror
.!schema:hub/paylog
&float
amount=99999.00
payee=EVIL LLC
The reference says hub/paylog. The declaration above it says
“resolve hub from ./mirror” — a base the producer ships
alongside the document, serving a hub/paylog of the
producer’s own composition, with the constraints filed off.
Both declarations survive the build, and validation does what
the document asks:
$ kaiv build suspect.kaiv > suspect.daiv
$ kaiv -v validate suspect.daiv
pass
kaiv: resolved hub/paylog.csaiv via layer 1 (.!registry) ./mirror -> ./mirror/hub/paylog.csaiv
pass — says who? The schema identity is intact; identity is
a logical path, and no resolution layer can change what
hub/paylog means as a name. What the producer chose is the
content bound to that name for this run. The verdict is
real, the contract is counterfeit, and nothing about the
document’s face gives it away — only the -v provenance line
under the verdict, which reports each resolved artifact with
the layer that won and where the bytes actually came from.
Refusing the producer’s hand
The fix cannot be a marker in the document — any syntax the producer writes, the producer controls. It has to be a refusal on the consumer’s side:
$ kaiv --registry-strict validate suspect.daiv
kaiv: RegistryStrictError: .!registry hub=./mirror would control resolution of hub/paylog.csaiv; rerun without --registry-strict, or vendor the artifact via kaiv.kaiv
Strict mode draws the line at authorship: a .!registry
declaration that would determine where an artifact comes from
is refused, before any read or fetch. Note the precision —
declarations themselves are not errors. They survive into
canonical form by design, so a document may carry dormant
.!registry lines for prefixes nothing resolves; strict mode
refuses only the one that would actually win.
And the refusal is not a dead end; it names the sanctioned way
through. Under strict mode the layer order inverts: your
kaiv.kaiv shadows the document’s declaration for every
prefix it covers. Vendor the real schema, point your
configuration at it, and the document’s steering wheel comes
off in your hands:
$ printf '.!kaiv\n\n/registries::hub=./vendor\n' > kaiv.kaiv
$ kaiv --registry-strict validate suspect.daiv
kaiv: ConstraintViolationError: ::amount=99999.00 (type !float) violates !float[0,10000] (line 4)
Same document, same reference, same command shape — and the
verdict flips, because the schema is now the one you chose:
the real hub/paylog, which caps amount at 10000. The
counterfeit pass becomes the true fail. That is the whole
design in one diff: strict mode does not decide what to trust,
it moves the decision to the party running the validator.
Peace of mind, spelled --registry-canonical
Strict mode still honors your own overrides — which means a
forgotten kaiv.kaiv three directories up, or a
KAIV_REGISTRY_HUB exported in some CI image, still steers
resolution. Sometimes the assurance you want is stronger: this
verdict depended on nothing but the reference and the canonical
hosts. That is canonical mode:
$ KAIV_OFFLINE=1 KAIV_CACHE_DIR=./nocache kaiv --registry-canonical validate suspect.daiv
kaiv: SchemaResolutionError
Under --registry-canonical, Layers 1–2 are not refused — they
are ignored. The document’s ./mirror, the kaiv.kaiv we
just wrote, any environment override: none are consulted, and
the only place hub/paylog can come from is the default
registry. The transcript proves it the blunt way — offline,
with an empty cache, resolution has nowhere to go and fails,
even though two overrides that both resolve locally are sitting
right there. Online, the same command would fetch the genuine
article from the schema registry and validate against that,
full stop.
The two flags answer different questions. Strict protects a consumer from a document’s overrides and refuses loudly; canonical asserts a provenance — no override anywhere, so there is nothing to refuse and Layer 1 is dormant by construction. Canonical subsumes strict.
The embedded end of the spectrum
One end of the spectrum trusts everything; where is the other end? Not canonical mode — canonical still trusts the network. Keep going.
The reference implementation’s network stack is a cargo feature
(net, on by default), and switching it off is a supported
build, not a degraded one: the core pipeline has zero
dependencies. In a no-net build there is no HTTP, no cache,
no registry client — a resolution whose winning base is a URL
fails at exactly the point it would have fetched. What still
works is precisely what an embedded device should rely on:
$ printf '.!kaiv\n&int\nport=8080\n' | KAIV_OFFLINE=1 KAIV_CACHE_DIR=./nocache kaiv build
.!daiv
!int'::port=8080
&int resolves because std/core is compiled into the binary
— the standard library is not fetched, it is embedded. Beyond
that, an embedding host can preload artifact bytes directly
into the resolver from memory, which is how a device ships with
exactly the schemas it was provisioned with and can never be
talked into loading another. Core and preloaded artifacts: that
is the complete resolution surface, by construction rather than
by flag.
And beneath even that sits the quietest layer of the design:
the certified runtime never resolves at
all. Resolution
is a build-time activity; what crosses the certification
boundary is .daiv plus compiled .csaiv, names already bound
to bytes. The device-side reader does not have a resolver to
harden.
The spectrum, stated
| You are willing to trust | Run with |
|---|---|
| the document’s own declarations | (default) |
| your configuration, else the registry | --registry-strict |
| the canonical registry alone | --registry-canonical |
| only what was compiled or preloaded in | a no-net build |
Four positions, one axis: whose resolution choices can reach you. The default honors all of them, because vendoring, mirrors, and air-gapped trees are real and legitimate. Each step down removes one author’s hand — the producer’s, then yours, then the network’s — until the only schemas a reader can consult are the ones it was born with. kaiv does not pick a point on this spectrum for you. It makes every point explicit, one flag apart, with the provenance line to tell you where you are standing.
kaiv is an immutable structural type system for data at rest. The specification § Type Registry Resolution defines the four layers, strict mode, and canonical mode normatively; the eternalink promise covers what the registries themselves guarantee; the playground runs the toolchain — build and validation included — entirely in your browser.