Files
alknet/docs/architecture/crates/core
glm-5.2 7d812af8f4 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.
2026-06-28 08:49:36 +00:00
..

status, last_updated
status last_updated
draft 2026-06-27

alknet-core

Core library for ALPN-based protocol dispatch. Every handler crate depends on alknet-core.

Documents

Document Status Description
core-types.md draft ProtocolHandler trait, HandlerError, Connection, BiStream, StreamError
endpoint.md draft ALPN router, HandlerRegistry, accept loop, graceful shutdown
auth.md draft AuthContext, Identity, IdentityProvider, AuthToken, resolution flow, PeerEntry, CredentialStore
config.md draft StaticConfig, DynamicConfig, ArcSwap, ConfigReloadHandle, AuthPolicy.peers

Applicable ADRs

ADR Title Relevance
001 ALPN-Based Protocol Dispatch Core architectural model
002 ProtocolHandler Trait The trait every handler implements
003 Crate Decomposition alknet-core's position in the crate graph
004 Auth as Shared Core IdentityProvider in core
006 ALPN String Convention ALPN format, one-ALPN-per-connection
007 BiStream Type Definition Connection, BiStream trait, SendStream, RecvStream
009 One-Way Door Framework Decision classification
010 ALPN Router and Endpoint Endpoint, HandlerRegistry, accept loop
011 AuthContext Structure AuthContext fields and resolution flow
015 Privilege Model and Authority Context Per-request identity on OperationContext; admin scope for config reload
030 PeerEntry and Identity.id Decoupling authorized_fingerprintspeers: Vec<PeerEntry>; Identity.id = peer_id (stable)
031 CredentialStore Repo Trait Second repo trait in core; InMemoryCredentialStore default adapter
033 Storage Boundary and Repo/Adapter Pattern Core defines traits + in-memory defaults; persistence adapters are separate crates

Relevant Open Questions

OQ Title Status Relevance
OQ-04 Dynamic handler registration resolved (start static) HandlerRegistry is immutable at startup
OQ-05 Multi-connectivity endpoint resolved (quinn + iroh) AlknetEndpoint supports both, both feature-gated
OQ-11 Handler-level auth resolution observability resolved Handlers store resolved identity on Connection; two identity scopes (connection-level for observability, per-request for ACL)
OQ-33 PeerId — logical id vs crypto identity resolved by ADR-030 PeerId = Identity.id = PeerEntry.peer_id (stable across key rotation)
OQ-34 Persistent peer registry (storage boundary) resolved by ADR-030+031+033 Core defines repo traits + in-memory defaults; persistence adapters are separate crates
OQ-35 API key asymmetry dissolved PeerEntry supports multiple credential paths; ApiKeyEntry is for tokens that ARE the identity
OQ-36 Concrete persistence adapter shapes open (deferred for exploration) The repo/adapter pattern is committed (ADR-033); in-memory adapters ship with core; persistence adapters deferred
OQ-37 X.509 outgoing-only case open Three auth types; how X.509 server identity fits the peer model. Not blocking.

Key Design Principles

  1. One trait, one dispatch point: ProtocolHandler is the only abstraction handlers implement. No StreamInterface/MessageInterface split.
  2. ALPN does the routing: The endpoint dispatches by ALPN string. No byte-peeking, no ListenerConfig enum.
  3. Handlers own their wire format: Each handler manages its own protocol parsing. alknet-core provides the Connection, not the framing.
  4. Auth is hybrid: The endpoint provides what it can (TLS-level auth). Handlers complete what they need. AuthContext may be partial.
  5. WASM door preserved: BiStream is a trait, Connection is an opaque type. Core types don't assume tokio or quinn in public APIs.