Skip to main content

Crate lonca_client

Crate lonca_client 

Source
Expand description

§lonca-client

A client of the Lonca daemon socket, for harnesses and runners: what a program that starts agents on Lonca, or speaks to the daemon for them, depends on.

Only what a client needs: the protocol’s request and response types and its number (PROTOCOL), the socket client with the checks that make sure the daemon is on the other end (trust), framing, timeouts and typed errors (client), and the human session file (session). No store, no policy evaluation, no daemon: a client is never a second writer.

use std::path::Path;
use std::time::Duration;

use lonca_client::{Client, PROTOCOL};
use serde_json::json;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // $LONCA_SOCKET, else $LONCA_HOME/lonca.sock, else ~/.lonca/lonca.sock.
    let home = std::env::var("HOME")?;
    let socket = Path::new(&home).join(".lonca/lonca.sock");

    // Checks that the daemon this machine trusts is on the other end
    // before anything is sent: a token goes nowhere unverified.
    let mut client = Client::connect(&socket)?;
    client.set_timeout(Some(Duration::from_secs(30)))?;

    let ping = client.call("ping", json!({}))?;
    if ping["protocol"] != PROTOCOL {
        return Err("a daemon of another protocol".into());
    }

    // An agent's token, from the workspace `lonca session start` made.
    let token = std::fs::read_to_string("/work/claude-1/.lonca/session")?;
    client.token = Some(token.trim().to_owned());
    client.repo = Some("demo".into());
    let status = client.call("status", json!({}))?;
    println!("{status:#}");
    Ok(())
}

Every method, its credential and its shapes are in the daemon protocol; running agents on Lonca is in the runners guide.

Versions: a change of PROTOCOL is a major version of this crate (while it is 0.x, a minor one). Fields added to a shape are not a change of the number. Pin an exact version and read the release notes when you move. The version is the workspace’s: every Lonca crate has the same one.

Re-exports§

pub use client::CallError;
pub use client::Client;
pub use client::ErrorKind;
pub use session::HumanSession;
pub use trust::Trust;

Modules§

client
Client for the daemon socket.
protocol
Wire protocol: one JSON object per line over a Unix socket (docs/protocol.md). The parameters of repo.prepare_init are the daemon’s (lonca_daemon::protocol::PrepareInit): they name a policy profile, and this crate knows no policy.
session
The human session file (docs/protocol.md, “The human session file is an interface”): lonca human login keeps a human’s session token at <humans>/<name>.session, mode 0600, as {"token", "expires_ms"}.
sys
The calls into libc a client needs: who it is, and who is on the other end of a socket. Unsafe code is refused in the rest of the workspace ([workspace.lints]); each block here says why it is sound.
trust
Who a client expects on the other end of the daemon socket (design §3.3, measure 2). The daemon checks its peer’s uid; this is the other half, so that a process answering on the socket path gets neither a session token nor the last word on what a hook allows.

Constants§

PROTOCOL
The runner-facing protocol number (docs/protocol.md), the one the daemon answers in ping.protocol. It goes up when a shape changes in a way a client must know about; adding a field does not count.