docs(arch): ADR-035 — concrete persistence adapter shapes, resolve OQ-36
Commits the concrete adapter shape deferred by ADR-033: read-sync / write-async split with honker NOTIFY/LISTEN for no-restart cache invalidation, against SQLite, in a separate alknet-store-sqlite crate. Two constraints drive the design: (1) the hot-path read trait (IdentityProvider::resolve_from_fingerprint, CredentialStore::get) is sync — called in the accept loop, no .await — so a SQLite-backed adapter must cache in memory and serve sync reads from the cache; (2) auth changes must take effect without a restart (an early issue the project already fixed for ConfigIdentityProvider via ArcSwap config reload). honker's SQLite NOTIFY/LISTEN (single-digit-ms wake, no polling) is the cache-invalidation mechanism that makes both hold: write commits to SQLite + emits NOTIFY, the running process's LISTEN wakes, the in-memory index reloads and atomically swaps, the next read sees the new state. Same ArcSwap-reload pattern as config, generalized from 'config file is source of truth' to 'SQLite is source of truth, honker signals when it changed.' New async IdentityStore write trait (put_peer / update_peer / remove_peer) extends the sync IdentityProvider read trait for peer mutations. ConfigIdentityProvider does NOT implement it (config reload is its write path — a posture enforced by the absence of a backend, not a type-system constraint); SqliteIdentityProvider implements both. CredentialStore::put/delete refined to async (within ADR-031's one-way door — the contract was get/put/delete keyed by provider persisting EncryptedData never decrypting; sync-vs-async was unspecified). CredentialStoreError renamed to shared StoreError covering both traits. alknet-store-sqlite is one crate implementing both IdentityStore and CredentialStore with shared SQLite connection + honker LISTEN infra (splitting later is a two-way door). Schema shape committed (one row per PeerEntry with JSON columns for fingerprints/scopes/resources; one row per EncryptedData blob keyed by provider); exact DDL is an implementation-detail two-way door in the adapter crate. The keypal adapter-factory pattern is intentionally not ported to Rust (runtime column-mapping is a TS affordance; in Rust each adapter is a concrete type, cross-cutting concerns are a shared helper module). Amends ADR-031 (put/delete async refinement, StoreError rename), ADR-033 (concrete adapter shape now specified, two-crate framing collapsed to one), ADR-034 (OQ-36 now resolved), auth.md (IdentityStore section, cache-invalidation summary, OQ-36 reference), config.md (two write paths note), and the OQ-36/OQ-34 entries in open-questions.md. Review fixed 4 criticals (error-type name divergence, duplicate IdentityProvider sketch, upsert/Duplicate ambiguity, 'shape unchanged' contradiction), 7 warnings, 5 suggestions.
This commit is contained in:
@@ -706,10 +706,13 @@ See [open-questions.md](../../open-questions.md) for full details.
|
||||
`PeerEntry` supports multiple credential paths (fingerprints +
|
||||
auth_token_hash), `ApiKeyEntry` is for tokens that ARE the identity.
|
||||
See OQ-35 in open-questions.md.
|
||||
- **OQ-36** (open, deferred for exploration): Concrete persistence adapter
|
||||
shapes — the repo/adapter pattern is committed (ADR-033); the in-memory
|
||||
adapters ship with core; the persistence adapter shapes (SQLite, etc.)
|
||||
are deferred for exploration. See OQ-36 in open-questions.md.
|
||||
- **OQ-36** (resolved by ADR-035): Concrete persistence adapter shapes —
|
||||
read-sync / write-async split (`IdentityStore` async write trait
|
||||
extends the sync `IdentityProvider` read trait); SQLite adapter caches
|
||||
in memory and uses honker NOTIFY/LISTEN for no-restart cache
|
||||
invalidation; `alknet-store-sqlite` crate implements both
|
||||
`IdentityStore` and `CredentialStore`. See ADR-035 and OQ-36 in
|
||||
open-questions.md.
|
||||
- **OQ-37** (resolved by ADR-034): X.509 outgoing-only case — three
|
||||
remote roles named (public X.509 endpoint, transport relay, hub).
|
||||
`PeerEntry` asymmetry is correct: a pure-client connection to a public
|
||||
|
||||
Reference in New Issue
Block a user