1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
# Contributing
## Development
Rust is pinned by `rust-toolchain.toml` (1.97.0). The atirc client is a git
dependency pinned to a revision in `Cargo.toml`; builds need fetch access to
`tangled.org`, and the revision only moves when bumped deliberately (with
`Cargo.lock` updated in the same change).
```bash
cargo build --release --bins
```
Binaries:
- `asyncai`
- `asyncai-mcp-simple`
- `socialweb-computer-kcp-readonly`
- `socialweb-computer-kcp-readwrite`
`cargo install --path .` installs all four. `prompts/*.md` and
`src/code_mode/helpers.py` are embedded at build time, so `prompts/` must be
present in any build context.
## Checks
```bash
cargo fmt --check
cargo clippy --all-targets -- -D warnings
cargo test
```
Pool sandbox tests need the Monty worker:
```bash
cargo +1.97.0 install monty-runtime --locked # installs `monty`
MONTY_WORKER=$(command -v monty) cargo test --test code_mode_pool
```
Tests mock the model, MCP servers, and the atirc bus; no live services or
network are used. Never commit endpoints or tokens.
## Linux amd64 build
```bash
container system start
# Drop stopped build containers from earlier runs before building.
container list --all --format json \
| jq -r '.[] | select(.status.state=="stopped") | .id' \
| while read -r id; do container delete "$id"; done
rm -rf $HOME/.tmp/asyncai-out
mkdir -p $HOME/.tmp/asyncai-out
container build --platform linux/amd64 -m 8G -c 4 \
-f Dockerfile \
-o type=local,dest=$HOME/.tmp/asyncai-out .
find $HOME/.tmp/asyncai-out -name asyncai -type f
file $HOME/.tmp/asyncai-out/*/linux_amd64/out/asyncai # ELF 64-bit, static-pie
```
Build context and destination must live under `$HOME`: the builder cannot read
`/tmp` (the context transfers as 2B and `COPY` fails). The container uses the
image's pinned 1.97.x toolchain; `rust-toolchain.toml` is not copied into the
build, so local builds can stay on 1.97.0 while the image builds with its own
1.97 patch release. `-j 2` keeps `monty`'s rustc within the 8G builder memory
limit (4 jobs gets OOM-killed).
Smoke-run in an Alpine container:
```bash
container run --rm --platform linux/amd64 --memory 1G \
-v $HOME/.tmp/asyncai-out/*/linux_amd64/out:/out \
alpine:latest /out/asyncai --help
```
## Windows amd64 build (cross-compile)
Cross-compiles self-contained PE32+ x86-64 binaries with mingw-w64 inside the
build container. No Windows host, MSVC SDK, `cargo-xwin` or zig is needed.
```bash
container system start
# Drop stopped build containers from earlier runs before building.
container list --all --format json \
| jq -r '.[] | select(.status.state=="stopped") | .id' \
| while read -r id; do container delete "$id"; done
rm -rf $HOME/.tmp/asyncai-win-out
mkdir -p $HOME/.tmp/asyncai-win-out
# Apple Silicon: linux/arm64. Intel Macs: linux/amd64. The build container
# architecture does not change the Windows binaries.
container build --platform linux/arm64 -m 8G -c 4 \
-f Dockerfile.windows \
-o type=local,dest=$HOME/.tmp/asyncai-win-out .
find $HOME/.tmp/asyncai-win-out -name '*.exe' -type f
file $HOME/.tmp/asyncai-win-out/*/linux_arm64/out/asyncai.exe
# PE32+ executable (console) x86-64, for MS Windows
```
`Dockerfile.windows` installs the mingw-w64 toolchain plus `cmake`, `perl` and
`nasm` (used by `aws-lc-sys`), adds the Rust `x86_64-pc-windows-gnu` target, and
builds with a static CRT so the `.exe` files do not require
`libgcc_s_seh-1.dll` or `libwinpthread-1.dll`. The target is added to the
image's pinned 1.97.x toolchain; `rust-toolchain.toml` is not copied into the
build so rustup cannot switch to a toolchain without that target.
A PE binary cannot be run on macOS without Windows or Wine, so this target is
compile-verified only. Windows runtime notes:
- `--monitor` and the kcp runner spawn `sh -c`, so they need Git Bash or WSL.
- The Claude Code session harness reads `HOME`; on Windows set `HOME` (Git
Bash) or pass `--session-dir FORMAT=PATH`, or run with `--no-session`.
## macOS arm64 build
Native build on an Apple Silicon Mac:
```bash
cargo build --release --bins
file target/release/asyncai # Mach-O 64-bit executable arm64
```
All four binaries land in `target/release/`.
## Building on a Linux host
The `container` commands above are Apple-container-specific. On a Linux host
use the same Dockerfiles with BuildKit (`docker buildx` or `podman build`, both
support `--output type=local`), or build directly with the host toolchain as
below. All three platforms can be produced from one Linux host except macOS.
### Linux amd64 on a Linux host
```bash
# Debian/Ubuntu
sudo apt-get install -y build-essential cmake perl pkg-config
# Fedora/RHEL
sudo dnf install -y gcc gcc-c++ make cmake git perl pkgconf-pkg-config
cargo build --release --bins
```
`git` is required because `.cargo/config.toml` sets `git-fetch-with-cli` for
the pinned atirc dependency.
For the same static-pie binary the container build produces, add the musl
target (static against musl, no glibc dependency on the target host):
```bash
sudo apt-get install -y musl-tools
rustup target add x86_64-unknown-linux-musl
cargo build --release --bins --target x86_64-unknown-linux-musl
```
The four binaries land in `target/x86_64-unknown-linux-musl/release/`.
### Windows amd64 on a Linux host (cross-compile)
```bash
# Debian/Ubuntu
sudo apt-get install -y \
gcc-mingw-w64-x86-64 g++-mingw-w64-x86-64 binutils-mingw-w64-x86-64 \
nasm cmake perl pkg-config
# Fedora/RHEL
sudo dnf install -y mingw64-gcc mingw64-gcc-c++ mingw64-binutils \
nasm cmake perl pkgconf-pkg-config
rustup target add x86_64-pc-windows-gnu
export CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER=x86_64-w64-mingw32-gcc
export CARGO_TARGET_X86_64_PC_WINDOWS_GNU_RUSTFLAGS="-C target-feature=+crt-static"
cargo build --release --bins --target x86_64-pc-windows-gnu
```
`nasm` is only needed for x86_64 Windows (`aws-lc-sys`); the static CRT flag
is what keeps the `.exe` files free of `libgcc_s_seh-1.dll` and
`libwinpthread-1.dll`.
### macOS arm64
A macOS artifact needs a macOS host: there is no supported way to produce one
from a Linux host without an Apple SDK. Build it with `cargo build --release
--bins` on an Apple Silicon Mac (or via an osxcross toolchain with a legally
obtained SDK, which is out of scope here). Linux (glibc or musl) and Windows
artifacts can both be built on one Linux host.
### Container builds on a Linux host
```bash
docker buildx build --platform linux/amd64 -f Dockerfile \
--output type=local,dest=$PWD/out/linux .
# The build container arch does not affect the Windows artifacts.
docker buildx build --platform linux/amd64 -f Dockerfile.windows \
--output type=local,dest=$PWD/out/windows .
```
`--output type=local` requires BuildKit (default in current Docker). The
`$HOME/.tmp` and `-m`/`-c` builder-memory notes in the Apple sections do not
apply; Docker inherits the host's resources.
## Cold container builds and cargo cache seeding
A cold container build can sit ~10 minutes at "Updating git repository":
the container VM has no IPv6 route while musl prefers AAAA for
`tangled.org`/crates.io. Seed the host cargo caches and build offline to skip
the hang. Keep the `/work/target` cache mount — when it is warm only the
`asyncai` crate recompiles; the cache also makes the copied registry layers
cheap.
Create the seed from the host caches (both git repositories must be present:
`atirc` and `atproto-crates`):
```bash
mkdir -p cargo-seed/git/db cargo-seed/git/checkouts cargo-seed/registry
cp -R ~/.cargo/git/db/atirc-* ~/.cargo/git/db/atproto-crates-* cargo-seed/git/db/
cp -R ~/.cargo/git/checkouts/atirc-* ~/.cargo/git/checkouts/atproto-crates-* cargo-seed/git/checkouts/
cp -R ~/.cargo/registry/index ~/.cargo/registry/cache cargo-seed/registry/
```
Then apply this diff to `Dockerfile` (and `Dockerfile.windows` for the same
offline behavior):
```diff
COPY src ./src
COPY prompts ./prompts
+COPY cargo-seed/git /usr/local/cargo/git
+COPY cargo-seed/registry/index /usr/local/cargo/registry/index
+COPY cargo-seed/registry/cache /usr/local/cargo/registry/cache
-RUN --mount=type=cache,target=/usr/local/cargo/registry \
- --mount=type=cache,target=/usr/local/cargo/git \
- --mount=type=cache,target=/work/target \
+RUN --mount=type=cache,target=/work/target \
TOOLCHAIN=$(echo /usr/local/rustup/toolchains/*) && \
- PATH="$TOOLCHAIN/bin:$PATH" cargo build --release --bins -j 2 && \
+ PATH="$TOOLCHAIN/bin:$PATH" CARGO_NET_OFFLINE=true \
+ cargo build --release --bins -j 2 && \
```
`cargo-seed/` is local scratch state; leave it untracked and do not commit it.
It must stay in the build context (do not gitignore it) or the `COPY` steps
fail.
## Disk hygiene
`container build` caches aggressively and never prunes itself. Each distinct
build adds an image (~0.4–0.5 GB), and the BuildKit builder container
(`~/Library/Application Support/com.apple.container/containers/buildkit`,
~13 GB) keeps cargo registry/target caches between builds. Apple's CLI has no
automatic garbage collection, so this grows every day a build runs.
Reclaim stopped containers (safe; the running `buildkit` builder is skipped):
```bash
container list --all --format json \
| jq -r '.[] | select(.status.state=="stopped") | .id' \
| while read -r id; do container delete "$id"; done
```
Deleting the `buildkit` container (`container stop buildkit && container
delete buildkit`) drops its ~13 GB cache; the next build recreates the builder
automatically and is cold. Rust build output is the other grower:
`target/` reaches multiple GB; `cargo clean` resets it.
## Installing on a host
There is no server deploy: copy the four binaries to the machine that runs the
agent and put them on `PATH` together.
```bash
scp target/release/asyncai \
target/release/asyncai-mcp-simple \
target/release/socialweb-computer-kcp-readonly \
target/release/socialweb-computer-kcp-readwrite \
<host>:/tmp/
ssh <host> 'sudo install -m 0755 /tmp/asyncai /tmp/asyncai-mcp-simple \
/tmp/socialweb-computer-kcp-readonly /tmp/socialweb-computer-kcp-readwrite \
/usr/local/bin/'
```
Use the matching Linux, macOS or Windows build for the target host; the Linux
amd64 binaries are static-pie and need no libc or CA bundle on the host.