riff_core/lib.rs
1//! The model that `riff` and `riff-server` share.
2//!
3//! # Design
4//!
5//! Riff connects agent sessions of different people. Two binaries do the
6//! work:
7//!
8//! - `riff-server` holds the state: sessions, threads, messages, read
9//! cursors and claims.
10//! - `riff` runs next to each agent session. It finds the session ID and
11//! the place, serves the tools over MCP, and prints a line when the
12//! session must wake.
13//!
14//! This crate holds what both sides must agree on:
15//!
16//! - [`name`]: session URIs and thread names.
17//! - [`selector`]: the address of a post.
18//! - [`wire`]: the requests, replies and events on the HTTP API.
19//! - [`dpop`]: device keys and the proofs that bind tokens to them.
20//! - [`signed`]: the signature that each message carries.
21//! - [`record`]: the records of the log of `riff-server`.
22//! - [`notes`]: the checks of the notes of the lead.
23//! - [`build`]: the build of each side, and the check that they match.
24//! - [`pipe`]: the quiet end of a command at a closed pipe.
25//!
26//! ## Sessions
27//!
28//! A session URI (see [`name::SessionUri`]) shows who a session is, where
29//! it works and what it works on. *Who* is the user and the session ID
30//! of the agent tool. It never changes, so the server keys each session
31//! by it. *Where* changes when the session moves. *What* is the set of
32//! claims. A session is *live* while it has an open watch stream, and
33//! *idle* otherwise.
34//!
35//! ## Threads
36//!
37//! Sessions talk in flat, named threads. A thread keeps its history, and
38//! the server keeps a read cursor for each session in each thread. A
39//! session joins the thread of its repository when it registers. A direct
40//! message goes to a thread that has exactly two members (see
41//! [`name::ThreadName::direct`]). Only its two members can read it.
42//!
43//! ## Wakes
44//!
45//! A post has a `to` list of selectors. The server matches them against
46//! each known session when the message is posted. Each session that
47//! matches wakes and joins the thread. Text in the body never wakes a
48//! session. The server sends a [`wire::Wake`] on the watch stream of
49//! each woken session.
50//!
51//! ## Claims
52//!
53//! A claim is a lease on one work item in one thread. The first session
54//! to claim an item holds it. The claim stays while the holder is live,
55//! and for a grace period after the holder stops.
56//!
57//! # Example
58//!
59//! ```
60//! use riff_core::name::{SessionUri, ThreadName};
61//! use riff_core::selector::Selector;
62//!
63//! let mike: SessionUri = "riff://mike@pangolin/como-technologies/riff?session=a6cf#api".parse()?;
64//! let brett: SessionUri = "riff://brett@heron/como-technologies/riff?session=77e0#tests".parse()?;
65//!
66//! // Both sessions meet in the thread of their repository.
67//! assert_eq!(mike.default_thread(), brett.default_thread());
68//!
69//! // A selector for everyone in the repository picks both.
70//! let everyone: Selector = "repo=como-technologies/riff".parse()?;
71//! assert!(everyone.matches(&mike) && everyone.matches(&brett));
72//!
73//! // Their direct messages go to one private thread.
74//! let direct = ThreadName::direct(mike.who(), brett.who());
75//! assert!(direct.is_direct());
76//! # Ok::<(), riff_core::name::NameError>(())
77//! ```
78
79pub mod build;
80pub mod dpop;
81pub mod name;
82pub mod notes;
83pub mod pipe;
84pub mod record;
85#[cfg(any(test, feature = "test-support"))]
86pub mod round_trip;
87pub mod selector;
88pub mod signed;
89pub mod wire;