Apply #[rpc_requests(message = SecretMessage)] to SecretProtocol enum with
#[rpc(tx=oneshot::Sender<Result<T, SecretServiceError>>)] and #[wrap] attributes
on each variant. Add SecretServiceActor that wraps SecretServiceHandle and
processes SecretMessage variants via mpsc channel. Update DerivedKey
serialization to use is_human_readable() so postcard preserves private_key
bytes while JSON redacts them. Add Serialize/Deserialize to SecretServiceError
for irpc wire format compatibility. Add tokio dependency for actor runtime.
Per ADR-038, DerivedKey.private_key now derives Zeroize with #[zeroize(drop)]
ensuring sensitive key material is zeroized before deallocation. DerivedKey
is now move-only (no Clone), and JSON/debug output redacts private_key as
"[REDACTED]". Deserialization still works for postcard/irpc wire format.
Also fixes clippy needless_borrows_for_generic_args in encryption.rs and
applies cargo fmt to existing code.
- Add irpc (0.16) and irpc-derive (0.16) as workspace dependencies
- Add irpc, irpc-derive, and secp256k1 (optional) to alknet-secret Cargo.toml
- Clarify encryption-salt-kdf task: Option B (document salt as reserved) is the
chosen path per spec update, removing Option A acceptance criteria
- Update irpc-secret-protocol-integration task with concrete irpc crate details:
real crate on crates.io v0.16, #[rpc_requests] macro, workspace config,
AuthProtocol pattern reference, DerivedKey serialization considerations
- Fix secp256k1-ethereum-derivation task: correct crate name is secp256k1
(not libsecp256k1), add version pin 0.29
Create the alknet-secret crate with BIP39 mnemonic generation, SLIP-0010
Ed25519 HD key derivation, AES-256-GCM encryption, and SecretProtocol
irpc service definition. This is Phase 3.1 from the integration plan.
Architecture changes:
- Promote secret-service.md to reviewed status with full spec format
(crate structure, public API, security model, phase progression,
ADR/OQ cross-references, wire format compatibility section)
- Add ADR-038 (seed lifecycle and memory security): zeroize for v1,
mlock deferred to Phase B
- Add OQ-SEC-01 (mlock/VirtualLock for seed RAM) to open-questions.md
- Update README.md with ADR-038 and secret-service status
Crate structure:
- src/mnemonic.rs: BIP39 phrase generation, validation, seed derivation
- src/derivation.rs: SLIP-0010 HD key derivation, path constants (74')
- src/encryption.rs: AES-256-GCM encrypt/decrypt, EncryptedData type
- src/protocol.rs: SecretProtocol irpc enum, DerivedKey, KeyType
- src/service.rs: SecretServiceHandle with Unlock/Lock lifecycle
- 40 passing tests (unit + integration + doc)
Add http feature flag with axum, hyper, hyper-util, tower, and http-body-util
dependencies. Create http module with auth middleware (extracts Bearer token,
calls IdentityProvider::resolve_from_token, attaches Identity to extensions)
and router scaffold (default 404 fallback, no operational routes yet). Replace
send_fake_nginx_404 with axum router handoff when http feature is enabled;
fake 404 behavior preserved when http is disabled. Wire HttpInterface with
build_router() method and pass IdentityProvider through Server to handle_connection.
- Add reloadAuth(), reloadForwarding(), reloadAll() methods to AlknetServer
- Add NAPI type definitions: AuthConfigNapi, ForwardingPolicyConfig, ForwardingRuleConfig
- Refactor NapiServerHandler to use ArcSwap<DynamicConfig> for atomic config swaps
- Add ConfigReloadHandle::dynamic_arc() accessor for sharing ArcSwap between NAPI and accept loop
- Add ipnetwork dependency to alknet-napi for TargetPattern CIDR parsing
- Add builder functions for AuthPolicy and ForwardingPolicy from NAPI config types
- All swaps are atomic via ArcSwap per ADR-030
Split alknet-core configuration into StaticConfig (immutable after startup)
and DynamicConfig (hot-reloadable at runtime via ArcSwap).
- Add StaticConfig struct in config/static_config.rs with all fields per ADR-030
- Add DynamicConfig struct with AuthPolicy, ForwardingPolicy, RateLimitConfig
- Add ForwardingPolicy with allow_all()/deny_all() defaults (ADR-031)
- Add ConfigReloadHandle with reload() method for runtime config updates
- Replace Arc<ServerAuthConfig> with Arc<ArcSwap<DynamicConfig>> in ServerHandler
- Add config_reload_handle() to Server for obtaining reload handles
- Add AuthPolicy with authenticate_publickey/authenticate_certificate methods
- All existing tests pass with the new config structure
- Default DynamicConfig produces identical behavior to current code
- Restrict auth methods to PUBLICKEY only (no none, password, hostbased,
or keyboard-interactive advertised during negotiation)
- Log all denied channel types (session, x11, forwarded-tcpip) and
dangerous request types (exec, shell, subsystem, pty, env, x11, agent)
- Explicitly reject all dangerous channel request handlers (exec, shell,
subsystem, pty, env, x11, agent forwarding) with channel_failure
responses instead of russh's default silent Ok(()) which leaves clients
hanging and is a footgun if session channels are ever allowed
- Explicitly reject tcpip_forward, streamlocal_forward with logged warnings
- Log signal requests at debug level (harmless, no response needed)
- Override handlers in both core ServerHandler and NapiServerHandler
- Add tracing dependency to wraith-napi for security event logging
- Set preferred algorithms explicitly (russh::Preferred::DEFAULT which
uses only modern KEX/cipher/MAC algorithms)
Expose NAPI serve() per ADR-016. WraithServer provides close() and
onConnection(callback) for receiving SSH channel streams from
incoming connections. Each connection produces a WraithServerStream
(Duplex-like read/write/close) with ConnectionInfo (remoteAddr,
transportKind). Supports TCP transport with optional authorizedKeys
and certAuthority auth. TLS and iroh transports return helpful errors
indicating future support.
- Add WraithConnectOptions struct with napi fields: server, peer, transport,
identity (string path or Buffer), tlsServerName, insecure, irohRelay, proxy
- Add WraithStream napi class wrapping SSH channel read/write halves via
ChannelStream::into_stream() + tokio::io::split()
- Implement connect() async function: transport creation (tcp, tls), SSH client
connection, authenticate, open direct_tcpip channel, return WraithStream
- Identity field accepts file path (string) or in-memory key data (Buffer)
- All Rust errors marshalled to JavaScript exceptions with descriptive messages
- Add ForwardError enum to wraith-core (required by forward.rs)
- Enable tls, iroh features on wraith-core dependency
- 7 unit tests for key source resolution and address parsing
Add iroh QUIC P2P transport using tokio::io::join for stream duplexing
per ADR-003. Default relay is n0's https://relay.iroh.network/ (ADR-009).
Proxy URL passed to Endpoint::builder (ADR-010). Integration test marked
#[ignore] for CI since it requires iroh relay connectivity.