-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathCargo.toml
More file actions
489 lines (435 loc) · 19.6 KB
/
Copy pathCargo.toml
File metadata and controls
489 lines (435 loc) · 19.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
[workspace]
resolver = "2"
members = [
"crates/sbproxy",
"crates/sbproxy-core",
"crates/sbproxy-config",
"crates/sbproxy-modules",
"crates/sbproxy-plugin",
"crates/sbproxy-httpkit",
"crates/sbproxy-security",
"crates/sbproxy-observe",
"crates/sbproxy-platform",
"crates/sbproxy-cache",
"crates/sbproxy-transport",
"crates/sbproxy-util",
"crates/sbproxy-vault",
"crates/sbproxy-keystore",
"crates/sbproxy-storage",
"crates/sbproxy-mesh",
"crates/sbproxy-meter",
"crates/sbproxy-middleware",
"crates/sbproxy-extension",
"crates/sbproxy-ai",
"crates/sbproxy-rag",
"crates/sbproxy-billing",
"crates/sbproxy-tls",
"crates/sbproxy-openapi",
"crates/sbproxy-k8s-operator",
"crates/sbproxy-k8s-controller",
"crates/sbproxy-classifiers",
"crates/sbproxy-classifier-proto",
"crates/sbproxy-classifier-sidecar",
"crates/sbproxy-classifier-client",
"crates/sbproxy-agent-detect",
"crates/sbproxy-model-host",
"crates/sbproxy-capability",
"e2e",
]
# The k8s operator and the Gateway API controller both pull kube +
# k8s-openapi, which adds ~30-60s to a cold `cargo build --workspace`.
# Exclude them from default-members so plain `cargo build` and
# `cargo test` (no --workspace flag) skip them for day-to-day proxy
# work. CI's `cargo build --workspace --locked` still picks them up.
default-members = [
"crates/sbproxy",
"crates/sbproxy-core",
"crates/sbproxy-config",
"crates/sbproxy-modules",
"crates/sbproxy-plugin",
"crates/sbproxy-httpkit",
"crates/sbproxy-security",
"crates/sbproxy-observe",
"crates/sbproxy-platform",
"crates/sbproxy-cache",
"crates/sbproxy-transport",
"crates/sbproxy-util",
"crates/sbproxy-vault",
"crates/sbproxy-keystore",
"crates/sbproxy-storage",
"crates/sbproxy-mesh",
"crates/sbproxy-meter",
"crates/sbproxy-middleware",
"crates/sbproxy-extension",
"crates/sbproxy-ai",
"crates/sbproxy-rag",
"crates/sbproxy-billing",
"crates/sbproxy-tls",
"crates/sbproxy-openapi",
"crates/sbproxy-classifiers",
"crates/sbproxy-classifier-proto",
"crates/sbproxy-classifier-sidecar",
"crates/sbproxy-classifier-client",
"crates/sbproxy-agent-detect",
"e2e",
]
[workspace.package]
version = "1.11.0"
edition = "2021"
# Raised from 1.82 on 2026-08-07, because 1.82 was not remotely true and
# nothing had ever checked. The msrv lane added alongside this change found
# it in two steps, and both are worth recording because the first answer was
# not the real one:
#
# 1.82 could not even parse the graph. `block-buffer 0.12.0` declares
# edition 2024, and Cargo 1.82 rejects the manifest outright.
#
# 1.85 parses, and then cargo reports the actual constraint: 106 packages
# in the graph declare a floor above it, 62 of them at 1.94.0.
#
# 1.94 satisfies every dependency and then fails on our own code, with ten
# `mismatched types` errors in compression_metrics.rs where inference
# resolves `with_label_values` differently against prometheus 0.14 than it
# does on 1.95.
#
# That third step is the real finding. The dependency floor is 1.94, but the
# workspace does not compile there, which means this code has been written
# against whatever `stable` happened to be with nothing enforcing a floor at
# all. 1.82 was not a considered decision that drifted; it was a number
# nobody had checked since it was written.
#
# So this is set to what the code actually builds on rather than to a
# version we would like to support. Lowering it means fixing those ten sites
# for the benefit of a six-week-old toolchain, which is worth doing only if
# a packager asks. The msrv lane holds the line either way: it pins this
# exact version, so the next time someone reaches for a newer feature the
# job fails instead of the floor quietly moving again.
rust-version = "1.95"
authors = ["Soap Bucket LLC"]
license = "Apache-2.0"
repository = "https://github.com/soapbucket/sbproxy"
homepage = "https://sbproxy.dev"
documentation = "https://github.com/soapbucket/sbproxy/tree/main/docs"
readme = "README.md"
[workspace.dependencies]
# Internal crates
sbproxy-core = { path = "crates/sbproxy-core" }
sbproxy-config = { path = "crates/sbproxy-config" }
sbproxy-modules = { path = "crates/sbproxy-modules" }
sbproxy-plugin = { path = "crates/sbproxy-plugin" }
sbproxy-httpkit = { path = "crates/sbproxy-httpkit" }
sbproxy-security = { path = "crates/sbproxy-security" }
sbproxy-observe = { path = "crates/sbproxy-observe" }
sbproxy-platform = { path = "crates/sbproxy-platform" }
sbproxy-cache = { path = "crates/sbproxy-cache" }
sbproxy-transport = { path = "crates/sbproxy-transport" }
sbproxy-util = { path = "crates/sbproxy-util" }
sbproxy-vault = { path = "crates/sbproxy-vault" }
sbproxy-keystore = { path = "crates/sbproxy-keystore" }
sbproxy-storage = { path = "crates/sbproxy-storage" }
sbproxy-mesh = { path = "crates/sbproxy-mesh" }
# WOR-2123: attested consumption metering, plus the hash chain and receipt
# signing hoisted into it by WOR-2125. It takes third-party crates but no
# workspace crate, so an operator metering a plain REST API never compiles the
# AI stack to do it.
sbproxy-meter = { path = "crates/sbproxy-meter" }
sbproxy-middleware = { path = "crates/sbproxy-middleware" }
sbproxy-extension = { path = "crates/sbproxy-extension" }
sbproxy-model-host = { path = "crates/sbproxy-model-host" }
sbproxy-ai = { path = "crates/sbproxy-ai" }
# WOR-2098: retrieval-augmented generation runtime. Providers are cargo
# features that default off, so consumers opt into exactly the embedding
# and vector-store adapters they compile.
sbproxy-rag = { path = "crates/sbproxy-rag", default-features = false }
# WOR-2100: payment settlement runtime. The default build compiles only the
# normalized money, intent, and receipt domain. The durable store, the
# authoritative service, the recovery worker, and every rail adapter sit
# behind cargo features, so a binary carries exactly the settlement code it
# opted into and no payment feature joins the binary default set.
sbproxy-billing = { path = "crates/sbproxy-billing", default-features = false }
sbproxy-tls = { path = "crates/sbproxy-tls" }
sbproxy-openapi = { path = "crates/sbproxy-openapi" }
sbproxy-classifiers = { path = "crates/sbproxy-classifiers" }
sbproxy-classifier-client = { path = "crates/sbproxy-classifier-client" }
sbproxy-agent-detect = { path = "crates/sbproxy-agent-detect" }
sbproxy-capability = { path = "crates/sbproxy-capability" }
# Async runtime
tokio = { version = "1", features = ["full"] }
futures = "0.3"
# Bytes and buffers
bytes = "1"
zeroize = "1"
# Serialization
serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde_yaml = { package = "yaml_serde", version = "0.10" }
# Compact serde binary format shared by the internal mesh wire (RPC frames +
# gossip) and the semantic-cache wire. Maintained, unlike bincode
# (RUSTSEC-2025-0141). One declaration so both crates resolve the same copy.
postcard = { version = "1", features = ["use-std"] }
# Concurrency and data structures
arc-swap = "1"
dashmap = "6"
# Bounded LRU maps (sbproxy-ai response caches, sbproxy-rag stale-context
# cache). Same 0.16 line sbproxy-ai already pins so the lockfile keeps one
# copy.
lru = "0.18"
parking_lot = "0.12"
smallvec = { version = "1", features = ["union"] }
compact_str = "0.8"
# Allocator (replaces system malloc on the binary crate)
mimalloc = { version = "0.1", default-features = false }
# Hot reload
notify = "7"
# Plugin system
inventory = "0.3"
# Capability-based filesystem traversal
cap-std = "4.0.2"
# Observability
# release_max_level_info compile-strips debug!/trace! calls from release
# builds. Hot paths in server.rs / policy evaluation get their log macros
# eliminated entirely rather than evaluating arguments and being filtered
# at runtime. See docs/RUST_OPTIMIZATIONS.md B1.
tracing = { version = "0.1", features = ["release_max_level_info"] }
tracing-subscriber = { version = "0.3", features = ["json", "env-filter"] }
prometheus = "0.14"
# WOR-1081: JSON Schema generation from the Rust config types.
# `preserve_order` keeps the generated schema stable across runs
# so the regenerator's CI gate diffs byte-for-byte (otherwise a
# HashMap iteration reorder would surface as a no-op churn diff).
schemars = { version = "0.8", features = ["preserve_order"] }
# Error handling
anyhow = "1"
thiserror = "2"
# HTTP
http = "1"
httpdate = "1"
http-body-util = "0.1"
hyper = { version = "1.9", features = ["client", "http2", "server"] }
hyper-util = { version = "0.1", features = ["tokio"] }
# Pingora
pingora = "0.8"
pingora-core = { version = "0.8", features = ["rustls"] }
pingora-proxy = "0.8"
pingora-http = "0.8"
pingora-error = "0.8"
# URL parsing
url = "2"
# Encoding
base64 = "0.22"
percent-encoding = "2"
# Text processing
regex = "1"
minijinja = "2"
pulldown-cmark = "0.12"
graphql-parser = "0.4.1"
# Networking
ipnetwork = "0.20"
# Async
async-trait = "0.1"
# CEL expressions
cel = { version = "0.13", features = ["regex", "chrono", "json"] }
# Rego interpreter for `policy: rego` (WOR-2408). Default features are
# load bearing: `default-features = false` fails to build because the
# `net` builtin assumes `arc`.
regorus = "0.11"
# Lua scripting
mlua = { version = "0.11", features = ["luau", "serialize", "vendored", "error-send"] }
# JavaScript scripting
rquickjs = { version = "0.11", features = ["bindgen", "classes", "properties", "parallel"] }
swc_common = "24"
swc_ecma_ast = "27"
swc_ecma_codegen = "30"
swc_ecma_parser = { version = "43", default-features = false, features = ["typescript"] }
swc_ecma_transforms_base = "47"
swc_ecma_transforms_typescript = "53"
swc_ecma_visit = "27"
# WebAssembly scripting (sandboxed module runtime).
# wasmtime + wasi-common move in lockstep; bump both together. The version
# floor is set by the cargo-deny advisory gate, which fails on any
# unpatched RUSTSEC entry against wasmtime.
wasmtime = { version = "46.0.2", default-features = false, features = ["cranelift", "runtime", "parallel-compilation"] }
wasi-common = "46"
wasmtime-wasi = { version = "46.0.2", default-features = false, features = ["p1"] }
# HTTP client
reqwest = { version = "0.12", features = ["json", "stream", "blocking"] }
# gRPC for the classifier sidecar (WOR-704). The proxy speaks one
# InferenceService contract to either the minimal OSS sidecar or the
# enterprise rich sidecar. Versions match the enterprise client (tonic 0.12 /
# prost 0.13); all three are already present transitively in Cargo.lock.
# Default features only (transport + codegen) -- UDS/localhost needs no TLS;
# a remote-sidecar TLS feature can be added when that transport lands.
tonic = "0.12"
prost = "0.13"
tonic-build = "0.12"
# Vendored `protoc` binary for deterministic protobuf generation. Pinned
# exactly so the LND gRPC stubs generated by sbproxy-billing come from one
# known compiler build rather than whatever is on the host PATH. It is an
# optional build-dependency of an off-by-default feature, so a default
# workspace build never compiles or runs it.
protoc-bin-vendored = "=3.2.0"
# Dynamic protobuf reflection for descriptor-driven REST <-> gRPC
# transcoding (WOR-819). Parses a FileDescriptorSet at config load and
# builds DynamicMessage values for JSON <-> protobuf at runtime, so the
# proxy can transcode without compiled stubs. Pinned to the 0.14 line
# because it tracks prost 0.13 (the workspace prost major); the 0.16
# line moved to prost 0.14. The `serde` feature provides the proto3 JSON
# mapping used to convert request/response bodies. Dual MIT/Apache-2.0,
# so no NOTICE attribution is required.
prost-reflect = { version = "0.14", features = ["serde"] }
prost-types = "0.13"
# Background task tracking (graceful shutdown drains).
tokio-util = { version = "0.7", features = ["rt"] }
# Redis client. Already used by sbproxy-platform; promoted to a workspace
# dep so sbproxy-modules can drive XREAD directly for feature feeds (WAF
# rule subscription) without going through the KVStore facade.
# `streams` enables the XREAD typed reply parser (StreamReadReply).
redis = { version = "0.31.0", default-features = false, features = ["tokio-comp", "streams"] }
# Bloom filter
bloomfilter = "1"
# Crypto
sha2 = "0.11"
hex = "0.4"
hmac = "0.13"
# Constant-time equality for digest, MAC, and token comparison. Promoted to a
# workspace dependency so new crates reach for one audited implementation
# instead of hand-rolling another byte-by-byte comparison loop.
subtle = "2"
# MCP tool-versioning compatibility oracle (mcp/compat). `semver` compares
# declared tool versions for the version-bump linter; `serde_json_canonicalizer`
# produces the RFC 8785 (JCS) canonical form the tool contract digest is
# computed over, so the digest is stable across key order and reproducible.
semver = { version = "1", features = ["serde"] }
serde_json_canonicalizer = "0.3"
# MD5 is intentionally bundled here only for JA3 fingerprint computation
# (RFC-out, weak hash, but the JA3 spec mandates MD5). The `md-5` crate
# is the RustCrypto family member and matches the `sha2` API.
md-5 = "0.10"
# WOR-892 PR1: AES-256-GCM AEAD for sealed OIDC session cookies.
# RustCrypto family, default features keep it pure-Rust.
aes-gcm = "0.10"
# CBOR encoder for the sealed-cookie payload. Smaller wire size than
# JSON and avoids percent-encoding gymnastics inside the Set-Cookie
# header value.
ciborium = "0.2"
# WOR-808 PR9: Argon2id password hashing for the OLP /introspect
# Basic-auth credentials. Operators paste the PHC string output of
# `argon2 -t 3 -m 65536 -p 4 -i` into the config; the proxy verifies
# without re-deriving the parameters.
argon2 = "0.5"
# JWT verification (HS*, RS*, ES* etc.).
jsonwebtoken = { version = "10", default-features = false, features = ["aws_lc_rs", "use_pem"] }
# JSON Schema validation.
jsonschema = { version = "0.17", default-features = false }
# File locking (cross-platform flock(2)/LockFileEx wrapper). Used by
# `sbproxy apply` to take an exclusive lock at the config-path's
# applylock companion so two operators do not race the same reload.
fs2 = "0.4"
# Compile-time directory embedding. WOR-227 (scaffold) gates this
# behind the `embed-admin-ui` feature so the default build does not
# touch `ui/dist/` and the binary stays slim. The macro form is
# `include_dir!("path")`; the served subtree is the contents of
# `ui/dist/` after `pnpm build`. An empty `dist/.gitkeep` keeps the
# macro resolvable in checkouts that have not run a UI build yet.
include_dir = "0.7"
# UUID. v7 is the canonical request-id shape (RFC 9562 monotonic +
# time-ordered); v4 stays available for legacy random-id sites.
uuid = { version = "1", features = ["v4", "v7"] }
# ULID (lexicographically sortable IDs). The capture envelope uses ULIDs
# for request_id / session_id / parent_session_id. The `serde` feature
# lets us (de)serialize directly without a String intermediate.
ulid = { version = "1", features = ["serde"] }
# Random
rand = "0.8"
# Property-based testing. Used by sbproxy-modules to fuzz the rate-limit
# token-bucket arithmetic; pinned at the workspace level so downstream
# crates pick up shrinker / regression-file improvements together.
proptest = "1"
# Time
chrono = "0.4"
# Object storage (S3, GCS, Azure Blob, local filesystem). Used by the
# storage action and the S3-backed enterprise cache. Features pulled
# in cover all four backends so a single dep is enough for both.
object_store = { version = "0.11", features = ["aws", "gcp", "azure"] }
# Embedded databases
redb = "2"
rusqlite = { version = "0.32", features = ["bundled"] }
# TLS / QUIC / HTTP3
quinn = "0.11"
h3 = "0.0.8"
h3-quinn = "0.0.10"
rcgen = "0.13"
# Default rustls features include the `aws-lc-rs` crypto provider, which
# pulls aws-lc-sys and its x86 assembly build. We explicitly use `ring`
# instead (installed via rustls::crypto::ring::default_provider() in
# crates/sbproxy/src/main.rs), so aws-lc is unused overhead and breaks
# cross-compilation under qemu. Opt into only the features we need.
rustls = { version = "0.23", default-features = false, features = ["ring", "std", "logging", "tls12"] }
# Async rustls adapter for tokio streams (mesh peer mTLS). Match rustls's
# ring-only crypto provider; the default would pull aws-lc-rs.
tokio-rustls = { version = "0.26", default-features = false, features = ["ring", "tls12", "logging"] }
# PEM parsing is provided by rustls-pki-types directly via the PemObject
# trait; replaces the unmaintained rustls-pemfile crate (RUSTSEC-2025-0134).
rustls-pki-types = "1"
ring = "0.17"
x509-parser = "0.16"
# ----------------------------------------------------------------------------
# Release profile. LTO disabled for now: proc-macro2's build script fails
# under qemu-emulated cross-compile when LTO is on. Once we have a native
# amd64 builder (or switch to CI with a real x86 host), enable:
# lto = "fat"
# codegen-units = 1
# These would add an additional 5-15% rps per RUST_OPTIMIZATIONS.md A1.
# ----------------------------------------------------------------------------
# WOR-2066: the two capability guards parse every production source file in the
# workspace with `syn` (677 files, ~17.6MB). Those guards run as tests, so they
# build under the dev/test profiles, where an unoptimised `syn` dominated their
# runtime and set the floor for the whole suite. Optimising just these three
# packages leaves every other crate at dev opt-level, so incremental rebuilds
# stay fast, and it costs one extra optimised compile of dependencies that
# rarely change. This also speeds up proc-macro expansion across the workspace.
[profile.dev.package.syn]
opt-level = 3
[profile.dev.package.proc-macro2]
opt-level = 3
[profile.dev.package.unicode-ident]
opt-level = 3
[profile.test.package.syn]
opt-level = 3
[profile.test.package.proc-macro2]
opt-level = 3
[profile.test.package.unicode-ident]
opt-level = 3
[profile.release]
opt-level = 3
strip = "symbols"
# LTO + codegen-units=1: per RUST_OPTIMIZATIONS.md A1, +5-15% rps at
# the tail. Landed 2026-04-24 for the publishable matrix-v7 run.
# Build time jumps ~2x; only a concern on cold cargo caches.
lto = "fat"
codegen-units = 1
[profile.release-fast]
inherits = "release"
# CI smoke images and local Docker iterations care about link-time cost
# more than the last few percent of tail latency. Production release
# artifacts continue to use `[profile.release]`.
lto = "off"
codegen-units = 16
# WOR-1772: fork of Cloudflare Pingora 0.8.0 adding
# `TlsSettings::with_cert_resolver` so the rustls HTTPS listener selects the
# server cert from the dynamic CertResolver (ACME certs served live, renewals
# swap with no restart). All pingora-* crates are patched to the one fork so
# the workspace resolves a single consistent copy. Re-sync on a pingora
# upgrade: rebase soapbucket/pingora sbproxy-0.8.0 onto the new tag.
[patch.crates-io]
pingora-core = { git = "https://github.com/soapbucket/pingora", branch = "sbproxy-0.8.0" }
pingora-proxy = { git = "https://github.com/soapbucket/pingora", branch = "sbproxy-0.8.0" }
pingora-http = { git = "https://github.com/soapbucket/pingora", branch = "sbproxy-0.8.0" }
pingora-error = { git = "https://github.com/soapbucket/pingora", branch = "sbproxy-0.8.0" }
pingora-cache = { git = "https://github.com/soapbucket/pingora", branch = "sbproxy-0.8.0" }
pingora-rustls = { git = "https://github.com/soapbucket/pingora", branch = "sbproxy-0.8.0" }
pingora-lru = { git = "https://github.com/soapbucket/pingora", branch = "sbproxy-0.8.0" }
pingora-pool = { git = "https://github.com/soapbucket/pingora", branch = "sbproxy-0.8.0" }
pingora-runtime = { git = "https://github.com/soapbucket/pingora", branch = "sbproxy-0.8.0" }
pingora-timeout = { git = "https://github.com/soapbucket/pingora", branch = "sbproxy-0.8.0" }
pingora-header-serde = { git = "https://github.com/soapbucket/pingora", branch = "sbproxy-0.8.0" }