tasks: decompose ADR-029/030/031/032/034/035 source sync into 17 tasks
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.
This commit is contained in:
114
tasks/core/config-identity-provider-peerentry.md
Normal file
114
tasks/core/config-identity-provider-peerentry.md
Normal file
@@ -0,0 +1,114 @@
|
||||
---
|
||||
id: core/config-identity-provider-peerentry
|
||||
name: Rewrite ConfigIdentityProvider resolution to use PeerEntry multi-credential path (ADR-030)
|
||||
status: pending
|
||||
depends_on: [core/peer-entry-model]
|
||||
scope: narrow
|
||||
risk: medium
|
||||
impact: component
|
||||
level: 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)
|
||||
|
||||
```rust
|
||||
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)
|
||||
|
||||
```rust
|
||||
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_fingerprint` helper → `config_with_peer_entry` helper
|
||||
- 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 `PeerEntry` in the new config
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `ConfigIdentityProvider::resolve_from_fingerprint` delegates to `AuthPolicy::resolve_identity_from_fingerprint` (PeerEntry path)
|
||||
- [ ] `ConfigIdentityProvider::resolve_from_token` delegates to `AuthPolicy::resolve_identity_from_token` (PeerEntry.auth_token_hash → fall through to ApiKeyEntry)
|
||||
- [ ] `ConfigIdentityProvider` reads from ArcSwap on every call (hot-reloadable — unchanged)
|
||||
- [ ] `ConfigIdentityProvider` does NOT implement `IdentityStore`
|
||||
- [ ] 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_fingerprints` references)
|
||||
- [ ] 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-core` succeeds
|
||||
- [ ] `cargo clippy -p alknet-core` succeeds 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 wires `ConfigIdentityProvider` to the
|
||||
> new `AuthPolicy` methods. The semantic shift: `Identity.id` changes from the
|
||||
> fingerprint to the `peer_id` on 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
|
||||
Reference in New Issue
Block a user