Review #003 found 11 critical, 14 warning, and 6 suggestion findings
after reviews #001 (governance/security) and #002 (cross-document
consistency/two-way-door audit) were resolved. The theme: types and
APIs that were *referenced* but never *defined*, and stale ADR sketches
that didn't match the now-updated spec docs.
Critical fixes (11):
- C1: DerivedKey #[derive(Deserialize)] contradicted the custom
Deserialize that rejects "[REDACTED]" — dropped the derive, added
explicit manual Serialize/Deserialize impls (protocol.md).
- C2: encrypt prose said "derived at PATHS::ENCRYPTION" but the
signature takes key_version — updated to encryption_path_for_version
(service.md).
- C3: derive_encryption_key returned DerivedKey, derive_encryption_key
_for_version returned EncryptionKey (same cache) — unified on
DerivedKey, defined CachedKey (service.md).
- C4: tokio vs std::sync::RwLock contradiction — specified
std::sync::RwLock, dropped tokio from vault deps (ADR-018, ADR-025,
service.md).
- C5: Missing drift rows in vault README — added #9 (key_version
ignored) and #10 (rotate not implemented).
- C6: ADR-022 build_root_context and invoke() sketches omitted
abort_policy (9 fields vs 10) — added the field to both sketches.
- C7: Capabilities type referenced 20+ times, never defined — added
struct definition to core-types.md with Clone+Send+Sync, Zeroize,
sealed builder API, immutability guard.
- C8: SessionOverlaySource on CallAdapter but never defined, crate
violation (alknet-call can't depend on alknet-agent) — defined the
trait in alknet-call (call-protocol.md), matching the IdentityProvider
pattern.
- C9: CompositeOperationEnv dispatch fall-through was "a two-way door"
— added contains() to OperationEnv trait, made the composite probe
before dispatching, eliminating the sentinel ambiguity.
- C10: No API for Layer 2 (connection overlay) registration, CallConnection
undefined — defined CallConnection struct + register_imported() API
(call-protocol.md).
- C11: with_local signature diverged between two examples (4 args vs 5)
— added capabilities as the 5th arg, made both examples consistent.
Warning fixes (14):
- W1: invoke_with_policy restructured as required method, invoke gets a
default impl delegating to it — eliminates duplication across impls.
- W2: CachedKey defined (service.md).
- W3: EncryptionKey constructor/glue specified, added to re-export list.
- W4: Secp256k1ExtendedPrivKey defined, derive_ethereum_key glue shown.
- W5: encryption_path_for_version rejects version < 2 (v1 is TS PBKDF2).
- W6: Wire payload schemas for all event types + ResponseEnvelope →
EventEnvelope conversion table (call-protocol.md).
- W7: Timeout section — deadline on OperationContext, composed calls
inherit parent's deadline, CallAdapter::with_timeout().
- W8: Request ID generation spec — UUID v4 for composed calls, wire ID
vs internal ID relationship for abort cascade.
- W9: unlock_new already-unlocked behavior specified (returns
AlreadyUnlocked).
- W10: KeyType Serialize/Deserialize justification corrected (stale
irpc reference removed).
- W11: OperationProvenance and CompositionAuthority defined inline in
operation-registry.md (were only in ADR-022).
- W12: encrypt/decrypt free functions marked pub(crate), relationship
to VaultServiceHandle methods stated.
- W13: rotate signature removed from encryption.md (it's a
VaultServiceHandle method, not a free function).
- W14: CallAdapter::new() + with_session_source() + with_timeout()
constructors shown.
Suggestion fixes (6): Seed: Clone note, VaultServiceInner invariant,
ExtendedPrivKey accessor signatures, CURRENT_KEY_VERSION location, ADR-018
stale actor text, derivation helpers re-export note.
The password-manager pattern (deterministic per-site passwords from HD
derivation) is not relevant to an RPC system's vault. Handlers call APIs
(using API keys, OAuth tokens, mTLS), not websites with passwords. The
vault is for cryptographic key derivation and credential encryption.
Removes:
- derive_password, derive_password_string from service.md
- site_password_path from mnemonic-derivation.md
- m/74'/1'/0'/{hash}' path from PATHS module and path semantics table
- derive_password row from the cache table
Resolves review #002 C9 (site_password_path hash mapping underspecified)
by removing the feature rather than specifying the non-standard
string→u32 mapping and Ed25519-as-password-entropy construction.
If deterministic password generation is ever needed (browser-automation
edge case), it can be re-added — the cost is near-zero. Removing it now
eliminates permanent API surface inherited from a prior project's
password-manager pattern.
Drops irpc from alknet-vault entirely. The vault's dispatch is now direct
method calls on VaultServiceHandle — no VaultProtocol enum, no
VaultMessage, no VaultServiceActor, no mpsc channel, no Service trait, no
RemoteService trait, no postcard serialization. The vault is local-only by
construction.
The core security argument: irpc made the vault remote-capable by default
(RemoteService generated unless no_rpc is passed). The IrohProtocol handler
forwards all messages without auth. The docs framed 'register an ALPN' as a
server-setup change. This is the default-insecure anti-pattern — security
should be opt-in, not opt-out. ADR-025 inverts the default: local-only is
the only mode, and remote access requires building a separate vault-server
crate (a visible architectural act, not a flag flip).
The actor path was already dead code — service.md said 'prefer
VaultServiceHandle directly — no channel, no serialization.' The actor
existed only to make irpc's Service trait work, which existed only to make
RemoteService work, which was the footgun. VaultServiceHandle's
Arc<RwLock> provides concurrent reads and exclusive writes — better
throughput than the actor's sequential processing.
DerivedKey serialization simplifies: always redact on serialize (for
logging safety), reject '[REDACTED]' on deserialize with an error. No
'postcard preserves bytes' path. This resolves review #002 W8 (silent
corruption on JSON-deserialized DerivedKey).
Resolves:
- OQ-21: remote vault access — resolved (not deferred). Not a vault crate
feature; if needed, a separate vault-server crate with its own ADR.
- C7: vault-server-crate question decided — not created now, not precluded.
- C8: operation access policy table dissolved — all operations local-only
by default; if a vault-server crate exposes some remotely, that crate
defines the policy.
- W8: DerivedKey JSON deserialization — resolved (reject redacted payloads).
Amends ADR-005 (irpc remains for alknet-call, not for alknet-vault),
ADR-018 (vault is even more standalone — zero RPC framework deps),
ADR-019 (vault is the only layer, not just the only direct-caller layer),
ADR-008 (vault integration point unchanged, but now local-only by
construction).