The ported docs still framed alknet siblings (alknet-core/-call/-channels/
-http/-tls/-client) as live load-bearing context of this crate. With the
extraction complete and alknet headed for glue-level replacement, the docs
now speak as alkcall and cite the real sibling crates (alkhttp, alktls,
alktty, alktunnels, alktrader).
- ADR-054 (new): re-own the verifier-selection rule in-crate — three
remote roles (public X.509 endpoint / transport relay / hub),
ServerCertVerifier by PeerEntry presence, RemoteIdentity None
load-bearing. Provenance: alknet ADR-034 (never ported), demoted to
footnote. Resolves OQ-21's cross-repo authority gap.
- OQ-43 (new, deferred(scope)): composition-layer ownership and shape
after the alknet replacement — the named receiver for the
downstream-side deferrals (OQ-33 endpoint layer, OQ-41 QUIC-native
substrate, OQ-42 hub broker, OQ-19 persistence adapters, ADR-045 dial
seam). README layering diagram redrawn with the composition layer as
TBD-ownership instead of "alknet / alknode" in concrete.
- Spec docs reframed: titles, self-references, dial/TLS/from_jsonschema
homes, malformed legacy cite rows removed; dead relative research
paths qualified as source-repo paths.
- ADR amendments (append-only): 027 (alknet-http -> alkhttp), 031 (post-
extraction state), 044 (split landed as modules in alkcall), 045
(name normalization; citation-space note).
- Citation hygiene: unprefixed/colliding source-repo cites prefixed
("alknet ADR-NNN (provenance)") across 009/011/022/034/035/038/040/
043/044/045; stale OQ cites corrected (session registries OQ-04, batch
semantics OQ-03, verifier rule ADR-054); stale anchors fixed.
- README provenance note consolidated; citation conventions documented.
Verification: markdown link check (all local links resolve, all in-crate
ADR cites point at existing decisions/ files); table integrity check
(uniform row widths across open-questions.md and edited tables); residual
alknet-mention sweep (only provenance/historical/intentional framing
remains); architecture review subagent pass with zero remaining criticals.
Docs-only change — no code paths touched.
10 KiB
ADR-027: from_jsonschema as an HTTP-Backed Single-Endpoint Adapter in alknet-http
Status
Accepted (supersedes the from_jsonschema clause of ADR-022 §5 and the
FromJsonSchema provenance row of ADR-018 — both described a schema-only,
no-handler adapter in alknet-call)
Context
from_jsonschema was originally specified (ADR-022 §5) as a schema-only
adapter living in alknet-call: it produced HandlerRegistration bundles
with a NOT_FOUND-returning placeholder handler and FromJsonSchema
provenance. The stated use case was validation, discovery, and
composition-graph construction without a runtime — type-checking a
composition plan without executing it, building a UI of available
operations without standing up the transports.
This is broken. An operation in the OperationRegistry needs a real
handler. A placeholder that returns NOT_FOUND does not work with how
the registry is supposed to function: an Internal op registered with
a dead handler is a trap, not a feature. The "schema-only, no handler"
concept conflated two things — schema validation (a compile-time /
planning activity that doesn't need a registry entry at all) and
operation registration (which always needs a handler). Validation
against a JSON Schema does not require a HandlerRegistration; it
requires the schema and a validator. Registering an operation requires
a handler. The old from_jsonschema tried to do the former by abusing
the latter, and produced something that works for neither.
The misplacement was compounded by a location error: the adapter lived
in alknet-call (which is supposed to stay lean — no HTTP client), but
a from_jsonschema that is actually useful for calling non-standard
endpoints needs reqwest, exactly like from_openapi and from_mcp.
The adapter location map in ADR-022 / client-and-adapters.md already
establishes that HTTP-backed adapters live in alknet-http; the old
from_jsonschema violated its own stated principle by living in
alknet-call.
A concrete use case now forces the decision: composing a non-standard,
non-OpenAPI, basic REST endpoint that does not have a full OpenAPI
document. The endpoint has a method, a URL, an input/output JSON Schema,
and an auth scheme — but no paths object, no operationId, no
components. from_openapi requires an OpenAPI document; this endpoint
doesn't have one. The gap is: register a single HTTP endpoint as a
call-protocol operation, one at a time, with the caller supplying the
schema directly.
Decision
from_jsonschema becomes an HTTP-backed single-endpoint adapter in
alknet-http, functionally similar to from_openapi but registering
one endpoint at a time instead of parsing a full OpenAPI document:
-
Move the adapter implementation to
alknet-http(crates/alknet-http/src/adapters/from_jsonschema.rs). The forwarding handler uses the same reqwest-backedSharedHttpClientand the same no-env-vars credential injection asfrom_openapi. The adapter implementsOperationAdapter(the trait fromalknet-call, ADR-022 §5 — unchanged). -
Give it a real forwarding handler. A
from_jsonschema-imported operation is a leaf with a reqwest forwarding handler, identical in shape to afrom_openapi-imported operation — it builds an HTTP request from the input (path/query/body split per a path template), injects credentials fromcontext.capabilities, sends via the shared HTTP client, and parses the response (JSON, text, or binary — same content-type branching asfrom_openapi). For aSubscriptionop type withtext/event-streamresponse, it registers aStreamingHandler(ADR-021), same asfrom_openapi. -
Single-endpoint registration. The caller supplies:
- An
OperationSpec(name, op type, input/output JSON Schema,error_schemas,access_control,visibility). - An
HttpServiceConfig(base URL, auth scheme, default headers — the same config typefrom_openapiuses). - A path template + HTTP method (the one endpoint).
The adapter builds one
HandlerRegistrationwithFromJsonSchemaprovenance and a real forwarding handler. The caller registers it in theOperationRegistry. This is the "one endpoint at a time" shape: nopathsobject to iterate, nooperationIdto normalize. - An
-
FromJsonSchemaprovenance stays inalknet-call(in theOperationProvenanceenum,registration.rs). The provenance type lives where the registry types live; only the adapter implementation moves.FromJsonSchemais now a leaf provenance — it has a handler (a reqwest forwarding handler), same trust model asFromOpenAPI(HTTP endpoint trusted; handler is a forwarding stub). -
Remove the "schema-only, no handler" concept. The placeholder handler and the "schema-only ops are
Internal, so dispatch should never reach them" rationale are removed. An op registered withFromJsonSchemaprovenance is a real, callable, HTTP-forwarding operation —Internalby default (adapter-registered ops are composition material, ADR-017), but it actually forwards if invoked.The schema-validation-without-a-handler use case (type-checking a composition plan, building a UI) does not require a
HandlerRegistrationat all. That use case is served by consuming theOperationSpecdirectly (the spec already carries the input/ output JSON Schemas); no adapter, no registry entry, no handler is needed. If a future use case requires registering a schema-only op for discovery purposes, that is a separate feature and would warrant its own ADR — it is not whatfrom_jsonschemais.
Relationship to from_openapi
from_openapi |
from_jsonschema |
|
|---|---|---|
| Input | A full OpenAPI 3.x document (JSON or YAML) | A single endpoint: OperationSpec + HttpServiceConfig + path template + method |
| Granularity | One HandlerRegistration per (path, method) in the doc |
One HandlerRegistration per call |
| Schema source | Parsed from the OpenAPI doc (parameters, request body, responses) | Supplied directly by the caller |
| Handler | reqwest forwarding handler (shared HTTP client) | Same reqwest forwarding handler |
| Provenance | FromOpenAPI |
FromJsonSchema |
| Location | alknet-http |
alknet-http |
| Use case | Standard OpenAPI APIs (GitHub, OpenAI, Anthropic) | Non-standard, non-OpenAPI, or basic REST endpoints without a full spec |
The two adapters share the forwarding-handler implementation, the
credential injection path, the error-fidelity rule (HTTP_<status>
prefix, ADR-016), and the no-env-vars invariant (ADR-010). The
difference is purely the input shape: a full document vs. a single
endpoint.
Consequences
Positive:
from_jsonschemaactually works — it has a real handler, not a placeholder. A concrete use case (non-standard REST endpoints) is served.- The adapter location is consistent: all HTTP-backed adapters
(
from_openapi,from_mcp,from_jsonschema) live inalknet-http, where reqwest is.alknet-callstays lean. - The "schema-only, no handler" trap is removed. An op in the registry is always callable.
FromJsonSchemaprovenance becomes a real leaf, consistent withFromOpenAPI/FromMCP/FromCall.
Negative:
- The schema-validation-without-a-handler use case (the original stated
purpose) is no longer served by
from_jsonschema. That use case is served by consumingOperationSpecdirectly, but any code that relied on the placeholder handler returningNOT_FOUNDbreaks. The only existing consumer is the call crate's own tests; no downstream consumer depended on this — the placeholder was a trap, not a contract. alknet-callloses a public export (from_jsonschema,FromJsonSchemathe adapter struct). TheFromJsonSchemaprovenance variant stays; the adapter struct moves. Downstream consumers that referenced the adapter (none currently) would need to usealknet-http's re-export.
Neutral:
FromJsonSchemaprovenance is now a leaf (handler-bearing), not a "no handler" provenance. The ADR-018 table row updates: it can compose? No. Has composition authority? No. Default visibility? Internal. Trust model? HTTP endpoint trusted; handler is a forwarding stub. This aligns with the other leaves. ADR-022 §5 and ADR-018's provenance table/enum-doc are amended (2026-07-09) to point here — the supersession is recorded in the superseded ADRs, not only in this one.
References
- Supersedes the
from_jsonschemaclause of ADR-022 §5 ("FromJsonSchema— imports from a JSON Schema definition (schema-only, no handler)") and the operational spec indocs/architecture/crates/call/client-and-adapters.md§"from_jsonschema". - Supersedes the
FromJsonSchemarow of ADR-018 (the "no handler — schema only" framing). - Aligns with the adapter location principle in
ADR-022 §5 and
client-and-adapters.md§"Adapter Location Map": HTTP-backed adapters live inalknet-http. - Reuses the forwarding handler, credential injection, error fidelity
(
HTTP_<status>prefix, ADR-016), streaming shape (ADR-021), and no-env-vars invariant (ADR-010) established byfrom_openapi. - Reuses
HttpServiceConfigandSharedHttpClientfromfrom_openapi(inalknet-http).
Amendment 2026-10-13: name normalization (alknet-http → alkhttp)
This ADR's decision text was authored when the HTTP host crate was
named alknet-http inside the alknet mono-repo. The HTTP host is now
the standalone sibling crate alkhttp, which wraps alkcall
(extraction context: ADR-031; the adapter-location rule is unchanged —
the trait lives in alkcall, HTTP-backed adapters live in alkhttp).
The title's "in alknet-http" and the body's crate-name references
should be read as "in alkhttp"; the References below link to the
source-repo originals (provenance). The FromJsonSchema provenance
variant remains in alkcall.