27.9 mib · 19.2 mib rss · no node at runtime

A coding agent that is one binary, not a stack.

The agent, the API and the whole UI in one file, with one SQLite database beside it. Nothing to deploy but the binary.

shell

        
        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 one process

karucode serve --workspace .

api
127.0.0.1:8787 · UI, same origin
loop
approvals · subagents · budgets
model
any OpenAI-compatible endpoint
tools
read · search · edit · shell · git · mcp
state
one SQLite file, WAL mode
budget
no turn ceiling · 24 h wall clock

ready in 37 ms19.2 MiB rss

read · search · edit · shell · git · subagents · mcp

MIT licensedx86_64 Linux

The workspace, not a dashboard demo.

a real session · 1440 × 900

The karucode browser workspace during a real run: a session transcript on the left showing streamed shell output, an approval area, and a file explorer on the right.

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

It reads the repo, runs the command, and keeps going.

Six things that decide whether an agent is useful at hour six as well as hour one.

01

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.

02

Real tools, real side effects

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.

03

Approvals before mutations

ask is the default for shell and writes. Command-prefix allowlists, per-path auto-approval, allow-once / session / deny — all audited.

04

Contained file tools

Every path resolves through a confined workspace — no .., no absolute paths outside, symlink escapes refused. Writes are atomic.

05

Crash-tolerant

Cancelling aborts the provider stream and kills shell process groups. On restart, runs become interrupted and approvals expired; nothing is retried.

06

Self-documenting

Facts are @kc-doc comments beside the code they describe, compiled into the binary — the model reads them with karucode_docs.

02 Architecture

One binary, five jobs, and a SQLite file.

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.

browser Preact UI, served by the same origin
karucode one Rust binary
  • axum API + SSE embedded UI, same origin
  • agent loop approvals, subagents, budgets
  • provider any OpenAI-compatible endpoint
  • tools files, search, shell, git, docs
  • plugins + MCP out of process, namespaced

Those last two run out of process and are namespaced (kc__<plugin>__<tool>). Supervised, so a crash is a diagnostic rather than an outage.

SQLite WAL projects, sessions, runs, agents
figure 02 — processes on one host; the only network hop is the browser's
  • Same origin

    The UI is embedded and served by the same axum process — no CORS, no second static host.

  • Streamed by default

    REST for requests, SSE for the transcript — output arrives as it is written.

  • State you can inspect

    Projects, sessions, runs and agents in one SQLite database in WAL mode.

  • Out-of-process tools

    Plugins and MCP servers are namespaced and supervised: a crash yields a diagnostic, not an outage.

03 Approvals

Nothing mutates until you say so.

Approval mode is ask by default. You see the exact command, and the decision is recorded.

approval request pending

Illustration of the approval flow. The real one runs in your browser.

tool
shell · own process group
cwd
~/work/website
mode
ask

git push --force origin main

ReasonRewrites three commits that are already on origin/main, which other people have.

Awaiting a decision.

audit log
  1. No decisions recorded yet.

04 Performance

Twenty-eight megabytes on disk, nineteen in memory.

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

  • Cold start 37.4 ms average of 37.5 / 38.1 / 36.6
  • Idle RSS 19.2 MiB sampled after 3 s idle
  • Peak RSS 28.0 MiB with 3 concurrent runs
  • Binary 27.9 MiB UI embedded, strip + thin LTO
  • 3 runs, wall 266 ms three concurrent runs
Resident memory — idle, then three runs streaming
idle 19.2
3 runs 28.0
Cold start — three runs, polled on /api/health

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

The list a vendor usually leaves out.

You should know these before you run it, not after. There are nine.

  • No shell sandbox

    Commands run as the server user. Use a container or VM for untrusted code or unattended auto mode.

  • Shared workspace

    Subagents see the parent’s live workspace, dirty and untracked files included. owned_paths is advisory, not a lock.

  • Ordered side effects

    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.

  • MCP is tools only

    stdio and streamable HTTP. No legacy SSE transport, and no resources or prompts are surfaced.

  • Plugins are processes

    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.

  • Install-wide providers

    Provider profiles are not per project. A session can still pin its own provider, model and effort.

  • No built-in TLS

    Put a reverse proxy in front for remote access, or reach it over an SSH tunnel.

  • Trimming, not summarising

    Compaction trims the oldest context. It is not model-assisted summarisation.

  • Provider-reported tokens

    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

One script, one binary, one service.

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.

  1. 01

    Fetch the installer from the repository. It is a plain POSIX shell script, so you can read it before you run it.

    shell
    
            
            curl -fsSLO https://raw.githubusercontent.com/karutoil/karucode/main/packaging/install.sh
          
  2. 02

    Run it with the directory sessions start in. It installs to /usr/local/bin, enables the unit at boot and starts it.

    shell
    
            
            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.

  1. 01

    The same script, with no root and no systemd: the binary goes into your own ~/.local/bin.

    shell
    
            
            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.

  1. 01

    Re-running the installer is the upgrade: it fetches the latest release and restarts the service. To stay on a version, name it.

    shell
    
            
            sudo sh install.sh
          
            
            sudo 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.

  • checksum The tarball is checked against the release SHA256SUMS before anything is unpacked.
  • runs as you The service runs as the account that ran the installer — never root. A system account only when no human can be identified.
  • next Open 127.0.0.1:8787: the wizard takes the model endpoint and a password or passkey.
Read the quick start MIT licensed · x86_64 Linux release · the installer verifies SHA256SUMS

07 Questions

The things people ask first.

Straight answers, including the ones with a no in them.

Is this a hosted service?

No. There is no signup, no pricing, no cloud. You run the binary; the browser UI is served by it. MIT licensed.

Which model does it use?

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.

Does it need Rust or Node to install?

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.

Is the shell sandboxed?

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.

How long can one run go?

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.

What does it send to my provider?

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

One binary. One file.
Your machine.

Install it, point it at a project, and the UI is already inside the binary. Nothing to provision, and nothing to keep in sync.

shell

        
        curl -fsSLO https://raw.githubusercontent.com/karutoil/karucode/main/packaging/install.sh
      
        
        sudo sh install.sh --workspace /srv/karucode/workspace
      
binary
27.9 MiB
idle rss
19.2 MiB
state
one SQLite file
licence
MIT