Menu
Public repository / 16baa88e7934816edd8435507847f72fc196163f
AkurAI-Router / src/routing.rs
Latest change 16baa88e7934816edd8435507847f72fc196163f - OLI-301: claude-cli runner backend + static routes.conf (stale window 0) by Olafur Bui
use std::fs::{self, OpenOptions};
use std::io::Write;
use std::os::unix::fs::PermissionsExt;
use crate::auth::ApiActor;
use crate::config::{
Config, PROVIDER_EMBEDDINGS, canonical_provider_id, load_embedding_config, load_providers,
};
use crate::http::Request;
use crate::json::Json;
use crate::util::{now_secs, random_hex};
#[derive(Clone, Debug)]
pub struct Route {
pub installation_id: String,
pub organization_id: String,
pub workspace_id: String,
pub app_id: String,
pub key_id: String,
pub provider_id: String,
pub installed: bool,
pub entitled: bool,
}
#[derive(Clone, Debug)]
pub struct ControlPlane {
pub revision: String,
pub status: &'static str,
routes: Vec<Route>,
}
pub enum ResolveError {
Unavailable,
Denied,
}
pub fn ensure_route_file(cfg: &Config) -> Result<(), String> {
let path = cfg.data_dir.join("routes.conf");
if path.exists() {
return Ok(());
}
atomic_write(
&path,
"# revision=unconfigured\n# updated_at=0\n# installation_id|organization_id|workspace_id|app_id|key_id|provider_id|installed|entitled\n",
)
}
/// A stale window of 0 marks routes.conf as static (hand-managed), so it never expires.
fn is_fresh(updated_at: u64, now: u64, stale_secs: u64) -> bool {
stale_secs == 0 || now.saturating_sub(updated_at) <= stale_secs
}
pub fn load(cfg: &Config) -> ControlPlane {
let path = cfg.data_dir.join("routes.conf");
let Ok(text) = fs::read_to_string(path) else {
return unknown();
};
let mut revision = None;
let mut updated_at = None;
let mut routes = Vec::new();
for line in text.lines().map(str::trim) {
if let Some(value) = line.strip_prefix("# revision=") {
revision = valid_id(value).then(|| value.to_string());
continue;
}
if let Some(value) = line.strip_prefix("# updated_at=") {
updated_at = value.parse::<u64>().ok();
continue;
}
if line.is_empty() || line.starts_with('#') {
continue;
}
let fields: Vec<_> = line.split('|').collect();
if fields.len() != 8 || fields.iter().take(6).any(|field| !valid_id(field)) {
return unknown();
}
let (Some(installed), Some(entitled)) = (parse_bool(fields[6]), parse_bool(fields[7]))
else {
return unknown();
};
let provider_id = canonical_provider_id(fields[5]);
if !["codex", "claude", "opencode-go", PROVIDER_EMBEDDINGS].contains(&provider_id.as_str())
{
return unknown();
}
let route = Route {
installation_id: fields[0].to_string(),
organization_id: fields[1].to_string(),
workspace_id: fields[2].to_string(),
app_id: fields[3].to_string(),
key_id: fields[4].to_string(),
provider_id,
installed,
entitled,
};
if routes.iter().any(|existing: &Route| {
existing.organization_id == route.organization_id
&& existing.workspace_id == route.workspace_id
&& existing.app_id == route.app_id
&& existing.key_id == route.key_id
}) {
return unknown();
}
routes.push(route);
}
let Some(revision) = revision else {
return unknown();
};
let Some(updated_at) = updated_at else {
return unknown();
};
if !is_fresh(updated_at, now_secs(), cfg.control_plane_stale_secs) {
return unknown();
}
ControlPlane {
revision,
status: if routes.iter().all(|route| route.installed && route.entitled) {
"ready"
} else {
"degraded"
},
routes,
}
}
pub fn resolve(
plane: &ControlPlane,
req: &Request,
actor: &ApiActor,
) -> Result<Route, ResolveError> {
if plane.status == "unknown" {
return Err(ResolveError::Unavailable);
}
let (Some(organization_id), Some(workspace_id), Some(app_id)) = (
req.header("x-akurai-organization"),
req.header("x-akurai-workspace"),
req.header("x-akurai-app"),
) else {
return Err(ResolveError::Denied);
};
plane
.routes
.iter()
.find(|route| {
route.organization_id == organization_id
&& route.workspace_id == workspace_id
&& route.app_id == app_id
&& route.key_id == actor.key_id
&& route.installed
&& route.entitled
})
.cloned()
.ok_or(ResolveError::Denied)
}
pub fn health_json(plane: &ControlPlane) -> String {
let mut body = Json::object();
body.set("app", Json::String("akurai-router".to_string()));
body.set("status", Json::String(plane.status.to_string()));
body.set(
"configuration_revision",
Json::String(plane.revision.clone()),
);
body.stringify()
}
pub fn status_json(plane: &ControlPlane, cfg: &Config, actor: &ApiActor) -> String {
let providers = load_providers(cfg);
let embedding = load_embedding_config(cfg);
let mut routes = Vec::new();
for route in plane
.routes
.iter()
.filter(|route| route.key_id == actor.key_id)
{
let provider_ready = if route.provider_id == PROVIDER_EMBEDDINGS {
embedding.enabled && !embedding.upstream_url.trim().is_empty()
} else {
providers.iter().any(|provider| {
canonical_provider_id(&provider.id) == route.provider_id
&& provider.enabled
&& provider.auth_path.exists()
})
};
let mut item = Json::object();
item.set(
"installation_id",
Json::String(route.installation_id.clone()),
);
item.set(
"organization_id",
Json::String(route.organization_id.clone()),
);
item.set("workspace_id", Json::String(route.workspace_id.clone()));
item.set("app_id", Json::String(route.app_id.clone()));
item.set(
"status",
Json::String(
if plane.status == "unknown" {
"unknown"
} else if route.installed && route.entitled && provider_ready {
"ready"
} else {
"degraded"
}
.to_string(),
),
);
routes.push(item);
}
let mut body = Json::object();
body.set("status", Json::String(plane.status.to_string()));
body.set(
"configuration_revision",
Json::String(plane.revision.clone()),
);
body.set("routes", Json::Array(routes));
body.stringify()
}
pub fn grant(
cfg: &Config,
installation_id: &str,
organization_id: &str,
workspace_id: &str,
app_id: &str,
key_id: &str,
provider_id: &str,
) -> Result<(), String> {
let fields = [
installation_id,
organization_id,
workspace_id,
app_id,
key_id,
provider_id,
];
if fields.iter().any(|field| !valid_id(field)) {
return Err("route fields must contain only letters, digits, '.', '_', or '-'".to_string());
}
let provider_id = canonical_provider_id(provider_id);
if !["codex", "claude", "opencode-go", PROVIDER_EMBEDDINGS].contains(&provider_id.as_str()) {
return Err("route provider must be codex, claude, opencode-go, or embeddings".to_string());
}
let mut routes = load(cfg).routes;
routes.retain(|route| {
!(route.organization_id == organization_id
&& route.workspace_id == workspace_id
&& route.app_id == app_id
&& route.key_id == key_id)
});
routes.push(Route {
installation_id: installation_id.to_string(),
organization_id: organization_id.to_string(),
workspace_id: workspace_id.to_string(),
app_id: app_id.to_string(),
key_id: key_id.to_string(),
provider_id,
installed: true,
entitled: true,
});
save(cfg, &routes, "grant")
}
pub fn revoke(cfg: &Config, installation_id: &str) -> Result<(), String> {
if !valid_id(installation_id) {
return Err("installation id is invalid".to_string());
}
let mut routes = load(cfg).routes;
let mut changed = false;
for route in &mut routes {
if route.installation_id == installation_id {
route.entitled = false;
changed = true;
}
}
if !changed {
return Err("installation route not found".to_string());
}
save(cfg, &routes, "revoke")
}
fn save(cfg: &Config, routes: &[Route], action: &str) -> Result<(), String> {
let revision = format!("cfg_{}", random_hex(12));
let updated_at = now_secs();
let mut text = format!(
"# revision={revision}\n# updated_at={updated_at}\n# installation_id|organization_id|workspace_id|app_id|key_id|provider_id|installed|entitled\n"
);
for route in routes {
text.push_str(&format!(
"{}|{}|{}|{}|{}|{}|{}|{}\n",
route.installation_id,
route.organization_id,
route.workspace_id,
route.app_id,
route.key_id,
route.provider_id,
route.installed,
route.entitled
));
}
atomic_write(&cfg.data_dir.join("routes.conf"), &text)?;
let mut audit = OpenOptions::new()
.create(true)
.append(true)
.open(cfg.data_dir.join("route-audit.tsv"))
.map_err(|err| err.to_string())?;
writeln!(
audit,
"{updated_at}\t{revision}\t{action}\t{}",
routes.len()
)
.map_err(|err| err.to_string())?;
let _ = fs::set_permissions(
cfg.data_dir.join("route-audit.tsv"),
fs::Permissions::from_mode(0o600),
);
Ok(())
}
fn atomic_write(path: &std::path::Path, text: &str) -> Result<(), String> {
let temp = path.with_extension(format!("{}.tmp", random_hex(8)));
fs::write(&temp, text).map_err(|err| err.to_string())?;
fs::set_permissions(&temp, fs::Permissions::from_mode(0o600)).map_err(|err| err.to_string())?;
fs::rename(temp, path).map_err(|err| err.to_string())
}
fn unknown() -> ControlPlane {
ControlPlane {
revision: "unknown".to_string(),
status: "unknown",
routes: Vec::new(),
}
}
fn valid_id(value: &str) -> bool {
!value.is_empty()
&& value.len() <= 128
&& value
.bytes()
.all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'.' | b'_' | b'-'))
}
fn parse_bool(value: &str) -> Option<bool> {
match value {
"true" => Some(true),
"false" => Some(false),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::is_fresh;
#[test]
fn zero_stale_window_means_static_config() {
assert!(is_fresh(0, 1_790_000_000, 0));
}
#[test]
fn seed_file_is_stale_with_default_window() {
assert!(!is_fresh(0, 1_790_000_000, 300));
assert!(is_fresh(1_789_999_900, 1_790_000_000, 300));
}
}