docs: remove CallCredentials — dead field, dead from_call path, auth_token is per-request payload
ADR-091 amended 2026-07-17: CallCredentials removed (not retained in alknet-call). Trace showed CallCredentials.auth_token had no reader (connect() read only tls_identity + remote_identity; spawn_dispatch takes no credentials; from_call's credentials_auth_token was a different type, always None, never connected). auth_token is a per-request payload field — browsers send it in the WS payload; the HTTP gateway resolves bearer → Identity at its boundary. from_call's credentials_auth_token dead path removed in the same pass (OpSummary field, handler params, build_forwarded_payload param, and the two tests asserting the never-exercised Some path). ADR-089 §5 further amended, ADR-080 noted, all spec READMEs and overview updated. Migration plan (findings.md) corrected: Phase 5 prune now includes CallCredentials removal + from_call dead-path removal; test audit corrected (4 unchanged + 2 move to core, not 6 unchanged); integration-test split documented; all 'or' hedges resolved.
This commit is contained in:
1 parent
ec46fc6d59
commit
e91d943857
13 files changed
+422
-200
No files matched your search
@@ -210,7 +210,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 |
|
| [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/) |
|
| [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` stays in `alknet-call`) |
|
| [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/core-types.md](crates/core/core-types.md) | draft | ProtocolHandler, HandlerError, Connection (`Box<dyn BidiStreamSource>` — ADR-070), BidiStreamSource trait, BiStream, StreamError |
|
| [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/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 |
|
| [crates/core/auth.md](crates/core/auth.md) | draft | AuthContext (incl. `anonymous` constructor), Identity, IdentityProvider, AuthToken, resolution flow |
|
||||||
@@ -345,9 +345,9 @@ adapter location map is now consistent: all HTTP-backed adapters
|
|||||||
| [086](decisions/086-endpoint-types-and-entry-points.md) | Endpoint Types and Entry Points | Accepted |
|
| [086](decisions/086-endpoint-types-and-entry-points.md) | Endpoint Types and Entry Points | Accepted |
|
||||||
| [087](decisions/087-tlsclientconfig-not-blocked-on-dial.md) | `TlsClientConfig` Not Blocked on Dial Seam | Accepted (§5 amended by ADR-089 — `FingerprintPinVerifier` moves to `alknet-tls`; `alknet-call` sheds TLS deps; input framing amended by ADR-091 — `ClientVerifierContext` derived from `ConnectionCredentials`, not `CallCredentials`) |
|
| [087](decisions/087-tlsclientconfig-not-blocked-on-dial.md) | `TlsClientConfig` Not Blocked on Dial Seam | Accepted (§5 amended by ADR-089 — `FingerprintPinVerifier` moves to `alknet-tls`; `alknet-call` sheds TLS deps; input framing amended by ADR-091 — `ClientVerifierContext` derived from `ConnectionCredentials`, not `CallCredentials`) |
|
||||||
| [088](decisions/088-tlserror-shape.md) | `TlsError` Shape — Single Enum, Owned by `alknet-tls` | Accepted (§5 added — `webpki-roots` fallback when platform store is empty; §7 references ADR-089 for handshake-error surfacing) |
|
| [088](decisions/088-tlserror-shape.md) | `TlsError` Shape — Single Enum, Owned by `alknet-tls` | Accepted (§5 added — `webpki-roots` fallback when platform store is empty; §7 references ADR-089 for handshake-error surfacing) |
|
||||||
| [089](decisions/089-alknetclient-native-dial-seam.md) | AlknetClient — Native Client Dial Seam | Accepted (resolves OQ-55; `CallClient::connect` / `ChannelClient::connect_quic` removed; §3/§5 amended by ADR-091 — dial takes `ConnectionCredentials`, not `CallCredentials`; `CallCredentials` stays in `alknet-call`; `FingerprintPinVerifier` moved to `alknet-tls`; `ClientError` removed; `alknet-call` sheds TLS deps) |
|
| [089](decisions/089-alknetclient-native-dial-seam.md) | AlknetClient — Native Client Dial Seam | Accepted (resolves OQ-55; `CallClient::connect` / `ChannelClient::connect_quic` removed; §3/§5 amended by ADR-091 — dial takes `ConnectionCredentials`, not `CallCredentials`; `CallCredentials` removed per ADR-091 Am. 2026-07-17; `FingerprintPinVerifier` moved to `alknet-tls`; `ClientError` removed; `alknet-call` sheds TLS deps) |
|
||||||
| [090](decisions/090-client-dial-socks5-proxy-seam.md) | Client-Dial SOCKS5 Proxy Seam | Accepted (§5 amended 2026-07-16 — OQ-67 resolved: iroh force-relay-only + HTTP-to-SOCKS5 bridge) |
|
| [090](decisions/090-client-dial-socks5-proxy-seam.md) | Client-Dial SOCKS5 Proxy Seam | Accepted (§5 amended 2026-07-16 — OQ-67 resolved: iroh force-relay-only + HTTP-to-SOCKS5 bridge) |
|
||||||
| [091](decisions/091-connectioncredentials-decouple-dial-from-call.md) | `ConnectionCredentials` — Decouple Dial Credentials from Call Protocol | Accepted (amends ADR-089 §3/§5 and ADR-087 input framing; dial takes `ConnectionCredentials` not `CallCredentials`; all three dial signatures unified; `dial_iroh`'s `node_id` derived from `remote_identity`; `auth_token` stays in call-protocol layer) |
|
| [091](decisions/091-connectioncredentials-decouple-dial-from-call.md) | `ConnectionCredentials` — Decouple Dial Credentials from Call Protocol | Accepted (amends ADR-089 §3/§5 and ADR-087 input framing; dial takes `ConnectionCredentials` not `CallCredentials`; all three dial signatures unified; `dial_iroh`'s `node_id` derived from `remote_identity`; `auth_token` is a per-request payload field; `CallCredentials` removed per Am. 2026-07-17) |
|
||||||
|
|
||||||
## Open Questions
|
## Open Questions
|
||||||
|
|
||||||
|
|||||||
@@ -130,12 +130,14 @@ impl CallClient {
|
|||||||
/// and to let `alknet-call` shed its TLS/transport deps entirely.
|
/// and to let `alknet-call` shed its TLS/transport deps entirely.
|
||||||
/// Callers compose `AlknetClient::dial_quic(...).await?` +
|
/// Callers compose `AlknetClient::dial_quic(...).await?` +
|
||||||
/// `CallClient::new(...).spawn_dispatch(conn)`. `ClientError` is
|
/// `CallClient::new(...).spawn_dispatch(conn)`. `ClientError` is
|
||||||
/// removed (it was produced only by `connect`).
|
/// 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")]
|
#[cfg(feature = "quinn")]
|
||||||
pub async fn connect(
|
pub async fn connect(
|
||||||
&self,
|
&self,
|
||||||
addr: SocketAddr,
|
addr: SocketAddr,
|
||||||
credentials: CallCredentials,
|
credentials: CallCredentials, // REMOVED — CallCredentials is removed
|
||||||
) -> Result<CallConnection, ClientError>;
|
) -> Result<CallConnection, ClientError>;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -246,7 +248,8 @@ attribution, filtered by the calling peer's authorization). See
|
|||||||
|
|
||||||
### Credential sources for connections
|
### Credential sources for connections
|
||||||
|
|
||||||
The credential dimensions are split across two layers (ADR-091):
|
The credential dimensions are split across two layers (ADR-091, amended
|
||||||
|
2026-07-17):
|
||||||
|
|
||||||
- **`ConnectionCredentials`** (in `alknet-core`, moved from
|
- **`ConnectionCredentials`** (in `alknet-core`, moved from
|
||||||
`alknet-call` per ADR-091) — the **transport-level** credential
|
`alknet-call` per ADR-091) — the **transport-level** credential
|
||||||
@@ -254,16 +257,21 @@ The credential dimensions are split across two layers (ADR-091):
|
|||||||
transport-identity dimensions: `local_identity` (the local node's
|
transport-identity dimensions: `local_identity` (the local node's
|
||||||
`TlsIdentity`) and `remote_identity` (the expected fingerprint). The
|
`TlsIdentity`) and `remote_identity` (the expected fingerprint). The
|
||||||
dial does not depend on the call protocol for this type.
|
dial does not depend on the call protocol for this type.
|
||||||
- **`CallCredentials`** (stays in `alknet-call`) — the
|
- **`auth_token`** — a **per-request payload field**, not a
|
||||||
**call-protocol** credential bundle. The `auth_token` dimension
|
call-protocol credential bundle. `Dispatcher::resolve_identity`
|
||||||
(ADR-017 §7) is a call-protocol / hub-layer concept: a bearer token
|
reads `payload.get("auth_token")` on each `call.requested` payload.
|
||||||
correlated to an identity via `IdentityProvider::resolve_from_token`,
|
Browsers send it directly in the WebSocket call payload; the HTTP
|
||||||
used for browsers (no raw-key support) and `alknet/register` (no
|
gateway resolves the bearer token to an `Identity` at its boundary
|
||||||
prior peer relationship). It is a per-request field on
|
(the call layer sees the identity, not the token). `CallCredentials`
|
||||||
`call.requested` payloads, not a transport credential.
|
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.
|
||||||
|
|
||||||
Credentials come from `Capabilities` (ADR-014), never from environment
|
Credentials come from `Capabilities` (ADR-014), never from environment
|
||||||
variables. The three credential dimensions (ADR-017 §7):
|
variables. The transport-identity dimensions (ADR-017 §7):
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// Transport-level (alknet-core, consumed by the dial — ADR-091)
|
// Transport-level (alknet-core, consumed by the dial — ADR-091)
|
||||||
@@ -272,16 +280,15 @@ pub struct ConnectionCredentials {
|
|||||||
pub remote_identity: Option<RemoteIdentity>, // expected fingerprint (None = CA path / fail-closed)
|
pub remote_identity: Option<RemoteIdentity>, // expected fingerprint (None = CA path / fail-closed)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call-protocol-level (alknet-call — the auth_token stays here)
|
// auth_token is a per-request payload field, not a credential struct.
|
||||||
// The auth_token is set per-request on call.requested payloads, not
|
// Browsers send it in the WebSocket call payload; the HTTP gateway
|
||||||
// carried by the dial.
|
// resolves bearer → Identity at its boundary.
|
||||||
|
// Dispatcher::resolve_identity reads payload.get("auth_token").
|
||||||
```
|
```
|
||||||
|
|
||||||
`CallCredentials` retains `auth_token` (and may assemble from
|
There is no call-protocol credential bundle. `CallCredentials` is
|
||||||
`ConnectionCredentials` + `auth_token` at the take-over site, or the
|
removed. The transport dimensions (`local_identity`, `remote_identity`)
|
||||||
caller provides `auth_token` per-request via `call_with_payload`). The
|
moved to `ConnectionCredentials` in `alknet-core` per ADR-091.
|
||||||
transport dimensions (`local_identity`, `remote_identity`) moved to
|
|
||||||
`ConnectionCredentials` in `alknet-core` per ADR-091.
|
|
||||||
|
|
||||||
/// Expected identity of the remote node (ADR-017 §7, extended by
|
/// Expected identity of the remote node (ADR-017 §7, extended by
|
||||||
/// ADR-034 §2). Carries a fingerprint string the assembly layer
|
/// ADR-034 §2). Carries a fingerprint string the assembly layer
|
||||||
@@ -710,8 +717,10 @@ Based on the gap analysis and the downstream unblock chain:
|
|||||||
(mis)placed in `alknet-call` as a schema-only placeholder; ADR-066
|
(mis)placed in `alknet-call` as a schema-only placeholder; ADR-066
|
||||||
moved it to `alknet-http` as a real HTTP-backed adapter. See Adapter
|
moved it to `alknet-http` as a real HTTP-backed adapter. See Adapter
|
||||||
Location Map.
|
Location Map.
|
||||||
- **No secret material on the wire.** `CallCredentials` carries vault-derived
|
- **No secret material on the wire.** `ConnectionCredentials` carries vault-derived
|
||||||
material for the *outbound* connection (TLS identity, auth token); the
|
material for the *outbound* connection (TLS identity); `auth_token` is a
|
||||||
|
per-request payload field (browsers send it in the WebSocket call payload;
|
||||||
|
the HTTP gateway resolves bearer → `Identity` at its boundary). The
|
||||||
call protocol's wire format carries no private keys, API keys, or decrypted
|
call protocol's wire format carries no private keys, API keys, or decrypted
|
||||||
credentials (ADR-014). The no-env-vars invariant (above) is the dispatch-side
|
credentials (ADR-014). The no-env-vars invariant (above) is the dispatch-side
|
||||||
corollary.
|
corollary.
|
||||||
@@ -748,7 +757,7 @@ Based on the gap analysis and the downstream unblock chain:
|
|||||||
`ServerCertVerifier` uses CA verification (`WebPkiServerVerifier`) for
|
`ServerCertVerifier` uses CA verification (`WebPkiServerVerifier`) for
|
||||||
such remotes; known peers (hub with `PeerEntry`) use fingerprint
|
such remotes; known peers (hub with `PeerEntry`) use fingerprint
|
||||||
pinning. See [ADR-034](../../decisions/034-outgoing-only-x509-and-three-peer-roles.md).
|
pinning. See [ADR-034](../../decisions/034-outgoing-only-x509-and-three-peer-roles.md).
|
||||||
- **`CallCredentials.remote_identity: None` is load-bearing.** `None`
|
- **`ConnectionCredentials.remote_identity: None` is load-bearing.** `None`
|
||||||
means "no `PeerEntry` for this remote → use CA verification (X.509)
|
means "no `PeerEntry` for this remote → use CA verification (X.509)
|
||||||
or fail closed (Ed25519 raw key)" per the ADR-034 §3 verifier rule.
|
or fail closed (Ed25519 raw key)" per the ADR-034 §3 verifier rule.
|
||||||
The implementation must not default `remote_identity` to a placeholder
|
The implementation must not default `remote_identity` to a placeholder
|
||||||
|
|||||||
@@ -61,9 +61,12 @@ impl ChannelClient {
|
|||||||
/// not delegated, to avoid `alknet-channels-call` depending on
|
/// not delegated, to avoid `alknet-channels-call` depending on
|
||||||
/// `alknet-client`. Callers compose `AlknetClient::dial_quic` +
|
/// `alknet-client`. Callers compose `AlknetClient::dial_quic` +
|
||||||
/// `from_connection`. See "Relationship to `AlknetClient`" below.
|
/// `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(
|
pub async fn connect_quic(
|
||||||
addr: SocketAddr,
|
addr: SocketAddr,
|
||||||
credentials: CallCredentials,
|
credentials: CallCredentials, // REMOVED — CallCredentials is removed
|
||||||
) -> Result<Self, ChannelError>;
|
) -> Result<Self, ChannelError>;
|
||||||
|
|
||||||
/// Open a data channel with the given ALPN and params. Sends
|
/// Open a data channel with the given ALPN and params. Sends
|
||||||
|
|||||||
@@ -346,7 +346,7 @@ and passes them to each dial.
|
|||||||
The call-protocol `auth_token` (a hub-correlated bearer for browsers
|
The call-protocol `auth_token` (a hub-correlated bearer for browsers
|
||||||
and `alknet/register` — ADR-017 §7) is a per-request field on
|
and `alknet/register` — ADR-017 §7) is a per-request field on
|
||||||
`call.requested` payloads, not a transport credential. It stays in the
|
`call.requested` payloads, not a transport credential. It stays in the
|
||||||
call-protocol layer (`CallCredentials` in `alknet-call`); the dial does
|
call-protocol layer (`auth_token` is a per-request payload field); the dial does
|
||||||
not carry it. `Dispatcher::resolve_identity` resolves it via
|
not carry it. `Dispatcher::resolve_identity` resolves it via
|
||||||
`IdentityProvider::resolve_from_token` at dispatch time; the `from_call`
|
`IdentityProvider::resolve_from_token` at dispatch time; the `from_call`
|
||||||
forwarding handler sets it via `build_forwarded_payload`. This keeps
|
forwarding handler sets it via `build_forwarded_payload`. This keeps
|
||||||
@@ -675,7 +675,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`) |
|
| [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 |
|
| [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` stays in the call-protocol layer; `CallCredentials` stays in `alknet-call` |
|
| [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 |
|
||||||
|
|
||||||
## Open Questions
|
## Open Questions
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ shared constructors (gated on core's `quinn` / `iroh` features).
|
|||||||
`ConnectionCredentials` and `RemoteIdentity` move to `alknet-core`
|
`ConnectionCredentials` and `RemoteIdentity` move to `alknet-core`
|
||||||
(from `alknet-call`, per ADR-091) — the transport-level credential
|
(from `alknet-call`, per ADR-091) — the transport-level credential
|
||||||
bundle consumed by the dial (`alknet-client`) and by server-side
|
bundle consumed by the dial (`alknet-client`) and by server-side
|
||||||
transport construction. `CallCredentials` (the call-protocol credential
|
transport construction. `ConnectionCredentials` (the transport-level credential
|
||||||
bundle, including `auth_token`) stays in `alknet-call` — the dial does
|
bundle, including `auth_token`) stays in `alknet-call` — the dial does
|
||||||
not carry call-protocol dimensions.
|
not carry call-protocol dimensions.
|
||||||
|
|
||||||
|
|||||||
@@ -312,7 +312,7 @@ impl Hub {
|
|||||||
pub async fn connect_quic_worker(
|
pub async fn connect_quic_worker(
|
||||||
&self,
|
&self,
|
||||||
addr: SocketAddr,
|
addr: SocketAddr,
|
||||||
credentials: CallCredentials,
|
credentials: ConnectionCredentials,
|
||||||
config: FromCallConfig,
|
config: FromCallConfig,
|
||||||
) -> Result<(PeerId, ChannelClient), HubError>;
|
) -> Result<(PeerId, ChannelClient), HubError>;
|
||||||
}
|
}
|
||||||
@@ -542,7 +542,7 @@ impl Hub {
|
|||||||
```
|
```
|
||||||
|
|
||||||
The supervision loop takes a `dial` closure rather than a `SocketAddr`
|
The supervision loop takes a `dial` closure rather than a `SocketAddr`
|
||||||
+ `CallCredentials` pair. This keeps the loop transport-agnostic —
|
+ `ConnectionCredentials` pair. This keeps the loop transport-agnostic —
|
||||||
the caller decides the transport by what the closure does. The
|
the caller decides the transport by what the closure does. The
|
||||||
backoff and re-discovery logic is the same regardless of transport.
|
backoff and re-discovery logic is the same regardless of transport.
|
||||||
|
|
||||||
|
|||||||
@@ -696,7 +696,7 @@ alknet-core (loses TLS setup code + endpoint)
|
|||||||
|
|
||||||
alknet-call (pure protocol crate — no TLS/transport deps per ADR-089 §5)
|
alknet-call (pure protocol crate — no TLS/transport deps per ADR-089 §5)
|
||||||
└── alknet-core (ProtocolHandler, Connection, types; ConnectionCredentials/
|
└── alknet-core (ProtocolHandler, Connection, types; ConnectionCredentials/
|
||||||
RemoteIdentity moved to core per ADR-091; CallCredentials stays in
|
RemoteIdentity moved to core per ADR-091; CallCredentials removed per ADR-091 Am. 2026-07-17
|
||||||
alknet-call)
|
alknet-call)
|
||||||
|
|
||||||
alknet-hub (multi-transport endpoint)
|
alknet-hub (multi-transport endpoint)
|
||||||
|
|||||||
@@ -122,9 +122,16 @@ impl ChannelClient {
|
|||||||
/// ADR-034 verifier selection), then calls `from_connection`.
|
/// ADR-034 verifier selection), then calls `from_connection`.
|
||||||
/// Additive and two-way-door — `connect_tcp_tls`,
|
/// Additive and two-way-door — `connect_tcp_tls`,
|
||||||
/// `connect_webtransport`, etc. join it as transports are added.
|
/// `connect_webtransport`, etc. join it as transports are added.
|
||||||
|
///
|
||||||
|
/// **REMOVED per ADR-089 §5.** The dial is extracted into
|
||||||
|
/// `AlknetClient`; `connect_quic` is deleted, not delegated.
|
||||||
|
/// Callers compose `AlknetClient::dial_quic` + `from_connection`.
|
||||||
|
/// 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(
|
pub async fn connect_quic(
|
||||||
addr: SocketAddr,
|
addr: SocketAddr,
|
||||||
credentials: CallCredentials,
|
credentials: CallCredentials, // REMOVED — CallCredentials is removed
|
||||||
) -> Result<Self, ChannelError>;
|
) -> Result<Self, ChannelError>;
|
||||||
|
|
||||||
/// Open a data channel with the given ALPN and params. Sends
|
/// Open a data channel with the given ALPN and params. Sends
|
||||||
|
|||||||
@@ -9,7 +9,10 @@ unify on `&ConnectionCredentials`; `dial_iroh`'s `node_id` parameter is
|
|||||||
derived from `remote_identity`; the `auth_token` stays in the
|
derived from `remote_identity`; the `auth_token` stays in the
|
||||||
call-protocol layer, not the dial; `CallCredentials` stays in
|
call-protocol layer, not the dial; `CallCredentials` stays in
|
||||||
`alknet-call`, only `ConnectionCredentials`/`RemoteIdentity` move to
|
`alknet-call`, only `ConnectionCredentials`/`RemoteIdentity` move to
|
||||||
`alknet-core`)
|
`alknet-core`; §5 further amended 2026-07-17 by ADR-091 —
|
||||||
|
`CallCredentials` is removed entirely (its `auth_token` field had no
|
||||||
|
reader); `auth_token` is a per-request payload field; the `from_call`
|
||||||
|
`credentials_auth_token` dead path is removed)
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
@@ -307,6 +310,25 @@ the dep graph, and the dep graph requires it.
|
|||||||
> `CallCredentials`. All three dial signatures unify on
|
> `CallCredentials`. All three dial signatures unify on
|
||||||
> `&ConnectionCredentials`. See
|
> `&ConnectionCredentials`. See
|
||||||
> [ADR-091](091-connectioncredentials-decouple-dial-from-call.md).
|
> [ADR-091](091-connectioncredentials-decouple-dial-from-call.md).
|
||||||
|
>
|
||||||
|
> **Further amendment 2026-07-17 (ADR-091):** `CallCredentials` is
|
||||||
|
> **removed**, not retained in `alknet-call`. The "stays in
|
||||||
|
> `alknet-call`" framing above is itself superseded: a trace of the code
|
||||||
|
> showed `CallCredentials.auth_token` 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`). The original ADR-091
|
||||||
|
> rationale ("the `from_call` forwarding handler populates `auth_token`
|
||||||
|
> from `CallCredentials`") cited a code path that does not exist.
|
||||||
|
> `auth_token` is a per-request payload field — browsers send it in the
|
||||||
|
> WebSocket call payload; the HTTP gateway resolves bearer → `Identity`
|
||||||
|
> at its boundary (the call layer sees the identity, not the token);
|
||||||
|
> `Dispatcher::resolve_identity` reads `payload.get("auth_token")`.
|
||||||
|
> There is no call-protocol credential bundle. The `from_call`
|
||||||
|
> `credentials_auth_token` dead path is removed in the same pass. See
|
||||||
|
> [ADR-091](091-connectioncredentials-decouple-dial-from-call.md) §"`CallCredentials`
|
||||||
|
> is removed."
|
||||||
|
|
||||||
**Consequence: `FingerprintPinVerifier` moves to `alknet-tls`.** With
|
**Consequence: `FingerprintPinVerifier` moves to `alknet-tls`.** With
|
||||||
`connect` removed and the verifier-selection logic centralized in
|
`connect` removed and the verifier-selection logic centralized in
|
||||||
@@ -391,11 +413,14 @@ several possible native clients sharing the same wire protocols.
|
|||||||
- **`CallClient::spawn_dispatch` / `ChannelClient::from_connection`**
|
- **`CallClient::spawn_dispatch` / `ChannelClient::from_connection`**
|
||||||
— the take-over APIs are unchanged. They consume the `Connection`
|
— the take-over APIs are unchanged. They consume the `Connection`
|
||||||
the dial produces; they do not know `AlknetClient` produced it.
|
the dial produces; they do not know `AlknetClient` produced it.
|
||||||
- **`CallCredentials` / `RemoteIdentity`** — **moved to `alknet-core`**
|
- **`RemoteIdentity`** — **moved to `alknet-core`** (see §5, as amended
|
||||||
(see §5). The shape is unchanged; the location changes from
|
by ADR-091). The location changes from `alknet-call` to `alknet-core`
|
||||||
`alknet-call` to `alknet-core` so the dial does not depend on the
|
so the dial does not depend on the call protocol. The call and
|
||||||
call protocol. Both the call and channels clients consume them from
|
channels clients consume it from core. (`CallCredentials` is removed
|
||||||
core.
|
per ADR-091's 2026-07-17 amendment — it is not moved, it is deleted;
|
||||||
|
its `auth_token` field had no reader. `ConnectionCredentials` is the
|
||||||
|
new transport-level credential bundle in core, carrying
|
||||||
|
`local_identity` + `remote_identity`.)
|
||||||
- **The channels substrate (ADR-071)** — unchanged. The dial produces a
|
- **The channels substrate (ADR-071)** — unchanged. The dial produces a
|
||||||
`Connection`; the channels protocol runs on it.
|
`Connection`; the channels protocol runs on it.
|
||||||
- **ADR-086 (endpoint types / entry points)** — the endpoint-type model
|
- **ADR-086 (endpoint types / entry points)** — the endpoint-type model
|
||||||
@@ -450,14 +475,17 @@ several possible native clients sharing the same wire protocols.
|
|||||||
**Negative:**
|
**Negative:**
|
||||||
|
|
||||||
- **Breaking change: `CallClient::connect` / `ChannelClient::connect_quic`
|
- **Breaking change: `CallClient::connect` / `ChannelClient::connect_quic`
|
||||||
removed; `CallCredentials` / `RemoteIdentity` / `FingerprintPinVerifier`
|
removed; `RemoteIdentity` / `FingerprintPinVerifier` relocated;
|
||||||
relocated; `ClientError` removed.** Call sites that used the
|
`CallCredentials` removed; `ClientError` removed.** Call sites that
|
||||||
convenience constructors must switch to `AlknetClient::dial_*` +
|
used the convenience constructors must switch to `AlknetClient::dial_*`
|
||||||
`spawn_dispatch` / `from_connection`. Import paths for
|
+ `spawn_dispatch` / `from_connection`. Import paths for
|
||||||
`CallCredentials` / `RemoteIdentity` change from `alknet_call` to
|
`RemoteIdentity` change from `alknet_call` to `alknet_core`;
|
||||||
`alknet_core`. This is expected — the develop branch is a total
|
`CallCredentials` is removed (per ADR-091's 2026-07-17 amendment) —
|
||||||
rewrite; there are no external consumers to preserve compatibility
|
callers pass `ConnectionCredentials` to the dial and, where needed,
|
||||||
for. The migration plan handles the call-site + import updates.
|
`auth_token` as a per-request payload field. This is expected — the
|
||||||
|
develop branch is a total rewrite; there are no external consumers to
|
||||||
|
preserve compatibility for. The migration plan handles the call-site +
|
||||||
|
import updates.
|
||||||
- **A new crate.** `alknet-client` is one more crate in the workspace.
|
- **A new crate.** `alknet-client` is one more crate in the workspace.
|
||||||
The cost is low (the dial is narrow), and the dependency profile
|
The cost is low (the dial is narrow), and the dependency profile
|
||||||
rules out the alternatives, but it is a new entry in the crate
|
rules out the alternatives, but it is a new entry in the crate
|
||||||
@@ -490,11 +518,11 @@ shared client dial crate is structural — every outbound-dialing role
|
|||||||
re-distributing the dial across crates, reintroducing the duplicated
|
re-distributing the dial across crates, reintroducing the duplicated
|
||||||
boilerplate. The three-dial API (`dial_quic` / `dial_tcp_tls` /
|
boilerplate. The three-dial API (`dial_quic` / `dial_tcp_tls` /
|
||||||
`dial_iroh`) is one-way — changing the signatures after consumers exist
|
`dial_iroh`) is one-way — changing the signatures after consumers exist
|
||||||
is a rewrite. The internal implementation (how `CallCredentials` feeds
|
is a rewrite. The internal implementation (how `ConnectionCredentials`
|
||||||
`TlsClientConfig::new`, how the iroh dial maps the `Ed25519SecretKey`)
|
feeds `TlsClientConfig::new`, how the iroh dial maps the
|
||||||
is two-way. The `alknet/register` ALPN name is one-way (wire
|
`Ed25519SecretKey`) is two-way. The `alknet/register` ALPN name is
|
||||||
compatibility); its wire protocol is two-way until the dedicated ADR
|
one-way (wire compatibility); its wire protocol is two-way until the
|
||||||
lands.
|
dedicated ADR lands.
|
||||||
|
|
||||||
## References
|
## References
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,10 @@
|
|||||||
## Status
|
## Status
|
||||||
|
|
||||||
Accepted (amends ADR-089 §3 and §5; amends ADR-087's `TlsClientConfig::new`
|
Accepted (amends ADR-089 §3 and §5; amends ADR-087's `TlsClientConfig::new`
|
||||||
input framing)
|
input framing; amended 2026-07-17 — `CallCredentials` is removed, not
|
||||||
|
retained in `alknet-call`; `from_call`'s `credentials_auth_token` dead
|
||||||
|
path removed; `auth_token` is a per-request payload field, not a
|
||||||
|
call-protocol credential)
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
@@ -173,21 +176,104 @@ Each dial extracts what its transport's identity layer needs:
|
|||||||
`Ed25519SecretKey` → `iroh::SecretKey::from_bytes`;
|
`Ed25519SecretKey` → `iroh::SecretKey::from_bytes`;
|
||||||
`creds.remote_identity.fingerprint` → `NodeId` (verifier).
|
`creds.remote_identity.fingerprint` → `NodeId` (verifier).
|
||||||
|
|
||||||
### `CallCredentials` stays in `alknet-call`
|
### `CallCredentials` is removed (amendment 2026-07-17)
|
||||||
|
|
||||||
`CallCredentials` remains the **call-protocol** credential bundle. Its
|
> **This section supersedes the original "CallCredentials stays in
|
||||||
`auth_token` field stays — the call protocol uses it (the `from_call`
|
> `alknet-call`" decision.** The original rationale rested on a code
|
||||||
forwarding handler populates `auth_token` on outgoing `call.requested`
|
> path that does not exist. The trace below is the correction.
|
||||||
payloads; the hub's `Dispatcher::resolve_identity` resolves it via
|
|
||||||
`IdentityProvider::resolve_from_token`). The call protocol layer
|
|
||||||
assembles `CallCredentials` from `ConnectionCredentials` (the
|
|
||||||
transport-level dimensions) + the call-protocol `auth_token`, or the
|
|
||||||
caller provides the `auth_token` per-request via `call_with_payload`.
|
|
||||||
|
|
||||||
`CallCredentials` does **not** move to `alknet-core`. ADR-089 §5's move
|
`CallCredentials` is **removed**, not retained. Once the transport
|
||||||
of `CallCredentials` to core is superseded by this ADR: only
|
dimensions (`local_identity`, `remote_identity`) move to
|
||||||
`ConnectionCredentials` and `RemoteIdentity` move to core. The call
|
`ConnectionCredentials` in `alknet-core`, `CallCredentials` would
|
||||||
protocol's own credential type stays in the call crate.
|
reduce to a one-field struct `{ auth_token: Option<AuthToken> }` — and
|
||||||
|
that field has **no reader**.
|
||||||
|
|
||||||
|
**The trace (why the original rationale was wrong).** The original
|
||||||
|
section claimed the call protocol uses `CallCredentials.auth_token`
|
||||||
|
because "the `from_call` forwarding handler populates `auth_token` on
|
||||||
|
outgoing `call.requested` payloads." That chain does not connect:
|
||||||
|
|
||||||
|
- `from_call`'s signature is `from_call(connection: &CallConnection,
|
||||||
|
config: FromCallConfig)` — no `CallCredentials` parameter.
|
||||||
|
`FromCallConfig` has no credential field.
|
||||||
|
- The `auth_token` the `from_call` forwarding handlers *can* set on
|
||||||
|
payloads is sourced from `OpSummary.credentials_auth_token`, an
|
||||||
|
`Option<String>` that is **hardcoded to `None` at every construction
|
||||||
|
site** (`from_call.rs:185, 748, 757`). It is not read from
|
||||||
|
`CallCredentials.auth_token`, and it is a different type
|
||||||
|
(`Option<String>` vs `Option<AuthToken>`). The two were never
|
||||||
|
connected, even in intent.
|
||||||
|
- The consuming side — `Dispatcher::resolve_identity`
|
||||||
|
(`dispatch.rs:119`) — reads `payload.get("auth_token").as_str()` from
|
||||||
|
the per-request call payload. It does not read `CallCredentials`.
|
||||||
|
|
||||||
|
**Where `auth_token` actually originates.** It is a per-request payload
|
||||||
|
field, populated by two real paths, neither of which touches
|
||||||
|
`CallCredentials`:
|
||||||
|
|
||||||
|
- **Browsers over WebSocket** — the browser sends `auth_token` directly
|
||||||
|
in the `call.requested` JSON payload (`websocket/mod.rs:202–206`); the
|
||||||
|
WS layer (`upgrade.rs:178–181`) passes `envelope.payload` straight to
|
||||||
|
`dispatch_requested`. The browser is the originator; the WS layer is
|
||||||
|
a transparent passthrough.
|
||||||
|
- **HTTP gateway (bearer)** — `gateway/dispatch.rs` resolves the
|
||||||
|
`Authorization: Bearer` header to an `Identity` at the HTTP boundary
|
||||||
|
(`resolve_bearer`, line 58) and passes the `Identity` into
|
||||||
|
`build_root_context`. The call protocol sees the resolved `Identity`,
|
||||||
|
not the token. `auth_token` does not enter the call payload on this
|
||||||
|
path.
|
||||||
|
|
||||||
|
So `CallCredentials.auth_token` is a write-only field (it has a setter,
|
||||||
|
`with_auth_token`, and zero readers). `connect()` — `CallCredentials`'s
|
||||||
|
only consumer — is removed in Phase 5 of the migration. With `connect`
|
||||||
|
gone, nothing constructs or reads `CallCredentials` except the tests.
|
||||||
|
|
||||||
|
**`auth_token`'s two real use cases (confirming no call-protocol
|
||||||
|
credential bundle is needed):**
|
||||||
|
|
||||||
|
1. **HTTP auth** — the inbound case. The HTTP gateway resolves the
|
||||||
|
bearer token to an `Identity` via `IdentityProvider::resolve_from_token`
|
||||||
|
at the HTTP boundary. The call protocol receives the `Identity`, not
|
||||||
|
the token.
|
||||||
|
2. **Registration** (`alknet/register` native ALPN, `/register` HTTP
|
||||||
|
endpoint) — a client not yet associated with a hub presents a
|
||||||
|
one-time registration token; the hub creates a `PeerEntry` (a new
|
||||||
|
identity based on the fingerprint). Outbound, the vault manages the
|
||||||
|
token on the client side; inbound, the hub's registration handler
|
||||||
|
consumes it. Neither path involves `CallCredentials`.
|
||||||
|
|
||||||
|
A hub does not "forward with its own token" in the way the original
|
||||||
|
rationale assumed. Where the hub authenticates to an outside service
|
||||||
|
(another hub's HTTP interface, an external API), the vault manages that
|
||||||
|
outbound token — it is not a call-protocol credential. The
|
||||||
|
`from_call` `credentials_auth_token` path was a future hatch for a
|
||||||
|
use case that dissolved once `IdentityProvider::resolve_from_token`
|
||||||
|
solved the inbound identity problem: the hub authenticates as itself
|
||||||
|
(its `Identity` is on the connection), and the spoke authorizes the hub
|
||||||
|
as the direct caller. No per-forwarded-call token is needed.
|
||||||
|
|
||||||
|
**`from_call`'s `credentials_auth_token` is removed too.** It is the
|
||||||
|
same family of dead code — an always-`None` field of a different type
|
||||||
|
than `CallCredentials.auth_token`, never connected to anything. The
|
||||||
|
`credentials_auth_token` field on `OpSummary`, the `credentials_auth_token`
|
||||||
|
parameters on `make_forwarding_handler` / `make_streaming_forwarding_handler`,
|
||||||
|
and the `auth_token` parameter on `build_forwarded_payload` are removed.
|
||||||
|
The forwarding handlers stop emitting `auth_token` in payloads (which
|
||||||
|
they never did in practice — the source was always `None`). The two
|
||||||
|
`from_call` tests asserting the `Some` path
|
||||||
|
(`build_forwarded_payload_sets_auth_token_when_provided`,
|
||||||
|
`streaming_forwarding_handler_sets_auth_token_when_provided`) are
|
||||||
|
removed — they test a code path never exercised in production. If a
|
||||||
|
future hub needs its own token on forwarded payloads, that is a fresh,
|
||||||
|
end-to-end-wired feature, not a vestigial path.
|
||||||
|
|
||||||
|
**What does NOT move to `alknet-core`:** `ConnectionCredentials` and
|
||||||
|
`RemoteIdentity` move (the original decision). `CallCredentials` does
|
||||||
|
not move — it is removed. ADR-089 §5's move of `CallCredentials` to
|
||||||
|
core is superseded twice over: first by the original ADR-091 (move
|
||||||
|
`ConnectionCredentials` instead), and now by this amendment (remove
|
||||||
|
`CallCredentials` entirely). There is no call-protocol credential
|
||||||
|
bundle; `auth_token` is a per-request payload field, full stop.
|
||||||
|
|
||||||
### `TlsClientConfig::new` input framing
|
### `TlsClientConfig::new` input framing
|
||||||
|
|
||||||
@@ -229,8 +315,8 @@ the credential dimensions.
|
|||||||
- **The dial is fully decoupled from the call protocol.**
|
- **The dial is fully decoupled from the call protocol.**
|
||||||
`ConnectionCredentials` carries only transport-identity dimensions;
|
`ConnectionCredentials` carries only transport-identity dimensions;
|
||||||
`alknet-client` has no call-protocol coupling in its credential type.
|
`alknet-client` has no call-protocol coupling in its credential type.
|
||||||
The `auth_token` (a call-protocol / hub-layer concept) stays in
|
There is no call-protocol credential bundle — `auth_token` is a
|
||||||
`alknet-call` where it belongs.
|
per-request payload field, not a credential.
|
||||||
- **All three dial signatures are unified.** A caller no longer needs to
|
- **All three dial signatures are unified.** A caller no longer needs to
|
||||||
know that iroh takes a bare key while quinn/tcp take a credential
|
know that iroh takes a bare key while quinn/tcp take a credential
|
||||||
bundle — all take `&ConnectionCredentials`. The `node_id` parameter on
|
bundle — all take `&ConnectionCredentials`. The `node_id` parameter on
|
||||||
@@ -240,74 +326,99 @@ the credential dimensions.
|
|||||||
`auth_token` "travels with the `Connection` into the protocol
|
`auth_token` "travels with the `Connection` into the protocol
|
||||||
take-over, where it is sent as the first call-protocol frame." This
|
take-over, where it is sent as the first call-protocol frame." This
|
||||||
was aspirational — `Connection` carries no `auth_token`, and
|
was aspirational — `Connection` carries no `auth_token`, and
|
||||||
`spawn_dispatch` takes no credentials. With `auth_token` out of the
|
`spawn_dispatch` takes no credentials. With `CallCredentials` removed,
|
||||||
dial's credential bundle entirely, the claim is no longer needed. The
|
the claim is not merely unneeded; the field it described was never
|
||||||
token is a per-request field on `call.requested` payloads, set by the
|
read. `auth_token` is a per-request field on `call.requested` payloads,
|
||||||
caller or the `from_call` forwarding handler.
|
set by browsers (in the WS payload) or resolved by the HTTP gateway at
|
||||||
|
its boundary (bearer → `Identity`).
|
||||||
- **`dial_ssh` fits the same shape when it arrives.** The credential
|
- **`dial_ssh` fits the same shape when it arrives.** The credential
|
||||||
dimensions SSH needs (local key + expected host key) are exactly what
|
dimensions SSH needs (local key + expected host key) are exactly what
|
||||||
`ConnectionCredentials` carries. No future ADR needed for the SSH dial
|
`ConnectionCredentials` carries. No future ADR needed for the SSH dial
|
||||||
signature.
|
signature.
|
||||||
- **`CallCredentials` stays in `alknet-call` — its home.** The call
|
- **A dead credential type and a dead forwarding-token path are removed
|
||||||
protocol's own credential type is not dragged into core for the dial's
|
(amendment 2026-07-17).** `CallCredentials` is removed (its
|
||||||
benefit. Core gets `ConnectionCredentials` (transport-level); the call
|
`auth_token` field had no reader). `from_call`'s
|
||||||
crate keeps `CallCredentials` (protocol-level).
|
`credentials_auth_token` is removed (always `None`, different type
|
||||||
|
than `CallCredentials.auth_token`, never connected). Both were future
|
||||||
|
hatches from the era before `IdentityProvider::resolve_from_token`
|
||||||
|
solved the inbound identity problem; the hatches dissolved once it
|
||||||
|
did. See the amended §"`CallCredentials` is removed" above for the
|
||||||
|
trace.
|
||||||
|
|
||||||
**Negative:**
|
**Negative:**
|
||||||
|
|
||||||
- **`CallCredentials` loses two fields.** `tls_identity` and
|
- **`CallCredentials` is removed (a public type).** Callers that
|
||||||
`remote_identity` move to `ConnectionCredentials` in core;
|
constructed `CallCredentials` (the integration test; any future
|
||||||
`CallCredentials` becomes `{auth_token: Option<AuthToken>}` (or is
|
assembly-layer code) switch to `ConnectionCredentials` for the dial.
|
||||||
restructured — the call protocol may assemble it from
|
`auth_token`, where needed, is a per-request payload field (browsers
|
||||||
`ConnectionCredentials` + `auth_token` at the take-over site, or the
|
send it in the WS payload; the HTTP gateway resolves bearer →
|
||||||
caller provides `auth_token` per-request). The exact restructure is a
|
`Identity` at its boundary). This is expected — `connect()` was
|
||||||
two-way-door implementation detail; the one-way decision is that the
|
`CallCredentials`'s only consumer and is removed in the same
|
||||||
transport dimensions leave `CallCredentials`.
|
migration. There are no external consumers (develop branch is a total
|
||||||
- **The assembly layer assembles two credential bundles, not one.** Where
|
rewrite).
|
||||||
ADR-089 had the assembly layer build one `CallCredentials`, it now
|
- **The assembly layer builds one credential bundle, not two.** Where
|
||||||
builds `ConnectionCredentials` (for the dial) and, separately,
|
ADR-089 had the assembly layer build one `CallCredentials` (and the
|
||||||
provides the `auth_token` to the call-protocol layer (for the
|
original ADR-091 reframed it as two — `ConnectionCredentials` for the
|
||||||
per-request payload). This is the correct layering — the dial and the
|
dial + a per-request `auth_token`), the assembly layer now builds
|
||||||
protocol consume different dimensions — but it is one more type at the
|
`ConnectionCredentials` for the dial only. `auth_token` is not a
|
||||||
assembly site.
|
credential the assembly layer constructs; it is a per-request payload
|
||||||
- **ADR-089 §5's "CallCredentials moves to core" is superseded.** The
|
field the browser (or the HTTP gateway's bearer resolution) supplies.
|
||||||
move target changes from `CallCredentials` to `ConnectionCredentials`
|
This is fewer types at the assembly site, not more.
|
||||||
+ `RemoteIdentity`. `CallCredentials` stays in `alknet-call`. This
|
- **ADR-089 §5's "CallCredentials moves to core" is superseded twice.**
|
||||||
affects the extraction plan's Phase 0 (the additive credentials move).
|
The original ADR-091 reframed the move target as `ConnectionCredentials`
|
||||||
|
(not `CallCredentials`); this amendment removes `CallCredentials`
|
||||||
|
entirely. What moves to `alknet-core`: `ConnectionCredentials` +
|
||||||
|
`RemoteIdentity`. What does not move: `CallCredentials` (removed, not
|
||||||
|
relocated). This affects the extraction plan's Phase 0 (additive
|
||||||
|
credentials move) and Phase 5 (the call prune now removes
|
||||||
|
`CallCredentials` and the `from_call` dead path, not just `connect`
|
||||||
|
and the TLS helpers).
|
||||||
|
|
||||||
## Door type
|
## Door type
|
||||||
|
|
||||||
**One-way.** The dial signatures (`dial_quic` / `dial_tcp_tls` /
|
**One-way.** The dial signatures (`dial_quic` / `dial_tcp_tls` /
|
||||||
`dial_iroh` all taking `&ConnectionCredentials`) are the public API
|
`dial_iroh` all taking `&ConnectionCredentials`) are the public API
|
||||||
surface of `alknet-client`. The credential-type decoupling
|
surface of `alknet-client`. The credential-type decoupling
|
||||||
(`ConnectionCredentials` in core, `CallCredentials` in call) determines
|
(`ConnectionCredentials` in core, no call-protocol credential bundle)
|
||||||
the dep graph (`alknet-client` depends on `alknet-core` for
|
determines the dep graph (`alknet-client` depends on `alknet-core` for
|
||||||
`ConnectionCredentials`, not on `alknet-call` for `CallCredentials`).
|
`ConnectionCredentials`, not on `alknet-call`). Reversing would mean
|
||||||
Reversing would mean re-coupling the dial to the call protocol's
|
re-coupling the dial to the call protocol's credential type and
|
||||||
credential type and re-asymmetrizing the iroh dial. The crate is
|
re-asymmetrizing the iroh dial. The `CallCredentials` removal
|
||||||
greenfield (Phase 3 of the extraction plan), so the door is still open
|
(amendment 2026-07-17) is the same door — removing a public type whose
|
||||||
now — this ADR records the decision before implementation.
|
only consumer (`connect`) is removed in the same migration. The crate
|
||||||
|
is greenfield (Phase 3 of the extraction plan), so the door is still
|
||||||
|
open now — this ADR records the decisions before implementation.
|
||||||
|
|
||||||
## References
|
## References
|
||||||
|
|
||||||
- ADR-089 — `AlknetClient` native dial seam (§3 dial signatures amended
|
- ADR-089 — `AlknetClient` native dial seam (§3 dial signatures amended
|
||||||
— all take `&ConnectionCredentials`; §5 move amended —
|
— all take `&ConnectionCredentials`; §5 move amended —
|
||||||
`ConnectionCredentials`/`RemoteIdentity` move to core, not
|
`ConnectionCredentials`/`RemoteIdentity` move to core, not
|
||||||
`CallCredentials`)
|
`CallCredentials`; §5 further amended 2026-07-17 — `CallCredentials`
|
||||||
|
removed, not retained in `alknet-call`)
|
||||||
- ADR-087 — `TlsClientConfig` not blocked on dial (input framing
|
- ADR-087 — `TlsClientConfig` not blocked on dial (input framing
|
||||||
amended — `ClientVerifierContext` derived from
|
amended — `ClientVerifierContext` derived from
|
||||||
`ConnectionCredentials.remote_identity`, not `CallCredentials`)
|
`ConnectionCredentials.remote_identity`, not `CallCredentials`)
|
||||||
- ADR-034 — client-side verifier selection (the rule
|
- ADR-034 — client-side verifier selection (the rule
|
||||||
`ConnectionCredentials.remote_identity` drives — unchanged)
|
`ConnectionCredentials.remote_identity` drives — unchanged)
|
||||||
- ADR-017 §7 — the three credential dimensions (the source of
|
- ADR-017 §7 — the three credential dimensions (the historical source of
|
||||||
`CallCredentials`'s three fields; this ADR splits the transport
|
`CallCredentials`'s three fields; the transport dimensions moved to
|
||||||
dimensions from the protocol dimension)
|
`ConnectionCredentials`, the `auth_token` dimension is a per-request
|
||||||
|
payload field, and `CallCredentials` itself is removed)
|
||||||
- `crates/alknet-call/src/protocol/dispatch.rs` —
|
- `crates/alknet-call/src/protocol/dispatch.rs` —
|
||||||
`Dispatcher::resolve_identity` reads `payload.get("auth_token")`
|
`Dispatcher::resolve_identity` reads `payload.get("auth_token")`
|
||||||
(per-request, not connection-level)
|
(per-request, not connection-level — the consumer of `auth_token`)
|
||||||
- `crates/alknet-call/src/client/from_call.rs` —
|
- `crates/alknet-call/src/client/from_call.rs` — the
|
||||||
`build_forwarded_payload` sets `auth_token` on outgoing payloads
|
`credentials_auth_token` field on `OpSummary` and the
|
||||||
(per-request, the hub's own token)
|
`auth_token` parameter on `build_forwarded_payload` (the removed dead
|
||||||
|
path; always `None`, different type than `CallCredentials.auth_token`,
|
||||||
|
never connected)
|
||||||
|
- `crates/alknet-http/src/gateway/dispatch.rs` — `resolve_bearer` (the
|
||||||
|
HTTP path: bearer → `Identity` at the boundary; the call layer sees
|
||||||
|
the identity, not the token)
|
||||||
|
- `crates/alknet-http/src/websocket/mod.rs` — the WS path:
|
||||||
|
`auth_token` in the browser's call payload, passed through to
|
||||||
|
`dispatch_requested` unchanged
|
||||||
- `docs/research/references/ssh/russh/06-usage-patterns.md` — the SSH
|
- `docs/research/references/ssh/russh/06-usage-patterns.md` — the SSH
|
||||||
client usage patterns (check_server_key + authenticate_publickey)
|
client usage patterns (check_server_key + authenticate_publickey)
|
||||||
validating the `ConnectionCredentials` shape for a future `dial_ssh`
|
validating the `ConnectionCredentials` shape for a future `dial_ssh`
|
||||||
@@ -96,10 +96,11 @@ alknet-vault (standalone — foundational to ACL: key derivation, identity)
|
|||||||
├── Substrate
|
├── Substrate
|
||||||
│ alknet-core ProtocolHandler, Connection, BidiStreamSource, AuthContext,
|
│ alknet-core ProtocolHandler, Connection, BidiStreamSource, AuthContext,
|
||||||
│ │ IdentityProvider, StaticConfig, DynamicConfig, fingerprint,
|
│ │ IdentityProvider, StaticConfig, DynamicConfig, fingerprint,
|
||||||
│ │ CallCredentials, RemoteIdentity
|
│ │ ConnectionCredentials, RemoteIdentity
|
||||||
│ │ (endpoint extracted to alknet-endpoint; core is now lightweight
|
│ │ (endpoint extracted to alknet-endpoint; core is now lightweight
|
||||||
│ │ types+auth+config — no quinn/iroh/rcgen deps; CallCredentials
|
│ │ types+auth+config — no quinn/iroh/rcgen deps; ConnectionCredentials
|
||||||
│ │ moved here from alknet-call per ADR-089 §5)
|
│ │ + RemoteIdentity moved here from alknet-call per ADR-091;
|
||||||
|
│ │ CallCredentials removed per ADR-091 Am. 2026-07-17)
|
||||||
│ ├── alknet-tls TlsServerConfig + TlsClientConfig + FingerprintPinVerifier — shared TLS config across quinn + TCP+TLS + iroh (ADR-082/087; FingerprintPinVerifier moved from alknet-call per ADR-089 §5)
|
│ ├── alknet-tls TlsServerConfig + TlsClientConfig + FingerprintPinVerifier — shared TLS config across quinn + TCP+TLS + iroh (ADR-082/087; FingerprintPinVerifier moved from alknet-call 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 only — connect removed per ADR-089 §5), OperationRegistry, adapters (no TLS/transport deps)
|
||||||
│ ├── alknet-channels
|
│ ├── alknet-channels
|
||||||
|
|||||||
@@ -61,7 +61,7 @@
|
|||||||
4. **Session credential return** — what does the hub return on
|
4. **Session credential return** — what does the hub return on
|
||||||
successful registration? A `PeerEntry`? A session token? Both?
|
successful registration? A `PeerEntry`? A session token? Both?
|
||||||
How does the returned credential feed into the subsequent
|
How does the returned credential feed into the subsequent
|
||||||
`alknet/channels` connection's `CallCredentials`?
|
`alknet/channels` connection's `ConnectionCredentials`?
|
||||||
5. **Relationship to OQ-58** — the HTTP registration endpoint
|
5. **Relationship to OQ-58** — the HTTP registration endpoint
|
||||||
(OQ-58) and `alknet/register` share the enrollment semantics
|
(OQ-58) and `alknet/register` share the enrollment semantics
|
||||||
(create `PeerEntry`, return credential) but differ in transport
|
(create `PeerEntry`, return credential) but differ in transport
|
||||||
|
|||||||
@@ -47,16 +47,24 @@ but the extraction unwinds that.
|
|||||||
|
|
||||||
| Lines | Concern | Destination | LOC |
|
| Lines | Concern | Destination | LOC |
|
||||||
|-------|---------|-------------|-----|
|
|-------|---------|-------------|-----|
|
||||||
| 40-88 | `RemoteIdentity`, `CallCredentials` (struct + builder) | split: `RemoteIdentity` + new `ConnectionCredentials` → `alknet-core` (`credentials.rs`); `CallCredentials` restructured (transport dimensions leave, `auth_token` stays) → stays in `alknet-call` (ADR-091) | ~48 |
|
| 40-88 | `RemoteIdentity`, `CallCredentials` (struct + builder) | split: `RemoteIdentity` + new `ConnectionCredentials` → `alknet-core` (`credentials.rs`); `CallCredentials` **removed** (its `auth_token` field had no reader — see Phase 5) | ~48 |
|
||||||
| 90-100 | `ClientError` enum | **removed** (only produced by `connect`) | ~10 |
|
| 90-100 | `ClientError` enum | **removed** (only produced by `connect`) | ~10 |
|
||||||
| 102-187 | `CallClient` struct + `new` + `spawn_dispatch` | **stays** (the pure protocol take-over) | ~85 |
|
| 102-187 | `CallClient` struct + `new` + `spawn_dispatch` | **stays** (the pure protocol take-over) | ~85 |
|
||||||
| 189-320 | `build_quinn_client_config`, `build_client_auth`, `select_server_verifier`, `load_platform_root_cert_store`, `load_cert_chain`, `load_private_key`, `Ed25519SigningKey`, `RawKeyClientCertResolver`, `NoClientCertResolver`, `FingerprintPinVerifier` | `alknet-tls` | ~130 |
|
| 189-320 | `build_quinn_client_config`, `build_client_auth`, `select_server_verifier`, `load_platform_root_cert_store`, `load_cert_chain`, `load_private_key`, `Ed25519SigningKey`, `RawKeyClientCertResolver`, `NoClientCertResolver`, `FingerprintPinVerifier` | `alknet-tls` | ~130 |
|
||||||
| 321-640 | `CallConnection`, `Dispatcher` wiring, wire-protocol helpers | **stays** (protocol) | ~320 |
|
| 321-640 | `CallConnection`, `Dispatcher` wiring, wire-protocol helpers | **stays** (protocol) | ~320 |
|
||||||
| 640-930 | Tests (use `connect`, `CallCredentials`, TLS helpers) | rewrite to use `spawn_dispatch` directly or `AlknetClient` | ~290 |
|
| 640-930 | Tests (16 total — see Phase 5 audit) | split: 10 TLS/verifier tests → `alknet-tls` (Phase 1); 4 protocol-level tests stay in `alknet-call` unchanged; 2 `CallCredentials`-field tests → `alknet-core` (testing `ConnectionCredentials`); 0 lib tests call `connect()` | ~290 |
|
||||||
|
|
||||||
The call crate's prune is ~140 lines of implementation + ~290 lines of
|
The call crate's prune is ~140 lines of implementation + `CallCredentials`
|
||||||
tests that need rewriting. What remains is the pure protocol: `CallClient`
|
removal + `from_call`'s `credentials_auth_token` dead-path removal. The
|
||||||
+ `CallConnection` + `Dispatcher` + the wire protocol.
|
test work: 10 tests move to `alknet-tls` (Phase 1), 2 `CallCredentials`
|
||||||
|
tests move to `alknet-core` (testing `ConnectionCredentials`), 4
|
||||||
|
protocol-level tests stay unchanged. The integration test
|
||||||
|
(`two_node_call.rs`, 2 tests) splits: the dial+takeover composition test
|
||||||
|
moves to `alknet-client/tests/` (Phase 3, rewritten with a minimal echo
|
||||||
|
`ProtocolHandler`); the `from_call` test stays in `alknet-call` (Phase 5,
|
||||||
|
rewritten to use `spawn_dispatch` + loopback `Connection`). What remains
|
||||||
|
is the pure protocol: `CallClient` + `CallConnection` + `Dispatcher` +
|
||||||
|
the wire protocol.
|
||||||
|
|
||||||
### `crates/alknet-http/src/server/adapter.rs` — the residual
|
### `crates/alknet-http/src/server/adapter.rs` — the residual
|
||||||
|
|
||||||
@@ -93,14 +101,16 @@ already removed per ADR-065; tests use `from_stream` with
|
|||||||
`crates/alknet-core/src/credentials.rs` (~40 lines).
|
`crates/alknet-core/src/credentials.rs` (~40 lines).
|
||||||
`ConnectionCredentials` is the transport-level credential bundle
|
`ConnectionCredentials` is the transport-level credential bundle
|
||||||
(ADR-091) — it carries `local_identity` + `remote_identity` (the two
|
(ADR-091) — it carries `local_identity` + `remote_identity` (the two
|
||||||
dimensions the dial consumes). `CallCredentials` stays in
|
dimensions the dial consumes). `CallCredentials` is **removed** (its
|
||||||
`alknet-call` (it is the call-protocol bundle; its `auth_token` field
|
`auth_token` field had no reader — `connect()` read only
|
||||||
is a per-request call-protocol concept, not a transport credential).
|
`tls_identity` + `remote_identity`; `spawn_dispatch` takes no
|
||||||
Update `alknet-core/src/lib.rs` to `pub mod credentials` + re-export.
|
credentials; the `from_call` forwarding path's `auth_token` source was
|
||||||
Update `alknet-call` to import `ConnectionCredentials` + `RemoteIdentity`
|
a different, always-`None` field never connected to `CallCredentials`).
|
||||||
from core and re-export them; `CallCredentials` retains `auth_token` and
|
`auth_token` is a per-request payload field, not a call-protocol
|
||||||
references the core types for the transport dimensions. No other
|
credential. Update `alknet-core/src/lib.rs` to `pub mod credentials` +
|
||||||
changes.
|
re-export. Update `alknet-call` to import `ConnectionCredentials` +
|
||||||
|
`RemoteIdentity` from core and re-export them; remove `CallCredentials`
|
||||||
|
and its builder methods. No other changes.
|
||||||
|
|
||||||
**Why first:** It's independent of the three new crates, purely
|
**Why first:** It's independent of the three new crates, purely
|
||||||
additive (core gains types, nothing breaks), and means `alknet-client`
|
additive (core gains types, nothing breaks), and means `alknet-client`
|
||||||
@@ -115,7 +125,7 @@ continue to work via the re-export.
|
|||||||
|
|
||||||
**Done when:** `cargo test` passes, `ConnectionCredentials` +
|
**Done when:** `cargo test` passes, `ConnectionCredentials` +
|
||||||
`RemoteIdentity` are defined in `alknet-core`, `alknet-call` imports
|
`RemoteIdentity` are defined in `alknet-core`, `alknet-call` imports
|
||||||
them from core, `CallCredentials` stays in `alknet-call`.
|
them from core, `CallCredentials` is removed from `alknet-call`.
|
||||||
|
|
||||||
### Phase 1: Create `alknet-tls` (greenfield, additive)
|
### Phase 1: Create `alknet-tls` (greenfield, additive)
|
||||||
|
|
||||||
@@ -299,16 +309,28 @@ lightweight (~3200 LOC, no heavy transport deps).
|
|||||||
|
|
||||||
### Phase 5: Prune `alknet-call` (subtractive, breakage confined)
|
### Phase 5: Prune `alknet-call` (subtractive, breakage confined)
|
||||||
|
|
||||||
**What:** Delete `connect()` + all TLS helpers + `ClientError` from
|
**What:** Delete `connect()` + all TLS helpers + `ClientError` +
|
||||||
`call_client.rs`. The transport dimensions (`ConnectionCredentials`/
|
`CallCredentials` from `call_client.rs`. The transport dimensions
|
||||||
`RemoteIdentity`) already moved to core in Phase 0; here we remove the
|
(`ConnectionCredentials`/`RemoteIdentity`) already moved to core in
|
||||||
old definitions from `call_client.rs` and update imports.
|
Phase 0; here we remove the old definitions from `call_client.rs` and
|
||||||
`CallCredentials` stays in `alknet-call` (retaining `auth_token`,
|
update imports. `CallCredentials` is **removed** (its `auth_token`
|
||||||
referencing the core types — ADR-091). Update `Cargo.toml` to drop
|
field had no reader — `connect()` read only `tls_identity` +
|
||||||
`quinn`/`rustls`/`rustls-native-certs`/`rustls-pemfile`. Rewrite the
|
`remote_identity`; `spawn_dispatch` takes no credentials; the
|
||||||
tests that used `connect` to use `spawn_dispatch` directly (with
|
`from_call` forwarding path's `auth_token` source was
|
||||||
`Connection::from_stream` mocks) or `AlknetClient::dial_quic` +
|
`OpSummary.credentials_auth_token: Option<String>`, always `None`,
|
||||||
`spawn_dispatch`.
|
never connected to `CallCredentials.auth_token`). `auth_token` is a
|
||||||
|
per-request payload field, not a call-protocol credential. Update
|
||||||
|
`Cargo.toml` to drop `quinn`/`rustls`/`rustls-native-certs`/
|
||||||
|
`rustls-pemfile`. Also remove `from_call`'s `credentials_auth_token`
|
||||||
|
dead path: the `credentials_auth_token` field on `OpSummary`, the
|
||||||
|
`credentials_auth_token` parameters on `make_forwarding_handler` /
|
||||||
|
`make_streaming_forwarding_handler`, and the `auth_token` parameter on
|
||||||
|
`build_forwarded_payload` are all removed (always `None`, different
|
||||||
|
type than `CallCredentials.auth_token`, never connected). The two
|
||||||
|
`from_call` tests asserting the `Some` path
|
||||||
|
(`build_forwarded_payload_sets_auth_token_when_provided`,
|
||||||
|
`streaming_forwarding_handler_sets_auth_token_when_provided`) are
|
||||||
|
removed — they test a code path never exercised in production.
|
||||||
|
|
||||||
**The `call_client.rs` after prune:**
|
**The `call_client.rs` after prune:**
|
||||||
- `CallClient` struct + `new` + `registry` + `identity_provider` +
|
- `CallClient` struct + `new` + `registry` + `identity_provider` +
|
||||||
@@ -316,13 +338,10 @@ tests that used `connect` to use `spawn_dispatch` directly (with
|
|||||||
- `CallConnection` + `Dispatcher` wiring (stays — protocol)
|
- `CallConnection` + `Dispatcher` wiring (stays — protocol)
|
||||||
- `RemoteIdentity` — removed from `call_client.rs` (moved to
|
- `RemoteIdentity` — removed from `call_client.rs` (moved to
|
||||||
`alknet-core` in Phase 0; re-imported from there)
|
`alknet-core` in Phase 0; re-imported from there)
|
||||||
- `CallCredentials` — restructured, stays in `alknet-call`: the
|
- `CallCredentials` — **removed** (its `auth_token` field had no
|
||||||
transport dimensions (`tls_identity`, `remote_identity`) leave for
|
reader; `connect()` was its only consumer and is removed in this
|
||||||
`ConnectionCredentials` in `alknet-core` (Phase 0); the `auth_token`
|
phase; `auth_token` is a per-request payload field, not a credential
|
||||||
field stays (it is a call-protocol concept, not a transport credential
|
— ADR-091, amended 2026-07-17)
|
||||||
— ADR-091). `CallCredentials` references the core types for the
|
|
||||||
transport dimensions or assembles from `ConnectionCredentials` +
|
|
||||||
`auth_token` at the take-over site.
|
|
||||||
- `ClientError` — removed
|
- `ClientError` — removed
|
||||||
- `connect` + all `build_*`/`select_*`/`load_*`/`Ed25519SigningKey`/
|
- `connect` + all `build_*`/`select_*`/`load_*`/`Ed25519SigningKey`/
|
||||||
`RawKeyClientCertResolver`/`NoClientCertResolver`/
|
`RawKeyClientCertResolver`/`NoClientCertResolver`/
|
||||||
@@ -337,23 +356,37 @@ any stray `#[cfg(feature = "quinn")]` the prune missed). `alknet-call`
|
|||||||
becomes a pure protocol crate.
|
becomes a pure protocol crate.
|
||||||
|
|
||||||
**Test impact (per the test audit below):** the lib tests in
|
**Test impact (per the test audit below):** the lib tests in
|
||||||
`call_client.rs` (16 tests) split into 6 that stay unchanged
|
`call_client.rs` (16 tests) split into 4 that stay unchanged
|
||||||
(protocol-level, use `spawn_dispatch(stub_connection())`) and 10 that
|
(protocol-level, use `spawn_dispatch(stub_connection())`), 10 that
|
||||||
move to `alknet-tls` in Phase 1 (TLS/verifier tests). **Zero lib tests
|
move to `alknet-tls` in Phase 1 (TLS/verifier tests), and 2 that move
|
||||||
need rewriting** — no test in `call_client.rs` calls `connect()`. The
|
to `alknet-core` (testing `ConnectionCredentials` — the
|
||||||
`from_call.rs` tests (27 tests) stay unchanged — they use
|
`call_credentials_builder_methods` and
|
||||||
`CallConnection` directly. The one integration test file
|
`remote_identity_none_is_load_bearing_not_defaulted` tests, which
|
||||||
(`tests/two_node_call.rs`, 2 tests) calls `connect()` twice — it moves
|
access `remote_identity`/`tls_identity` fields that moved to
|
||||||
to `alknet-client/tests/` (Phase 3) or is updated to use
|
`ConnectionCredentials`). The `from_call.rs` tests: 25 stay unchanged
|
||||||
`AlknetClient::dial_quic` + `spawn_dispatch` (Phase 5).
|
(protocol-level, use `CallConnection` directly), 2 are removed (the
|
||||||
|
`credentials_auth_token` `Some`-path tests — see above). The
|
||||||
|
integration test file (`tests/two_node_call.rs`, 2 tests) splits:
|
||||||
|
`two_node_call_round_trip` (dial + take-over composition) moves to
|
||||||
|
`alknet-client/tests/` (Phase 3, rewritten with a minimal echo
|
||||||
|
`ProtocolHandler` on a test ALPN — no `alknet-call` dependency);
|
||||||
|
`from_call_discovers_and_forwards_over_quic_loopback` (call-protocol-
|
||||||
|
specific, uses `from_call`) stays in `alknet-call` (Phase 5, rewritten
|
||||||
|
to use `spawn_dispatch` + loopback `Connection` — **not**
|
||||||
|
`AlknetClient::dial_quic`, which would re-create the dep the prune
|
||||||
|
removes).
|
||||||
|
|
||||||
The implementation prune is mechanical: delete the error enum, delete
|
The implementation prune: delete `ClientError`, delete `connect`,
|
||||||
`connect`, delete the TLS helpers (~140 lines). The test work is: the
|
delete `CallCredentials` and its builder methods, delete the TLS
|
||||||
10 TLS tests already moved in Phase 1, the 6 protocol tests stay, the
|
helpers (~140 lines), delete `from_call`'s `credentials_auth_token`
|
||||||
integration test is handled separately.
|
dead path (~30 lines). The test work: 10 TLS tests already moved in
|
||||||
|
Phase 1, 2 `CallCredentials` tests move to `alknet-core`, 4 protocol
|
||||||
|
tests stay, 2 `from_call` dead-path tests removed, the integration
|
||||||
|
test splits as above.
|
||||||
|
|
||||||
**Compilable state:** `cargo test -p alknet-call` passes with the
|
**Compilable state:** `cargo test -p alknet-call` passes with the
|
||||||
rewritten tests. The crate has no TLS/transport deps.
|
rewritten tests. The crate has no TLS/transport deps, no
|
||||||
|
`CallCredentials`, no `from_call` dead path.
|
||||||
|
|
||||||
**Done when:** `cargo test -p alknet-call` passes, the crate is a pure
|
**Done when:** `cargo test -p alknet-call` passes, the crate is a pure
|
||||||
protocol crate.
|
protocol crate.
|
||||||
@@ -396,20 +429,21 @@ wrapper is gone.
|
|||||||
|
|
||||||
| After phase | State |
|
| After phase | State |
|
||||||
|-------------|-------|
|
|-------------|-------|
|
||||||
| 0 (credentials) | `ConnectionCredentials`/`RemoteIdentity` in core; call imports from core; `CallCredentials` stays in call; no breakage |
|
| 0 (credentials) | `ConnectionCredentials`/`RemoteIdentity` in core; call imports from core; `CallCredentials` removed; no breakage |
|
||||||
| 1 (tls) | `alknet-tls` builds standalone; core/call/http unchanged (old code duplicated) |
|
| 1 (tls) | `alknet-tls` builds standalone; core/call/http unchanged (old code duplicated) |
|
||||||
| 2 (endpoint) | `alknet-endpoint` builds standalone; core still has old `endpoint.rs` (duplicate) |
|
| 2 (endpoint) | `alknet-endpoint` builds standalone; core still has old `endpoint.rs` (duplicate) |
|
||||||
| 3 (client) | `alknet-client` builds standalone; call still has old `connect` (duplicate) |
|
| 3 (client) | `alknet-client` builds standalone; call still has old `connect` (duplicate) |
|
||||||
| 4 (core prune) | core is lightweight; `endpoint.rs` gone; `ConnectionCredentials` in core |
|
| 4 (core prune) | core is lightweight; `endpoint.rs` gone; `ConnectionCredentials` in core |
|
||||||
| 5 (call prune) | call is pure protocol; `connect` + TLS helpers gone; Category B tests already moved |
|
| 5 (call prune) | call is pure protocol; `connect` + TLS helpers + `CallCredentials` + `from_call` dead path gone; Category B tests already moved; 2 `CallCredentials` tests moved to core |
|
||||||
| 6 (http fix) | http has no `QuicStream` wrapper; clean `accept_bi` path |
|
| 6 (http fix) | http has no `QuicStream` wrapper; clean `accept_bi` path |
|
||||||
|
|
||||||
Phases 0-3 are purely additive — no existing code breaks, no tests
|
Phases 0-3 are purely additive — no existing code breaks, no tests
|
||||||
break. Phases 4-5 are subtractive — the pruned code's callers don't
|
break. Phases 4-5 are subtractive — the pruned code's callers don't
|
||||||
exist yet (no assembly layer), so the breakage is confined to the
|
exist yet (no assembly layer), so the breakage is confined to the
|
||||||
crate's own tests (and per the test audit, the call prune breaks zero
|
crate's own tests (and per the test audit, the call prune removes
|
||||||
tests — the TLS tests moved in Phase 1, the protocol tests use
|
`CallCredentials` and the `from_call` dead path; the TLS tests moved in
|
||||||
`spawn_dispatch` directly). Phase 6 is a small fix.
|
Phase 1, the `CallCredentials` tests moved to core, the protocol tests
|
||||||
|
use `spawn_dispatch` directly). Phase 6 is a small fix.
|
||||||
|
|
||||||
## Ordering rationale
|
## Ordering rationale
|
||||||
|
|
||||||
@@ -454,43 +488,58 @@ to clean up later.
|
|||||||
`ConnectionCredentials` (not `CallCredentials`) is what moves — it is
|
`ConnectionCredentials` (not `CallCredentials`) is what moves — it is
|
||||||
the transport-level credential bundle (`local_identity` +
|
the transport-level credential bundle (`local_identity` +
|
||||||
`remote_identity`), carrying only the dimensions the dial consumes.
|
`remote_identity`), carrying only the dimensions the dial consumes.
|
||||||
`CallCredentials` stays in `alknet-call` because its `auth_token` field
|
`CallCredentials` is **removed** (its `auth_token` field had no reader
|
||||||
is a call-protocol / hub-layer concept (bearer-token identity
|
— `connect()` read only `tls_identity` + `remote_identity`;
|
||||||
correlation for browsers and `alknet/register`), not a transport
|
`spawn_dispatch` takes no credentials; the `from_call` forwarding
|
||||||
credential. See ADR-091 for the full rationale.
|
path's `auth_token` source was `OpSummary.credentials_auth_token:
|
||||||
|
Option<String>`, always `None`, never connected to
|
||||||
|
`CallCredentials.auth_token`). `auth_token` is a per-request payload
|
||||||
|
field, not a call-protocol credential. See ADR-091 (amended
|
||||||
|
2026-07-17) for the full rationale and trace.
|
||||||
|
|
||||||
The move is ~40 lines (struct definitions + builder impls) into a new
|
The move is ~40 lines (struct definitions + builder impls) into a new
|
||||||
`crates/alknet-core/src/credentials.rs` (or `auth.rs` — `auth.rs`
|
`crates/alknet-core/src/credentials.rs`. `alknet-call`'s
|
||||||
already holds `AuthToken`, so `credentials.rs` is cleaner to keep the
|
`client/mod.rs` imports `ConnectionCredentials` + `RemoteIdentity`
|
||||||
auth module from growing). `alknet-call`'s `client/mod.rs` imports
|
from core and re-exports them; `CallCredentials` is removed from
|
||||||
`ConnectionCredentials` + `RemoteIdentity` from core and re-exports
|
`alknet-call`. Test changes: the two `CallCredentials`-field tests
|
||||||
them; `CallCredentials` stays defined in `alknet-call` (retaining
|
(`call_credentials_builder_methods`,
|
||||||
`auth_token`, referencing the core types for the transport dimensions).
|
`remote_identity_none_is_load_bearing_not_defaulted`) move to
|
||||||
Test changes are minimal — the Category A tests that reference
|
`alknet-core` testing `ConnectionCredentials`; `call_client_is_send_sync`
|
||||||
`CallCredentials` directly may need import updates depending on how
|
drops the `CallCredentials`/`RemoteIdentity` assertions (or they move
|
||||||
`CallCredentials` is restructured.
|
with the types).
|
||||||
|
|
||||||
### Phase 5 test audit — `call_client.rs` (16 tests)
|
### Phase 5 test audit — `call_client.rs` (16 tests)
|
||||||
|
|
||||||
The 16 tests in `call_client.rs` split into three categories:
|
The 16 tests in `call_client.rs` split into three categories:
|
||||||
|
|
||||||
**Category A — protocol-level, stay in `alknet-call`, no rewrite
|
**Category A — protocol-level, stay in `alknet-call`, no rewrite
|
||||||
needed (6 tests):**
|
needed (4 tests):**
|
||||||
|
|
||||||
These tests use `spawn_dispatch(stub_connection())` or
|
These tests use `spawn_dispatch(stub_connection())` and don't touch
|
||||||
`CallCredentials` directly. They don't touch `connect` or any TLS
|
`connect`, `CallCredentials` fields, or any TLS helper.
|
||||||
helper. `stub_connection()` (line 582) uses
|
`stub_connection()` (line 582) uses
|
||||||
`Connection::from_stream(tokio::io::channel(...))` — already
|
`Connection::from_stream(tokio::io::channel(...))` — already
|
||||||
transport-agnostic. These survive the prune unchanged.
|
transport-agnostic. These survive the prune unchanged.
|
||||||
|
|
||||||
| Test | Line | What it tests |
|
| Test | Line | What it tests |
|
||||||
|------|------|---------------|
|
|------|------|---------------|
|
||||||
| `call_credentials_builder_methods` | 652 | `CallCredentials` builder |
|
|
||||||
| `external_op_dispatches_and_populates_capabilities` | 665 | dispatch + capabilities |
|
| `external_op_dispatches_and_populates_capabilities` | 665 | dispatch + capabilities |
|
||||||
| `unknown_op_returns_not_found` | 679 | dispatch error path |
|
| `unknown_op_returns_not_found` | 679 | dispatch error path |
|
||||||
| `spawn_dispatch_returns_live_call_connection` | 691 | `spawn_dispatch` + ALPN |
|
| `spawn_dispatch_returns_live_call_connection` | 691 | `spawn_dispatch` + ALPN |
|
||||||
| `call_client_is_send_sync` | 705 | trait bounds |
|
| `call_client_is_send_sync` | 705 | trait bounds (import update: `RemoteIdentity` moved to core) |
|
||||||
| `remote_identity_none_is_load_bearing_not_defaulted` | 921 | `CallCredentials::new()` |
|
|
||||||
|
**Category A2 — `CallCredentials`-field tests, move to `alknet-core`
|
||||||
|
(2 tests):**
|
||||||
|
|
||||||
|
These test `CallCredentials` fields (`remote_identity`, `tls_identity`)
|
||||||
|
that moved to `ConnectionCredentials` in `alknet-core` (ADR-091). They
|
||||||
|
move to `alknet-core` testing `ConnectionCredentials::new()` +
|
||||||
|
`with_remote_identity()`.
|
||||||
|
|
||||||
|
| Test | Line | What it tests | Move target |
|
||||||
|
|------|------|---------------|-------------|
|
||||||
|
| `call_credentials_builder_methods` | 652 | `CallCredentials` builder (now `ConnectionCredentials`) | `alknet-core` |
|
||||||
|
| `remote_identity_none_is_load_bearing_not_defaulted` | 921 | `CallCredentials::new()` (now `ConnectionCredentials`) | `alknet-core` |
|
||||||
|
|
||||||
**Category B — TLS/verifier tests, move to `alknet-tls` (10 tests):**
|
**Category B — TLS/verifier tests, move to `alknet-tls` (10 tests):**
|
||||||
|
|
||||||
@@ -529,28 +578,42 @@ use `spawn_dispatch(stub_connection())`.
|
|||||||
|
|
||||||
**The `from_call.rs` tests (27 tests):** these use `CallConnection`
|
**The `from_call.rs` tests (27 tests):** these use `CallConnection`
|
||||||
directly (constructed from `stub_connection()` or a mock), not
|
directly (constructed from `stub_connection()` or a mock), not
|
||||||
`connect`. They're protocol-level and stay in `alknet-call` unchanged.
|
`connect`. 25 are protocol-level and stay in `alknet-call` unchanged.
|
||||||
|
2 are removed: `build_forwarded_payload_sets_auth_token_when_provided`
|
||||||
|
and `streaming_forwarding_handler_sets_auth_token_when_provided` —
|
||||||
|
they test the `credentials_auth_token` `Some` path, which is removed
|
||||||
|
(the field was always `None`, never connected to `CallCredentials`).
|
||||||
The one reference to `connect()` is in a doc comment (line 76:
|
The one reference to `connect()` is in a doc comment (line 76:
|
||||||
"the assembly layer calls `from_call` immediately after `connect()`")
|
"the assembly layer calls `from_call` immediately after `connect()`")
|
||||||
— update the comment to say "after `AlknetClient::dial_*` +
|
— update the comment to say "after `AlknetClient::dial_*` +
|
||||||
`spawn_dispatch`".
|
`spawn_dispatch`".
|
||||||
|
|
||||||
**Net Phase 5 test impact:** 6 tests stay unchanged (Category A), 10
|
**Net Phase 5 test impact:** 4 tests stay unchanged (Category A), 2
|
||||||
tests move to `alknet-tls` in Phase 1 (Category B), 0 tests need
|
tests move to `alknet-core` (Category A2), 10 tests move to
|
||||||
rewriting. The `from_call.rs` tests (27) stay unchanged. The prune
|
`alknet-tls` in Phase 1 (Category B), 2 `from_call` dead-path tests
|
||||||
of `call_client.rs` is mechanical: delete `ClientError`, `connect`,
|
removed. The `from_call.rs` tests: 25 stay unchanged, 2 removed. The
|
||||||
and all the TLS helpers (`build_*`, `select_*`, `load_*`,
|
prune of `call_client.rs` is mechanical: delete `ClientError`,
|
||||||
`Ed25519SigningKey`, `RawKeyClientCertResolver`, `NoClientCertResolver`,
|
`connect`, `CallCredentials` and its builder methods, and all the TLS
|
||||||
`FingerprintPinVerifier`); keep `CallClient` + `new` + `spawn_dispatch`
|
helpers (`build_*`, `select_*`, `load_*`, `Ed25519SigningKey`,
|
||||||
unchanged; update imports. The test suite keeps the Category A tests,
|
`RawKeyClientCertResolver`, `NoClientCertResolver`,
|
||||||
removes the Category B tests (moved in Phase 1), and updates the one
|
`FingerprintPinVerifier`); keep `CallClient` + `new` +
|
||||||
doc comment.
|
`spawn_dispatch` unchanged; update imports. Also remove `from_call`'s
|
||||||
|
`credentials_auth_token` dead path: the field on `OpSummary`, the
|
||||||
|
parameters on `make_forwarding_handler` /
|
||||||
|
`make_streaming_forwarding_handler`, and the `auth_token` parameter on
|
||||||
|
`build_forwarded_payload`. The test suite keeps the Category A tests,
|
||||||
|
removes the Category A2 tests (moved to core), removes the Category B
|
||||||
|
tests (moved in Phase 1), removes the 2 `from_call` dead-path tests,
|
||||||
|
and updates the one doc comment.
|
||||||
|
|
||||||
This is much simpler than the initial estimate of "~290 lines of test
|
This is a larger prune than the initial estimate of "~140 lines of
|
||||||
restructuring." The actual test work is: move 10 tests to `alknet-tls`
|
implementation + ~290 lines of test restructuring." The actual work:
|
||||||
in Phase 1 (adapted to the new API), keep 6 tests unchanged, update one
|
delete `connect` + TLS helpers (~140 lines), delete `CallCredentials`
|
||||||
doc comment. The `connect` removal breaks zero tests because no test
|
+ builder methods (~50 lines), delete `from_call`'s
|
||||||
calls `connect`.
|
`credentials_auth_token` dead path (~30 lines), move 10 tests to
|
||||||
|
`alknet-tls` (Phase 1), move 2 tests to `alknet-core`, keep 4 tests
|
||||||
|
unchanged, remove 2 `from_call` dead-path tests. The `connect` removal
|
||||||
|
breaks zero lib tests because no lib test calls `connect`.
|
||||||
|
|
||||||
## Resolved questions
|
## Resolved questions
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user