# 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-attest = { version = "0.1.0", path = "crates/didbot-attest" } didbot-avatar = { version = "0.1.0", path = "crates/didbot-avatar" } 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-hookd = { version = "0.1.0", path = "crates/didbot-hookd" } didbot-mcp = { version = "0.1.0", path = "crates/didbot-mcp" } didbot-serve = { version = "0.1.0", path = "crates/didbot-serve" } didbot-swarm = { version = "0.1.0", path = "crates/didbot-swarm" } didbot-hook = { version = "0.1.0", path = "crates/didbot-hook" } 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-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-reconcile = { version = "0.1.0", path = "crates/didbot-reconcile" } didbot-verify = { version = "0.1.0", path = "crates/didbot-verify" } didbot-policy-source = { version = "0.1.0", path = "crates/didbot-policy-source" } # 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`, the only crate in this workspace # that authenticates *as* an atproto account rather than serving one. 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"] } 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"] } hex = "0.4" 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"] } rmcp = { version = "3", features = ["server", "transport-io", "transport-streamable-http-server", "macros"] } 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" unicode-width = "0.2" 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"] } # 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" rustls-pemfile = "2" # 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-dev` 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" [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.