nerve_ipc_core/lib.rs
1//! Core IPC layer for the NERVE protocol.
2//!
3//! `nerve-ipc-core` provides the runtime infrastructure for local NERVE
4//! daemon communication: it accepts NERVE-framed binary messages from a
5//! browser extension (via WebSocket) and from local tools (via Unix Domain
6//! Socket), authenticates connections, dispatches frames, and manages
7//! per-connection request lifecycles.
8//!
9//! # When to use this crate
10//!
11//! Use `nerve-ipc-core` when you are building a transport layer for the
12//! NERVE protocol — a local daemon, a custom server, or a system-integration
13//! test harness.
14//!
15//! If you only need to encode and decode NERVE frames without running a
16//! server, use [`nerve-ipc`](https://crates.io/crates/nerve-ipc) directly.
17//! `nerve-ipc-core` does not re-export any `nerve-ipc` types; you will need
18//! `nerve-ipc` as a direct dependency to access types such as
19//! `nerve_protocol::types::RequestId` or the codec functions.
20//!
21//! # Quick start
22//!
23//! The daemon binary starts a WebSocket server (for the browser extension) and
24//! a UDS server (for local tools) concurrently:
25//!
26//! ```no_run
27//! use nerve_ipc_core::{Config, auth};
28//! use std::thread;
29//!
30//! fn main() -> std::io::Result<()> {
31//! let config = Config::default();
32//!
33//! // Load the per-install secret token, creating it on first run.
34//! let token = auth::load_or_create_token(&config.token_path)?;
35//!
36//! // Start the WebSocket server (browser extension transport) in a thread.
37//! let ws_config = config.clone();
38//! let ws_token = token.clone();
39//! thread::spawn(move || {
40//! nerve_ipc_core::ws_server::run_ws(ws_config, ws_token)
41//! .expect("WebSocket server failed");
42//! });
43//!
44//! // Start the Unix Domain Socket server (local tool transport).
45//! // This call blocks until the listener is closed.
46//! nerve_ipc_core::server::run(&config.uds_path)
47//! }
48//! ```
49//!
50//! # Core concepts
51//!
52//! ## Transports
53//!
54//! Two independent transports share the same dispatch logic:
55//!
56//! - **[`server`]** — Unix Domain Socket server for local tool integrations.
57//! Entry point: [`server::run`].
58//! - **[`ws_server`]** — WebSocket server on `127.0.0.1:9001` for the browser
59//! extension. Entry point: [`ws_server::run_ws`]. All connections are
60//! authenticated at the HTTP upgrade step via Origin check and per-install
61//! token.
62//!
63//! Both transports call the same [`dispatch_frame`] function:
64//!
65//! ```text
66//! Browser Extension ──WebSocket──▶ ws_server ──┐
67//! ├──▶ dispatch_frame() ──▶ RequestTable
68//! Local Tool ──UDS──────▶ server ──┘
69//! ```
70//!
71//! ## Dispatch
72//!
73//! [`dispatch_frame`] is transport-agnostic: it reads a decoded NERVE frame,
74//! updates the [`RequestTable`], and returns a [`DispatchAction`] telling the
75//! transport what to do next — write a reply, forward to the AI daemon, or do
76//! nothing. It performs no I/O itself.
77//!
78//! ## Request lifecycle
79//!
80//! Each accepted connection owns a [`RequestTable`] that tracks in-flight
81//! requests for that connection only. A `SearchQuery` frame causes
82//! [`dispatch_frame`] to insert the request and return
83//! [`DispatchAction::ForwardToAiDaemon`]. A `Cancel` frame marks it
84//! [`RequestState::Cancelled`]. The AI daemon checks
85//! [`RequestTable::is_cancelled`] before each result and calls
86//! [`RequestTable::remove`] on completion. Request IDs are scoped per
87//! connection; the same ID on two different connections refers to two
88//! independent requests.
89//!
90//! # Relationship to nerve-ipc
91//!
92//! | Crate | Role |
93//! |---|---|
94//! | [`nerve-ipc`](https://crates.io/crates/nerve-ipc) | Wire format, codec, frame types, protocol constants |
95//! | `nerve-ipc-core` *(this crate)* | Authentication, request lifecycle, dispatch, UDS server, WebSocket server |
96//!
97//! `nerve-ipc-core` does not redefine any protocol types. All frame encoding
98//! and decoding is performed by `nerve-ipc`. Types such as
99//! `nerve_protocol::types::RequestId` and `nerve_protocol::Frame` come from
100//! `nerve-ipc` and appear directly in this crate's public API.
101
102pub use config::Config;
103pub use dispatch::{DispatchAction, dispatch_frame};
104pub use request_table::{RequestState, RequestTable};
105
106pub mod auth;
107pub mod config;
108pub mod dispatch;
109pub mod request_table;
110pub mod server;
111pub mod ws_server;