Condense the documentation and thin the server's comments

The markdown had accumulated a lot that was stale rather than wrong.
PLAN.md still described pi as the llama.cpp harness, a refcounted
LlamaServerManager, and a providers-by-hosts cross-product, all of which
were superseded or never built; it also carried a second copy of the HTTP
table that routes.rs owns. EXPLORER.md and TRANSCRIPT_CACHE.md held
implementation checklists for work that has since landed. AGENTS.md
restated most of PLAN.md's design instead of being the working-notes
layer it says it is. 3225 lines of markdown to 2180, with the stale
sections gone rather than reworded.

On the server, comments explaining what the code already says are out and
the ones recording a constraint, a measurement or an incident are kept but
cut to a few lines each: 5504 comment lines to 4586.

Four doc comments in session/mod.rs, and one each in process.rs and
usage.rs, had drifted onto the item above the one they describe --
functions were reordered without them, so `stop_session`'s doc sat on
`set_session_cwd`, `stat_of`'s on `struct Stat`, and `UsageMonitor`'s on
`type Cached`. Each is back on its own item.

routes.rs's module table also claimed later phases would add `/hosts`,
which setups replaced.

cargo test (127 passed), clippy --all-targets and fmt are clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
irisandClaude Opus 5 committed 2026-09-04 15:45:43 -04:00
1 parent e3e02d55f7
commit 79682f03a7
24 files changed
+4572 -6821

No files matched your search

