1[workspace] 2resolver = "2" 3members = [ 4 "crates/choir-actor", 5 "crates/choir-bridge", 6 "crates/choir-cli", 7 "crates/choir-demo", 8 "crates/choir-fs", 9 "crates/choir-guards", 10 "crates/choir-hash", 11 "crates/choir-identity", 12 "crates/choir-oplog", 13 "crates/choir-sequencer", 14 "crates/choir-merge", 15 "crates/choir-node", 16 "crates/choir-queue", 17 "crates/choir-spike", 18 "crates/choir-store", 19 "crates/choir-view", 20] 21 22# choir-actor is the D3 second implementation of the actor-runtime seam. It 23# pulls rivetkit, tokio and a bundled libsqlite3 that nothing else in the 24# workspace touches, and compiling it costs more than the other thirteen 25# crates put together. Excluding it from the default set means a bare 26# `cargo build`/`cargo test` skips that tree; the seam's conformance suite 27# still runs under an explicit `-p choir-actor` or `--workspace`, which is 28# what the hand-run gate uses. 29default-members = [ 30 "crates/choir-bridge", 31 "crates/choir-cli", 32 "crates/choir-demo", 33 "crates/choir-fs", 34 "crates/choir-guards", 35 "crates/choir-hash", 36 "crates/choir-identity", 37 "crates/choir-oplog", 38 "crates/choir-sequencer", 39 "crates/choir-merge", 40 "crates/choir-node", 41 "crates/choir-queue", 42 "crates/choir-spike", 43 "crates/choir-store", 44 "crates/choir-view", 45] 46 47[workspace.package] 48version = "0.0.1" 49edition = "2021" 50license = "MIT OR Apache-2.0" 51authors = ["asxeem"] 52# Where releases are downloaded from. `dist` refuses to generate a 53# GitHub workflow without it, and `cargo binstall` builds its `pkg-url` 54# out of it, so this one string decides where two independent installers 55# look. This carried `<owner>` as a placeholder, the way every host and 56# account name in a tracked file here does, until the commit that first 57# pushed to a remote filled it in. It is the one such name in the tree 58# that is meant to be real: it is where the public code lives, and the 59# installers read it. Every other host and account name stays a 60# placeholder. 61repository = "https://github.com/deadcaf3/choir" 62 63# Release packaging (`dist`, formerly `cargo-dist`). Read only by that 64# tool, never by a build: it generates the release workflow, the archives 65# and the shell installer from this table, so what a contributor 66# downloads is described in the repository rather than in CI YAML nobody 67# reads. 68# 69# `choir` and `choir-node` are both published. This reverses the 70# original rule here, which was that only the client shipped and an 71# operator built the daemon from source "because that is the audited 72# path". The audience is what changed: that sentence describes an 73# operator who already has a Rust toolchain and a checkout, and the 74# person self-hosting a node now is somebody with a fresh VPS and 75# neither. Making them install rustup and openssl headers before they 76# can run a daemon is the same wall the client install just removed, 77# one step further in. The audited path is unchanged and still 78# documented; it stopped being the *only* path. 79# 80# What that costs, and what pays it: a prebuilt daemon must carry its 81# own build stamp (`CHOIR_GIT_HEAD`, which the workflow sets, since a 82# dist build has no HEAD of its own to read) and must not depend on the 83# runner's libssl (`features`, below). Both are release-time 84# arrangements; neither changes what a source build does. 85# 86# `choir-demo`, `choir-spike` and `choir-bridge` opt out in their own 87# manifests, each with the reason there. 88# 89# `dist init` writes the mechanical keys below and reformats this table 90# when it does, which is why every comment in it is load-bearing prose 91# that has to be put back by hand afterwards rather than a note on a 92# line dist owns. 93[workspace.metadata.dist] 94# The exact packaging tool this release was cut with. Written by `dist 95# init`, never by hand: a version typed from memory is a pin that says 96# something untrue about how the artifact was built. 97cargo-dist-version = "0.32.0" 98ci = "github" 99installers = ["shell"] 100targets = ["aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu"] 101install-path = "CARGO_HOME" 102# The one build difference between a release artifact and a source 103# build: statically link OpenSSL, so a downloaded binary does not name 104# the runner's `libssl` soname. See `crates/choir-node/Cargo.toml`. 105features = ["vendored-tls"] 106# The gate is the gate. Release packaging runs after it rather than 107# beside it, so nothing is ever published that `./gate` refused: see 108# `plan-jobs` below for where that is enforced. 109# 110# Empty, and spelled out rather than left to the default: nothing here 111# is published to crates.io. A registry release is a name nobody can 112# take back, and this is a research prototype. 113publish-jobs = [] 114# The quick gate, in front of everything. `plan-jobs` become a job the 115# build jobs `needs:`, so a refused gate stops the release before a 116# single artifact is built rather than after. The job it names is the 117# same reusable workflow `ci.yml` calls. 118plan-jobs = ["./gate-quick"] 119# Extra steps spliced into every build job, resolved relative to 120# `.github/workflows/`. One step, and it is the build stamp: 121# `.github/build-setup.yml`, one level up, because GitHub parses every 122# file under `workflows/` as a workflow and this one is a step list. 123github-build-setup = "../build-setup.yml" 124 125# No self-updater. `choir` telling itself to fetch a new binary is a 126# second, unaudited install path, and the one command that installs it 127# is also the one command that upgrades it. 128install-updater = false 129 130# Native runners for the two Linux targets rather than dist's default, 131# which cross-compiles aarch64 on an x86 runner through 132# `cargo-zigbuild`. Two reasons, both about this workspace rather than 133# a preference: the release build statically links OpenSSL, and 134# vendored OpenSSL is a C build with its own configure step that a 135# cross toolchain is the least reliable place to run; and a native 136# runner is the only build whose `libc` is the one the artifact will 137# meet. `ubuntu-22.04-arm` is a GitHub-hosted runner, so this adds no 138# self-hosted machine for anybody to maintain. 139[workspace.metadata.dist.github-custom-runners] 140x86_64-unknown-linux-gnu = "ubuntu-22.04" 141# `host` is spelled out because dist knows the platform of the runner 142# images it shipped with and `ubuntu-22.04-arm` is not one of them; a 143# bare string here is rejected rather than guessed, which is the right 144# refusal and this is the answer to it. 145aarch64-unknown-linux-gnu = { runner = "ubuntu-22.04-arm", host = "aarch64-unknown-linux-gnu" } 146 147[workspace.dependencies] 148blake3 = "1" 149serde = { version = "1", features = ["derive"] } 150serde_json = "1" 151 152# The workspace shipped on cargo defaults until now, which for release 153# means lto = false and codegen-units = 16 — the compiler declining to 154# optimise across crate boundaries in a workspace that is almost entirely 155# crate boundaries. 156# 157# Deliberately absent: panic = "abort". `Node::serve_forever` spawns a 158# thread per request and relies on a panicking handler killing only that 159# thread; under abort, one bad request takes down the daemon. Revisit only 160# once handlers are proven panic-free. 161[profile.release] 162opt-level = 3 163# "fat", not the usual "thin" default advice. Measured on this workspace, 164# fat won every metric including the one it is supposed to lose: clean 165# build 169.7 s vs 206.6 s, binary 1.98 MB vs 2.28 MB. Small workspace, 166# codegen-units = 1 already, so thin's incrementality buys nothing here and 167# its own bookkeeping costs. Re-measure if the workspace grows a lot. 168lto = "fat" 169codegen-units = 1 170strip = true 171# Keeps a symbolicatable profile without carrying full debuginfo. `strip` 172# removes the symbol table from the binary; this leaves enough for a 173# sampling profiler to name frames. 174debug = "line-tables-only" 175 176# Full DWARF isn't needed for a suite that asserts behaviour, not 177# breakpoints; backtraces still carry line numbers under line-tables-only. 178# Applies workspace-wide, including dependency builds below (they only 179# override opt-level, so this is inherited). 180[profile.dev] 181debug = "line-tables-only" 182 183# Dependencies are optimised even in dev builds: they are compiled once and 184# then cached, while the workspace crates are rebuilt constantly. This 185# keeps `cargo test` from running the whole suite through an unoptimised 186# blake3 and ed25519. 187[profile.dev.package."*"] 188opt-level = 3 189 190# The profile release artifacts are built with. `dist init` writes this 191# with `lto = "thin"`; that line is deliberately removed, because 192# `[profile.release]` above chose "fat" from a measurement on this 193# workspace where fat won every metric including build time. A release 194# build is the one build where the measurement matters most, so it 195# inherits release and overrides nothing. 196[profile.dist] 197inherits = "release" 198 199[workspace.lints.rust] 200missing_docs = "warn" 201 202# Perf lints, enabled with a measured caveat: they do NOT find the thing 203# this optimization pass is chasing. 204# 205# The expectation was that redundant_clone would flag the full-View clone 206# in ChoirPolicy::check. It does not, and cannot: the clone is immediately 207# mutated by trial.apply, so it is genuinely used and the lint has nothing 208# to complain about. Six redundant_clone hits fired across the workspace 209# and every one is in a test or the demo. An O(state) clone on a hot path 210# is a design fact, not a lint-visible one. 211# 212# Kept anyway -- they are cheap and they caught real slips in new code -- 213# but nobody should read a clean clippy run as evidence of no allocation 214# problem. 215# 216# "warn" not "deny" deliberately: the gate here is run by hand, and a 217# denied lint turns an advisory into a broken build for whoever is mid-edit. 218[workspace.lints.clippy] 219perf = { level = "warn", priority = -1 } 220redundant_clone = "warn" 221needless_collect = "warn" 222large_enum_variant = "warn" 223 224[workspace.lints.rustdoc] 225broken_intra_doc_links = "deny" 226# The neighbouring lint, and the reason it is spelled out rather than left 227# at its default warn. `broken_intra_doc_links` catches a link to an item 228# that does not exist; a link to an item that exists but is *private* 229# is a different lint entirely, and it sat at warn while CLAUDE.md told 230# readers doc-link integrity was enforced. Two such links lived in 231# `assign_reviewers` for two days without anything failing, because no 232# gate step ran `cargo doc` at all. Denying it is half the fix; running 233# `cargo doc --workspace --no-deps` in the ship gate is the other half. 234private_intra_doc_links = "deny"