From 5414a1f972a3fe048c9d39f84f32cc81a31cd7bf Mon Sep 17 00:00:00 2001 From: ANonABento Date: Sun, 7 Jun 2026 13:10:12 -0400 Subject: [PATCH] feat(api): enable the local HTTP API by default (opt out with KAITENCODE_LOCAL_API=0) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The local API powers the MCP server and Claude Code's board automation. It was off unless KAITENCODE_LOCAL_API=1 was set, so a normal Dock/.deb launch wrote no ~/.kaitencode/api.port and every MCP mutation failed with "app is not running" — a silent, hard-to-diagnose footgun. Flip the default to ON. It's safe to default on: the server binds 127.0.0.1 only and authenticates every request with a per-run bearer token in a 0600 discovery file. Explicit opt-out is preserved: KAITENCODE_LOCAL_API=0 (or false/no/off) disables it. Updates the disabled-path log message and the README accordingly. --- README.md | 2 +- src-tauri/src/api.rs | 38 +++++++++++++++++++++++++++----------- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index f65d9836..7e24cc7c 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ Packaged macOS and Linux builds expect these tools to be available on `PATH` for Desktop-launched apps on macOS/Linux may not inherit your shell dotfiles. First-run onboarding shows a system check for required tools. If a CLI is not detected, set its path in Settings > Agents & Models. -The local HTTP API used by external automation is disabled by default in packaged apps. Start KaitenCode with `KAITENCODE_LOCAL_API=1` only when you intentionally want a local MCP/automation client to control the running app; the app writes a user-readable-only JSON discovery file with the selected port and a per-run bearer token to `~/.kaitencode/api.port` while enabled. +The local HTTP API powers the MCP server and any external automation that drives the board, so it is **enabled by default**. It binds to `127.0.0.1` only and authenticates every request with a per-run bearer token, written along with the chosen port to a user-readable-only (`0600`) discovery file at `~/.kaitencode/api.port`. To opt out (no MCP/automation control), start KaitenCode with `KAITENCODE_LOCAL_API=0` (or `false`/`no`/`off`). ### Building diff --git a/src-tauri/src/api.rs b/src-tauri/src/api.rs index 4a15b29b..659391aa 100644 --- a/src-tauri/src/api.rs +++ b/src-tauri/src/api.rs @@ -868,7 +868,7 @@ fn write_api_discovery_file(path: &std::path::Path, contents: &str) -> std::io:: pub fn start(app: AppHandle) { if !local_api_enabled() { let _ = std::fs::remove_file(port_file_path()); - log::info!("[api] Local HTTP API disabled; set KAITENCODE_LOCAL_API=1 to enable"); + log::info!("[api] Local HTTP API disabled via KAITENCODE_LOCAL_API; MCP/automation clients won't be able to reach the app"); return; } @@ -967,15 +967,20 @@ pub fn cleanup() { let _ = std::fs::remove_file(port_file_path()); } +/// Whether to start the local HTTP API. Defaults to **ON**: the MCP server and +/// Claude Code drive the board through it, so off-by-default silently breaks +/// every MCP mutation. The server binds `127.0.0.1` only and authenticates with +/// a per-process bearer token written to a `0600` discovery file, so enabling it +/// by default is local-only and safe. Set `KAITENCODE_LOCAL_API=0` (or +/// `false`/`no`/`off`) to opt out. fn local_api_enabled() -> bool { - std::env::var("KAITENCODE_LOCAL_API") - .map(|value| { - matches!( - value.trim().to_ascii_lowercase().as_str(), - "1" | "true" | "yes" | "on" - ) - }) - .unwrap_or(false) + match std::env::var("KAITENCODE_LOCAL_API") { + Ok(value) => !matches!( + value.trim().to_ascii_lowercase().as_str(), + "0" | "false" | "no" | "off" + ), + Err(_) => true, + } } #[cfg(test)] @@ -1010,10 +1015,11 @@ mod tests { } #[test] - fn local_api_disabled_by_default() { + fn local_api_enabled_by_default() { let _guard = ENV_LOCK.lock().expect("env lock"); std::env::remove_var("KAITENCODE_LOCAL_API"); - assert!(!local_api_enabled()); + // On by default — MCP depends on it. + assert!(local_api_enabled()); } #[test] @@ -1025,4 +1031,14 @@ mod tests { } std::env::remove_var("KAITENCODE_LOCAL_API"); } + + #[test] + fn local_api_respects_explicit_disable_values() { + let _guard = ENV_LOCK.lock().expect("env lock"); + for value in ["0", "false", "no", "off", "OFF", " false "] { + std::env::set_var("KAITENCODE_LOCAL_API", value); + assert!(!local_api_enabled(), "value {value:?}"); + } + std::env::remove_var("KAITENCODE_LOCAL_API"); + } }