docs(architecture): remove derive_password and site_password_path from vault

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.
This commit is contained in:
2026-06-23 06:06:11 +00:00
parent 7dda6eec68
commit 91159bf574
5 changed files with 24 additions and 29 deletions

View File

@@ -125,6 +125,7 @@ truth for drift tracking — if an item is fixed in source, update this table.
| 4 | irpc dependency | `VaultProtocol` enum with `#[rpc_requests]`, `VaultServiceActor`, `Client<VaultProtocol>`, irpc/postcard deps | Remove entirely — direct method calls on `VaultServiceHandle` (ADR-025) | `protocol.rs`, `service.rs`, `Cargo.toml` | [ADR-025](../../decisions/025-vault-local-only-dispatch.md) | | 4 | irpc dependency | `VaultProtocol` enum with `#[rpc_requests]`, `VaultServiceActor`, `Client<VaultProtocol>`, irpc/postcard deps | Remove entirely — direct method calls on `VaultServiceHandle` (ADR-025) | `protocol.rs`, `service.rs`, `Cargo.toml` | [ADR-025](../../decisions/025-vault-local-only-dispatch.md) |
| 5 | `DerivedKey` dual serialization | JSON redacts, postcard preserves bytes | Always redact on serialize; reject `"[REDACTED]"` on deserialize with error (ADR-025, resolves W8) | `protocol.rs` | [protocol.md → Serialization Redaction](protocol.md#serialization-redaction), [ADR-025](../../decisions/025-vault-local-only-dispatch.md) | | 5 | `DerivedKey` dual serialization | JSON redacts, postcard preserves bytes | Always redact on serialize; reject `"[REDACTED]"` on deserialize with error (ADR-025, resolves W8) | `protocol.rs` | [protocol.md → Serialization Redaction](protocol.md#serialization-redaction), [ADR-025](../../decisions/025-vault-local-only-dispatch.md) |
| 6 | `HashMap::clear` zeroization | `KeyCache::clear()` removes entries and relies on `CachedKey`'s `Drop` impl for zeroization | Verify `HashMap::clear()` actually drops values (it does, but worth a test) | `cache.rs` | [service.md → Security Constraints](service.md#security-constraints) | | 6 | `HashMap::clear` zeroization | `KeyCache::clear()` removes entries and relies on `CachedKey`'s `Drop` impl for zeroization | Verify `HashMap::clear()` actually drops values (it does, but worth a test) | `cache.rs` | [service.md → Security Constraints](service.md#security-constraints) |
| 7 | `derive_password` / `site_password_path` | `derive_password`, `derive_password_string`, `site_password_path` methods exist | Remove entirely — password-manager pattern not relevant to RPC system's vault (ADR-025, resolves C9) | `service.rs`, `mnemonic-derivation.rs` | [ADR-025](../../decisions/025-vault-local-only-dispatch.md) |
## Public API ## Public API

View File

@@ -199,9 +199,8 @@ pub mod PATHS {
Helper functions construct parameterized paths: Helper functions construct parameterized paths:
```rust ```rust
pub fn device_path(index: u32) -> String; // m/74'/0'/0'/{index}' pub fn device_path(index: u32) -> String; // m/74'/0'/0'/{index}'
pub fn site_password_path(site_hash: &str) -> String; // m/74'/1'/0'/{site_hash}' pub fn encryption_path_for_version(version: u32) -> String; // m/74'/2'/0'/{version-2}'
pub fn encryption_path_for_version(version: u32) -> String; // m/74'/2'/0'/{version-2}'
``` ```
### Path semantics ### Path semantics
@@ -211,7 +210,6 @@ pub fn encryption_path_for_version(version: u32) -> String; // m/74'/2'/0'/{vers
| `m/74'/0'/0'/0'` | Primary node identity (Ed25519) | Ed25519 | TLS raw key (ADR-010), node identity | | `m/74'/0'/0'/0'` | Primary node identity (Ed25519) | Ed25519 | TLS raw key (ADR-010), node identity |
| `m/74'/0'/0'/{n}'` | Worker/device identity | Ed25519 | Multi-device nodes, workers | | `m/74'/0'/0'/{n}'` | Worker/device identity | Ed25519 | Multi-device nodes, workers |
| `m/74'/0'/1'/0'` | SSH host key | Ed25519 | SSH handler | | `m/74'/0'/1'/0'` | SSH host key | Ed25519 | SSH handler |
| `m/74'/1'/0'/{hash}'` | Site-specific deterministic password | Ed25519 bytes | Per-site passwords (not cached) |
| `m/74'/2'/0'/0'` | Encryption key for external credentials | AES-256-GCM | Credential encryption (v2, see [encryption.md](encryption.md)) | | `m/74'/2'/0'/0'` | Encryption key for external credentials | AES-256-GCM | Credential encryption (v2, see [encryption.md](encryption.md)) |
| `m/44'/60'/0'/0/0` | Ethereum signing key | secp256k1 | Ethereum signing (feature-gated) | | `m/44'/60'/0'/0/0` | Ethereum signing key | secp256k1 | Ethereum signing (feature-gated) |

View File

@@ -156,23 +156,6 @@ Derive a secp256k1 keypair at the given BIP-0032 path. Returns
`UnsupportedKeyType` when the `secp256k1` feature is disabled. Returns a `UnsupportedKeyType` when the `secp256k1` feature is disabled. Returns a
`DerivedKey` with `KeyType::Secp256k1` (33-byte compressed public key). `DerivedKey` with `KeyType::Secp256k1` (33-byte compressed public key).
### derive_password(path, length) → Vec<u8>
```rust
pub fn derive_password(&self, path: &str, length: usize) -> Result<Vec<u8>, VaultServiceError>;
pub fn derive_password_string(&self, path: &str, length: usize) -> Result<String, VaultServiceError>;
```
Derive deterministic password bytes at the given path, truncated to
`length`. This is **not cached** — password derivation is cheap and
passwords are typically one-shot (derive, use, discard). The string
variant base64url-encodes the bytes (URL-safe, no padding).
`derive_password` is the mechanism for per-site deterministic passwords:
the same seed + path always produces the same password. The path includes
a site hash (`site_password_path(site_hash)`) so different sites get
different passwords.
## Encrypt and Decrypt ## Encrypt and Decrypt
### encrypt(plaintext, key_version) → EncryptedData ### encrypt(plaintext, key_version) → EncryptedData
@@ -250,13 +233,8 @@ pub struct CacheConfig {
| `derive_ed25519` | Yes | Derivation is expensive; keys are reused | | `derive_ed25519` | Yes | Derivation is expensive; keys are reused |
| `derive_encryption_key` | Yes | Same — encryption key reused across calls | | `derive_encryption_key` | Yes | Same — encryption key reused across calls |
| `derive_ethereum_key` | Yes | Same | | `derive_ethereum_key` | Yes | Same |
| `derive_password` | No | Cheap derivation; passwords are one-shot |
| `encrypt` / `decrypt` | Key cached | The encryption key (at `PATHS::ENCRYPTION`) is cached; the plaintext is not | | `encrypt` / `decrypt` | Key cached | The encryption key (at `PATHS::ENCRYPTION`) is cached; the plaintext is not |
`derive_password` does not cache because it's a truncation of derived
bytes, not a keypair that's reused. Caching it would grow the cache with
unique paths (one per site hash) for no reuse benefit.
## Dispatch ## Dispatch
The vault uses **direct method calls** on `VaultServiceHandle` — no actor, The vault uses **direct method calls** on `VaultServiceHandle` — no actor,
@@ -316,7 +294,7 @@ alknet-core error types at the assembly boundary (ADR-018).
| RwLock for thread safety | — | Multiple readers (derive), exclusive writer (unlock/lock) | | RwLock for thread safety | — | Multiple readers (derive), exclusive writer (unlock/lock) |
| TTL + LRU cache | — | Bounded memory, fresh keys, zeroized eviction | | TTL + LRU cache | — | Bounded memory, fresh keys, zeroized eviction |
| Direct method calls (no actor) | [ADR-025](../../decisions/025-vault-local-only-dispatch.md) | No irpc, no message enum, no remote dispatch capability | | Direct method calls (no actor) | [ADR-025](../../decisions/025-vault-local-only-dispatch.md) | No irpc, no message enum, no remote dispatch capability |
| `derive_password` not cached | — | One-shot; caching grows cache with no reuse | | `derive_password` removed | [ADR-025](../../decisions/025-vault-local-only-dispatch.md) | Password-manager pattern not relevant to RPC system's vault; resolves C9 |
## Open Questions ## Open Questions

View File

@@ -20,8 +20,8 @@ perspective?**
The vault provides a `VaultServiceHandle` with `unlock`, `lock`, The vault provides a `VaultServiceHandle` with `unlock`, `lock`,
`derive_ed25519`, `derive_encryption_key`, `derive_ethereum_key`, `derive_ed25519`, `derive_encryption_key`, `derive_ethereum_key`,
`derive_password`, `encrypt`, and `decrypt` methods. Who is allowed to call `encrypt`, and `decrypt` methods. Who is allowed to call these, and
these, and through what path? through what path?
The candidates: The candidates:

View File

@@ -203,6 +203,19 @@ version of ADR-018's intent.
exclusive writes (unlock/lock). The actor's sequential processing was exclusive writes (unlock/lock). The actor's sequential processing was
actually *worse* for throughput than the RwLock. Removing the actor actually *worse* for throughput than the RwLock. Removing the actor
makes the concurrency model visible and correct. makes the concurrency model visible and correct.
- `derive_password` and `site_password_path` are removed from the vault's
API and path model. 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. This 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 or implemented as a
separate concern — the cost is near-zero, and removing it now eliminates
permanent API surface that was inherited from a prior project's
password-manager pattern.
**Negative:** **Negative:**
@@ -247,6 +260,11 @@ version of ADR-018's intent.
in protocol.md goes away. If a future vault-server crate exposes some in protocol.md goes away. If a future vault-server crate exposes some
operations remotely, *that crate* defines the access policy in its own operations remotely, *that crate* defines the access policy in its own
ADR. ADR.
- **C9 (site_password_path hash mapping underspecified)**: resolved. The
`derive_password` / `derive_password_string` / `site_password_path`
methods are removed from the vault's API. The password-manager pattern
is not relevant to an RPC system's vault. No hash mapping to specify,
no Ed25519-as-password-entropy question to answer.
## Assumptions ## Assumptions