# Nanocode: The Harness for Swarms

nanocode is an agent harness built for swarms. One process runs a terminal agent in a few milliseconds and a tree of a million subagents in under an hour. We measured both against six other coding agents and against itself, and dated every number.

**Agents should be cheap enough to run by the thousand.** That takes a harness that answers in milliseconds, holds a waiting agent in kilobytes, and keeps every agent in a store that survives a crash. So we built one and benchmarked it against everything else we could install.

## TL;DR

* **4.7x faster replies:** nc answered a one-word prompt in 4.83 ms. The next fastest agent, fx, took 22.9 ms, and Claude Code took 163 ms.
* **4.7x faster tool loops:** nc ran 50 shell steps in one turn in 63.1 ms. The next fastest, Pi, took 299 ms, and fx took 1,057 ms.
* **A million subagents in one process:** ten roots of 100,000 children each ran to completion in 1,749 s on Postgres.
* **174 KB per waiting agent:** one Claude Code process answering one prompt holds the memory of about a thousand of them.
* **7 to 29x less memory than the Node and Rust agents:** 32 nc runs at once held 19.2 MB each, against 135 MB for Pi, 197 MB for Codex, 221 MB for Claude Code, and 584 MB for OpenCode.
* **Where we trail:** fx draws its first frame 0.16 ms sooner and holds less than half the memory.

The CLI numbers are from 2026-11-12 and the swarm numbers from 2026-11-04 to 2026-11-10.

## Seven Agents

On 2026-11-12 we ran nanocode's CLI (nc), Vercel fx 0.0.16, Pi 1.5.2, OpenCode 1.19.5, Claude Code 2.1.322, and Codex CLI 0.167.2 through the same scripted work. Each lane talked only to a local fixture that streams a fixed reply, in a network namespace with only loopback.

The servers were AMD EPYC 4245P bare metal (12 threads, Ubuntu 24.04). Lanes took turns in every round. Each number is a median of 30 runs (10 for long turns, fleets, and idle), and CPU and memory cover the whole process tree.

nc is the release build at 7837aae16: non-PIE and laid out by BOLT. nc and fx ran on one server, the others on four more of the same model, where fx agreed within 8% on wall-clock medians.

## The Numbers

|                          |   nc |    fx |   Pi | OpenCode | Claude Code | Codex |
| ------------------------ | ---: | ----: | ---: | -------: | ----------: | ----: |
| First frame, ms          | 2.02 |  1.86 |  138 |    1,376 |         151 |   126 |
| Memory after it, MB      | 19.0 |  8.91 |  121 |      n/a |         209 |   175 |
| One-word reply, ms       | 4.83 |  22.9 |  151 |      997 |         163 | 5,045 |
| CPU for that reply, ms   | 4.93 |  1.62 |  203 |    1,853 |         183 |  90.4 |
| 50 shell steps, ms       | 63.1 | 1,057 |  299 |    1,593 |         656 | 5,045 |
| Idle CPU, ms per s       | 0.00 |  0.62 | 0.00 |     24.2 |        5.46 |  0.41 |
| 32 runs at once, MB each | 19.2 |  7.69 |  135 |      584 |         221 |   197 |
| Install, MB              | 78.3 |  12.2 |  159 |      409 |         326 |   437 |

OpenCode's memory sample one second after its first frame came back empty in every round, and its peak was about 960 MB. Pi's install does not count the Node runtime it needs.

## Side by Side

Every agent on every measure, in the same order. Shorter bars are better.

```mermaid
---
config:
  xyChart:
    showDataLabel: true
---
xychart-beta horizontal
  title "First frame, median ms, 2026-11-12"
  x-axis ["nc", "fx", "Pi", "OpenCode", "Claude Code", "Codex"]
  y-axis "ms" 0 --> 1400
  bar [2.02, 1.86, 138, 1376, 151, 126]
```

```mermaid
---
config:
  xyChart:
    showDataLabel: true
---
xychart-beta horizontal
  title "One-word reply, median ms, 2026-11-12"
  x-axis ["nc", "fx", "Pi", "OpenCode", "Claude Code", "Codex"]
  y-axis "ms" 0 --> 5100
  bar [4.83, 22.9, 151, 997, 163, 5045]
```

```mermaid
---
config:
  xyChart:
    showDataLabel: true
---
xychart-beta horizontal
  title "50 shell steps in one turn, median ms, 2026-11-12"
  x-axis ["nc", "fx", "Pi", "OpenCode", "Claude Code", "Codex"]
  y-axis "ms" 0 --> 5100
  bar [63.1, 1057, 299, 1593, 656, 5045]
```

```mermaid
---
config:
  xyChart:
    showDataLabel: true
---
xychart-beta horizontal
  title "Memory per run, 32 runs at once, MB, 2026-11-12"
  x-axis ["nc", "fx", "Pi", "OpenCode", "Claude Code", "Codex"]
  y-axis "MB" 0 --> 600
  bar [19.2, 7.69, 135, 584, 221, 197]
```

```mermaid
---
config:
  xyChart:
    showDataLabel: true
---
xychart-beta horizontal
  title "CPU for a one-word reply, ms, 2026-11-12"
  x-axis ["nc", "fx", "Pi", "OpenCode", "Claude Code", "Codex"]
  y-axis "ms" 0 --> 1900
  bar [4.93, 1.62, 203, 1853, 183, 90.4]
```

```mermaid
---
config:
  xyChart:
    showDataLabel: true
---
xychart-beta horizontal
  title "Memory after first frame, MB, 2026-11-12, OpenCode not sampled"
  x-axis ["nc", "fx", "Pi", "Claude Code", "Codex"]
  y-axis "MB" 0 --> 220
  bar [19.0, 8.91, 121, 209, 175]
```

