refactor: rename alknet-secret to alknet-vault

Rename the crate from alknet-secret to alknet-vault to better reflect its
purpose as a local key vault (seed management, key derivation, encryption)
rather than a network service.

Symbol renames:
- SecretService → VaultService
- SecretServiceHandle → VaultServiceHandle
- SecretServiceActor → VaultServiceActor
- SecretServiceError → VaultServiceError
- SecretProtocol → VaultProtocol
- SecretMessage → VaultMessage
- ServiceLocked → VaultLocked
- alknet_secret → alknet_vault (crate name)

Update ADR-008 with vault access pattern: the vault is a capability source,
not a service endpoint. The CLI injects derived/decrypted material into
operation contexts — handlers never hold vault references.
This commit is contained in:
2026-06-16 11:10:07 +00:00
parent b47a6fe70b
commit 80128a56e5
22 changed files with 262 additions and 256 deletions

View File

@@ -7,7 +7,7 @@ last_updated: 2026-06-16
## 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-secret` (stable) and research/reference material. Foundational ADRs (001009) are in place, including the BiStream type definition (ADR-007), secret service integration (ADR-008), and the one-way door decision framework (ADR-009). Architecture specs are ready for Phase 1 implementation planning.
**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) and research/reference material. Foundational ADRs (001009) are in place, including the BiStream type definition (ADR-007), vault integration (ADR-008), and the one-way door decision framework (ADR-009). Architecture specs are ready for Phase 1 implementation planning.
**Next step**: Resolve remaining two-way-door questions during implementation. Start with alknet-core (ProtocolHandler trait, Connection, endpoint, router, auth types, config).
@@ -31,7 +31,7 @@ Crate-specific specs will be created when each crate is ready for Phase 1 archit
| [005](decisions/005-irpc-as-call-protocol-foundation.md) | irpc as Call Protocol Foundation | Accepted |
| [006](decisions/006-alpn-convention-and-connection-model.md) | ALPN String Convention and Connection Model | Accepted |
| [007](decisions/007-bistream-type-definition.md) | BiStream Type Definition | Accepted |
| [008](decisions/008-secret-service-integration.md) | Secret Service Integration Point | Accepted |
| [008](decisions/008-secret-service-integration.md) | Vault Integration Point | Accepted |
| [009](decisions/009-one-way-door-decision-framework.md) | One-Way Door Decision Framework | Accepted |
## Open Questions
@@ -43,7 +43,7 @@ See [open-questions.md](open-questions.md) for the full tracker.
- **OQ-02**: AuthContext timing — hybrid model (ADR-004)
- **OQ-03**: ALPN naming — `alknet/` prefix, no version (ADR-006)
- **OQ-06**: ALPN per connection, not per stream (ADR-006)
- **OQ-08**: Secret service — CLI-embedded via call protocol (ADR-008)
- **OQ-08**: Vault integration — CLI-embedded via call protocol (ADR-008)
**Two-way doors (deferred to implementation):**
- **OQ-04**: Dynamic handler registration — start static, add ArcSwap later

View File

