docs(arch): multi-credential PeerEntry, resolve OQ-29, dissolve OQ-35, add OQ-37
Amend ADR-030 with three changes from the auth-type analysis: 1. PeerEntry is now multi-credential: fingerprints: Vec<String> (Ed25519 and/or X.509) + auth_token_hash: Option<String> (bearer token). All resolve to the same peer_id. A peer that authenticates via Ed25519 today and via auth_token tomorrow gets the same PeerId. The 'peer bearer vs auth bearer' distinction was wrong — the correct framing is the three credential types (Ed25519, X.509, bearer token) and whether the token needs a stable logical id across rotation (PeerEntry) or not (ApiKeyEntry). 2. Fingerprint normalization (§6): quinn extracts the raw Ed25519 public key from the SPKI cert and formats as ed25519:<hex>, matching iroh. The same key has the same fingerprint regardless of transport. X.509 fingerprints stay as SHA256:<hex of DER>. This also simplifies the coming WebTransport relay work. 3. The 'API keys' section is replaced with 'Bearer tokens' — correctly framing the three auth types and the two bearer-token paths (PeerEntry.auth_token_hash vs ApiKeyEntry). Resolve OQ-29 (CallClient TLS client-auth): wire quinn client-auth (present Ed25519 key as raw public key client cert — the server-side extraction already works); key-type-aware server cert verification (raw key = fingerprint match, X.509 = CA verification via WebPkiServerVerifier — AcceptAnyServerCertVerifier is only safe for raw keys); fingerprint normalization. The iroh path already works (RFC 7250 raw keys, both sides exchange automatically); the gap was quinn-only. Dissolve OQ-35: the 'API key asymmetry' framing was wrong. PeerEntry supports multiple credential paths; ApiKeyEntry is for tokens that ARE the identity. Add OQ-37: X.509 outgoing-only case — the three auth types and how X.509 server identity fits the peer model. Not blocking the ADR-029 migration; downstream (HTTP crate phase). Update auth.md, config.md, client-and-adapters.md, call/README.md, core/README.md, open-questions.md, README.md, and call_client.rs source comment. Workspace green: 326 tests pass, build clean.
This commit is contained in:
@@ -195,39 +195,48 @@ fingerprint → `PeerEntry` → `Identity { id: peer_id, ... }`, so
|
||||
```rust
|
||||
pub struct PeerEntry {
|
||||
/// Stable logical peer id ("worker-a", "alice"). Does NOT change on
|
||||
/// key rotation. This becomes Identity.id on resolution.
|
||||
/// key rotation. This becomes Identity.id on resolution, regardless of
|
||||
/// which credential path resolved the identity.
|
||||
pub peer_id: String,
|
||||
|
||||
/// Current cryptographic material — the fingerprint the endpoint
|
||||
/// extracts from the TLS handshake (SHA256:... for X.509, ed25519:...
|
||||
/// for RFC 7250 raw keys). Changes on key rotation.
|
||||
pub fingerprint: String,
|
||||
/// TLS fingerprints for this peer — one or more. A peer may have
|
||||
/// multiple keys (e.g., an Ed25519 raw key for P2P and an X.509 cert
|
||||
/// for domain-facing). Resolution matches against any entry.
|
||||
/// Format: "ed25519:<hex of 32-byte pub key>" for RFC 7250 raw keys
|
||||
/// (normalized across quinn and iroh — ADR-030 §6), "SHA256:<hex>" for
|
||||
/// X.509 certs (DER hash). Changes on key rotation.
|
||||
pub fingerprints: Vec<String>,
|
||||
|
||||
/// Optional: bearer-token authentication for this peer. A peer that
|
||||
/// also authenticates via auth_token (e.g., HTTP clients that can't
|
||||
/// do TLS client-auth) stores the SHA-256 hash of the token here.
|
||||
/// Resolution via resolve_from_token matches this field and returns
|
||||
/// the same Identity { id: peer_id, ... } as the fingerprint path.
|
||||
pub auth_token_hash: Option<String>,
|
||||
|
||||
/// Authorization scopes granted to this peer. Resolved into
|
||||
/// Identity.scopes.
|
||||
pub scopes: Vec<String>,
|
||||
|
||||
/// Named resource lists granted to this peer. Resolved into
|
||||
/// Identity.resources. Populated from config (ADR-030 lifts the
|
||||
/// pre-ADR-030 limitation that fingerprint-resolved identities had
|
||||
/// empty resources).
|
||||
/// Identity.resources.
|
||||
pub resources: HashMap<String, Vec<String>>,
|
||||
|
||||
/// Human-readable display name for logs / UIs. Optional.
|
||||
pub display_name: Option<String>,
|
||||
|
||||
/// Whether this peer is authorized at all. false = the fingerprint
|
||||
/// is recognized but the peer is disabled (token-revoked-equivalent
|
||||
/// for fingerprints). Resolution returns None.
|
||||
/// Whether this peer is authorized at all. false = recognized but
|
||||
/// disabled (revoked). Resolution returns None.
|
||||
pub enabled: bool,
|
||||
}
|
||||
```
|
||||
|
||||
See [ADR-030](../../decisions/030-peerentry-and-identity-id-decoupling.md)
|
||||
for the `PeerEntry` model, the id-fingerprint decoupling rationale, and
|
||||
the key-rotation story (vault rotates locally; the remote side updates
|
||||
the `PeerEntry.fingerprint` field; the `peer_id` and all ACL / routing
|
||||
references stay stable).
|
||||
for the `PeerEntry` model, the multi-credential resolution path, the
|
||||
fingerprint normalization rationale, and the key-rotation story (vault
|
||||
rotates locally; the remote side updates the `PeerEntry.fingerprints` or
|
||||
`auth_token_hash` field; the `peer_id` and all ACL / routing references
|
||||
stay stable).
|
||||
|
||||
Certificate authority entries for cert-based auth are omitted from
|
||||
`AuthPolicy` until alknet-ssh is implemented, to avoid referencing an
|
||||
|
||||
Reference in New Issue
Block a user