refactor(core,tls,client): align ConnectionCredentials field name with ADR-091 (tls_identity -> local_identity)
ADR-091 decided `ConnectionCredentials.local_identity`; the code implemented
`tls_identity` (tasks/core/connection-credentials.md deferred the rename as
"path of least resistance" during the extraction). The tangle that made the
rename hard no longer exists, so align the code with the decision.
Scope is the `ConnectionCredentials` field + builder only:
- alknet-core/credentials.rs: field, with_local_identity, doc, test
- alknet-tls/client.rs: field access in TlsClientConfig::new, test builders, docs
- alknet-client/dial/quinn.rs: test builder
NOT renamed (distinct concepts sharing the words):
- StaticConfig.tls_identity (server-side static config; ADR-082/027/083)
- TlsIdentity enum type name
- alknet-tls server fn params named tls_identity (&TlsIdentity value)
Also fixes dial_iroh.rs doc comments that claimed the local key is extracted
from creds.local_identity — the key is actually on the pre-built iroh endpoint
(set at with_iroh time); the dial reads only creds.remote_identity and ignores
creds.local_identity (per client/README.md §iroh).
Architecture specs updated to match (call/client-and-adapters.md, tls/README.md,
client/README.md). Historical ADR context describing the old CallCredentials
field stays as-is; tasks/ and docs/research/ are historical artifacts.
Resolves follow-up #2 from the post-extraction spec sync (c6eef73).
This commit is contained in:
1 parent
c6eef730e4
commit
3b10fc1817
7 files changed
+31
-25
No files matched your search
@@ -2,9 +2,12 @@
|
||||
//!
|
||||
//! Feature-gated on `iroh`. The iroh path does NOT use `TlsClientConfig` —
|
||||
//! iroh has its own TLS (shares the `Ed25519SecretKey`, not the rustls config
|
||||
//! — ADR-087 §3, ADR-089 §3). The local key is extracted from
|
||||
//! `creds.local_identity`; the remote `EndpointId` is derived from
|
||||
//! `creds.remote_identity.fingerprint`.
|
||||
//! — ADR-087 §3, ADR-089 §3). The local key is set on the pre-built iroh
|
||||
//! endpoint at `with_iroh` time (the assembly layer reads it from
|
||||
//! `StaticConfig` and feeds it to `iroh::Endpoint::builder().secret_key()`);
|
||||
//! the dial consumes only `creds.remote_identity` (deriving the remote
|
||||
//! `EndpointId` from `creds.remote_identity.fingerprint`) and ignores
|
||||
//! `creds.local_identity`.
|
||||
|
||||
use alknet_core::credentials::ConnectionCredentials;
|
||||
use alknet_core::types::Connection;
|
||||
@@ -16,8 +19,11 @@ impl AlknetClient {
|
||||
/// Iroh dial. Dials on `alpn` via the iroh endpoint. The iroh path
|
||||
/// does NOT use `TlsClientConfig` — iroh has its own TLS (shares the
|
||||
/// `Ed25519SecretKey`, not the rustls config — ADR-087 §3, ADR-089
|
||||
/// §3). The local key is extracted from `creds.local_identity`; the
|
||||
/// remote `EndpointId` is derived from `creds.remote_identity.fingerprint`
|
||||
/// §3). The local key is on the pre-built iroh endpoint (set at
|
||||
/// `with_iroh` time); the dial consumes only
|
||||
/// `creds.remote_identity` and ignores `creds.local_identity`.
|
||||
/// The remote `EndpointId` is derived from
|
||||
/// `creds.remote_identity.fingerprint`
|
||||
/// (`ed25519:<hex>` → `EndpointId::from_bytes`). The verifier is iroh's
|
||||
/// `EndpointId` match (fingerprint pin by another name — ADR-034 §3).
|
||||
/// An unknown iroh remote fails closed (no CA). Feature-gated on
|
||||
|
||||
@@ -100,7 +100,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn dial_quic_tls_config_error_on_acme_identity() {
|
||||
use alknet_core::config::{AcmeDirectory, TlsIdentity};
|
||||
let creds = ConnectionCredentials::new().with_tls_identity(TlsIdentity::Acme {
|
||||
let creds = ConnectionCredentials::new().with_local_identity(TlsIdentity::Acme {
|
||||
domains: vec!["example.com".into()],
|
||||
directory: AcmeDirectory::Staging,
|
||||
cache_dir: std::path::PathBuf::from("/tmp"),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Transport-level credential bundle for outbound connections (ADR-091).
|
||||
//!
|
||||
//! `ConnectionCredentials` carries the two dimensions the dial consumes:
|
||||
//! the local node's TLS identity and the expected remote identity.
|
||||
//! the local node's identity and the expected remote identity.
|
||||
//! It is transport-agnostic — consumed by `alknet-tls` (TLS setup) and
|
||||
//! `alknet-client` (dial).
|
||||
|
||||
@@ -36,9 +36,9 @@ pub struct RemoteIdentity {
|
||||
/// `docs/architecture/crates/call/client-and-adapters.md`.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ConnectionCredentials {
|
||||
/// The local node's TLS identity (RFC 7250 raw key or X.509), derived
|
||||
/// The local node's identity (RFC 7250 raw key or X.509), derived
|
||||
/// from the vault at startup.
|
||||
pub tls_identity: Option<TlsIdentity>,
|
||||
pub local_identity: Option<TlsIdentity>,
|
||||
/// Expected fingerprint/cert of the remote node, stored as a capability.
|
||||
/// `Some` → fingerprint pin (known peer with a `PeerEntry`); `None` → CA
|
||||
/// verification for X.509 remotes, fail-closed for Ed25519 raw-key remotes
|
||||
@@ -52,8 +52,8 @@ impl ConnectionCredentials {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn with_tls_identity(mut self, tls_identity: TlsIdentity) -> Self {
|
||||
self.tls_identity = Some(tls_identity);
|
||||
pub fn with_local_identity(mut self, local_identity: TlsIdentity) -> Self {
|
||||
self.local_identity = Some(local_identity);
|
||||
self
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ mod tests {
|
||||
creds.remote_identity.as_ref().unwrap().fingerprint,
|
||||
"SHA256:abc"
|
||||
);
|
||||
assert!(creds.tls_identity.is_none());
|
||||
assert!(creds.local_identity.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -23,7 +23,7 @@ impl TlsClientConfig {
|
||||
pub fn new(credentials: &ConnectionCredentials, alpn: &[u8]) -> Result<Self, TlsError> {
|
||||
let provider = Arc::new(rustls::crypto::aws_lc_rs::default_provider());
|
||||
|
||||
let client_auth = build_client_auth(&provider, &credentials.tls_identity)?;
|
||||
let client_auth = build_client_auth(&provider, &credentials.local_identity)?;
|
||||
let verifier = select_server_verifier(&provider, &credentials.remote_identity)?;
|
||||
|
||||
let mut config = rustls::ClientConfig::builder_with_provider(provider)
|
||||
@@ -60,7 +60,7 @@ impl TlsClientConfig {
|
||||
/// identity. For `TlsIdentity::RawKey` the Ed25519 key is presented as an RFC
|
||||
/// 7250 raw public key client cert (`only_raw_public_keys() == true`) — the
|
||||
/// client-side equivalent of the server's `RawKeyCertResolver`. For X.509 the
|
||||
/// cert chain + key are loaded from disk. `None` (no `tls_identity` configured)
|
||||
/// cert chain + key are loaded from disk. `None` (no `local_identity` configured)
|
||||
/// resolves to no client cert (the server gets nothing to fingerprint).
|
||||
fn build_client_auth(
|
||||
provider: &Arc<rustls::crypto::CryptoProvider>,
|
||||
@@ -193,7 +193,7 @@ impl rustls::client::ResolvesClientCert for RawKeyClientCertResolver {
|
||||
}
|
||||
}
|
||||
|
||||
/// Client cert resolver that presents no client cert (the `tls_identity: None`
|
||||
/// Client cert resolver that presents no client cert (the `local_identity: None`
|
||||
/// or `SelfSigned` path). The server gets nothing to fingerprint — the
|
||||
/// `PeerEntry` fingerprint → `peer_id` resolution path is not activated for
|
||||
/// this connection.
|
||||
@@ -496,7 +496,7 @@ mod tests {
|
||||
fn build_quinn_client_config_with_raw_key_identity_builds_without_error() {
|
||||
let sk = Ed25519SecretKey::generate();
|
||||
let credentials = ConnectionCredentials::new()
|
||||
.with_tls_identity(TlsIdentity::RawKey(sk))
|
||||
.with_local_identity(TlsIdentity::RawKey(sk))
|
||||
.with_remote_identity(RemoteIdentity {
|
||||
fingerprint: "ed25519:deadbeef".to_string(),
|
||||
});
|
||||
@@ -510,7 +510,7 @@ mod tests {
|
||||
#[test]
|
||||
fn build_quinn_client_config_with_no_remote_identity_builds_without_error() {
|
||||
let sk = Ed25519SecretKey::generate();
|
||||
let credentials = ConnectionCredentials::new().with_tls_identity(TlsIdentity::RawKey(sk));
|
||||
let credentials = ConnectionCredentials::new().with_local_identity(TlsIdentity::RawKey(sk));
|
||||
let config = TlsClientConfig::new(&credentials, b"alknet/call")
|
||||
.expect("TlsClientConfig::new must build for CA-verification path");
|
||||
let quinn_config = config.for_quinn().expect("for_quinn must convert");
|
||||
|
||||
@@ -254,7 +254,7 @@ The credential dimensions are split across two layers (ADR-091, amended
|
||||
- **`ConnectionCredentials`** (in `alknet-core`, per ADR-091) — the
|
||||
**transport-level** credential bundle, consumed by the dial
|
||||
(`AlknetClient`). Carries the two transport-identity dimensions:
|
||||
`tls_identity` (the local node's `TlsIdentity`) and `remote_identity`
|
||||
`local_identity` (the local node's `TlsIdentity`) and `remote_identity`
|
||||
(the expected fingerprint). The dial does not depend on the call
|
||||
protocol for this type.
|
||||
- **`auth_token`** — a **per-request payload field**, not a
|
||||
@@ -276,7 +276,7 @@ variables. The transport-identity dimensions (ADR-017 §7):
|
||||
```rust
|
||||
// Transport-level (alknet-core, consumed by the dial — ADR-091)
|
||||
pub struct ConnectionCredentials {
|
||||
pub tls_identity: Option<TlsIdentity>, // RFC 7250 raw key or X.509
|
||||
pub local_identity: Option<TlsIdentity>, // RFC 7250 raw key or X.509
|
||||
pub remote_identity: Option<RemoteIdentity>, // expected fingerprint (None = CA path / fail-closed)
|
||||
}
|
||||
|
||||
@@ -306,7 +306,7 @@ pub struct RemoteIdentity { pub fingerprint: String }
|
||||
```
|
||||
|
||||
There is no call-protocol credential bundle. `CallCredentials` is
|
||||
removed. The transport dimensions (`tls_identity`, `remote_identity`)
|
||||
removed. 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,
|
||||
@@ -781,7 +781,7 @@ Based on the gap analysis and the downstream unblock chain:
|
||||
| Abort cascade for nested calls | [ADR-016](../../decisions/016-abort-cascade-for-nested-calls.md) | Cross-node abort through `from_call` forwarding handler's `parent_request_id` |
|
||||
| Operation error schemas | [ADR-023](../../decisions/023-operation-error-schemas.md) | `error_schemas` mirrored by `from_call` from remote op's spec |
|
||||
| Streaming handler for subscriptions | [ADR-049](../../decisions/049-streaming-handler-for-subscriptions.md) | `from_call` `Subscription` ops register a `StreamingHandler` (`HandlerKind::Stream`) that calls `CallConnection::subscribe()` and forwards the remote stream; `Query`/`Mutation` stay `HandlerKind::Once` |
|
||||
| TLS identity redesign | [ADR-027](../../decisions/027-tls-identity-redesign-acme-rawkey-decoupling.md) | RFC 7250 raw key / X.509 cert dimensions of the local `TlsIdentity` (now carried by `ConnectionCredentials.tls_identity`) |
|
||||
| TLS identity redesign | [ADR-027](../../decisions/027-tls-identity-redesign-acme-rawkey-decoupling.md) | RFC 7250 raw key / X.509 cert dimensions of the local `TlsIdentity` (now carried by `ConnectionCredentials.local_identity`) |
|
||||
| Outgoing-only X.509 and three peer roles | [ADR-034](../../decisions/034-outgoing-only-x509-and-three-peer-roles.md) | Public X.509 endpoint is not a `PeerEntry` on the client side (no `PeerId`, not in peer graph); client-side verifier by `PeerEntry` presence (CA vs fingerprint pin); hub = mixed-fingerprint `PeerEntry` |
|
||||
| HD derivation for encryption keys | [ADR-020](../../decisions/020-hd-derivation-for-encryption-keys.md) | Vault-derived TLS identity material |
|
||||
| Vault key model | [ADR-026](../../decisions/026-vault-key-model-hd-derivation.md) | Vault-derived TLS identity material |
|
||||
|
||||
@@ -431,7 +431,7 @@ consumes only `creds.remote_identity` (deriving the remote `NodeId`);
|
||||
the local key is not in `ConnectionCredentials` for the iroh path — it
|
||||
is on the endpoint. The dial signature is unified — all three dials
|
||||
take `&ConnectionCredentials` (ADR-091) — and the iroh dial simply
|
||||
ignores the `tls_identity` field (the key is already on the endpoint).
|
||||
ignores the `local_identity` field (the key is already on the endpoint).
|
||||
The verifier is iroh's `NodeId` match — the remote's `NodeId` (Ed25519
|
||||
public key) is verified against the expected `NodeId`, which is
|
||||
fingerprint-pinning by another name. An unknown iroh remote fails
|
||||
@@ -640,7 +640,7 @@ let client = AlknetClient::new()
|
||||
|
||||
// 3. Derive credentials from the vault (ADR-014 — no env vars).
|
||||
let creds = ConnectionCredentials::new()
|
||||
.with_tls_identity(TlsIdentity::RawKey(local_key))
|
||||
.with_local_identity(TlsIdentity::RawKey(local_key))
|
||||
.with_remote_identity(RemoteIdentity {
|
||||
fingerprint: hub_fingerprint, // known peer → fingerprint pin
|
||||
});
|
||||
@@ -674,7 +674,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: `tls_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`), 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
|
||||
|
||||
|
||||
@@ -473,7 +473,7 @@ impl TlsClientConfig {
|
||||
/// dial's ALPN. `ConnectionCredentials` (ADR-091, in `alknet-core`)
|
||||
/// carries the two dimensions the dial consumes:
|
||||
///
|
||||
/// 1. `tls_identity` — the local node's `TlsIdentity` (RFC 7250
|
||||
/// 1. `local_identity` — the local node's `TlsIdentity` (RFC 7250
|
||||
/// raw key or X.509), presented as the client cert. `None` →
|
||||
/// no client cert (the server gets nothing to fingerprint).
|
||||
/// `SelfSigned` → no client cert (dev-only). `Acme` →
|
||||
|
||||
Reference in new issue
Block a user