docs(architecture): remove removed-thing remnants from spec docs
Spec docs should describe WHAT IS, not WHAT WAS. ADRs and OQ files are historical records by design and are left alone; the ADR-index Status column records ADR status (stable fact). What changed in the specs: - API code blocks no longer list removed methods. ChannelClient::connect_quic (channel-client.md) and CallClient::connect (client-and-adapters.md) are gone from the impl blocks; surrounding prose describes the current from_connection / spawn_dispatch primary + AlknetClient dial shape. - Amendment (ADR-093): stream_types field is removed blockquotes dropped from channel-client.md and channel-operations.md (the code already reflects the current state). - Historical is-removed / reversed-by / amended-by prose rewritten to current state across channels crate, call crate, hub, tls, core, endpoint, client READMEs, and the top-level README/overview. - Dropped the EndpointError — removed subsection from endpoint/README.md (the type doesn't exist anymore, so it shouldn't have a subsection). - Replaced stale connect() references in flow descriptions with the dial (in AlknetClient) since connect() is no longer a method. - Removed strikethrough ADR-028 / from_jsonschema-clause rows from client-and-adapters.md and operation-registry.md ADR tables. - Rewrote the ADR-066 update blockquote in operation-registry.md to describe FromJsonSchema's current shape. 19 files modified, net -116 lines. No ADRs or OQ files touched.
This commit is contained in:
1 parent
a3cb44968e
commit
762d9c7bd2
19 files changed
+172
-288
No files matched your search
@@ -1,7 +1,7 @@
|
||||
---
|
||||
status: draft
|
||||
last_updated: 2026-07-17
|
||||
review: call/review-call passed 2026-06-23 — registry, protocol, ADR (005/012/014/015/016/017/022/023/024), security, and pattern-consistency checks all conformant; 159 unit/integration tests green; `cargo build`, `cargo clippy -- -D warnings`, `cargo fmt --check`, `cargo test` clean. Call-completion gap (ADR-017 client/adapter surface) addressed 2026-06-26; ADR-029 migration landed. Transport generalization sweep (ADR-064 supersedes ADR-005; ADR-065 `from_stream`) synced 2026-07-09. Crate-extraction sweep (phases 0–5) landed 2026-07-17: `ConnectionCredentials`/`RemoteIdentity` in `alknet-core` (ADR-091), `CallCredentials` removed (ADR-091 Am. 2026-07-17), TLS helpers in `alknet-tls` (ADR-089 §5), `connect`/`ClientError` removed, `alknet-call` is a pure protocol crate with no TLS/transport deps.
|
||||
review: call/review-call passed 2026-06-23 — registry, protocol, ADR (005/012/014/015/016/017/022/023/024), security, and pattern-consistency checks all conformant; 159 unit/integration tests green; `cargo build`, `cargo clippy -- -D warnings`, `cargo fmt --check`, `cargo test` clean. Call-completion gap (ADR-017 client/adapter surface) addressed 2026-06-26; ADR-029 migration landed. Transport generalization sweep (ADR-064 supersedes ADR-005; ADR-065 `from_stream`) synced 2026-07-09. Crate-extraction sweep (phases 0–5) landed 2026-07-17: `ConnectionCredentials`/`RemoteIdentity` in `alknet-core` (ADR-091); TLS helpers in `alknet-tls` (ADR-089 §5); dial in `alknet-client` (ADR-089); `alknet-call` is a pure protocol crate with no TLS/transport deps.
|
||||
---
|
||||
|
||||
# alknet-call
|
||||
@@ -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 (transport-agnostic `spawn_dispatch` primary; `connect` removed per ADR-089 §5 — dial extracted to `AlknetClient`), from_call, OperationAdapter trait, adapter location map, no-env-vars invariant, exchange-of-operations pattern (from_jsonschema moved to alknet-http per ADR-066) |
|
||||
| [client-and-adapters.md](client-and-adapters.md) | draft | CallClient (transport-agnostic `spawn_dispatch` primary; dial lives in `AlknetClient` per ADR-089), from_call, OperationAdapter trait, adapter location map, no-env-vars invariant, exchange-of-operations pattern (`from_jsonschema` in alknet-http per ADR-066) |
|
||||
|
||||
## Applicable ADRs
|
||||
|
||||
@@ -36,7 +36,7 @@ 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. ~~`from_jsonschema` clause superseded by ADR-066~~ |
|
||||
| [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 |
|
||||
| [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 |
|
||||
@@ -46,8 +46,8 @@ Structured RPC: operations, request/response, streaming subscriptions, and servi
|
||||
| [030](../../decisions/030-peerentry-and-identity-id-decoupling.md) | PeerEntry and Identity.id Decoupling | `PeerId` source = `Identity.id` = `PeerEntry.peer_id` (stable); supersedes ADR-029's UUID source |
|
||||
| [032](../../decisions/032-forwarded-for-identity.md) | Forwarded-For Identity | `forwarded_for` on `OperationContext` and `call.requested`; metadata only, never used by `AccessControl::check` |
|
||||
| [033](../../decisions/033-storage-boundary-and-repo-adapter-pattern.md) | Storage Boundary and Repo/Adapter Pattern | Core defines repo traits + in-memory defaults; persistence adapters are separate crates |
|
||||
| [089](../../decisions/089-alknetclient-native-dial-seam.md) | AlknetClient — Native Client Dial Seam | `CallClient::connect` removed; the dial is in `alknet-client`; `ClientError` removed; `alknet-call` sheds TLS/transport deps (pure protocol crate) |
|
||||
| [091](../../decisions/091-connectioncredentials-decouple-dial-from-call.md) | `ConnectionCredentials` — Decouple Dial from Call Protocol | `ConnectionCredentials`/`RemoteIdentity` in `alknet-core` (not `alknet-call`); `CallCredentials` removed (Am. 2026-07-17); `auth_token` is a per-request payload field |
|
||||
| [089](../../decisions/089-alknetclient-native-dial-seam.md) | AlknetClient — Native Client Dial Seam | The dial is in `alknet-client`; `CallClient` is `spawn_dispatch` only; `alknet-call` is a pure protocol crate with no TLS/transport deps |
|
||||
| [091](../../decisions/091-connectioncredentials-decouple-dial-from-call.md) | `ConnectionCredentials` — Decouple Dial from Call Protocol | `ConnectionCredentials`/`RemoteIdentity` in `alknet-core` (not `alknet-call`); `auth_token` is a per-request payload field |
|
||||
|
||||
## Relevant Open Questions
|
||||
|
||||
@@ -60,7 +60,7 @@ Structured RPC: operations, request/response, streaming subscriptions, and servi
|
||||
| OQ-19 | Session-scoped operation registries | resolved | Agent-written operations overlaid on curated registry via `OperationEnv` trait layering. Protocol doesn't need changes; `OperationEnv` must remain a trait. Generalized by ADR-024 to cover connection-scoped overlays. |
|
||||
| OQ-25 | ~~Remote-safe marking shape~~ | **dissolved** (ADR-029) | `remote_safe`/`trusted_peer` retired; peer authorization is `AccessControl::check(peer_identity)` |
|
||||
| OQ-26 | OperationAdapter error type (AdapterError variants) | **resolved** | `DiscoveryFailed`, `SchemaParse`, `Transport`, `Unauthorized`, `SamePeerCollision`; `#[non_exhaustive]` |
|
||||
| OQ-27 | from_call re-import trigger | **resolved** | `from_call` is a manual free function; the assembly layer calls it after `connect()`. `refresh()` is a genuine feature addition. See ADR-069. |
|
||||
| OQ-27 | from_call re-import trigger | **resolved** | `from_call` is a manual free function; the assembly layer calls it after the dial (in `AlknetClient`). `refresh()` is a genuine feature addition. See ADR-069. |
|
||||
| OQ-28 | from_call namespace collision | **resolved** | Same-peer collision = error; cross-peer dissolved by ADR-029 (separate sub-overlays) |
|
||||
| OQ-29 | CallClient TLS client-auth | **resolved** | Wire quinn client-auth; key-type-aware server cert verification; fingerprint normalization |
|
||||
| OQ-30 | `PeerRef::Any` routing policy | **resolved** | Insertion-order first-match; richer routing is a feature extension |
|
||||
@@ -83,7 +83,7 @@ Structured RPC: operations, request/response, streaming subscriptions, and servi
|
||||
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`, `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 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` takes them over (`spawn_dispatch` primary; `connect` removed per ADR-089 §5 — dial extracted to `AlknetClient`); both produce the same `CallConnection` and dispatch through the same loop. See ADR-017, [client-and-adapters.md](client-and-adapters.md).
|
||||
11. **Connection direction is independent of call direction**: Who opens the 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` takes them over (`spawn_dispatch` primary; dial in `AlknetClient` per ADR-089); 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` 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).
|
||||
@@ -177,11 +177,10 @@ The adapter:
|
||||
|
||||
The dispatch loop is **shared** with `CallClient` (ADR-017 §1): both
|
||||
`CallAdapter::handle` (accept path) and `CallClient::spawn_dispatch`
|
||||
(connect path — the dial is now `AlknetClient::dial_*` per ADR-089 §5;
|
||||
`CallClient::connect` is removed) construct a `Dispatcher`
|
||||
(`protocol/dispatch.rs`) and call `run_loop` — the dispatch half is one
|
||||
implementation, the connection-establishment half differs (accept vs
|
||||
dial). Peer authorization flows through the existing
|
||||
(connect path — the dial is `AlknetClient::dial_*` per ADR-089) construct
|
||||
a `Dispatcher` (`protocol/dispatch.rs`) and call `run_loop` — the
|
||||
dispatch half is one implementation, the connection-establishment half
|
||||
differs (accept vs dial). Peer authorization flows through the existing
|
||||
`AccessControl::check(peer_identity)` — no `RemoteFilter`/`remote_safe` gate
|
||||
(ADR-029 §3). The composition env is peer-keyed (`PeerCompositeEnv`,
|
||||
ADR-029 §1) to handle head→N-workers routing. See
|
||||
@@ -595,8 +594,9 @@ See [open-questions.md](../../open-questions.md) for full details.
|
||||
variants (`DiscoveryFailed`, `SchemaParse`, `Transport`, `Unauthorized`,
|
||||
`SamePeerCollision`); `#[non_exhaustive]`. See
|
||||
[client-and-adapters.md](client-and-adapters.md).
|
||||
- **OQ-27** (resolved): `from_call` re-import trigger — `from_call` is a manual
|
||||
free function; the assembly layer calls it after `connect()`. See
|
||||
- **OQ-27** (resolved): `from_call` re-import trigger — `from_call` is a
|
||||
manual free function; the assembly layer calls it after the dial (in
|
||||
`AlknetClient`). See
|
||||
[ADR-069](../../decisions/069-from-call-manual-free-function.md).
|
||||
- **OQ-28** (resolved): `from_call` namespace collision — same-peer collision
|
||||
= error; cross-peer dissolved by ADR-029 (separate sub-overlays). See
|
||||
|
||||
@@ -23,8 +23,8 @@ This document specifies three components, all in `alknet-call`:
|
||||
1. **`CallClient`** — takes over an established transport `Connection`
|
||||
on ALPN `alknet/call`, spawns the shared dispatch loop, and produces
|
||||
a `CallConnection`. Transport-agnostic (`spawn_dispatch` primary;
|
||||
`connect` removed per ADR-089 §5 — dial extracted to `AlknetClient`);
|
||||
the dispatch loop is shared with the server-side `CallAdapter`
|
||||
dial lives in `AlknetClient` per ADR-089); the dispatch loop is
|
||||
shared with the server-side `CallAdapter`
|
||||
(ADR-017 §1); `CallClient` is the connection-take-over half, not a
|
||||
parallel protocol implementation.
|
||||
2. **`from_call`** — discovers operations on a remote call-protocol endpoint
|
||||
@@ -100,10 +100,10 @@ the producer on the inbound side. Both produce the same
|
||||
ordered, reliable bidirectional stream — QUIC, TCP+TLS, WebTransport,
|
||||
SSH `direct-tcpip`, a WebSocket (ADR-065 `Connection::from_stream` /
|
||||
`from_bidi`). The primary constructor (`spawn_dispatch`) takes a
|
||||
pre-established `Connection` from any transport; the QUIC convenience
|
||||
(`connect`) dials QUIC and calls `spawn_dispatch`. This mirrors
|
||||
`ChannelClient::from_connection` / `connect_quic` (ADR-080) and is the
|
||||
client-side analogue of the server-side generalization ADR-065 made.
|
||||
pre-established `Connection` from any transport; the dial lives in
|
||||
`AlknetClient` (`alknet-client`, ADR-089). This mirrors
|
||||
`ChannelClient::from_connection` (ADR-080) and is the client-side
|
||||
analogue of the server-side generalization ADR-065 made.
|
||||
|
||||
```rust
|
||||
pub struct CallClient {
|
||||
@@ -123,22 +123,6 @@ impl CallClient {
|
||||
/// API surface (ADR-017 Am. 2026-07-13) — it must not be coupled to
|
||||
/// a transport.
|
||||
pub fn spawn_dispatch(&self, connection: Connection) -> CallConnection;
|
||||
|
||||
/// **REMOVED per ADR-089 §5.** The dial is extracted into
|
||||
/// `AlknetClient` (`alknet-client`); `connect` is deleted, not
|
||||
/// delegated, to avoid `alknet-call` depending on `alknet-client`
|
||||
/// and to let `alknet-call` shed its TLS/transport deps entirely.
|
||||
/// Callers compose `AlknetClient::dial_quic(...).await?` +
|
||||
/// `CallClient::new(...).spawn_dispatch(conn)`. `ClientError` is
|
||||
/// removed (it was produced only by `connect`). `CallCredentials`
|
||||
/// is removed (its `auth_token` field had no reader; `auth_token`
|
||||
/// is a per-request payload field — ADR-091, amended 2026-07-17).
|
||||
#[cfg(feature = "quinn")]
|
||||
pub async fn connect(
|
||||
&self,
|
||||
addr: SocketAddr,
|
||||
credentials: CallCredentials, // REMOVED — CallCredentials is removed
|
||||
) -> Result<CallConnection, ClientError>;
|
||||
}
|
||||
```
|
||||
|
||||
@@ -191,19 +175,17 @@ authorization machinery that gates every other call. No `RemoteFilter`, no
|
||||
`CallClient::spawn_dispatch(connection)` is the transport-agnostic
|
||||
primary constructor — it takes a pre-established `Connection`,
|
||||
constructs a `CallConnection`, builds a `Dispatcher`, spawns the
|
||||
dispatch task, and returns the live `CallConnection`. `connect()` is
|
||||
**removed** per ADR-089 §5: the dial is extracted into `AlknetClient`
|
||||
(`alknet-client`), and keeping a QUIC convenience constructor on
|
||||
`CallClient` would make `alknet-call` depend on `alknet-client`,
|
||||
contradicting the dep graph (the protocol crates are parallel to the
|
||||
dial, not downstream of it). Callers compose `AlknetClient::dial_quic`
|
||||
+ `spawn_dispatch` — two lines, the dial then the take-over. Tests use
|
||||
`spawn_dispatch` directly to wire mock/loopback connections. The
|
||||
one-way-door surface is `spawn_dispatch`; the dial lives in
|
||||
`alknet-client`.
|
||||
dispatch task, and returns the live `CallConnection`. The dial lives in
|
||||
`AlknetClient` (`alknet-client`, ADR-089): keeping a QUIC convenience
|
||||
constructor on `CallClient` would make `alknet-call` depend on
|
||||
`alknet-client`, contradicting the dep graph (the protocol crates are
|
||||
parallel to the dial, not downstream of it). Callers compose
|
||||
`AlknetClient::dial_quic` + `spawn_dispatch` — two lines, the dial then
|
||||
the take-over. Tests use `spawn_dispatch` directly to wire mock/loopback
|
||||
connections. The one-way-door surface is `spawn_dispatch`; the dial
|
||||
lives in `alknet-client`.
|
||||
|
||||
This mirrors `ChannelClient::from_connection` (ADR-080; its
|
||||
`connect_quic` is likewise removed per ADR-089 §5) and is the
|
||||
This mirrors `ChannelClient::from_connection` (ADR-080) and is the
|
||||
client-side analogue of the server-side generalization ADR-065 made.
|
||||
The call protocol, like the channels protocol, is transport-agnostic —
|
||||
`Connection::from_stream` / `from_bidi` (ADR-065) accept any
|
||||
@@ -238,13 +220,12 @@ peer-keying is at the aggregation layer (the head node's composition env).
|
||||
#### services/list
|
||||
|
||||
`services/list` filters by `AccessControl::check(calling_peer_identity)` —
|
||||
the calling peer sees only ops it is authorized to call. The
|
||||
`services_list_handler` / `services_list_handler_peer_scoped` split collapses
|
||||
to a single `AccessControl`-filtered handler (the `peer_scoped` variant and
|
||||
the `remote_safe` filter are removed). `services/list-peers` is the opt-in for
|
||||
peer-attributed re-export listing (each peer's sub-overlay listed with
|
||||
attribution, filtered by the calling peer's authorization). See
|
||||
[ADR-029](../../decisions/029-peer-graph-routing-model.md) §6.
|
||||
the calling peer sees only ops it is authorized to call. There is a
|
||||
single `AccessControl`-filtered handler (no `peer_scoped` variant, no
|
||||
`remote_safe` filter — both retired by ADR-029). `services/list-peers`
|
||||
is the opt-in for peer-attributed re-export listing (each peer's
|
||||
sub-overlay listed with attribution, filtered by the calling peer's
|
||||
authorization). See [ADR-029](../../decisions/029-peer-graph-routing-model.md) §6.
|
||||
|
||||
### Credential sources for connections
|
||||
|
||||
@@ -262,13 +243,8 @@ The credential dimensions are split across two layers (ADR-091, amended
|
||||
reads `payload.get("auth_token")` on each `call.requested` payload.
|
||||
Browsers send it directly in the WebSocket call payload; the HTTP
|
||||
gateway resolves the bearer token to an `Identity` at its boundary
|
||||
(the call layer sees the identity, not the token). `CallCredentials`
|
||||
is **removed** (its `auth_token` field had no reader — `connect()`
|
||||
read only `tls_identity` + `remote_identity`; `spawn_dispatch` takes
|
||||
no credentials; the `from_call` forwarding path's `auth_token` source
|
||||
was `OpSummary.credentials_auth_token: Option<String>`, always
|
||||
`None`, never connected to `CallCredentials.auth_token`). See
|
||||
ADR-091 (amended 2026-07-17) for the full trace.
|
||||
(the call layer sees the identity, not the token). See ADR-091 for
|
||||
the credential-bundle decoupling.
|
||||
|
||||
Credentials come from `Capabilities` (ADR-014), never from environment
|
||||
variables. The transport-identity dimensions (ADR-017 §7):
|
||||
@@ -305,9 +281,9 @@ default `remote_identity` to a placeholder value to "satisfy" the field
|
||||
pub struct RemoteIdentity { pub fingerprint: String }
|
||||
```
|
||||
|
||||
There is no call-protocol credential bundle. `CallCredentials` is
|
||||
removed. The transport dimensions (`local_identity`, `remote_identity`)
|
||||
are in `ConnectionCredentials` in `alknet-core` per ADR-091.
|
||||
There is no call-protocol credential bundle. The transport dimensions
|
||||
(`local_identity`, `remote_identity`) are in `ConnectionCredentials` in
|
||||
`alknet-core` per ADR-091.
|
||||
|
||||
- **TLS identity** — the local node's Ed25519 raw key (RFC 7250) or X.509 cert,
|
||||
derived from the vault at startup (ADR-020, ADR-026, ADR-027).
|
||||
@@ -424,12 +400,12 @@ The flow (ADR-017 §3):
|
||||
`CallConnection::register_imported_all()`.
|
||||
|
||||
**Re-import on reconnection** (DC-2, OQ-27): `from_call` is a free function;
|
||||
the assembly layer calls it after `connect()`. The overlay is per-connection
|
||||
(Layer 2, ADR-024), so a stale overlay dies with the connection; re-import on
|
||||
reconnect is naturally scoped to the new connection. A
|
||||
`CallConnection::refresh()` method for mid-connection re-discovery is a
|
||||
genuine feature addition — non-breaking, additive — if a deployment needs
|
||||
manual re-discovery without drop-and-reconnect. See
|
||||
the assembly layer calls it after the dial (in `AlknetClient`). The overlay
|
||||
is per-connection (Layer 2, ADR-024), so a stale overlay dies with the
|
||||
connection; re-import on reconnect is naturally scoped to the new
|
||||
connection. A `CallConnection::refresh()` method for mid-connection
|
||||
re-discovery is a genuine feature addition — non-breaking, additive — if a
|
||||
deployment needs manual re-discovery without drop-and-reconnect. See
|
||||
[ADR-069](../../decisions/069-from-call-manual-free-function.md).
|
||||
|
||||
**Namespace collision** (DC-3, OQ-28): under the peer-graph model (ADR-029),
|
||||
@@ -542,8 +518,8 @@ alknet-call (lean — no HTTP client, no HTTP server)
|
||||
├── OperationAdapter trait (the contract — async, per ADR-017 §5)
|
||||
├── from_call (transport-agnostic — discovers remote ops via
|
||||
│ call protocol over any Connection)
|
||||
└── CallClient (outbound connection take-over — spawn_dispatch
|
||||
transport-agnostic, connect QUIC convenience)
|
||||
└── CallClient (outbound connection take-over —
|
||||
spawn_dispatch, transport-agnostic; dial in AlknetClient)
|
||||
|
||||
alknet-http (owns HTTP server + HTTP client — separate crate, separate Phase 0)
|
||||
├── ProtocolHandler for h2/http1.1/h3 (axum server — inbound HTTP)
|
||||
@@ -733,9 +709,9 @@ Based on the gap analysis and the downstream unblock chain:
|
||||
holds a `PeerCompositeEnv` with `connections: HashMap<PeerId, Arc<dyn OperationEnv>>`,
|
||||
not a singular connection overlay. `invoke_peer()` routes to the right peer
|
||||
via `PeerRef::Specific` / `PeerRef::Any` (ADR-029 §1-2).
|
||||
- **`from_call` is a manual free function.** The assembly layer calls it after
|
||||
`connect()`. The overlay is per-connection so re-import on reconnect is
|
||||
naturally scoped (DC-2, OQ-27). See
|
||||
- **`from_call` is a manual free function.** The assembly layer calls it
|
||||
after the dial (in `AlknetClient`). The overlay is per-connection so
|
||||
re-import on reconnect is naturally scoped (DC-2, OQ-27). See
|
||||
[ADR-069](../../decisions/069-from-call-manual-free-function.md).
|
||||
- **`from_call` namespace collision is same-peer only.** Cross-peer collision
|
||||
dissolves (same name on different peers is fine — separate sub-overlays,
|
||||
@@ -767,13 +743,12 @@ 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. ~~`from_jsonschema` clause superseded by ADR-066~~ |
|
||||
| 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` 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` |
|
||||
| Storage boundary and repo/adapter pattern | [ADR-033](../../decisions/033-storage-boundary-and-repo-adapter-pattern.md) | Core defines repo traits + in-memory defaults; persistence adapters are separate crates |
|
||||
| ~~Peer-scoped registry filtering~~ (superseded) | ~~[ADR-028](../../decisions/028-callclient-peer-scoped-registry-filtering.md)~~ | ~~Default-deny; `remote_safe: bool`; trusted-peer opt-in~~ — superseded by ADR-029 (flat-namespace single-peer model couldn't express head→N-workers; parallel auth system duplicated existing `AccessControl`) |
|
||||
| Secret material flow and capability injection | [ADR-014](../../decisions/014-secret-material-flow-and-capability-injection.md) | The no-env-vars invariant's foundation; capabilities injected at assembly layer |
|
||||
| Handler registration, provenance, and composition authority | [ADR-022](../../decisions/022-handler-registration-provenance-and-composition-authority.md) | The registration bundle adapters produce; `composition_authority: None` for leaves |
|
||||
| Operation registry layering | [ADR-024](../../decisions/024-operation-registry-layering.md) | Layer 2 per-connection overlay where `from_call` imports land |
|
||||
@@ -799,10 +774,11 @@ See [open-questions.md](../../open-questions.md) for full details.
|
||||
- **OQ-26** (resolved): `AdapterError` variants — `DiscoveryFailed`,
|
||||
`SchemaParse`, `Transport`, `Unauthorized`, `SamePeerCollision`
|
||||
(replaces flat `Conflict`). `#[non_exhaustive]`.
|
||||
- **OQ-27** (resolved): `from_call` re-import trigger — `from_call` is a manual
|
||||
free function; the assembly layer calls it after `connect()`. A
|
||||
`CallConnection::refresh()` method is a genuine feature addition —
|
||||
non-breaking, additive. See [ADR-069](../../decisions/069-from-call-manual-free-function.md).
|
||||
- **OQ-27** (resolved): `from_call` re-import trigger — `from_call` is a
|
||||
manual free function; the assembly layer calls it after the dial (in
|
||||
`AlknetClient`). A `CallConnection::refresh()` method is a genuine
|
||||
feature addition — non-breaking, additive. See
|
||||
[ADR-069](../../decisions/069-from-call-manual-free-function.md).
|
||||
- **OQ-28** (resolved): `from_call` namespace collision — same-peer
|
||||
collision = error; cross-peer dissolved by ADR-029 (separate sub-overlays).
|
||||
`namespace_prefix` is optional local-naming sugar.
|
||||
@@ -823,8 +799,7 @@ See [open-questions.md](../../open-questions.md) for full details.
|
||||
(ADR-029 §3.7).
|
||||
- **OQ-33** (resolved by ADR-030): `PeerId` is a logical id. Source is
|
||||
`Identity.id` from `IdentityProvider` resolution (= `PeerEntry.peer_id`,
|
||||
stable across key rotation), not a connection-assigned UUID. The UUID
|
||||
workaround is removed. See OQ-33 in open-questions.md.
|
||||
stable across key rotation). See OQ-33 in open-questions.md.
|
||||
- **OQ-34** (resolved by ADR-030 + ADR-033): Persistent peer registry —
|
||||
the storage boundary is `core trait + in-memory default` (config-backed
|
||||
`ConfigIdentityProvider` now; persistence adapters additive in separate
|
||||
@@ -856,9 +831,8 @@ See [open-questions.md](../../open-questions.md) for full details.
|
||||
|
||||
- ADR-017: Call Protocol Client and Adapter Contract (the spec this document
|
||||
operationally fills)
|
||||
- ADR-029: Peer-Graph Routing Model (supersedes ADR-028; resolves DC-1 with
|
||||
peer-keyed overlays + `AccessControl`-based peer authorization)
|
||||
- ~~ADR-028~~: Peer-Scoped Registry Filtering (superseded by ADR-029)
|
||||
- ADR-029: Peer-Graph Routing Model (resolves DC-1 with peer-keyed overlays
|
||||
+ `AccessControl`-based peer authorization)
|
||||
- `call-protocol.md` — `CallAdapter`, `CallConnection`, dispatch loop, stream
|
||||
model (the server-side complement to this document)
|
||||
- `operation-registry.md` — `HandlerRegistration`, provenance, capability
|
||||
|
||||
@@ -397,15 +397,13 @@ pub enum OperationProvenance {
|
||||
| `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.
|
||||
> **`FromJsonSchema` provenance.** `from_jsonschema` is an HTTP-backed
|
||||
> single-endpoint adapter in `alknet-http` (ADR-066): a real reqwest
|
||||
> forwarding handler, not a schema-only placeholder. `FromJsonSchema`
|
||||
> is a leaf, same trust model as `FromOpenAPI` (HTTP endpoint trusted;
|
||||
> handler is a forwarding stub). Schema validation without a handler is
|
||||
> served by consuming `OperationSpec` directly, not by registering a
|
||||
> placeholder op.
|
||||
|
||||
#### CompositionAuthority
|
||||
|
||||
@@ -929,11 +927,10 @@ 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`/`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~~ |
|
||||
| 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` 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 |
|
||||
| Streaming handler for subscriptions | [ADR-049](../../decisions/049-streaming-handler-for-subscriptions.md) | `StreamingHandler` type alongside `Handler`; `HandlerKind` enum on `HandlerRegistration` validated against `op_type`; `invoke_streaming()` on `OperationRegistry`; `invoke()` and `OperationEnv::invoke()` error with `INVALID_OPERATION_TYPE` on `Subscription` ops; composition stays request/response-only, stream composition is handler-level |
|
||||
| Dynamic resource ownership for runtime-spawned resources | [ADR-050](../../decisions/050-dynamic-resource-ownership-for-runtime-spawned-resources.md) | `AccessControl::check` consults an `OwnershipProvider` (sync read trait, ADR-033 repo/adapter pattern); `OperationSpec` gains `resource_id_path` (JSON pointer into the input); proxy-only access pattern (spawner owns, proxy to share, teardown revokes); `list` = scope-gate + result-filter; teardown = automatic, handler-driven; composition = two orthogonal checks, ADR-015/022 unchanged |
|
||||
|
||||
@@ -953,10 +950,11 @@ See [open-questions.md](../../open-questions.md) for full details.
|
||||
variants: `DiscoveryFailed`, `SchemaParse`, `Transport`, `Unauthorized`,
|
||||
`SamePeerCollision` (replaces flat `Conflict`). `#[non_exhaustive]`. See
|
||||
[client-and-adapters.md](client-and-adapters.md).
|
||||
- **OQ-27** (resolved): `from_call` re-import trigger — `from_call` is a manual
|
||||
free function; the assembly layer calls it after `connect()`. A
|
||||
`CallConnection::refresh()` method is a genuine feature addition —
|
||||
non-breaking, additive. See [ADR-069](../../decisions/069-from-call-manual-free-function.md).
|
||||
- **OQ-27** (resolved): `from_call` re-import trigger — `from_call` is a
|
||||
manual free function; the assembly layer calls it after the dial (in
|
||||
`AlknetClient`). A `CallConnection::refresh()` method is a genuine
|
||||
feature addition — non-breaking, additive. See
|
||||
[ADR-069](../../decisions/069-from-call-manual-free-function.md).
|
||||
- **OQ-28** (resolved): `from_call` namespace collision — same-peer
|
||||
collision = error; cross-peer dissolved by ADR-029 (separate sub-overlays).
|
||||
`namespace_prefix` is optional local-naming sugar. See
|
||||
|
||||
Reference in new issue
Block a user