+53 -70
View File
@@ -1,50 +1,43 @@
//! Finding out what a machine can run, rather than being told.
//!
//! The phone adds a machine by giving connection details; this asks the
//! machine itself which of the known programs it has, and the answer
//! becomes its providers. That is a security property, not a convenience:
//! **no route accepts a command from the phone.** If it did, the enrolled
//! token would be able to introduce arbitrary programs to run on every
//! machine a setup names, and the transport already reaches those over
//! ssh. Here the phone's authority is "add this machine", never "run
//! this".
//! The phone adds a machine by giving connection details; this asks the machine
//! itself which of the known programs it has, and the answer becomes its
//! providers. That is a security property, not a convenience: **no route accepts
//! a command from the phone.** If it did, the enrolled token could introduce
//! arbitrary programs to run on every machine a setup names.
//!
//! It is also the better interface. Nobody wants to type an absolute path
//! on a phone keyboard, and a machine that has moved its binaries answers
//! correctly on the next probe without anyone editing anything.
//! It is also the better interface: nobody wants to type an absolute path on a
//! phone keyboard, and a machine that has moved its binaries answers correctly
//! on the next probe.
//!
//! The cost is that a program somewhere unusual is invisible. That is a
//! deliberate trade rather than an oversight: the escape hatch is editing
//! `config.ron` on the backend, which is exactly the authority the phone
//! is not being given.
//! The cost is that a program somewhere unusual is invisible. The escape hatch
//! is editing `config.ron` on the backend, which is exactly the authority the
//! phone is not being given.
use anyhow::Result;
use crate::config::{DriverKind, ProviderConfig};
use crate::session::transport::{Launch, Transport};
/// What is looked for, and what finding it makes.
///
/// Extending this is how a new driver becomes discoverable -- one row, not
/// a branch anywhere. The name is what the provider gets called, so it is
/// what the phone shows and what a session stores.
/// What is looked for, and what finding it makes. Extending this is how a new
/// driver becomes discoverable -- one row, not a branch anywhere. The name is
/// what the provider gets called, so it is what the phone shows and what a
/// session stores.
const PROBES: &[(&str, &str, DriverKind)] = &[
("claude-cli", "claude", DriverKind::ClaudeCli),
("local-llama", "llama-server", DriverKind::LlamaCpp),
];
/// Models offered for a discovered Claude CLI. A shortcut list for the
/// spawn screen, not a restriction -- the field stays free text.
/// Models offered for a discovered Claude CLI. A shortcut list for the spawn
/// screen, not a restriction -- the field stays free text.
const CLAUDE_MODELS: &[&str] = &["fable", "opus", "sonnet", "haiku"];
/// Asks `transport`'s machine which of [`PROBES`] it has.
///
/// One round trip rather than one per program: over ssh each would be a
/// separate connection and handshake, and a person waiting on "test this
/// setup" notices. `command -v` is POSIX and a shell builtin, so it works
/// whatever is installed -- and `|| true` keeps a missing program from
/// ending the loop, since the caller wants the whole answer rather than
/// the first failure.
/// One round trip rather than one per program: over ssh each would be a separate
/// connection and handshake. `command -v` is POSIX and a shell builtin, so it
/// works whatever is installed -- and `|| true` keeps a missing program from
/// ending the loop, since the caller wants the whole answer.
pub async fn discover(transport: &Transport) -> Result<Vec<ProviderConfig>> {
let wanted: Vec<&str> = PROBES.iter().map(|(_, binary, _)| *binary).collect();
let script = format!(
@@ -55,9 +48,9 @@ pub async fn discover(transport: &Transport) -> Result<Vec<ProviderConfig>> {
let found = transport.capture(&launch).await.map_err(explain)?;
let mut providers = Vec::new();
// Echo runs inside this server, so it exists exactly where this server
// does and nowhere else. Nothing to probe for, and offering it on a
// remote machine would be a choice that changes nothing.
// Echo runs inside this server, so it exists exactly where this server does
// and nowhere else. Offering it on a remote machine would be a choice that
// changes nothing.
if matches!(transport, Transport::Here) {
providers.push(ProviderConfig {
name: crate::config::ECHO_PROVIDER.to_string(),
@@ -78,8 +71,8 @@ pub async fn discover(transport: &Transport) -> Result<Vec<ProviderConfig>> {
name: (*name).to_string(),
kind: *kind,
// The resolved path rather than the bare name: PATH under a
// non-interactive ssh session is not the one a person sees
// when they log in, so "it is on my PATH" is not enough.
// non-interactive ssh session is not the one a person sees when they
// log in, so "it is on my PATH" is not enough.
command: Some(path.to_string()),
models: match kind {
DriverKind::ClaudeCli => CLAUDE_MODELS.iter().map(|m| (*m).to_string()).collect(),
@@ -92,16 +85,14 @@ pub async fn discover(transport: &Transport) -> Result<Vec<ProviderConfig>> {
/// Adds what to do to failures whose own wording does not say.
///
/// ssh's messages are written for someone at a terminal on the backend,
/// which is exactly who is not reading this one. Host key verification is
/// the case that matters: **every** machine fails it the first time,
/// because its key is not in `known_hosts` yet -- so without this, adding
/// a machine from the phone looks broken rather than unfinished.
/// ssh's messages are written for someone at a terminal on the backend, which is
/// exactly who is not reading this one. Host key verification is the case that
/// matters: **every** machine fails it the first time, so without this, adding a
/// machine from the phone looks broken rather than unfinished.
///
/// Deliberately not fixed by relaxing the check. `StrictHostKeyChecking`
/// stays at its default, so a first connection is a decision somebody
/// makes on the backend with the key in front of them, rather than
/// something this app quietly accepts on their behalf.
/// Deliberately not fixed by relaxing the check. `StrictHostKeyChecking` stays
/// at its default, so a first connection is a decision somebody makes on the
/// backend with the key in front of them.
fn explain(err: anyhow::Error) -> anyhow::Error {
let message = format!("{err:#}");
if message.contains("Host key verification failed") {
@@ -120,11 +111,9 @@ fn explain(err: anyhow::Error) -> anyhow::Error {
err
}
/// A short, stable, filename-safe id derived from a label.
///
/// Derived once when a setup is added and then fixed, so the label stays
/// editable. Collisions are resolved by the caller, which is the only
/// place that knows what already exists.
/// A short, stable, filename-safe id derived from a label. Derived once when a
/// setup is added and then fixed, so the label stays editable. Collisions are
/// resolved by the caller, which is the only place that knows what exists.
pub fn id_from(label: &str) -> String {
let slug: String = label
.chars()
@@ -160,25 +149,20 @@ pub fn tidy(value: &str) -> Option<String> {
})
}
/// The inverse of [`tidy`]'s expansion: an absolute path under this
/// machine's home, written back as `~/…`.
/// The inverse of [`tidy`]'s expansion: an absolute path under this machine's
/// home, written back as `~/…`, so that a working directory reads on a phone the
/// way it is written by hand.
///
/// So that a working directory reads on a phone the way it is written by
/// hand. `/home/bob/repos/ai-app-2` is most of a line on that screen and
/// almost all of it is the part nobody is reading.
///
/// Applied only to paths on **this** machine. `$HOME` here says nothing
/// about the home directory of a machine reached over ssh, so a remote
/// path is stored exactly as it was typed -- where a `~` somebody wrote
/// stays a `~`, and the remote shell is what expands it
/// (`ssh::quote_path`).
/// Applied only to paths on **this** machine. `$HOME` here says nothing about
/// the home directory of a machine reached over ssh, so a remote path is stored
/// exactly as it was typed and the remote shell is what expands it.
pub fn shorten_home(path: &str) -> String {
let Some(home) = std::env::home_dir() else {
return path.to_string();
};
let home = home.to_string_lossy();
// The separator has to be part of the match, or `/home/bobby` would be
// read as a path inside `/home/bob`.
// The separator has to be part of the match, or `/home/bobby` would be read
// as a path inside `/home/bob`.
match path.strip_prefix(home.as_ref()) {
Some("") => "~".to_string(),
Some(rest) if rest.starts_with('/') => format!("~{rest}"),
@@ -188,11 +172,10 @@ pub fn shorten_home(path: &str) -> String {
/// Runs a launch to completion and returns its stdout as text.
///
/// The common case of [`Transport::capture_with_input`]: nothing on stdin,
/// a failure reported as the machine's own words (ssh's "Permission
/// denied" or "Could not resolve hostname" is the useful half of why a
/// setup cannot be reached), and the output read as text because every
/// caller here is asking a question whose answer is words.
/// The common case of [`Transport::capture_with_input`]: nothing on stdin, a
/// failure reported as the machine's own words (ssh's "Permission denied" is the
/// useful half of why a setup cannot be reached), and the output read as text
/// because every caller here is asking a question whose answer is words.
impl Transport {
pub async fn capture(&self, launch: &Launch) -> Result<String> {
let captured = self
@@ -206,9 +189,9 @@ impl Transport {
mod tests {
use super::*;
/// The two halves of a home-relative path, which have to be inverses:
/// what is stored is what the phone draws, and what the phone sends
/// back is what a process is started in.
/// The two halves of a home-relative path, which have to be inverses: what
/// is stored is what the phone draws, and what the phone sends back is what
/// a process is started in.
#[test]
fn a_home_path_shortens_and_expands_back() {
let Some(home) = std::env::home_dir() else {
@@ -220,8 +203,8 @@ mod tests {
assert_eq!(shorten_home(&home.to_string_lossy()), "~");
assert_eq!(tidy("~/repos/ai-app-2").as_deref(), Some(full.as_ref()));
// Not a prefix match on the characters: a sibling directory whose
// name merely starts with the home directory's is not inside it.
// Not a prefix match on the characters: a sibling directory whose name
// merely starts with the home directory's is not inside it.
let sibling = format!("{}-backup/notes", home.to_string_lossy());
assert_eq!(shorten_home(&sibling), sibling);
assert_eq!(shorten_home("/etc/hosts"), "/etc/hosts");