Decompose the source-to-spec sync for the core and call crates into atomic, dependency-ordered tasks for implementation agents: Core (7 tasks + review): - peer-entry-model: PeerEntry struct, AuthPolicy.peers (ADR-030 keystone) - credential-store-trait: CredentialStore/InMemoryCredentialStore/StoreError (ADR-031/035) - identity-store-trait: IdentityStore async write trait (ADR-035) - config-identity-provider-peerentry: ConfigIdentityProvider PeerEntry resolution (ADR-030) - fingerprint-normalization: ed25519:hex for raw keys across quinn/iroh (ADR-030 §6) - three-remote-roles-docs: document ADR-034 roles and verifier selection - review-core-sync: phase gate before call consumes new identity semantics Call (9 tasks + review): - retire-remote-safe: remove ADR-028 machinery, AccessControl is the gate (ADR-029 §3) - operation-context-forwarded-for: forwarded_for field, wire-ingress only (ADR-032) - peer-composite-env: PeerCompositeEnv, PeerId=Identity.id, remove UUID (ADR-029/030) - operation-env-invoke-peer: invoke_peer/peer_contains/PeerRef (ADR-029 §2) - services-list-accesscontrol-filtered: AccessControl filter, list-peers opt-in (ADR-029 §6) - call-client-verifier-selection: TLS client-auth, verifier by PeerEntry (OQ-29, ADR-034) - from-call-forwarded-for: populate forwarded_for, peer-keyed registration (ADR-029 §5, ADR-032) - dispatch-peer-identity: AccessControl::check(peer_identity), PeerId from resolution (ADR-029 §3, ADR-030 §5) - review-call-sync: phase gate for the call sync Validated: 58 tasks, no cycles, logical topo order, two review checkpoints.
5.1 KiB
id, name, status, depends_on, scope, risk, impact, level
| id | name | status | depends_on | scope | risk | impact | level | |
|---|---|---|---|---|---|---|---|---|
| core/config-identity-provider-peerentry | Rewrite ConfigIdentityProvider resolution to use PeerEntry multi-credential path (ADR-030) | pending |
|
narrow | medium | component | implementation |
Description
Rewrite ConfigIdentityProvider::resolve_from_fingerprint and
resolve_from_token to use the new PeerEntry-based resolution from
core/peer-entry-model. This is the resolution-logic half of the ADR-030
change — the data model (PeerEntry struct, AuthPolicy.peers) lands in
core/peer-entry-model; this task updates the ConfigIdentityProvider methods
that delegate to AuthPolicy.
Current state (pre-ADR-030)
impl IdentityProvider for ConfigIdentityProvider {
fn resolve_from_fingerprint(&self, fingerprint: &str) -> Option<Identity> {
let config = self.dynamic.load();
config.auth.resolve_identity_from_fingerprint(fingerprint)
}
fn resolve_from_token(&self, token: &AuthToken) -> Option<Identity> {
let config = self.dynamic.load();
let token_str = String::from_utf8_lossy(&token.raw);
config.auth.resolve_api_key(&token_str) // ← only ApiKeyEntry path
}
}
Target state (ADR-030)
impl IdentityProvider for ConfigIdentityProvider {
fn resolve_from_fingerprint(&self, fingerprint: &str) -> Option<Identity> {
let config = self.dynamic.load();
config.auth.resolve_identity_from_fingerprint(fingerprint)
// Now resolves: fingerprint → PeerEntry → Identity { id: peer_id, ... }
}
fn resolve_from_token(&self, token: &AuthToken) -> Option<Identity> {
let config = self.dynamic.load();
let token_str = String::from_utf8_lossy(&token.raw);
config.auth.resolve_identity_from_token(&token_str)
// Now tries PeerEntry.auth_token_hash first, falls through to ApiKeyEntry
}
}
The key change in resolve_from_token: it now calls
AuthPolicy::resolve_identity_from_token (added in core/peer-entry-model),
which tries the PeerEntry.auth_token_hash path first (token is one credential
path among several for a stable logical peer → Identity.id = peer_id), then
falls through to resolve_api_key (token IS the identity → Identity.id = prefix).
ConfigIdentityProvider stays read-only
ConfigIdentityProvider still reads from ArcSwap<DynamicConfig> on every call
(hot-reloadable). It does NOT implement IdentityStore (that's
core/identity-store-trait — config reload is its write path, not a method
call).
Test migration
The existing auth.rs tests use authorized_fingerprints: HashSet<String> and
expect Identity.id == fingerprint. These must migrate to the PeerEntry model:
config_with_fingerprinthelper →config_with_peer_entryhelper- Fingerprint resolution tests expect
Identity.id == peer_id(not the fingerprint) - Add token-resolution-via-PeerEntry tests (auth_token_hash path)
- Config reload tests use
PeerEntryin the new config
Acceptance Criteria
ConfigIdentityProvider::resolve_from_fingerprintdelegates toAuthPolicy::resolve_identity_from_fingerprint(PeerEntry path)ConfigIdentityProvider::resolve_from_tokendelegates toAuthPolicy::resolve_identity_from_token(PeerEntry.auth_token_hash → fall through to ApiKeyEntry)ConfigIdentityProviderreads from ArcSwap on every call (hot-reloadable — unchanged)ConfigIdentityProviderdoes NOT implementIdentityStore- Fingerprint resolution returns
Identity { id: peer_id, ... }(stable, not the fingerprint) - Token resolution: PeerEntry.auth_token_hash match →
Identity { id: peer_id }; no match → ApiKeyEntry fall-through →Identity { id: prefix } - All existing auth.rs tests migrated to PeerEntry model (no
authorized_fingerprintsreferences) - Unit test: fingerprint resolution via PeerEntry (known → Some with peer_id, unknown → None)
- Unit test: token resolution via PeerEntry.auth_token_hash (matching → Some with peer_id)
- Unit test: token resolution falls through to ApiKeyEntry when no PeerEntry matches
- Unit test: config reload changes resolution results immediately (PeerEntry model)
- Unit test: disabled PeerEntry returns None
cargo test -p alknet-coresucceedscargo clippy -p alknet-coresucceeds with no warnings
References
- docs/architecture/crates/core/auth.md — ConfigIdentityProvider, multi-credential resolution
- docs/architecture/crates/core/config.md — AuthPolicy.peers, PeerEntry
- docs/architecture/decisions/030-peerentry-and-identity-id-decoupling.md — ADR-030 §2 (resolution semantics)
Notes
This is the resolution-logic half of the ADR-030 change. The data model lands in
core/peer-entry-model; this task wiresConfigIdentityProviderto the newAuthPolicymethods. The semantic shift:Identity.idchanges from the fingerprint to thepeer_idon the fingerprint path. The token path gains a new first-try (PeerEntry.auth_token_hash) before the existing ApiKeyEntry fall-through. ConfigIdentityProvider stays read-only and ArcSwap-backed.
Summary
To be filled on completion