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
@@ -125,11 +125,10 @@ data channels byte-forwarded with `channel_id` rewrite (4-byte field
|
||||
rewrite within the 8-byte header); the hub never runs protocol-specific
|
||||
handlers),
|
||||
[ADR-080](decisions/080-channelclient.md) (`ChannelClient`,
|
||||
transport-agnostic `from_connection` primary; `connect_quic` removed
|
||||
per ADR-089 §5 (dial extracted to `AlknetClient`),
|
||||
bidirectionality preserved; `AlknetClient` dial-seam extracted as
|
||||
`alknet-client` per ADR-089, resolving OQ-55; amended by ADR-093 —
|
||||
`stream_types` field removed from `open_channel` and `Channel`),
|
||||
transport-agnostic `from_connection` primary; dial lives in
|
||||
`AlknetClient` (`alknet-client`, ADR-089, resolving OQ-55);
|
||||
bidirectionality preserved; no `stream_types` on `open_channel`/`Channel`
|
||||
per ADR-093),
|
||||
[ADR-081](decisions/081-channels-subcrate-decomposition.md) (sub-crate
|
||||
decomposition — `channels-core` (pure multiplexer, depends on alknet-core
|
||||
only, no call dependency) / `channels-call` (channel 0 pre-negotiation +
|
||||
@@ -219,7 +218,7 @@ adapter location map is now consistent: all HTTP-backed adapters
|
||||
|----------|--------|-------------|
|
||||
| [overview.md](overview.md) | draft | Workspace-level overview, crate graph (core mono-repo scope per ADR-085), hub/worker model, shared types, design principles |
|
||||
| [open-questions.md](open-questions.md) | draft | OQ index — theme-grouped tables + Deferred/Blocked section; per-OQ files in [`questions/`](questions/) |
|
||||
| [crates/core/README.md](crates/core/README.md) | draft | alknet-core crate index — shared types + auth + config (endpoint extracted to `alknet-endpoint` per ADR-083 Am. 2026-07-15; `ConnectionCredentials`/`RemoteIdentity` moved here from `alknet-call` per ADR-091; `CallCredentials` removed per ADR-091 Am. 2026-07-17) |
|
||||
| [crates/core/README.md](crates/core/README.md) | draft | alknet-core crate index — shared types + auth + config (endpoint in `alknet-endpoint` per ADR-083 Am. 2026-07-15; `ConnectionCredentials`/`RemoteIdentity` here per ADR-091) |
|
||||
| [crates/core/core-types.md](crates/core/core-types.md) | draft | ProtocolHandler, HandlerError, Connection (`Box<dyn BidiStreamSource>` — ADR-070), BidiStreamSource trait, BiStream, StreamError |
|
||||
| [crates/core/endpoint.md](crates/core/endpoint.md) | deprecated | Endpoint spec — **moved to `alknet-endpoint`** (ADR-083 Am. 2026-07-15); see [`crates/endpoint/README.md`](crates/endpoint/README.md) |
|
||||
| [crates/core/auth.md](crates/core/auth.md) | draft | AuthContext (incl. `anonymous` constructor), Identity, IdentityProvider, AuthToken, resolution flow |
|
||||
@@ -227,7 +226,7 @@ adapter location map is now consistent: all HTTP-backed adapters
|
||||
| [crates/call/README.md](crates/call/README.md) | draft | alknet-call crate index |
|
||||
| [crates/call/call-protocol.md](crates/call/call-protocol.md) | draft | CallAdapter, hand-rolled EventEnvelope framing (no irpc — ADR-064), stream model, PendingRequestMap, bidirectional calls, streaming subscribe example |
|
||||
| [crates/call/operation-registry.md](crates/call/operation-registry.md) | draft | OperationSpec, Handler, OperationRegistry, AccessControl, capability injection, service discovery (hand-rolled, no irpc) |
|
||||
| [crates/call/client-and-adapters.md](crates/call/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) |
|
||||
| [crates/call/client-and-adapters.md](crates/call/client-and-adapters.md) | draft | CallClient (transport-agnostic `spawn_dispatch` primary; dial 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) |
|
||||
| [crates/http/README.md](crates/http/README.md) | draft | alknet-http crate index |
|
||||
| [crates/http/overview.md](crates/http/overview.md) | draft | Crate purpose, two roles (server + client host), dependencies, adapter location map |
|
||||
| [crates/http/http-server.md](crates/http/http-server.md) | draft | HttpAdapter for h2/http1.1 + WebSocket upgrade route, axum over QUIC, Bearer auth, stealth, /healthz |
|
||||
@@ -252,15 +251,15 @@ adapter location map is now consistent: all HTTP-backed adapters
|
||||
| [crates/vault/protocol.md](crates/vault/protocol.md) | stable | DerivedKey redaction, KeyType, serialization behavior |
|
||||
| [crates/hub/README.md](crates/hub/README.md) | draft | alknet-hub crate — composes a subset of three endpoint types (web/native/iroh — ADR-086), channels substrate (ADR-079 relay), worker registration flow (OQ-58), identity over transports, aggregated peer env, connection lifecycle, service discovery |
|
||||
| [crates/tls/README.md](crates/tls/README.md) | reviewed | alknet-tls crate — shared TLS config (`TlsServerConfig` + `TlsClientConfig`) shared across quinn + TCP+TLS + iroh; one cert, one ACME state machine, N transports; split ALPN lists per endpoint type (ADR-086, resolves OQ-62); `FingerprintPinVerifier` in `alknet-tls` (ADR-089 §5); `webpki-roots` fallback for empty platform stores (ADR-088 §5); isolates cert-reuse from transport wrappers (ADR-082) |
|
||||
| [crates/client/README.md](crates/client/README.md) | draft | alknet-client crate — the native client dial seam (`AlknetClient`), client-side analogue of `AlknetEndpoint`; three dials (QUIC + TCP+TLS via `TlsClientConfig`, iroh via key) unified on `&ConnectionCredentials` (ADR-091); optional SOCKS5 proxy (ADR-090 — UDP ASSOCIATE for QUIC, CONNECT for TCP+TLS, force-relay-only + HTTP-to-SOCKS5 bridge for iroh; OQ-67 resolved); produces `Connection` for `CallClient`/`ChannelClient` take-over; `CallClient::connect`/`ChannelClient::connect_quic` removed (dial centralized here); `alknet/register` named (wire protocol deferred, OQ-66) |
|
||||
| [crates/endpoint/README.md](crates/endpoint/README.md) | draft | alknet-endpoint crate — the server-side accept-loop runner (`AlknetEndpoint`), extracted from `alknet-core` (ADR-083 Am. 2026-07-15); takes pre-built transports via `with_quinn`/`with_iroh`/`with_tcp_tls`; public `dispatch` for SSH/WT; `EndpointError` removed (vestigial); handler crates no longer transitively link quinn/iroh |
|
||||
| [crates/client/README.md](crates/client/README.md) | draft | alknet-client crate — the native client dial seam (`AlknetClient`), client-side analogue of `AlknetEndpoint`; three dials (QUIC + TCP+TLS via `TlsClientConfig`, iroh via key) unified on `&ConnectionCredentials` (ADR-091); optional SOCKS5 proxy (ADR-090 — UDP ASSOCIATE for QUIC, CONNECT for TCP+TLS, force-relay-only + HTTP-to-SOCKS5 bridge for iroh; OQ-67 resolved); produces `Connection` for `CallClient`/`ChannelClient` take-over; dial centralized here; `alknet/register` named (wire protocol deferred, OQ-66) |
|
||||
| [crates/endpoint/README.md](crates/endpoint/README.md) | draft | alknet-endpoint crate — the server-side accept-loop runner (`AlknetEndpoint`), extracted from `alknet-core` (ADR-083 Am. 2026-07-15); takes pre-built transports via `with_quinn`/`with_iroh`/`with_tcp_tls`; public `dispatch` for SSH/WT; handler crates no longer transitively link quinn/iroh |
|
||||
| [crates/channels/README.md](crates/channels/README.md) | draft | alknet-channels crate — multiplexing proxy, 8-byte chunk format, N channels over one transport stream |
|
||||
| [crates/channels/overview.md](crates/channels/overview.md) | draft | Crate purpose, the multiplexing collapse, dependencies, transport agnosticism, WASM, relationship to existing crates |
|
||||
| [crates/channels/channels-wire.md](crates/channels/channels-wire.md) | draft | 8-byte chunk format, the add/strip composition, sentinels, framing disambiguation, wire-level invariants (REQ-CH-01..05) |
|
||||
| [crates/channels/channels-connection.md](crates/channels/channels-connection.md) | draft | `ChannelBidiStreamSource` (implements `BidiStreamSource`), `accept_bi` yields `BiStream`, recursive composition |
|
||||
| [crates/channels/channels-adapter.md](crates/channels/channels-adapter.md) | draft | `ChannelsAdapter`, `ChannelManager`, demux/mux contracts (REQ-CH-01..04), two-pump pattern (ADR-078) |
|
||||
| [crates/channels/channel-operations.md](crates/channels/channel-operations.md) | draft | `channel/open`/`close`/`control`/`resources/subscribe`, ACL flow, `direction` semantics, hub relay contract (ADR-079) |
|
||||
| [crates/channels/channel-client.md](crates/channels/channel-client.md) | draft | `ChannelClient` — client side of a channels connection, transport-agnostic `from_connection` primary; `connect_quic` removed per ADR-089 §5 (dial extracted to `AlknetClient`); bidirectionality preserved |
|
||||
| [crates/channels/channel-client.md](crates/channels/channel-client.md) | draft | `ChannelClient` — client side of a channels connection, transport-agnostic `from_connection` primary; dial lives in `AlknetClient` (ADR-089); bidirectionality preserved |
|
||||
|
||||
## ADR Table
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -25,7 +25,7 @@ handler owns its sub-stream multiplexing on the `BiStream` it receives.
|
||||
| [channels-connection.md](channels-connection.md) | draft | `ChannelBidiStreamSource` (implements `BidiStreamSource` — ADR-070/074, as amended by ADR-093), `accept_bi` yields one `BiStream` per channel, recursive composition |
|
||||
| [channels-adapter.md](channels-adapter.md) | draft | `ChannelsAdapter` (`ProtocolHandler` on `alknet/channels`), `ChannelManager`, demux/mux contracts (REQ-CH-01..04), the two-pump pattern (ADR-078) |
|
||||
| [channel-operations.md](channel-operations.md) | draft | `channel/open`, `channel/close`, `channel/control`, `channel/resources/subscribe` — call-protocol operations on channel 0, ACL flow, `direction` semantics, the hub relay contract (ADR-079) |
|
||||
| [channel-client.md](channel-client.md) | draft | `ChannelClient` — the client side of a channels connection; transport-agnostic `from_connection` primary; `connect_quic` removed per ADR-089 §5 (dial extracted to `AlknetClient`); bidirectionality preserved |
|
||||
| [channel-client.md](channel-client.md) | draft | `ChannelClient` — the client side of a channels connection; transport-agnostic `from_connection` primary; dial lives in `AlknetClient` (ADR-089); bidirectionality preserved |
|
||||
|
||||
## Applicable ADRs
|
||||
|
||||
@@ -38,10 +38,10 @@ handler owns its sub-stream multiplexing on the `BiStream` it receives.
|
||||
| [074](../../decisions/074-channelconnection-bidistreamsource.md) | ChannelConnection — BidiStreamSource over Chunk Reassembly | Per-channel `BidiStreamSource` impl; `accept_bi` yields `BiStream` (amended by ADR-093 — `into_sub_streams` removed) |
|
||||
| [075](../../decisions/075-channelsadapter-and-channelmanager.md) | ChannelsAdapter and ChannelManager | Substrate-agnostic demux loop; REQ-CH-01..04 contracts |
|
||||
| [076](../../decisions/076-backpressure-channel-limits-id-reuse.md) | Backpressure, Channel Limits, and ID Reuse | Bounded-buffer (1 MiB default), 256-channel cap, monotonic IDs with wrap |
|
||||
| [077](../../decisions/077-tty-inside-channels.md) | TTY Inside Channels — Sub-Streams, Not Wire Format | TTY's two modes (direct vs channels); **reversed by ADR-093 — TTY always uses its 5-byte format, carried transparently** |
|
||||
| [077](../../decisions/077-tty-inside-channels.md) | TTY Inside Channels — Sub-Streams, Not Wire Format | TTY's two modes (direct vs channels); TTY always uses its 5-byte format, carried transparently in the channels payload |
|
||||
| [078](../../decisions/078-two-pump-shutdown-on-completion.md) | Two-Pump Shutdown-on-Completion Pattern | The two-pump deadlock contract; handler-level, not channels-layer |
|
||||
| [079](../../decisions/079-hub-relay-translate-not-forward.md) | Hub Relay — Translate, Not Transparently Forward | The hub translates channel 0, byte-forwards data channels with ID rewrite |
|
||||
| [080](../../decisions/080-channelclient.md) | ChannelClient — the Client Side of a Channels Connection | `ChannelClient`, transport-agnostic `from_connection` primary; `connect_quic` removed per ADR-089 §5; `AlknetClient` dial-seam extracted (ADR-089, resolves OQ-55) |
|
||||
| [080](../../decisions/080-channelclient.md) | ChannelClient — the Client Side of a Channels Connection | `ChannelClient`, transport-agnostic `from_connection` primary; dial lives in `AlknetClient` (ADR-089, resolves OQ-55) |
|
||||
| [081](../../decisions/081-channels-subcrate-decomposition.md) | channels Sub-Crate Decomposition | `channels-core` (pure multiplexer) / `channels-call` (call coupling + ChannelClient); hub and worker are consumers, not sub-crates |
|
||||
| [070](../../decisions/070-bidistreamsource-trait.md) | BidiStreamSource Trait | The `Connection` extension point `ChannelBidiStreamSource` implements |
|
||||
| [092](../../decisions/092-bistream-as-the-handler-leaf.md) | `BiStream` as the Handler Leaf | `accept_bi` returns `BiStream`; the transport-leaf decision ADR-093 builds on |
|
||||
@@ -55,7 +55,7 @@ handler owns its sub-stream multiplexing on the `BiStream` it receives.
|
||||
|
||||
| OQ | Title | Status | Relevance |
|
||||
|----|-------|--------|-----------|
|
||||
| OQ-55 | AlknetClient / Client Establishment Extraction | resolved (ADR-089) | `ChannelClient`'s API is decided (ADR-080): transport-agnostic `from_connection` primary; `connect_quic` removed (ADR-089 §5). `AlknetClient` core extraction is now resolved — the native dial seam is `alknet-client` (ADR-089) |
|
||||
| OQ-55 | AlknetClient / Client Establishment Extraction | resolved (ADR-089) | `ChannelClient`'s API is decided (ADR-080): transport-agnostic `from_connection` primary; dial lives in `AlknetClient` (`alknet-client`, ADR-089) |
|
||||
| OQ-56 | Full channel-level flow-control windowing | deferred(scope) | Bounded-buffer is decided (ADR-076); full windowing is an extension blocked on "a real deployment observes HOL blocking on a saturated channel where bounded buffer is insufficient" |
|
||||
| OQ-57 | Two-pump helper extraction to alknet-core | deferred(scope) | The shutdown-on-completion *contract* is decided (ADR-078); the *helper* extraction is blocked on a second two-pump handler existing (shape convergence) |
|
||||
| OQ-68 | Add/strip API shape (built-in vs utility) | open | Whether the 8-byte header add/strip is built into the channels read/write path or exposed as a standalone utility. The *contract* is decided (ADR-093); the *function surface* is not |
|
||||
|
||||
@@ -48,27 +48,6 @@ impl ChannelClient {
|
||||
pub async fn from_connection(connection: Connection)
|
||||
-> Result<Self, ChannelError>;
|
||||
|
||||
/// QUIC convenience constructor. Dials a QUIC connection to `addr`
|
||||
/// on ALPN `alknet/channels` (using `credentials` for the TLS
|
||||
/// handshake — ADR-034 verifier selection), then calls
|
||||
/// `from_connection`. This is the "I just want QUIC" one-liner;
|
||||
/// it is additive over `from_connection` and is a two-way door —
|
||||
/// `connect_tcp_tls`, `connect_webtransport`, etc. can be added
|
||||
/// alongside it without touching the one-way-door surface.
|
||||
///
|
||||
/// **REMOVED per ADR-089 §5.** The dial is extracted into
|
||||
/// `AlknetClient` (`alknet-client`); `connect_quic` is deleted,
|
||||
/// not delegated, to avoid `alknet-channels-call` depending on
|
||||
/// `alknet-client`. Callers compose `AlknetClient::dial_quic` +
|
||||
/// `from_connection`. See "Relationship to `AlknetClient`" below.
|
||||
/// The `CallCredentials` parameter is moot — `CallCredentials` is
|
||||
/// removed per ADR-091 (amended 2026-07-17); the dial consumes
|
||||
/// `ConnectionCredentials` from `alknet-core`.
|
||||
pub async fn connect_quic(
|
||||
addr: SocketAddr,
|
||||
credentials: CallCredentials, // REMOVED — CallCredentials is removed
|
||||
) -> Result<Self, ChannelError>;
|
||||
|
||||
/// Open a data channel with the given ALPN and params. Sends
|
||||
/// `channel/open` on channel 0, waits for the response, and returns
|
||||
/// the channel.
|
||||
@@ -119,12 +98,6 @@ pub struct ResourceEntry {
|
||||
}
|
||||
```
|
||||
|
||||
> **Amendment (ADR-093, 2026-07-18):** the `stream_types` field is
|
||||
> **removed** from `open_channel`'s signature and from `Channel`. The
|
||||
> channels layer has no `stream_type` concept (ADR-093) — the handler
|
||||
> owns its sub-stream multiplexing on the `BiStream` it receives. The
|
||||
> handler's sub-stream set is implicit in its ALPN's wire format.
|
||||
|
||||
## Transport-agnostic by construction
|
||||
|
||||
`ChannelClient` is the client side of the channels protocol. The channels
|
||||
@@ -143,14 +116,12 @@ a WebTransport `BiStream`, an SSH `direct-tcpip` channel wrapped via
|
||||
ADR-044) — all produce a `Connection` that `from_connection` accepts
|
||||
unchanged. This mirrors the server side's `ChannelsAdapter::handle(Connection)`, which is substrate-agnostic by the same mechanism.
|
||||
|
||||
`connect_quic(addr, credentials)` was a **convenience** constructor —
|
||||
dial QUIC, then `from_connection`. It is **removed** per ADR-089 §5:
|
||||
keeping it as a thin wrapper over `AlknetClient::dial_quic` would make
|
||||
`alknet-channels-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(...).await?` +
|
||||
`ChannelClient::from_connection(conn).await?` — two lines, the dial
|
||||
then the take-over.
|
||||
The dial (QUIC, TCP+TLS, iroh) lives in `AlknetClient` (`alknet-client`,
|
||||
ADR-089), not on `ChannelClient`. Callers compose
|
||||
`AlknetClient::dial_quic(...).await?` + `ChannelClient::from_connection(conn).await?`
|
||||
— two lines, the dial then the take-over. Keeping the dial off
|
||||
`ChannelClient` avoids `alknet-channels-call` depending on `alknet-client`;
|
||||
the protocol crates are parallel to the dial, not downstream of it.
|
||||
|
||||
The credential/verifier-selection rule (ADR-034) lives in the dial
|
||||
(`AlknetClient`), not in `from_connection` — `from_connection` receives
|
||||
@@ -172,21 +143,20 @@ populates what operations they expose).
|
||||
name follows the `CallClient` convention (the side that dialed), not a
|
||||
request/response role.
|
||||
|
||||
## Relationship to `AlknetClient` (ADR-089 — resolved)
|
||||
## Relationship to `AlknetClient`
|
||||
|
||||
`ChannelClient`'s *API* is transport-agnostic — `from_connection` takes a
|
||||
pre-established `Connection`. The shared *dial+TLS* seam
|
||||
(`AlknetClient`, OQ-55) is now extracted: [`alknet-client`](../client/README.md)
|
||||
(`AlknetClient`, OQ-55) is [`alknet-client`](../client/README.md), which
|
||||
provides `AlknetClient` with three dial methods (`dial_quic` /
|
||||
`dial_tcp_tls` / `dial_iroh`), each producing a `Connection` that
|
||||
`from_connection` consumes. The dial is transport-specific (QUIC,
|
||||
TCP+TLS, iroh); the take-over (`from_connection`) is
|
||||
transport-agnostic. The two concerns are separated.
|
||||
|
||||
`connect_quic` is removed (see above) — `AlknetClient::dial_quic` is the
|
||||
dial that feeds `from_connection`. A caller that needs transport
|
||||
selection (QUIC with TCP+TLS fallback) uses `AlknetClient` directly;
|
||||
the fallback policy is a caller concern. See
|
||||
`AlknetClient::dial_quic` is the dial that feeds `from_connection`. A
|
||||
caller that needs transport selection (QUIC with TCP+TLS fallback) uses
|
||||
`AlknetClient` directly; the fallback policy is a caller concern. See
|
||||
[ADR-089](../../decisions/089-alknetclient-native-dial-seam.md) for the
|
||||
full decision and [OQ-55](../../questions/055-alknetclient-establishment-extraction.md)
|
||||
(resolved).
|
||||
@@ -197,21 +167,20 @@ All design decisions are documented as ADRs in [decisions/](../../decisions/).
|
||||
|
||||
| ADR | Decision | Summary |
|
||||
|-----|----------|---------|
|
||||
| [080](../../decisions/080-channelclient.md) | ChannelClient | Client side; transport-agnostic `from_connection` primary; `connect_quic` convenience **removed** per ADR-089 §5 (dial extracted to `AlknetClient`); `AlknetClient` dial-seam extracted (ADR-089, resolves OQ-55) |
|
||||
| [093](../../decisions/093-channels-pure-channel-multiplexing.md) | channels Pure Channel Multiplexing | `stream_types` removed from `open_channel` and `Channel`; handler owns sub-stream multiplexing |
|
||||
| [080](../../decisions/080-channelclient.md) | ChannelClient | Client side; transport-agnostic `from_connection` primary; dial lives in `AlknetClient` (ADR-089, resolves OQ-55) |
|
||||
| [093](../../decisions/093-channels-pure-channel-multiplexing.md) | channels Pure Channel Multiplexing | No `stream_types` on `open_channel`/`Channel`; handler owns sub-stream multiplexing |
|
||||
|
||||
## Open Questions
|
||||
|
||||
- **OQ-55** (resolved by ADR-089): `AlknetClient` core **dial+TLS seam**
|
||||
— extracted as `alknet-client` with three dial methods.
|
||||
`ChannelClient`'s API is transport-agnostic (`from_connection`); the
|
||||
dial is the shared seam, now extracted. See
|
||||
[ADR-089](../../decisions/089-alknetclient-native-dial-seam.md).
|
||||
— `alknet-client` with three dial methods. `ChannelClient`'s API is
|
||||
transport-agnostic (`from_connection`); the dial is the shared seam.
|
||||
See [ADR-089](../../decisions/089-alknetclient-native-dial-seam.md).
|
||||
|
||||
## References
|
||||
|
||||
- ADR-080: ChannelClient (the decision)
|
||||
- ADR-093: channels pure channel multiplexing (`stream_types` removed)
|
||||
- ADR-093: channels pure channel multiplexing (no `stream_types`)
|
||||
- ADR-073: channel lifecycle operations (`open_channel` sends `channel/open`)
|
||||
- ADR-074: ChannelBidiStreamSource (what `Channel.source` wraps, as
|
||||
amended by ADR-093 — `accept_bi` yields a `BiStream`)
|
||||
|
||||
@@ -53,16 +53,6 @@ data flows — the same round-trip the call protocol makes for every
|
||||
operation. All current channel types (TTY, tunnel, SSH) already require a
|
||||
negotiation round-trip, so the open round-trip is not additive latency.
|
||||
|
||||
> **Amendment (ADR-093, 2026-07-18):** the `stream_types` field is **removed**
|
||||
> from `channel/open`'s input and output. The channels layer has no
|
||||
> `stream_type` concept (ADR-093) — the handler owns its sub-stream
|
||||
> multiplexing on the `BiStream` it receives. The handler's sub-stream set
|
||||
> is implicit in its ALPN's wire format (e.g., TTY's 5-byte format
|
||||
> declares its own `stream_type` set internally; the channels layer carries
|
||||
> the bytes transparently). The `channel:stream_type_unavailable` error
|
||||
> code is removed (the channels layer can't refuse a `stream_type` it
|
||||
> doesn't know about).
|
||||
|
||||
**Error codes** (new `CallError.code` strings, not new framing):
|
||||
|
||||
| code | meaning | retryable |
|
||||
@@ -117,13 +107,6 @@ The channels layer routes `message` to the handler's control handle for
|
||||
`channel_id`. The `message` JSON is ALPN-specific; the channels layer does
|
||||
not interpret it.
|
||||
|
||||
> **Amendment (ADR-093, 2026-07-18):** the `stream_type` field is **removed**
|
||||
> from `channel/control`'s input. Under ADR-093, the channels layer has no
|
||||
> `stream_type` concept — the control message is routed to the handler's
|
||||
> control handle (an ALPN-specific concept the handler owns), not to a
|
||||
> channels-layer `(channel_id, stream_type)` reassembly buffer. The
|
||||
> handler decides what to do with the message.
|
||||
|
||||
### `channel/resources/subscribe` — live resource discovery
|
||||
|
||||
**This is a `Subscription` operation (ADR-049), not a polled Query.** The
|
||||
@@ -284,7 +267,7 @@ All design decisions are documented as ADRs in [decisions/](../../decisions/).
|
||||
| [073](../../decisions/073-channel-lifecycle-operations.md) | Channel Lifecycle Operations | The four ops; `direction` pinned; subscribe not poll |
|
||||
| [072](../../decisions/072-channel-0-pre-negotiated-call.md) | Channel 0 Pre-Negotiated | Channel 0 = `alknet/call` |
|
||||
| [079](../../decisions/079-hub-relay-translate-not-forward.md) | Hub Relay | Translate channel 0, byte-forward data channels |
|
||||
| [093](../../decisions/093-channels-pure-channel-multiplexing.md) | channels Pure Channel Multiplexing | `stream_types` field removed from `channel/open`; `stream_type` removed from `channel/control`; handler owns sub-stream multiplexing |
|
||||
| [093](../../decisions/093-channels-pure-channel-multiplexing.md) | channels Pure Channel Multiplexing | No `stream_types` on `channel/open`; no `stream_type` on `channel/control`; handler owns sub-stream multiplexing |
|
||||
| [049](../../decisions/049-streaming-handler-for-subscriptions.md) | StreamingHandler | The machinery `channel/resources/subscribe` uses |
|
||||
| [032](../../decisions/032-forwarded-for-identity.md) | Forwarded-For Identity | The auth chain for hub-relayed opens |
|
||||
| [050](../../decisions/050-dynamic-resource-ownership-for-runtime-spawned-resources.md) | Dynamic Resource Ownership | The ownership store the spoke queries |
|
||||
|
||||
@@ -74,9 +74,7 @@ per channel at `channel/open` time and wraps it in a `Connection` via
|
||||
|
||||
Every handler — TTY, tunnel, SSH, call — receives a `Connection`, calls
|
||||
`accept_bi()` once, gets a `BiStream`, and sub-multiplexes it however it
|
||||
wants. There is one accessor; the two-accessor design
|
||||
(`accept_bi` vs `into_sub_streams`) from ADR-074's original shape is
|
||||
removed by ADR-093.
|
||||
wants. There is one accessor.
|
||||
|
||||
```rust
|
||||
// Tunnel handler — ~15 lines, zero channels-layer awareness
|
||||
@@ -105,8 +103,8 @@ async fn handle(&self, connection: Connection, _auth: &AuthContext)
|
||||
```
|
||||
|
||||
```rust
|
||||
// TTY handler (inside-channels mode, ADR-077 reversed by ADR-093) —
|
||||
// the SAME code as direct mode, just a different BiStream source.
|
||||
// TTY handler (inside-channels mode) — the SAME code as direct
|
||||
// mode, just a different BiStream source.
|
||||
async fn handle(&self, connection: Connection, _auth: &AuthContext)
|
||||
-> Result<(), HandlerError>
|
||||
{
|
||||
@@ -160,21 +158,21 @@ All design decisions are documented as ADRs in [decisions/](../../decisions/).
|
||||
|
||||
| ADR | Decision | Summary |
|
||||
|-----|----------|---------|
|
||||
| [074](../../decisions/074-channelconnection-bidistreamsource.md) | ChannelConnection | Per-channel `BidiStreamSource`; yield-once `accept_bi` (amended by ADR-093 — `into_sub_streams` removed, `accept_bi` is the only accessor) |
|
||||
| [093](../../decisions/093-channels-pure-channel-multiplexing.md) | channels Pure Channel Multiplexing | The umbrella decision: 8-byte header, no `stream_type`, `into_sub_streams` removed, `BiStream`-only |
|
||||
| [074](../../decisions/074-channelconnection-bidistreamsource.md) | ChannelConnection | Per-channel `BidiStreamSource`; yield-once `accept_bi` is the only accessor |
|
||||
| [093](../../decisions/093-channels-pure-channel-multiplexing.md) | channels Pure Channel Multiplexing | The umbrella decision: 8-byte header, no `stream_type`, `BiStream`-only |
|
||||
| [070](../../decisions/070-bidistreamsource-trait.md) | BidiStreamSource Trait | The extension point `ChannelBidiStreamSource` implements |
|
||||
| [092](../../decisions/092-bistream-as-the-handler-leaf.md) | `BiStream` as the Handler Leaf | `accept_bi` returns `BiStream` (the transport-leaf decision this doc builds on) |
|
||||
| [065](../../decisions/065-connection-from-stream-generic-single-stream.md) | `Connection::from_stream` | The yield-once path generalized for channels |
|
||||
|
||||
## References
|
||||
|
||||
- ADR-074: ChannelConnection (the decision, amended by ADR-093)
|
||||
- ADR-074: ChannelConnection (the decision)
|
||||
- ADR-093: channels pure channel multiplexing (the umbrella decision)
|
||||
- ADR-070: BidiStreamSource trait
|
||||
- ADR-092: `BiStream` as the handler leaf
|
||||
- ADR-065: `Connection::from_stream` (the yield-once path generalized)
|
||||
- ADR-077: TTY inside channels (reversed by ADR-093 — TTY always uses
|
||||
its 5-byte format, carried transparently in the channels payload)
|
||||
- ADR-077: TTY inside channels (TTY always uses its 5-byte format,
|
||||
carried transparently in the channels payload)
|
||||
- `docs/research/alknet-channels/poc-summary.md` §POC Target 2 (the
|
||||
yield-once `Connection::from_stream` validation)
|
||||
- `docs/research/stream-unification/findings.md` — the research that
|
||||
|
||||
@@ -233,10 +233,10 @@ inside-channels mode (`channel/open` with ALPN `alknet/tty`), the TTY
|
||||
adapter uses its own 5-byte wire format (ADR-052). The two modes differ
|
||||
only in *where the `BiStream` comes from* — a top-level connection vs a
|
||||
channels-backed `Connection`. The same `wire.rs` code runs in both modes
|
||||
(ADR-077, reversed by ADR-093): the channels layer strips its 8-byte
|
||||
header and hands TTY the payload bytes; TTY parses its 5-byte header from
|
||||
the payload. The `TtyBackend` trait and `TtyHandle` are unchanged;
|
||||
backends don't know which mode the adapter is in.
|
||||
(ADR-077): the channels layer strips its 8-byte header and hands TTY the
|
||||
payload bytes; TTY parses its 5-byte header from the payload. The
|
||||
`TtyBackend` trait and `TtyHandle` are unchanged; backends don't know
|
||||
which mode the adapter is in.
|
||||
|
||||
### alknet-ssh (future)
|
||||
|
||||
@@ -279,10 +279,10 @@ All design decisions are documented as ADRs in [decisions/](../../decisions/).
|
||||
| [074](../../decisions/074-channelconnection-bidistreamsource.md) | ChannelConnection | Per-channel `BidiStreamSource`; yield-once `accept_bi` (amended by ADR-093 — `into_sub_streams` removed) |
|
||||
| [075](../../decisions/075-channelsadapter-and-channelmanager.md) | ChannelsAdapter and ChannelManager | Substrate-agnostic demux loop; REQ-CH-01..04 |
|
||||
| [076](../../decisions/076-backpressure-channel-limits-id-reuse.md) | Backpressure, Limits, ID Reuse | Bounded-buffer (1 MiB), 256-channel cap, monotonic IDs |
|
||||
| [077](../../decisions/077-tty-inside-channels.md) | TTY Inside Channels | Two modes (direct vs channels); **reversed by ADR-093 — TTY always uses its 5-byte format, carried transparently** |
|
||||
| [077](../../decisions/077-tty-inside-channels.md) | TTY Inside Channels | TTY's two modes (direct vs channels); TTY always uses its 5-byte format, carried transparently in the channels payload |
|
||||
| [078](../../decisions/078-two-pump-shutdown-on-completion.md) | Two-Pump Pattern | Shutdown-on-completion contract; handler-level |
|
||||
| [079](../../decisions/079-hub-relay-translate-not-forward.md) | Hub Relay | Translate channel 0, byte-forward data channels with ID rewrite |
|
||||
| [080](../../decisions/080-channelclient.md) | ChannelClient | Client side; transport-agnostic `from_connection` primary; `connect_quic` removed per ADR-089 §5; `AlknetClient` dial-seam extracted (ADR-089, resolves OQ-55) |
|
||||
| [080](../../decisions/080-channelclient.md) | ChannelClient | Client side; transport-agnostic `from_connection` primary; dial lives in `AlknetClient` (ADR-089, resolves OQ-55) |
|
||||
| [081](../../decisions/081-channels-subcrate-decomposition.md) | Sub-Crate Decomposition | `channels-core` (pure multiplexer) / `channels-call` (call coupling + ChannelClient); hub and worker are consumers |
|
||||
|
||||
## Open Questions
|
||||
|
||||
@@ -20,16 +20,17 @@ HTTP-to-SOCKS5 bridge for iroh).
|
||||
|
||||
## What
|
||||
|
||||
`AlknetClient` is the dial. Before this crate, each protocol client
|
||||
(`CallClient::connect`, `ChannelClient::connect_quic`) built its own
|
||||
QUIC dial inline — building a `TlsClientConfig`, constructing a
|
||||
`quinn::Endpoint`, calling `connect_with`, wrapping as a `Connection`.
|
||||
The dial boilerplate was duplicated, and there was no place for a
|
||||
second transport's dial (TCP+TLS, iroh) to live without each protocol
|
||||
client growing its own per-transport dial helper. Those convenience
|
||||
constructors are removed (see "Relationship to `CallClient` /
|
||||
`ChannelClient`" below); `AlknetClient` is the single dial home, and
|
||||
the protocol crates shed their TLS/transport deps entirely.
|
||||
`AlknetClient` is the dial. It owns the transport-specific work each
|
||||
outbound connection needs — building a `TlsClientConfig`, constructing
|
||||
a `quinn::Endpoint`, calling `connect_with`, wrapping as a
|
||||
`Connection` — for each of three transports (QUIC, TCP+TLS, iroh).
|
||||
Centralizing the dial in one crate keeps the dial boilerplate in one
|
||||
place and gives a natural home for a second transport's dial (TCP+TLS,
|
||||
iroh) without each protocol client growing its own per-transport dial
|
||||
helper. `AlknetClient` is the single dial home; the protocol crates
|
||||
(`CallClient`, `ChannelClient`) shed their TLS/transport deps entirely
|
||||
and take over the `Connection` `AlknetClient` produces (see
|
||||
"Relationship to `CallClient` / `ChannelClient`" below).
|
||||
|
||||
`alknet-client` extracts the dial the same way ADR-083 extracted the
|
||||
accept loop on the server side: one type that takes pre-built transport
|
||||
@@ -406,15 +407,12 @@ let conn = client.dial_tcp_tls("hub.example", addr, b"alknet/call", &creds).awai
|
||||
let call = CallClient::new(registry, idp).spawn_dispatch(conn);
|
||||
```
|
||||
|
||||
The per-protocol QUIC convenience constructors that previously lived on
|
||||
`CallClient` / `ChannelClient` (`connect` / `connect_quic`) are
|
||||
**removed**. They welded the dial into the protocol crate — every
|
||||
`CallClient` user transitively pulled `quinn` + `rustls` + the TLS
|
||||
verifier machinery, and the convenience constructor's existence made
|
||||
`alknet-call` / `alknet-channels-call` depend on `alknet-client` (or
|
||||
duplicate the dial), contradicting the dep graph below. The dial is a
|
||||
distinct concern from the protocol take-over; `AlknetClient` is the
|
||||
single home for it. A caller that wants the old one-liner shape composes
|
||||
The dial is a distinct concern from the protocol take-over;
|
||||
`AlknetClient` is the single home for it. Keeping the dial off
|
||||
`CallClient` / `ChannelClient` means every `CallClient` user doesn't
|
||||
transitively pull `quinn` + `rustls` + the TLS verifier machinery, and
|
||||
`alknet-call` / `alknet-channels-call` don't depend on `alknet-client`
|
||||
(or duplicate the dial) — see the dep graph below. A caller composes
|
||||
two lines: `client.dial_quic(...).await?` then
|
||||
`CallClient::new(...).spawn_dispatch(conn)` (or
|
||||
`ChannelClient::from_connection(conn).await?`). See
|
||||
@@ -675,7 +673,7 @@ All design decisions are documented as ADRs in
|
||||
|-----|----------|---------|
|
||||
| [089](../../decisions/089-alknetclient-native-dial-seam.md) | AlknetClient — native client dial seam | New crate `alknet-client`; client-side analogue of `AlknetEndpoint`; three dials (QUIC + TCP+TLS via `TlsClientConfig`, iroh via key); resolves OQ-55; `alknet/register` named, wire protocol deferred (§3/§5 amended by ADR-091 — dial takes `ConnectionCredentials`, not `CallCredentials`) |
|
||||
| [090](../../decisions/090-client-dial-socks5-proxy-seam.md) | Client-Dial SOCKS5 Proxy Seam | `AlknetClient` gains `with_socks5_proxy`; `dial_quic` routes via UDP ASSOCIATE, `dial_tcp_tls` via CONNECT, `dial_iroh` forces relay-only via an HTTP-to-SOCKS5 bridge; OQ-67 resolved; grounded in the quinn-proxy + iroh-proxy PoCs |
|
||||
| [091](../../decisions/091-connectioncredentials-decouple-dial-from-call.md) | `ConnectionCredentials` — decouple dial from call protocol | The dial credential bundle is `ConnectionCredentials` (transport-level: `local_identity` + `remote_identity`), not `CallCredentials` (call-protocol-level); all three dial signatures unify on `&ConnectionCredentials`; `dial_iroh`'s `node_id` derived from `remote_identity`; `auth_token` is a per-request payload field; `CallCredentials` removed per Am. 2026-07-17 |
|
||||
| [091](../../decisions/091-connectioncredentials-decouple-dial-from-call.md) | `ConnectionCredentials` — decouple dial from call protocol | The dial credential bundle is `ConnectionCredentials` (transport-level: `local_identity` + `remote_identity`); all three dial signatures unify on `&ConnectionCredentials`; `dial_iroh`'s `node_id` derived from `remote_identity`; `auth_token` is a per-request payload field |
|
||||
|
||||
## Open Questions
|
||||
|
||||
|
||||
@@ -10,19 +10,16 @@ dispatch. Every handler crate depends on `alknet-core` for
|
||||
`ProtocolHandler`, `Connection`, `AuthContext`, `IdentityProvider`, and
|
||||
config types. The endpoint (`AlknetEndpoint`, `HandlerRegistry`) lives
|
||||
in [`alknet-endpoint`](../endpoint/README.md) (ADR-083 Amendment
|
||||
2026-07-15; `EndpointError` is removed — both variants were vestigial);
|
||||
core does not carry the accept-loop runner or its transport deps
|
||||
(quinn, iroh, rcgen, rustls-acme). `Connection::from_quinn` /
|
||||
`from_iroh` are in core's `types.rs` as shared constructors (gated on
|
||||
core's `quinn` / `iroh` features).
|
||||
2026-07-15); core does not carry the accept-loop runner or its
|
||||
transport deps (quinn, iroh, rcgen, rustls-acme).
|
||||
`Connection::from_quinn` / `from_iroh` are in core's `types.rs` as
|
||||
shared constructors (gated on core's `quinn` / `iroh` features).
|
||||
|
||||
`ConnectionCredentials` and `RemoteIdentity` live in `alknet-core` (per
|
||||
ADR-091) — the transport-level credential bundle consumed by the dial
|
||||
(`alknet-client`) and by server-side transport construction. There is no
|
||||
call-protocol credential bundle: `CallCredentials` is removed (ADR-091
|
||||
Am. 2026-07-17 — its `auth_token` field had no reader; `auth_token` is
|
||||
a per-request payload field on `call.requested`, not a transport
|
||||
credential).
|
||||
call-protocol credential bundle; `auth_token` is a per-request payload
|
||||
field on `call.requested`, not a transport credential.
|
||||
|
||||
## Documents
|
||||
|
||||
|
||||
@@ -374,7 +374,7 @@ registration bundle.
|
||||
|----------|-----|---------|
|
||||
| ProtocolHandler receives Connection, not BiStream | [ADR-007](../../decisions/007-bistream-type-definition.md) | Handlers that need multiple streams (SSH, call) have direct access to the Connection |
|
||||
| BiStream is a trait | [ADR-007](../../decisions/007-bistream-type-definition.md) | WASM door preserved, test mocks possible |
|
||||
| `Connection::from_stream` — generic single-stream connections | [ADR-065](../../decisions/065-connection-from-stream-generic-single-stream.md) | `from_stream`/`from_bidi` accept any `AsyncRead + AsyncWrite`; yield-once `accept_bi` contract; unblocks TCP+TLS, SSH channels, WebTransport, wasm; QUIC variants feature-gated, `Stream` variant always available; `MockConnection`/`ConnectionKind::Mock` removed (tests use `from_stream` with `sink`/`empty`) |
|
||||
| `Connection::from_stream` — generic single-stream connections | [ADR-065](../../decisions/065-connection-from-stream-generic-single-stream.md) | `from_stream`/`from_bidi` accept any `AsyncRead + AsyncWrite`; yield-once `accept_bi` contract; unblocks TCP+TLS, SSH channels, WebTransport, wasm; QUIC variants feature-gated, `Stream` variant always available; tests use `from_stream` with `sink`/`empty` |
|
||||
| `BidiStreamSource` — open `Connection` for extension | [ADR-070](../../decisions/070-bidistreamsource-trait.md) | `Connection` holds `Box<dyn BidiStreamSource>`; QUIC/iroh/stream wrap crate-private impls; `from_source` is the public constructor for downstream crates that implement the trait (channels, future transports); `from_quinn`/`from_iroh`/`from_stream`/`from_bidi` preserved; `close(code, reason)` kept on the trait (non-QUIC impls ignore the args — fixes the ADR-065 leftover clippy warning under `--no-default-features`) |
|
||||
| HandlerError is non-fatal | [ADR-010](../../decisions/010-alpn-router-and-endpoint.md) | Handler errors close the connection, not the endpoint |
|
||||
| SendStream/RecvStream wrap quinn + iroh + generic streams | [ADR-010](../../decisions/010-alpn-router-and-endpoint.md), [ADR-065](../../decisions/065-connection-from-stream-generic-single-stream.md) | Internal enum dispatch for QUIC sources and the generic `Stream` variant |
|
||||
|
||||
@@ -7,8 +7,7 @@ last_updated: 2026-07-17
|
||||
|
||||
> **This document is deprecated.** The `AlknetEndpoint` and
|
||||
> `HandlerRegistry` types live in a separate crate, `alknet-endpoint`
|
||||
> (ADR-083 Amendment 2026-07-15). `EndpointError` is removed (both
|
||||
> variants were vestigial). The canonical spec is
|
||||
> (ADR-083 Amendment 2026-07-15). The canonical spec is
|
||||
> [`crates/endpoint/README.md`](../endpoint/README.md).
|
||||
>
|
||||
> The shared types the endpoint imports (`ProtocolHandler`,
|
||||
|
||||
@@ -29,8 +29,7 @@ shared types (every handler crate). No handler crate imports
|
||||
`AlknetEndpoint` or `HandlerRegistry` — they depend on `alknet-core`
|
||||
for `ProtocolHandler`, `Connection`, `AuthContext`, and types only.
|
||||
This keeps the heavy transport deps (quinn, iroh, tokio-rustls) out of
|
||||
the handler crates' dep closure. (`EndpointError` is removed — see
|
||||
below.)
|
||||
the handler crates' dep closure.
|
||||
|
||||
## Why
|
||||
|
||||
@@ -140,26 +139,6 @@ Registration is static at startup (ADR-010, OQ-04). The assembly layer
|
||||
builds a `HandlerRegistry`, inserts all handlers, and passes it to
|
||||
`AlknetEndpoint::new()`.
|
||||
|
||||
### `EndpointError` — removed
|
||||
|
||||
The endpoint previously had an `EndpointError { BindFailed(io::Error),
|
||||
HandlerNotFound(Vec<u8>) }` enum. Both variants are vestigial after
|
||||
ADR-083:
|
||||
|
||||
- `BindFailed` — the endpoint takes pre-built, pre-bound transports
|
||||
(the assembly layer does the binding); the endpoint performs no bind,
|
||||
so it cannot produce a bind error.
|
||||
- `HandlerNotFound` — `dispatch` swallows no-handler matches (close +
|
||||
log per ADR-083), so this variant is never returned.
|
||||
|
||||
The enum is removed. `shutdown()` is infallible (`async fn shutdown(&self)`,
|
||||
no `Result`). If a future requirement adds a real failure path to
|
||||
shutdown or dispatch, a fresh error type is cleaner than retrofitting
|
||||
this one. The `EndpointError` type, its `TlsConfig` variant (already
|
||||
removed by ADR-083), and the `BindFailed`/`HandlerNotFound` variants all
|
||||
move out of the codebase with the endpoint extraction — none survives
|
||||
into `alknet-endpoint`.
|
||||
|
||||
### `TcpTlsListener`
|
||||
|
||||
The type held by the endpoint's `tcp_tls` field — a tuple of the TCP
|
||||
@@ -264,10 +243,8 @@ alknet-endpoint
|
||||
`alknet-endpoint` depends on `alknet-core` (for `Connection`,
|
||||
`ProtocolHandler`, `AuthContext`, `IdentityProvider`, `DynamicConfig`).
|
||||
`HandlerRegistry` lives in `alknet-endpoint` (it moves with the
|
||||
endpoint from core). `EndpointError` is removed (both variants were
|
||||
vestigial — see "`EndpointError` — removed" above). The endpoint does
|
||||
**not** depend on `alknet-tls` — it takes pre-built transports, so TLS
|
||||
config
|
||||
endpoint from core). The endpoint does **not** depend on `alknet-tls` —
|
||||
it takes pre-built transports, so TLS config
|
||||
construction stays at the assembly layer.
|
||||
|
||||
### Crate dependencies (in the dep graph)
|
||||
|
||||
@@ -60,12 +60,11 @@ enabled. It serves two things on a single `h3` connection:
|
||||
1. **HTTP/3 requests** — the standard HTTP/3 over QUIC framing. An
|
||||
HTTP/3 request is dispatched through the same axum `Router` as `h2`/
|
||||
`http/1.1` requests (ADR-042 + ADR-047 — the gateway endpoints are
|
||||
the sole invoke path; the direct-call `POST /{service}/{op}` surface
|
||||
was removed). From the axum router's perspective, an HTTP/3 request
|
||||
is just another HTTP request; the framing difference is handled
|
||||
below the router. The HTTP/3 request path is the **one-directional
|
||||
projection** (client→server calls only — HTTP is request/response;
|
||||
see [http-server.md](http-server.md) §"One-directional projection").
|
||||
the sole invoke path). From the axum router's perspective, an HTTP/3
|
||||
request is just another HTTP request; the framing difference is
|
||||
handled below the router. The HTTP/3 request path is the
|
||||
**one-directional projection** (client→server calls only — HTTP is
|
||||
request/response; see [http-server.md](http-server.md) §"One-directional projection").
|
||||
2. **WebTransport sessions** — the **bidirectional** path. WebTransport
|
||||
is a transport substrate that carries ALPN protocols as
|
||||
bidirectional streams (ADR-043), not a browser→hub one-way path. A
|
||||
|
||||
@@ -287,8 +287,8 @@ another hub (A) is a client from A's perspective. The dial needs a
|
||||
client-side TLS config (`TlsClientConfig`, ADR-087) for the outbound
|
||||
connection's `rustls::ClientConfig` (verifier selection per ADR-034:
|
||||
fingerprint pin for the worker's known key). The dial path mirrors the
|
||||
`from_connection` primary (ADR-080; `ChannelClient::connect_quic` is
|
||||
removed per ADR-089 §5 — the dial lives in `AlknetClient`):
|
||||
`from_connection` primary (ADR-080; the dial lives in `AlknetClient`,
|
||||
ADR-089):
|
||||
|
||||
```rust
|
||||
impl Hub {
|
||||
@@ -296,9 +296,8 @@ impl Hub {
|
||||
/// connection. Transport-agnostic — the caller (or a transport
|
||||
/// helper) produces the `Connection`. This is the primary path;
|
||||
/// `connect_quic_worker` (a hub-level convenience, distinct from
|
||||
/// the removed `ChannelClient::connect_quic` per-protocol
|
||||
/// constructor — ADR-089 §5) and future `connect_tcp_tls_worker`
|
||||
/// are conveniences over it.
|
||||
/// the per-protocol dial in `AlknetClient`) and future
|
||||
/// `connect_tcp_tls_worker` are conveniences over it.
|
||||
pub async fn dial_worker_connection(
|
||||
&self,
|
||||
connection: Connection,
|
||||
@@ -791,7 +790,7 @@ into `CallAdapter::with_aggregated_env`.
|
||||
| Peer-graph routing model | [ADR-029](../../decisions/029-peer-graph-routing-model.md) | Peer-keyed overlays, `PeerRef` routing, `AccessControl`-based peer auth |
|
||||
| PeerEntry and Identity.id | [ADR-030](../../decisions/030-peerentry-and-identity-id-decoupling.md) | `PeerId` = `Identity.id` = `PeerEntry.peer_id` (stable) |
|
||||
| Three peer roles | [ADR-034](../../decisions/034-outgoing-only-x509-and-three-peer-roles.md) | Hub = role-3 `PeerEntry` (mixed fingerprints); browsers not peers; bearer-token identity over TCP/WebTransport |
|
||||
| ChannelClient — transport-agnostic | [ADR-080](../../decisions/080-channelclient.md) | `from_connection` primary; `connect_quic` removed per ADR-089 §5 (dial extracted to `AlknetClient`); the dial path the hub uses |
|
||||
| ChannelClient — transport-agnostic | [ADR-080](../../decisions/080-channelclient.md) | `from_connection` primary; dial in `AlknetClient` (ADR-089) — the dial path the hub uses |
|
||||
| Channels transport-agnostic | [ADR-071](../../decisions/071-channels-wire-format.md) | Substrate modes; `Connection::from_stream`/`from_bidi` (ADR-065) — the substrate the hub relays |
|
||||
| TCP+TLS as first-class owned transport | [ADR-083](../../decisions/083-endpoint-as-accept-loop-runner.md) | `with_tcp_tls(listener, acceptor)` — TCP+TLS is owned by the endpoint, not a sibling loop; supersedes ADR-010 Am. 1 |
|
||||
| Channel 0 pre-negotiated | [ADR-072](../../decisions/072-channel-0-pre-negotiated-call.md) | Channel 0 = `alknet/call`; the `CallAdapter` runs here |
|
||||
@@ -841,8 +840,7 @@ See [open-questions.md](../../open-questions.md) for full details.
|
||||
## References
|
||||
|
||||
- [channel-client.md](../channels/channel-client.md) — `ChannelClient`
|
||||
(`from_connection` — the take-over; `connect_quic` removed per
|
||||
ADR-089 §5, dial now via `AlknetClient`)
|
||||
(`from_connection` — the take-over; dial via `AlknetClient` per ADR-089)
|
||||
- [channels-adapter.md](../channels/channels-adapter.md) —
|
||||
`ChannelsAdapter`, `ChannelManager`, the accept path
|
||||
- [channel-operations.md](../channels/channel-operations.md) —
|
||||
|
||||
@@ -165,9 +165,9 @@ copies in `alknet-tls`, used by both `TlsServerConfig::new` and
|
||||
across the server (in core's endpoint module) and the client (in call's
|
||||
client module); the extraction consolidated them.
|
||||
|
||||
`CallClient::connect` is removed (ADR-089 §5) — the dial is in
|
||||
`AlknetClient` (`alknet-client`); `CallClient` keeps only
|
||||
`spawn_dispatch`, and `alknet-call` has no TLS/transport deps.
|
||||
The dial is in `AlknetClient` (`alknet-client`, ADR-089);
|
||||
`CallClient` keeps only `spawn_dispatch`, and `alknet-call` has no
|
||||
TLS/transport deps.
|
||||
|
||||
### `TlsServerConfig`
|
||||
|
||||
@@ -645,9 +645,8 @@ arrive asynchronously and are logged (ADR-082 §"Behavior-preservation
|
||||
invariants").
|
||||
|
||||
**Ownership.** `TlsError` lives in `alknet-tls`, owned by the crate
|
||||
that produces it. It is not re-exported from `alknet-core`; there is no
|
||||
`EndpointError` (removed per ADR-083 — both variants were vestigial),
|
||||
so core has no endpoint error type and does not need to know about
|
||||
that produces it. It is not re-exported from `alknet-core`; core has
|
||||
no endpoint error type, so core does not need to know about
|
||||
`TlsError`. The assembly layer (hub/worker) depends on `alknet-tls`
|
||||
directly and gets `TlsError` from that dependency.
|
||||
|
||||
@@ -662,8 +661,7 @@ alknet-core (lightweight — types + auth + config + fingerprint + credentials)
|
||||
|
||||
alknet-call (pure protocol crate — no TLS/transport deps per ADR-089 §5)
|
||||
└── alknet-core (ProtocolHandler, Connection, types; ConnectionCredentials/
|
||||
RemoteIdentity from core per ADR-091; CallCredentials removed per
|
||||
ADR-091 Am. 2026-07-17)
|
||||
RemoteIdentity from core per ADR-091)
|
||||
|
||||
alknet-hub (multi-transport endpoint)
|
||||
├── alknet-tls (TlsServerConfig — shared across quinn + TCP)
|
||||
@@ -762,12 +760,10 @@ ALPNs via channel 0). Both consume `TlsClientConfig` through the dial
|
||||
for TCP+TLS); iroh is the exception (shares the key, not the config).
|
||||
`AlknetClient` is the dial that feeds them — it produces a `Connection`
|
||||
and the protocol take-overs (`spawn_dispatch`, `from_connection`)
|
||||
consume it. The per-protocol QUIC convenience constructors
|
||||
(`CallClient::connect` / `ChannelClient::connect_quic`) are removed
|
||||
per ADR-089 §5 — the dial is centralized in `AlknetClient`, and the
|
||||
consume it. The dial is centralized in `AlknetClient` (ADR-089); the
|
||||
protocol crates have no TLS/transport deps. The `alknet/register` ALPN
|
||||
(native registration entry point, parallel to HTTP registration in
|
||||
OQ-58) is named by ADR-089; its wire protocol is deferred (OQ-66).
|
||||
(named by ADR-089; wire protocol deferred, OQ-66) is the native
|
||||
registration entry point, parallel to HTTP registration in OQ-58.
|
||||
|
||||
## References
|
||||
|
||||
|
||||
@@ -100,14 +100,13 @@ alknet-vault (standalone — foundational to ACL: key derivation, identity)
|
||||
│ │ (lightweight types+auth+config+credentials; endpoint in
|
||||
│ │ alknet-endpoint; no rcgen/rustls-pemfile/rustls-acme deps;
|
||||
│ │ quinn/iroh stay for Connection::from_quinn/from_iroh;
|
||||
│ │ ConnectionCredentials + RemoteIdentity per ADR-091;
|
||||
│ │ CallCredentials removed per ADR-091 Am. 2026-07-17)
|
||||
│ │ ConnectionCredentials + RemoteIdentity per ADR-091)
|
||||
│ ├── alknet-tls TlsServerConfig + TlsClientConfig + FingerprintPinVerifier — shared TLS config across quinn + TCP+TLS + iroh (ADR-082/087; FingerprintPinVerifier per ADR-089 §5)
|
||||
│ ├── alknet-call CallAdapter on alknet/call, CallClient (spawn_dispatch only — connect removed per ADR-089 §5), OperationRegistry, adapters (no TLS/transport deps)
|
||||
│ ├── alknet-call CallAdapter on alknet/call, CallClient (spawn_dispatch primary; dial in AlknetClient per ADR-089), OperationRegistry, adapters (no TLS/transport deps)
|
||||
│ ├── alknet-channels
|
||||
│ │ ├── alknet-channels-core pure multiplexer (wire format, demux/mux) — ADR-081
|
||||
│ │ └── alknet-channels-call channel 0 pre-negotiation + lifecycle ops — ADR-081
|
||||
│ ├── alknet-client AlknetClient — native client dial seam (QUIC + TCP+TLS + iroh); produces Connection for CallClient/ChannelClient take-over (ADR-089; CallClient::connect/ChannelClient::connect_quic removed — dial centralized here)
|
||||
│ ├── alknet-client AlknetClient — native client dial seam (QUIC + TCP+TLS + iroh); produces Connection for CallClient/ChannelClient take-over (ADR-089; dial centralized here)
|
||||
│ └── alknet-endpoint AlknetEndpoint — multi-transport accept-loop runner, extracted from core (ADR-083 Am. 2026-07-15); takes pre-built transports; public dispatch for SSH/WT
|
||||
│
|
||||
├── Deployment shapes
|
||||
@@ -358,7 +357,7 @@ All design decisions are documented as ADRs in [decisions/](decisions/).
|
||||
| [014](decisions/014-secret-material-flow-and-capability-injection.md) | Secret Material Flow and Capability Injection | Capabilities carry outbound credentials; call protocol carries no secret material |
|
||||
| [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` takes over connections (`spawn_dispatch` transport-agnostic primary; `connect` removed per ADR-089 §5 — dial extracted to `AlknetClient`); `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` takes over connections (`spawn_dispatch` transport-agnostic primary; dial in `AlknetClient` per ADR-089); `from_call` imports remote ops; connection direction independent of call direction |
|
||||
| [018](decisions/018-vault-standalone-crate.md) | Vault as Standalone Crate | Zero alknet crate dependencies; vault defines own types and errors |
|
||||
| [019](decisions/019-vault-assembly-layer-only.md) | Vault Assembly-Layer-Only Access | The assembly layer (CLI binary) is the sole direct caller; handlers never hold a vault reference |
|
||||
| [020](decisions/020-hd-derivation-for-encryption-keys.md) | HD Derivation for Encryption Keys | SLIP-0010 derivation from seed, not PBKDF2; salt field unused in v2 |
|
||||
|
||||
Reference in new issue
Block a user