@@ -12,7 +12,7 @@ The new ALPN dispatch model eliminates the need for a shared interface layer. Ea
Key constraints:
- Protocol crates must depend on alknet-core for auth/identity/config — but not on each other
- alknet-secret is already standalone (no alknet-core dependency) and must remain so
- alknet-secret is already standalone (no alknet-core dependency) and must remain so (renamed to alknet-vault — see ADR-008)
- The CLI binary assembles everything — it's the only crate that depends on all handler crates
- Some handlers (SFTP, call protocol) need to compile to WASM for browser/client use
- irpc is the foundation for the call protocol — it provides the operation registry, framing, and pub/sub patterns
@@ -24,7 +24,7 @@ The workspace decomposes into the following crates:
| Crate | Responsibility | Depends on |
|-------|---------------|------------|
| `alknet-core` | ProtocolHandler trait, ALPN router, endpoint, BiStream, AuthContext, IdentityProvider, config, ArcSwap dynamic config | tokio, quinn, rustls, irpc |
| `alknet-secret` | BIP39/SLIP-0010/AES-GCM key derivation and encryption, SecretProtocol service | (standalone, no alknet-core) |
| `alknet-vault` | Local key vault: BIP39/SLIP-0010/AES-GCM key derivation, encryption, VaultProtocol dispatch | (standalone, no alknet-core) |
| `alknet-ssh` | SshAdapter (russh, SOCKS5, port forwarding) | alknet-core, russh |
| `alknet-call` | CallAdapter (JSON-RPC via irpc, operation registry, pub/sub, access control) | alknet-core, irpc |
| `alknet-git` | GitAdapter (gix, pkt-line protocol) | alknet-core, gix |
@@ -37,7 +37,7 @@ The workspace decomposes into the following crates:
Dependency flow:
```
alknet-secret (standalone)
alknet-vault (standalone)
alknet-core ← all handler crates ← alknet (CLI)
alknet-call ← alknet-napi
```
@@ -49,7 +49,7 @@ No handler crate depends on another handler crate. Cross-handler communication g
**Positive:**
- Each handler can be developed, tested, and versioned independently
- WASM-compatible handlers (sftp, call) don't pull in heavy dependencies (russh, axum)
- alknet-secret remains standalone — no circular dependency risk
- alknet-vault remains standalone — no circular dependency risk
- New handlers are added by creating a crate and registering it with the endpoint
- Clean separation of concerns — each crate has one job

View File

