Remote builds and clients¶
This page describes the host launcher on a dedicated builder, the clients that start a build, and where build data lives. To create a builder, see Set up a builder. To change a builder that already runs jobs, see Runtime updates.
Host launcher¶
The host launcher is /usr/local/bin/oxidex-remote-build. Its configuration is /etc/oxidex-remote-build.json.
It checks the project identifier, the allowed command, and the image identity. The Docker admission wrapper
then checks the mounts and limits and takes the admission ledger lease before the container starts. Containers run as UID and GID 1001 without a Docker socket, added capabilities, or
privilege escalation. See the security model.
Use the approved uploader account for source transfer. A control account with sudo access isn't necessarily
allowed to traverse the source directories. Don't change directory permissions to work around an identity
mismatch.
On a host with an already prepared source project, an example build command is:
sudo -n /usr/local/bin/oxidex-remote-build oxidex-example \
cargo build --release --locked --bin oxidex
preparecreates the project directories, gives the uploader source access, and gives UID 1001 the project's target directory and the shared Cargo directory. It doesn't erase existing source or stop a build. It's refused while the builder drains.- Every launcher call on a dedicated builder resets the shared parent directories to
root:1001with mode0710and refuses symbolic links in those paths. cleanupremoves everything in the project's source directory after it checks the cache locks and container mounts. It keeps the targets, and it runs during a drain.- Transfer the source completely before you start the build. Don't prepare or synchronize a project that another build is using.
- Cancellation and reconciliation checks in the admission and Docker adapter paths depend on exact run and container identities. They don't authorize a broad container stop.
Two Rust compilations can run inside the one aggregate container, but they share its CPU and memory limits. They aren't two independent 28 GiB reservations. Measure the actual concurrent workload before you claim that it fits.
Choose a client¶
OxiDex's repository-owned just recipes and worker registry are maintained in the OxiDex repository. Configure
that client for the approved dedicated host, uploader identity, source, image, and cache before you enable its
route. This repository doesn't update OxiDex's workflow or registry. See
Simplify remote builds for the target workflow.
Explicit client for a selected host¶
src/remote_rust_build.py builds on a host that you select. It works with a compatible legacy host:
python3 src/remote_rust_build.py \
--source "$HOME/git/oxidex" \
--instance YOUR_INSTANCE --zone YOUR_ZONE --project YOUR_PROJECT \
--worktree-id oxidex-example \
--profile release \
--artifact-dir "$HOME/git/oxidex/target/remote-linux/release" \
--evidence-dir "$HOME/oxidex-ops/evidence/remote-build-example"
- Use a new evidence directory for each attempt to keep earlier receipts.
- The client snapshots the working-tree contents of tracked files, so uncommitted edits are included. It skips
paths under
.git,.codex,.claude,.agents,target,node_modules, and.venv, files named.env*,*.pem, or*.key, and symbolic links. It transfers and verifies the archive, runs the build, checks the artifact checksum, and records source and timing evidence. The archive (remote-source.tar.gz), the transaction log, and the receipt stay in the evidence directory. --artifact-dirdownloads the verifiedoxidexbinary into that directory. Without it, the client keeps a receipt and doesn't download the binary.- The client uses
gcloud compute sshandgcloud compute scp. Verify that the selected account and host access match the target. Don't point this client at a dedicatedbuilder-*host: those hosts accept only the approved uploader over direct SSH. See the security model.
Cargo shims¶
Install the optional Cargo shims:
The installer writes cargo-oxidex-remote and cargo-oxidex-auto into $CARGO_HOME/bin (or ~/.cargo/bin),
and --bin-dir overrides the location. The shims run scripts from this checkout, so keep the checkout in place.
The command names are cargo oxidex-remote and cargo oxidex-auto.
- For
cargo oxidex-remote, the configured OxiDex checkout supplies the remote instance, zone, project, and worktree settings through its environment or.cargo/config.toml.cargo oxidex-autoreads only the project and selects the instance and zone itself. Its selection connects to each candidate withgcloud compute ssh, and it always downloads the artifact intotarget/remote-linux/<profile>in the checkout. - Use
--profile debugor--profile releasewith the explicit client. cargo oxidex-autopicks a lightly used worker and delegates the build over SSH. It makes up to three attempts by default (--max-attempts). It retries only when the failed attempt's receipt is marked retryable, so compiler or header errors stop instead of moving to another host.- The automatic selector considers running legacy
oxidex-runners-*andoxidex-buildbench-*hosts. It doesn't consider the dedicatedbuilder-registry. - The helper calls
cargo remote-build. That separate alias or command must already exist in the configured OxiDex checkout, because the installer creates only the twooxidex-*shims. Installing the shims alone doesn't configure automatic selection or the dedicated builder registry.
Linux artifacts from these clients don't run natively on macOS. Cross targets and SDK-free framework interfaces don't replace native artifact validation. A full ExifTool comparison needs its own pinned oracle, fixtures, and qualification workflow: the standalone Dockerfile doesn't install the full oracle corpus.
Qualification source admission¶
Projects with the dedicated qualification contract authenticate signed source, bundle, and run identities before they import any executable. A bounded importer and supervisor keep protective locks until all descendants are reaped. The adapter seals executable source separately from writable measurement Git metadata, and it validates the required mounts and the allowed oracle fetch.
These source controls don't certify an installed deployment. Production qualification still needs evidence from the actual startup, authority, mounts, process lifetime, disk, network, and workload. Don't mark a worker qualified from a successful source-only test suite or a bootstrap tool probe.
Storage and cache lifecycle¶
| Data | Host path |
|---|---|
| Docker images and cache | /mnt/runner-data/docker |
| containerd layers | /mnt/runner-data/containerd |
| Runner state | /mnt/runner-data/instances/<runner> |
| Runner workspaces | /mnt/runner-data/work/<runner> |
| Build source | /mnt/runner-data/remote-build/sources/<project> |
| Cargo downloads | /mnt/runner-data/remote-build/cargo |
| Per-project targets | /mnt/runner-data/remote-build/targets/<project> |
| Exported artifacts (legacy client transactions only, created and removed for each run) | /mnt/runner-data/remote-build/exports/<run> |
Mounting only the Docker data root isn't enough if containerd still stores layers on the boot disk. Bootstrap configures both on attached storage. Caches persist across builds on the same surviving disk. That retention isn't a backup, and it doesn't prove cache reuse across VMs. Don't format, delete, or detach an existing cache disk to make a failed bootstrap retry succeed.
Builder drains deny new builds while active builds, units, leases, and cache or upload and export locks finish. Preserve foreign or uncertain work, and record why a drain is blocked. Runner drains and builder drains have different completion tests. See Fleet and autoscaler.