Contributing · 1. Build from source
Four commands get you a running daemon, and you do not need protoc despite four documents in the repository telling you that you do. This chapter gives the exact sequence, the measured cost in time and disk, and the failure modes you will hit.
Follow it top to bottom the first time. It is a tutorial: one path, no choices.
1.1 Prerequisites
| Requirement | Status | Where it is pinned |
|---|---|---|
| Rust 1.88.0 | Required | rust-toolchain.toml:2, channel = "1.88.0", profile = "minimal", components = ["rustfmt", "clippy"] |
A C toolchain and linker (cc) | Required | CI fails hard without it at ci.yml:117 |
git | Recommended, not required | Three build scripts shell out to git rev-parse --short HEAD; they fall back to "unknown" |
jq and curl | Only for the shell gates | scripts/demo-receipt-tamper.sh, scripts/assert-context-custody.sh, scripts/assert-no-phone-home.sh |
strace or unshare plus ip | Only for the no-phone-home assertion | assert-no-phone-home.sh exits 2 (skip) if neither is present |
protoc | NOT required | See below |
| Disk | 50 GB minimum, 150 GB if you run coverage | See section 1.5 |
rustup reads rust-toolchain.toml automatically on your first cargo invocation inside the tree and installs 1.88.0 if you do not have it. The same version is declared as the MSRV at Cargo.toml:47 and gated by a required CI job named MSRV (1.88.0).
On Debian or Ubuntu, the C toolchain is one package:
sudo apt install build-essential
You do not need protoc
Four documents in the repository say you do. They are wrong.
crates/corecrux-proto/build.rs:9 resolves a vendored compiler and sets the PROTOC environment variable itself, before tonic_prost_build runs:
fn main() -> anyhow::Result<()> {
let protoc = protoc_bin_vendored::protoc_bin_path()?;
std::env::set_var("PROTOC", protoc);
The dependency is protoc-bin-vendored = "3.2.0" at crates/corecrux-proto/Cargo.toml:16. This was verified during the 2026-07-27 audit on a machine with no protoc anywhere on PATH: the workspace builds.
The four documents that claim otherwise:
| Document | Line | Claim |
|---|---|---|
CONTRIBUTING.md | 18 | "protoc (Protocol Buffers compiler), required by corecrux-proto" |
README.md | 89 | "From source (Rust 1.88+, protobuf-compiler)" |
README.md | 386 | Same, in the Operations reference table |
docs/getting-started.md | 98 | "Rust 1.88+, protobuf-compiler" |
Installing protobuf-compiler does no harm, the build script overrides PROTOC regardless, but nothing in the workspace consults a system protoc. Fixing these four lines is good first contribution number 5.
The four build scripts
The workspace has exactly four build.rs files, and knowing this saves you diagnosis time when one of them is the thing that failed:
| Build script | Does |
|---|---|
| crates/corecrux-proto/build.rs | Compiles the two protobuf packages using the vendored protoc |
crates/corecrux-types/build.rs | Embeds the short git SHA |
crates/corecruxctl/build.rs | Embeds the short git SHA |
| crates/corecruxd/build.rs | Embeds the short git SHA; falls back to "unknown" |
A tarball checkout with no .git builds fine. --version will report unknown for the SHA.
1.2 Clone to a running binary
Verbatim. Nothing here needs substituting.
git clone https://github.com/CueCrux/Crux.git
cd Crux
cargo build --release
CORECRUXD_AUTH_MODE=dev_scopes CORECRUXD_DATA_DIR=./data ./target/release/corecruxd
Two binaries land in target/release/: corecruxd (the daemon) and corecruxctl (the CLI).
The daemon opens three listeners on loopback: HTTP on 14800, MCP on 14801, gRPC on 4007. Confirm it is alive from a second terminal:
curl -sf http://127.0.0.1:14800/readyz && echo OK
curl -s http://127.0.0.1:14800/v1/version | jq .
CORECRUXD_AUTH_MODE is mandatory and has no default. The daemon refuses to start without it and refuses to start on an unrecognised value; this is deliberate fail-closed behaviour, tracked at config.rs:876 and documented at config.example.env:12. See 3.3 Auth modes for what each value means.
1.3 Build the way CI builds
For any build whose result you intend to push, add --locked. CI uses it everywhere, and it stops a stray Cargo.lock bump from sneaking into your diff:
cargo build --locked --workspace
AGENTS.md:58 states the same, and every cargo invocation in .github/workflows/ci.yml goes through scripts/ci-cargo-with-fallback.sh with --locked.
For iteration, use the debug profile. Everything except the smoke and performance gates works against target/debug/:
cargo build --locked --workspace # debug: much faster
cargo build --locked --release # release: slow, see 1.4
1.4 What the build actually costs
The release profile is deliberately slow. Cargo.toml:185:
[profile.release]
opt-level = 3
lto = "thin"
codegen-units = 1
strip = "symbols"
codegen-units = 1 plus thin LTO makes --release substantially slower than a stock release build. The debug profile is opt-level = 0 (Cargo.toml:182).
Measured 2026-07-27 on a 16-core machine with 30 GB RAM and a warm target/:
| Measurement | Result |
|---|---|
| Resolved dependency graph | 525 packages, 28 of them workspace members |
cargo test -p corecrux-frame (leaf crate, 6 tests) | 2.04 s wall |
cargo build --workspace --offline, all 497 third-party deps already compiled, 28 workspace crates rebuilding, debug | 90.75 s wall |
Cold cargo build --release on typical developer hardware | Realistically 20 to 45 minutes |
| Full test suite on a warm CI runner | About 10 minutes |
So: the 28 workspace crates alone are a roughly 90-second debug rebuild on 16 cores. Scale down accordingly for fewer cores.
docs/troubleshooting.md:162claims "First Rust build downloads and compiles all dependencies (~5 minutes)." That is not plausible for 525 packages atcodegen-units = 1with thin LTO, and it will set your expectations wrong. Treat the measured range above as authoritative.
1.5 Disk: the 178 GB number
A tree that has built debug, release and cargo llvm-cov reaches 178 GB in target/ (du -sh target, measured 2026-07-27). target/llvm-cov-target/ and target/doc/ are separate trees stacked on top of debug and release.
| Workflow | Budget |
|---|---|
| Debug only | 50 GB or more |
| Debug plus release | 100 GB or more |
| Anything that runs coverage | 150 GB or more |
If your home partition cannot take that, redirect the build output before your first cargo command:
export CARGO_TARGET_DIR=/mnt/big-volume/crux-target
Reclaim space at any time with cargo clean, which is safe and only costs you a rebuild.
This is stated up front because a contributor whose disk fills mid-build, with no warning, does not come back.
1.6 First-build failures and their fixes
| Symptom | Cause | Fix |
|---|---|---|
error: linker 'cc' not found | No C toolchain | sudo apt install build-essential |
error: failed to run custom build command for proc-macro2 | The same absent or broken C toolchain | sudo apt install build-essential |
FATAL: CORECRUXD_AUTH_MODE is required | The daemon was started with no auth mode | export CORECRUXD_AUTH_MODE=dev_scopes |
address already in use | 14800, 14801 or 4007 is already bound | Stop the other daemon, or override CORECRUXD_HTTP_PORT / CORECRUXD_MCP_PORT / CORECRUXD_GRPC_PORT for this run only |
| Disk fills partway through the build | target/ growth, section 1.5 | cargo clean, or set CARGO_TARGET_DIR onto a larger volume |
Stack overflow in a corecruxctl test thread | RUST_MIN_STACK unset because you ran the test binary directly rather than through cargo | export RUST_MIN_STACK=8388608 |
The last one deserves explanation. .cargo/config.toml sets:
[env]
RUST_MIN_STACK = "8388608"
The comment there records why: corecruxctl's clap command tree is large enough that the debug-build parser overflows the default 2 MiB test-thread stack. That setting applies only to cargo-spawned processes. If you invoke a compiled test binary directly, export it yourself.
1.7 Documents in the repo that are wrong
Fixing any of these is a legitimate, self-contained first pull request. All are verified against main at 93b41a7.
| Document | Line | Claim | Truth |
|---|---|---|---|
CONTRIBUTING.md | 18 | protoc is required | It is not. Section 1.1 |
README.md | 89 and 386 | protobuf-compiler is required | It is not. Section 1.1 |
docs/getting-started.md | 98 | protobuf-compiler is required | It is not. Section 1.1 |
docs/getting-started.md | 99 | Links [README Quickstart](../README.md#quickstart) | Broken anchor. The README heading is "Up and running in 60 seconds" at README.md:58, so the anchor is #up-and-running-in-60-seconds |
docs/troubleshooting.md | 162 | First build takes about 5 minutes | Section 1.4 |
1.8 What to do next
| Next | Chapter |
|---|---|
| Understand what you just built | 2. Repository tour |
| Run it with a useful configuration | 3. Running locally |
| Run the tests | 4. Testing |
| Verify the trust claims rather than believing them | 4.9 Verifying the claims yourself |
Sources
- rust-toolchain.toml:2, pinned toolchain 1.88.0
- Cargo.toml:47,
rust-version = "1.88.0" - Cargo.toml:182,
[profile.dev] opt-level = 0 - Cargo.toml:185, the release profile
- crates/corecrux-proto/build.rs:9, vendored
protocresolution - crates/corecrux-proto/Cargo.toml:16,
protoc-bin-vendored = "3.2.0" - crates/corecruxd/build.rs, git SHA embedding with
"unknown"fallback - .cargo/config.toml,
RUST_MIN_STACK - crates/corecruxd/src/config.rs:876, auth-mode fail-closed tracking
- .github/workflows/ci.yml:117, the
ccpreflight probe - scripts/assert-no-phone-home.sh,
straceorunsharerequirement