@@ -1,4 +1,4 @@
# ADR-008: Secret Service Integration Point
# ADR-008: Vault Integration Point
## Status
@@ -6,19 +6,25 @@ Accepted
## Context
alknet-secret is a standalone crate with zero alknet crate dependencies. It provides BIP39 mnemonic generation, SLIP-0010 Ed25519 HD key derivation, AES-256-GCM encryption, and an irpc-based `SecretProtocol` service. It is already implemented and stable.
alknet-vault (formerly alknet-secret) is a standalone crate with zero alknet crate dependencies. It provides BIP39 mnemonic generation, SLIP-0010 Ed25519 HD key derivation, AES-256-GCM encryption, and an irpc-based `VaultProtocol` for message dispatch. It is already implemented and stable.
The question (OQ-08) is: how does the rest of the alknet system access alknet-secret's capabilities? The options are:
The question (OQ-08) was: how does the rest of the alknet system access alknet-vault's capabilities? The options were:
1. **irpc service over `alknet/call`**: Other services call SecretProtocol operations through the call protocol on `alknet/call`. The secret service is just another set of operations registered in the call protocol's operation registry.
1. **irpc service over `alknet/call`**: Other services call vault operations through the call protocol.
2. **ALPN handler on `alknet/secret`**: alknet-vault implements ProtocolHandler and gets its own ALPN.
3. **Direct library dependency**: alknet-core or handler crates depend on alknet-vault directly, breaking its independence.
4. **CLI-embedded with call protocol exposure**: The CLI binary instantiates VaultServiceHandle locally and registers vault operations in the call protocol's registry.
2. **ALPN handler on `alknet/secret`**: alknet-secret implements `ProtocolHandler` and gets its own ALPN. Remote nodes call it over a dedicated QUIC connection.
This is a one-way door because if alknet-vault gets pulled into alknet-core as a dependency, its independence is permanently lost. The standalone property is valuable — alknet-vault has no QUIC, no tokio runtime requirement (the handle works without it), and no alknet crate dependencies. It can be used in contexts where QUIC networking doesn't exist (CLI tools, test harnesses, WASM key derivation).
3. **Direct library dependency**: alknet-core or handler crates depend on alknet-secret directly, breaking its independence.
Beyond the integration point, there's a question of access patterns. The vault holds the master seed and can derive keys and encrypt/decrypt arbitrary data. This is used for:
4. **CLI-embedded with call protocol exposure**: The CLI binary instantiates SecretServiceHandle locally and registers secret operations in the call protocol's registry.
- Identity key derivation (SSH host keys, node identity)
- Provider API key storage (Vast.ai, LLM providers) — encrypted at rest, decrypted on demand
- Credential encryption for storage
- Multi-tenant key derivation (different derivation paths per tenant)
This is a one-way door because if alknet-secret gets pulled into alknet-core as a dependency, its independence is permanently lost. The standalone property is valuable — alknet-secret has no QUIC, no tokio runtime requirement (the handle works without it), and no alknet crate dependencies. It can be used in contexts where QUIC networking doesn't exist (CLI tools, test harnesses, WASM key derivation).
The vault is a capability source, not a service endpoint. Operations that need provider keys don't hold a reference to the vault — they receive the derived/decrypted material through their operation context. The vault is unlocked at startup by the CLI, and the CLI injects material into operation contexts as needed.
## Decision
@@ -26,38 +32,37 @@ This is a one-way door because if alknet-secret gets pulled into alknet-core as
The CLI binary (the `alknet` crate) is the integration point. It:
1. Instantiates `SecretServiceHandle` locally at startup (or on-demand with Unlock/Lock lifecycle).
2. Registers secret operations (DeriveEd25519, DeriveEncryptionKey, Encrypt, Decrypt, etc.) in the call protocol's operation registry.
3. Other handlers access secret capabilities by calling operations on `alknet/call` — they don't import alknet-secret directly.
1. Instantiates `VaultServiceHandle` locally at startup (or on-demand with Unlock/Lock lifecycle).
2. Registers vault operations (DeriveEd25519, DeriveEncryptionKey, Encrypt, Decrypt, etc.) in the call protocol's operation registry.
3. Other handlers access vault capabilities by calling operations on `alknet/call` — they don't import alknet-vault directly.
alknet-secret remains standalone with no alknet crate dependencies. Its `SecretServiceHandle` is used directly (in-process, no serialization) by the CLI binary. Its `SecretProtocol` irpc service is available for remote access through the call protocol.
**alknet-vault does NOT get its own ALPN.** Key derivation is a local operation — the master seed never crosses the network. If a remote node needs derived public keys (e.g., for identity verification), they're shared through the call protocol, not through direct vault access.
**alknet-secret does NOT get its own ALPN.** Here's why:
**The vault is accessed at the assembly layer, not by individual handlers.** The CLI (or a configuration middleware it sets up) is the only component that talks to the vault directly. Derived keys and decrypted credentials are injected into operation contexts — handlers receive the material they need, not a vault reference.
- `alknet/secret` as a separate ALPN would mean a remote node opens a QUIC connection to access key derivation — this is architecturally wrong. Key derivation is a local operation that should never cross the network in its raw form.
- If a remote node needs derived keys (e.g., for end-to-end encryption), the local node derives them and sends only the public component over `alknet/call` — never the seed or private key.
- The secret service's Unlock/Lock lifecycle (holding the master seed in RAM) is inherently local. There's no safe way to expose Unlock/Lock over the network.
**What if a handler needs a key at runtime?** The handler calls through the call protocol. The CLI registers secret operations in the call registry at startup. The call protocol routes the request to the locally-running SecretServiceHandle. No handler crate depends on alknet-secret.
This is analogous to the reverse-proxy admin key pattern (ADR-028 in the reverse-proxy project): the proxy reads the key file once at startup, hashes it, and individual handlers never see the file. Here, the CLI unlocks the vault once at startup, and individual handlers receive the results of vault operations through their contexts.
## Consequences
**Positive:**
- alknet-secret remains fully standalone — no QUIC dependency, no tokio runtime requirement for the handle
- alknet-vault remains fully standalone — no QUIC dependency, no tokio runtime requirement for the handle
- Key derivation and encryption are local-only by default — the master seed never leaves the node
- Remote access to public key material (not secrets) flows through the existing call protocol — no separate ALPN needed
- The CLI binary is the single integration point — clean dependency graph, no circular dependencies
- The `SecretServiceHandle` is used in-process with zero serialization overhead — direct method calls, not irpc messages
- Test harnesses can use `SecretServiceHandle` directly without any QUIC infrastructure
- The `VaultServiceHandle` is used in-process with zero serialization overhead — direct method calls, not irpc messages
- Test harnesses can use `VaultServiceHandle` directly without any QUIC infrastructure
- Access pattern is clear: vault → CLI → operation contexts, not vault ← handlers
**Negative:**
- Handlers that need keys must go through the call protocol — this adds a hop even for local calls (mitigated: local call protocol calls can be short-circuited to direct method calls via irpc's local dispatch)
- The CLI binary has a larger dependency tree since it imports both alknet-call and alknet-secret (expected: the CLI assembles everything)
- If the call protocol is not yet running when a handler needs a key, the handler must wait for initialization (mitigated: the CLI starts SecretServiceHandle before accepting connections)
- Handlers that need keys must receive them through their operation context — this requires the CLI or call protocol to mediate
- The CLI binary has a larger dependency tree since it imports both alknet-call and alknet-vault (expected: the CLI assembles everything)
- If the call protocol is not yet running when a handler needs a key, the handler must wait for initialization (mitigated: the CLI starts VaultServiceHandle before accepting connections)
## References
- ADR-003: Crate decomposition (alknet-secret is standalone)
- ADR-003: Crate decomposition (alknet-vault is standalone)
- ADR-005: irpc as call protocol foundation
- ADR-009: One-way door decision framework
- OQ-08: Secret service integration point (resolved by this ADR)
- alknet-secret implementation: `crates/alknet-secret/`
- alknet-vault implementation: `crates/alknet-vault/`
- Reverse-proxy ADR-028: Admin HTTP API (analogous key management pattern)

View File

@@ -20,7 +20,7 @@ Every architectural decision is classified as one of:
**One-way door** — Reversing this decision requires rewriting significant code across multiple crates or permanently closes a capability door. Examples:
- BiStream as a concrete quinn type (closes WASM door permanently)
- alknet-secret pulled into alknet-core as a dependency (loses standalone property permanently)
- alknet-vault pulled into alknet-core as a dependency (loses standalone property permanently)
- ProtocolHandler signature changes (every handler must be rewritten)
**Two-way door** — Reversing this decision is cheap or additive. Examples:

View File

@@ -84,13 +84,13 @@ Door type classifications follow ADR-009:
## Theme: Security
### OQ-08: Secret Service Integration Point
### OQ-08: Vault Integration Point
- **Origin**: [overview.md](overview.md)
- **Status**: resolved
- **Door type**: One-way
- **Priority**: medium
- **Resolution**: CLI-embedded with call protocol exposure. The CLI binary instantiates `SecretServiceHandle` locally and registers secret operations in the call protocol's operation registry. alknet-secret has no ALPN and no alknet-core dependency. Key derivation is local-only; only public key material crosses the network via `alknet/call`. See ADR-008.
- **Resolution**: CLI-embedded with call protocol exposure. The CLI binary instantiates `VaultServiceHandle` locally and registers vault operations in the call protocol's operation registry. alknet-vault has no ALPN and no alknet-core dependency. Key derivation is local-only; only public key material crosses the network via `alknet/call`. The vault is a capability source — derived keys and decrypted credentials are injected into operation contexts at the assembly layer, not passed as vault references to handlers. See ADR-008.
- **Cross-references**: ADR-003, ADR-005, ADR-008
## Deferred Questions

View File

@@ -25,12 +25,12 @@ See [ADR-001](decisions/001-alpn-protocol-dispatch.md) for the full rationale.
## Crate Graph
```
alknet-secret (standalone, no alknet-core dependency)
alknet-vault (standalone, no alknet-core dependency)
alknet-core
│ ├── ProtocolHandler trait
│ ├── ALPN router / endpoint
│ ├── BiStream type
│ ├── BiStream trait, Connection type
│ ├── AuthContext, IdentityProvider
│ └── StaticConfig, DynamicConfig (ArcSwap)
@@ -44,15 +44,15 @@ alknet-core
├── alknet-napi (depends on alknet-call, napi-rs)
└── alknet (CLI binary, depends on all handler crates)
└── alknet (CLI binary, depends on all handler crates + alknet-vault)
```
Dependency rules:
- No handler crate depends on another handler crate
- All handler crates depend on alknet-core
- alknet-secret has zero alknet crate dependencies
- alknet-vault has zero alknet crate dependencies
- alknet-napi depends only on alknet-call (call protocol client)
- alknet (CLI) is the only crate that depends on all handler crates
- alknet (CLI) is the only crate that depends on all handler crates and alknet-vault
See [ADR-003](decisions/003-crate-decomposition.md) for the full decomposition rationale.
@@ -91,7 +91,7 @@ See [ADR-002](decisions/002-protocol-handler-trait.md) and [ADR-007](decisions/0
| `h3` | HttpAdapter (WebTransport upgrade) | Browser-compatible WebTransport, then ALPN upgrade |
| `h2` / `http/1.1` | HttpAdapter | Standard HTTP for browsers, curl |
> **Note**: `alknet/secret` is not in the ALPN registry. alknet-secret is a standalone crate with no alknet-core dependency. The CLI binary embeds it and exposes its operations through `alknet/call`. See ADR-008 for the integration rationale.
> **Note**: `alknet/vault` is not in the ALPN registry. alknet-vault is a standalone local key vault with no alknet-core dependency. The CLI binary embeds it and exposes its operations through `alknet/call`. The vault is a capability source — derived keys and decrypted credentials are injected into operation contexts at the assembly layer, not passed as vault references to handlers. See ADR-008 for the integration rationale.
## Authentication
@@ -181,7 +181,7 @@ All design decisions are documented as ADRs in [decisions/](decisions/).
| [005](decisions/005-irpc-as-call-protocol-foundation.md) | irpc as Call Protocol Foundation | Call protocol uses irpc for registry, framing, dispatch |
| [006](decisions/006-alpn-convention-and-connection-model.md) | ALPN String Convention and Connection Model | `alknet/` prefix, one ALPN per connection |
| [007](decisions/007-bistream-type-definition.md) | BiStream Type Definition | BiStream is a trait, handlers receive Connection not BiStream |
| [008](decisions/008-secret-service-integration.md) | Secret Service Integration Point | CLI-embedded, exposed via call protocol, no ALPN for secrets |
| [008](decisions/008-secret-service-integration.md) | Vault Integration Point | CLI-embedded, exposed via call protocol, vault is a capability source |
| [009](decisions/009-one-way-door-decision-framework.md) | One-Way Door Decision Framework | Classify decisions by reversal cost; one-way doors need ADRs |
## Open Questions
@@ -192,7 +192,7 @@ Open questions are tracked in [open-questions.md](open-questions.md). Key questi
- **OQ-02**: AuthContext resolution timing (resolved: hybrid — see ADR-004)
- **OQ-03**: ALPN string naming convention (resolved: see ADR-006)
- **OQ-04**: Dynamic handler registration at runtime vs static at startup (two-way door, defer to implementation)
- **OQ-08**: Secret service integration point (resolved: CLI-embedded via call protocol — see ADR-008)
- **OQ-08**: Vault integration point (resolved: CLI-embedded via call protocol — see ADR-008)
## Failure Modes