ADR-0033: The kernel wire route surface — /sys/<verb>/<name>
ADR-0033: The kernel wire route surface — /sys/<verb>/<name>
Status
Accepted. Supersedes the routing and doorway decisions of
ADR-0028, and amends the error
type-URI spelling of ADR-0014
(/sys/errors/{CODE} → /sys/meta/errors/{CODE}). 0028’s
QuerySpec-is-the-wire-surface thesis, condition tree, envelope, delivery matrix,
and conformance discipline stand unchanged; so do 0014’s error-model decisions
apart from that URI spelling.
Date
2026-07-04
Context
ADR-0028 ratified that QuerySpec is
the wire surface and the QueryIR never serializes, and named provisional doorway
routes: POST /v0/query, /v0/aggregate, /v0/series, /v0/subscribe. The
implementation then diverged from that — query-surface.md and the server
(crates/services/vinxi-kernel-server/src/routes.rs) grew a different, PascalCase
/sys/<verb>/<Name> surface (/sys/query/Entities, /sys/act/RegisterResourceVersion)
and treated ADR-0028’s /v0/* as shorthand for it. That /sys/* spelling was
never itself a governed decision, and the surface it grew is inconsistent:
- PascalCase in paths (
Entities,RegisterResourceVersion) — unusual, and since HTTP paths are case-sensitive, an invitation to/Entitiesvs/entities404s. - The third path segment means two different things: for Act it is an open,
reality-declared operation name (
RegisterResourceVersion); for Query a closed answer-kind (Entities). /sys/query/Subscribecollides with the fact that Subscribe is one of the six verbs (ADR-0002), not a Query answer-kind. The implementation folded it under the query pipeline; the six-verb model disagrees./sys/errorsis a resource-style observability outlier squatting under the same/sys/prefix as the RPC verbs, muddying what/sys/means.
The verb-first paradigm is not in question. The seam is a syscall surface (the-seam §V, “the syscall table”); ADR-0002 makes the six verbs the only interaction surface; ADR-0028 explicitly rejected resource-REST and GraphQL for the read seam (per-field resolvers invite the post-hoc policy DP-4 bans). What was missing is one governed spelling rule.
Decision
One rule: POST /sys/<verb>/<name>. Lowercase-kebab. The path is operation
identity, the query string is modifiers (?explain=…, ?dryRun=…), the
body is payload (the QuerySpec, the Act input, ReadOpts). The name is always
in the path — never a query param, never a body discriminator — so a gateway,
audit log, per-operation metric, rate-limit, or capability grant can key off the
URL without parsing the body (the transport-legibility DP-4 wants).
No version segment. QuerySpec is v0 — one grammar that evolves compatibly
(extensions + NOT_YET_SERVING staging + the conformance mirror), so a path
version would be dead weight. The “v2/v3” labels were parallel-workstream churn
and are retired. A header/content-type version is reserved as the break-glass
for a genuinely-breaking future ABI change; it is a floor, never a licence to skip
compatible evolution.
The six verbs organize the surface (ᴿ = ratified shape, HTTP execution
reserved; only /sys/query/entities and /sys/act/register-resource-version are
served today):
Query (one-shot pull) Subscribe (standing push) /sys/query/entities /sys/subscribe/view ᴿ /sys/query/aggregate ᴿ /sys/subscribe/events ᴿ (undesigned) /sys/query/series ᴿAct /sys/act/<action> served: register-resource-version ; reserved base: create-entity · install-schema ; reality-declared: open-ticket · raise-alarm · … (logic in userland — ADR-0031)Assert /sys/assert/<fact> ᴿ series · link · stateCall /sys/call/<driver-op> ᴿSchedule /sys/schedule/<name> ᴿ- Subscribe is its own verb, not a Query doorway.
<name>says what you subscribe to —view(a QuerySpec, membership diffsenter|update|leave) orevents(a governed, seam-filtered event stream — reserved, distinct from the raw-CDC 0028 banned). Query = pull answer-kinds; Subscribe = push subscription-kinds. Pull-vs-push is the verb boundary. - Act
<name>may be domain vocabulary — an Action is a declared, kernel-visible, SDK-projected operation-type (the-seam §VIII), so the reality extends the Act namespace with its declared actions. - Assert
<name>is a domain-neutral fact-kind —seriesandlinkexist as kernel syscalls (assert_series,assert_link);stateis reserved — entity state is written today via the Actcreate-entitypath, not a dedicated Assert. Assert declares facts, not named operations; there is no “assertion type” to name (ADR-0031). No/sys/assert/*HTTP doorway is mounted yet.
/sys/meta/<topic> (GET, read-only) is the kernel’s self-description, not a
verb: the error catalog (/sys/meta/errors, /sys/meta/errors/{code} — was
/sys/errors, amending ADR-0014’s type-URI), version, capabilities (the
SERVING vs NOT_YET_SERVING staging), and reality/schema/ontology introspection
— the installed kinds/traits/links/declared-actions/paths that sdkgen, the GraphQL
projection, and MCP manifests all project from. Two flavors: static (no context)
and scoped reality introspection (carries tenant/universe/branch). Reading the
schema is meta; changing it is an Act (/sys/act/install-schema) — introspection
is read-only, mutation is always a verb. /healthz and /metrics stay at root
(infra convention; not the seam).
Alternatives Considered
- Name-in-body (
POST /sys/actwith{action, input}, one route per verb): rejected — six stable routes, but every operation becomes invisible to the transport, forcing body-parsing policy (the DP-4 smell); the “unbounded Act paths” worry is moot since the path set is exactly the reality’s declared actions, which sdkgen enumerates. - Resource-REST (
/entities,/entities/{id}): already rejected by ADR-0028; the seam is verbs, not CRUD resources. - Path versioning (
/sys/v0/…): rejected now — the grammar evolves compatibly by construction; header versioning is reserved instead. - PascalCase paths: rejected — preserves the SDK symbol on the wire (a codegen convenience) at the cost of the URL idiom and case-sensitivity bugs; the kebab→symbol transform is deterministic and owned by sdkgen.
Consequences
- The live route
/sys/query/Entitiesrenames to/sys/query/entities;/sys/errors[/…]moves to/sys/meta/errors[/…], and the RFC-7807 errortypeURIs move with it. Small, mechanical, and it is the “fix the lame URL” the workstream set out to do. - ADR-0014’s error-model contract stands, but its
type-URI spelling is amended here (/sys/errors/{CODE}→/sys/meta/errors/{CODE}); the index carries the forward pointer since 0014 is immutable. - ADR-0028’s grammar, condition tree, envelope, delivery matrix, and conformance
discipline are untouched;
query-surface.mdis updated to the new routes and to retire the “v3” label (→ v0). - Subscribe leaving the Query doorways is a doc change now and a code change when
Subscribe’s execution rung lands (it is
NOT_YET_SERVINGtoday). - sdkgen, the GraphQL projection, and MCP tool manifests project from stable
verb + namepaths and the/sys/metaintrospection surface. /sys/is blessed as the kernel’s own namespace: the six-verb governed seam (POST /sys/<verb>/<name>, context + body) plus kernel-meta introspection (GET /sys/meta/<topic>, no verb).