Rename the crate from alknet-vault to alkvault across source and docs: - Cargo.toml: package name and lib name (alknet_vault -> alkvault) - src/ doc comments and doc-test use statements - tests/ use statements and one string literal Convert references to non-vault alknet ADRs (003, 005, 008, 010, 014, 064) that were broken local links into @alkdev/alknet: cross-repo references, matching the alktype sibling pattern. Local ADR/OQ references are now proper links. Rewrote monorepo path references (crates/alknet-vault/src/...) to the flat layout (src/...). Fixed sdd_process.md package name (@alkdev/storage -> @alkdev/alkvault). ADR/OQ renumbering is deferred to a subsequent pass per the alknet- origin numbering convention. Generic prose 'vault' and type names (VaultServiceHandle, VaultServiceError, etc.) are unchanged. Build, 108 tests, and clippy all pass clean.
170 lines
8.4 KiB
Markdown
170 lines
8.4 KiB
Markdown
---
|
|
status: stable
|
|
last_updated: 2026-06-23
|
|
---
|
|
|
|
# alkvault
|
|
|
|
Local key vault: BIP39 mnemonic generation, SLIP-0010 Ed25519 HD key
|
|
derivation, BIP-0032 secp256k1 derivation (feature-gated), and AES-256-GCM
|
|
encryption. Holds the master seed — the root of trust for all derived keys
|
|
and encrypted credentials in the alknet system.
|
|
|
|
## What This Crate Is
|
|
|
|
alkvault is a **standalone crate** with zero alknet crate dependencies
|
|
(ADR-018) and zero RPC framework dependencies (ADR-025). It provides the
|
|
cryptographic primitives and runtime API for managing the root of trust.
|
|
The CLI binary (the `alknet` crate) is the sole component that talks to the
|
|
vault directly (ADR-019) — handlers receive derived/decrypted material
|
|
through capabilities, never through a vault reference.
|
|
|
|
The vault is **not a network service**. It has no ALPN, no
|
|
`ProtocolHandler` implementation, no operations registered in the call
|
|
protocol (ADR-008, ADR-014), and no remote dispatch capability (ADR-025).
|
|
The vault is **local-only by construction** — direct method calls on
|
|
`VaultServiceHandle`, no actor, no message enum, no wire format. The master
|
|
seed and derived private keys never cross the network.
|
|
|
|
## Documents
|
|
|
|
| Document | Status | Description |
|
|
|----------|--------|-------------|
|
|
| [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
|
|
|
|
| ADR | Title | Relevance |
|
|
|-----|-------|-----------|
|
|
| [018](decisions/018-vault-standalone-crate.md) | Vault as Standalone Crate | Zero alknet crate dependencies |
|
|
| [019](decisions/019-vault-assembly-layer-only.md) | Vault Assembly-Layer-Only Access | The assembly layer is the sole caller |
|
|
| [020](decisions/020-hd-derivation-for-encryption-keys.md) | HD Derivation for Encryption Keys | SLIP-0010 derivation, not PBKDF2; salt unused in v2 |
|
|
| [021](decisions/021-key-rotation-via-version-indexed-paths.md) | Key Rotation via Version-Indexed Paths | Version-indexed paths; `rotate` re-encrypts |
|
|
| [025](decisions/025-vault-local-only-dispatch.md) | Vault Local-Only Dispatch | Dropped irpc; direct method calls; local-only by construction |
|
|
| [026](decisions/026-vault-key-model-hd-derivation.md) | Vault Key Model — HD Derivation | HD derivation from BIP39 seed; `74'` coin type; AES-256-GCM |
|
|
|
|
### Related ADRs in the parent `@alkdev/alknet` repo
|
|
|
|
These ADRs are not copied here (they are not vault-specific), but the
|
|
vault's design depends on them. They live in the parent `@alkdev/alknet`
|
|
workspace.
|
|
|
|
| ADR | Title | Relevance |
|
|
|-----|-------|-----------|
|
|
| `@alkdev/alknet: docs/architecture/decisions/003-crate-decomposition.md` | Crate Decomposition | alkvault's standalone position |
|
|
| `@alkdev/alknet: docs/architecture/decisions/008-secret-service-integration.md` | Vault Integration Point | CLI-embedded, capability source |
|
|
| `@alkdev/alknet: docs/architecture/decisions/010-alpn-router-and-endpoint.md` | ALPN Router and Endpoint | Ed25519 as default curve for TLS raw key identity |
|
|
| `@alkdev/alknet: docs/architecture/decisions/014-secret-material-flow-and-capability-injection.md` | Secret Material Flow and Capability Injection | Capabilities carry vault-derived material |
|
|
|
|
## Relevant Open Questions
|
|
|
|
| OQ | Title | Status | Relevance |
|
|
|----|-------|--------|-----------|
|
|
| OQ-20 | Encryption key derivation | resolved (ADR-020) | HD derivation from seed; salt field unused in v2 |
|
|
| OQ-21 | Remote vault access | resolved (ADR-025) | Vault is local-only by construction; remote access requires a separate vault-server crate with its own ADR |
|
|
| OQ-22 | Key rotation mechanism | resolved (ADR-021) | Version-indexed paths; `rotate` method |
|
|
|
|
## Key Design Principles
|
|
|
|
1. **Standalone**: The vault depends on no alknet crate and no RPC framework.
|
|
It defines its own types and errors. External crates depend on the vault;
|
|
the vault depends on nothing in alknet.
|
|
2. **Assembly-layer only**: The vault's API is consumed by the CLI binary,
|
|
not by handlers. Handlers receive material through capabilities
|
|
(ADR-014). The vault is not on the wire.
|
|
3. **Local-only by construction**: The vault has no remote dispatch
|
|
capability. Direct method calls on `VaultServiceHandle` — no actor, no
|
|
message enum, no wire format (ADR-025). Remote access, if ever needed,
|
|
requires a separate crate with its own ADR.
|
|
4. **Zeroize everything sensitive**: The mnemonic, seed, derived private
|
|
keys, encryption keys, and cached keys all implement `Zeroize` and
|
|
`ZeroizeOnDrop`. Secret material does not linger in freed heap memory.
|
|
5. **Deterministic derivation**: The same mnemonic + passphrase + path
|
|
always produces the same key. Derivation is reproducible across runs
|
|
and across nodes.
|
|
6. **OsRng for nonces**: AES-GCM IVs and any cryptographic nonces use
|
|
`OsRng` (or equivalent CSPRNG), never `rand::random()`. IV reuse under
|
|
the same key is catastrophic for GCM.
|
|
7. **No `unwrap()` or `expect()` outside tests**: vault operations
|
|
propagate errors. A poisoned lock is recovered with
|
|
`unwrap_or_else(|e| e.into_inner())`, not `unwrap()`. A panic in one
|
|
vault operation must not brick the vault for all other operations.
|
|
|
|
## Security Constraints
|
|
|
|
These are security-critical implementation requirements, not architectural
|
|
decisions (the architecture is locked by the ADRs above). They are
|
|
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()`.
|
|
- **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.
|
|
- **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).
|
|
Deserialization rejects `"[REDACTED]"` with an error (resolves review
|
|
#002 W8). The redaction is a defense-in-depth measure for logging safety,
|
|
not the primary control — the primary control is that `DerivedKey` never
|
|
crosses the call protocol wire (ADR-014).
|
|
|
|
## Public API
|
|
|
|
The vault re-exports its primary types from the crate root:
|
|
|
|
```rust
|
|
// Mnemonic and seed
|
|
pub use mnemonic::{Language, Mnemonic, Seed};
|
|
|
|
// Derivation
|
|
pub use derivation::{DerivationError, ExtendedPrivKey, PATHS};
|
|
// Derivation helpers (derive_path_from_seed, parse_derivation_path,
|
|
// device_path, encryption_path_for_version) are accessible as
|
|
// alkvault::derivation::* — not re-exported at crate root to avoid
|
|
// clutter, but fully public.
|
|
|
|
// Encryption
|
|
pub use encryption::{EncryptedData, EncryptionError, EncryptionKey};
|
|
pub use encryption::CURRENT_KEY_VERSION;
|
|
|
|
// Key types (DerivedKey, KeyType)
|
|
pub use protocol::{DerivedKey, KeyType};
|
|
|
|
// Service (runtime)
|
|
pub use service::{VaultServiceError, VaultServiceHandle};
|
|
|
|
// Cache
|
|
pub use cache::CacheConfig;
|
|
```
|
|
|
|
The `secp256k1` feature flag gates Ethereum (BIP-0032) derivation:
|
|
|
|
```rust
|
|
#[cfg(feature = "secp256k1")]
|
|
pub mod ethereum;
|
|
```
|
|
|
|
## References
|
|
|
|
- `@alkdev/alknet: docs/architecture/decisions/003-crate-decomposition.md` —
|
|
crate decomposition (alkvault's standalone position)
|
|
- `@alkdev/alknet: docs/architecture/decisions/008-secret-service-integration.md` —
|
|
vault integration point (CLI-embedded, capability source)
|
|
- `@alkdev/alknet: docs/architecture/decisions/010-alpn-router-and-endpoint.md` —
|
|
ALPN router and endpoint (Ed25519 as default curve for TLS raw key identity)
|
|
- `@alkdev/alknet: docs/architecture/decisions/014-secret-material-flow-and-capability-injection.md` —
|
|
secret material flow and capability injection (capabilities carry
|
|
vault-derived material)
|
|
|
|
> **Note**: Cross-repo references (`@alkdev/alknet: ...`) point to the
|
|
> parent `@alkdev/alknet` workspace where this crate originated. The
|
|
> artifacts are preserved there as historical context; they are not part
|
|
> of this standalone repo. |