use crate::cli::output::{OutputConfig, OutputFormat};
use crate::cli::BoundaryCommands;
use crate::error::{OlError, ERR_BOUNDARY_FINDING_NOT_FOUND};
pub fn run(cmd: &BoundaryCommands, output: &OutputConfig) -> Result<(), OlError> {
match cmd {
BoundaryCommands::Status => status(output),
BoundaryCommands::Explain { finding_id } => explain(finding_id, output),
}
}
#[derive(Debug, PartialEq)]
pub(crate) enum PortOwnership {
Owned,
Foreign,
Unreachable,
}
const CONNECT_TIMEOUT: std::time::Duration = std::time::Duration::from_millis(100);
const PROBE_TIMEOUT: std::time::Duration = std::time::Duration::from_millis(500);
fn get_admin_status(port: u16) -> Option<reqwest::blocking::Response> {
let url = format!("http://127.0.0.1:{port}/admin/boundary/status");
reqwest::blocking::Client::builder()
.timeout(PROBE_TIMEOUT)
.build()
.ok()?
.get(&url)
.send()
.ok()
}
pub(crate) fn verify_port_ownership(port: u16) -> PortOwnership {
let addr = std::net::SocketAddr::from(([127, 0, 0, 1], port));
if std::net::TcpStream::connect_timeout(&addr, CONNECT_TIMEOUT).is_err() {
return PortOwnership::Unreachable;
}
let Some(resp) = get_admin_status(port) else {
return PortOwnership::Foreign;
};
if !resp.status().is_success() {
return PortOwnership::Foreign;
}
match resp.json::<serde_json::Value>() {
Ok(v) if v.get("status").is_some() && v.get("upstream").is_some() => PortOwnership::Owned,
_ => PortOwnership::Foreign,
}
}
pub fn status(output: &OutputConfig) -> Result<(), OlError> {
let port = crate::config::Config::load(None, None, false)
.map(|c| c.boundary.port)
.unwrap_or_else(|_| crate::boundary::resolve_boundary_port());
let probe = probe_boundary(port);
if output.format == OutputFormat::Json {
output.print_json(&serde_json::json!({
"port": port,
"up": probe.is_some(),
"detail": probe,
}));
} else {
crate::cli::header::print(output, &["boundary status"]);
match probe {
Some(v) => {
eprintln!(" Boundary: up (port {port})");
if let Some(up) = v.get("upstream").and_then(|x| x.as_str()) {
eprintln!(" Upstream: {up}");
}
if let Some(f) = v.get("pass_through_failures").and_then(|x| x.as_u64()) {
eprintln!(" Pass-through failures: {f}");
}
match v.get("preflight").and_then(|x| x.as_str()) {
Some("ok") => eprintln!(" Preflight: ok (agent wired)"),
Some("failed") => {
let why = v
.get("preflight_error")
.and_then(|x| x.as_str())
.unwrap_or("no round trip to the provider completed");
eprintln!(" Preflight: FAILED — {why}");
eprintln!(
" Agent left unwired on purpose: model calls go straight to the \
provider and nothing is captured."
);
}
Some(other) => eprintln!(" Preflight: {other}"),
None => {}
}
}
None => {
eprintln!(" Boundary: down (pinned port {port})");
eprintln!(
" Suggestion: run 'openlatch start' to bring the listener up (it wires \
the agent once bound)."
);
}
}
}
Ok(())
}
pub fn explain(finding_id: &str, output: &OutputConfig) -> Result<(), OlError> {
let record = crate::boundary::retention::load(finding_id).ok_or_else(|| {
OlError::new(
ERR_BOUNDARY_FINDING_NOT_FOUND,
format!("no local churn finding '{finding_id}'"),
)
.with_suggestion(
"Findings resolve only on the host that produced them, and expire from the bounded \
local store. Check the id from the `ai.openlatch.prefix.finding_id` field.",
)
})?;
if output.format == OutputFormat::Json {
output.print_json(&serde_json::json!({
"finding_id": record.finding_id,
"captured_at": record.captured_at,
"churn_layer": record.churn_layer,
"churn_class": record.churn_class,
"divergence_offset": record.divergence_offset,
"churn_byte_len": record.churn_byte_len,
"churn_block_index": record.churn_block_index,
"block": record.block,
}));
} else {
crate::cli::header::print(output, &["boundary explain"]);
eprintln!(" finding : {}", record.finding_id);
eprintln!(" captured : {}", record.captured_at);
eprintln!(" layer : {}", record.churn_layer);
eprintln!(" class : {}", record.churn_class);
eprintln!(
" offset/len : {} / {} (block #{})",
record.divergence_offset, record.churn_byte_len, record.churn_block_index
);
eprintln!(" block (local, never emitted):");
println!("{}", record.block);
}
Ok(())
}
pub fn probe_boundary(port: u16) -> Option<serde_json::Value> {
let resp = get_admin_status(port)?;
if !resp.status().is_success() {
return None;
}
resp.json().ok()
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum BoundaryState {
Disabled,
Isolated,
Wired,
WiredButDown,
WiredToForeign,
PreflightFailed(String),
PreflightPending,
UpUnwired,
Down,
ForeignIdle,
}
impl BoundaryState {
pub(crate) fn label(&self) -> &'static str {
match self {
BoundaryState::Disabled => "disabled",
BoundaryState::Isolated => "isolated",
BoundaryState::Wired => "up",
BoundaryState::WiredButDown => "down",
BoundaryState::WiredToForeign => "failed",
BoundaryState::PreflightFailed(_) => "preflight-failed",
BoundaryState::PreflightPending => "preflight-pending",
BoundaryState::UpUnwired => "unwired",
BoundaryState::Down => "down",
BoundaryState::ForeignIdle => "down",
}
}
}
pub(crate) fn classify_boundary(cfg: &crate::config::Config, wired: Option<&str>) -> BoundaryState {
if !cfg.boundary.enabled {
return BoundaryState::Disabled;
}
if !cfg.boundary.owns_agent_wiring() {
return BoundaryState::Isolated;
}
let port = cfg.boundary.port;
match (wired, verify_port_ownership(port)) {
(Some(_), PortOwnership::Owned) => BoundaryState::Wired,
(Some(_), PortOwnership::Unreachable) => BoundaryState::WiredButDown,
(Some(_), PortOwnership::Foreign) => BoundaryState::WiredToForeign,
(None, PortOwnership::Owned) => {
let live = probe_boundary(port);
match live
.as_ref()
.and_then(|v| v.get("preflight"))
.and_then(|v| v.as_str())
{
Some("failed") => BoundaryState::PreflightFailed(
live.as_ref()
.and_then(|v| v.get("preflight_error"))
.and_then(|v| v.as_str())
.unwrap_or("no round trip to the provider completed")
.to_string(),
),
Some("pending") => BoundaryState::PreflightPending,
_ => BoundaryState::UpUnwired,
}
}
(None, PortOwnership::Unreachable) => BoundaryState::Down,
(None, PortOwnership::Foreign) => BoundaryState::ForeignIdle,
}
}
pub(crate) fn read_boundary_base_url(settings_path: &std::path::Path) -> Option<String> {
let raw = std::fs::read_to_string(settings_path).ok()?;
let parsed = crate::hooks::jsonc::parse_settings_value(&raw).ok()?;
let url = parsed
.get("env")?
.get("ANTHROPIC_BASE_URL")?
.as_str()?
.to_string();
reqwest::Url::parse(url.trim())
.ok()
.filter(|u| u.host_str() == Some("127.0.0.1"))
.map(|_| url)
}
#[cfg(test)]
mod tests {
use super::{verify_port_ownership, PortOwnership};
#[test]
fn verify_port_ownership_refuses_closed_and_foreign_ports() {
let l = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
let closed = l.local_addr().unwrap().port();
drop(l);
assert_eq!(verify_port_ownership(closed), PortOwnership::Unreachable);
let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
let foreign = listener.local_addr().unwrap().port();
std::thread::spawn(move || {
use std::io::{Read, Write};
for mut s in listener.incoming().flatten() {
let mut buf = [0u8; 1024];
let _ = s.read(&mut buf);
let body = br#"{"foo":"bar"}"#;
let head = format!(
"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\
Content-Length: {}\r\nConnection: close\r\n\r\n",
body.len()
);
let _ = s.write_all(head.as_bytes());
let _ = s.write_all(body);
let _ = s.flush();
}
});
std::thread::sleep(std::time::Duration::from_millis(50));
assert_eq!(verify_port_ownership(foreign), PortOwnership::Foreign);
}
#[test]
fn disabled_in_config_short_circuits_before_any_probe() {
use crate::cli::commands::boundary::{classify_boundary, BoundaryState};
let mut cfg = crate::config::Config::defaults();
cfg.boundary.enabled = false;
assert_eq!(classify_boundary(&cfg, None), BoundaryState::Disabled);
assert_eq!(
classify_boundary(&cfg, Some("http://127.0.0.1:7600")),
BoundaryState::Disabled
);
assert_eq!(BoundaryState::Disabled.label(), "disabled");
}
#[test]
fn isolated_instance_short_circuits_before_any_probe() {
use crate::cli::commands::boundary::{classify_boundary, BoundaryState};
let mut cfg = crate::config::Config::defaults();
cfg.boundary.enabled = true;
cfg.boundary.port = crate::boundary::default_boundary_port() + 1;
assert_eq!(classify_boundary(&cfg, None), BoundaryState::Isolated);
}
}