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.
status, last_updated
| status | last_updated |
|---|---|
| draft | 2026-06-27 |
Alknet Architecture
Current State
Pre-implementation of the storage/repo pattern. The project has completed a pivot from a three-layer model to an ALPN-as-service model. The greenfield workspace contains alknet-vault (stable — implementation complete and verified, local-only by construction per ADR-025, HD-derivation key model per ADR-026) and research/reference material. Foundational ADRs (001–029) are in place, with ADR-029 (peer-graph routing) Proposed and the call crate implemented and reviewed.
The storage and auth strategy research (docs/research/alknet-storage-strategy/findings.md) surfaced the repo/adapter pattern as the answer to cross-node state (peer identity, credentials). This has now landed as four ADRs:
- ADR-030 (PeerEntry and Identity.id decoupling):
authorized_fingerprints: HashSet<String>→peers: Vec<PeerEntry>;Identity.idbecomes the stablepeer_id(not the fingerprint); key rotation changes the fingerprint, not the identity. Supersedes ADR-029's v1 UUID source (the one-way door —PeerIdis logical, not crypto — is preserved; the source changes from UUID toIdentity.idfromPeerEntry). Resolves OQ-33 and the storage-boundary half of OQ-34. - ADR-031 (CredentialStore repo trait): the second repo trait in core (alongside
IdentityProvider), withInMemoryCredentialStoredefault adapter. Establishes the credential-persistence abstraction. - ADR-032 (Forwarded-for identity):
forwarded_forfield oncall.requestedandOperationContext; metadata only —AccessControl::checknever reads it; thefrom_callhandler populates it. Wire-format one-way door, included with the ADR-029 migration window. - ADR-033 (Storage boundary and repo/adapter pattern): core defines repo traits + in-memory defaults; persistence adapters are separate crates; the assembly layer wires the adapter. Resolves OQ-34's storage-boundary question. Concrete adapter shapes are deferred for exploration (OQ-36).
The alknet-call crate is implemented and reviewed — both the server-side core and the client/adapter surface (207 lib + 2 integration tests passing). The alknet-core and alknet-call crate specs are in draft; the alknet-vault crate specs are stable.
Next step: The storage/repo-pattern ADRs (030–033) are accepted and amend the core and call specs. The next implementation phase is the ADR-029 migration (peer-keyed overlays, PeerRef routing, retire remote_safe/trusted_peer) with the ADR-030 PeerEntry change and the ADR-032 forwarded_for field folded in — the OperationContext, from_call handler, and AuthPolicy are all under edit, making this the cheapest window. After that: alknet-http (Phase 0 findings in docs/research/alknet-http/), which consumes the CredentialStore trait and the OperationAdapter contract.
Architecture Documents
| Document | Status | Description |
|---|---|---|
| overview.md | draft | Workspace-level overview, crate graph, shared types, design principles |
| open-questions.md | draft | Centralized OQ tracker with door-type classifications |
| crates/core/README.md | draft | alknet-core crate index |
| crates/core/core-types.md | draft | ProtocolHandler, HandlerError, Connection, BiStream, StreamError |
| crates/core/endpoint.md | draft | ALPN router, HandlerRegistry, accept loop, shutdown |
| crates/core/auth.md | draft | AuthContext, Identity, IdentityProvider, AuthToken, resolution flow |
| crates/core/config.md | draft | StaticConfig, DynamicConfig, ArcSwap, ConfigReloadHandle |
| crates/call/README.md | draft | alknet-call crate index |
| crates/call/call-protocol.md | draft | CallAdapter, EventEnvelope framing, stream model, PendingRequestMap, bidirectional calls, streaming subscribe example |
| crates/call/operation-registry.md | draft | OperationSpec, Handler, OperationRegistry, AccessControl, capability injection, service discovery, irpc integration |
| crates/call/client-and-adapters.md | draft | CallClient (outbound connection opener), from_call / from_jsonschema, OperationAdapter trait, adapter location map, no-env-vars invariant, exchange-of-operations pattern |
| crates/vault/README.md | stable | alknet-vault crate index |
| crates/vault/mnemonic-derivation.md | stable | BIP39, SLIP-0010, BIP-0032, derivation paths, key types |
| crates/vault/encryption.md | stable | AES-256-GCM, EncryptedData, key versioning, salt (Phase B reserved) |
| crates/vault/service.md | stable | VaultServiceHandle lifecycle, direct dispatch, cache, error model |
| crates/vault/protocol.md | stable | DerivedKey redaction, KeyType, serialization behavior |
ADR Table
| ADR | Title | Status |
|---|---|---|
| 001 | ALPN-Based Protocol Dispatch | Accepted |
| 002 | ProtocolHandler Trait | Accepted |
| 003 | Crate Decomposition | Accepted |
| 004 | Auth as Shared Core (IdentityProvider) | Accepted |
| 005 | irpc as Call Protocol Foundation | Accepted |
| 006 | ALPN String Convention and Connection Model | Accepted |
| 007 | BiStream Type Definition | Accepted |
| 008 | Vault Integration Point | Accepted |
| 009 | One-Way Door Decision Framework | Accepted |
| 010 | ALPN Router and Endpoint | Accepted |
| 011 | AuthContext Structure and Resolution Flow | Accepted |
| 012 | Call Protocol Stream Model | Accepted |
| 013 | Rust as Canonical Implementation Language | Accepted |
| 014 | Secret Material Flow and Capability Injection | Accepted |
| 015 | Privilege Model and Authority Context | Accepted |
| 016 | Abort Cascade for Nested Calls | Accepted |
| 017 | Call Protocol Client and Adapter Contract | Accepted |
| 018 | Vault as Standalone Crate | Accepted |
| 019 | Vault Assembly-Layer-Only Access | Accepted |
| 020 | HD Derivation for Encryption Keys | Accepted |
| 021 | Key Rotation via Version-Indexed Paths | Accepted |
| 022 | Handler Registration, Provenance, and Composition Authority | Accepted |
| 023 | Operation Error Schemas | Accepted |
| 024 | Operation Registry Layering | Accepted |
| 025 | Vault Local-Only Dispatch | Accepted |
| 026 | Vault Key Model — HD Derivation | Accepted |
| 027 | TLS Identity Redesign — ACME + RawKey Decoupling | Accepted |
| 028 | Peer-Scoped Registry Filtering for CallClient Inbound Dispatch | |
| 029 | Peer-Graph Routing Model for alknet-call Composition | Accepted (Assumption 1's PeerId source superseded by ADR-030) |
| 030 | PeerEntry and Identity.id Decoupling | Accepted (supersedes ADR-029 Assumption 1's UUID source) |
| 031 | CredentialStore Repo Trait | Accepted |
| 032 | Forwarded-For Identity (Metadata, Not Authority) | Accepted |
| 033 | Storage Boundary and Repo/Adapter Pattern | Accepted |
Open Questions
See open-questions.md for the full tracker.
Resolved one-way doors:
- OQ-01: BiStream type — trait with Connection parameter (ADR-007)
- OQ-02: AuthContext timing — hybrid model (ADR-004)
- OQ-03: ALPN naming —
alknet/prefix, no version (ADR-006) - OQ-05: Multi-connectivity endpoint — quinn + iroh, both feature-gated (ADR-010)
- OQ-06: ALPN per connection, not per stream (ADR-006)
- OQ-08: Vault integration — CLI-embedded, assembly-layer only (ADR-008, ADR-014)
- OQ-16: Safe vault operations for call protocol exposure — none for now (ADR-014)
- OQ-18: Privilege model —
internal= authority switch, External/Internal visibility, handler identity + scoped env (ADR-015) - OQ-17: Abort cascade —
call.abortedcascades to descendants; defaultabort-dependents,continue-runningopt-in (ADR-016) - OQ-15: Call protocol client and adapter contract —
CallClientopens connections;from_callimports remote ops; connection direction independent of call direction (ADR-017)
Resolved two-way doors:
- OQ-04: Dynamic handler registration — static at startup (ADR-010); scoped to the
HandlerRegistry(ALPN-level) by ADR-024, which governsOperationRegistrymutability separately - OQ-07: Call protocol scope — bidirectional streams, EventEnvelope, ID-based correlation (ADR-012)
- OQ-11: Handler-level auth resolution observability — handlers store resolved identity on Connection (Option B); two identity scopes: connection-level (observability) and per-request (ACL)
- OQ-12: TLS identity provisioning — two use cases: RFC 7250 raw keys (default, P2P) and X.509 certs (domain-hosted, browsers). ACME designed in ADR-027; RawKey decoupled from iroh feature.
- OQ-13: Operation path format —
/{service}/{op}is the correct design for alknet-call, not a simplification - OQ-14: Batch operation semantics — multiple correlated
call.requestedevents is the correct protocol design, not a simplification - OQ-19: Session-scoped registries — agent-written operations via
OperationEnvtrait layering; protocol doesn't need changes;OperationEnvmust remain a trait. Generalized by ADR-024 to cover connection-scoped overlays as well. - OQ-20: Encryption key derivation — HD derivation from BIP39 seed, not PBKDF2; salt field unused in v2 (wire-format compat) (ADR-020)
- OQ-21: Remote vault access — resolved (ADR-025): vault is local-only by construction; remote access requires a separate vault-server crate with its own ADR
- OQ-22: Key rotation — version-indexed derivation paths;
rotatemethod re-encrypts (ADR-021) - OQ-23: Handler identity registration path — registration bundle with provenance, composition authority, scoped env, capabilities (ADR-022)
- OQ-24: Operation error schemas — declared domain errors with typed
detailspayload; adapter fidelity forfrom_openapi/to_openapi(ADR-023)
Resolved by the storage/repo-pattern ADRs (ADR-030–033):
- OQ-33:
PeerId stability— resolved by ADR-030 (logical id; source isIdentity.id=PeerEntry.peer_id, stable across key rotation; UUID workaround removed) - OQ-34:
Persistent peer registry— resolved by ADR-030 + ADR-031 + ADR-033 (storage boundary: core defines repo traits + in-memory defaults; persistence adapters are separate crates) - OQ-35:
API key asymmetry— dissolved (the framing was wrong;PeerEntrysupports multiple credential paths)
Resolved by the call-completion / ADR-029 work:
- OQ-27:
— resolved (auto-re-import on connection establishment;from_callre-import triggerrefresh()is a feature addition) - OQ-28:
— resolved (same-peer collision = error; cross-peer dissolved by ADR-029)from_callnamespace collision - OQ-29:
CallClient TLS client-auth— resolved (wire quinn client-auth; key-type-aware server cert verification; fingerprint normalization toed25519:across quinn/iroh) - OQ-30:
— resolved (insertion-order first-match; richer routing is a feature extension)PeerRef::Anyrouting policy - OQ-31:
— resolved (opt-inservices/list-peersre-export semanticsservices/list-peers;services/listis "own ops only")
Open (feature extensions, not blocking):
- OQ-32: Multi-hop federation — the one-hop model is the architectural commitment; multi-hop is a feature extension that doesn't break downstream
- OQ-36: Concrete persistence adapter shapes — the repo/adapter pattern is committed (ADR-033); in-memory adapters ship with core; persistence adapters (SQLite, etc.) are deferred for exploration
- OQ-37: X.509 outgoing-only case — the three auth types (Ed25519, X.509, bearer token) and how X.509 server identity fits the peer model. Not blocking the ADR-029 migration; downstream (HTTP crate phase)
Deferred (not active):
- OQ-09: WASM target boundaries — design constraint, not deliverable
- OQ-10: Git adapter scope — start with smart protocol, add ERC721 later
Document Lifecycle
| Status | Meaning | Transitions |
|---|---|---|
draft |
Under active development. May change significantly. | → reviewed when open questions are resolved |
reviewed |
Architecture is final. Implementation may begin. Changes require review. | → stable when implementation is complete and verified |
stable |
Locked. Changes require review and may warrant an ADR. | → deprecated when superseded |
deprecated |
Superseded. Kept for reference. | Removed when no longer referenced |
References
- Pivot proposal:
docs/research/pivot/alpn-service-architecture.md - Cleanup plan:
docs/research/pivot/cleanup-plan.md - SDD process:
docs/sdd_process.md - Reference implementation:
/workspace/@alkdev/alknet-main/