```mermaid
---
config:
  xyChart:
    showDataLabel: true
---
xychart-beta horizontal
  title "Idle CPU, ms per s, 2026-11-12"
  x-axis ["nc", "fx", "Pi", "OpenCode", "Claude Code", "Codex"]
  y-axis "ms per s" 0 --> 25
  bar [0.00, 0.62, 0.00, 24.2, 5.46, 0.41]
```

```mermaid
---
config:
  xyChart:
    showDataLabel: true
---
xychart-beta horizontal
  title "Install size, MB, 2026-11-12"
  x-axis ["nc", "fx", "Pi", "OpenCode", "Claude Code", "Codex"]
  y-axis "MB" 0 --> 450
  bar [78.3, 12.2, 159, 409, 326, 437]
```

## Where Time Goes

* **Runtime start:** Pi and OpenCode spend 100 to 200 ms before they print a version. The native binaries (nc, fx) print it in 2.7 ms, the time of /usr/bin/true under this harness.
* **Tool steps:** nc sends the next request 0.83 ms after a tool result. fx waits 20.5 ms, so its 50-step turn takes 17 times as long.
* **The screen:** at 100 tokens a second, fx shows a token 200 ms after it arrives, and Claude Code after 151 ms. nc shows it after 0.08 ms (dist build at 3c16e6c8e).

We want to be clear about Codex: it waits about five seconds after every headless reply. In the same runs it tried to reach github.com and chatgpt.com, which the namespace blocked. We did not trace whether it waits on those connections.

## Where We Trail

fx is smaller. It holds 8.9 MB after its first frame where nc holds 19.0 MB, and it spends 1.6 ms of CPU on a one-word reply where nc spends 4.9 ms. Its binary is 12.2 MB, and nc's is 78.3 MB.

Most of nc's resident memory is pages of its own code. The release build has no position-independent code, and BOLT packs the code a training run executed. A plain build holds 30.9 MB. nc-lite, the same SDK cut down to what fx ships, holds 5.2 MB.

## One Million

The CLI's agent loop is the SDK's, and the SDK runs subagents as durable sessions in one process. **On 2026-11-04 one machine with ten cores ran one million agents to completion:** ten roots of 100,000 subagents each.

| Store    | Completed in | Peak memory | Reopened in |
| -------- | -----------: | ----------: | ----------: |
| Postgres |      1,749 s |      6.8 GB |       118 s |
| SQLite   |      2,801 s |      8.8 GB |       102 s |

A reopen restores all 1,000,000 agents from the store, and the SQLite file held 32 GB. Every child called a scripted model in the same process, so the runs measure the runtime and its store, not a model.

## A Hundred Thousand

On 2026-11-10, with 5,000 children running at a time, 100,000 subagents completed on every store the SDK ships:

```mermaid
---
config:
  xyChart:
    showDataLabel: true
---
xychart-beta horizontal
  title "100,000 subagents, seconds to complete"
  x-axis ["JSONL", "SQLite", "Postgres", "Rows on SQLite", "Rows on Postgres"]
  y-axis "seconds" 0 --> 260
  bar [68, 88, 85, 113, 246]
```

A store killed at 50,000 finished agents had 5,000 children waiting on the model again 3.4 s after the next process started. All 10,000 IDs of a 10,000-agent tree were assigned in 0.7 to 1.5 s.

## One Agent

On 2026-11-09, a child that messages a sibling and submits a result cost the same in a tree of 1,000 and in a tree of 100,000: 3 model requests, 16 commits, 96 KB written to the journal, and 62 KB kept in the store.

Records are named by the hash of their content, so the system prompt and tool schemas that every child shares are stored once. Each commit's new keys land in random pages of the index, and SQLite writes whole pages, so the disk wrote 977 KB per agent under SQLite.

## Why It Scales

* **One notice:** completions that arrive together reach the root as one turn, not one turn each.
* **No locks:** the registry is a mailbox. Every operation is a job posted to it, so agents never queue for a lock, and the SDK has no mutex at all.
* **Grouped commits:** the store commits every queued write as one transaction, and the journal writes behind, acknowledging a save when it is queued.
* **Shared prompts:** tool descriptions, schemas, and the system prompt are shared pointers in memory and single records on disk.

## Crash Recovery

A swarm of 600 ran with one second of model latency, and we killed the process with SIGKILL when 310 children had finished. A second process restored the tree in 0.7 s and told the 310 that the runtime had restarted. All 600 had completed 5.4 s after it started.

## Budgets

A swarm that spends money needs a limit per branch. spawn_agent takes budget_usd: the most that a child and its descendants may spend together. The SDK records it in the journal and refuses a budget above the smallest one in the spawner's lineage.

## Agents Are Cheap

In a tree of 20,000 children all waiting on a model (2026-11-10), each child held 174 KB, and 39 KB with hibernation on. One Claude Code process answering one prompt peaked at 186 MB, the memory of about a thousand waiting children.

**A fleet of agents does not need a fleet of processes.** One nanocode process holds thousands of waiting children, keeps each one in its store, and wakes them when the model answers.

## Reproduce It

The harness is in bench/cli on the cli-bench branch of nanocodexyz/nanocode, with the raw JSONL, the commands, and a generated report for each run. The swarm figures come from nanocode's DECISIONS.md entries of 2026-11-04 to 2026-11-10, measured with its durability-ledger harness and a scripted model.

If you find a number you cannot reproduce, or an agent we should add to the benchmark, tell us on [GitHub](https://github.com/nanocodexyz). We will rerun it and publish the result.

- [Subagents](/docs/guides/subagents): Spawn, steer, and wait for children.

- [CLI](/docs/guides/cli): Install and run nc.

- [Durability](/docs/guides/durability): Keep the agent tree across restarts.
