# One SDK, Every Language

Write the agent once in Rust and run it everywhere. nanocode is one Rust SDK, and TypeScript, Swift, Python, and C do not reimplement it: each is a thin layer over the same compiled code.

**An agent harness is mostly behavior, and behavior drifts when you copy it by hand.** Which prompt the model reads, when a tool call is journaled, what a crash reports: a port rewrites all of it and then diverges. A binding compiles it once.

## TL;DR

* **One module for the web:** both harnesses, durability, and Code Mode fit in 12.1 MB of WebAssembly, 2.7 MB with Brotli. It instantiates in 0.9 ms and runs a one-reply turn in 3.7 ms.
* **Three C functions:** the whole native interface is start, send, and stop. It carries 40 commands today and has never changed shape.
* **Any language that calls C:** a C example is 86 lines with no JSON library, and a Python one is 74 lines of the standard library.
* **Inside a game loop:** polling the SDK costs 9 to 13 µs a frame at 60 Hz.
* **Fix once, ship everywhere:** a one-line change to the SDK's manifest reached every binding, and no binding changed a line.

All of these were measured on 2026-11-09.

## Bindings, Not Ports

| Language   | How It Gets the SDK                                                    | Boundary                     | Runs In                            |
| ---------- | ---------------------------------------------------------------------- | ---------------------------- | ---------------------------------- |
| Rust       | The `nanocode` crate                                                   | Function calls               | `nc`, servers                      |
| TypeScript | One WebAssembly module                                                 | Eight host imports           | Workers, Functions, Node, browsers |
| Swift      | A static library                                                       | Three C functions            | macOS and iOS apps                 |
| C          | [libnanocode](https://github.com/nanocodexyz/libnanocode), one library | The same three, and two more | Game engines, any program          |
| Python     | The Swift library, loaded with `ctypes`                                | The same three               | Any script                         |

## Built to Travel

Three rules in the SDK make this possible, and every build checks them.

* **Two targets:** all 47 crates build for native code and for WebAssembly. A crate that needs a native API without a WebAssembly path fails the build.
* **No I/O in the core:** storage, HTTP, and tools are interfaces. The core asks, and the host answers.
* **No locks:** the SDK has no mutex, so it runs on a thread pool, on one thread, and in a module that has no threads at all.

## Two Shapes

The bindings differ in where the I/O runs, because their hosts differ.

```mermaid
flowchart LR
  sdk[nanocode SDK in Rust]
  sdk -->|wasm32| module[One module]
  sdk -->|native targets| library[One library]
  module --> ts[TypeScript host: SQL, HTTP, tools]
  library --> swift[Swift app]
  library --> c[C program]
  library --> python[Python script]
```

A WebAssembly module can do nothing on its own. So in TypeScript the host supplies storage, HTTP, and tools, and the module asks for each through an import. That is why one module runs in a Durable Object, a Function, and a browser tab.

A native library can do everything. So in Swift, C, and Python the SDK keeps its own HTTPS client, SQLite file, and JavaScript engine. The program supplies configuration, tools, and credentials.

## How Fast?

The module holds both harnesses, the durability rules, and QuickJS for Code Mode cells. It never touches the network, the disk, or a credential. In Node, against a scripted model:

| Measurement                               | Result                                        |
| ----------------------------------------- | --------------------------------------------- |
| Module size                               | 12.1 MB, 4.0 MB with gzip, 2.7 MB with Brotli |
| Compile the module                        | 16 ms                                         |
| Instantiate it                            | 0.9 ms                                        |
| Open a new session                        | 2.3 ms                                        |
| Reopen a session from its journal         | 3.1 ms                                        |
| A turn with one reply                     | 3.7 ms                                        |
| A turn with a cell that calls a host tool | 12.6 ms                                       |

```mermaid
xychart-beta horizontal
  title "Milliseconds in Node, median of 20"
  x-axis ["Compile", "Instantiate", "Open", "Reopen", "Turn", "Turn with a tool"]
  y-axis "ms" 0 --> 18
  bar [16.1, 0.9, 2.3, 3.1, 3.7, 12.6]
```

**The harness costs milliseconds.** The rest of a turn's time is the model.

## Three Functions

The whole native interface is three C functions: start a runtime with a callback, send it a message, and stop it. Everything else is JSON over them.

* **Commands:** the program sends a command with an ID, and the runtime answers that ID.
* **Events:** session events arrive at the callback as they happen.
* **Calls:** when the SDK needs the program, such as to run a tool, it sends a call and waits for the reply.

A new command is a new variant in Rust. The three functions do not change: the interface has 40 commands today and the same functions it started with.

## C and Python

Swift is not special here. Any language that can call C can drive the library, and two examples in [nanocode-swift](https://github.com/nanocodexyz/nanocode-swift) do.

| Program                   | Size                            | What It Does                                       |
| ------------------------- | ------------------------------- | -------------------------------------------------- |
| `Examples/C/main.c`       | 86 lines, no JSON library       | Opens a session, declares a tool, answers its call |
| `Examples/Python/main.py` | 74 lines, standard library only | The same, with a Python function as the tool       |

Both ran a turn against a scripted model whose reply is one Code Mode cell. The cell passed `0.09596104461529853` to the tool, and each program printed that number, the assistant's reply, and a completed turn.

## In a Game

A game cannot give its frame to a library. [libnanocode](https://github.com/nanocodexyz/libnanocode) is the SDK alone as a C library, with two more functions: one takes the game's allocator, and one delivers queued messages on the thread that calls it.

| Measurement                        | Result                                                |
| ---------------------------------- | ----------------------------------------------------- |
| Poll in a 60 Hz loop during a turn | 9 to 13 µs a frame                                    |
| Threads, as the game bounds them   | 16, of which 10 are QuickJS workers                   |
| Memory after a turn                | 1.8 MB through the game's allocator, 44 KB outside it |
| Stripped program with the library  | 21.3 MB, 9.9 MB with gzip                             |

So a tool the agent calls runs on the game thread, between two frames. The repository has an Unreal Engine plugin of that shape. We have not yet compiled it against the engine.

## Fix Once

A fix or a feature lands once. On 2026-11-09 the SDK changed how it parses JSON numbers, in one line of its manifest. Each binding rebuilt, and no binding changed a line of its own.

| Language     | Check                                               |
| ------------ | --------------------------------------------------- |
| Rust         | 0 of 1,000,000 doubles changed in a JSON round trip |
| TypeScript   | A host tool received `0.09596104461529853`          |
| Swift        | A Swift tool received the same number               |
| C and Python | Their tools received the same number                |

## What Stays Native

A binding is thin only if the platform's own parts stay outside it.

* **Interface:** the Swift package has AppKit and UIKit views. nanocode-ts has headless React components, styled by [nds](https://github.com/nanocodexyz/nds).
* **Credentials:** a Swift app keeps sign-ins in the Keychain, and a Worker keeps keys in its bindings.
* **Platform tools:** an iPhone's calendar, contacts, and location are Swift tools that the app registers.

## The Tradeoffs

We want to be upfront about what this design costs.

* **Size:** the module is 12.1 MB. libnanocode links the SDK, both harnesses, QuickJS, and SQLite into a 21 MB program. The Swift library adds a shell and payments, so its C program is 26 MB and its Python library 29 MB.
* **A JSON boundary:** every command and event crosses as JSON text, which costs a parse on each side. In WebAssembly, request bodies and SQL rows cross as raw bytes.
* **One build per target:** the Swift library is built for macOS, iOS, and the simulator, and released as one 89 MB archive.
* **Platforms:** libnanocode ran a turn on macOS and Linux. Its Windows build ran only under an emulator, and no console build exists yet.

## Build With It

If your language can call C, it can run nanocode today. If you want a binding we do not ship yet, open an issue on [GitHub](https://github.com/nanocodexyz) and tell us what you are building.

- [nanocode-ts](/docs/guides/nanocode-ts): The WebAssembly module and its TypeScript host.

- [nanocode-swift](https://github.com/nanocodexyz/nanocode-swift): The Swift package, its views, and the C and Python examples.

- [libnanocode](https://github.com/nanocodexyz/libnanocode): The C library, the game loop example, and the Unreal Engine plugin.

- [In the Browser](/docs/guides/browser): Run the module in a page over sqlite-wasm.
