Long-horizon runs
No turn ceiling by default — one measured run went ~10,200 turns before the 24 h wall clock, not a counter, ended it.
27.9 mib · 19.2 mib rss · no node at runtime
The agent, the API and the whole UI in one file, with one SQLite database beside it. Nothing to deploy but the binary.
curl -fsSLO https:// raw.githubusercontent.com/ karutoil/ karucode/ main/ packaging/ install.sh sudo sh install.sh --workspace .
Then open 127.0.0.1:8787: the wizard takes your endpoint and a password.
karucode serve --workspace .
ready in 37 ms19.2 MiB rss
read · search · edit · shell · git · subagents · mcp
MIT licensedx86_64 Linux
Streamed tool output, the model and reasoning effort in use, the context budget, and an explorer rooted at the workspace — all served by the binary you just started.
no embed · no iframe · no second service
01 What it does
Six things that decide whether an agent is useful at hour six as well as hour one.
No turn ceiling by default — one measured run went ~10,200 turns before the 24 h wall clock, not a counter, ended it.
Read, search, edit; shell with streamed output in its own process group; git; subagents. Independent calls run at the same time; every result is bounded with a truncation marker.
ask is the default for shell and writes. Command-prefix allowlists, per-path
auto-approval, allow-once / session / deny — all audited.
Every path resolves through a confined workspace — no .., no absolute paths
outside, symlink escapes refused. Writes are atomic.
Cancelling aborts the provider stream and kills shell process groups. On restart, runs
become interrupted and approvals expired; nothing is retried.
Facts are @kc-doc comments beside the code they describe, compiled into the
binary — the model reads them with karucode_docs.
02 Architecture
The server embeds the UI, so the browser and the agent share an origin. There is no second process to deploy and no Node at runtime.
Those last two run out of process and are namespaced (kc__<plugin>__<tool>). Supervised, so a crash is a diagnostic rather than an outage.
The UI is embedded and served by the same axum process — no CORS, no second static host.
REST for requests, SSE for the transcript — output arrives as it is written.
Projects, sessions, runs and agents in one SQLite database in WAL mode.
Plugins and MCP servers are namespaced and supervised: a crash yields a diagnostic, not an outage.
03 Approvals
Approval mode is ask by default. You see the exact command, and the decision is recorded.
Illustration of the approval flow. The real one runs in your browser.
git push --force origin main
ReasonRewrites three commits that are already on origin/main, which other people have.
Awaiting a decision.
04 Performance
Measured on one host with a local mock provider — cold start polled on /api/health, RSS sampled after three seconds idle and during three concurrent runs.
measured, not promised · one host · mock provider
avg 37.4 ms — the spread, not a single number
Host: AMD Ryzen 7 3700X, Linux 6.12, rustc 1.96, release build with strip + thin LTO. The binary embeds the whole web UI and its fonts; that is why most of it is not agent code. Numbers are specific to this host and to the mock provider; RSS during a real run also depends on the provider TLS stack and response sizes. No memory target is promised.
05 What it doesn’t do
You should know these before you run it, not after. There are nine.
Commands run as the server user. Use a container or VM for untrusted code or unattended auto mode.
Subagents see the parent’s live workspace, dirty and untracked files included. owned_paths is advisory, not a lock.
Shell, plugins, MCP and subagents always run in call order. Only calls the runtime can prove independent — reads, searches, disjoint writes — run at the same time.
stdio and streamable HTTP. No legacy SSE transport, and no resources or prompts are surfaced.
Not Wasm: no filesystem sandbox around a plugin. A rebuilt one is picked up by a reload — POST /api/plugins/reload, or plugins_watch — not by restarting the server.
Provider profiles are not per project. A session can still pin its own provider, model and effort.
Put a reverse proxy in front for remote access, or reach it over an SSH tunnel.
Compaction trims the oldest context. It is not model-assisted summarisation.
Token counts are whatever your provider reports. Absent usage is shown as unknown.
The shell is not sandboxed. Approvals are the gate, not isolation — use a container or VM for untrusted code.
06 Install
It downloads the release tarball, verifies it against SHA256SUMS, then installs the binary and a systemd unit. No Rust, no Node, no container — and nothing piped into a shell.
Fetch the installer from the repository. It is a plain POSIX shell script, so you can read it before you run it.
curl -fsSLO https://raw.githubusercontent.com/karutoil/karucode/main/packaging/install.sh
Run it with the directory sessions start in. It installs to /usr/local/bin, enables the unit at boot and starts it.
sudo sh install.sh --workspace /srv/karucode/workspace
It reports success only once /api/health answers. The previous binary is kept as karucode.old, and a failed start restores it. There is no default workspace: drop --workspace and the server starts with no project, which you add in the web UI.
The same script, with no root and no systemd: the binary goes into your own ~/.local/bin.
curl -fsSLO https:// raw.githubusercontent.com/ karutoil/ karucode/ main/ packaging/ install.sh sh install.sh --prefix "$HOME/.local" --no-systemd
Then run it in the foreground with karucode --workspace /path/to/project. State lives in ~/.local/share/karucode.
Re-running the installer is the upgrade: it fetches the latest release and restarts the service. To stay on a version, name it.
sudo sh install.shsudo sh install.sh --version 0.1.0 # pin a release
Your workspace, user, port and data directory live in a drop-in at /etc/systemd/system/karucode.service.d/10-install.conf, which every run rewrites from what it finds — so an upgrade cannot lose the configuration that made the service start. --dry-run prints a run without doing it.
07 Questions
Straight answers, including the ones with a no in them.
No. There is no signup, no pricing, no cloud. You run the binary; the browser UI is served by it. MIT licensed.
Anything that speaks the OpenAI chat-completions API with streaming tool calls — a gateway, vLLM, Ollama, llama.cpp, OpenRouter. The provider is install-wide; a session can pin its own provider, model and reasoning effort.
Neither. The release is a prebuilt binary: the installer fetches the tarball, verifies it against SHA256SUMS and installs it — Rust 1.89+ and Node 22.12+ are only needed to build from source.
No — and that is stated plainly. Commands run as the server user; approvals are the gate. Use a container or VM for untrusted code or unattended auto mode. File tools are confined: no .., no absolute paths outside, symlink escapes refused, writes atomic.
There is no turn ceiling by default: max_iterations is 0, so a long task is not cut off by a counter and no continuation turn is injected into the transcript. A run ends when the work is done, when you cancel it, at the 24 h wall clock (max_run_secs), or when the loop guard catches it repeating itself. A non-zero max_iterations is an optional backstop.
The conversation, tool results and the model’s requests — nothing else. There is no telemetry in the product, and child processes get a credential-free environment allowlist so a prompt-injected curl cannot read the harness token or the provider key.
Configuration, the HTTP API and the security model are in the documentation. The version history is the release log.
Ready when you are
Install it, point it at a project, and the UI is already inside the binary. Nothing to provision, and nothing to keep in sync.
curl -fsSLO https:// raw.githubusercontent.com/ karutoil/ karucode/ main/ packaging/ install.sh sudo sh install.sh --workspace /srv/ karucode/ workspace