104 lines
8.3 KiB
Markdown
104 lines
8.3 KiB
Markdown
# 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<T>` — 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`).
|