# OpenSpec Change: native-messaging - **Change ID**: native-messaging - **Package**: `@suaveplan/native-messaging` - **Status**: PROPOSED - **Date**: 2026-04-29 ## Summary Create `@suaveplan/native-messaging`, a typed, encrypted transport layer between browser extensions and the desktop vault application. The package abstracts Chrome stdio, Firefox stdio, and Safari `NSExtensionContext` behind a single interface, using X25519 key exchange for handshake and ChaCha20-Poly1305 for per-frame encryption. ## Why The Vaulted password-manager architecture requires browser extensions to communicate with the desktop app without exposing vault keys inside the extension runtime. No existing Suaveplan package provides a cross-browser native-messaging abstraction with built-in encryption. External libraries either lack typed message framing or bundle their own crypto primitives, which violates the monorepo's policy of reusing `@suaveplan/crypto-curve` and `@suaveplan/crypto-cipher`. ## What changes - ADDED: `NativeMessagingClient` — platform-agnostic encrypted channel client used by browser extension background scripts. - ADDED: `NativeMessagingHost` — desktop-side encrypted channel host accepting connections from extensions. - ADDED: `Handshake` — X25519 ephemeral key exchange producing a shared secret and session keys. - ADDED: `FrameEncoder` / `FrameDecoder` — length-prefixed, ChaCha20-Poly1305-encrypted message framing with AAD. - ADDED: `ChromeTransport`, `FirefoxTransport`, `SafariTransport` — platform-specific transport adapters behind a common `Transport` interface. - ADDED: `TypedMessage` — discriminated-union envelope for request/response/error messages with Zod schema validation at boundaries. - ADDED: `ReconnectPolicy` — configurable exponential-backoff reconnection with jitter and max-retry caps. - ADDED: `NativeMessagingError` — typed error hierarchy (`HandshakeFailed`, `FrameCorrupted`, `TransportClosed`, `Timeout`) with error codes. ## Out of scope - Mobile Capacitor bridge communication — mobile uses in-process bridging, not native messaging. - Biometric unlock flow integration — `@suaveplan/biometrics` will orchestrate that layer. - Vault item CRUD logic — belongs in `@suaveplan/vault`. - WebSocket or WebRTC fallbacks — separate transport packages already exist in the monorepo. - Extension content-script injection — handled by `@suaveplan/form-detection` and extension build pipelines. ## Gap analysis: sub-standard → enterprise Most browser-extension ↔ native-application bridges ship plaintext JSON over stdio with no authentication, no replay protection, and no message-level typing. This leaves them vulnerable to man-in-the-browser attacks, host-process impersonation, and trivial message injection. Enterprise deployments require encrypted, authenticated, typed, and recoverable channels. This proposal closes those gaps by mandating X25519 key exchange, ChaCha20-Poly1305 per-frame AEAD, Zod-validated discriminated-union messages, and configurable reconnection with session isolation. ## Security model - **Threat model**: Malicious extension impersonation, compromised host process, man-in-the-browser, replay attacks, and ephemeral-key exfiltration. - **Encryption boundaries**: X25519 ephemeral key exchange for handshake; ChaCha20-Poly1305 for every frame with AAD binding the message type and sequence number. - **Input validation**: Zod schemas at all public boundaries (`TypedMessage`, `HandshakePayload`, `FrameHeader`). Any validation failure terminates the session. - **Side-channel resistance**: Constant-time shared-secret comparison during handshake; nonce generation via monorepo CSPRNG (`@suaveplan/crypto-curve`). - **Key zeroization**: Session keys and ephemeral private keys are explicitly overwritten with zero-filled buffers immediately after handshake completion or transport disconnect. - **Privacy guarantees**: No vault plaintext, no passwords, and no biometric data traverse the channel or appear in logs. Logs contain only error codes, connection IDs, and frame sequence numbers. ## Performance SLOs - **Handshake latency**: < 50 ms p99 on target hardware. - **Per-frame crypto**: < 1 ms for encryption or decryption p99. - **Throughput**: ≥ 1 000 messages/sec per active channel. - **Memory**: < 2 MB per active connection (keys, buffers, pending queue). - **Bundle size**: client core < 10 KB gzipped; host core < 15 KB gzipped; tree-shakeable by transport adapter. ## Observability plan - **OTel spans**: `native_messaging.handshake`, `native_messaging.frame.encode`, `native_messaging.frame.decode`, `native_messaging.transport.send`, `native_messaging.transport.receive`. - **Metrics**: active connection gauge, reconnection rate counter, frame error rate counter, handshake duration histogram. - **Structured logging**: JSON-only, with `trace_id`, `connection_id`, and `error_code`; never includes payloads or keys. - **Error telemetry**: automatic capture of `FrameCorrupted`, `HandshakeFailed`, and `TransportClosed` with stack-traces scrubbed of PII. - **Health checks**: `NativeMessagingHost` exposes an internal `health()` returning active connection count, last error timestamp, and handshake success rate over the last 60 s. ## Dependency audit - `@suaveplan/crypto-curve` — X25519 primitives. Justified: monorepo policy, audited, no external WebCrypto leakage. - `@suaveplan/crypto-cipher` — ChaCha20-Poly1305. Justified: monorepo policy, audited, constant-time where applicable. - `zod` — runtime schema validation. Justified: existing monorepo standard, MIT license, fully tree-shakeable. - **Zero additional runtime dependencies**. Supply-chain risk is minimized via lockfile pinning and provenance manifest generation. All listed deps are tree-shakeable and carry permissive licenses. ## Operational readiness - **Graceful degradation**: If the handshake fails or the transport closes unexpectedly, the client surfaces a typed `HandshakeFailed` or `TransportClosed` error. There is no silent fallback to plaintext. - **Resource limits**: Max 64 concurrent connections per host; max frame size 1 MB; max pending send queue depth 256 messages. - **Error recovery and reconnection**: `ReconnectPolicy` provides exponential backoff with full jitter, capped retry count, and per-session isolation so a poisoned session cannot corrupt a new one. - **Backpressure**: Bounded send buffer; exceeding the bound throws `BackpressureExceeded` rather than allowing unbounded memory growth. - **Version compatibility**: Major-version negotiation during handshake; incompatible versions terminate immediately with a typed `VersionMismatch` error. ## Verification approach - `src/handshake/handshake.test.ts` — covers X25519 key generation, shared-secret derivation, and session-key split. - `src/frame/frame.test.ts` — covers encryption, decryption, AAD integrity, tampered-frame rejection, and nonce sequencing. - `src/transport/transport.test.ts` — covers Chrome/Firefox/Safari adapter lifecycle, `send`, `receive`, `close`, and event emission. - `src/client/client.test.ts` — covers typed request/response round-trips, timeout handling, and error propagation. - `src/host/host.test.ts` — covers host accept/reject, concurrent connections, and session isolation. - `src/reconnect/reconnect.test.ts` — covers backoff timing, jitter distribution, and max-retry exhaustion. - `src/error/error.test.ts` — covers error code uniqueness, `instanceof` branches, and serialization round-trip. - `src/spec-conformance.test.ts` — pins no-internal-CSPRNG invariant, subpath-export count, and sibling-doc presence. - `tests/dep-graph.test.ts` — pins dependency invariants (only `@suaveplan/crypto-curve` and `@suaveplan/crypto-cipher`). - **100 % coverage target** for all non-test source files. - **Property-based tests** (`fast-check`): frame round-trip invariants, nonce uniqueness across 10 000 iterations, backoff monotonicity and jitter bounds. - **Load / stress criteria**: 10 000 concurrent handshakes without memory growth; 1 000 000 frame round-trips with < 0.01 % corruption; 24-hour churn test with no handle leaks. - **Security audit checklist**: key zeroization verified in heap dumps; replay-attack simulation rejected by AAD; tampered-frame rejection rate 100 %; CSPRNG bypass attempts blocked. ## Migration impact None — additive only. ## Impact ### Affected dependents No dependents (verified by `bunx turbo ls --filter=...@suaveplan/native-messaging`).