# 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 \ :/tmp/ ssh '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.