Skip to content

feat: NMH dual-path IPC — WebSocket primary + NMH fallback#4

Merged
easyvibecoding merged 1 commit intomainfrom
feat/native-messaging-host
Mar 18, 2026
Merged

feat: NMH dual-path IPC — WebSocket primary + NMH fallback#4
easyvibecoding merged 1 commit intomainfrom
feat/native-messaging-host

Conversation

@easyvibecoding
Copy link
Copy Markdown
Owner

Summary

  • Upgrade Native Messaging Host from config-only to full WS relay, enabling Chrome Extension to submit keys and query state when
    WebSocket is disconnected (SW wake, Core restart)
  • New sendRequest() unified dispatch in service-worker.ts: WS → NMH fallback with request-response correlation + 5s timeout
  • Popup shows three connection states: Connected (WebSocket) green / Connected (NMH) blue / Offline red
  • NMHInstaller auto-installs NMH binary + Chrome manifest on Core startup

Changes

NativeMessagingHost.swift

  • Add WS relay support: get_state, submit_captured_key, toggle_demo_mode
  • Short-lived URLSessionWebSocketTask connections (~20-60ms)
  • clientType "nmh", 5s timeout, auto-skips event messages
  • writeJSON fallback on serialization failure

IPCServer.swift

  • Add .nmh case to ClientType enum
  • broadcast() skips .nmh clients (short-lived connections don't receive events)

service-worker.ts

  • sendRequest() unified entry: WS → NMH fallback
  • pendingRequests Map for WS request-response tracking (with reject on close)
  • ws.onclose properly rejects all pending promises so NMH fallback triggers
  • Security red line: submit_captured_key does NOT queue plaintext keys to chrome.storage.local on dual failure
  • NMH get_state does NOT set isConnected = true (NMH is one-shot relay, not persistent)
  • Silent errors replaced with structured console.warn logging

popup.ts / popup.html

  • Connection path display: WebSocket (green) / NMH (blue) / Offline (red)
  • Capture mode visible when NMH is reachable

NMHInstaller.swift (new file)

  • Auto-checks and installs NMH binary + Chrome manifest on Core startup
  • Binary size comparison for version checking
  • install.sh retained as manual fallback

Documentation

  • CLAUDE.md: 4 new Key Technical Decisions
  • implementation-status.md: NMH ❌→✅
  • chrome-extension-architecture.md: NMH section rewritten (dual-path diagram)
  • extension-boundaries.md: NMH spec table expanded
  • protocol-spec.md: new NMH Relay section + clientType nmh

Security

Test plan

  • swiftc NativeMessagingHost.swift compiles successfully (93KB)
  • NMH get_config returns correct port/token
  • NMH get_state relay returns isDemoMode + activeContext (~23ms)
  • NMH toggle_demo_mode relay toggles correctly (~18ms)
  • NMH submit_captured_key relay stores successfully (~56ms)
  • Core offline → NMH returns core_unreachable (<100ms)
  • Unknown action → structured error response
  • Popup: debugDisconnectWS() → Offline → reopen popup → Connected (NMH) blue dot
  • Popup: debugReconnectWS() → Connected (WebSocket) green dot
  • Swift Core build passes
  • TypeScript type-check + lint + build pass

🤖 Generated with Claude Code

Upgrade Native Messaging Host from config-only to full WS relay,
enabling Chrome Extension to submit keys and query state even when
WebSocket is disconnected (e.g., Service Worker wake, Core restart).

- NativeMessagingHost.swift: add WS relay for get_state,
  submit_captured_key, toggle_demo_mode via URLSessionWebSocketTask
- IPCServer.swift: add .nmh clientType, skip in broadcast
- service-worker.ts: sendRequest() dual-path dispatch with
  pending request tracking, NMH fallback, structured error logging
- popup.ts/html: show connection path (WebSocket/NMH/Offline)
  with color-coded dots (green/blue/red)
- NMHInstaller.swift: Core auto-installs NMH binary + manifest
- ipc-protocol: add 'nmh' to clientType union
- Security: plaintext keys are NOT queued to chrome.storage
- Docs: update all architecture, protocol, and status docs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@easyvibecoding easyvibecoding merged commit ccfabb1 into main Mar 18, 2026
2 checks passed
@easyvibecoding easyvibecoding deleted the feat/native-messaging-host branch March 18, 2026 02:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant