# Virtual workspace. Every crate shares one version, because they are one # product released together rather than independently useful libraries; see # release.toml, which relies on that. [workspace] resolver = "3" members = ["crates/*"] [workspace.package] version = "0.1.0" edition = "2021" rust-version = "1.90" license = "MIT" repository = "https://tangled.org/permadeath.com/didbot" publish = false [workspace.dependencies] didbot-config = { version = "0.1.0", path = "crates/didbot-config" } didbot-http = { version = "0.1.0", path = "crates/didbot-http" } didbot-attest = { version = "0.1.0", path = "crates/didbot-attest" } didbot-avatar = { version = "0.1.0", path = "crates/didbot-avatar" } didbot-brand = { version = "0.1.0", path = "crates/didbot-brand" } didbot-site-anim = { version = "0.1.0", path = "crates/didbot-site-anim" } didbot-dns = { version = "0.1.0", path = "crates/didbot-dns" } didbot-fsm = { version = "0.1.0", path = "crates/didbot-fsm" } didbot-name = { version = "0.1.0", path = "crates/didbot-name" } didbot-pds = { version = "0.1.0", path = "crates/didbot-pds" } didbot-policy = { version = "0.1.0", path = "crates/didbot-policy" } didbot-policy-regex = { version = "0.1.0", path = "crates/didbot-policy-regex" } didbot-policy-cedar = { version = "0.1.0", path = "crates/didbot-policy-cedar" } didbot-policy-records = { version = "0.1.0", path = "crates/didbot-policy-records" } didbot-serve = { version = "0.1.0", path = "crates/didbot-serve" } didbot-swarm = { version = "0.1.0", path = "crates/didbot-swarm" } didbot-identity = { version = "0.1.0", path = "crates/didbot-identity" } didbot-key = { version = "0.1.0", path = "crates/didbot-key" } didbot-lexicon = { version = "0.1.0", path = "crates/didbot-lexicon" } didbot-data = { version = "0.1.0", path = "crates/didbot-data" } didbot-repo = { version = "0.1.0", path = "crates/didbot-repo" } didbot-schema = { version = "0.1.0", path = "crates/didbot-schema" } didbot-stack = { version = "0.1.0", path = "crates/didbot-stack" } didbot-setup = { version = "0.1.0", path = "crates/didbot-setup" } didbot-tls = { version = "0.1.0", path = "crates/didbot-tls" } didbot-claim = { version = "0.1.0", path = "crates/didbot-claim" } didbot-agentd = { version = "0.1.0", path = "crates/didbot-agentd" } didbot-reconcile = { version = "0.1.0", path = "crates/didbot-reconcile" } didbot-verify = { version = "0.1.0", path = "crates/didbot-verify" } # Default features off, so a crate names the network half (`fetch`) only when # it polls; the rest builds for wasm32. didbot-policy-source = { version = "0.1.0", path = "crates/didbot-policy-source", default-features = false } # jacquard is pinned exactly. Its unreleased mainline moves serde_ipld_dagcbor, # reqwest, base64 and sha2 all at once, and a graph carrying two versions of # those ends up with two incompatible copies of the CID types. jacquard-common = { version = "=0.12.1", default-features = false, features = ["std", "crypto", "crypto-k256"] } # The real atproto OAuth 2.1 client -- PAR, PKCE, DPoP and the loopback # callback server -- for `didbot-claim`, which authenticates *as* an atproto # account rather than serving one. `didbot-swarm` uses its DPoP proofs and # PKCE to sign synthetic agents in. Pinned to the same release as # `jacquard-common` for the reason given above. # `loopback` is the local-redirect-server flow a one-shot CLI needs; # `browser-open` is the convenience of not making the operator copy a URL by # hand. Neither pulls in `jacquard-lexicon`'s generated scope tables -- # `scope-check` stays off, because the scope this crate requests is built # from `didbot-serve`'s own grammar, not validated against jacquard's copy of # it. jacquard-oauth = { version = "=0.12.1", default-features = false, features = ["loopback", "browser-open"] } # The key type `jacquard-oauth` generates for DPoP and does not re-export. jose-jwk = { version = "0.1", default-features = false } argon2 = "0.6" # `ws` is not one of axum's default features and `com.atproto.sync.subscribeRepos` # is a WebSocket, so it is named here rather than per-crate: the workspace pins # one axum, and a second feature set would be a second build of it. axum = { version = "0.8", features = ["ws"] } # The `subscribeRepos` frame's encoded bytes are held by the replay buffer and # handed to every subscriber. `Bytes` makes each of those a refcount bump on # one allocation rather than a copy, and it is what `axum`'s # `Message::Binary` already takes, so a frame reaches the socket without one. # `didbot-tls` takes the same type from its ACME HTTP client's body, so the # version is pinned here rather than per-crate. bytes = "1" # `didbot-tls`'s ACME client builds `http::Request`/`Response` directly, and # `didbot-http`'s test suite does the same to construct a response to bound. http = "1" hex = "0.4" # Reads a blob's type from its magic bytes, for policy on uploads. infer = "0.22" futures-core = "0.3" # `stream` is what lets a client read a server-sent-event body as it # arrives rather than waiting for a response that never ends. reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls", "stream"] } sha2 = "0.10" hmac = "0.12" k256 = { version = "0.13", features = ["ecdsa"] } # Verification only, and never signing: see the `secp256r1` module in # `didbot-key` for why this project mints no P-256 key. p256 = { version = "0.13", features = ["ecdsa"] } rand = "0.9" rand_core = { version = "0.6", features = ["getrandom"] } serde = { version = "1", features = ["derive"] } serde_json = "1" subtle = "2" thiserror = "2" tokio = { version = "1", features = ["rt-multi-thread", "macros", "net", "signal", "time", "sync", "io-util"] } tower-http = { version = "0.6", features = ["trace", "cors", "timeout"] } tracing = "0.1" unicode-segmentation = "1" tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt", "time"] } time = { version = "0.3", features = ["formatting", "parsing", "macros"] } toml = "0.9" tower = { version = "0.5", features = ["util", "limit", "make"] } # Two crates parse URLs directly -- `didbot-tls` for the ACME directory and # `didbot-verify` for an ownership claim's endpoint -- so the version is # pinned here rather than per-crate, the way every other shared dependency # is. url = "2" # Two crates hand a browser a wasm module: didbot-site-anim and # didbot-policy-check. scripts/build-wasm.sh checks wasm-bindgen-cli against # the one version Cargo.lock pins for this. wasm-bindgen = "0.2" # PKCE's `S256` challenge is base64url, no padding — RFC 7636 §4.2. base64 = "0.22" # `default-features = false` plus an explicit `ring` here, not the default # `aws-lc-rs`, keeps this workspace on the one crypto backend it already has # via `rustls-webpki` -- see the jacquard comment above for why a second copy # of a crypto crate in the graph is a problem here, not just weight. # # The `hyper-rustls` feature is deliberately left off, even though it is the # obvious way to get instant-acme's built-in HTTP client: that feature always # turns on `rustls-platform-verifier`, which pulls `aws-lc-rs` in regardless # of this `ring` choice -- checked with `cargo tree -i aws-lc-rs` against the # `hyper-rustls` feature before deciding against it. `didbot-tls` supplies its # own `instant_acme::HttpClient` over `reqwest`, which is already on `ring` # via `rustls-tls`, instead. instant-acme = { version = "0.8", default-features = false, features = ["ring", "rcgen", "time"] } rustls = { version = "0.23", default-features = false, features = ["ring", "logging", "std", "tls12"] } # `tls-rustls-no-provider` gets the hot-reloadable `RustlsConfig` without # pulling `aws-lc-rs` in as axum-server's own default feature would; the # `ring` provider above is what actually gets installed at startup. axum-server = { version = "0.8", default-features = false, features = ["tls-rustls-no-provider"] } # Pure-Rust ASN.1/X.509 parsing, only to read a certificate's `notAfter` back # out for the renewal clock. No crypto backend of its own. x509-parser = "0.16" # Already in the graph via axum-server's `tls-rustls-no-provider` feature; # named explicitly so the certificate swap can use the same primitive rather # than a second one, per the atomicity requirement in `didbot-tls`. arc-swap = "1" # Test-only cert generation for `didbot-tls`'s rotation test. Its default # features already pick `ring`, matching the rest of the workspace. rcgen = "0.14" # Debug information is most of what a build of this workspace produces, and # almost none of it is used. Nothing here is debugged by stepping: the server # is watched through its log, and a failure arrives as a backtrace. Line tables # are what a backtrace needs — file and line for every frame — and they are a # fraction of the size of the full DWARF a stepping debugger wants. # # Measured by building `didbot-pds` from cold into a target directory of # its own, once with each setting, so that neither answer was polluted by # artefacts of the other: the target directory went from 1.1 GB to 827 MB, the # binary from 106 MB to 43 MB, and the build from 59.6 s to 47.2 s. The # workspace as a whole gains more than that share, because the facade crate's # test binaries link every crate in it and there is one per suite. # # It matters beyond the seconds: four live worktrees have filled this machine's # disk mid-link more than once, and the failure reads as the linker crashing # rather than as a full disk. # # The saving arrives on the next `cargo clean` and not before. Cargo leaves # artefacts it has superseded, so a target directory that has been built in for # a while holds both shapes and briefly grows. On the worktree this was written # in, one that had reached 31 GB rebuilt from clean — the whole workspace, # every test binary included — into 3.4 GB. # # Set `debug = true` here, temporarily, if you do need a stepping debugger. [profile.dev] debug = "line-tables-only" # The same for test binaries, which are the largest artefacts in the tree: the # suites in the facade crate link every crate in the workspace, and there is # one such binary per suite. [profile.test] debug = "line-tables-only" # `lto` and one codegen unit are what actually ship: this is what runs on the # public internet, built once and run for months, so the extra link time is # worth a smaller and faster binary. `strip` drops the symbols that buys. # # `overflow-checks` stays on despite the profile's default of off. The CBOR, # CAR and WAL frame parsers do length and offset arithmetic on bytes read # straight off the network, and a silent wraparound there is a correctness # bug, not a slow path worth optimizing away. [profile.release] lto = "fat" codegen-units = 1 strip = true overflow-checks = true [workspace.lints.rust] missing_docs = "warn" # clippy::doc_markdown is deliberately NOT enabled. The narrative pages under # docs/ are compiled into rustdoc, and that lint wants backticks around every # CamelCase word it sees — which in this project means PreToolUse, # SessionStart, ATProto and every other protocol noun that belongs in prose. # Backticking a proper noun to satisfy a lint makes the sentence worse.