choir/choir

choir/choir

main32eea3631639git clone https://choirs.dev/choir/choir.git
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"