Add ADR-026 (vault key model — HD derivation) recording the foundational
HD-derivation decision, 74' coin type reservation, SLIP-0010/Ed25519
default, secp256k1 feature-gating, and AES-256-GCM cipher choice. These
were previously inline rationale with no ADR (W9).
Extend ADR-018 with an explicit EncryptedData wire format lock — fields,
encoding, and semantics are frozen; no removal without a format-version
migration (W10).
Resolve the remaining guard clauses and spec decisions:
- W2: Capabilities must be immutable after construction (no interior
mutability). Makes the Arc vs deep-copy clone semantics genuinely
two-way.
- W5: Published to_* specs are compatibility contracts — best-effort
mappings are two-way before first publication, one-way after. Version
generated specs.
- W6: Salt field clarification — v2 salt is permanently unused; a future
KDF is a different derivation family, not a version-indexed path; the
field saves a wire-format change only.
- W7: unlock_new returns Zeroizing<String> — the mnemonic is the root of
trust and must not linger in freed memory.
- W17: OQ-09 WASM — server-side dispatch door is honestly closed
(Connection is concrete, tokio-bound), not implicitly preserved.
- W18: OQ-10 git — composability fork (raw smart protocol vs call-protocol
projection) is a separate decision from ERC721 scope.
- W20: from_openapi must prefix imported error codes (HTTP_404) to avoid
collision with protocol-level codes (NOT_FOUND). Normative rule, not
naming convention.
- W21: ScopedOperationEnv field is private — construction via new()/
empty(), query via allows(). Makes the future subgraph refactor
non-breaking.
- C13: Connection::set_identity — the endpoint does not read identity()
after handle() returns (Connection is moved into the spawned task).
Observability is handler-side logging. Simplest honest answer.
- W1: OperationAdapter trait is async, returns Vec<HandlerRegistration>.
from_call requires async discovery; ADR-022 changed the return type.
- W11: CompositionAuthority::as_identity() defined — constructs a
synthetic Identity (label as id, scopes, resources) not resolvable via
IdentityProvider. Second Identity construction path, acknowledged.
- W14: SecretKey is iroh::SecretKey (Ed25519) — consistent with the
endpoint's iroh dependency.
- W19: Grandchild abort propagation is inherit-by-default (option a) —
invoke() with no explicit policy inherits parent's policy. ContinueRunning
auto-propagates to grandchildren unless explicitly overridden.
ADR-022 wires the three controls ADR-015 specified but left without
registration paths (C1-C4 from review #001): composition authority,
scoped env, and capabilities now enter through a HandlerRegistration
bundle. Provenance (Local, FromOpenAPI, FromMCP, FromCall, Session)
determines which ops can compose — leaves don't get composition
authority. CompositionAuthority replaces handler_identity: Identity
(it's a declared authority bundle, not a peer identity). Capabilities
are per-request from the bundle (resolves closure-capture vs context
ambiguity). Kernel/user analogy: user's authority checked at External
gate; handler's composition authority used inside; scoped env bounds
reachability.
Also fixes W1 (stale ADR-020 path example) and W3 (from_mcp missing
from adapter lists in operation-registry.md).
Spec updates: operation-registry.md (OperationRegistry,
HandlerRegistration, OperationContext, OperationEnv, registration
example, capability injection), call-protocol.md (build_root_context),
README.md, overview.md, open-questions.md (OQ-23), call/README.md.
The vault uses SLIP-0010 HD derivation from the BIP39 seed for the
AES-256-GCM encryption key, not PBKDF2. This replaces the TypeScript
predecessor's (@alkdev/storage/src/graphs/crypto.ts) PBKDF2-based
approach.
Key decisions:
- HD derivation at m/74'/2'/0'/0' produces the encryption key
- PBKDF2 is not implemented in the vault; no password-based derivation
- salt field is unused in v2 (wire-format compat only)
- key_version=1 reserved for TS PBKDF2 data; key_version=2 for vault HD
- TS-encrypted data requires one-time migration to v2
- CURRENT_KEY_VERSION changes from 1 to 2 (source drift flagged)
OQ-20 resolved: the encryption key derivation method is locked. OQ-22
(key rotation workflow) remains open but does not block implementation.