Skip to main content

nomoreide_remote_protocol/
lib.rs

1//! The frozen v1 wire protocol between a local NoMoreIDE daemon and the hosted
2//! remote-control relay.
3//!
4//! Nothing here talks to a socket, a service or an agent. It is the shape of
5//! what may be said and the rules for refusing everything else.
6//!
7//! **Why this is its own package.** Two independently deployed programs speak
8//! this protocol — the daemon on a developer's machine and the platform's API
9//! container, which live in different repositories and different Cargo
10//! workspaces. Writing it twice would make it two implementations of one
11//! meaning, which is the mistake the desktop app already made and is still
12//! paying 150 duplicated commands for. So it is one implementation, in a
13//! package light enough for both: `serde`, `serde_json`, `chrono`, and nothing
14//! else. The daemon reaches it through `nomoreide_core::remote::protocol`.
15//!
16//! The daemon always dials **out** over TLS. Nothing in this protocol opens a
17//! port, asks for a port forward, or accepts an inbound connection — a machine
18//! running NoMoreIDE is not reachable from the internet because of this
19//! feature, and that is the property the whole design is arranged around.
20//!
21//! Where to look:
22//!
23//! - [`envelope`] — the invariant frame, and the order a frame is checked in.
24//! - [`device_bound`] — every command the platform may send. This union *is*
25//!   the remote attack surface.
26//! - [`platform_bound`] — every event a daemon may send.
27//! - [`snapshot`] and [`agent_event`] — the sanitized shapes those carry.
28//! - [`limits`] — every number either side is allowed to assume.
29//! - [`errors`] — the refusal codes, and which of them may ever be retried.
30//! - [`version`] — negotiation, capabilities, and what a phone does against a
31//!   machine that has not been updated.
32//! - [`idempotency`] — why a mutation is never automatically re-sent.
33//! - [`fixtures`] — one sample of every frame, and the golden files an
34//!   independent implementation checks itself against.
35//!
36//! Three properties hold across all of it, and each has a test that fails if it
37//! stops holding:
38//!
39//! 1. **Unknown is refused, not ignored.** A `type` outside the union is
40//!    [`errors::ErrorCode::UnknownCommand`] in either direction, whatever its
41//!    payload looks like.
42//! 2. **No frame can carry what the allowlist excludes.** There is no variant
43//!    for a shell command, a path, an environment, a terminal, a database
44//!    query, a git mutation or a process id — so no payload can smuggle one.
45//! 3. **Ambiguity never retries.** A mutation whose outcome is unknown is a
46//!    question for a human, not a frame to send again.
47//!
48//! The contract in prose, including the threat model, is
49//! `docs/remote-protocol-v1.md` in the repository.
50
51pub mod agent_event;
52pub mod device_bound;
53pub mod envelope;
54pub mod errors;
55pub mod fixtures;
56pub mod idempotency;
57pub mod limits;
58pub mod linear;
59pub mod platform_bound;
60pub mod snapshot;
61pub mod terminal_bytes;
62pub mod version;
63
64pub use device_bound::DeviceBound;
65pub use envelope::Envelope;
66pub use errors::{ErrorCode, ProtocolError};
67pub use platform_bound::PlatformBound;
68pub use terminal_bytes::TerminalBytes;
69pub use version::{Capability, CapabilitySet, SessionMode, PROTOCOL_VERSION};
70
71#[cfg(test)]
72mod tests;