diff --git a/Cargo.lock b/Cargo.lock index 2a2ad1c..74c8353 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -38,7 +38,7 @@ dependencies = [ ] [[package]] -name = "alknet-vault" +name = "alkvault" version = "0.1.0" dependencies = [ "aes-gcm", diff --git a/Cargo.toml b/Cargo.toml index 440e1a7..1e00d98 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "alknet-vault" +name = "alkvault" version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" @@ -7,7 +7,7 @@ description = "Local key vault: BIP39 mnemonic generation, SLIP-0010 Ed25519 HD repository = "https://git.alk.dev/alkdev/alkvault" [lib] -name = "alknet_vault" +name = "alkvault" [features] default = [] diff --git a/docs/architecture/README.md b/docs/architecture/README.md index 3de9065..582788a 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -3,7 +3,7 @@ status: stable last_updated: 2026-06-23 --- -# alknet-vault +# alkvault Local key vault: BIP39 mnemonic generation, SLIP-0010 Ed25519 HD key derivation, BIP-0032 secp256k1 derivation (feature-gated), and AES-256-GCM @@ -12,7 +12,7 @@ and encrypted credentials in the alknet system. ## What This Crate Is -alknet-vault is a **standalone crate** with zero alknet crate dependencies +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 @@ -39,10 +39,6 @@ seed and derived private keys never cross the network. | ADR | Title | Relevance | |-----|-------|-----------| -| [003](decisions/003-crate-decomposition.md) | Crate Decomposition | alknet-vault's standalone position | -| [008](decisions/008-secret-service-integration.md) | Vault Integration Point | CLI-embedded, capability source | -| [010](decisions/010-alpn-router-and-endpoint.md) | ALPN Router and Endpoint | Ed25519 as default curve for TLS raw key identity | -| [014](decisions/014-secret-material-flow-and-capability-injection.md) | Secret Material Flow and Capability Injection | Capabilities carry vault-derived material | | [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 | @@ -50,6 +46,19 @@ seed and derived private keys never cross the network. | [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 | @@ -119,7 +128,7 @@ pub use mnemonic::{Language, Mnemonic, Seed}; pub use derivation::{DerivationError, ExtendedPrivKey, PATHS}; // Derivation helpers (derive_path_from_seed, parse_derivation_path, // device_path, encryption_path_for_version) are accessible as -// alknet_vault::derivation::* — not re-exported at crate root to avoid +// alkvault::derivation::* — not re-exported at crate root to avoid // clutter, but fully public. // Encryption @@ -141,4 +150,21 @@ The `secp256k1` feature flag gates Ethereum (BIP-0032) derivation: ```rust #[cfg(feature = "secp256k1")] pub mod ethereum; -``` \ No newline at end of file +``` + +## 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. \ No newline at end of file diff --git a/docs/architecture/decisions/018-vault-standalone-crate.md b/docs/architecture/decisions/018-vault-standalone-crate.md index 9e3eec9..16e182f 100644 --- a/docs/architecture/decisions/018-vault-standalone-crate.md +++ b/docs/architecture/decisions/018-vault-standalone-crate.md @@ -6,12 +6,12 @@ Accepted ## Context -alknet-vault provides BIP39 mnemonic generation, SLIP-0010 Ed25519 HD key +alkvault provides BIP39 mnemonic generation, SLIP-0010 Ed25519 HD key derivation, BIP-0032 secp256k1 derivation (feature-gated), and AES-256-GCM encryption. It holds the master seed — the root of trust for all derived keys and encrypted credentials in the alknet system. -The question is: what does alknet-vault depend on? The candidates: +The question is: what does alkvault depend on? The candidates: 1. **Depend on alknet-core** for shared types (errors, maybe Identity). This pulls QUIC, quinn, iroh, rustls, and tokio runtime dependencies into the @@ -64,7 +64,7 @@ wraps the vault (see ADR-025, OQ-021). ## Decision -**alknet-vault has zero alknet crate dependencies.** It depends only on +**alkvault has zero alknet crate dependencies.** It depends only on external crates (`bip39`, `ed25519-bip32`, `aes-gcm`, `sha2`, `hmac`, `secp256k1`, `serde`, `zeroize`, `thiserror`, `base64`, `rand`). ADR-025 dropped `irpc`, `irpc-derive`, `postcard`, and `tokio` — the vault no longer @@ -75,18 +75,18 @@ dependency. The vault does not depend on: - `alknet-core` — no shared types, no `Identity`, no `AuthContext` - `alknet-call` — no `OperationSpec`, no `OperationContext`, no call protocol -- `alknet-vault` does not implement `ProtocolHandler` — it has no ALPN (see +- `alkvault` does not implement `ProtocolHandler` — it has no ALPN (see ADR-019) Dependency flow is strictly one-directional: ``` -alknet-vault (standalone) +alkvault (standalone) ↑ -alknet (CLI binary) — the only crate that depends on alknet-vault +alknet (CLI binary) — the only crate that depends on alkvault ``` -No handler crate depends on alknet-vault directly. Handlers receive derived +No handler crate depends on alkvault directly. Handlers receive derived material through capabilities injected by the assembly layer (ADR-014). The CLI binary is the sole integration point (ADR-008, ADR-019). @@ -200,14 +200,22 @@ makes the freeze explicit and enforceable by review. ## References -- ADR-003: Crate decomposition (alknet-vault is standalone) -- ADR-005: irpc as call protocol foundation (superseded by ADR-064 — irpc - was never integrated into alknet-call; the vault no longer uses irpc - either — see ADR-025) -- ADR-025: Vault local-only dispatch (dropped irpc from the vault; the - vault uses direct method calls, no actor, no remote capability) -- ADR-008: Vault integration point (CLI-embedded, assembly-layer only) -- ADR-014: Secret material flow and capability injection -- ADR-019: Vault assembly-layer-only access -- [crates/vault/README.md](../README.md) -- Implementation: `crates/alknet-vault/` \ No newline at end of file +- `@alkdev/alknet: ADR-003` — crate decomposition (alkvault is standalone) +- `@alkdev/alknet: ADR-005` — irpc as call protocol foundation (superseded + by ADR-064 — irpc was never integrated into alknet-call; the vault no + longer uses irpc either — see ADR-025) +- [ADR-025](025-vault-local-only-dispatch.md) — vault local-only dispatch + (dropped irpc from the vault; the vault uses direct method calls, no + actor, no remote capability) +- `@alkdev/alknet: ADR-008` — vault integration point (CLI-embedded, + assembly-layer only) +- `@alkdev/alknet: ADR-014` — secret material flow and capability injection +- [ADR-019](019-vault-assembly-layer-only.md) — vault assembly-layer-only + access +- [README.md](../README.md) +- Implementation: `src/` + +> 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. \ No newline at end of file diff --git a/docs/architecture/decisions/019-vault-assembly-layer-only.md b/docs/architecture/decisions/019-vault-assembly-layer-only.md index 1e1cdc4..4c2f1d9 100644 --- a/docs/architecture/decisions/019-vault-assembly-layer-only.md +++ b/docs/architecture/decisions/019-vault-assembly-layer-only.md @@ -73,7 +73,7 @@ Handlers never: - Hold a `VaultServiceHandle` reference - Call `derive_*`, `encrypt`, or `decrypt` directly - Receive the master seed or mnemonic -- Import `alknet_vault` as a dependency +- Import `alkvault` as a dependency Handlers receive secret material through `OperationContext.capabilities` (ADR-014). The `Capabilities` type holds non-serializable, zeroized secret @@ -135,7 +135,7 @@ that door; it simply does not open it. (ADR-025) — no remote dispatch capability exists in the vault crate. If remote vault access is needed in the future, it requires a separate vault-server crate that depends on both alknet-core (for auth) and - alknet-vault (for the handle), with a heavily restricted mechanism + alkvault (for the handle), with a heavily restricted mechanism (admin scope, mTLS-only, never expose the mnemonic over an unauthenticated channel) and its own ADR. @@ -160,10 +160,17 @@ that door; it simply does not open it. ## References -- ADR-003: Crate decomposition (alknet-vault is standalone) -- ADR-008: Vault integration point (CLI-embedded, capability source) -- ADR-014: Secret material flow and capability injection (the injection - mechanism this ADR relies on) -- ADR-018: Vault as standalone crate (the independence this ADR preserves) -- [crates/vault/service.md](../service.md) -- [crates/vault/README.md](../README.md) \ No newline at end of file +- `@alkdev/alknet: ADR-003` — crate decomposition (alkvault is standalone) +- `@alkdev/alknet: ADR-008` — vault integration point (CLI-embedded, + capability source) +- `@alkdev/alknet: ADR-014` — secret material flow and capability injection + (the injection mechanism this ADR relies on) +- [ADR-018](018-vault-standalone-crate.md) — vault as standalone crate + (the independence this ADR preserves) +- [service.md](../service.md) +- [README.md](../README.md) + +> 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. \ No newline at end of file diff --git a/docs/architecture/decisions/020-hd-derivation-for-encryption-keys.md b/docs/architecture/decisions/020-hd-derivation-for-encryption-keys.md index 1c2ce70..c71a5b4 100644 --- a/docs/architecture/decisions/020-hd-derivation-for-encryption-keys.md +++ b/docs/architecture/decisions/020-hd-derivation-for-encryption-keys.md @@ -220,11 +220,20 @@ not another version index. See OQ-22 (key rotation) and ADR-018 ## References -- ADR-018: Vault as standalone crate -- ADR-019: Vault assembly-layer-only access +- [ADR-018](018-vault-standalone-crate.md) — vault as standalone crate +- [ADR-019](019-vault-assembly-layer-only.md) — vault assembly-layer-only + access - [encryption.md](../encryption.md) — AES-256-GCM, EncryptedData - [mnemonic-derivation.md](../mnemonic-derivation.md) — SLIP-0010, PATHS::ENCRYPTION -- OQ-20: Salt/KDF Phase B (resolved by this ADR) -- OQ-22: Key rotation mechanism (still open — this ADR defines v2 but not the rotation workflow) -- TypeScript predecessor: `/workspace/@alkdev/storage/src/graphs/crypto.ts` -- TypeScript secret graph: `/workspace/@alkdev/storage/src/graphs/modules/secret-graph.ts` \ No newline at end of file +- [OQ-20](../questions/020-salt-kdf-and-encryption-key-derivation-method.md) — + Salt/KDF Phase B (resolved by this ADR) +- [OQ-22](../questions/022-key-rotation-mechanism.md) — key rotation + mechanism (still open — this ADR defines v2 but not the rotation + workflow) +- TypeScript predecessor: `@alkdev/storage: src/graphs/crypto.ts` +- TypeScript secret graph: `@alkdev/storage: src/graphs/modules/secret-graph.ts` + +> Cross-repo references (`@alkdev/...: ...`) point to other packages in +> the `@alkdev` org where this crate originated. The artifacts are +> preserved there as historical context; they are not part of this +> standalone repo. \ No newline at end of file diff --git a/docs/architecture/decisions/021-key-rotation-via-version-indexed-paths.md b/docs/architecture/decisions/021-key-rotation-via-version-indexed-paths.md index 9bba0fc..0225a3d 100644 --- a/docs/architecture/decisions/021-key-rotation-via-version-indexed-paths.md +++ b/docs/architecture/decisions/021-key-rotation-via-version-indexed-paths.md @@ -244,9 +244,10 @@ are expected. ## References -- ADR-020: HD derivation for encryption keys (this ADR builds on the - version-indexed path scheme) -- OQ-22: Key rotation mechanism (resolved by this ADR) +- [ADR-020](020-hd-derivation-for-encryption-keys.md) — HD derivation for + encryption keys (this ADR builds on the version-indexed path scheme) +- [OQ-22](../questions/022-key-rotation-mechanism.md) — key rotation + mechanism (resolved by this ADR) - [encryption.md](../encryption.md) — AES-256-GCM, EncryptedData - [service.md](../service.md) — encrypt, decrypt, rotate methods - [mnemonic-derivation.md](../mnemonic-derivation.md) — diff --git a/docs/architecture/decisions/025-vault-local-only-dispatch.md b/docs/architecture/decisions/025-vault-local-only-dispatch.md index 4a89e7e..3cc7ba8 100644 --- a/docs/architecture/decisions/025-vault-local-only-dispatch.md +++ b/docs/architecture/decisions/025-vault-local-only-dispatch.md @@ -6,7 +6,7 @@ Accepted ## Context -alknet-vault uses irpc for its internal dispatch. The `VaultProtocol` enum is +alkvault uses irpc for its internal dispatch. The `VaultProtocol` enum is annotated with `#[rpc_requests(message = VaultMessage, no_spans)]`, which generates a `Service` trait impl (for in-process mpsc dispatch) and a `RemoteService` trait impl (for remote QUIC dispatch). The vault's @@ -103,7 +103,7 @@ which exists only to make `RemoteService` work, which is the footgun. ## Decision -### 1. alknet-vault drops irpc entirely +### 1. alkvault drops irpc entirely The vault's dispatch is direct method calls on `VaultServiceHandle`. No `VaultProtocol` enum, no `VaultMessage`, no `VaultServiceActor`, no mpsc @@ -121,7 +121,7 @@ The vault crate has no remote dispatch capability. There is no `RemoteService` trait, no remote handler, no wire format for vault messages. Enabling remote vault access is not a flag flip or a server-setup change — it requires *building a separate crate* that depends on both alknet-core -(for auth) and alknet-vault (for the handle) and adds the remote transport +(for auth) and alkvault (for the handle) and adds the remote transport + auth-wrapping handler. That is a visible architectural act that shows up in code review, not a runtime config flip on a macro that was already generating the remote code. @@ -152,7 +152,7 @@ defines the wire representation. The vault-server-crate question (review #002 C7) is decided: *if* remote vault access is ever needed, it is a separate crate that depends on both alknet-core (for `IdentityProvider`, scopes, auth-wrapping) and -alknet-vault (for `VaultServiceHandle`). The vault crate itself remains +alkvault (for `VaultServiceHandle`). The vault crate itself remains local-only. This is a decision not to create the crate now, and not to preclude it. It is the path of least commitment, and it matches ADR-018's standalone-vault principle. @@ -311,26 +311,35 @@ version of ADR-018's intent. ## References -- ADR-005: irpc as call protocol foundation (this ADR amends the vault - reference in ADR-005's Decision and Consequences; ~~irpc remains the - foundation for alknet-*call*, just not for alknet-*vault*~~ — **this - claim is itself superseded by [ADR-064](064-irpc-never-integrated-hand-rolled-framing.md)**, - which records that irpc was never integrated into alknet-call either; - neither the vault nor the call protocol uses irpc) -- ADR-008: Vault integration point (the vault is a capability source - accessed at assembly time — this ADR makes that the *only* mode) -- ADR-014: Secret material flow and capability injection (`DerivedKey` - never appears in call protocol payloads — the redacting `Serialize` - is defense-in-depth for logging, not for wire transport) -- ADR-018: Vault as standalone crate (this ADR strengthens the - standalone principle: zero alknet crate dependencies *and* zero RPC - framework dependencies) -- ADR-019: Vault assembly-layer-only access (this ADR makes the vault - local-only, not just assembly-layer-only-for-direct-calls) -- OQ-21: Remote vault administration (resolved by this ADR — not a vault - crate feature; if needed, a separate crate with its own ADR) -- docs/reviews/002-pre-implementation-architecture-sanity-check.md +- `@alkdev/alknet: ADR-005` — irpc as call protocol foundation (this ADR + amends the vault reference in ADR-005's Decision and Consequences; + ~~irpc remains the foundation for alknet-*call*, just not for + alkvault~~ — **this claim is itself superseded by + `@alkdev/alknet: ADR-064`**, which records that irpc was never + integrated into alknet-call either; neither the vault nor the call + protocol uses irpc) +- `@alkdev/alknet: ADR-008` — vault integration point (the vault is a + capability source accessed at assembly time — this ADR makes that the + *only* mode) +- `@alkdev/alknet: ADR-014` — secret material flow and capability injection + (`DerivedKey` never appears in call protocol payloads — the redacting + `Serialize` is defense-in-depth for logging, not for wire transport) +- [ADR-018](018-vault-standalone-crate.md) — vault as standalone crate + (this ADR strengthens the standalone principle: zero alknet crate + dependencies *and* zero RPC framework dependencies) +- [ADR-019](019-vault-assembly-layer-only.md) — vault assembly-layer-only + access (this ADR makes the vault local-only, not just + assembly-layer-only-for-direct-calls) +- [OQ-21](../questions/021-remote-vault-administration.md) — remote vault + administration (resolved by this ADR — not a vault crate feature; if + needed, a separate crate with its own ADR) +- `@alkdev/alknet: docs/reviews/002-pre-implementation-architecture-sanity-check.md` (findings C7, C8, W8 — resolved or dissolved by this ADR) -- irpc design patterns: `docs/research/references/iroh/irpc/09-design-patterns-and-examples.md` +- `@alkdev/alknet: docs/research/references/iroh/irpc/09-design-patterns-and-examples.md` (Pattern 3: `no_rpc` flag — this ADR goes further by dropping irpc - entirely, since the actor pattern is also unnecessary) \ No newline at end of file + entirely, since the actor pattern is also unnecessary) + +> 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. \ No newline at end of file diff --git a/docs/architecture/decisions/026-vault-key-model-hd-derivation.md b/docs/architecture/decisions/026-vault-key-model-hd-derivation.md index bf7426b..c4ba4d9 100644 --- a/docs/architecture/decisions/026-vault-key-model-hd-derivation.md +++ b/docs/architecture/decisions/026-vault-key-model-hd-derivation.md @@ -165,15 +165,16 @@ and the v1→v2 migration from PBKDF2. ## References -- ADR-020: HD derivation for encryption keys (a special case of this ADR — - covers the encryption key at `m/74'/2'/0'/0'` and the v1→v2 migration - from PBKDF2) -- ADR-010: ALPN router and endpoint (Ed25519 as the default curve for TLS - raw key identity — the identity key at `m/74'/0'/0'/0'`) -- ADR-018: Vault as standalone crate (the vault defines its own key types - and derivation paths) -- ADR-025: Vault local-only dispatch (the vault is local-only; the seed - never crosses the network) +- [ADR-020](020-hd-derivation-for-encryption-keys.md) — HD derivation for + encryption keys (a special case of this ADR — covers the encryption key + at `m/74'/2'/0'/0'` and the v1→v2 migration from PBKDF2) +- `@alkdev/alknet: ADR-010` — ALPN router and endpoint (Ed25519 as the + default curve for TLS raw key identity — the identity key at + `m/74'/0'/0'/0'`) +- [ADR-018](018-vault-standalone-crate.md) — vault as standalone crate + (the vault defines its own key types and derivation paths) +- [ADR-025](025-vault-local-only-dispatch.md) — vault local-only dispatch + (the vault is local-only; the seed never crosses the network) - [mnemonic-derivation.md](../mnemonic-derivation.md) — BIP39, SLIP-0010, BIP-0032, derivation paths, PATHS module - [encryption.md](../encryption.md) — AES-256-GCM, @@ -182,4 +183,9 @@ and the v1→v2 migration from PBKDF2. - SLIP-0044: Registered coin types for BIP-0032 / SLIP-0010 (`74'` is unallocated) - BIP-0032: Hierarchical deterministic wallets (secp256k1) -- BIP-39: Mnemonic code for generating deterministic keys \ No newline at end of file +- BIP-39: Mnemonic code for generating deterministic keys + +> 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. \ No newline at end of file diff --git a/docs/architecture/encryption.md b/docs/architecture/encryption.md index d6d1d6f..60abcb9 100644 --- a/docs/architecture/encryption.md +++ b/docs/architecture/encryption.md @@ -290,7 +290,7 @@ These are security-critical implementation requirements. - [NIST SP 800-38D](https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf) — AES-GCM specification -- Implementation: `crates/alknet-vault/src/encryption.rs` -- Tests: `crates/alknet-vault/tests/test_vectors.rs`, - `crates/alknet-vault/src/encryption.rs` (unit tests) +- Implementation: `src/encryption.rs` +- Tests: `tests/test_vectors.rs`, + `src/encryption.rs` (unit tests) - [service.md](service.md) — how the vault caches the encryption key \ No newline at end of file diff --git a/docs/architecture/mnemonic-derivation.md b/docs/architecture/mnemonic-derivation.md index ddc2344..f9923c0 100644 --- a/docs/architecture/mnemonic-derivation.md +++ b/docs/architecture/mnemonic-derivation.md @@ -317,6 +317,6 @@ See [open-questions.md](open-questions.md) for full details. secp256k1 HD derivation - [SLIP-0044](https://github.com/satoshilabs/slips/blob/master/slip-0044.md) — registered coin types (74' is unallocated) -- Implementation: `crates/alknet-vault/src/mnemonic.rs`, - `crates/alknet-vault/src/derivation.rs`, `crates/alknet-vault/src/ethereum.rs` -- Test vectors: `crates/alknet-vault/tests/test_vectors.rs` \ No newline at end of file +- Implementation: `src/mnemonic.rs`, + `src/derivation.rs`, `src/ethereum.rs` +- Test vectors: `tests/test_vectors.rs` \ No newline at end of file diff --git a/docs/architecture/open-questions.md b/docs/architecture/open-questions.md index fbf27ab..7fbc2aa 100644 --- a/docs/architecture/open-questions.md +++ b/docs/architecture/open-questions.md @@ -48,7 +48,7 @@ Door type is separate from whether a decision is made. A two-way door is a decis ## By Theme -### alknet-vault +### alkvault All vault open questions are **resolved** — the vault is a stable crate with implementation complete and verified. diff --git a/docs/architecture/protocol.md b/docs/architecture/protocol.md index 08b59de..e28fefa 100644 --- a/docs/architecture/protocol.md +++ b/docs/architecture/protocol.md @@ -194,7 +194,7 @@ where a long-lived node exposes a restricted vault API to ephemeral workers), it requires a **separate vault-server crate** that: 1. Depends on both alknet-core (for `IdentityProvider`, scopes, - auth-wrapping) and alknet-vault (for `VaultServiceHandle`). + auth-wrapping) and alkvault (for `VaultServiceHandle`). 2. Defines its own threat model, access policy, and operation filtering (`Unlock`/`Lock` must be local-only; other operations may be remote-capable depending on the policy). @@ -224,9 +224,9 @@ the network at call time. | Vault is standalone | [ADR-018](decisions/018-vault-standalone-crate.md) | Zero alknet crate dependencies | | Vault is local-only | [ADR-025](decisions/025-vault-local-only-dispatch.md) | Direct method calls, no irpc, no remote dispatch capability | | HD derivation (not stored keys) | — | One seed, many keys, no key storage | -| `DerivedKey` is move-only | [ADR-014](decisions/014-secret-material-flow-and-capability-injection.md) | Prevents accidental duplication of secret material | -| JSON redacts private key (always) | [ADR-014](decisions/014-secret-material-flow-and-capability-injection.md) | Defense-in-depth for logging accidents | -| No vault operations on call protocol | [ADR-008](decisions/008-secret-service-integration.md), [ADR-014](decisions/014-secret-material-flow-and-capability-injection.md) | Master seed never crosses the network | +| `DerivedKey` is move-only | `@alkdev/alknet: ADR-014` | Prevents accidental duplication of secret material | +| JSON redacts private key (always) | `@alkdev/alknet: ADR-014` | Defense-in-depth for logging accidents | +| No vault operations on call protocol | `@alkdev/alknet: ADR-008`, `@alkdev/alknet: ADR-014` | Master seed never crosses the network | | No remote dispatch in vault crate | [ADR-025](decisions/025-vault-local-only-dispatch.md) | Remote access requires a separate vault-server crate with its own ADR | ## Open Questions @@ -236,8 +236,8 @@ ADR-025 and [open-questions.md](open-questions.md). ## References -- Implementation: `crates/alknet-vault/src/protocol.rs` -- Tests: `crates/alknet-vault/src/protocol.rs` (unit tests for redaction +- Implementation: `src/protocol.rs` +- Tests: `src/protocol.rs` (unit tests for redaction 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/questions/020-salt-kdf-and-encryption-key-derivation-method.md b/docs/architecture/questions/020-salt-kdf-and-encryption-key-derivation-method.md index 5357fcc..7ed5a1d 100644 --- a/docs/architecture/questions/020-salt-kdf-and-encryption-key-derivation-method.md +++ b/docs/architecture/questions/020-salt-kdf-and-encryption-key-derivation-method.md @@ -5,4 +5,4 @@ - **Door type**: One-way (key derivation method), two-way (salt field usage) - **Priority**: high - **Resolution**: The vault uses SLIP-0010 HD derivation from the BIP39 seed at path `m/74'/2'/0'/0'` to produce the AES-256-GCM encryption key — not PBKDF2. The `salt` field in `EncryptedData` is unused for key derivation (kept for wire-format compatibility with the TS predecessor). The TypeScript `@alkdev/storage` crypto module used PBKDF2 with a password + salt; data encrypted by that method (key_version=1) cannot be decrypted by the vault and must be migrated via one-time re-encryption to key_version=2. See ADR-020 for the full rationale and migration path. -- **Cross-references**: ADR-020, [encryption.md](../encryption.md) +- **Cross-references**: [ADR-020](../decisions/020-hd-derivation-for-encryption-keys.md), [encryption.md](../encryption.md) diff --git a/docs/architecture/questions/021-remote-vault-administration.md b/docs/architecture/questions/021-remote-vault-administration.md index 23657a4..e40ec9c 100644 --- a/docs/architecture/questions/021-remote-vault-administration.md +++ b/docs/architecture/questions/021-remote-vault-administration.md @@ -6,9 +6,9 @@ - **Priority**: medium - **Resolution**: Remote vault access is **not a feature of the vault crate**. ADR-025 dropped irpc from the vault, making the vault local-only by construction — no `RemoteService` trait, no wire format for vault messages, no default-insecure remote handler. The vault's API is `VaultServiceHandle` (direct method calls), nothing else. - If remote vault access is ever needed (e.g., the machine→worker pattern), it requires a **separate vault-server crate** that depends on both alknet-core (for `IdentityProvider`, scopes, auth-wrapping) and alknet-vault (for `VaultServiceHandle`). That crate would define its own threat model, access policy, operation filtering (Unlock/Lock local-only), and wire format — and requires its own ADR. This is a deliberate addition, not a flag flip on a default that was already loaded. + If remote vault access is ever needed (e.g., the machine→worker pattern), it requires a **separate vault-server crate** that depends on both alknet-core (for `IdentityProvider`, scopes, auth-wrapping) and alkvault (for `VaultServiceHandle`). That crate would define its own threat model, access policy, operation filtering (Unlock/Lock local-only), and wire format — and requires its own ADR. This is a deliberate addition, not a flag flip on a default that was already loaded. The pre-ADR-025 deferral framed remote access as "non-breaking" (the wire format was additive). That framing was misleading: once workers build dependencies on the remote vault API, disabling it breaks them — the door is operationally one-way even if the wire format is additive. ADR-025 inverts the default: the vault is local-only by construction, and remote access requires building something new, not removing a default. Per-node vaults are the recommended pattern for multi-node deployments: each node has its own vault and mnemonic; credentials are encrypted *for* the receiving node's public key, not decrypted centrally. This is end-to-end encryption between nodes, matching ADR-008's "capability source" model. -- **Cross-references**: ADR-005, ADR-008, ADR-014, ADR-018, ADR-019, ADR-025, [protocol.md](../protocol.md), [service.md](../service.md) +- **Cross-references**: `@alkdev/alknet: ADR-005`, `@alkdev/alknet: ADR-008`, `@alkdev/alknet: ADR-014`, [ADR-018](../decisions/018-vault-standalone-crate.md), [ADR-019](../decisions/019-vault-assembly-layer-only.md), [ADR-025](../decisions/025-vault-local-only-dispatch.md), [protocol.md](../protocol.md), [service.md](../service.md) diff --git a/docs/architecture/questions/022-key-rotation-mechanism.md b/docs/architecture/questions/022-key-rotation-mechanism.md index 27d3f5b..4bcf8bc 100644 --- a/docs/architecture/questions/022-key-rotation-mechanism.md +++ b/docs/architecture/questions/022-key-rotation-mechanism.md @@ -5,4 +5,4 @@ - **Door type**: One-way (path scheme), two-way (rotation policy) - **Priority**: medium - **Resolution**: Key rotation uses version-indexed derivation paths. Each key version maps to a distinct SLIP-0010 path: `m/74'/2'/0'/{version-2}'`. v2 (current) is at `m/74'/2'/0'/0'`; v3 is at `m/74'/2'/0'/1'`; etc. The `decrypt` method derives the key at the path indicated by `encrypted.key_version` (not always at `PATHS::ENCRYPTION`). The `rotate` method decrypts with the old version's key and re-encrypts with the new version's key — no new mnemonic needed. The assembly layer or a migration tool iterates stored blobs and calls `rotate` on each; the vault does not self-rotate. Partial rotation is safe (old keys remain derivable). See ADR-021. -- **Cross-references**: ADR-020, ADR-021, [encryption.md](../encryption.md), [service.md](../service.md) +- **Cross-references**: [ADR-020](../decisions/020-hd-derivation-for-encryption-keys.md), [ADR-021](../decisions/021-key-rotation-via-version-indexed-paths.md), [encryption.md](../encryption.md), [service.md](../service.md) diff --git a/docs/architecture/service.md b/docs/architecture/service.md index a5f0507..971bb97 100644 --- a/docs/architecture/service.md +++ b/docs/architecture/service.md @@ -376,10 +376,10 @@ don't miss them. ## References -- Implementation: `crates/alknet-vault/src/service.rs`, - `crates/alknet-vault/src/cache.rs` -- Tests: `crates/alknet-vault/tests/service_tests.rs`, - `crates/alknet-vault/src/service.rs` (unit tests), - `crates/alknet-vault/src/cache.rs` (unit tests) +- Implementation: `src/service.rs`, + `src/cache.rs` +- Tests: `tests/service_tests.rs`, + `src/service.rs` (unit tests), + `src/cache.rs` (unit tests) - [protocol.md](protocol.md) — `DerivedKey` and `KeyType` - [encryption.md](encryption.md) — `encrypt` / `decrypt` cryptographic details \ No newline at end of file diff --git a/docs/sdd_process.md b/docs/sdd_process.md index 3df7550..245cfaf 100644 --- a/docs/sdd_process.md +++ b/docs/sdd_process.md @@ -2,7 +2,7 @@ ## Overview -This document defines the SDD process for the @alkdev/storage package. It +This document defines the SDD process for the @alkdev/alkvault package. It leverages: - **OpenCode CLI** as the agent execution environment diff --git a/src/derivation.rs b/src/derivation.rs index 40c24cb..f1fbc43 100644 --- a/src/derivation.rs +++ b/src/derivation.rs @@ -108,8 +108,8 @@ impl ExtendedPrivKey { /// # Example /// /// ``` -/// use alknet_vault::derivation::{derive_path_from_seed, PATHS}; -/// use alknet_vault::mnemonic::Mnemonic; +/// use alkvault::derivation::{derive_path_from_seed, PATHS}; +/// use alkvault::mnemonic::Mnemonic; /// /// let mnemonic = Mnemonic::generate(24).unwrap(); /// let seed = mnemonic.to_seed(None); diff --git a/src/ethereum.rs b/src/ethereum.rs index 1abe631..4a6cb6d 100644 --- a/src/ethereum.rs +++ b/src/ethereum.rs @@ -138,8 +138,8 @@ fn derive_child( /// # Example /// /// ```ignore -/// use alknet_vault::ethereum::derive_secp256k1_path; -/// use alknet_vault::derivation::PATHS; +/// use alkvault::ethereum::derive_secp256k1_path; +/// use alkvault::derivation::PATHS; /// /// let key = derive_secp256k1_path(seed, PATHS::ETHEREUM).unwrap(); /// assert_eq!(key.private_key().len(), 32); diff --git a/src/lib.rs b/src/lib.rs index a99a73d..5dec31d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -//! # alknet-vault +//! # alkvault //! //! Local key vault: BIP39 mnemonic generation, SLIP-0010 Ed25519 HD key derivation, //! AES-256-GCM encryption for securing provider keys, credentials, and identity material. @@ -10,7 +10,7 @@ //! //! ## Crate Independence //! -//! alknet-vault does **not** depend on alknet-core or any other alknet crate. It is +//! alkvault does **not** depend on alknet-core or any other alknet crate. It is //! fully independent and usable in contexts where QUIC networking doesn't exist (CLI //! tools, test harnesses, WASM key derivation). //! diff --git a/tests/derivation_tests.rs b/tests/derivation_tests.rs index 894a4fc..86d9721 100644 --- a/tests/derivation_tests.rs +++ b/tests/derivation_tests.rs @@ -3,8 +3,8 @@ //! These tests verify that SLIP-0010 derivation produces correct results //! against known test vectors and that path constants produce expected key types. -use alknet_vault::derivation::PATHS; -use alknet_vault::service::VaultServiceHandle; +use alkvault::derivation::PATHS; +use alkvault::service::VaultServiceHandle; #[test] fn test_identity_key_derivation() { @@ -12,7 +12,7 @@ fn test_identity_key_derivation() { let _phrase = service.unlock_new(24).unwrap(); let key = service.derive_ed25519(PATHS::IDENTITY).unwrap(); - assert_eq!(key.key_type, alknet_vault::protocol::KeyType::Ed25519); + assert_eq!(key.key_type, alkvault::protocol::KeyType::Ed25519); assert!(!key.private_key.is_empty()); assert!(!key.public_key.is_empty()); } @@ -23,7 +23,7 @@ fn test_encryption_key_derivation() { service.unlock_new(24).unwrap(); let key = service.derive_encryption_key(PATHS::ENCRYPTION).unwrap(); - assert_eq!(key.key_type, alknet_vault::protocol::KeyType::Aes256Gcm); + assert_eq!(key.key_type, alkvault::protocol::KeyType::Aes256Gcm); } #[test] diff --git a/tests/encryption_tests.rs b/tests/encryption_tests.rs index 48867f3..fd5c639 100644 --- a/tests/encryption_tests.rs +++ b/tests/encryption_tests.rs @@ -3,8 +3,8 @@ //! These tests verify round-trip encryption, key version handling, //! and wire format compatibility. -use alknet_vault::encryption::CURRENT_KEY_VERSION; -use alknet_vault::service::VaultServiceHandle; +use alkvault::encryption::CURRENT_KEY_VERSION; +use alkvault::service::VaultServiceHandle; #[test] fn test_encrypt_decrypt_round_trip_via_service() { @@ -52,7 +52,7 @@ fn test_encrypted_data_serialization() { assert!(json.contains("data")); // Verify round-trip through JSON - let deserialized: alknet_vault::encryption::EncryptedData = + let deserialized: alkvault::encryption::EncryptedData = serde_json::from_str(&json).unwrap(); assert_eq!(deserialized, encrypted); } diff --git a/tests/service_tests.rs b/tests/service_tests.rs index 9020fa7..49565dd 100644 --- a/tests/service_tests.rs +++ b/tests/service_tests.rs @@ -3,8 +3,8 @@ //! These tests verify the unlock/lock lifecycle, error conditions, //! and that the vault correctly manages state transitions. -use alknet_vault::derivation::PATHS; -use alknet_vault::service::{VaultServiceError, VaultServiceHandle}; +use alkvault::derivation::PATHS; +use alkvault::service::{VaultServiceError, VaultServiceHandle}; #[test] fn test_full_lifecycle() { diff --git a/tests/test_vectors.rs b/tests/test_vectors.rs index 7103212..0bf4574 100644 --- a/tests/test_vectors.rs +++ b/tests/test_vectors.rs @@ -17,9 +17,9 @@ //! byte-for-byte matching against SLIP-0010 raw hex, since the crate's internal //! representation handles clamping differently. -use alknet_vault::derivation::{derive_path_from_seed, PATHS}; -use alknet_vault::mnemonic::{Language, Mnemonic}; -use alknet_vault::protocol::KeyType; +use alkvault::derivation::{derive_path_from_seed, PATHS}; +use alkvault::mnemonic::{Language, Mnemonic}; +use alkvault::protocol::KeyType; // --------------------------------------------------------------------------- // BIP39 Test Vectors @@ -290,7 +290,7 @@ fn test_aes256gcm_known_key_encrypt_decrypt() { ]; let nonce = Nonce::from_slice(&nonce_bytes); - let plaintext = b"hello, alknet vault!"; + let plaintext = b"hello, alkvault!"; // Encrypt with known key and nonce let ciphertext = cipher.encrypt(nonce, plaintext.as_ref()).unwrap(); @@ -369,7 +369,7 @@ fn test_alknet_encryption_path_regression() { /// direct derivation (integration test). #[test] fn test_service_derive_matches_direct_derivation() { - use alknet_vault::service::VaultServiceHandle; + use alkvault::service::VaultServiceHandle; let service = VaultServiceHandle::new(); let phrase = service.unlock_new(24).unwrap();