docs(arch): ADR-066 — move from_jsonschema to alknet-http as HTTP-backed single-endpoint adapter
from_jsonschema was in alknet-call as a schema-only placeholder with a NOT_FOUND handler — broken (an op in the registry needs a real handler) and in the wrong crate (alknet-call has no HTTP client; a useful from_jsonschema needs reqwest like from_openapi). ADR-066 moves it to alknet-http as a real reqwest-backed single-endpoint adapter for non-standard/non-OpenAPI REST endpoints, functionally similar to from_openapi but one endpoint at a time. FromJsonSchema provenance stays in alknet-call (now a handler-bearing leaf). - New ADR-066 (supersedes ADR-017 §5 from_jsonschema clause + ADR-022 FromJsonSchema row; both amended with strikethrough + pointer) - Updated specs: call/client-and-adapters, call/README, call/operation-registry, http/http-adapters (new from_jsonschema section), http/overview, http/README, arch README + overview - New task: tasks/http/adapters/from-jsonschema.md (depends on from-openapi; includes alknet-call cleanup of the broken placeholder) - Old task tasks/call/client/from-jsonschema.md marked superseded taskgraph validate: 116 tasks valid
This commit is contained in:
1 parent
06ab5db459
commit
97456bc608
13 files changed
+739
-95
No files matched your search
@@ -14,7 +14,7 @@ Structured RPC: operations, request/response, streaming subscriptions, and servi
|
||||
|----------|--------|-------------|
|
||||
| [call-protocol.md](call-protocol.md) | draft | CallAdapter, hand-rolled EventEnvelope framing (no irpc — ADR-064), stream model, PendingRequestMap, bidirectional calls |
|
||||
| [operation-registry.md](operation-registry.md) | draft | OperationSpec, Handler, OperationRegistry, AccessControl, service discovery, hand-rolled framing (no irpc — ADR-064) |
|
||||
| [client-and-adapters.md](client-and-adapters.md) | draft | CallClient (outbound connection opener), from_call / from_jsonschema, OperationAdapter trait, adapter location map, no-env-vars invariant, exchange-of-operations pattern |
|
||||
| [client-and-adapters.md](client-and-adapters.md) | draft | CallClient (outbound connection opener), from_call, OperationAdapter trait, adapter location map, no-env-vars invariant, exchange-of-operations pattern (from_jsonschema moved to alknet-http per ADR-066) |
|
||||
|
||||
## Applicable ADRs
|
||||
|
||||
@@ -36,7 +36,8 @@ Structured RPC: operations, request/response, streaming subscriptions, and servi
|
||||
| [014](../../decisions/014-secret-material-flow-and-capability-injection.md) | Secret Material Flow and Capability Injection | Call protocol carries no secret material; capabilities injected at assembly layer |
|
||||
| [015](../../decisions/015-privilege-model-and-authority-context.md) | Privilege Model and Authority Context | `internal` = authority switch not ACL skip; External/Internal visibility; handler identity + scoped env |
|
||||
| [016](../../decisions/016-abort-cascade-for-nested-calls.md) | Abort Cascade for Nested Calls | `call.aborted` cascades to descendants; default `abort-dependents`, `continue-running` opt-in |
|
||||
| [017](../../decisions/017-call-protocol-client-and-adapter-contract.md) | Call Protocol Client and Adapter Contract | `CallClient` opens connections; `from_call` imports remote ops; connection direction independent of call direction |
|
||||
| [017](../../decisions/017-call-protocol-client-and-adapter-contract.md) | Call Protocol Client and Adapter Contract | `CallClient` opens connections; `from_call` imports remote ops; connection direction independent of call direction. ~~`from_jsonschema` clause superseded by ADR-066~~ |
|
||||
| [066](../../decisions/066-from-jsonschema-as-http-adapter.md) | `from_jsonschema` as HTTP-Backed Single-Endpoint Adapter in alknet-http | Moved `from_jsonschema` from `alknet-call` (broken schema-only placeholder) to `alknet-http` as a real reqwest-backed single-endpoint adapter; `FromJsonSchema` provenance stays in `alknet-call` as a leaf |
|
||||
| [022](../../decisions/022-handler-registration-provenance-and-composition-authority.md) | Handler Registration, Provenance, and Composition Authority | Registration bundle carries provenance, composition authority, scoped env, capabilities |
|
||||
| [023](../../decisions/023-operation-error-schemas.md) | Operation Error Schemas | Operations declare domain errors; `call.error` carries typed `details`; adapter fidelity |
|
||||
| [024](../../decisions/024-operation-registry-layering.md) | Operation Registry Layering | Curated (static) + session/connection overlays (dynamic); `OperationEnv` as trait-object integration point; `OperationContext.env` split into `scoped_env` (data) and `env` (dispatch trait) |
|
||||
@@ -79,8 +80,8 @@ Structured RPC: operations, request/response, streaming subscriptions, and servi
|
||||
7. **No secret material on the wire**: The call protocol carries no private keys, API keys, mnemonics, or decrypted credentials. Handlers receive outbound credentials through `OperationContext.capabilities`, injected at the assembly layer. See ADR-014.
|
||||
8. **Abort cascades to descendants**: `call.aborted` for a parent request cascades to all non-terminal descendants. Default `abort-dependents`; `continue-running` opt-in. See ADR-016.
|
||||
9. **Internal calls switch authority context, not skip ACL**: The `internal` flag marks composition-originated calls. ACL runs against the handler's composition authority, not the caller's and not as a blanket skip. Operations have External/Internal visibility. Scoped composition env bounds reachability. See ADR-015, ADR-022.
|
||||
10. **Provenance determines composition capability**: Only `Local` and `Session` ops can compose. Leaves (`FromOpenAPI`, `FromMCP`, `FromCall`) are forwarding stubs — they don't get composition authority or a scoped env. The assembly layer is the sole grantor of composition authority. See ADR-022.
|
||||
10. **Provenance determines composition capability**: Only `Local` and `Session` ops can compose. Leaves (`FromOpenAPI`, `FromMCP`, `FromCall`, `FromJsonSchema`) are forwarding stubs — they don't get composition authority or a scoped env. The assembly layer is the sole grantor of composition authority. See ADR-022. (`FromJsonSchema` is now a real HTTP-forwarding leaf per ADR-066, not a schema-only placeholder.)
|
||||
11. **Connection direction is independent of call direction**: Who opens the QUIC connection is a connection-layer concern, not a protocol-layer concern. Both sides can call each other once connected. The `CallAdapter` accepts connections; the `CallClient` opens them; both produce the same `CallConnection` and dispatch through the same loop. See ADR-017, [client-and-adapters.md](client-and-adapters.md).
|
||||
12. **Peer authorization via `AccessControl`**: A remote peer's call is authorized by `AccessControl::check(peer_identity)` against the op's `AccessControl` — the same mechanism that gates every other call. No `remote_safe` flag, no `trusted_peer` bypass. An op with `AccessControl::default()` is callable by any peer; an op with `required_scopes` is callable only by peers whose `Identity.scopes` satisfy them; an op with `Visibility::Internal` is never callable from the wire. See ADR-029.
|
||||
13. **Adapter trait lives with the types; implementations live with their transport**: `OperationAdapter` is in `alknet-call`; `from_call`/`from_jsonschema` are in `alknet-call` (QUIC / pure parse); `from_openapi`/`from_mcp`/`to_openapi`/`to_mcp` are in `alknet-http` (reqwest / axum). `alknet-call` stays lean — no HTTP client, no HTTP server. See [client-and-adapters.md](client-and-adapters.md).
|
||||
13. **Adapter trait lives with the types; implementations live with their transport**: `OperationAdapter` is in `alknet-call`; `from_call` is in `alknet-call` (QUIC); `from_jsonschema`/`from_openapi`/`from_mcp`/`to_openapi`/`to_mcp` are in `alknet-http` (reqwest / axum). `alknet-call` stays lean — no HTTP client, no HTTP server. (`from_jsonschema` was originally in `alknet-call` as a schema-only placeholder; ADR-066 moved it to `alknet-http` as a real HTTP-backed adapter.) See [client-and-adapters.md](client-and-adapters.md).
|
||||
14. **No handler reads outbound credentials from any source other than `OperationContext.capabilities`** (no-env-vars invariant): the credential injection path is vault → assembly layer → `Capabilities` → `HandlerRegistration.capabilities` → `OperationContext.capabilities` → handler. Downstream consumers' `std::env::var` reads are unreachable because the assembly layer never calls `Default::default()`. See ADR-014, [client-and-adapters.md](client-and-adapters.md).
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
status: draft
|
||||
last_updated: 2026-07-02
|
||||
last_updated: 2026-07-09
|
||||
---
|
||||
|
||||
# alknet-call — Client and Adapters
|
||||
@@ -9,14 +9,16 @@ The outbound half of the call protocol: opening connections, importing remote
|
||||
operations, and the adapter contract that ties import-style adapters together.
|
||||
This document covers what ADR-017 specced but the server-side implementation
|
||||
(`call-protocol.md`, `operation-registry.md`) did not include — the `CallClient`
|
||||
that *opens* a connection, the `from_call`/`from_jsonschema` adapters, and the
|
||||
`OperationAdapter` trait. The server-side `CallAdapter` and `CallConnection`
|
||||
that *opens* a connection, the `from_call` adapter, and the
|
||||
`OperationAdapter` trait. (`from_jsonschema` was originally specced here
|
||||
too, but ADR-066 moved it to `alknet-http` — see §"from_jsonschema" below.)
|
||||
The server-side `CallAdapter` and `CallConnection`
|
||||
dispatch loop are covered in `call-protocol.md`; this document covers the
|
||||
client-side connection-establishment half and the adapter surface.
|
||||
|
||||
## What
|
||||
|
||||
This document specifies four components, all in `alknet-call`:
|
||||
This document specifies three components, all in `alknet-call`:
|
||||
|
||||
1. **`CallClient`** — opens an outbound `alknet/call` QUIC connection and
|
||||
produces a `CallConnection`. The dispatch loop is shared with the
|
||||
@@ -27,12 +29,19 @@ This document specifies four components, all in `alknet-call`:
|
||||
via `services/list` + `services/schema` (already implemented in
|
||||
`registry/discovery.rs`) and registers them in the connection's Layer 2
|
||||
overlay as `FromCall`-provenance leaves with forwarding handlers.
|
||||
3. **`from_jsonschema`** — schema-only registration: produces
|
||||
`HandlerRegistration` bundles with no handler, for validation, discovery,
|
||||
and composition-graph construction without a runtime.
|
||||
4. **`OperationAdapter` trait** — the async trait that `from_call`,
|
||||
3. **`OperationAdapter` trait** — the async trait that `from_call`,
|
||||
`from_openapi`, `from_mcp`, and `from_jsonschema` all implement.
|
||||
|
||||
> **`from_jsonschema` moved.** ADR-066 moved `from_jsonschema` from
|
||||
> `alknet-call` to `alknet-http` and gave it a real reqwest-backed
|
||||
> forwarding handler (it was a broken schema-only placeholder before).
|
||||
> It is now an HTTP-backed single-endpoint adapter for non-standard /
|
||||
> non-OpenAPI / basic REST endpoints, functionally similar to
|
||||
> `from_openapi` but one endpoint at a time. See
|
||||
> [`crates/http/http-adapters.md`](../http/http-adapters.md) §"from_jsonschema".
|
||||
> The `FromJsonSchema` provenance variant stays in `alknet-call`
|
||||
> (`OperationProvenance`); only the adapter implementation moved.
|
||||
|
||||
It also records two cross-cutting architectural mechanisms that the adapter
|
||||
surface rests on:
|
||||
|
||||
@@ -58,8 +67,8 @@ trait is the enabling gap for `alknet-http`'s `from_openapi`/`from_mcp`.
|
||||
|
||||
ADR-017 specced this surface. This document is the spec that operationally
|
||||
fills the gap ADR-017 left to implementation: the `CallClient` API, the
|
||||
`from_call`/`from_jsonschema` flows, the trait signature, the adapter
|
||||
location, the credential invariant, and the bilateral pattern. The gap
|
||||
`from_call` flow, the trait signature, the adapter location, the credential
|
||||
invariant, and the bilateral pattern. The gap
|
||||
analysis (`docs/research/alknet-call-completion/gap-analysis.md`) identified
|
||||
four decisions (DC-1..4) needed before implementation. DC-1 was initially
|
||||
resolved by ADR-028 (`remote_safe`/`trusted_peer`), but a subsequent research
|
||||
@@ -377,29 +386,31 @@ want to disclose the originator. See [ADR-032](../../decisions/032-forwarded-for
|
||||
|
||||
### from_jsonschema
|
||||
|
||||
Schema-only registration: produces `HandlerRegistration` bundles with no
|
||||
handler (`FromJsonSchema` provenance). Used for 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, etc.
|
||||
`from_jsonschema` was originally specified here (ADR-017 §5) as a
|
||||
schema-only adapter in `alknet-call` — a placeholder handler returning
|
||||
`NOT_FOUND`. That was broken: an op in the registry needs a real handler,
|
||||
and the "schema-only, no handler" concept conflated schema validation
|
||||
(a planning activity that doesn't need a registry entry) with operation
|
||||
registration (which always needs a handler).
|
||||
|
||||
```rust
|
||||
pub fn from_jsonschema(
|
||||
spec: OperationSpec,
|
||||
schema: serde_json::Value,
|
||||
) -> HandlerRegistration;
|
||||
```
|
||||
[ADR-066](../../decisions/066-from-jsonschema-as-http-adapter.md) moved
|
||||
`from_jsonschema` to `alknet-http` as an HTTP-backed single-endpoint
|
||||
adapter: the caller supplies an `OperationSpec` + `HttpServiceConfig` +
|
||||
path template + method, and the adapter builds one
|
||||
`HandlerRegistration` with a real reqwest forwarding handler and
|
||||
`FromJsonSchema` provenance. It is functionally similar to `from_openapi`
|
||||
but one endpoint at a time, for non-standard / non-OpenAPI / basic REST
|
||||
endpoints that don't have a full OpenAPI document. See
|
||||
[`crates/http/http-adapters.md`](../http/http-adapters.md) §"from_jsonschema".
|
||||
|
||||
Distinct from `from_call` (gap analysis DC-5, confirmed not a decision):
|
||||
The schema-validation-without-a-handler use case (the original stated
|
||||
purpose) is served by consuming `OperationSpec` directly — the spec
|
||||
already carries the input/output JSON Schemas. No adapter, no registry
|
||||
entry, no handler is needed for that.
|
||||
|
||||
| | `from_jsonschema` | `from_call` |
|
||||
|---|---|---|
|
||||
| Schema source | Provided directly (caller fetches, passes in) | Discovered over wire (`services/list` + `services/schema`) |
|
||||
| Handler at call time | None (schema-only, `FromJsonSchema` provenance) | Forwards over QUIC (`FromCall` provenance, leaf) |
|
||||
| Use case | Type validation, discovery, composition graph construction | Actually invoking remote operations |
|
||||
|
||||
Keeping them separate preserves the "schema-only, no execution" use case
|
||||
(type checking, safe composition planning without runtime).
|
||||
The `FromJsonSchema` provenance variant stays in `alknet-call`
|
||||
(`OperationProvenance` in `registry/registration.rs`); only the adapter
|
||||
implementation moved.
|
||||
|
||||
### OperationAdapter trait
|
||||
|
||||
@@ -433,8 +444,9 @@ door, recorded here.
|
||||
|
||||
Implementations:
|
||||
- `FromCall` — QUIC-backed (in `alknet-call`).
|
||||
- `FromJsonSchema` — pure parse, no transport (in `alknet-call`).
|
||||
- `FromOpenAPI` — HTTP-backed (in `alknet-http`).
|
||||
- `FromJsonSchema` — HTTP-backed, single-endpoint (in `alknet-http` per
|
||||
ADR-066; was a broken schema-only placeholder in `alknet-call`).
|
||||
- `FromMCP` — MCP streamable-HTTP-backed (in `alknet-http`, feature-gated).
|
||||
|
||||
The `to_*` adapters (`to_openapi`, `to_mcp`) are outbound projections, not
|
||||
@@ -451,12 +463,12 @@ dependencies live.**
|
||||
alknet-call (lean — no HTTP client, no HTTP server)
|
||||
├── OperationAdapter trait (the contract — async, per ADR-017 §5)
|
||||
├── from_call (QUIC — discovers remote ops via call protocol)
|
||||
├── from_jsonschema (pure parse — caller fetches the doc, passes it in)
|
||||
└── CallClient (outbound connection opener — the #1 gap)
|
||||
|
||||
alknet-http (owns HTTP server + HTTP client — separate crate, separate Phase 0)
|
||||
├── ProtocolHandler for h2/http1.1/h3 (axum server — inbound HTTP)
|
||||
├── from_openapi (parse OpenAPI doc + reqwest forwarding handler)
|
||||
├── from_jsonschema (single-endpoint reqwest forwarding handler — ADR-066)
|
||||
├── to_openapi (generate OpenAPI doc from local registry)
|
||||
├── from_mcp (feature-gated) (import remote MCP tools over streamable HTTP — reqwest)
|
||||
└── to_mcp (feature-gated) (expose local ops as MCP tools over streamable HTTP — axum)
|
||||
@@ -591,12 +603,10 @@ Based on the gap analysis and the downstream unblock chain:
|
||||
already-implemented `services/list` + `services/schema` discovery API.
|
||||
|
||||
3. **`OperationAdapter` trait** (enabling) — the async trait. Small,
|
||||
standalone, unblocks `alknet-http` Phase 1.
|
||||
standalone, unblocks `alknet-http` Phase 1 (including `from_jsonschema`
|
||||
per ADR-066).
|
||||
|
||||
4. **`from_jsonschema`** (medium, standalone) — schema-only registration, no
|
||||
handler. Small.
|
||||
|
||||
5. **DC-1 resolution** (peer-graph routing model, ADR-029) — the
|
||||
4. **DC-1 resolution** (peer-graph routing model, ADR-029) — the
|
||||
peer-keyed overlay + `AccessControl`-based peer authorization model that
|
||||
replaces ADR-028's `remote_safe`/`trusted_peer`. This is a structural
|
||||
change to `CompositeOperationEnv` (→ `PeerCompositeEnv`), the dispatch
|
||||
@@ -617,10 +627,12 @@ Based on the gap analysis and the downstream unblock chain:
|
||||
|
||||
## Constraints
|
||||
|
||||
- **No HTTP in alknet-call.** `from_openapi`/`from_mcp`/`to_openapi`/`to_mcp`
|
||||
live in `alknet-http`. The `OperationAdapter` trait and the QUIC-backed
|
||||
adapters (`from_call`, `from_jsonschema`) live in `alknet-call`. See
|
||||
Adapter Location Map.
|
||||
- **No HTTP in alknet-call.** `from_openapi`/`from_mcp`/`from_jsonschema`/
|
||||
`to_openapi`/`to_mcp` live in `alknet-http`. The `OperationAdapter`
|
||||
trait and the QUIC-backed adapter (`from_call`) live in `alknet-call`.
|
||||
`from_jsonschema` was originally (mis)placed in `alknet-call` as a
|
||||
schema-only placeholder; ADR-066 moved it to `alknet-http` as a real
|
||||
HTTP-backed adapter. See Adapter Location Map.
|
||||
- **No secret material on the wire.** `CallCredentials` carries vault-derived
|
||||
material for the *outbound* connection (TLS identity, auth token); the
|
||||
call protocol's wire format carries no private keys, API keys, or decrypted
|
||||
@@ -669,7 +681,8 @@ Based on the gap analysis and the downstream unblock chain:
|
||||
|
||||
| Decision | ADR | Summary |
|
||||
|----------|-----|---------|
|
||||
| Call protocol client and adapter contract | [ADR-017](../../decisions/017-call-protocol-client-and-adapter-contract.md) | `CallClient` opens connections; `from_call` imports remote ops; connection direction independent of call direction; trait is async; adapters produce `HandlerRegistration` bundles |
|
||||
| Call protocol client and adapter contract | [ADR-017](../../decisions/017-call-protocol-client-and-adapter-contract.md) | `CallClient` opens connections; `from_call` imports remote ops; connection direction independent of call direction; trait is async; adapters produce `HandlerRegistration` bundles. ~~`from_jsonschema` clause superseded by ADR-066~~ |
|
||||
| `from_jsonschema` as HTTP-backed single-endpoint adapter in alknet-http | [ADR-066](../../decisions/066-from-jsonschema-as-http-adapter.md) | Moved `from_jsonschema` from `alknet-call` (broken schema-only placeholder) to `alknet-http` as a real reqwest-backed single-endpoint adapter; `FromJsonSchema` provenance stays in `alknet-call` as a leaf |
|
||||
| Peer-graph routing model (DC-1, supersedes ADR-028) | [ADR-029](../../decisions/029-peer-graph-routing-model.md) | Peer-keyed overlays + `PeerRef` routing; peer authorization via existing `AccessControl::check(peer_identity)`; retires `remote_safe`/`trusted_peer` |
|
||||
| PeerEntry and Identity.id decoupling | [ADR-030](../../decisions/030-peerentry-and-identity-id-decoupling.md) | `PeerId` source changes from UUID to `Identity.id` (= `PeerEntry.peer_id`, stable across key rotation); `Identity.id` decoupled from crypto material on the fingerprint path |
|
||||
| Forwarded-for identity | [ADR-032](../../decisions/032-forwarded-for-identity.md) | `forwarded_for` field on `call.requested` and `OperationContext`; the `from_call` handler populates it; metadata only, never used by `AccessControl::check` |
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
status: draft
|
||||
last_updated: 2026-07-05
|
||||
last_updated: 2026-07-09
|
||||
---
|
||||
|
||||
# Operation Registry
|
||||
@@ -74,7 +74,7 @@ Operation names use slash-based paths without a leading slash, aligned with URL
|
||||
|
||||
The `namespace` field is derived from the name: for `fs/readFile` it's `fs`, for `agent/chat` it's `agent`. It's a convenience accessor for ACL matching and service grouping.
|
||||
|
||||
Visibility (ADR-015) controls whether an operation is callable from the wire. `External` operations are wire-facing — they appear in `services/list` and accept `call.requested` from clients. `Internal` operations are composition-only — they return `NOT_FOUND` (not `FORBIDDEN`) when called from the wire, and do not appear in `services/list`. The assembly layer declares visibility at registration. All import adapters (`from_openapi`, `from_mcp`, `from_jsonschema`, `from_call`) register operations as `Internal` by default (they're composition material, not directly callable); the handler that composes them is `External`.
|
||||
Visibility (ADR-015) controls whether an operation is callable from the wire. `External` operations are wire-facing — they appear in `services/list` and accept `call.requested` from clients. `Internal` operations are composition-only — they return `NOT_FOUND` (not `FORBIDDEN`) when called from the wire, and do not appear in `services/list`. The assembly layer declares visibility at registration. All import adapters (`from_openapi`, `from_mcp`, `from_jsonschema`, `from_call`) register operations as `Internal` by default (they're composition material, not directly callable); the handler that composes them is `External`. (`from_jsonschema` is now a real HTTP-backed adapter in `alknet-http` per ADR-066, not the schema-only placeholder it was.)
|
||||
|
||||
### AccessControl
|
||||
|
||||
@@ -383,7 +383,7 @@ pub enum OperationProvenance {
|
||||
FromOpenAPI, // HTTP forwarding stub (from_openapi), leaf
|
||||
FromMCP, // MCP forwarding stub (from_mcp), leaf
|
||||
FromCall, // QUIC forwarding stub (from_call), leaf locally
|
||||
FromJsonSchema, // JSON Schema definition, no handler — schema only
|
||||
FromJsonSchema, // HTTP forwarding stub (from_jsonschema, single endpoint), leaf
|
||||
Session, // Agent-written, sandboxed, can compose within sandbox
|
||||
}
|
||||
```
|
||||
@@ -394,9 +394,19 @@ pub enum OperationProvenance {
|
||||
| `FromOpenAPI` | No (leaf) | No | Internal |
|
||||
| `FromMCP` | No (leaf) | No | Internal |
|
||||
| `FromCall` | No (leaf in local registry) | No | Internal |
|
||||
| `FromJsonSchema` | N/A (no handler) | No | N/A |
|
||||
| `FromJsonSchema` | No (leaf) | No | Internal |
|
||||
| `Session` | Yes (within sandbox) | Yes — scopes set at sandbox creation | Internal always |
|
||||
|
||||
> **ADR-066 update.** `FromJsonSchema` was originally a schema-only
|
||||
> provenance with no handler (the old row read "N/A (no handler) /
|
||||
> N/A"). ADR-066 moved `from_jsonschema` to `alknet-http` as a real
|
||||
> HTTP-backed single-endpoint adapter with a reqwest forwarding
|
||||
> handler. `FromJsonSchema` is now a leaf, same trust model as
|
||||
> `FromOpenAPI` (HTTP endpoint trusted; handler is a forwarding stub).
|
||||
> The "schema-only, no handler" concept is removed — schema validation
|
||||
> without a handler is served by consuming `OperationSpec` directly,
|
||||
> not by registering a placeholder op.
|
||||
|
||||
#### CompositionAuthority
|
||||
|
||||
The declared authority (label + scopes + resources) the handler operates
|
||||
@@ -906,7 +916,8 @@ The `Capabilities` type holds non-serializable, zeroized secret material. It doe
|
||||
| Handler registration, provenance, and composition authority | [ADR-022](../../decisions/022-handler-registration-provenance-and-composition-authority.md) | Registration bundle carries provenance, composition authority, scoped env, capabilities; dispatch path reads from bundle |
|
||||
| Operation registry layering | [ADR-024](../../decisions/024-operation-registry-layering.md) | Curated (static, immutable) + session and connection overlays (dynamic); `OperationEnv` as trait-object integration point; `OperationContext.env` split into `scoped_env` (data) and `env` (dispatch trait) |
|
||||
| Operation error schemas | [ADR-023](../../decisions/023-operation-error-schemas.md) | Operations declare domain errors; `call.error` carries typed `details`; adapter fidelity for `from_openapi`/`to_openapi` |
|
||||
| Call protocol client and adapter contract | [ADR-017](../../decisions/017-call-protocol-client-and-adapter-contract.md) | `from_call`/`from_jsonschema`/`OperationAdapter` produce `HandlerRegistration` bundles; adapter-registered ops are `Internal` leaves. Surface specced in [client-and-adapters.md](client-and-adapters.md) |
|
||||
| Call protocol client and adapter contract | [ADR-017](../../decisions/017-call-protocol-client-and-adapter-contract.md) | `from_call`/`OperationAdapter` produce `HandlerRegistration` bundles; adapter-registered ops are `Internal` leaves. Surface specced in [client-and-adapters.md](client-and-adapters.md). ~~`from_jsonschema` clause superseded by ADR-066~~ |
|
||||
| `from_jsonschema` as HTTP-backed single-endpoint adapter | [ADR-066](../../decisions/066-from-jsonschema-as-http-adapter.md) | Moved `from_jsonschema` from `alknet-call` (broken schema-only placeholder) to `alknet-http` as a real reqwest-backed single-endpoint adapter; `FromJsonSchema` provenance stays in `alknet-call` as a leaf (now handler-bearing, not "no handler") |
|
||||
| Peer-graph routing model (supersedes ADR-028) | [ADR-029](../../decisions/029-peer-graph-routing-model.md) | Peer-keyed overlays + `PeerRef` routing; peer authorization via `AccessControl::check(peer_identity)`; retires `remote_safe`/`trusted_peer` (the field this doc's `HandlerRegistration` previously gained) |
|
||||
| Forwarded-for identity | [ADR-032](../../decisions/032-forwarded-for-identity.md) | `forwarded_for` field on `OperationContext` and `call.requested`; metadata only — `AccessControl::check` never reads it; the `from_call` handler populates it |
|
||||
| ~~Peer-scoped registry filtering~~ (superseded) | ~~[ADR-028](../../decisions/028-callclient-peer-scoped-registry-filtering.md)~~ | ~~`remote_safe` marking on `HandlerRegistration`~~ — superseded by ADR-029 |
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
status: draft
|
||||
last_updated: 2026-07-06
|
||||
last_updated: 2026-07-09
|
||||
---
|
||||
|
||||
# alknet-http
|
||||
@@ -19,7 +19,7 @@ protocol), and hosts the HTTP-backed call-protocol adapters
|
||||
| [overview.md](overview.md) | draft | Crate purpose, two roles (server + client host), dependencies, adapter location map |
|
||||
| [http-server.md](http-server.md) | draft | `HttpAdapter` (`ProtocolHandler` for `h2`/`http/1.1` + WS upgrade route), axum over QUIC, Bearer auth, stealth, `/healthz`; WS hands off to the native session spec |
|
||||
| [websocket.md](websocket.md) | draft | WebSocket browser bidirectional path — native `EventEnvelope` call-protocol session (not the gateway shape, ADR-048); framing, dispatch, bidirectionality, connection-local Layer 2 overlay, browsers-are-not-peers rationale, streaming (native `call.responded`, no SSE), deferred `from_wss` adapter |
|
||||
| [http-adapters.md](http-adapters.md) | draft | `from_openapi` (reqwest client; JSON + YAML input per ADR-051) and `to_openapi` (OpenAPI projection); no-env-vars invariant point |
|
||||
| [http-adapters.md](http-adapters.md) | draft | `from_openapi` (reqwest client; JSON + YAML input per ADR-051), `from_jsonschema` (single-endpoint reqwest forwarding handler per ADR-066), and `to_openapi` (OpenAPI projection); no-env-vars invariant point |
|
||||
| [http-mcp.md](http-mcp.md) | draft | `from_mcp` / `to_mcp` (feature-gated), streamable-HTTP-only, stdio exclusion |
|
||||
| [webtransport.md](webtransport.md) | deferred | `h3`/WebTransport handler — **deferred per ADR-044**; spec kept intact for revival |
|
||||
|
||||
@@ -36,8 +36,8 @@ protocol), and hosts the HTTP-backed call-protocol adapters
|
||||
| [014](../../decisions/014-secret-material-flow-and-capability-injection.md) | Secret Material Flow | `from_openapi`/`from_mcp` are the credential injection point |
|
||||
| [015](../../decisions/015-privilege-model-and-authority-context.md) | Privilege Model | Adapter-registered ops are `Internal` by default |
|
||||
| [017](../../decisions/017-call-protocol-client-and-adapter-contract.md) | Call Protocol Client and Adapter Contract | `OperationAdapter` trait; `to_*` are projections; published-spec contract |
|
||||
| [022](../../decisions/022-handler-registration-provenance-and-composition-authority.md) | Handler Registration, Provenance, Composition Authority | `from_openapi`/`from_mcp` produce leaf bundles |
|
||||
| [023](../../decisions/023-operation-error-schemas.md) | Operation Error Schemas | `from_openapi`/`to_openapi` error fidelity; `HTTP_<status>` error codes |
|
||||
| [022](../../decisions/022-handler-registration-provenance-and-composition-authority.md) | Handler Registration, Provenance, Composition Authority | `from_openapi`/`from_mcp`/`from_jsonschema` produce leaf bundles (`FromJsonSchema` now handler-bearing per ADR-066) |
|
||||
| [023](../../decisions/023-operation-error-schemas.md) | Operation Error Schemas | `from_openapi`/`from_jsonschema`/`to_openapi` error fidelity; `HTTP_<status>` error codes |
|
||||
| [027](../../decisions/027-tls-identity-redesign-acme-rawkey-decoupling.md) | TLS Identity Redesign | Browsers require X.509; applies to WebTransport (deferred) and any browser-facing TLS |
|
||||
| [034](../../decisions/034-outgoing-only-x509-and-three-peer-roles.md) | Outgoing-Only X.509 and Three Peer Roles | Browsers are not alknet peers (§4 amended by ADR-044 §5 with the addressability rationale) |
|
||||
| [036](../../decisions/036-http-to-call-operation-mapping.md) | HTTP-to-Call Operation Mapping | ~~Direct path mapping~~ — **routing superseded by ADR-047**; non-routing clauses survive (SSE projection, Bearer auth, `/healthz`, stealth, error mapping) |
|
||||
@@ -55,6 +55,7 @@ protocol), and hosts the HTTP-backed call-protocol adapters
|
||||
| [048](../../decisions/048-websocket-native-session-not-gateway.md) | WebSocket Carries the Native Call-Protocol Session, Not the Gateway Shape | WS is the native `EventEnvelope` session; the gateway endpoints (`/search`/`/schema`/`/call`/`/batch`/`/subscribe`) are HTTP-only and do not appear on WS; discovery via `services/list`/`services/schema` as call-protocol ops |
|
||||
| [049](../../decisions/049-streaming-handler-for-subscriptions.md) | Streaming Handler for Subscription Operations | `from_openapi` `Subscription` ops register a `StreamingHandler` (`HandlerKind::Stream`); SSE → `BoxStream<ResponseEnvelope>` |
|
||||
| [051](../../decisions/051-yaml-input-for-from-openapi.md) | YAML Input Format for from_openapi | `from_openapi` accepts JSON and YAML (`from_json`/`from_yaml`/`from_str`); `from_str` is JSON-first/YAML-fallback (defensive default, §2 amended — `yaml_serde` 0.10.x is YAML 1.2, not 1.1); YAML dep is `yaml_serde` (maintained fork of deprecated `serde_yaml`); `to_openapi` output stays JSON (out of scope, §4) |
|
||||
| [066](../../decisions/066-from-jsonschema-as-http-adapter.md) | `from_jsonschema` as HTTP-Backed Single-Endpoint Adapter in alknet-http | Moved `from_jsonschema` from `alknet-call` (broken schema-only placeholder) to `alknet-http` as a real reqwest-backed single-endpoint adapter; `FromJsonSchema` provenance stays in `alknet-call` as a handler-bearing leaf; supersedes ADR-017 §5's `from_jsonschema` clause and ADR-022's `FromJsonSchema` row |
|
||||
|
||||
## Relevant Open Questions
|
||||
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
---
|
||||
status: draft
|
||||
last_updated: 2026-07-06
|
||||
last_updated: 2026-07-09
|
||||
---
|
||||
|
||||
# HTTP Adapters — from_openapi and to_openapi
|
||||
# HTTP Adapters — from_openapi, from_jsonschema, and to_openapi
|
||||
|
||||
The OpenAPI-direction adapters: `from_openapi` imports external HTTP APIs
|
||||
as call-protocol operations (reqwest-backed forwarding handlers), and
|
||||
The OpenAPI-direction adapters plus the single-endpoint adapter:
|
||||
`from_openapi` imports external HTTP APIs described by a full OpenAPI
|
||||
document, `from_jsonschema` imports a single non-standard / non-OpenAPI
|
||||
HTTP endpoint described by a caller-supplied `OperationSpec`, and
|
||||
`to_openapi` generates an OpenAPI spec from the local registry's
|
||||
`External` operations. This document covers both, the error fidelity
|
||||
(ADR-023), and the no-env-vars credential injection point.
|
||||
`External` operations. This document covers all three, the error
|
||||
fidelity (ADR-023), and the no-env-vars credential injection point.
|
||||
|
||||
## What
|
||||
|
||||
Two adapters, both in `alknet-http`:
|
||||
Three adapters, all in `alknet-http`:
|
||||
|
||||
1. **`from_openapi`** — parses an OpenAPI document, constructs a
|
||||
`HandlerRegistration` bundle per OpenAPI operation with a forwarding
|
||||
@@ -23,7 +25,15 @@ Two adapters, both in `alknet-http`:
|
||||
`alknet-call`, ADR-017 §5). Provenance is `FromOpenAPI` (leaf,
|
||||
`composition_authority: None`, `scoped_env: None`, `Internal` by
|
||||
default — ADR-015/022).
|
||||
2. **`to_openapi`** — generates an OpenAPI document from the local
|
||||
2. **`from_jsonschema`** — registers a single HTTP endpoint as a
|
||||
call-protocol operation, one at a time, for non-standard /
|
||||
non-OpenAPI / basic REST endpoints that don't have a full OpenAPI
|
||||
document. The caller supplies an `OperationSpec` + `HttpServiceConfig`
|
||||
+ path template + HTTP method; the adapter builds one
|
||||
`HandlerRegistration` with a reqwest forwarding handler (the same
|
||||
handler shape as `from_openapi`) and `FromJsonSchema` provenance.
|
||||
Implements `OperationAdapter`. See ADR-066.
|
||||
3. **`to_openapi`** — generates an OpenAPI document from the local
|
||||
registry's `External` operations. A pure projection: it consumes the
|
||||
registry, it does not produce entries for it (ADR-017 §5 — the `to_*`
|
||||
adapters are outbound projections, not `OperationAdapter`
|
||||
@@ -271,6 +281,80 @@ source other than `OperationContext.capabilities`. See
|
||||
[overview.md](overview.md) and
|
||||
[client-and-adapters.md](../call/client-and-adapters.md).
|
||||
|
||||
### from_jsonschema
|
||||
|
||||
`from_jsonschema` registers a single HTTP endpoint as a call-protocol
|
||||
operation, one at a time. It is functionally similar to `from_openapi`
|
||||
but for one endpoint instead of a full OpenAPI document — for
|
||||
non-standard, non-OpenAPI, or basic REST endpoints that don't have a
|
||||
`paths` object, an `operationId`, or `components`. The caller supplies
|
||||
the schema directly; the adapter builds a reqwest forwarding handler
|
||||
identical in shape to `from_openapi`'s. See
|
||||
[ADR-066](../../decisions/066-from-jsonschema-as-http-adapter.md).
|
||||
|
||||
```rust
|
||||
pub struct FromJsonSchema {
|
||||
spec: OperationSpec,
|
||||
config: HttpServiceConfig,
|
||||
path_template: String,
|
||||
method: String,
|
||||
http_client: Arc<SharedHttpClient>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl OperationAdapter for FromJsonSchema {
|
||||
async fn import(&self) -> Result<Vec<HandlerRegistration>, AdapterError>;
|
||||
}
|
||||
```
|
||||
|
||||
The adapter:
|
||||
|
||||
1. Takes 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 type `from_openapi` uses), a path template
|
||||
(e.g. `/users/{id}/posts`), and an HTTP method (e.g. `GET`).
|
||||
2. Builds one `HandlerRegistration`:
|
||||
- `spec` = the caller-supplied `OperationSpec` (the caller already
|
||||
has the JSON Schemas; no parsing needed).
|
||||
- `handler` = a reqwest forwarding handler, identical in shape to
|
||||
`from_openapi`'s: builds the HTTP request (path-template
|
||||
substitution, query params, body), injects credentials from
|
||||
`context.capabilities`, sends via the shared HTTP client, parses
|
||||
the response (JSON / text / binary — same content-type branching).
|
||||
For `Subscription` op type, registers a `StreamingHandler`
|
||||
(ADR-049) expecting `text/event-stream`.
|
||||
- `provenance` = `FromJsonSchema` (leaf, `composition_authority: None`,
|
||||
`scoped_env: None` — ADR-022).
|
||||
- `capabilities` = the credentials the forwarding handler needs
|
||||
(same no-env-vars path as `from_openapi`).
|
||||
3. Returns the single bundle. The caller registers it in the
|
||||
`OperationRegistry`.
|
||||
|
||||
#### Relationship to from_openapi
|
||||
|
||||
`from_jsonschema` is functionally similar to `from_openapi` but for one
|
||||
endpoint instead of a full OpenAPI document. The two adapters share the
|
||||
forwarding-handler implementation, the credential injection path, the
|
||||
error-fidelity rule (`HTTP_<status>` prefix, ADR-023), the streaming
|
||||
shape (ADR-049), and the no-env-vars invariant (ADR-014). The difference
|
||||
is purely the input shape: a full document vs. a single endpoint. See
|
||||
[ADR-066](../../decisions/066-from-jsonschema-as-http-adapter.md)
|
||||
§"Relationship to `from_openapi`" for the comparison table.
|
||||
|
||||
#### Origin (ADR-066)
|
||||
|
||||
`from_jsonschema` was originally placed in `alknet-call` (ADR-017 §5) as a
|
||||
schema-only adapter with a `NOT_FOUND`-returning placeholder handler —
|
||||
broken, because an op in the registry needs a real handler.
|
||||
[ADR-066](../../decisions/066-from-jsonschema-as-http-adapter.md) moved
|
||||
it to `alknet-http` and gave it a real reqwest forwarding handler. The
|
||||
`FromJsonSchema` provenance variant stays in `alknet-call`
|
||||
(`OperationProvenance`); only the adapter implementation moved. See
|
||||
[ADR-066](../../decisions/066-from-jsonschema-as-http-adapter.md) for the
|
||||
full rationale (why the placeholder was broken, why the "schema-only"
|
||||
concept conflated two things, why it was mispaced in `alknet-call`).
|
||||
|
||||
### to_openapi
|
||||
|
||||
```rust
|
||||
@@ -397,6 +481,14 @@ its errors are typed. The agent crate's LLM provider calls go through
|
||||
`from_openapi`-imported operations — that's how the no-env-vars
|
||||
invariant makes aisdk's env-var reads unreachable.
|
||||
|
||||
`from_jsonschema` fills the gap that `from_openapi` can't: endpoints
|
||||
that have no OpenAPI document. A non-standard REST endpoint, a basic
|
||||
internal API, or a third-party service with only a JSON Schema
|
||||
description can be registered as a call-protocol operation one at a
|
||||
time, with the same reqwest forwarding handler and the same
|
||||
no-env-vars credential path. The caller supplies the schema; the
|
||||
adapter supplies the handler. See ADR-066.
|
||||
|
||||
`to_openapi` is how external systems discover the alknet operation
|
||||
surface. A client generator, a human developer, or a `fetch`-based
|
||||
client reads the OpenAPI doc to learn the gateway's shape (5 fixed
|
||||
@@ -415,12 +507,15 @@ once published, the 5-endpoint gateway shape is one-way.
|
||||
- **`from_openapi`/`from_mcp` handlers read credentials from
|
||||
`OperationContext.capabilities`, not `std::env::var`.** This is the
|
||||
no-env-vars invariant (ADR-014). The handler implementations are
|
||||
verified against this invariant.
|
||||
verified against this invariant. `from_jsonschema` shares this
|
||||
invariant — same handler shape, same credential path (ADR-066).
|
||||
- **`from_openapi`-registered ops are `Internal` by default.** They are
|
||||
composition material, not directly callable from the wire (ADR-015).
|
||||
The handler that composes them is `External`.
|
||||
The handler that composes them is `External`. `from_jsonschema`
|
||||
ops are `Internal` by default for the same reason (ADR-066).
|
||||
- **`from_openapi` error codes are prefixed `HTTP_<status>`.** No
|
||||
collision with protocol-level codes (ADR-023, review #002 W20).
|
||||
`from_jsonschema` shares this rule (ADR-066).
|
||||
- **`from_openapi` accepts JSON and YAML; `from_str` detects format
|
||||
JSON-first.** JSON-first is a defensive default (ADR-051 §2 as
|
||||
amended): JSON's stricter grammar is immune to any YAML-specific type
|
||||
@@ -460,7 +555,8 @@ once published, the 5-endpoint gateway shape is one-way.
|
||||
|
||||
| Decision | ADR | Summary |
|
||||
|----------|-----|---------|
|
||||
| `from_openapi` is an `OperationAdapter` | [ADR-017](../../decisions/017-call-protocol-client-and-adapter-contract.md) | Async trait; produces `HandlerRegistration` bundles |
|
||||
| `from_openapi` is an `OperationAdapter` | [ADR-017](../../decisions/017-call-protocol-client-and-adapter-contract.md) | Async trait; produces `HandlerRegistration` bundles. ~~`from_jsonschema` clause superseded by ADR-066~~ |
|
||||
| `from_jsonschema` as HTTP-backed single-endpoint adapter in alknet-http | [ADR-066](../../decisions/066-from-jsonschema-as-http-adapter.md) | Moved `from_jsonschema` from `alknet-call` (broken schema-only placeholder) to `alknet-http` as a real reqwest-backed single-endpoint adapter; `FromJsonSchema` provenance stays in `alknet-call` as a leaf |
|
||||
| `to_openapi` is a projection, not an adapter | [ADR-017](../../decisions/017-call-protocol-client-and-adapter-contract.md) | Consumes the registry, doesn't produce entries |
|
||||
| Adapter-registered ops are `Internal` | [ADR-015](../../decisions/015-privilege-model-and-authority-context.md) | `from_openapi` ops are composition material |
|
||||
| `from_openapi` provenance is a leaf | [ADR-022](../../decisions/022-handler-registration-provenance-and-composition-authority.md) | `composition_authority: None`, `scoped_env: None` |
|
||||
@@ -469,7 +565,7 @@ once published, the 5-endpoint gateway shape is one-way.
|
||||
| HTTP path = operation path (~~direct-call surface~~) | [ADR-036](../../decisions/036-http-to-call-operation-mapping.md) → superseded by [ADR-047](../../decisions/047-remove-direct-call-http-surface.md) | ~~`POST /{service}/{op}` → `call.requested`~~ — removed; the gateway `/call` with `{ operation, input }` is the sole invoke path; `to_openapi` describes the gateway, not a per-operation surface |
|
||||
| `to_openapi` gateway pattern | [ADR-042](../../decisions/042-openapi-gateway-pattern.md) | 5 fixed gateway endpoints (search/schema/call/batch/subscribe), not one path per operation; per-caller AccessControl-filtered. Supersedes ADR-036's original `to_openapi` "paths mirror `/{service}/{op}`" clause |
|
||||
| `to_openapi` published-spec versioning | [ADR-045](../../decisions/045-to-openapi-gateway-spec-versioning.md) | `info.version` semver tracks the gateway endpoint contract, not the operation set; consumers detect breaking changes via the major version |
|
||||
| Streaming handler for subscriptions | [ADR-049](../../decisions/049-streaming-handler-for-subscriptions.md) | `from_openapi` `Subscription` ops register a `StreamingHandler` (`HandlerKind::Stream`); SSE response → `BoxStream<ResponseEnvelope>`; `Query`/`Mutation` stay `HandlerKind::Once` |
|
||||
| Streaming handler for subscriptions | [ADR-049](../../decisions/049-streaming-handler-for-subscriptions.md) | `from_openapi` / `from_jsonschema` `Subscription` ops register a `StreamingHandler` (`HandlerKind::Stream`); SSE response → `BoxStream<ResponseEnvelope>`; `Query`/`Mutation` stay `HandlerKind::Once` |
|
||||
| YAML input + JSON-first format detection | [ADR-051](../../decisions/051-yaml-input-for-from-openapi.md) | `from_openapi` accepts JSON and YAML (`from_json`/`from_yaml`/`from_str`); `from_str` is JSON-first/YAML-fallback (defensive default, §2 amended — `yaml_serde` 0.10.x is YAML 1.2, not 1.1; JSON-first locks the contract against a future parser swap); YAML dep is `yaml_serde`; `to_openapi` output stays JSON (out of scope, §4) |
|
||||
|
||||
## Open Questions
|
||||
@@ -492,6 +588,9 @@ See [open-questions.md](../../open-questions.md) for full details.
|
||||
|
||||
- [ADR-017](../../decisions/017-call-protocol-client-and-adapter-contract.md)
|
||||
— `OperationAdapter` trait, `to_*` are projections
|
||||
- [ADR-066](../../decisions/066-from-jsonschema-as-http-adapter.md) —
|
||||
`from_jsonschema` as HTTP-backed single-endpoint adapter in
|
||||
`alknet-http` (supersedes ADR-017 §5's `from_jsonschema` clause)
|
||||
- [ADR-023](../../decisions/023-operation-error-schemas.md) — error
|
||||
fidelity, `HTTP_<status>` prefix rule
|
||||
- [overview.md](overview.md) — adapter location map, no-env-vars
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
status: draft
|
||||
last_updated: 2026-07-06
|
||||
last_updated: 2026-07-09
|
||||
---
|
||||
|
||||
# alknet-http — Overview
|
||||
@@ -154,7 +154,6 @@ implementations live where their transport dependencies live.
|
||||
alknet-call (lean — no HTTP client, no HTTP server)
|
||||
├── OperationAdapter trait (the contract — async, ADR-017 §5)
|
||||
├── from_call (QUIC — discovers remote ops via call protocol)
|
||||
├── from_jsonschema (pure parse — caller fetches the doc, passes it in)
|
||||
└── CallClient (outbound connection opener)
|
||||
|
||||
alknet-http (owns HTTP server + HTTP client)
|
||||
@@ -162,6 +161,7 @@ alknet-http (owns HTTP server + HTTP client)
|
||||
├── [WS upgrade → native session] (hands the WS message stream to the shared Dispatcher —
|
||||
│ not an adapter; see websocket.md, ADR-048)
|
||||
├── from_openapi (parse OpenAPI doc + reqwest forwarding handler)
|
||||
├── from_jsonschema (single-endpoint reqwest forwarding handler — ADR-066)
|
||||
├── to_openapi (generate OpenAPI doc from local registry)
|
||||
├── from_mcp (feature-gated) (import remote MCP tools over streamable HTTP — reqwest)
|
||||
├── to_mcp (feature-gated) (expose local ops as MCP tools over streamable HTTP — axum)
|
||||
@@ -169,10 +169,12 @@ alknet-http (owns HTTP server + HTTP client)
|
||||
from_call-aligned, same-protocol; see websocket.md §"Future")
|
||||
```
|
||||
|
||||
`alknet-call` never sees the HTTP client. The `from_openapi`/`from_mcp`
|
||||
forwarding handlers are opaque `Arc<dyn Handler>` from the registry's
|
||||
perspective. `alknet-call` stays lean; `alknet-http` owns both HTTP
|
||||
directions.
|
||||
`alknet-call` never sees the HTTP client. The `from_openapi`/
|
||||
`from_mcp`/`from_jsonschema` forwarding handlers are opaque
|
||||
`Arc<dyn Handler>` from the registry's perspective. `alknet-call` stays
|
||||
lean; `alknet-http` owns both HTTP directions. `from_jsonschema` was
|
||||
originally (mis)placed in `alknet-call` as a schema-only placeholder;
|
||||
ADR-066 moved it to `alknet-http` as a real HTTP-backed adapter.
|
||||
|
||||
## Feature Gates
|
||||
|
||||
@@ -208,9 +210,9 @@ Rust, no native code), consistent with the default-features philosophy.
|
||||
|
||||
## The No-Env-Vars Invariant
|
||||
|
||||
The `from_openapi`/`from_mcp` forwarding handlers are the **credential
|
||||
injection point** for the no-env-vars architecture. The path (from the
|
||||
gap analysis):
|
||||
The `from_openapi`/`from_mcp`/`from_jsonschema` forwarding handlers are
|
||||
the **credential injection point** for the no-env-vars architecture. The
|
||||
path (from the gap analysis):
|
||||
|
||||
```
|
||||
vault → assembly layer → Capabilities → HandlerRegistration.capabilities
|
||||
@@ -227,8 +229,9 @@ through `from_openapi` operations that carry the credential in
|
||||
|
||||
**This is a spec-level invariant**: no handler reads outbound
|
||||
credentials from any source other than `OperationContext.capabilities`.
|
||||
The `from_openapi`/`from_mcp` implementations in `alknet-http` are
|
||||
verified against this invariant. See ADR-014 and
|
||||
The `from_openapi`/`from_mcp`/`from_jsonschema` implementations in
|
||||
`alknet-http` are verified against this invariant (same handler shape —
|
||||
`from_jsonschema` per ADR-066). See ADR-014 and
|
||||
[client-and-adapters.md](../call/client-and-adapters.md).
|
||||
|
||||
## Architecture (component pointers)
|
||||
@@ -245,7 +248,9 @@ verified against this invariant. See ADR-014 and
|
||||
browsers-are-not-peers rationale, streaming (native `call.responded`,
|
||||
no SSE), and the deferred `from_wss` adapter.
|
||||
- **[http-adapters.md](http-adapters.md)** — `from_openapi` (parse
|
||||
OpenAPI, build forwarding handlers with `reqwest`) and `to_openapi`
|
||||
OpenAPI, build forwarding handlers with `reqwest`),
|
||||
`from_jsonschema` (single-endpoint reqwest forwarding handler for
|
||||
non-standard/non-OpenAPI endpoints, ADR-066), and `to_openapi`
|
||||
(generate an OpenAPI doc from the registry's `External` operations).
|
||||
Error fidelity per ADR-023.
|
||||
- **[http-mcp.md](http-mcp.md)** — `from_mcp`/`to_mcp` (feature-gated),
|
||||
|
||||
Reference in new issue
Block a user