# alkvault
Local key vault: BIP39 mnemonic generation, SLIP-0010 Ed25519 HD key
derivation, and AES-256-GCM encryption for securing provider keys,
credentials, and identity material.
`alkvault` is a standalone crate with **zero network dependencies**. It
holds the master seed — the root of trust for all derived keys and
encrypted credentials — and provides the cryptographic primitives and a
runtime API for managing it. Nothing important goes in env vars.
## What it is
A local key vault built on the principle that **secrets are derived, not
stored**. From a single BIP39 mnemonic, the vault deterministically
derives Ed25519 identity keys, SSH host keys, and AES-256-GCM encryption
keys on demand. External credentials (API keys, OAuth tokens) that can't
be derived are encrypted with a seed-derived key and stored as
`EncryptedData` blobs.
The vault is **local-only by construction** — direct method calls on
`VaultServiceHandle`, no actor, no message enum, no wire format, no
remote dispatch. The master seed and derived private keys never cross a
process boundary.
## Usage
```rust
use alkvault::derivation::PATHS;
use alkvault::{VaultServiceHandle, KeyType};
// Generate a new mnemonic and unlock the vault.
let vault = VaultServiceHandle::new();
let phrase = vault.unlock_new(24)?; // 24-word BIP39 mnemonic
// Store `phrase` securely — it is the root of trust.
// Derive an Ed25519 identity keypair at m/74'/0'/0'/0'.
let identity = vault.derive_ed25519(PATHS::IDENTITY)?;
assert_eq!(identity.key_type, KeyType::Ed25519);
assert_eq!(identity.private_key.len(), 32);
// Encrypt an external credential with a seed-derived AES-256-GCM key.
let plaintext = "sk-proj-abc123xyz789";
let encrypted = vault.encrypt(plaintext, 2)?;
let decrypted = vault.decrypt(&encrypted)?;
assert_eq!(decrypted, plaintext);
// Lock the vault — purges the seed and all cached derived keys.
vault.lock();
# Ok::<(), Box<dyn std::error::Error>>(())
```
## Features
| `secp256k1` | off | BIP-0032 secp256k1 HD key derivation for Ethereum signing keys (`m/44'/60'/0'/0/0`) |
Without `secp256k1`, `derive_ethereum_key` returns
`UnsupportedKeyType`. The `secp256k1` crate is a heavy dependency (C
library for curve operations); feature-gating it keeps the default build
lightweight.
## Derivation paths
`alkvault` reserves the `74'` coin type (unallocated per SLIP-0044).
| `m/74'/0'/0'/0'` | Primary identity keypair | Ed25519 |
| `m/74'/0'/0'/{n}'` | Worker/device identity | Ed25519 |
| `m/74'/0'/1'/0'` | SSH host key | Ed25519 |
| `m/74'/2'/0'/0'` | Encryption key (v2) | AES-256-GCM |
| `m/74'/2'/0'/{n}'` | Encryption key (v{n+2}) | AES-256-GCM |
| `m/44'/60'/0'/0/0` | Ethereum signing key | secp256k1 (feature-gated) |
Key rotation re-encrypts `EncryptedData` from one version to another via
version-indexed derivation paths — same seed, different keys, no new
mnemonic needed.
## Security model
- **Seed never persisted.** The BIP39 mnemonic is entered at startup or
via `unlock` and held only in `Zeroize`-protected RAM. `lock()` purges
the seed and all cached derived keys.
- **Zeroize everything sensitive.** `Mnemonic`, `Seed`,
`ExtendedPrivKey`, `EncryptionKey`, `DerivedKey`, and `CachedKey` all
implement `Zeroize` and `ZeroizeOnDrop`. Secret material does not
linger in freed heap memory.
- **OsRng for nonces.** AES-GCM IVs use the operating system's CSPRNG
(`SysRng`), never a thread-local RNG. IV reuse under the same key is
catastrophic for GCM.
- **`DerivedKey` redaction.** `DerivedKey` serializes `private_key` as
`"[REDACTED]"` in all formats (defense-in-depth for logging accidents)
and rejects redacted payloads on deserialization. `Debug` impls also
redact.
- **Move-only, not `Clone`.** `DerivedKey` and `EncryptionKey` are
move-only — no accidental duplication of secret material.
- **No `unwrap()` outside tests.** Vault operations propagate errors.
A poisoned lock is recovered with `unwrap_or_else(|e| e.into_inner())`,
not panicked.
## Crate independence
`alkvault` does **not** depend on any application or networking crate.
It defines its own types (`VaultServiceError`, `DerivedKey`,
`EncryptedData`, etc.) and is usable in contexts where networking doesn't
exist — CLI tools, test harnesses, key-derivation utilities, and future
WASM targets.
## Documentation
Architecture documentation lives under [`docs/architecture/`](docs/architecture/):
- [Mnemonic and key derivation](docs/architecture/mnemonic-derivation.md) — BIP39, SLIP-0010, BIP-0032, derivation paths
- [Encryption](docs/architecture/encryption.md) — AES-256-GCM, `EncryptedData`, key versioning
- [Service](docs/architecture/service.md) — `VaultServiceHandle` lifecycle, cache, error model
- [Protocol](docs/architecture/protocol.md) — `DerivedKey` redaction, `KeyType`, serialization
- [Architecture decisions (ADRs)](docs/architecture/decisions/) — standalone crate, local-only dispatch, HD derivation, key rotation
## License
Licensed under either of
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.