From 323ee85d40d52c077e067bc7f28f53657265e907 Mon Sep 17 00:00:00 2001 From: "glm-5.2" Date: Tue, 23 Jun 2026 14:15:13 +0000 Subject: [PATCH] docs(vault): remove drift tracking artifacts, bump vault docs to stable The vault spec-to-implementation sync is complete. Remove the drift tracking tools that were only needed during sync: - Remove the Known Source Drift table from vault/README.md - Remove 'known drift' / 'current source uses X' prose from Security Constraints sections in vault/README.md, encryption.md, and service.md. The permanent constraint statements (OsRng for IVs, zeroized drop, no unwrap, etc.) are preserved. - Remove the drift paragraph in encryption.md Key Versioning. - Remove stale 'to be updated per ADR-025' / 'postcard tests to be removed' notes in protocol.md References. - Bump status: draft -> stable in the frontmatter of all vault docs (README, mnemonic-derivation, encryption, service, protocol). - Update architecture/README.md: vault doc status entries to stable, Current State paragraph reflects vault implementation complete (no 'pending ADR-025/026 refactor' language). --- docs/architecture/README.md | 12 +++--- docs/architecture/crates/vault/README.md | 38 ++++--------------- docs/architecture/crates/vault/encryption.md | 18 +++------ .../crates/vault/mnemonic-derivation.md | 2 +- docs/architecture/crates/vault/protocol.md | 7 ++-- docs/architecture/crates/vault/service.md | 21 +++------- 6 files changed, 28 insertions(+), 70 deletions(-) diff --git a/docs/architecture/README.md b/docs/architecture/README.md index 54bd75f..08ed0fc 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -7,7 +7,7 @@ last_updated: 2026-06-23 ## Current State -**Pre-implementation.** The project has completed a pivot from a three-layer model to an ALPN-as-service model. The greenfield workspace contains only `alknet-vault` (stable — implementation exists, pending ADR-025/026 refactor to drop irpc and remove derive_password) and research/reference material. Foundational ADRs (001–026) are in place. ADR-024 resolves the registry mutability question and the `OperationContext.env` type identity crisis by layering the registry by trust boundary. ADR-025 drops irpc from the vault, making it local-only by construction. ADR-026 records the HD-derivation key model as a foundational decision. Review #003 (type/API surface completeness) resolved: `DerivedKey` derive contradiction, `encrypt` prose, return-type divergence, RwLock contradiction, drift table gaps, ADR-022 stale sketches, `Capabilities`/`SessionOverlaySource`/`CallConnection`/`CachedKey` definitions, `CompositeOperationEnv` dispatch contract, `with_local` signature, payload schemas, timeout propagation, and request ID generation. The alknet-core, alknet-call, and alknet-vault crate specs are in draft. +**Pre-implementation.** The project has completed a pivot from a three-layer model to an ALPN-as-service model. The greenfield workspace contains only `alknet-vault` (stable — implementation complete and verified, local-only by construction per ADR-025, HD-derivation key model per ADR-026) and research/reference material. Foundational ADRs (001–026) are in place. ADR-024 resolves the registry mutability question and the `OperationContext.env` type identity crisis by layering the registry by trust boundary. ADR-025 drops irpc from the vault, making it local-only by construction. ADR-026 records the HD-derivation key model as a foundational decision. Review #003 (type/API surface completeness) resolved: `DerivedKey` derive contradiction, `encrypt` prose, return-type divergence, RwLock contradiction, drift table gaps, ADR-022 stale sketches, `Capabilities`/`SessionOverlaySource`/`CallConnection`/`CachedKey` definitions, `CompositeOperationEnv` dispatch contract, `with_local` signature, payload schemas, timeout propagation, and request ID generation. The alknet-core and alknet-call crate specs are in draft; the alknet-vault crate specs are stable. **Next step**: Implementation. All open questions are resolved. The specs have passed three review passes (#001 governance/security model, #002 cross-document consistency/two-way-door audit, #003 type/API surface completeness). @@ -25,11 +25,11 @@ last_updated: 2026-06-23 | [crates/call/README.md](crates/call/README.md) | draft | alknet-call crate index | | [crates/call/call-protocol.md](crates/call/call-protocol.md) | draft | CallAdapter, EventEnvelope framing, stream model, PendingRequestMap, bidirectional calls, streaming subscribe example | | [crates/call/operation-registry.md](crates/call/operation-registry.md) | draft | OperationSpec, Handler, OperationRegistry, AccessControl, capability injection, service discovery, irpc integration | -| [crates/vault/README.md](crates/vault/README.md) | draft | alknet-vault crate index | -| [crates/vault/mnemonic-derivation.md](crates/vault/mnemonic-derivation.md) | draft | BIP39, SLIP-0010, BIP-0032, derivation paths, key types | -| [crates/vault/encryption.md](crates/vault/encryption.md) | draft | AES-256-GCM, EncryptedData, key versioning, salt (Phase B reserved) | -| [crates/vault/service.md](crates/vault/service.md) | draft | VaultServiceHandle lifecycle, actor dispatch, cache, error model | -| [crates/vault/protocol.md](crates/vault/protocol.md) | draft | DerivedKey redaction, KeyType, serialization behavior | +| [crates/vault/README.md](crates/vault/README.md) | stable | alknet-vault crate index | +| [crates/vault/mnemonic-derivation.md](crates/vault/mnemonic-derivation.md) | stable | BIP39, SLIP-0010, BIP-0032, derivation paths, key types | +| [crates/vault/encryption.md](crates/vault/encryption.md) | stable | AES-256-GCM, EncryptedData, key versioning, salt (Phase B reserved) | +| [crates/vault/service.md](crates/vault/service.md) | stable | VaultServiceHandle lifecycle, direct dispatch, cache, error model | +| [crates/vault/protocol.md](crates/vault/protocol.md) | stable | DerivedKey redaction, KeyType, serialization behavior | ## ADR Table diff --git a/docs/architecture/crates/vault/README.md b/docs/architecture/crates/vault/README.md index 6871416..1f7ed4c 100644 --- a/docs/architecture/crates/vault/README.md +++ b/docs/architecture/crates/vault/README.md @@ -1,5 +1,5 @@ --- -status: draft +status: stable last_updated: 2026-06-23 --- @@ -30,10 +30,10 @@ seed and derived private keys never cross the network. | Document | Status | Description | |----------|--------|-------------| -| [mnemonic-derivation.md](mnemonic-derivation.md) | draft | BIP39, SLIP-0010, BIP-0032, derivation paths, key types | -| [encryption.md](encryption.md) | draft | AES-256-GCM, EncryptedData, key versioning, HD derivation (ADR-020) | -| [service.md](service.md) | draft | VaultServiceHandle lifecycle, direct dispatch, cache, error model | -| [protocol.md](protocol.md) | draft | DerivedKey redaction, KeyType, serialization behavior | +| [mnemonic-derivation.md](mnemonic-derivation.md) | stable | BIP39, SLIP-0010, BIP-0032, derivation paths, key types | +| [encryption.md](encryption.md) | stable | AES-256-GCM, EncryptedData, key versioning, HD derivation (ADR-020) | +| [service.md](service.md) | stable | VaultServiceHandle lifecycle, direct dispatch, cache, error model | +| [protocol.md](protocol.md) | stable | DerivedKey redaction, KeyType, serialization behavior | ## Applicable ADRs @@ -92,17 +92,13 @@ documented here so implementation agents don't miss them. See [service.md → Security Constraints](service.md#security-constraints) for the full list. -- **OsRng for IVs**: AES-GCM IVs must use `OsRng`, not `rand::random()`. The - current source uses `rand::random()` — this is a known drift from the - spec and must be corrected during implementation sync. +- **OsRng for IVs**: AES-GCM IVs must use `OsRng`, not `rand::random()`. - **Zeroized drop**: `Seed`, `Mnemonic`, `ExtendedPrivKey`, `Secp256k1ExtendedPrivKey`, `EncryptionKey`, `CachedKey`, and `DerivedKey` all derive `Zeroize` and `ZeroizeOnDrop`. The cache must clear on drop, not just on explicit `lock()`. - **No `unwrap()` outside tests**: poisoned lock recovery uses - `unwrap_or_else(|e| e.into_inner())` or explicit error propagation. The - current source uses `unwrap()` in `VaultServiceHandle` methods — this - is a known drift and must be corrected. + `unwrap_or_else(|e| e.into_inner())` or explicit error propagation. - **DerivedKey redaction in serialization**: `DerivedKey` serializes the `private_key` as `"[REDACTED]"` in all formats (ADR-025 dropped the postcard/remote path that previously preserved bytes in binary formats). @@ -111,26 +107,6 @@ the full list. not the primary control — the primary control is that `DerivedKey` never crosses the call protocol wire (ADR-014). -## Known Source Drift - -The vault crate carries over source from the POC. The following items are -known divergences between the current source and the spec. All must be -corrected during implementation sync. This table is the single source of -truth for drift tracking — if an item is fixed in source, update this table. - -| # | Item | Current source behavior | Target behavior (per spec) | Source location | Spec reference | -|---|------|------------------------|-----------------------------|-----------------|----------------| -| 1 | IV generation | `rand::random()` | `OsRng` (CSPRNG) | `encryption.rs` L133 | [encryption.md → Security Constraints](encryption.md#security-constraints), [service.md → Security Constraints](service.md#security-constraints) | -| 2 | RwLock `unwrap()` | `unwrap()` on every `RwLock` acquisition (L142, 161, 182, 191, 196, 227, 264, 307, 340, 367) | `unwrap_or_else(\|e\| e.into_inner())` for poisoned lock recovery | `service.rs` (see line numbers) | [service.md → Security Constraints](service.md#security-constraints) | -| 3 | `CURRENT_KEY_VERSION` | `1` (HD-derived, but v1 is reserved for TS PBKDF2 legacy per ADR-020) | `2` (HD-derived, per ADR-020) | `encryption.rs` | [encryption.md → Key Versioning](encryption.md#key-versioning), [ADR-020](../../decisions/020-hd-derivation-for-encryption-keys.md) | -| 4 | irpc dependency | `VaultProtocol` enum with `#[rpc_requests]`, `VaultServiceActor`, `Client`, 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) | -| 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) | -| 8 | `unlock_new` return type | Returns `String` (not zeroized on drop) | Return `Zeroizing` — the mnemonic is the root of trust and must not linger in freed memory (resolves W7) | `service.rs` | [service.md → unlock_new](service.md#unlock_newword_count--phrase) | -| 9 | `key_version` ignored in encrypt/decrypt | `encrypt`/`decrypt` always derive at `PATHS::ENCRYPTION` regardless of `key_version` | Derive at `encryption_path_for_version(key_version)` — encrypt stamps the passed version, decrypt selects the key by the blob's version (ADR-021) | `service.rs` | [service.md → encrypt](service.md#encryptplaintext-key_version--encrypteddata), [ADR-021](../../decisions/021-key-rotation-via-version-indexed-paths.md) | -| 10 | `rotate` not implemented | No `rotate` method exists | Implement `rotate(encrypted, to_version)` — decrypt with old version's key, re-encrypt with new version's key (ADR-021) | `service.rs` | [service.md → rotate](service.md#rotateencrypted-to_version--encrypteddata), [ADR-021](../../decisions/021-key-rotation-via-version-indexed-paths.md) | - ## Public API The vault re-exports its primary types from the crate root: diff --git a/docs/architecture/crates/vault/encryption.md b/docs/architecture/crates/vault/encryption.md index 1537216..4dbd963 100644 --- a/docs/architecture/crates/vault/encryption.md +++ b/docs/architecture/crates/vault/encryption.md @@ -1,5 +1,5 @@ --- -status: draft +status: stable last_updated: 2026-06-23 --- @@ -219,12 +219,6 @@ Rotation decrypts with the old version's key and re-encrypts with the new version's key. No new mnemonic needed — the same seed produces all version keys via different paths. See ADR-021 for the full mechanism. -**The current source uses `CURRENT_KEY_VERSION = 1` with HD derivation and -does not implement version-indexed paths or `rotate`.** These are drift -items to be corrected during implementation sync. See ADR-020 (version -bump to 2) and ADR-021 (rotation mechanism). See the [Known Source -Drift](README.md#known-source-drift) table in the vault README. - ## Errors ```rust @@ -281,12 +275,10 @@ These are security-critical implementation requirements. - **OsRng for IVs**: The IV must be generated with `OsRng` (or an equivalent CSPRNG), never `rand::random()`. IV reuse under the same key is catastrophic for GCM — it breaks authenticity and creates a - two-time-pad on the plaintext. **The current source uses - `rand::random()` for IV generation (`encryption.rs` line 133) — this is a - known drift from the spec and must be corrected during implementation - sync.** `rand::random()` uses the thread-local RNG which may not be a - CSPRNG on all platforms; `OsRng` reads from the operating system's - entropy source and is the correct choice for cryptographic nonces. + two-time-pad on the plaintext. `rand::random()` uses the thread-local RNG + which may not be a CSPRNG on all platforms; `OsRng` reads from the + operating system's entropy source and is the correct choice for + cryptographic nonces. - **Zeroized drop**: `EncryptionKey` derives `Zeroize` and `ZeroizeOnDrop`. The key bytes are zeroized before deallocation. Do not store key material in types that don't zeroize. diff --git a/docs/architecture/crates/vault/mnemonic-derivation.md b/docs/architecture/crates/vault/mnemonic-derivation.md index 5f77d74..4056434 100644 --- a/docs/architecture/crates/vault/mnemonic-derivation.md +++ b/docs/architecture/crates/vault/mnemonic-derivation.md @@ -1,5 +1,5 @@ --- -status: draft +status: stable last_updated: 2026-06-23 --- diff --git a/docs/architecture/crates/vault/protocol.md b/docs/architecture/crates/vault/protocol.md index dc7f695..20801c4 100644 --- a/docs/architecture/crates/vault/protocol.md +++ b/docs/architecture/crates/vault/protocol.md @@ -1,5 +1,5 @@ --- -status: draft +status: stable last_updated: 2026-06-23 --- @@ -236,9 +236,8 @@ ADR-025 and [open-questions.md](../../open-questions.md). ## References -- Implementation: `crates/alknet-vault/src/protocol.rs` (to be updated - per ADR-025 — remove `VaultProtocol` enum and irpc usage) +- Implementation: `crates/alknet-vault/src/protocol.rs` - Tests: `crates/alknet-vault/src/protocol.rs` (unit tests for redaction - and zeroize behavior; postcard tests to be removed) + and zeroize behavior) - [service.md](service.md) — `VaultServiceHandle` runtime API - [mnemonic-derivation.md](mnemonic-derivation.md) — what `KeyType` means \ No newline at end of file diff --git a/docs/architecture/crates/vault/service.md b/docs/architecture/crates/vault/service.md index 90e7a81..d1af6f9 100644 --- a/docs/architecture/crates/vault/service.md +++ b/docs/architecture/crates/vault/service.md @@ -1,5 +1,5 @@ --- -status: draft +status: stable last_updated: 2026-06-23 --- @@ -356,29 +356,20 @@ don't miss them. - **OsRng for IVs**: AES-GCM IVs and any cryptographic nonces must use `OsRng` (or equivalent CSPRNG), not `rand::random()`. IV reuse under the same key is catastrophic for GCM (authenticity breaks, two-time-pad on - plaintext). **The current source uses `rand::random()` for IV generation - in `encryption::encrypt()` — this is a known drift and must be corrected - during implementation sync.** + plaintext). - **Zeroized drop**: `Seed`, `Mnemonic`, `CachedKey`, `EncryptionKey`, `ExtendedPrivKey`, `Secp256k1ExtendedPrivKey`, and `DerivedKey` all derive `Zeroize` and `ZeroizeOnDrop`. The cache must clear on drop, not - just on explicit `lock()`. **The current `KeyCache::clear()` removes - entries but relies on `CachedKey`'s `Drop` impl for zeroization — - verify that `HashMap::clear()` actually drops the values (it does, but - this is worth a test).** + just on explicit `lock()`. - **No `unwrap()` or `expect()` outside tests**: poisoned lock recovery uses `unwrap_or_else(|e| e.into_inner())` or explicit error propagation. A panic in one vault operation must not brick the vault for all other - operations. **The current source uses `unwrap()` on every `RwLock` - acquisition in `VaultServiceHandle` (lines 142, 161, 182, 191, 196, 227, - 264, 307, 340, 367) — this is a known drift and must be corrected. A - poisoned lock should be recovered with `unwrap_or_else(|e| - e.into_inner())`, not panicked.** + operations. A poisoned lock should be recovered with + `unwrap_or_else(|e| e.into_inner())`, not panicked. - **`DerivedKey` is move-only, not `Clone`**: `DerivedKey` does not derive `Clone`. It is move-only — consumers receive it by value and zeroize it when done (handled by `#[zeroize(drop)]`). This prevents accidental - duplication of secret material. **The current source does not derive - `Clone` on `DerivedKey` — this is correct.** + duplication of secret material. - **Cache eviction zeroizes**: when the cache evicts an entry (LRU or TTL), the `CachedKey` is dropped, which triggers `ZeroizeOnDrop`. Do not replace `CachedKey` with a type that doesn't zeroize.