← kowalski.yarenty.com

kowalski  ·  architecture  ·  2.x

Kowalski Architecture

A local-first agent runtime in one binary. Work is done by hordes: small teams of agents defined as plain folders, run as durable pipelines, with tools reached over MCP and numbers computed by engines rather than models.

Release line  2.1 Standing Orders Core  Rust · Tokio · Axum · SQLite UI  Vue 3 · TypeScript, compiled into the binary Models  Ollama or any OpenAI-compatible endpoint Tools  built-in + MCP (tableski, gateways)
The shape

One binary, your machine, your model

You download kowalski, run it, and a browser opens on the app. Everything it needs travels inside: the operator UI, the built-in hordes, the run database. The only things outside are the ones you choose: a model (a local Ollama, or an endpoint with your key) and the tools you connect.

That shape is deliberate. The people kowalski is for (a small-business owner with a folder of spreadsheets, a trader with a watchlist) will not run a cluster, and should not have to trust a service with their files. So the default is dependency-light: no required daemons besides the model, SQLite on disk, and optional extras (Postgres with pgvector, graph queries) that stay off until asked for.

Kowalski system map: the binary, its parts, and what it talks to You browser · terminal Events cron · file lands · webhook kowalski (one binary · 127.0.0.1:3456) Operator UI Setup · Horde · Chat · Rookery HTTP API /api/* token required off-localhost Horde runner catalog of hordes (folders) · orchestrator · triggers durable runs: every state change written to SQLite approval gate before steps that run commands or write files pending → running → awaiting_input → done Step handlers deterministic: ingest · table_profile sql_batch · xlsx_report · verify · apply model: process · deliver · compile · ask in-process or isolated child Agent core TemplateAgent · tool calling built-in: web_fetch · web_search · files MCP client hub (HTTP, stdio, OAuth) kowalski-core State on disk run store · memory tiers (SQLite) horde output/ folders optional: Postgres + pgvector Built-in hordes Spreadsheet analyst · Morning brief Folder watcher · URL summarizer Knowledge compiler Model Ollama (local, free) or OpenAI-compatible + your key tableski spreadsheets as SQL tables MCP · local binary or hosted list_tables · query_sql Other MCP servers Docker MCP gateway, rookery, anything that speaks MCP The web public pages only · search via Brave, Staan or SearXNG
What runs, and what talks to what. Everything inside the black frame is one process started by one command. Blue boxes are outside and chosen by you. The red path is the one that matters most for business users: a horde step calling tableski, so that questions about spreadsheets are answered by a SQL engine. Events (a schedule, a file landing in a folder, a webhook) start runs exactly as a click in the UI does.
Hordes

A horde is a folder you can read

A horde is a directory of Markdown files. horde.md names the pipeline and its triggers; each file in agents/ is one step (a "penguin") with TOML front matter saying what kind of step it is, what it reads and where it writes; prompts/ holds the instructions for the steps that use a model.

Nothing is hidden in a database or a UI-only format. A horde can be read in any editor, diffed in git, copied to another machine, exported as a portable .kwf.zip bundle, or built conversationally in the Rookery tab, which writes the same folder. Drop a folder next to your config and it appears in the Hordes screen; edit a file and the running server reloads it.

Step kindUses a model?What it does
ingestnoCollects the run's input: fetches the URLs (public addresses only), reads the files, keeps the text. Pages are reduced to their main content with absolute links.
table_profilenoAsks the data tool for every table, its columns, types and statistics, so the next step starts from exact names.
sql_batchnoRuns each SQL block of the previous step's plan through query_sql and records the results exactly as returned.
xlsx_reportnoTurns those results into a workbook: an index sheet and one sheet per question, numbers typed as numbers.
verify · applynoRun a check command on a project, or write proposed changes into it. Both wait for the operator's approval by default.
process · deliver · compile · ask · lintyesModel steps: a prompt plus attached context (the previous artifact, other steps' outputs), optionally with tools. The output is one Markdown file.

Pipelines and graphs

Most hordes are a straight line: pipeline = ["ingest", "profile", "plan", "run", "report", "deliver"]. When a job needs branches, edges turn the pipeline into a graph: fork and join, route on a step's pass/fail outcome, and loop back (a failed check returns to the step that writes the fix) with a cap on how often.

One run

What one question sets in motion

The Spreadsheet analyst is the clearest example of how the parts fit, because it uses both kinds of step and the division of labour between them is the point.

One Spreadsheet analyst run, step by step your questions "Who spent the most?" ingest records the questions no model profile tables, columns, types, statistics no model plan one SQL query per question model run each query through tableski no model report report.xlsx, typed numbers no model deliver HANDOFF.md, answers quoting the results model tableski (MCP) list_tables · get_schema · column_statistics · query_sql every sheet of your workbook is a table files written to the horde's output/ folder: debug/profile.md · debug/plan.md debug/results.md + .json · report.xlsx · HANDOFF.md
Two model steps, four deterministic ones. The model writes SQL (red) and later explains results it is handed (red). It never produces a figure: the profile comes from the data tool, the numbers from the SQL engine, the workbook from those numbers. A question the data cannot answer is reported as such, with the reason the plan gave.
Why deterministic steps exist at all

Small local models are unreliable at multi-step tool use: a 7B model will often print a tool call as text instead of making it, or invent a column name. Every place where that failure would corrupt the answer is a plain step instead. The model is left with the one thing it is good at, writing a query or a paragraph from material it has been given, and a wrong query fails loudly in the engine rather than quietly in a report.

Runs

Durable runs, standing orders, and the approval gate

Every run is a row in a SQLite store and every transition is written before it takes effect. A restart does not lose work: interrupted runs come back with their finished steps kept, and runs started by a trigger resume on their own.

Run lifecycle pending running awaiting_input done error cancelled verify / apply Approve all steps passed a step failed, no route operator cancelled restart: running runs resume (trigger runs on their own, operator runs on a click)
The red loop is the approval gate. Before a step that runs a command or writes into your project, the run parks as awaiting_input and says exactly what would run. Approve continues it; the approval covers that step for the rest of the run, so a fix-and-check loop asks once. A parked run survives restarts and never blocks a trigger.

Standing orders

A horde can declare triggers in horde.md: a cron schedule, a watch on a file or folder, or a webhook route. Each firing starts an ordinary durable run carrying its input (the watched path, pre-filled form answers, the payload) and a provenance marker. Overlap is a policy per trigger: skip, queue one, or run in parallel. Built-in schedules ship switched off, so nothing runs, or costs, until you turn it on in the Hordes screen.

Tools

Tools: a few built in, everything else over MCP

Agents reach the outside world through tools, and kowalski keeps its own set small on purpose. Capability lives in standalone MCP servers that any MCP client can use, kowalski included.

Built in
kowalski-core · tools/internal

web_fetch reads one public page (private and cloud-metadata addresses refused, every redirect re-checked). web_search uses Brave, Staan or SearXNG when configured. File tools stay inside the folder you chose in Setup.

tableski
separate project · MCP

Every spreadsheet as a SQL table (Excel, CSV, Parquet), on Apache DataFusion. Local binary or the hosted service; Setup signs in with OAuth, so there is no token to copy.

Any MCP server
[[mcp.servers]] in config

HTTP or stdio, with bearer tokens that refresh themselves. The Docker MCP gateway brings its whole catalog through one entry. First-party servers are built on the emperor-mcp framework.

rookery
kowalski-mcp-rookery

The horde builder as an MCP server: any MCP-capable assistant can design a horde, validate it and write the folder, without the kowalski UI.

Tool calls go over the model's native tool-calling interface when it has one, with a text fallback for models that do not. Each step lists the tools it may use; nothing else is offered to the model in that step.

Memory

Memory in three tiers, on SQLite by default

TierHoldsDefaultOptional
workingThe active conversation and task contextin process, bounded—
episodicA chronological log of recent interactionsSQLite filePostgres
semanticDistilled facts, found by similarity, with relationsin-process vectorsPostgres + pgvector, graph queries

Hordes mostly do not need memory: their state is the files they write, which is easier to inspect and to trust. Memory serves the chat agent and long-lived assistants.

Safety

Safe by default, because agents can act

RiskDefault
Someone else on the network drives your agentsOn 127.0.0.1 no token is needed. Bound to any other address, the API always requires the bearer token printed at first start.
A step runs a command you did not expectverify and apply wait for your approval, showing the command. [horde] confirm_commands = false turns this off for hordes you trust.
A workflow from elsewhere misbehavesImported bundles are checked like untrusted zips, their triggers arrive switched off, and every step runs in a separate process.
A page makes the fetcher reach inside your networkFetches go to public addresses only, and every redirect is checked again.
Keys leak into a repositoryThe sample config is config.example.toml; your config.toml is never committed, and Setup writes it owner-only when it holds a key.
Code

Four crates and a UI

kowalski
kowalski/

The server binary: HTTP API, horde runner, triggers, Setup, embedded UI and built-in hordes.

kowalski-core
kowalski-core/

Agents, model providers, tools, MCP client, memory, step handlers, manifests and bundles, the Rookery builder.

kowalski-cli
kowalski-cli/

Terminal chat, bundle export and import, MCP checks, migrations, and the child process for isolated steps.

kowalski-mcp-rookery
kowalski-mcp-rookery/

The horde builder over MCP (stdio or stateless HTTP).

ui
ui/ · Vue 3

Setup, Horde runs with live progress and approvals, Chat, Rookery, federation views. Built once, compiled into the server.

Pre-built binaries for macOS and Linux are attached to every release; install.sh downloads the right one and verifies its checksum.