From 3f23989396fca921e9fbda9fb34cf79671e71f58 Mon Sep 17 00:00:00 2001
From: "release-pleaze[bot]"
<262023388+release-pleaze[bot]@users.noreply.github.com>
Date: Wed, 9 Sep 2026 17:36:34 -0700
Subject: [PATCH 01/41] chore: release (#725)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## 🤖 New release
* `s2-common`: 0.41.1 -> 0.41.2 (✓ API compatible changes)
* `s2-api`: 0.31.1 -> 0.31.2 (✓ API compatible changes)
* `s2-lite`: 0.42.8 -> 0.42.9 (✓ API compatible changes)
* `s2-sdk`: 0.34.4 -> 0.34.5 (✓ API compatible changes)
* `s2-cli`: 0.42.8 -> 0.42.9
* `s2-testcontainers`: 0.42.8 -> 0.42.9
Changelog
## `s2-common`
## [0.41.2] - 2026-09-10
### Bug Fixes
- Reject NUL bytes in stream names and access token IDs
([#728](https://github.com/s2-streamstore/s2/issues/728))
## `s2-api`
## [0.31.2] - 2026-09-10
### Bug Fixes
- Reject NUL bytes in stream names and access token IDs
([#728](https://github.com/s2-streamstore/s2/issues/728))
- Switch JSON extractor from sonic-rs back to serde_json
([#729](https://github.com/s2-streamstore/s2/issues/729))
## `s2-lite`
## [0.42.9] - 2026-09-10
### Bug Fixes
- Close SlateDB on graceful shutdown
- Reject NUL bytes in stream names and access token IDs
([#728](https://github.com/s2-streamstore/s2/issues/728))
## `s2-sdk`
## [0.34.5] - 2026-09-10
### Bug Fixes
- Reject NUL bytes in stream names and access token IDs
([#728](https://github.com/s2-streamstore/s2/issues/728))
## `s2-cli`
## [0.42.9] - 2026-09-10
## `s2-testcontainers`
## [0.42.9] - 2026-09-10
---
This PR was generated with
[release-plz](https://github.com/release-plz/release-plz/).
Co-authored-by: release-pleaze[bot] <262023388+release-pleaze[bot]@users.noreply.github.com>
---
Cargo.lock | 12 ++++++------
api/CHANGELOG.md | 9 +++++++++
api/Cargo.toml | 2 +-
cli/CHANGELOG.md | 4 ++++
cli/Cargo.toml | 2 +-
common/CHANGELOG.md | 8 ++++++++
common/Cargo.toml | 2 +-
lite/CHANGELOG.md | 9 +++++++++
lite/Cargo.toml | 2 +-
sdk/CHANGELOG.md | 8 ++++++++
sdk/Cargo.toml | 2 +-
testcontainers/CHANGELOG.md | 4 ++++
testcontainers/Cargo.toml | 2 +-
13 files changed, 54 insertions(+), 12 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index 9fa1927f..51f290d9 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4878,7 +4878,7 @@ checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
[[package]]
name = "s2-api"
-version = "0.31.1"
+version = "0.31.2"
dependencies = [
"axum",
"base64ct",
@@ -4908,7 +4908,7 @@ dependencies = [
[[package]]
name = "s2-cli"
-version = "0.42.8"
+version = "0.42.9"
dependencies = [
"assert_cmd",
"async-stream",
@@ -4968,7 +4968,7 @@ dependencies = [
[[package]]
name = "s2-common"
-version = "0.41.1"
+version = "0.41.2"
dependencies = [
"axum",
"base64ct",
@@ -4992,7 +4992,7 @@ dependencies = [
[[package]]
name = "s2-lite"
-version = "0.42.8"
+version = "0.42.9"
dependencies = [
"async-stream",
"async-trait",
@@ -5051,7 +5051,7 @@ dependencies = [
[[package]]
name = "s2-sdk"
-version = "0.34.4"
+version = "0.34.5"
dependencies = [
"assert_matches",
"async-compression",
@@ -5111,7 +5111,7 @@ dependencies = [
[[package]]
name = "s2-testcontainers"
-version = "0.42.8"
+version = "0.42.9"
dependencies = [
"reqwest",
"s2-sdk",
diff --git a/api/CHANGELOG.md b/api/CHANGELOG.md
index 4c989e96..91342822 100644
--- a/api/CHANGELOG.md
+++ b/api/CHANGELOG.md
@@ -2,6 +2,15 @@
All notable changes to this project will be documented in this file.
+## [0.31.2] - 2026-09-10
+
+### Bug Fixes
+
+- Reject NUL bytes in stream names and access token IDs ([#728](https://github.com/s2-streamstore/s2/issues/728))
+- Switch JSON extractor from sonic-rs back to serde_json ([#729](https://github.com/s2-streamstore/s2/issues/729))
+
+
+
## [0.31.1] - 2026-08-13
### Features
diff --git a/api/Cargo.toml b/api/Cargo.toml
index 691ccc7a..f239cbbc 100644
--- a/api/Cargo.toml
+++ b/api/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "s2-api"
-version = "0.31.1"
+version = "0.31.2"
description = "API types for S2, the durable streams API"
edition.workspace = true
license.workspace = true
diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md
index 9dfeac91..4c38a855 100644
--- a/cli/CHANGELOG.md
+++ b/cli/CHANGELOG.md
@@ -2,6 +2,10 @@
All notable changes to this project will be documented in this file.
+## [0.42.9] - 2026-09-10
+
+
+
## [0.42.8] - 2026-09-01
### Bug Fixes
diff --git a/cli/Cargo.toml b/cli/Cargo.toml
index 64dfddeb..18ed5321 100644
--- a/cli/Cargo.toml
+++ b/cli/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "s2-cli"
-version = "0.42.8"
+version = "0.42.9"
description = "CLI for S2"
edition.workspace = true
license.workspace = true
diff --git a/common/CHANGELOG.md b/common/CHANGELOG.md
index 5d7db4c4..0dfc5ad3 100644
--- a/common/CHANGELOG.md
+++ b/common/CHANGELOG.md
@@ -2,6 +2,14 @@
All notable changes to this project will be documented in this file.
+## [0.41.2] - 2026-09-10
+
+### Bug Fixes
+
+- Reject NUL bytes in stream names and access token IDs ([#728](https://github.com/s2-streamstore/s2/issues/728))
+
+
+
## [0.41.1] - 2026-07-22
### Miscellaneous Tasks
diff --git a/common/Cargo.toml b/common/Cargo.toml
index 202963a7..d768f987 100644
--- a/common/Cargo.toml
+++ b/common/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "s2-common"
-version = "0.41.1"
+version = "0.41.2"
description = "Common stuff for client and servers for S2, the durable streams API"
edition.workspace = true
license.workspace = true
diff --git a/lite/CHANGELOG.md b/lite/CHANGELOG.md
index 3d7ff266..1dd3d5de 100644
--- a/lite/CHANGELOG.md
+++ b/lite/CHANGELOG.md
@@ -2,6 +2,15 @@
All notable changes to this project will be documented in this file.
+## [0.42.9] - 2026-09-10
+
+### Bug Fixes
+
+- Close SlateDB on graceful shutdown
+- Reject NUL bytes in stream names and access token IDs ([#728](https://github.com/s2-streamstore/s2/issues/728))
+
+
+
## [0.42.8] - 2026-09-01
### Miscellaneous Tasks
diff --git a/lite/Cargo.toml b/lite/Cargo.toml
index bb5c7709..5f32b3ac 100644
--- a/lite/Cargo.toml
+++ b/lite/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "s2-lite"
-version = "0.42.8"
+version = "0.42.9"
description = "Lightweight server implementation of S2, the durable streams API, backed by object storage"
edition.workspace = true
license.workspace = true
diff --git a/sdk/CHANGELOG.md b/sdk/CHANGELOG.md
index 8d07c476..db646914 100644
--- a/sdk/CHANGELOG.md
+++ b/sdk/CHANGELOG.md
@@ -2,6 +2,14 @@
All notable changes to this project will be documented in this file.
+## [0.34.5] - 2026-09-10
+
+### Bug Fixes
+
+- Reject NUL bytes in stream names and access token IDs ([#728](https://github.com/s2-streamstore/s2/issues/728))
+
+
+
## [0.34.4] - 2026-09-01
### Features
diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml
index ecdd764f..564b6ff0 100644
--- a/sdk/Cargo.toml
+++ b/sdk/Cargo.toml
@@ -1,7 +1,7 @@
[package]
name = "s2-sdk"
description = "Rust SDK for S2"
-version = "0.34.4"
+version = "0.34.5"
edition.workspace = true
license.workspace = true
repository = "https://github.com/s2-streamstore/s2/tree/main/sdk"
diff --git a/testcontainers/CHANGELOG.md b/testcontainers/CHANGELOG.md
index aee84165..a5f8c2b9 100644
--- a/testcontainers/CHANGELOG.md
+++ b/testcontainers/CHANGELOG.md
@@ -2,6 +2,10 @@
All notable changes to this project will be documented in this file.
+## [0.42.9] - 2026-09-10
+
+
+
## [0.42.8] - 2026-09-01
diff --git a/testcontainers/Cargo.toml b/testcontainers/Cargo.toml
index 3dfb58e1..810e646e 100644
--- a/testcontainers/Cargo.toml
+++ b/testcontainers/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "s2-testcontainers"
-version = "0.42.8"
+version = "0.42.9"
description = "Testcontainers helpers for the S2 Docker image"
edition.workspace = true
license.workspace = true
From 16eeede14aa1ad68cd36b23142285986d62cff43 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Thu, 10 Sep 2026 00:36:38 +0000
Subject: [PATCH 02/41] [create-pull-request] automated change
---
api/specs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/api/specs b/api/specs
index 973e0d92..158dede9 160000
--- a/api/specs
+++ b/api/specs
@@ -1 +1 @@
-Subproject commit 973e0d92166ee1386b71d4e7ade8bd0fc2aaf4b8
+Subproject commit 158dede97299fd4f7a86cd842f19157843e57e7d
From 198640ea044f0a861dba173ec9233cbe8584e3de Mon Sep 17 00:00:00 2001
From: "release-pleaze[bot]"
Date: Thu, 10 Sep 2026 01:12:01 +0000
Subject: [PATCH 03/41] Bump s2-lite-helm chart to appVersion 0.42.9
---
charts/s2-lite-helm/Chart.yaml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/charts/s2-lite-helm/Chart.yaml b/charts/s2-lite-helm/Chart.yaml
index 35c7c06e..cccc07a9 100644
--- a/charts/s2-lite-helm/Chart.yaml
+++ b/charts/s2-lite-helm/Chart.yaml
@@ -2,8 +2,8 @@ apiVersion: v2
name: s2-lite-helm
description: Self-hostable S2 streaming datastore using SlateDB on object storage
type: application
-version: 0.1.64
-appVersion: "0.42.8"
+version: 0.1.65
+appVersion: "0.42.9"
keywords:
- s2
- streaming
From 7eb44972ed40d535008a004d697cad43ea347d00 Mon Sep 17 00:00:00 2001
From: Stephen Balogh
Date: Thu, 10 Sep 2026 16:58:19 -0700
Subject: [PATCH 04/41] feat(sdk): set default request headers (`_hidden` only)
(#731)
Allow a set of additional `default_headers` to be specified. This will
be present on all requests, unless replaced by the SDK.
Setting content-encoding headers is not supported, SDK needs full
control of that.
This is motivated by an internal (s2 cloud) usecase, hence the gate
under `_hidden`.
---
sdk/src/api.rs | 259 ++++++++++++++++++++++++++++++++++++++++++++--
sdk/src/client.rs | 42 ++++++--
sdk/src/types.rs | 91 ++++++++++++++++
3 files changed, 376 insertions(+), 16 deletions(-)
diff --git a/sdk/src/api.rs b/sdk/src/api.rs
index e518babc..f412eaec 100644
--- a/sdk/src/api.rs
+++ b/sdk/src/api.rs
@@ -794,7 +794,10 @@ impl BaseClient {
C: client::Connect + Clone + Send + Sync + 'static,
{
let access_token_mode = config.access_token.mode();
- let mut default_headers = HeaderMap::new();
+ let mut default_headers = config.default_headers.clone();
+ // Authorization belongs to the configured token, including when a
+ // refreshable provider supplies it immediately before each attempt.
+ default_headers.remove(AUTHORIZATION);
#[cfg(feature = "_hidden")]
let mut access_token_provider = None;
match &config.access_token {
@@ -1262,10 +1265,9 @@ fn provision_result_from_parts(
#[cfg(test)]
mod tests {
#[cfg(feature = "_hidden")]
- use std::sync::{
- Mutex,
- atomic::{AtomicBool, AtomicUsize, Ordering},
- };
+ use std::sync::Mutex;
+ #[cfg(feature = "_hidden")]
+ use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
#[cfg(feature = "_hidden")]
use async_trait::async_trait;
@@ -1274,6 +1276,241 @@ mod tests {
use super::*;
+ #[cfg(feature = "_hidden")]
+ #[derive(Default)]
+ struct HeaderCapture {
+ unary: Mutex>,
+ streaming: Mutex>,
+ }
+
+ #[cfg(feature = "_hidden")]
+ #[async_trait]
+ impl client::RequestExecutor for HeaderCapture {
+ async fn execute_unary(
+ &self,
+ mut request: client::Request,
+ ) -> Result {
+ let mut headers = self.unary.lock().unwrap();
+ headers.push(request.headers_mut().clone());
+ // Exercise a real retry through RequestBuilder::send.
+ Ok(if headers.len() == 1 {
+ UnaryResponse::new_for_test(
+ StatusCode::SERVICE_UNAVAILABLE,
+ br#"{"code":"unavailable","message":"retry"}"#.to_vec(),
+ )
+ } else {
+ UnaryResponse::new_for_test(
+ StatusCode::OK,
+ br#"{"basins":[],"streams":[],"has_more":false}"#.to_vec(),
+ )
+ })
+ }
+
+ async fn init_streaming(
+ &self,
+ mut request: client::Request,
+ ) -> Result {
+ self.streaming
+ .lock()
+ .unwrap()
+ .push(request.headers_mut().clone());
+ // Capturing initiation is sufficient: do not construct a response body.
+ Err(client::HttpError::Timeout)
+ }
+ }
+
+ #[cfg(feature = "_hidden")]
+ #[tokio::test]
+ async fn default_headers_reach_account_basin_streaming_and_retry_requests() {
+ let mut session = HeaderValue::from_static("session-1");
+ session.set_sensitive(true);
+ let headers = HeaderMap::from_iter([
+ (
+ http::header::HeaderName::from_static("x-origin-session"),
+ session,
+ ),
+ (
+ AUTHORIZATION,
+ HeaderValue::from_static("Bearer wrong-token"),
+ ),
+ (
+ http::header::USER_AGENT,
+ HeaderValue::from_static("wrong-agent"),
+ ),
+ (
+ http::header::ACCEPT_ENCODING,
+ HeaderValue::from_static("wrong-encoding"),
+ ),
+ (
+ http::header::HeaderName::from_static(S2_BASIN),
+ HeaderValue::from_static("wrong-basin"),
+ ),
+ (CONTENT_TYPE, HeaderValue::from_static("wrong-content-type")),
+ ]);
+ let config = S2Config::new("actual-token")
+ .with_endpoints(S2Endpoints::for_endpoint("http://example.test").unwrap())
+ .with_compression(Compression::Gzip)
+ .with_default_headers(headers)
+ .unwrap();
+ let executor = Arc::new(HeaderCapture::default());
+ let mut base = BaseClient::init_with_connector(&config, HttpConnector::new()).unwrap();
+ base.client = executor.clone();
+ base.retry_builder = RetryBackoffBuilder::default()
+ .with_min_base_delay(Duration::ZERO)
+ .with_max_base_delay(Duration::ZERO)
+ .with_max_retries(1);
+ let account = AccountClient::init(config.clone(), base);
+ account
+ .list_basins(ListBasinsRequest {
+ prefix: None,
+ start_after: None,
+ limit: None,
+ })
+ .await
+ .unwrap();
+ let basin = account.basin_client("test-basin".parse().unwrap());
+ basin
+ .list_streams(ListStreamsRequest {
+ prefix: None,
+ start_after: None,
+ limit: None,
+ })
+ .await
+ .unwrap();
+
+ let stream = "test-stream".parse().unwrap();
+ assert!(
+ basin
+ .read_session(
+ &stream,
+ ReadStart {
+ seq_num: None,
+ timestamp: None,
+ tail_offset: None,
+ clamp: None
+ },
+ ReadEnd {
+ count: None,
+ bytes: None,
+ until: None,
+ wait: None
+ },
+ None,
+ ReconnectAdvice::default(),
+ )
+ .await
+ .is_err()
+ );
+ assert!(
+ basin
+ .append_session(
+ &stream,
+ futures_util::stream::empty(),
+ None,
+ None,
+ ReconnectAdvice::default(),
+ )
+ .await
+ .is_err()
+ );
+
+ let unary = executor.unary.lock().unwrap();
+ let streaming = executor.streaming.lock().unwrap();
+ assert_eq!(unary.len(), 3); // account, account retry, basin
+ assert_eq!(streaming.len(), 2); // read and append
+ for headers in unary.iter().chain(streaming.iter()) {
+ assert_eq!(headers["x-origin-session"], "session-1");
+ assert!(headers["x-origin-session"].is_sensitive());
+ assert_eq!(headers[AUTHORIZATION], "Bearer actual-token");
+ assert!(headers[AUTHORIZATION].is_sensitive());
+ assert_eq!(headers[http::header::USER_AGENT], config.user_agent);
+ assert_eq!(headers[http::header::ACCEPT_ENCODING], "gzip");
+ assert!(!headers.contains_key(http::header::CONTENT_ENCODING));
+ }
+ assert_eq!(unary[2][S2_BASIN], "test-basin");
+ for headers in streaming.iter() {
+ assert_eq!(headers[S2_BASIN], "test-basin");
+ assert_eq!(headers[CONTENT_TYPE], CONTENT_TYPE_S2S);
+ }
+ }
+
+ #[cfg(feature = "_hidden")]
+ #[rstest::rstest]
+ #[case::none(Compression::None, None, "gzip, zstd")]
+ #[case::gzip(Compression::Gzip, Some("gzip"), "gzip")]
+ #[case::zstd(Compression::Zstd, Some("zstd"), "zstd")]
+ #[tokio::test]
+ async fn default_accept_encoding_respects_configured_compression(
+ #[case] compression: Compression,
+ #[case] content_encoding: Option<&str>,
+ #[case] accept_encoding: &str,
+ ) {
+ let config = S2Config::new("token")
+ .with_compression(compression)
+ .with_default_headers(HeaderMap::from_iter([(
+ http::header::ACCEPT_ENCODING,
+ HeaderValue::from_static("gzip, zstd"),
+ )]))
+ .unwrap();
+ let client = BaseClient::init_with_connector(&config, HttpConnector::new()).unwrap();
+ let mut request = client
+ .post("http://example.test/v1/basins".parse().unwrap())
+ .json(&serde_json::json!({"name": "test-basin"}))
+ .build()
+ .unwrap()
+ .compress()
+ .await
+ .unwrap();
+ let headers = request.headers_mut();
+ assert_eq!(
+ headers
+ .get(http::header::CONTENT_ENCODING)
+ .map(|value| value.to_str().unwrap()),
+ content_encoding
+ );
+ assert_eq!(headers[http::header::ACCEPT_ENCODING], accept_encoding);
+ }
+
+ #[cfg(feature = "_hidden")]
+ #[tokio::test]
+ async fn default_headers_are_replaced_and_isolated_between_clients() {
+ let first_headers = HeaderMap::from_iter([
+ (
+ http::header::HeaderName::from_static("x-origin-session"),
+ HeaderValue::from_static("first"),
+ ),
+ (
+ http::header::HeaderName::from_static("x-first-only"),
+ HeaderValue::from_static("present"),
+ ),
+ ]);
+ let first = S2Config::new("token")
+ .with_default_headers(first_headers)
+ .unwrap();
+ let second = first
+ .clone()
+ .with_default_headers(HeaderMap::from_iter([(
+ http::header::HeaderName::from_static("x-origin-session"),
+ HeaderValue::from_static("second"),
+ )]))
+ .unwrap();
+ for (config, session) in [(&first, "first"), (&second, "second")] {
+ let client = BaseClient::init_with_connector(config, HttpConnector::new()).unwrap();
+ let mut request = client
+ .get("http://example.test".parse().unwrap())
+ .build()
+ .unwrap();
+ assert_eq!(request.headers_mut()["x-origin-session"], session);
+ assert_eq!(
+ request.headers_mut().contains_key("x-first-only"),
+ session == "first"
+ );
+ }
+ let cleared = second.with_default_headers(HeaderMap::new()).unwrap();
+ assert!(cleared.default_headers.is_empty());
+ assert!(S2Config::new("token").default_headers.is_empty());
+ }
+
#[cfg(feature = "_hidden")]
#[derive(Debug)]
struct RotatingTokenProvider {
@@ -1435,9 +1672,15 @@ mod tests {
#[cfg(feature = "_hidden")]
#[tokio::test]
async fn dynamic_access_token_is_loaded_for_each_attempt_and_marked_sensitive() {
- let config = S2Config::new("unused").with_access_token_provider(RotatingTokenProvider {
- generation: AtomicUsize::new(1),
- });
+ let config = S2Config::new("unused")
+ .with_default_headers(HeaderMap::from_iter([(
+ AUTHORIZATION,
+ HeaderValue::from_static("Bearer wrong-token"),
+ )]))
+ .unwrap()
+ .with_access_token_provider(RotatingTokenProvider {
+ generation: AtomicUsize::new(1),
+ });
let client = BaseClient::init_with_connector(&config, HttpConnector::new()).unwrap();
let uri = "http://example.test/v1/basins".parse().unwrap();
let mut request = client.get(uri).build().unwrap();
diff --git a/sdk/src/client.rs b/sdk/src/client.rs
index b38d3f31..abf927ab 100644
--- a/sdk/src/client.rs
+++ b/sdk/src/client.rs
@@ -304,9 +304,8 @@ impl RequestBuilder {
}
pub fn headers(mut self, headers: &HeaderMap) -> Self {
- for (key, value) in headers {
- self.headers.insert(key.clone(), value.clone());
- }
+ // An owned HeaderMap replaces each key's existing values while preserving duplicates.
+ self.headers.extend(headers.clone());
self
}
@@ -574,11 +573,7 @@ fn build_http_request(
let mut builder = http::Request::builder().method(method).uri(uri.clone());
if let Some(req_headers) = builder.headers_mut() {
- for (key, value) in headers {
- if let Some(key) = key {
- req_headers.insert(key, value);
- }
- }
+ *req_headers = headers;
if let Some(encoding) = content_encoding {
req_headers.insert(CONTENT_ENCODING, encoding);
}
@@ -1016,6 +1011,37 @@ mod tests {
Pool::new(HttpConnector::new())
}
+ #[test]
+ fn default_headers_preserve_multiple_values_and_sensitive_flags_on_wire_request() {
+ let mut headers = HeaderMap::new();
+ headers.append("x-tag", HeaderValue::from_static("first"));
+ let mut second = HeaderValue::from_static("second");
+ second.set_sensitive(true);
+ headers.append("x-tag", second);
+ headers.insert(CONTENT_ENCODING, HeaderValue::from_static("wrong-encoding"));
+
+ let request = RequestBuilder::get("http://example.test".parse().unwrap())
+ .header("x-tag", "replaced")
+ .headers(&headers)
+ .build()
+ .unwrap();
+ let cloned = request.try_clone().unwrap();
+ let http = build_http_request(
+ cloned.method,
+ &cloned.uri,
+ cloned.headers,
+ cloned.body.into_http_body(),
+ Some(HeaderValue::from_static("gzip")),
+ )
+ .unwrap();
+ let values = http.headers().get_all("x-tag").iter().collect::>();
+ assert_eq!(values.len(), 2);
+ assert_eq!(values[0], "first");
+ assert_eq!(values[1], "second");
+ assert!(values[1].is_sensitive());
+ assert_eq!(http.headers()[CONTENT_ENCODING], "gzip");
+ }
+
#[test]
fn uri_with_path_percent_encoded_segment() {
let base: Uri = "https://example.com".parse().unwrap();
diff --git a/sdk/src/types.rs b/sdk/src/types.rs
index 6d324782..9f2d6631 100644
--- a/sdk/src/types.rs
+++ b/sdk/src/types.rs
@@ -16,6 +16,7 @@ use std::{
use async_trait::async_trait;
use bytes::Bytes;
use http::{
+ HeaderMap,
header::HeaderValue,
uri::{Authority, Scheme},
};
@@ -511,6 +512,7 @@ pub struct S2Config {
pub(crate) retry: RetryConfig,
pub(crate) compression: Compression,
pub(crate) user_agent: HeaderValue,
+ pub(crate) default_headers: HeaderMap,
pub(crate) insecure_skip_cert_verification: bool,
pub(crate) rustls_crypto_provider: Option>,
}
@@ -528,6 +530,7 @@ impl S2Config {
user_agent: concat!("s2-sdk-rust/", env!("CARGO_PKG_VERSION"))
.parse()
.expect("valid user agent"),
+ default_headers: HeaderMap::new(),
insecure_skip_cert_verification: false,
rustls_crypto_provider: default_rustls_crypto_provider(),
}
@@ -547,6 +550,52 @@ impl S2Config {
Self { endpoints, ..self }
}
+ /// Set additional HTTP headers to send with every request.
+ ///
+ /// These headers apply to account, basin, and stream operations, including
+ /// retries and streaming requests. SDK-generated headers, such as
+ /// authorization and basin routing, take precedence over these defaults.
+ /// Calling this method again replaces the previous set of default headers.
+ ///
+ /// `Accept-Encoding` defaults are used only when [`Compression::None`] is
+ /// configured; otherwise the SDK sets the header to the configured
+ /// compression algorithm.
+ ///
+ /// Headers are sent to all configured S2 endpoints. Use
+ /// [`HeaderValue::set_sensitive`] for values that should be redacted in debug
+ /// output. Do not use these defaults for per-request identifiers, since the
+ /// same values are reused across requests.
+ ///
+ /// # Errors
+ ///
+ /// Returns an error if `default_headers` contains `Content-Encoding`,
+ /// `Content-Length`, or `Transfer-Encoding`. The SDK controls body framing.
+ /// Use [`Self::with_compression`] to configure request body encoding.
+ #[cfg(feature = "_hidden")]
+ #[doc(hidden)]
+ pub fn with_default_headers(self, default_headers: HeaderMap) -> Result {
+ if default_headers.contains_key(http::header::CONTENT_ENCODING) {
+ return Err(ValidationError(
+ "Content-Encoding cannot be set in default headers; use S2Config::with_compression instead"
+ .into(),
+ ));
+ }
+ for name in [
+ http::header::CONTENT_LENGTH,
+ http::header::TRANSFER_ENCODING,
+ ] {
+ if default_headers.contains_key(&name) {
+ return Err(ValidationError(format!(
+ "{name} cannot be set in default headers; the SDK controls request body framing"
+ )));
+ }
+ }
+ Ok(Self {
+ default_headers,
+ ..self
+ })
+ }
+
/// Set the timeout for establishing a connection to the server.
///
/// Defaults to `3s`.
@@ -3989,6 +4038,48 @@ mod tests {
assert!(!cfg.insecure_skip_cert_verification);
}
+ #[cfg(feature = "_hidden")]
+ #[rstest]
+ #[case::matching_compression("content-encoding", "gzip", Compression::Gzip)]
+ #[case::mixed_case("Content-Encoding", "identity", Compression::None)]
+ #[case::empty_value("content-encoding", "", Compression::None)]
+ fn default_headers_reject_content_encoding(
+ #[case] name: &str,
+ #[case] value: &str,
+ #[case] compression: Compression,
+ ) {
+ let headers = HeaderMap::from_iter([(
+ name.parse::().unwrap(),
+ HeaderValue::from_str(value).unwrap(),
+ )]);
+ let error = S2Config::new("token")
+ .with_compression(compression)
+ .with_default_headers(headers)
+ .unwrap_err();
+ assert!(error.0.contains("Content-Encoding"));
+ assert!(error.0.contains("with_compression"));
+ }
+
+ #[cfg(feature = "_hidden")]
+ #[rstest]
+ #[case::content_length("content-length", "123")]
+ #[case::content_length_mixed_case("Content-Length", "0")]
+ #[case::content_length_empty("content-length", "")]
+ #[case::transfer_encoding("transfer-encoding", "chunked")]
+ #[case::transfer_encoding_mixed_case("Transfer-Encoding", "chunked")]
+ #[case::transfer_encoding_empty("transfer-encoding", "")]
+ fn default_headers_reject_framing_headers(#[case] name: &str, #[case] value: &str) {
+ let headers = HeaderMap::from_iter([(
+ name.parse::().unwrap(),
+ HeaderValue::from_str(value).unwrap(),
+ )]);
+ let error = S2Config::new("token")
+ .with_default_headers(headers)
+ .unwrap_err();
+ assert!(error.0.contains(&name.to_ascii_lowercase()));
+ assert!(error.0.contains("framing"));
+ }
+
// -- StorageClass --
#[rstest]
From 24b3e7862022e1a1678c9b93ba4e6f61fe9f48ca Mon Sep 17 00:00:00 2001
From: "release-pleaze[bot]"
<262023388+release-pleaze[bot]@users.noreply.github.com>
Date: Thu, 10 Sep 2026 17:17:29 -0700
Subject: [PATCH 05/41] chore: release (#732)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## 🤖 New release
* `s2-lite`: 0.42.9 -> 0.42.10 (✓ API compatible changes)
* `s2-sdk`: 0.34.5 -> 0.34.6 (✓ API compatible changes)
* `s2-cli`: 0.42.9 -> 0.42.10
* `s2-testcontainers`: 0.42.9 -> 0.42.10
Changelog
## `s2-lite`
## [0.42.10] - 2026-09-11
### Miscellaneous Tasks
- Update Cargo.lock dependencies
## `s2-sdk`
## [0.34.6] - 2026-09-11
### Features
- Set default request headers (`_hidden` only)
([#731](https://github.com/s2-streamstore/s2/issues/731))
## `s2-cli`
## [0.42.10] - 2026-09-11
## `s2-testcontainers`
## [0.42.10] - 2026-09-11
---
This PR was generated with
[release-plz](https://github.com/release-plz/release-plz/).
Co-authored-by: release-pleaze[bot] <262023388+release-pleaze[bot]@users.noreply.github.com>
---
Cargo.lock | 8 ++++----
cli/CHANGELOG.md | 4 ++++
cli/Cargo.toml | 2 +-
lite/CHANGELOG.md | 8 ++++++++
lite/Cargo.toml | 2 +-
sdk/CHANGELOG.md | 8 ++++++++
sdk/Cargo.toml | 2 +-
testcontainers/CHANGELOG.md | 4 ++++
testcontainers/Cargo.toml | 2 +-
9 files changed, 32 insertions(+), 8 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index 51f290d9..6674ec5d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4908,7 +4908,7 @@ dependencies = [
[[package]]
name = "s2-cli"
-version = "0.42.9"
+version = "0.42.10"
dependencies = [
"assert_cmd",
"async-stream",
@@ -4992,7 +4992,7 @@ dependencies = [
[[package]]
name = "s2-lite"
-version = "0.42.9"
+version = "0.42.10"
dependencies = [
"async-stream",
"async-trait",
@@ -5051,7 +5051,7 @@ dependencies = [
[[package]]
name = "s2-sdk"
-version = "0.34.5"
+version = "0.34.6"
dependencies = [
"assert_matches",
"async-compression",
@@ -5111,7 +5111,7 @@ dependencies = [
[[package]]
name = "s2-testcontainers"
-version = "0.42.9"
+version = "0.42.10"
dependencies = [
"reqwest",
"s2-sdk",
diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md
index 4c38a855..e3577529 100644
--- a/cli/CHANGELOG.md
+++ b/cli/CHANGELOG.md
@@ -2,6 +2,10 @@
All notable changes to this project will be documented in this file.
+## [0.42.10] - 2026-09-11
+
+
+
## [0.42.9] - 2026-09-10
diff --git a/cli/Cargo.toml b/cli/Cargo.toml
index 18ed5321..e5922e1a 100644
--- a/cli/Cargo.toml
+++ b/cli/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "s2-cli"
-version = "0.42.9"
+version = "0.42.10"
description = "CLI for S2"
edition.workspace = true
license.workspace = true
diff --git a/lite/CHANGELOG.md b/lite/CHANGELOG.md
index 1dd3d5de..214caca7 100644
--- a/lite/CHANGELOG.md
+++ b/lite/CHANGELOG.md
@@ -2,6 +2,14 @@
All notable changes to this project will be documented in this file.
+## [0.42.10] - 2026-09-11
+
+### Miscellaneous Tasks
+
+- Update Cargo.lock dependencies
+
+
+
## [0.42.9] - 2026-09-10
### Bug Fixes
diff --git a/lite/Cargo.toml b/lite/Cargo.toml
index 5f32b3ac..72013081 100644
--- a/lite/Cargo.toml
+++ b/lite/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "s2-lite"
-version = "0.42.9"
+version = "0.42.10"
description = "Lightweight server implementation of S2, the durable streams API, backed by object storage"
edition.workspace = true
license.workspace = true
diff --git a/sdk/CHANGELOG.md b/sdk/CHANGELOG.md
index db646914..d2d6b1ec 100644
--- a/sdk/CHANGELOG.md
+++ b/sdk/CHANGELOG.md
@@ -2,6 +2,14 @@
All notable changes to this project will be documented in this file.
+## [0.34.6] - 2026-09-11
+
+### Features
+
+- Set default request headers (`_hidden` only) ([#731](https://github.com/s2-streamstore/s2/issues/731))
+
+
+
## [0.34.5] - 2026-09-10
### Bug Fixes
diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml
index 564b6ff0..aa3665ae 100644
--- a/sdk/Cargo.toml
+++ b/sdk/Cargo.toml
@@ -1,7 +1,7 @@
[package]
name = "s2-sdk"
description = "Rust SDK for S2"
-version = "0.34.5"
+version = "0.34.6"
edition.workspace = true
license.workspace = true
repository = "https://github.com/s2-streamstore/s2/tree/main/sdk"
diff --git a/testcontainers/CHANGELOG.md b/testcontainers/CHANGELOG.md
index a5f8c2b9..0fa9bdf8 100644
--- a/testcontainers/CHANGELOG.md
+++ b/testcontainers/CHANGELOG.md
@@ -2,6 +2,10 @@
All notable changes to this project will be documented in this file.
+## [0.42.10] - 2026-09-11
+
+
+
## [0.42.9] - 2026-09-10
diff --git a/testcontainers/Cargo.toml b/testcontainers/Cargo.toml
index 810e646e..e955d07f 100644
--- a/testcontainers/Cargo.toml
+++ b/testcontainers/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "s2-testcontainers"
-version = "0.42.9"
+version = "0.42.10"
description = "Testcontainers helpers for the S2 Docker image"
edition.workspace = true
license.workspace = true
From 4aebddaba02fb6541647a7f76d49ff094afbc98f Mon Sep 17 00:00:00 2001
From: "release-pleaze[bot]"
Date: Fri, 11 Sep 2026 00:56:03 +0000
Subject: [PATCH 06/41] Bump s2-lite-helm chart to appVersion 0.42.10
---
charts/s2-lite-helm/Chart.yaml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/charts/s2-lite-helm/Chart.yaml b/charts/s2-lite-helm/Chart.yaml
index cccc07a9..25315647 100644
--- a/charts/s2-lite-helm/Chart.yaml
+++ b/charts/s2-lite-helm/Chart.yaml
@@ -2,8 +2,8 @@ apiVersion: v2
name: s2-lite-helm
description: Self-hostable S2 streaming datastore using SlateDB on object storage
type: application
-version: 0.1.65
-appVersion: "0.42.9"
+version: 0.1.66
+appVersion: "0.42.10"
keywords:
- s2
- streaming
From a4f247aeed78fda270bfaa07ef4bc77d5f76d6d4 Mon Sep 17 00:00:00 2001
From: Mehul Arora
Date: Fri, 11 Sep 2026 21:01:21 +0530
Subject: [PATCH 07/41] feat: `s2-stream-config` header for auto-created
streams (#718)
## Summary
When a basin has `create_stream_on_append` enabled, an append can carry
an `s2-stream-config` header whose value is a compact JSON
`StreamConfig`. If that request is the one that creates the stream, the
config is layered over the basin's `default_stream_config`; unset fields
inherit the defaults. It is ignored once the stream exists, so clients
can attach it to every append without tracking whether the stream has
been created.
This gives per-stream config (e.g. retention, delete-on-empty) on
auto-created streams without a control-plane round trip.
Spec half: s2-streamstore/s2-specs#21 (this PR bumps the `api/specs`
submodule to that branch's commit; re-bump to the merge commit once it
lands).
## API
One header, same for JSON, proto and S2S (an append session is a single
request, so the header covers the whole session):
```sh
curl -X POST "https://$BASIN.b.s2.dev/v1/streams/tenant-42%2Fevents/records" \
-H "Authorization: Bearer $S2_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H 's2-stream-config: {"retention_policy":{"age":3600},"delete_on_empty":{"min_age_secs":300}}' \
-d '{"records": [{"body": "hello"}]}'
```
The value is validated exactly like a `CreateStream` config; an invalid
value is rejected with `400 bad_header` before any lookup and no stream
is created:
```json
{"code":"bad_header","message":"Invalid header `s2-stream-config`: age must be greater than 0 seconds"}
```
SDK: the option lives on the stream handle, like the encryption key, and
applies to unary appends, append sessions and producers:
```rust
let stream = basin
.stream(name)
.with_stream_config(
StreamConfig::new()
.with_retention_policy(RetentionPolicy::Age(3600))
.with_delete_on_empty(DeleteOnEmptyConfig::new().with_min_age(Duration::from_secs(300))),
);
stream.append(input).await?; // or
stream.producer(ProducerConfig::default()); // or stream.append_session(..)
```
CLI:
```sh
echo hello | s2 append s2://my-basin/tenant-42/events --format text \
--retention-policy 1h --delete-on-empty-min-age 5m
```
## Why a header
- No proto change: the same header carries the config for unary appends
and S2S sessions, so `AppendInput` (which flows into storage) stays
untouched.
- Known before the server responds. With a body/frame field, S2S
sessions needed the server to wait for the first frame before creating
the stream, while clients wait for response headers before sending it;
the header removes that ordering problem entirely.
- Reusable for read paths (`create_stream_on_read`) later, since `GET`
has no body.
## Changes
- **api**
- `v1::config::STREAM_CONFIG_HEADER` (`s2-stream-config`) and
`StreamConfigHeader`, a `ParseableHeader` that deserializes the JSON
`StreamConfig` and reuses `TryFrom for
OptionalStreamConfig` so validation lives in one place.
`to_header_value` for clients.
- `data::S2StreamConfigHeader` documents the header in OpenAPI (string
schema, with an example value; utoipa cannot express `content` on a
parameter).
- `AppendRequest::Unary` / `S2s` gain `stream_config:
OptionalStreamConfig`, parsed once in the extractor.
- **lite**
- `stream_handle_with_auto_create` takes an `AutoCreateOn` (`Append` /
`Read`) and the `OptionalStreamConfig` to layer over the basin defaults
when creating.
- `Backend::open_for_append(.., stream_config)` serves both unary
appends and sessions; the stream is created (or the request fails)
before the response, as before this feature.
- **sdk**: `S2Stream::with_stream_config`, mirroring
`with_encryption_key`. Internally, `AppendHeaders { encryption,
stream_config }` is threaded through sessions/producers and set on every
(re)connect.
- **cli**: `s2 append` accepts the same stream config flags as
`create-stream` (`--retention-policy`, `--storage-class`,
`--timestamping-*`, `--delete-on-empty-min-age`), listed under their own
help heading; set on the stream handle.
## Compatibility
- Old clients never send the header; old servers ignore unknown headers.
- `s2-api` public API change: `AppendRequest` variants gain a field.
## Testing
- `s2-api` unit: header parse/validate (valid, `{}`, invalid JSON, `age:
0`) and `to_header_value` roundtrip.
- Backend-level: applies + merges with basin defaults; existing stream
ignores config.
- HTTP-level: JSON unary with header; invalid header -> `400 bad_header`
with no stream created (both invalid config and non-JSON); S2S session
with header.
- SDK integration against `s2 lite`: unary + producer create with
config, existing stream unchanged.
- CLI integration against `s2 lite`: 47/47 pass.
- `clippy -D warnings` clean; workspace unit suites pass.
Made with [Cursor](https://cursor.com)
---------
Co-authored-by: Cursor
---
Cargo.lock | 4 +-
Cargo.toml | 2 +-
api/specs | 2 +-
api/src/data.rs | 19 ++
api/src/v1/config.rs | 98 +++++++-
api/src/v1/stream/extract.rs | 13 +-
api/src/v1/stream/mod.rs | 15 ++
cli/src/cli.rs | 18 ++
cli/src/main.rs | 10 +-
cli/src/ops.rs | 32 ++-
cli/tests/integration.rs | 89 +++++++
lite/src/backend/append.rs | 11 +-
lite/src/backend/core.rs | 99 ++++----
lite/src/backend/read.rs | 37 +--
lite/src/handlers/v1/records.rs | 230 ++++++++++++++++++-
lite/tests/backend/common/mod.rs | 3 +
lite/tests/backend/common/read.rs | 8 +-
lite/tests/backend/common/setup.rs | 9 +-
lite/tests/backend/data_plane/auto_create.rs | 139 ++++++++++-
sdk/src/api.rs | 23 +-
sdk/src/batching.rs | 1 +
sdk/src/ops.rs | 28 ++-
sdk/src/producer.rs | 29 ++-
sdk/src/session/append.rs | 49 ++--
sdk/src/session/mod.rs | 9 +
sdk/src/session/read.rs | 21 +-
sdk/src/types.rs | 32 +++
sdk/tests/stream_ops.rs | 117 +++++++++-
28 files changed, 1023 insertions(+), 124 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index 6674ec5d..7ffc34a3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -6615,7 +6615,7 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "utoipa"
version = "5.4.0"
-source = "git+https://github.com/infiniteregrets/utoipa?rev=9cd181d40ac0a50551ebe1995a4d73e8685890d6#9cd181d40ac0a50551ebe1995a4d73e8685890d6"
+source = "git+https://github.com/infiniteregrets/utoipa?rev=82bcb28a792ba9a0d29963827ec473823099fc94#82bcb28a792ba9a0d29963827ec473823099fc94"
dependencies = [
"indexmap 2.14.0",
"serde",
@@ -6626,7 +6626,7 @@ dependencies = [
[[package]]
name = "utoipa-gen"
version = "5.4.0"
-source = "git+https://github.com/infiniteregrets/utoipa?rev=9cd181d40ac0a50551ebe1995a4d73e8685890d6#9cd181d40ac0a50551ebe1995a4d73e8685890d6"
+source = "git+https://github.com/infiniteregrets/utoipa?rev=82bcb28a792ba9a0d29963827ec473823099fc94#82bcb28a792ba9a0d29963827ec473823099fc94"
dependencies = [
"proc-macro2",
"quote",
diff --git a/Cargo.toml b/Cargo.toml
index a092a9b7..d6801da5 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -97,7 +97,7 @@ xxhash-rust = "0.8"
zstd = "0.13"
[patch.crates-io]
-utoipa = { git = "https://github.com/infiniteregrets/utoipa", rev = "9cd181d40ac0a50551ebe1995a4d73e8685890d6" }
+utoipa = { git = "https://github.com/infiniteregrets/utoipa", rev = "82bcb28a792ba9a0d29963827ec473823099fc94" }
[profile.dev]
panic = "abort"
diff --git a/api/specs b/api/specs
index 973e0d92..f29cbcaa 160000
--- a/api/specs
+++ b/api/specs
@@ -1 +1 @@
-Subproject commit 973e0d92166ee1386b71d4e7ade8bd0fc2aaf4b8
+Subproject commit f29cbcaafeaa3d4970918a9a31f9e00b55fdabb4
diff --git a/api/src/data.rs b/api/src/data.rs
index f9fc4fc7..0b7c41d2 100644
--- a/api/src/data.rs
+++ b/api/src/data.rs
@@ -96,6 +96,25 @@ pub struct S2FormatHeader {
pub s2_format: Format,
}
+#[rustfmt::skip]
+#[derive(Debug)]
+#[cfg_attr(feature = "utoipa", derive(utoipa::IntoParams))]
+#[cfg_attr(feature = "utoipa", into_params(parameter_in = Header))]
+pub struct S2StreamConfigHeader {
+ /// JSON-encoded `StreamConfig` to apply if the stream is created on append or read.
+ /// Unset fields inherit the basin's default stream configuration.
+ /// Ignored if the stream already exists.
+ /// Compact JSON is preferred.
+ #[cfg_attr(feature = "utoipa", param(
+ required = false,
+ rename = "s2-stream-config",
+ content_type = "application/json",
+ value_type = crate::v1::config::StreamConfig,
+ example = json!({"retention_policy":{"age":3600},"delete_on_empty":{"min_age_secs":300}}),
+ ))]
+ pub s2_stream_config: String,
+}
+
#[rustfmt::skip]
#[derive(Debug)]
#[cfg_attr(feature = "utoipa", derive(utoipa::IntoParams))]
diff --git a/api/src/v1/config.rs b/api/src/v1/config.rs
index fc523439..d48c9a87 100644
--- a/api/src/v1/config.rs
+++ b/api/src/v1/config.rs
@@ -1,6 +1,7 @@
-use std::time::Duration;
+use std::{str::FromStr, time::Duration};
-use s2_common::maybe::Maybe;
+use http::{HeaderName, HeaderValue};
+use s2_common::{http::ParseableHeader, maybe::Maybe};
use serde::{Deserialize, Serialize};
#[rustfmt::skip]
@@ -331,6 +332,12 @@ impl StreamConfig {
Some(config)
}
}
+
+ /// Encode as compact JSON for the `s2-stream-config` header.
+ pub fn to_header_value(&self) -> HeaderValue {
+ let json = serde_json::to_string(self).expect("StreamConfig serializes to JSON");
+ HeaderValue::from_str(&json).expect("compact JSON of StreamConfig is a valid header value")
+ }
}
impl From for StreamConfig {
@@ -351,6 +358,30 @@ impl From for StreamConfig {
}
}
+pub static STREAM_CONFIG_HEADER: HeaderName = HeaderName::from_static("s2-stream-config");
+
+/// Value of the `s2-stream-config` header: a JSON-encoded [`StreamConfig`] to apply over the
+/// basin's default stream config if the stream is created on append or read. Ignored if the
+/// stream already exists.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct StreamConfigHeader(pub s2_common::config::OptionalStreamConfig);
+
+impl FromStr for StreamConfigHeader {
+ type Err = s2_common::ValidationError;
+
+ fn from_str(s: &str) -> Result {
+ let config: StreamConfig =
+ serde_json::from_str(s).map_err(|e| format!("invalid JSON: {e}"))?;
+ Ok(Self(config.try_into()?))
+ }
+}
+
+impl ParseableHeader for StreamConfigHeader {
+ fn name() -> &'static HeaderName {
+ &STREAM_CONFIG_HEADER
+ }
+}
+
impl TryFrom for s2_common::config::OptionalStreamConfig {
type Error = s2_common::ValidationError;
@@ -1057,4 +1088,67 @@ mod tests {
"delete_on_empty.min_age should be None"
);
}
+
+ #[test]
+ fn stream_config_header_parses_and_validates() {
+ let header: StreamConfigHeader =
+ r#"{"retention_policy":{"age":3600},"delete_on_empty":{"min_age_secs":300}}"#
+ .parse()
+ .unwrap();
+ assert_eq!(
+ header.0,
+ s2_common::config::OptionalStreamConfig {
+ retention_policy: Some(s2_common::config::RetentionPolicy::Age(
+ Duration::from_secs(3600)
+ )),
+ delete_on_empty: s2_common::config::OptionalDeleteOnEmptyConfig {
+ min_age: Some(Duration::from_secs(300)),
+ },
+ ..Default::default()
+ }
+ );
+
+ for spaced in [
+ r#"{ "retention_policy": { "age": 3600 }, "delete_on_empty": { "min_age_secs": 300 } }"#,
+ "{\t\"delete_on_empty\":\t{\"min_age_secs\":\t300},\t\"retention_policy\":\t{\"age\":\t3600}\t}",
+ " {\"retention_policy\":{\"age\":3600},\"delete_on_empty\":{\"min_age_secs\":300}} ",
+ ] {
+ let parsed: StreamConfigHeader = spaced.parse().unwrap();
+ assert_eq!(parsed, header, "{spaced:?}");
+ }
+
+ let empty: StreamConfigHeader = "{}".parse().unwrap();
+ assert_eq!(empty.0, Default::default());
+
+ let invalid_json = "not json".parse::().unwrap_err();
+ assert!(invalid_json.to_string().contains("invalid JSON"));
+
+ let invalid_age =
+ r#"{"retention_policy":{"age":0}}"#.parse::().unwrap_err();
+ assert!(
+ invalid_age
+ .to_string()
+ .contains("age must be greater than 0 seconds"),
+ "{invalid_age}"
+ );
+ }
+
+ #[test]
+ fn stream_config_header_value_roundtrips() {
+ let config = StreamConfig {
+ storage_class: Some(StorageClass::Express),
+ retention_policy: Some(RetentionPolicy::Infinite(InfiniteRetention {})),
+ timestamping: Some(TimestampingConfig {
+ mode: Some(TimestampingMode::ClientRequire),
+ uncapped: Some(true),
+ }),
+ delete_on_empty: Some(DeleteOnEmptyConfig { min_age_secs: 60 }),
+ };
+ let value = config.to_header_value();
+ let parsed: StreamConfigHeader = value.to_str().unwrap().parse().unwrap();
+ assert_eq!(
+ parsed.0,
+ s2_common::config::OptionalStreamConfig::try_from(config).unwrap()
+ );
+ }
}
diff --git a/api/src/v1/stream/extract.rs b/api/src/v1/stream/extract.rs
index 6d508d45..deae94fb 100644
--- a/api/src/v1/stream/extract.rs
+++ b/api/src/v1/stream/extract.rs
@@ -17,7 +17,7 @@ use crate::{
extract::{JsonExtractionRejection, ProtoRejection},
},
mime::JsonOrProto,
- v1::stream::sse::LastEventId,
+ v1::{config::StreamConfigHeader, stream::sse::LastEventId},
};
#[derive(Debug, thiserror::Error)]
@@ -54,6 +54,9 @@ where
async fn from_request(req: Request, state: &S) -> Result {
let content_type = crate::mime::content_type(req.headers());
let encryption_key = parse_header_opt::(req.headers())?;
+ let create_stream_config_patch = parse_header_opt::(req.headers())?
+ .map(|header| header.0)
+ .unwrap_or_default();
if content_type.as_ref().is_some_and(crate::mime::is_s2s_proto) {
let response_compression =
@@ -88,6 +91,7 @@ where
return Ok(Self::S2s {
encryption_key,
+ create_stream_config_patch,
inputs: Box::pin(inputs),
response_compression,
});
@@ -117,6 +121,7 @@ where
Ok(Self::Unary {
encryption_key,
+ create_stream_config_patch,
input,
response_mime,
})
@@ -132,12 +137,16 @@ where
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result {
let content_type = crate::mime::content_type(&parts.headers);
let encryption_key = parse_header_opt::(&parts.headers)?;
+ let create_stream_config_patch = parse_header_opt::(&parts.headers)?
+ .map(|header| header.0)
+ .unwrap_or_default();
if content_type.as_ref().is_some_and(crate::mime::is_s2s_proto) {
let response_compression =
s2s::CompressionAlgorithm::from_accept_encoding(&parts.headers);
return Ok(Self::S2s {
encryption_key,
+ create_stream_config_patch,
response_compression,
});
}
@@ -150,6 +159,7 @@ where
let last_event_id = parse_header_opt::(&parts.headers)?;
return Ok(Self::EventStream {
encryption_key,
+ create_stream_config_patch,
format,
last_event_id,
});
@@ -162,6 +172,7 @@ where
Ok(Self::Unary {
encryption_key,
+ create_stream_config_patch,
format,
response_mime,
})
diff --git a/api/src/v1/stream/mod.rs b/api/src/v1/stream/mod.rs
index 255879cd..2d8d66e8 100644
--- a/api/src/v1/stream/mod.rs
+++ b/api/src/v1/stream/mod.rs
@@ -11,6 +11,7 @@ use std::time::Duration;
use futures_core::stream::BoxStream;
use itertools::Itertools as _;
use s2_common::{
+ config::OptionalStreamConfig,
encryption::EncryptionKey,
record,
stream::{StreamName, StreamNamePrefix, StreamNameStartAfter},
@@ -210,18 +211,24 @@ pub enum ReadRequest {
/// Unary
Unary {
encryption_key: Option,
+ /// Parsed `s2-stream-config` header; empty if absent.
+ create_stream_config_patch: OptionalStreamConfig,
format: Format,
response_mime: JsonOrProto,
},
/// Server-Sent Events streaming response
EventStream {
encryption_key: Option,
+ /// Parsed `s2-stream-config` header; empty if absent.
+ create_stream_config_patch: OptionalStreamConfig,
format: Format,
last_event_id: Option,
},
/// S2S streaming response
S2s {
encryption_key: Option,
+ /// Parsed `s2-stream-config` header; empty if absent.
+ create_stream_config_patch: OptionalStreamConfig,
response_compression: s2s::CompressionAlgorithm,
},
}
@@ -230,12 +237,16 @@ pub enum AppendRequest {
/// Unary
Unary {
encryption_key: Option,
+ /// Parsed `s2-stream-config` header; empty if absent.
+ create_stream_config_patch: OptionalStreamConfig,
input: s2_common::stream::AppendInput,
response_mime: JsonOrProto,
},
/// S2S bi-directional streaming
S2s {
encryption_key: Option,
+ /// Parsed `s2-stream-config` header; empty if absent.
+ create_stream_config_patch: OptionalStreamConfig,
inputs: BoxStream<'static, Result>,
response_compression: s2s::CompressionAlgorithm,
},
@@ -246,21 +257,25 @@ impl std::fmt::Debug for AppendRequest {
match self {
AppendRequest::Unary {
encryption_key,
+ create_stream_config_patch,
input,
response_mime: response,
} => f
.debug_struct("AppendRequest::Unary")
.field("encryption_key", encryption_key)
+ .field("create_stream_config_patch", create_stream_config_patch)
.field("input", input)
.field("response", response)
.finish(),
AppendRequest::S2s {
encryption_key,
+ create_stream_config_patch,
response_compression,
..
} => f
.debug_struct("AppendRequest::S2s")
.field("encryption_key", encryption_key)
+ .field("create_stream_config_patch", create_stream_config_patch)
.field("response_compression", response_compression)
.finish(),
}
diff --git a/cli/src/cli.rs b/cli/src/cli.rs
index 74981c1a..fe5d148a 100644
--- a/cli/src/cli.rs
+++ b/cli/src/cli.rs
@@ -693,6 +693,15 @@ pub struct AppendArgs {
#[command(flatten)]
pub encryption_key: EncryptionKeyArgs,
+
+ /// Stream configuration to apply if the stream is created on append.
+ /// Unset fields inherit the basin's default stream configuration.
+ /// Ignored if the stream already exists.
+ #[command(
+ flatten,
+ next_help_heading = "Stream configuration (applied only if the stream is created on append)"
+ )]
+ pub stream_config: StreamConfig,
}
#[derive(Args, Debug, Clone, Default)]
@@ -771,6 +780,15 @@ pub struct ReadArgs {
#[command(flatten)]
pub encryption_key: EncryptionKeyArgs,
+
+ /// Stream configuration to apply if the stream is created on read.
+ /// Unset fields inherit the basin's default stream configuration.
+ /// Ignored if the stream already exists.
+ #[command(
+ flatten,
+ next_help_heading = "Stream configuration (applied only if the stream is created on read)"
+ )]
+ pub stream_config: StreamConfig,
}
#[derive(Args, Debug)]
diff --git a/cli/src/main.rs b/cli/src/main.rs
index cc809202..97d560f1 100644
--- a/cli/src/main.rs
+++ b/cli/src/main.rs
@@ -657,9 +657,13 @@ async fn run(cli: Cli) -> Result {
record_stream,
args.uri,
encryption_key.as_ref(),
- args.fencing_token,
- args.match_seq_num,
- *args.linger,
+ ops::AppendOptions {
+ fencing_token: args.fencing_token,
+ match_seq_num: args.match_seq_num,
+ linger: *args.linger,
+ stream_config: (!args.stream_config.is_empty())
+ .then(|| args.stream_config.into()),
+ },
);
let mut acks = std::pin::pin!(acks);
let mut last_printed_batch_end: Option = None;
diff --git a/cli/src/ops.rs b/cli/src/ops.rs
index 7ae3218d..468d6ab0 100644
--- a/cli/src/ops.rs
+++ b/cli/src/ops.rs
@@ -570,23 +570,32 @@ pub async fn read(
stop = stop.with_until(..until);
}
+ let mut input = ReadInput::new().with_start(start).with_stop(stop);
+ if !args.stream_config.is_empty() {
+ input = input.with_stream_config(args.stream_config.clone().into());
+ }
+
stream
- .read_session(
- ReadInput::new().with_start(start).with_stop(stop),
- ReadSessionConfig::default(),
- )
+ .read_session(input, ReadSessionConfig::default())
.await
.map_err(|e| CliError::op(OpKind::Read, e))
}
+/// Options controlling how records are appended.
+pub struct AppendOptions {
+ pub fencing_token: Option,
+ pub match_seq_num: Option,
+ pub linger: Duration,
+ /// Stream configuration to apply if the stream is created on append.
+ pub stream_config: Option,
+}
+
pub fn append<'a, S, E>(
s2: &'a S2,
records: S,
uri: S2BasinAndStreamUri,
encryption_key: Option<&'a EncryptionKey>,
- fencing_token: Option,
- match_seq_num: Option,
- linger: Duration,
+ options: AppendOptions,
) -> impl Stream- > + Send + 'a
where
S: Stream
- > + Send + Unpin + 'a,
@@ -594,12 +603,15 @@ where
{
let stream = stream_with_encryption(s2, uri, encryption_key);
- let batching_config = BatchingConfig::new().with_linger(linger);
+ let batching_config = BatchingConfig::new().with_linger(options.linger);
let mut producer_config = ProducerConfig::new().with_batching(batching_config);
- if let Some(ft) = fencing_token {
+ if let Some(config) = options.stream_config {
+ producer_config = producer_config.with_stream_config(config);
+ }
+ if let Some(ft) = options.fencing_token {
producer_config = producer_config.with_fencing_token(ft);
}
- if let Some(seq) = match_seq_num {
+ if let Some(seq) = options.match_seq_num {
producer_config = producer_config.with_match_seq_num(seq);
}
diff --git a/cli/tests/integration.rs b/cli/tests/integration.rs
index b193551f..7d0152f0 100644
--- a/cli/tests/integration.rs
+++ b/cli/tests/integration.rs
@@ -563,6 +563,95 @@ fn append_from_stdin() {
cleanup_stream(&basin, &stream);
}
+#[test]
+#[serial]
+fn append_with_stream_config() {
+ let basin = unique_name("test-cli-csoa-cfg");
+ s2().args([
+ "create-basin",
+ &basin,
+ "--retention-policy",
+ "7d",
+ "--create-stream-on-append",
+ ])
+ .assert()
+ .success();
+ wait_for_basin(&basin);
+
+ let stream = unique_name("test-csoa-new");
+ let uri = format!("s2://{basin}/{stream}");
+ s2().args([
+ "append",
+ &uri,
+ "--format",
+ "text",
+ "--input",
+ "-",
+ "--retention-policy",
+ "1h",
+ "--delete-on-empty-min-age",
+ "5m",
+ ])
+ .write_stdin("first record\n")
+ .assert()
+ .success();
+
+ s2().args(["get-stream-config", &uri])
+ .assert()
+ .success()
+ .stdout(
+ predicate::str::contains("1h")
+ .and(predicate::str::contains("5m"))
+ .and(predicate::str::contains("7days").not()),
+ );
+
+ s2().args([
+ "append",
+ &uri,
+ "--format",
+ "text",
+ "--input",
+ "-",
+ "--retention-policy",
+ "2h",
+ ])
+ .write_stdin("second record\n")
+ .assert()
+ .success();
+
+ s2().args(["get-stream-config", &uri])
+ .assert()
+ .success()
+ .stdout(predicate::str::contains("1h").and(predicate::str::contains("2h").not()));
+
+ cleanup_stream(&basin, &stream);
+ cleanup_basin(&basin);
+}
+
+#[test]
+#[serial]
+fn read_with_stream_config() {
+ let basin = unique_name("test-cli-csor-cfg");
+ s2().args(["create-basin", &basin, "--create-stream-on-read"])
+ .assert()
+ .success();
+ wait_for_basin(&basin);
+
+ let stream = unique_name("test-csor-new");
+ let uri = format!("s2://{basin}/{stream}");
+ s2().args(["read", &uri, "--count", "1", "--retention-policy", "1h"])
+ .assert()
+ .failure();
+
+ s2().args(["get-stream-config", &uri])
+ .assert()
+ .success()
+ .stdout(predicate::str::contains("1h"));
+
+ cleanup_stream(&basin, &stream);
+ cleanup_basin(&basin);
+}
+
#[test]
#[serial]
fn tail_stream() {
diff --git a/lite/src/backend/append.rs b/lite/src/backend/append.rs
index 82343edb..a8ebbe78 100644
--- a/lite/src/backend/append.rs
+++ b/lite/src/backend/append.rs
@@ -7,6 +7,7 @@ use std::{
use futures::{Stream, StreamExt as _, future::OptionFuture, stream::FuturesOrdered};
use s2_common::{
basin::BasinName,
+ config::OptionalStreamConfig,
encryption::{EncryptionKey, EncryptionSpec},
record::{SeqNum, StreamPosition},
stream::{AppendAck, AppendInput, StreamName},
@@ -14,20 +15,26 @@ use s2_common::{
use s2_storage::record::encrypt_append_input;
use tokio::sync::oneshot;
-use super::{Backend, StreamHandle};
+use super::{Backend, StreamHandle, core::AutoCreateOn};
use crate::backend::error::{AppendError, AppendErrorInternal, StorageError};
impl Backend {
+ /// Open a stream for an append or append session.
+ ///
+ /// `stream_config` is applied if the stream is created on append. Unset fields inherit the
+ /// basin's default stream configuration. Ignored if the stream already exists.
pub async fn open_for_append(
&self,
basin: &BasinName,
stream: &StreamName,
encryption_key: Option,
+ stream_config: OptionalStreamConfig,
) -> Result {
self.stream_handle_with_auto_create::(
basin,
stream,
- |config| config.create_stream_on_append,
+ AutoCreateOn::Append,
+ stream_config,
|cipher| Ok(EncryptionSpec::resolve(cipher, encryption_key)?),
)
.await
diff --git a/lite/src/backend/core.rs b/lite/src/backend/core.rs
index 0b915877..cf7558ad 100644
--- a/lite/src/backend/core.rs
+++ b/lite/src/backend/core.rs
@@ -331,11 +331,16 @@ impl Backend {
}
}
+ /// Resolve a handle for `stream`, creating it on demand if the basin config allows.
+ ///
+ /// `stream_config` is applied over the basin's default stream configuration only if the
+ /// stream is being created. It must already be validated.
pub(super) async fn stream_handle_with_auto_create(
&self,
basin: &BasinName,
stream: &StreamName,
- should_auto_create: impl FnOnce(&BasinConfig) -> bool,
+ auto_create_on: AutoCreateOn,
+ stream_config: OptionalStreamConfig,
resolve_encryption: impl FnOnce(Option) -> Result,
) -> Result
where
@@ -347,54 +352,66 @@ impl Backend {
+ From
+ From,
{
- match self.streamer_client_guarded(basin, stream).await {
- Ok(client) => Ok(StreamHandle {
- db: self.db.clone(),
- encryption: resolve_encryption(client.cipher())?,
- client,
- }),
+ let client = match self.streamer_client_guarded(basin, stream).await {
+ Ok(client) => client,
Err(StreamerError::StreamNotFound(e)) => {
let config = match self.get_basin_config(basin.clone()).await {
Ok(config) => config,
Err(GetBasinConfigError::Storage(e)) => Err(e)?,
Err(GetBasinConfigError::BasinNotFound(e)) => Err(e)?,
};
- if should_auto_create(&config) {
- if let Err(e) = self
- .provision_stream(
- basin.clone(),
- stream.clone(),
- OptionalStreamConfig::default(),
- ProvisionMode::CreateOnly {
- request_token: None,
- },
- )
- .await
- {
- match e {
- ProvisionStreamError::Storage(e) => Err(e)?,
- ProvisionStreamError::TransactionConflict(e) => Err(e)?,
- ProvisionStreamError::BasinDeletionPending(e) => Err(e)?,
- ProvisionStreamError::StreamDeletionPending(e) => Err(e)?,
- ProvisionStreamError::BasinNotFound(e) => Err(e)?,
- ProvisionStreamError::StreamAlreadyExists(_) => {}
- ProvisionStreamError::Validation(_) => {
- unreachable!("auto-create uses default config")
- }
+ if !auto_create_on.is_enabled(&config) {
+ return Err(e.into());
+ }
+ if let Err(e) = self
+ .provision_stream(
+ basin.clone(),
+ stream.clone(),
+ stream_config,
+ ProvisionMode::CreateOnly {
+ request_token: None,
+ },
+ )
+ .await
+ {
+ match e {
+ ProvisionStreamError::Storage(e) => Err(e)?,
+ ProvisionStreamError::TransactionConflict(e) => Err(e)?,
+ ProvisionStreamError::BasinDeletionPending(e) => Err(e)?,
+ ProvisionStreamError::StreamDeletionPending(e) => Err(e)?,
+ ProvisionStreamError::BasinNotFound(e) => Err(e)?,
+ ProvisionStreamError::StreamAlreadyExists(_) => {}
+ ProvisionStreamError::Validation(e) => {
+ unreachable!("auto-create config is validated at the API boundary: {e}")
}
}
- let client = self.streamer_client_guarded(basin, stream).await?;
- let encryption = resolve_encryption(client.cipher())?;
- Ok(StreamHandle {
- db: self.db.clone(),
- encryption,
- client,
- })
- } else {
- Err(e.into())
}
+ self.streamer_client_guarded(basin, stream).await?
}
- Err(e) => Err(e.into()),
+ Err(e) => return Err(e.into()),
+ };
+ Ok(StreamHandle {
+ db: self.db.clone(),
+ encryption: resolve_encryption(client.cipher())?,
+ client,
+ })
+ }
+}
+
+/// Which basin setting governs creating a missing stream on demand.
+#[derive(Debug, Clone, Copy)]
+pub(super) enum AutoCreateOn {
+ /// `create_stream_on_append`
+ Append,
+ /// `create_stream_on_read`
+ Read,
+}
+
+impl AutoCreateOn {
+ fn is_enabled(self, config: &BasinConfig) -> bool {
+ match self {
+ Self::Append => config.create_stream_on_append,
+ Self::Read => config.create_stream_on_read,
}
}
}
@@ -631,7 +648,9 @@ mod tests {
let basin = basin.clone();
let stream = stream.clone();
tokio::spawn(async move {
- let handle = backend.open_for_append(&basin, &stream, None).await?;
+ let handle = backend
+ .open_for_append(&basin, &stream, None, OptionalStreamConfig::default())
+ .await?;
handle.append(append_input(&format!("r{i}"))).await
})
})
diff --git a/lite/src/backend/read.rs b/lite/src/backend/read.rs
index 7043409f..c4e0df3d 100644
--- a/lite/src/backend/read.rs
+++ b/lite/src/backend/read.rs
@@ -4,6 +4,7 @@ use futures::{Stream, StreamExt as _};
use s2_common::{
basin::BasinName,
caps,
+ config::OptionalStreamConfig,
encryption::{EncryptionKey, EncryptionSpec},
read_extent::{EvaluatedReadLimit, ReadLimit, ReadUntil},
record::{Metered, MeteredSize as _, SeqNum, StreamPosition, Timestamp},
@@ -15,7 +16,7 @@ use s2_storage::record::{
use slatedb::config::{DurabilityLevel, ScanOptions};
use tokio::{sync::broadcast, time::Instant};
-use super::{Backend, StreamHandle};
+use super::{Backend, StreamHandle, core::AutoCreateOn};
use crate::{
backend::{
error::{
@@ -28,6 +29,7 @@ use crate::{
};
impl Backend {
+ /// Open a stream for a check tail.
pub async fn open_for_check_tail(
&self,
basin: &BasinName,
@@ -36,22 +38,29 @@ impl Backend {
self.stream_handle_with_auto_create::(
basin,
stream,
- |config| config.create_stream_on_read,
+ AutoCreateOn::Read,
+ OptionalStreamConfig::default(),
|_| Ok(EncryptionSpec::Plain),
)
.await
}
+ /// Open a stream for a read or read session.
+ ///
+ /// `stream_config` is applied if the stream is created on read. Unset fields inherit the
+ /// basin's default stream configuration. Ignored if the stream already exists.
pub async fn open_for_read(
&self,
basin: &BasinName,
stream: &StreamName,
encryption_key: Option,
+ stream_config: OptionalStreamConfig,
) -> Result {
self.stream_handle_with_auto_create::(
basin,
stream,
- |config| config.create_stream_on_read,
+ AutoCreateOn::Read,
+ stream_config,
|cipher| Ok(EncryptionSpec::resolve(cipher, encryption_key)?),
)
.await
@@ -562,7 +571,7 @@ mod tests {
let input = append_input(Record::try_from_parts(vec![], bytes::Bytes::from("x")).unwrap());
let ack = backend
- .open_for_append(&basin, &stream, None)
+ .open_for_append(&basin, &stream, None, OptionalStreamConfig::default())
.await
.unwrap()
.append(input)
@@ -585,7 +594,7 @@ mod tests {
wait: None,
};
let session = backend
- .open_for_read(&basin, &stream, None)
+ .open_for_read(&basin, &stream, None, OptionalStreamConfig::default())
.await
.unwrap()
.read(start, end)
@@ -642,7 +651,7 @@ mod tests {
};
let session = backend
- .open_for_read(&basin, &stream, None)
+ .open_for_read(&basin, &stream, None, OptionalStreamConfig::default())
.await
.unwrap()
.read(start, end)
@@ -716,7 +725,7 @@ mod tests {
let initial_input =
append_input(Record::try_from_parts(vec![], bytes::Bytes::from("initial")).unwrap());
backend
- .open_for_append(&basin, &stream, None)
+ .open_for_append(&basin, &stream, None, OptionalStreamConfig::default())
.await
.unwrap()
.append(initial_input)
@@ -736,7 +745,7 @@ mod tests {
};
let session = backend
- .open_for_read(&basin, &stream, None)
+ .open_for_read(&basin, &stream, None, OptionalStreamConfig::default())
.await
.unwrap()
.read(start, end)
@@ -776,7 +785,7 @@ mod tests {
let follow_input =
append_input(Record::try_from_parts(vec![], bytes::Bytes::from("follow-1")).unwrap());
backend
- .open_for_append(&basin, &stream, None)
+ .open_for_append(&basin, &stream, None, OptionalStreamConfig::default())
.await
.unwrap()
.append(follow_input)
@@ -881,7 +890,7 @@ mod tests {
wait: Some(wait),
};
let session = backend
- .open_for_read(&basin, &stream, None)
+ .open_for_read(&basin, &stream, None, OptionalStreamConfig::default())
.await
.unwrap()
.read(start, end)
@@ -906,7 +915,7 @@ mod tests {
Record::try_from_parts(vec![], bytes::Bytes::from(format!("lagged-{i}"))).unwrap(),
);
let ack = backend
- .open_for_append(&basin, &stream, None)
+ .open_for_append(&basin, &stream, None, OptionalStreamConfig::default())
.await
.unwrap()
.append(input)
@@ -960,7 +969,7 @@ mod tests {
let initial_input =
append_input(Record::try_from_parts(vec![], bytes::Bytes::from("initial")).unwrap());
backend
- .open_for_append(&basin, &stream, None)
+ .open_for_append(&basin, &stream, None, OptionalStreamConfig::default())
.await
.unwrap()
.append(initial_input)
@@ -977,7 +986,7 @@ mod tests {
wait: None,
};
let session = backend
- .open_for_read(&basin, &stream, None)
+ .open_for_read(&basin, &stream, None, OptionalStreamConfig::default())
.await
.unwrap()
.read(start, end)
@@ -1007,7 +1016,7 @@ mod tests {
let follow_input =
append_input(Record::try_from_parts(vec![], bytes::Bytes::from("follow-1")).unwrap());
backend
- .open_for_append(&basin, &stream, None)
+ .open_for_append(&basin, &stream, None, OptionalStreamConfig::default())
.await
.unwrap()
.append(follow_input)
diff --git a/lite/src/handlers/v1/records.rs b/lite/src/handlers/v1/records.rs
index 5532e20f..6221be80 100644
--- a/lite/src/handlers/v1/records.rs
+++ b/lite/src/handlers/v1/records.rs
@@ -161,6 +161,7 @@ pub struct ReadArgs {
v1t::StreamNamePathSegment,
s2_api::data::S2FormatHeader,
s2_api::data::S2EncryptionKeyHeader,
+ s2_api::data::S2StreamConfigHeader,
v1t::stream::ReadStart,
v1t::stream::ReadEnd,
),
@@ -186,12 +187,13 @@ pub async fn read(
match request {
v1t::stream::ReadRequest::Unary {
encryption_key,
+ create_stream_config_patch,
format,
response_mime,
} => {
let (start, end) = prepare_read(start, end, ReadMode::Unary)?;
let session = backend
- .open_for_read(&basin, &stream, encryption_key)
+ .open_for_read(&basin, &stream, encryption_key, create_stream_config_patch)
.await?
.read(start, end)
.await?;
@@ -209,13 +211,14 @@ pub async fn read(
}
v1t::stream::ReadRequest::EventStream {
encryption_key,
+ create_stream_config_patch,
format,
last_event_id,
} => {
let (start, end) = apply_last_event_id(start, end, last_event_id);
let (start, end) = prepare_read(start, end, ReadMode::Streaming)?;
let session = backend
- .open_for_read(&basin, &stream, encryption_key)
+ .open_for_read(&basin, &stream, encryption_key, create_stream_config_patch)
.await?
.read(start, end)
.await?;
@@ -265,11 +268,12 @@ pub async fn read(
}
v1t::stream::ReadRequest::S2s {
encryption_key,
+ create_stream_config_patch,
response_compression,
} => {
let (start, end) = prepare_read(start, end, ReadMode::Streaming)?;
let s2s_stream = backend
- .open_for_read(&basin, &stream, encryption_key)
+ .open_for_read(&basin, &stream, encryption_key, create_stream_config_patch)
.await?
.read(start, end)
.await?
@@ -364,6 +368,7 @@ pub struct AppendArgs {
v1t::StreamNamePathSegment,
s2_api::data::S2FormatHeader,
s2_api::data::S2EncryptionKeyHeader,
+ s2_api::data::S2StreamConfigHeader,
),
servers(
(url = super::paths::cloud_endpoints::BASIN, variables(
@@ -384,11 +389,12 @@ pub async fn append(
match request {
v1t::stream::AppendRequest::Unary {
encryption_key,
+ create_stream_config_patch,
input,
response_mime,
} => {
let handle = backend
- .open_for_append(&basin, &stream, encryption_key)
+ .open_for_append(&basin, &stream, encryption_key, create_stream_config_patch)
.await?;
let ack = handle.append(input).await?;
match response_mime {
@@ -404,11 +410,12 @@ pub async fn append(
}
v1t::stream::AppendRequest::S2s {
encryption_key,
+ create_stream_config_patch,
inputs,
response_compression,
} => {
let handle = backend
- .open_for_append(&basin, &stream, encryption_key)
+ .open_for_append(&basin, &stream, encryption_key, create_stream_config_patch)
.await?;
let (err_tx, err_rx) = tokio::sync::oneshot::channel();
@@ -469,13 +476,19 @@ mod tests {
use bytesize::ByteSize;
use futures::TryStreamExt as _;
use prost::Message as _;
- use s2_api::v1::stream::{
- proto,
- s2s::{FrameDecoder, SessionMessage},
+ use s2_api::v1::{
+ config::STREAM_CONFIG_HEADER,
+ stream::{
+ proto,
+ s2s::{self, FrameDecoder, SessionMessage},
+ },
};
use s2_common::{
basin::{BASIN_HEADER, BasinName},
- config::{BasinConfig, OptionalStreamConfig},
+ config::{
+ BasinConfig, DeleteOnEmptyConfig, OptionalStreamConfig, RetentionPolicy, StorageClass,
+ StreamConfig,
+ },
encryption::{EncryptionAlgorithm, EncryptionKey, S2_ENCRYPTION_KEY_HEADER},
read_extent::{ReadLimit, ReadUntil},
record::{EnvelopeRecord, Metered, Record},
@@ -597,7 +610,12 @@ mod tests {
encryption_key: EncryptionKey,
) {
backend
- .open_for_append(basin, stream, Some(encryption_key))
+ .open_for_append(
+ basin,
+ stream,
+ Some(encryption_key),
+ OptionalStreamConfig::default(),
+ )
.await
.expect("open append handle")
.append(append_input(body))
@@ -697,7 +715,12 @@ mod tests {
assert_eq!(ack.end.as_ref().map(|pos| pos.seq_num), Some(1));
let records = backend
- .open_for_read(&basin, &stream, Some(encryption_key.clone()))
+ .open_for_read(
+ &basin,
+ &stream,
+ Some(encryption_key.clone()),
+ OptionalStreamConfig::default(),
+ )
.await
.expect("open read handle")
.read(
@@ -731,6 +754,191 @@ mod tests {
assert_eq!(record.body().as_ref(), b"secret");
}
+ fn basin_config_with_create_stream_on_append() -> BasinConfig {
+ BasinConfig {
+ create_stream_on_append: true,
+ default_stream_config: OptionalStreamConfig {
+ storage_class: Some(StorageClass::Standard),
+ ..Default::default()
+ },
+ ..Default::default()
+ }
+ }
+
+ fn expected_auto_created_config() -> StreamConfig {
+ StreamConfig {
+ storage_class: StorageClass::Standard,
+ retention_policy: RetentionPolicy::Age(Duration::from_secs(3600)),
+ timestamping: Default::default(),
+ delete_on_empty: DeleteOnEmptyConfig {
+ min_age: Duration::from_secs(300),
+ },
+ }
+ }
+
+ const STREAM_CONFIG_HEADER_VALUE: &str =
+ r#"{"retention_policy":{"age":3600},"delete_on_empty":{"min_age_secs":300}}"#;
+
+ fn append_record_input(body: &'static [u8]) -> proto::AppendInput {
+ proto::AppendInput {
+ records: vec![proto::AppendRecord {
+ timestamp: None,
+ headers: vec![],
+ body: Bytes::from_static(body),
+ }],
+ match_seq_num: None,
+ fencing_token: None,
+ }
+ }
+
+ #[tokio::test]
+ async fn json_append_auto_creates_stream_with_stream_config_header() {
+ let (app, backend, basin, stream) = setup_app_without_stream(
+ "append-json-create-config",
+ basin_config_with_create_stream_on_append(),
+ )
+ .await;
+
+ let body = serde_json::json!({"records": [{"body": "hello"}]});
+ let response = send(
+ &app,
+ request_builder("POST", format!("/v1/streams/{stream}/records"), &basin)
+ .header(header::CONTENT_TYPE, "application/json")
+ .header(STREAM_CONFIG_HEADER.as_str(), STREAM_CONFIG_HEADER_VALUE)
+ .body(Body::from(body.to_string()))
+ .unwrap(),
+ )
+ .await;
+
+ assert_eq!(response.status(), StatusCode::OK);
+ let ack = response_json(response, "append ack body").await;
+ assert_eq!(ack["end"]["seq_num"], 1);
+ let config = backend
+ .get_stream_config(basin, stream)
+ .await
+ .expect("get stream config");
+ assert_eq!(config, expected_auto_created_config());
+ }
+
+ #[tokio::test]
+ async fn append_with_invalid_stream_config_header_is_rejected_without_creating() {
+ let (app, backend, basin, stream) = setup_app_without_stream(
+ "append-create-config-invalid",
+ basin_config_with_create_stream_on_append(),
+ )
+ .await;
+
+ for (value, expected_message) in [
+ (
+ r#"{"retention_policy":{"age":0}}"#,
+ "age must be greater than 0 seconds",
+ ),
+ ("not json", "invalid JSON"),
+ ] {
+ let body = serde_json::json!({"records": [{"body": "hello"}]});
+ let response = send(
+ &app,
+ request_builder("POST", format!("/v1/streams/{stream}/records"), &basin)
+ .header(header::CONTENT_TYPE, "application/json")
+ .header(STREAM_CONFIG_HEADER.as_str(), value)
+ .body(Body::from(body.to_string()))
+ .unwrap(),
+ )
+ .await;
+
+ assert_eq!(response.status(), StatusCode::BAD_REQUEST);
+ let info = response_json(response, "append error body").await;
+ assert_eq!(info["code"], "bad_header");
+ let message = info["message"].as_str().expect("error message string");
+ assert!(
+ message.contains("s2-stream-config") && message.contains(expected_message),
+ "{message}"
+ );
+ }
+ assert_no_streams(&backend, &basin).await;
+ }
+
+ #[tokio::test]
+ async fn s2s_append_session_auto_creates_stream_with_stream_config_header() {
+ let (app, backend, basin, stream) = setup_app_without_stream(
+ "append-s2s-create-config",
+ basin_config_with_create_stream_on_append(),
+ )
+ .await;
+
+ let frame = |body: &'static [u8]| {
+ SessionMessage::regular(s2s::CompressionAlgorithm::None, &append_record_input(body))
+ .expect("encode frame")
+ .encode()
+ };
+ let mut body = BytesMut::new();
+ body.extend_from_slice(&frame(b"first"));
+ body.extend_from_slice(&frame(b"second"));
+
+ let response = send(
+ &app,
+ request_builder("POST", format!("/v1/streams/{stream}/records"), &basin)
+ .header(header::CONTENT_TYPE, "s2s/proto")
+ .header(STREAM_CONFIG_HEADER.as_str(), STREAM_CONFIG_HEADER_VALUE)
+ .body(Body::from(body.freeze()))
+ .unwrap(),
+ )
+ .await;
+
+ assert_eq!(response.status(), StatusCode::OK);
+ let body = response_bytes(response, "s2s body").await;
+ let mut decoder = FrameDecoder;
+ let mut buf = BytesMut::from(body.as_ref());
+ let mut acks = Vec::new();
+ while let Some(frame) = decoder.decode(&mut buf).expect("frame decode") {
+ let SessionMessage::Regular(ack) = frame else {
+ panic!("expected regular frame");
+ };
+ acks.push(
+ ack.try_into_proto::()
+ .expect("decode append ack"),
+ );
+ }
+ assert_eq!(acks.len(), 2);
+ assert_eq!(acks[1].end.as_ref().map(|pos| pos.seq_num), Some(2));
+
+ let config = backend
+ .get_stream_config(basin, stream)
+ .await
+ .expect("get stream config");
+ assert_eq!(config, expected_auto_created_config());
+ }
+
+ #[tokio::test]
+ async fn read_auto_creates_stream_with_stream_config_header() {
+ let basin_config = BasinConfig {
+ create_stream_on_append: false,
+ create_stream_on_read: true,
+ ..basin_config_with_create_stream_on_append()
+ };
+ let (app, backend, basin, stream) =
+ setup_app_without_stream("read-create-config", basin_config).await;
+
+ let response = send(
+ &app,
+ request_builder(
+ "GET",
+ format!("/v1/streams/{stream}/records?seq_num=0"),
+ &basin,
+ )
+ .header(STREAM_CONFIG_HEADER.as_str(), STREAM_CONFIG_HEADER_VALUE)
+ .body(Body::empty())
+ .unwrap(),
+ )
+ .await;
+ assert_eq!(response.status(), StatusCode::RANGE_NOT_SATISFIABLE);
+ let config = backend
+ .get_stream_config(basin, stream)
+ .await
+ .expect("get stream config");
+ assert_eq!(config, expected_auto_created_config());
+ }
+
#[tokio::test]
async fn invalid_read_bounds_do_not_auto_create_stream() {
let basin_config = BasinConfig {
diff --git a/lite/tests/backend/common/mod.rs b/lite/tests/backend/common/mod.rs
index 019728e9..a4bea287 100644
--- a/lite/tests/backend/common/mod.rs
+++ b/lite/tests/backend/common/mod.rs
@@ -3,6 +3,7 @@ use std::pin::Pin;
use futures::Stream;
use s2_common::{
basin::BasinName,
+ config::OptionalStreamConfig,
encryption::EncryptionSpec,
record::StreamPosition,
stream::{AppendAck, AppendInput, StreamName},
@@ -30,6 +31,7 @@ pub async fn append(
&basin,
&stream,
encryption.and_then(encryption_key_for_spec),
+ OptionalStreamConfig::default(),
)
.await?
.append(input)
@@ -51,6 +53,7 @@ where
&basin,
&stream,
encryption.and_then(encryption_key_for_spec),
+ OptionalStreamConfig::default(),
)
.await?
.append_session(inputs);
diff --git a/lite/tests/backend/common/read.rs b/lite/tests/backend/common/read.rs
index 28cb6b33..6e3405ba 100644
--- a/lite/tests/backend/common/read.rs
+++ b/lite/tests/backend/common/read.rs
@@ -3,6 +3,7 @@ use std::{pin::Pin, task::Poll, time::Duration};
use futures::StreamExt;
use s2_common::{
basin::BasinName,
+ config::OptionalStreamConfig,
encryption::EncryptionSpec,
read_extent::{ReadLimit, ReadUntil},
record::{Record, SequencedRecord},
@@ -83,7 +84,12 @@ pub async fn try_open_read_session_with_encryption(
ReadError,
> {
let read_session = backend
- .open_for_read(basin, stream, encryption_key_for_spec(encryption))
+ .open_for_read(
+ basin,
+ stream,
+ encryption_key_for_spec(encryption),
+ OptionalStreamConfig::default(),
+ )
.await?
.read(start, end)
.await?;
diff --git a/lite/tests/backend/common/setup.rs b/lite/tests/backend/common/setup.rs
index 324ea3b6..d041c29b 100644
--- a/lite/tests/backend/common/setup.rs
+++ b/lite/tests/backend/common/setup.rs
@@ -237,7 +237,12 @@ pub async fn append_payloads_with_encryption(
fencing_token: None,
};
backend
- .open_for_append(basin, stream, encryption_key_for_spec(encryption))
+ .open_for_append(
+ basin,
+ stream,
+ encryption_key_for_spec(encryption),
+ OptionalStreamConfig::default(),
+ )
.await
.expect("Failed to open append handle")
.append(input)
@@ -257,7 +262,7 @@ pub async fn append_timestamped_payloads(
fencing_token: None,
};
backend
- .open_for_append(basin, stream, None)
+ .open_for_append(basin, stream, None, OptionalStreamConfig::default())
.await
.expect("Failed to open append handle")
.append(input)
diff --git a/lite/tests/backend/data_plane/auto_create.rs b/lite/tests/backend/data_plane/auto_create.rs
index 24bdfc00..42ae4d1f 100644
--- a/lite/tests/backend/data_plane/auto_create.rs
+++ b/lite/tests/backend/data_plane/auto_create.rs
@@ -3,7 +3,10 @@ use std::time::Duration;
use bytes::Bytes;
use s2_common::{
basin::BasinName,
- config::BasinConfig,
+ config::{
+ BasinConfig, DeleteOnEmptyConfig, OptionalDeleteOnEmptyConfig, OptionalStreamConfig,
+ RetentionPolicy, StorageClass, StreamConfig,
+ },
encryption::EncryptionAlgorithm,
read_extent::{ReadLimit, ReadUntil},
record::StreamPosition,
@@ -123,6 +126,140 @@ async fn test_backend_append_auto_creates_stream_with_basin_cipher() {
assert_eq!(envelope_bodies(&records), vec![b"secret".to_vec()]);
}
+fn basin_config_with_defaults() -> BasinConfig {
+ BasinConfig {
+ create_stream_on_append: true,
+ default_stream_config: OptionalStreamConfig {
+ storage_class: Some(StorageClass::Standard),
+ retention_policy: Some(RetentionPolicy::Age(Duration::from_secs(7 * 24 * 60 * 60))),
+ ..Default::default()
+ },
+ ..Default::default()
+ }
+}
+
+fn requested_stream_config() -> OptionalStreamConfig {
+ OptionalStreamConfig {
+ retention_policy: Some(RetentionPolicy::Age(Duration::from_secs(3600))),
+ delete_on_empty: OptionalDeleteOnEmptyConfig {
+ min_age: Some(Duration::from_secs(300)),
+ },
+ ..Default::default()
+ }
+}
+
+fn expected_merged_stream_config() -> StreamConfig {
+ StreamConfig {
+ storage_class: StorageClass::Standard,
+ retention_policy: RetentionPolicy::Age(Duration::from_secs(3600)),
+ timestamping: Default::default(),
+ delete_on_empty: DeleteOnEmptyConfig {
+ min_age: Duration::from_secs(300),
+ },
+ }
+}
+
+#[tokio::test]
+async fn test_backend_append_auto_create_applies_stream_config() {
+ let backend = create_backend().await;
+ let basin_name = create_test_basin(
+ &backend,
+ "backend-auto-create-config",
+ basin_config_with_defaults(),
+ )
+ .await;
+ let stream_name = test_stream_name("missing");
+
+ let input = AppendInput {
+ records: create_test_record_batch(vec![Bytes::from_static(b"hello")]),
+ match_seq_num: None,
+ fencing_token: None,
+ };
+ let ack = backend
+ .open_for_append(&basin_name, &stream_name, None, requested_stream_config())
+ .await
+ .expect("Failed to open append handle")
+ .append(input)
+ .await
+ .expect("Failed to append to auto-created stream");
+ assert_eq!(ack.end.seq_num, 1);
+
+ let config = backend
+ .get_stream_config(basin_name.clone(), stream_name.clone())
+ .await
+ .expect("Failed to get stream config");
+ assert_eq!(config, expected_merged_stream_config());
+}
+
+#[tokio::test]
+async fn test_backend_append_ignores_stream_config_for_existing_stream() {
+ let backend = create_backend().await;
+ let basin_name = create_test_basin(
+ &backend,
+ "backend-auto-create-config-existing",
+ basin_config_with_defaults(),
+ )
+ .await;
+ let stream_name = create_test_stream(
+ &backend,
+ &basin_name,
+ "existing",
+ OptionalStreamConfig::default(),
+ )
+ .await;
+ let before = backend
+ .get_stream_config(basin_name.clone(), stream_name.clone())
+ .await
+ .expect("Failed to get stream config");
+
+ let input = AppendInput {
+ records: create_test_record_batch(vec![Bytes::from_static(b"hello")]),
+ match_seq_num: None,
+ fencing_token: None,
+ };
+ backend
+ .open_for_append(&basin_name, &stream_name, None, requested_stream_config())
+ .await
+ .expect("Failed to open append handle")
+ .append(input)
+ .await
+ .expect("Failed to append to existing stream");
+
+ let after = backend
+ .get_stream_config(basin_name.clone(), stream_name.clone())
+ .await
+ .expect("Failed to get stream config");
+ assert_eq!(after, before);
+ assert_ne!(after, expected_merged_stream_config());
+}
+
+#[tokio::test]
+async fn test_backend_read_auto_create_applies_stream_config() {
+ let backend = create_backend().await;
+ let basin_name = create_test_basin(
+ &backend,
+ "backend-auto-create-read-config",
+ BasinConfig {
+ create_stream_on_append: false,
+ create_stream_on_read: true,
+ ..basin_config_with_defaults()
+ },
+ )
+ .await;
+ let stream_name = test_stream_name("missing");
+
+ backend
+ .open_for_read(&basin_name, &stream_name, None, requested_stream_config())
+ .await
+ .expect("Failed to open read handle on auto-created stream");
+
+ let config = backend
+ .get_stream_config(basin_name.clone(), stream_name.clone())
+ .await
+ .expect("Failed to get stream config");
+ assert_eq!(config, expected_merged_stream_config());
+}
+
#[tokio::test]
async fn test_backend_append_without_auto_create_returns_not_found() {
let backend = create_backend().await;
diff --git a/sdk/src/api.rs b/sdk/src/api.rs
index f412eaec..f67f4b99 100644
--- a/sdk/src/api.rs
+++ b/sdk/src/api.rs
@@ -18,7 +18,10 @@ use s2_api::v1::{
basin::{
BasinInfo, CreateBasinRequest, EnsureBasinRequest, ListBasinsRequest, ListBasinsResponse,
},
- config::{BasinConfig, BasinReconfiguration, StreamConfig, StreamReconfiguration},
+ config::{
+ BasinConfig, BasinReconfiguration, STREAM_CONFIG_HEADER, StreamConfig,
+ StreamReconfiguration,
+ },
location::LocationInfo,
metrics::{
AccountMetricSetRequest, BasinMetricSetRequest, MetricSetResponse, StreamMetricSetRequest,
@@ -384,6 +387,7 @@ impl BasinClient {
name: &StreamName,
input: AppendInput,
encryption: Option<&EncryptionKey>,
+ stream_config: Option<&StreamConfig>,
append_retry_policy: AppendRetryPolicy,
) -> Result {
let url = self.uri(format!("v1/streams/{}/records", urlencoding::encode(name)));
@@ -394,6 +398,7 @@ impl BasinClient {
.body(input.encode_to_vec())
.build()?;
set_encryption_header(&mut request, encryption);
+ set_stream_config_header(&mut request, stream_config);
let response = self
.request(request)
.with_append_retry_policy(append_retry_policy)
@@ -420,6 +425,7 @@ impl BasinClient {
start: ReadStart,
end: ReadEnd,
encryption: Option<&EncryptionKey>,
+ stream_config: Option<&StreamConfig>,
) -> Result {
let url = self.uri(format!("v1/streams/{}/records", urlencoding::encode(name)));
let mut builder = self
@@ -433,6 +439,7 @@ impl BasinClient {
}
let mut request = builder.build()?;
set_encryption_header(&mut request, encryption);
+ set_stream_config_header(&mut request, stream_config);
let response = self
.request(request)
.error_handler(read_response_error_handler)
@@ -446,6 +453,7 @@ impl BasinClient {
name: &StreamName,
inputs: I,
encryption: Option<&EncryptionKey>,
+ stream_config: Option<&StreamConfig>,
frame_signal: Option,
reconnect: ReconnectAdvice,
) -> Result, ApiError>
@@ -476,6 +484,7 @@ impl BasinClient {
add_basin_header_if_required(request_builder, &self.config.endpoints, &self.name);
let mut request = request_builder.build()?;
set_encryption_header(&mut request, encryption);
+ set_stream_config_header(&mut request, stream_config);
let (response, access_token) = self.client.init_streaming_authorized(request).await?;
let response = match response.into_result().await {
Ok(response) => response,
@@ -538,6 +547,7 @@ impl BasinClient {
start: ReadStart,
end: ReadEnd,
encryption: Option<&EncryptionKey>,
+ stream_config: Option<&StreamConfig>,
reconnect: ReconnectAdvice,
) -> Result, ApiError> {
let url = self.uri(format!("v1/streams/{}/records", urlencoding::encode(name)));
@@ -553,6 +563,7 @@ impl BasinClient {
add_basin_header_if_required(request_builder, &self.config.endpoints, &self.name);
let mut request = request_builder.build()?;
set_encryption_header(&mut request, encryption);
+ set_stream_config_header(&mut request, stream_config);
let (response, access_token) = self.client.init_streaming_authorized(request).await?;
let response = match response.into_result().await {
Ok(response) => response,
@@ -968,6 +979,14 @@ fn set_encryption_header(request: &mut client::Request, encryption: Option<&Encr
}
}
+fn set_stream_config_header(request: &mut client::Request, stream_config: Option<&StreamConfig>) {
+ if let Some(config) = stream_config {
+ request
+ .headers_mut()
+ .insert(STREAM_CONFIG_HEADER.clone(), config.to_header_value());
+ }
+}
+
pub fn retry_builder(config: &RetryConfig) -> RetryBackoffBuilder {
RetryBackoffBuilder::default()
.with_min_base_delay(config.min_base_delay)
@@ -1396,6 +1415,7 @@ mod tests {
wait: None
},
None,
+ None,
ReconnectAdvice::default(),
)
.await
@@ -1408,6 +1428,7 @@ mod tests {
futures_util::stream::empty(),
None,
None,
+ None,
ReconnectAdvice::default(),
)
.await
diff --git a/sdk/src/batching.rs b/sdk/src/batching.rs
index 00086f1f..eac2c5ab 100644
--- a/sdk/src/batching.rs
+++ b/sdk/src/batching.rs
@@ -174,6 +174,7 @@ impl Stream for AppendInputs {
records: batch,
match_seq_num,
fencing_token: self.fencing_token.clone(),
+ stream_config: None,
})))
}
Poll::Ready(Some(Err(err))) => Poll::Ready(Some(Err(err))),
diff --git a/sdk/src/ops.rs b/sdk/src/ops.rs
index be287bec..db339709 100644
--- a/sdk/src/ops.rs
+++ b/sdk/src/ops.rs
@@ -4,7 +4,9 @@ use crate::{
api::{AccountClient, BaseClient, BasinClient},
error::{AppendError, ReadError, RequestError},
producer::{Producer, ProducerConfig},
- session::{self, AppendSession, AppendSessionConfig, ReadSession, ReadSessionError},
+ session::{
+ self, AppendSession, AppendSessionConfig, ReadSession, ReadSessionError, StreamHeaders,
+ },
types::{
AccessTokenId, AccessTokenInfo, AppendAck, AppendInput, BasinConfig, BasinInfo, BasinName,
CreateBasinInput, CreateStreamInput, DeleteBasinInput, DeleteStreamInput, EncryptionKey,
@@ -437,6 +439,13 @@ impl S2Stream {
}
}
+ fn headers(&self, stream_config: Option<&StreamConfig>) -> StreamHeaders {
+ StreamHeaders {
+ encryption: self.encryption.clone(),
+ stream_config: stream_config.cloned().map(Into::into),
+ }
+ }
+
/// Check tail position.
pub async fn check_tail(&self) -> Result {
let response = self.client.check_tail(&self.name).await?;
@@ -444,13 +453,18 @@ impl S2Stream {
}
/// Append records.
- pub async fn append(&self, input: AppendInput) -> Result {
+ pub async fn append(&self, mut input: AppendInput) -> Result {
+ let stream_config = input
+ .stream_config
+ .take()
+ .map(s2_api::v1::config::StreamConfig::from);
let ack = self
.client
.append(
&self.name,
input.into(),
self.encryption.as_ref(),
+ stream_config.as_ref(),
self.client.config.retry.append_retry_policy,
)
.await?;
@@ -459,6 +473,9 @@ impl S2Stream {
/// Read records.
pub async fn read(&self, input: ReadInput) -> Result {
+ let stream_config = input
+ .stream_config
+ .map(s2_api::v1::config::StreamConfig::from);
let batch = self
.client
.read(
@@ -466,6 +483,7 @@ impl S2Stream {
input.start.into(),
input.stop.into(),
self.encryption.as_ref(),
+ stream_config.as_ref(),
)
.await?;
let mut batch = ReadBatch::from_api(batch);
@@ -480,7 +498,7 @@ impl S2Stream {
AppendSession::new(
self.client.clone(),
self.name.clone(),
- self.encryption.clone(),
+ self.headers(config.stream_config()),
config,
)
}
@@ -490,7 +508,7 @@ impl S2Stream {
Producer::new(
self.client.clone(),
self.name.clone(),
- self.encryption.clone(),
+ self.headers(config.stream_config()),
config,
)
}
@@ -504,7 +522,7 @@ impl S2Stream {
session::read_session(
self.client.clone(),
self.name.clone(),
- self.encryption.clone(),
+ self.headers(input.stream_config.as_ref()),
input,
config,
)
diff --git a/sdk/src/producer.rs b/sdk/src/producer.rs
index 632c31b5..293f774a 100644
--- a/sdk/src/producer.rs
+++ b/sdk/src/producer.rs
@@ -20,9 +20,11 @@ use crate::{
api::BasinClient,
batching::{AppendInputs, AppendRecordBatches, BatchingConfig},
error::ProducerError,
- session::{AppendPermit, AppendPermits, AppendSessionInternal, BatchSubmitTicket},
+ session::{
+ AppendPermit, AppendPermits, AppendSessionInternal, BatchSubmitTicket, StreamHeaders,
+ },
types::{
- AppendAck, AppendRecord, EncryptionKey, FencingToken, MeteredBytes, ONE_MIB, StreamName,
+ AppendAck, AppendRecord, FencingToken, MeteredBytes, ONE_MIB, StreamConfig, StreamName,
ValidationError,
},
};
@@ -75,6 +77,7 @@ pub struct ProducerConfig {
batching: BatchingConfig,
fencing_token: Option,
match_seq_num: Option,
+ stream_config: Option,
}
impl Default for ProducerConfig {
@@ -84,6 +87,7 @@ impl Default for ProducerConfig {
batching: BatchingConfig::default(),
fencing_token: None,
match_seq_num: None,
+ stream_config: None,
}
}
}
@@ -137,6 +141,23 @@ impl ProducerConfig {
..self
}
}
+
+ /// Set the stream configuration to apply if the stream is created on append.
+ ///
+ /// Unset fields inherit the basin's default stream configuration. Ignored if the stream
+ /// already exists.
+ ///
+ /// Defaults to `None`.
+ pub fn with_stream_config(self, stream_config: StreamConfig) -> Self {
+ Self {
+ stream_config: Some(stream_config),
+ ..self
+ }
+ }
+
+ pub(crate) fn stream_config(&self) -> Option<&StreamConfig> {
+ self.stream_config.as_ref()
+ }
}
/// High-level interface for submitting individual [`AppendRecord`]s.
@@ -154,12 +175,12 @@ impl Producer {
pub(crate) fn new(
client: BasinClient,
stream: StreamName,
- encryption: Option,
+ headers: StreamHeaders,
config: ProducerConfig,
) -> Self {
let (cmd_tx, cmd_rx) = mpsc::channel::(RECORD_BATCH_MAX.count);
let permits = AppendPermits::new(None, config.max_unacked_bytes);
- let session = AppendSessionInternal::new(client, stream, encryption);
+ let session = AppendSessionInternal::new(client, stream, headers);
let terminal_err = Arc::new(OnceLock::new());
let _handle = AbortOnDropHandle::new(tokio::spawn(Self::run(
session,
diff --git a/sdk/src/session/append.rs b/sdk/src/session/append.rs
index ea1f90fa..74f53641 100644
--- a/sdk/src/session/append.rs
+++ b/sdk/src/session/append.rs
@@ -24,9 +24,10 @@ use crate::{
frame_signal::FrameSignal,
reconnect::{AdvisedReconnects, ReconnectAdvice},
retry::RetryBackoffBuilder,
+ session::StreamHeaders,
types::{
- AccessTokenMode, AppendAck, AppendInput, AppendRetryPolicy, EncryptionKey, MeteredBytes,
- ONE_MIB, StreamName, StreamPosition, ValidationError,
+ AccessTokenMode, AppendAck, AppendInput, AppendRetryPolicy, MeteredBytes, ONE_MIB,
+ StreamConfig, StreamName, StreamPosition, ValidationError,
},
};
@@ -154,6 +155,7 @@ impl Future for BatchSubmitTicket {
pub struct AppendSessionConfig {
max_unacked_bytes: u32,
max_unacked_batches: Option,
+ stream_config: Option,
}
impl Default for AppendSessionConfig {
@@ -161,6 +163,7 @@ impl Default for AppendSessionConfig {
Self {
max_unacked_bytes: 5 * ONE_MIB,
max_unacked_batches: None,
+ stream_config: None,
}
}
}
@@ -195,6 +198,23 @@ impl AppendSessionConfig {
..self
}
}
+
+ /// Set the stream configuration to apply if the stream is created on append.
+ ///
+ /// Unset fields inherit the basin's default stream configuration. Ignored if the stream
+ /// already exists.
+ ///
+ /// Defaults to `None`.
+ pub fn with_stream_config(self, stream_config: StreamConfig) -> Self {
+ Self {
+ stream_config: Some(stream_config),
+ ..self
+ }
+ }
+
+ pub(crate) fn stream_config(&self) -> Option<&StreamConfig> {
+ self.stream_config.as_ref()
+ }
}
struct SessionState {
@@ -231,7 +251,7 @@ impl AppendSession {
pub(crate) fn new(
client: BasinClient,
stream: StreamName,
- encryption: Option,
+ headers: StreamHeaders,
config: AppendSessionConfig,
) -> Self {
let buffer_size = config
@@ -245,7 +265,7 @@ impl AppendSession {
let handle = AbortOnDropHandle::new(tokio::spawn(run_session_with_retry(
client,
stream,
- encryption,
+ headers,
cmd_rx,
retry_builder,
buffer_size,
@@ -356,11 +376,7 @@ pub(crate) struct AppendSessionInternal {
}
impl AppendSessionInternal {
- pub(crate) fn new(
- client: BasinClient,
- stream: StreamName,
- encryption: Option,
- ) -> Self {
+ pub(crate) fn new(client: BasinClient, stream: StreamName, headers: StreamHeaders) -> Self {
let buffer_size = DEFAULT_CHANNEL_BUFFER_SIZE;
let (cmd_tx, cmd_rx) = mpsc::channel(buffer_size);
let retry_builder = retry_builder(&client.config.retry);
@@ -368,7 +384,7 @@ impl AppendSessionInternal {
let handle = AbortOnDropHandle::new(tokio::spawn(run_session_with_retry(
client,
stream,
- encryption,
+ headers,
cmd_rx,
retry_builder,
buffer_size,
@@ -473,7 +489,7 @@ impl AppendPermits {
async fn run_session_with_retry(
client: BasinClient,
stream: StreamName,
- encryption: Option,
+ headers: StreamHeaders,
cmd_rx: mpsc::Receiver,
retry_builder: RetryBackoffBuilder,
buffer_size: usize,
@@ -503,7 +519,7 @@ async fn run_session_with_retry(
let result = run_session(
&client,
&stream,
- encryption.as_ref(),
+ &headers,
&mut state,
buffer_size,
&frame_signal,
@@ -604,7 +620,7 @@ enum SessionOutcome {
async fn run_session(
client: &BasinClient,
stream: &StreamName,
- encryption: Option<&EncryptionKey>,
+ headers: &StreamHeaders,
state: &mut SessionState,
buffer_size: usize,
frame_signal: &Option,
@@ -618,7 +634,7 @@ async fn run_session(
let (input_tx, mut acks) = connect(
client,
stream,
- encryption,
+ headers,
buffer_size,
frame_signal.clone(),
reconnect.clone(),
@@ -888,7 +904,7 @@ async fn drain_for_reconnect(
async fn connect(
client: &BasinClient,
stream: &StreamName,
- encryption: Option<&EncryptionKey>,
+ headers: &StreamHeaders,
buffer_size: usize,
frame_signal: Option,
reconnect: ReconnectAdvice,
@@ -899,7 +915,8 @@ async fn connect(
.append_session(
stream,
ReceiverStream::new(input_rx).map(|i| i.into()),
- encryption,
+ headers.encryption.as_ref(),
+ headers.stream_config.as_ref(),
frame_signal,
reconnect,
)
diff --git a/sdk/src/session/mod.rs b/sdk/src/session/mod.rs
index 107df6e4..7868ed29 100644
--- a/sdk/src/session/mod.rs
+++ b/sdk/src/session/mod.rs
@@ -5,3 +5,12 @@ pub(crate) use append::{AppendPermit, AppendPermits, AppendSessionInternal, Batc
pub use append::{AppendSession, AppendSessionConfig};
pub(crate) use read::read_session;
pub use read::{ReadSession, ReadSessionError};
+
+/// Per-stream options sent as request headers on every (re)connect of a session.
+#[derive(Debug, Clone, Default)]
+pub(crate) struct StreamHeaders {
+ /// `s2-encryption-key`
+ pub encryption: Option,
+ /// `s2-stream-config`
+ pub stream_config: Option,
+}
diff --git a/sdk/src/session/read.rs b/sdk/src/session/read.rs
index a3a07e99..83f848f4 100644
--- a/sdk/src/session/read.rs
+++ b/sdk/src/session/read.rs
@@ -22,8 +22,9 @@ use crate::{
error::{ReadError, RequestError},
reconnect::{AdvisedReconnects, ReconnectAdvice},
retry::RetryBackoff,
+ session::StreamHeaders,
types::{
- AccessTokenMode, EncryptionKey, MeteredBytes, ReadBatch, ReadInput, ReadSessionConfig,
+ AccessTokenMode, MeteredBytes, ReadBatch, ReadInput, ReadSessionConfig,
ReadSessionRetryPolicy, StreamName, StreamPosition,
},
};
@@ -371,7 +372,7 @@ impl Drop for ReadSession {
pub async fn read_session(
client: BasinClient,
name: StreamName,
- encryption: Option,
+ headers: StreamHeaders,
input: ReadInput,
config: ReadSessionConfig,
) -> Result {
@@ -379,6 +380,7 @@ pub async fn read_session(
start,
stop,
ignore_command_records,
+ stream_config: _,
} = input;
let mut start: ReadStart = start.into();
let mut end: ReadEnd = stop.into();
@@ -399,7 +401,7 @@ pub async fn read_session(
match session_inner(
client.clone(),
name.clone(),
- encryption.clone(),
+ headers.clone(),
start.clone(),
end.clone(),
ReconnectAdvice::default(),
@@ -439,7 +441,7 @@ pub async fn read_session(
match session_inner(
client.clone(),
name.clone(),
- encryption.clone(),
+ headers.clone(),
start.clone(),
end.clone(),
ReconnectAdvice::default(),
@@ -579,14 +581,21 @@ fn update_resume_start(start: &mut ReadStart, batch: &ReadBatch) {
async fn session_inner(
client: BasinClient,
name: StreamName,
- encryption: Option,
+ headers: StreamHeaders,
start: ReadStart,
end: ReadEnd,
reconnect: ReconnectAdvice,
advised_reconnects: AdvisedReconnects,
) -> Result, ReadSessionFailure> {
let mut batches = client
- .read_session(&name, start, end, encryption.as_ref(), reconnect.clone())
+ .read_session(
+ &name,
+ start,
+ end,
+ headers.encryption.as_ref(),
+ headers.stream_config.as_ref(),
+ reconnect.clone(),
+ )
.await?;
let mut declined_advice = false;
diff --git a/sdk/src/types.rs b/sdk/src/types.rs
index 9f2d6631..a66f8c44 100644
--- a/sdk/src/types.rs
+++ b/sdk/src/types.rs
@@ -3411,6 +3411,16 @@ pub struct AppendInput {
/// If unspecified, no matching is performed. If specified and mismatched,
/// the append fails. A stream defaults to `""` as its fencing token.
pub fencing_token: Option,
+ /// Stream configuration to apply if the stream is created on append.
+ ///
+ /// Unset fields inherit the basin's default stream configuration. Ignored if the stream
+ /// already exists.
+ ///
+ /// Only used by [`append`](crate::S2Stream::append). Append sessions send the header once
+ /// at connect; see
+ /// [`AppendSessionConfig::with_stream_config`](crate::append_session::AppendSessionConfig::with_stream_config)
+ /// and [`ProducerConfig::with_stream_config`](crate::producer::ProducerConfig::with_stream_config).
+ pub stream_config: Option,
}
impl AppendInput {
@@ -3420,6 +3430,15 @@ impl AppendInput {
records,
match_seq_num: None,
fencing_token: None,
+ stream_config: None,
+ }
+ }
+
+ /// Set the stream configuration to apply if the stream is created on append.
+ pub fn with_stream_config(self, stream_config: StreamConfig) -> Self {
+ Self {
+ stream_config: Some(stream_config),
+ ..self
}
}
@@ -3676,6 +3695,11 @@ pub struct ReadInput {
///
/// Defaults to `false`.
pub ignore_command_records: bool,
+ /// Stream configuration to apply if the stream is created on read.
+ ///
+ /// Unset fields inherit the basin's default stream configuration. Ignored if the stream
+ /// already exists.
+ pub stream_config: Option,
}
#[derive(Debug, Clone, Copy, Default, Eq, PartialEq)]
@@ -3743,6 +3767,14 @@ impl ReadInput {
..self
}
}
+
+ /// Set the stream configuration to apply if the stream is created on read.
+ pub fn with_stream_config(self, stream_config: StreamConfig) -> Self {
+ Self {
+ stream_config: Some(stream_config),
+ ..self
+ }
+ }
}
#[derive(Debug, Clone)]
diff --git a/sdk/tests/stream_ops.rs b/sdk/tests/stream_ops.rs
index 54d81180..3a953f4f 100644
--- a/sdk/tests/stream_ops.rs
+++ b/sdk/tests/stream_ops.rs
@@ -3,7 +3,7 @@ mod common;
use std::time::Duration;
use assert_matches::assert_matches;
-use common::{S2Stream, SharedS2Basin, s2_config, unique_basin_name, unique_stream_name};
+use common::{S2Stream, SharedS2Basin, s2, s2_config, unique_basin_name, unique_stream_name};
use futures_util::{StreamExt, poll};
use rstest::rstest;
use s2_sdk::{
@@ -2211,3 +2211,118 @@ async fn producer_for_non_existent_stream_errors(
Ok(())
}
+
+#[tokio::test]
+async fn stream_config_applies_only_when_append_creates_stream()
+-> Result<(), Box> {
+ let s2 = s2();
+ let basin_name = unique_basin_name();
+ s2.create_basin(
+ CreateBasinInput::new(basin_name.clone()).with_config(
+ BasinConfig::new()
+ .with_create_stream_on_append(true)
+ .with_default_stream_config(
+ StreamConfig::new().with_storage_class(StorageClass::Standard),
+ ),
+ ),
+ )
+ .await?;
+ let basin = s2.basin(basin_name.clone());
+
+ let stream_config = StreamConfig::new()
+ .with_retention_policy(RetentionPolicy::Age(3600))
+ .with_delete_on_empty(DeleteOnEmptyConfig::new().with_min_age(Duration::from_secs(300)));
+
+ let unary_stream = unique_stream_name();
+ basin
+ .stream(unary_stream.clone())
+ .append(
+ AppendInput::new(AppendRecordBatch::try_from_iter([AppendRecord::new(
+ "hello",
+ )?])?)
+ .with_stream_config(stream_config.clone()),
+ )
+ .await?;
+ let config = basin.get_stream_config(unary_stream).await?;
+ assert_matches!(
+ config,
+ StreamConfig {
+ storage_class: Some(StorageClass::Standard),
+ retention_policy: Some(RetentionPolicy::Age(3600)),
+ delete_on_empty: Some(DeleteOnEmptyConfig {
+ min_age_secs: 300,
+ ..
+ }),
+ ..
+ }
+ );
+
+ let session_stream = unique_stream_name();
+ let producer = basin
+ .stream(session_stream.clone())
+ .producer(ProducerConfig::new().with_stream_config(stream_config.clone()));
+ producer.submit(AppendRecord::new("hello")?).await?.await?;
+ producer.close().await?;
+ let config = basin.get_stream_config(session_stream).await?;
+ assert_matches!(
+ config,
+ StreamConfig {
+ retention_policy: Some(RetentionPolicy::Age(3600)),
+ delete_on_empty: Some(DeleteOnEmptyConfig {
+ min_age_secs: 300,
+ ..
+ }),
+ ..
+ }
+ );
+
+ let existing_stream = unique_stream_name();
+ basin
+ .create_stream(CreateStreamInput::new(existing_stream.clone()))
+ .await?;
+ let before = basin.get_stream_config(existing_stream.clone()).await?;
+ basin
+ .stream(existing_stream.clone())
+ .append(
+ AppendInput::new(AppendRecordBatch::try_from_iter([AppendRecord::new(
+ "hello",
+ )?])?)
+ .with_stream_config(stream_config),
+ )
+ .await?;
+ let after = basin.get_stream_config(existing_stream).await?;
+ assert_eq!(after, before);
+ assert_ne!(after.retention_policy, Some(RetentionPolicy::Age(3600)));
+
+ s2.delete_basin(DeleteBasinInput::new(basin_name)).await?;
+ Ok(())
+}
+
+#[tokio::test]
+async fn stream_config_applies_when_read_creates_stream() -> Result<(), Box>
+{
+ let s2 = s2();
+ let basin_name = unique_basin_name();
+ s2.create_basin(
+ CreateBasinInput::new(basin_name.clone())
+ .with_config(BasinConfig::new().with_create_stream_on_read(true)),
+ )
+ .await?;
+ let basin = s2.basin(basin_name.clone());
+
+ let stream_config = StreamConfig::new().with_retention_policy(RetentionPolicy::Age(3600));
+
+ let session_stream = unique_stream_name();
+ let _session = basin
+ .stream(session_stream.clone())
+ .read_session(
+ ReadInput::new().with_stream_config(stream_config),
+ ReadSessionConfig::default(),
+ )
+ .await?;
+ let config = basin.get_stream_config(session_stream).await?;
+ assert_eq!(config.retention_policy, Some(RetentionPolicy::Age(3600)));
+
+ s2.delete_basin(DeleteBasinInput::new(basin_name)).await?;
+ Ok(())
+}
From 1a0312afa097743f6d9ba686f8ee1c064880ab73 Mon Sep 17 00:00:00 2001
From: "release-pleaze[bot]"
<262023388+release-pleaze[bot]@users.noreply.github.com>
Date: Fri, 11 Sep 2026 21:12:01 +0530
Subject: [PATCH 08/41] chore: release (#733)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## 🤖 New release
* `s2-api`: 0.31.2 -> 0.31.3 (✓ API compatible changes)
* `s2-lite`: 0.42.10 -> 0.42.11 (✓ API compatible changes)
* `s2-sdk`: 0.34.6 -> 0.34.7 (✓ API compatible changes)
* `s2-cli`: 0.42.10 -> 0.42.11
* `s2-testcontainers`: 0.42.10 -> 0.42.11
Changelog
## `s2-api`
## [0.31.3] - 2026-09-11
### Features
- `s2-stream-config` header for auto-created streams
([#718](https://github.com/s2-streamstore/s2/issues/718))
## `s2-lite`
## [0.42.11] - 2026-09-11
### Features
- `s2-stream-config` header for auto-created streams
([#718](https://github.com/s2-streamstore/s2/issues/718))
## `s2-sdk`
## [0.34.7] - 2026-09-11
### Features
- `s2-stream-config` header for auto-created streams
([#718](https://github.com/s2-streamstore/s2/issues/718))
## `s2-cli`
## [0.42.11] - 2026-09-11
### Features
- `s2-stream-config` header for auto-created streams
([#718](https://github.com/s2-streamstore/s2/issues/718))
## `s2-testcontainers`
## [0.42.11] - 2026-09-11
---
This PR was generated with
[release-plz](https://github.com/release-plz/release-plz/).
Co-authored-by: release-pleaze[bot] <262023388+release-pleaze[bot]@users.noreply.github.com>
---
Cargo.lock | 10 +++++-----
api/CHANGELOG.md | 8 ++++++++
api/Cargo.toml | 2 +-
cli/CHANGELOG.md | 8 ++++++++
cli/Cargo.toml | 2 +-
lite/CHANGELOG.md | 8 ++++++++
lite/Cargo.toml | 2 +-
sdk/CHANGELOG.md | 8 ++++++++
sdk/Cargo.toml | 2 +-
testcontainers/CHANGELOG.md | 4 ++++
testcontainers/Cargo.toml | 2 +-
11 files changed, 46 insertions(+), 10 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index 7ffc34a3..9a5b2d9f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4878,7 +4878,7 @@ checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
[[package]]
name = "s2-api"
-version = "0.31.2"
+version = "0.31.3"
dependencies = [
"axum",
"base64ct",
@@ -4908,7 +4908,7 @@ dependencies = [
[[package]]
name = "s2-cli"
-version = "0.42.10"
+version = "0.42.11"
dependencies = [
"assert_cmd",
"async-stream",
@@ -4992,7 +4992,7 @@ dependencies = [
[[package]]
name = "s2-lite"
-version = "0.42.10"
+version = "0.42.11"
dependencies = [
"async-stream",
"async-trait",
@@ -5051,7 +5051,7 @@ dependencies = [
[[package]]
name = "s2-sdk"
-version = "0.34.6"
+version = "0.34.7"
dependencies = [
"assert_matches",
"async-compression",
@@ -5111,7 +5111,7 @@ dependencies = [
[[package]]
name = "s2-testcontainers"
-version = "0.42.10"
+version = "0.42.11"
dependencies = [
"reqwest",
"s2-sdk",
diff --git a/api/CHANGELOG.md b/api/CHANGELOG.md
index 91342822..70473d74 100644
--- a/api/CHANGELOG.md
+++ b/api/CHANGELOG.md
@@ -2,6 +2,14 @@
All notable changes to this project will be documented in this file.
+## [0.31.3] - 2026-09-11
+
+### Features
+
+- `s2-stream-config` header for auto-created streams ([#718](https://github.com/s2-streamstore/s2/issues/718))
+
+
+
## [0.31.2] - 2026-09-10
### Bug Fixes
diff --git a/api/Cargo.toml b/api/Cargo.toml
index f239cbbc..3615870a 100644
--- a/api/Cargo.toml
+++ b/api/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "s2-api"
-version = "0.31.2"
+version = "0.31.3"
description = "API types for S2, the durable streams API"
edition.workspace = true
license.workspace = true
diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md
index e3577529..9f50daa7 100644
--- a/cli/CHANGELOG.md
+++ b/cli/CHANGELOG.md
@@ -2,6 +2,14 @@
All notable changes to this project will be documented in this file.
+## [0.42.11] - 2026-09-11
+
+### Features
+
+- `s2-stream-config` header for auto-created streams ([#718](https://github.com/s2-streamstore/s2/issues/718))
+
+
+
## [0.42.10] - 2026-09-11
diff --git a/cli/Cargo.toml b/cli/Cargo.toml
index e5922e1a..7904e85d 100644
--- a/cli/Cargo.toml
+++ b/cli/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "s2-cli"
-version = "0.42.10"
+version = "0.42.11"
description = "CLI for S2"
edition.workspace = true
license.workspace = true
diff --git a/lite/CHANGELOG.md b/lite/CHANGELOG.md
index 214caca7..9a55a37e 100644
--- a/lite/CHANGELOG.md
+++ b/lite/CHANGELOG.md
@@ -2,6 +2,14 @@
All notable changes to this project will be documented in this file.
+## [0.42.11] - 2026-09-11
+
+### Features
+
+- `s2-stream-config` header for auto-created streams ([#718](https://github.com/s2-streamstore/s2/issues/718))
+
+
+
## [0.42.10] - 2026-09-11
### Miscellaneous Tasks
diff --git a/lite/Cargo.toml b/lite/Cargo.toml
index 72013081..9f6790cb 100644
--- a/lite/Cargo.toml
+++ b/lite/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "s2-lite"
-version = "0.42.10"
+version = "0.42.11"
description = "Lightweight server implementation of S2, the durable streams API, backed by object storage"
edition.workspace = true
license.workspace = true
diff --git a/sdk/CHANGELOG.md b/sdk/CHANGELOG.md
index d2d6b1ec..bf935a06 100644
--- a/sdk/CHANGELOG.md
+++ b/sdk/CHANGELOG.md
@@ -2,6 +2,14 @@
All notable changes to this project will be documented in this file.
+## [0.34.7] - 2026-09-11
+
+### Features
+
+- `s2-stream-config` header for auto-created streams ([#718](https://github.com/s2-streamstore/s2/issues/718))
+
+
+
## [0.34.6] - 2026-09-11
### Features
diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml
index aa3665ae..94dfb1e6 100644
--- a/sdk/Cargo.toml
+++ b/sdk/Cargo.toml
@@ -1,7 +1,7 @@
[package]
name = "s2-sdk"
description = "Rust SDK for S2"
-version = "0.34.6"
+version = "0.34.7"
edition.workspace = true
license.workspace = true
repository = "https://github.com/s2-streamstore/s2/tree/main/sdk"
diff --git a/testcontainers/CHANGELOG.md b/testcontainers/CHANGELOG.md
index 0fa9bdf8..2fa76cdc 100644
--- a/testcontainers/CHANGELOG.md
+++ b/testcontainers/CHANGELOG.md
@@ -2,6 +2,10 @@
All notable changes to this project will be documented in this file.
+## [0.42.11] - 2026-09-11
+
+
+
## [0.42.10] - 2026-09-11
diff --git a/testcontainers/Cargo.toml b/testcontainers/Cargo.toml
index e955d07f..68a46e6b 100644
--- a/testcontainers/Cargo.toml
+++ b/testcontainers/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "s2-testcontainers"
-version = "0.42.10"
+version = "0.42.11"
description = "Testcontainers helpers for the S2 Docker image"
edition.workspace = true
license.workspace = true
From c364f506eeb9feffc1ea24cb2add6d9f19d87fcc Mon Sep 17 00:00:00 2001
From: "release-pleaze[bot]"
Date: Fri, 11 Sep 2026 16:19:31 +0000
Subject: [PATCH 09/41] Bump s2-lite-helm chart to appVersion 0.42.11
---
charts/s2-lite-helm/Chart.yaml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/charts/s2-lite-helm/Chart.yaml b/charts/s2-lite-helm/Chart.yaml
index 25315647..8481fb0f 100644
--- a/charts/s2-lite-helm/Chart.yaml
+++ b/charts/s2-lite-helm/Chart.yaml
@@ -2,8 +2,8 @@ apiVersion: v2
name: s2-lite-helm
description: Self-hostable S2 streaming datastore using SlateDB on object storage
type: application
-version: 0.1.66
-appVersion: "0.42.10"
+version: 0.1.67
+appVersion: "0.42.11"
keywords:
- s2
- streaming
From 09b57bf77f154d391431e7c0bc05e72730ae1e86 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Sat, 12 Sep 2026 02:24:09 +0530
Subject: [PATCH 10/41] chore: sync specs submodule (#739)
This PR updates the following submodules:
| **Remote Repository** | **Submodule Path** | **Change** |
| --- | --- | --- |
|
[s2-streamstore/s2-specs](https://github.com/s2-streamstore/s2-specs.git)
| api/specs |
[f29cbca...edaa1fb](https://github.com/s2-streamstore/s2-specs/compare/f29cbcaafeaa3d4970918a9a31f9e00b55fdabb4...edaa1fbcb2507362d6c680c66cdc88660e73e036)
|
---
This PR description was generated by
[sgoudham/update-git-submodules](https://github.com/sgoudham/update-git-submodules).
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
---
api/specs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/api/specs b/api/specs
index f29cbcaa..edaa1fbc 160000
--- a/api/specs
+++ b/api/specs
@@ -1 +1 @@
-Subproject commit f29cbcaafeaa3d4970918a9a31f9e00b55fdabb4
+Subproject commit edaa1fbcb2507362d6c680c66cdc88660e73e036
From a271f7d9c4a185e4d0d4e73101f81980d7273737 Mon Sep 17 00:00:00 2001
From: "devin-ai-integration[bot]"
<158243242+devin-ai-integration[bot]@users.noreply.github.com>
Date: Fri, 11 Sep 2026 14:11:47 -0700
Subject: [PATCH 11/41] fix(sdk): reject Content-Type in default headers (#740)
## Summary
Reject `Content-Type` in `S2Config::with_default_headers`, alongside the
existing encoding/framing restrictions. The SDK chooses the protocol for
each operation: a default `Content-Type: s2s/proto` can make a unary
read receive streaming frames, causing a decode error or timeout.
This addresses #737 at configuration validation, as an alternative to
the per-read override in #738. All `Content-Type` values are rejected
because request format belongs to the SDK.
Extend the existing rejection tests to cover S2S, protobuf, JSON,
mixed-case header names, and empty values. Remove the now-invalid
Content-Type default from the header propagation fixture while
preserving its assertions.
Validation: the five new cases failed before the fix. `just test` passes
all 796 workspace tests; SDK clippy with all features/targets and `just
fmt` also pass.
Closes #737
Link to Devin session:
https://app.devin.ai/sessions/c3dea6e292ce4071a41baf4943f1557c
Open in Devin Desktop:
https://app.devin.ai/desktop/session/c3dea6e292ce4071a41baf4943f1557c?variant=devin
Requested by: @sgbalogh
Co-authored-by: Stephen Balogh
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
---
sdk/src/api.rs | 1 -
sdk/src/types.rs | 13 ++++++++++---
2 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/sdk/src/api.rs b/sdk/src/api.rs
index f67f4b99..3a72e173 100644
--- a/sdk/src/api.rs
+++ b/sdk/src/api.rs
@@ -1364,7 +1364,6 @@ mod tests {
http::header::HeaderName::from_static(S2_BASIN),
HeaderValue::from_static("wrong-basin"),
),
- (CONTENT_TYPE, HeaderValue::from_static("wrong-content-type")),
]);
let config = S2Config::new("actual-token")
.with_endpoints(S2Endpoints::for_endpoint("http://example.test").unwrap())
diff --git a/sdk/src/types.rs b/sdk/src/types.rs
index a66f8c44..3e312852 100644
--- a/sdk/src/types.rs
+++ b/sdk/src/types.rs
@@ -568,8 +568,9 @@ impl S2Config {
///
/// # Errors
///
- /// Returns an error if `default_headers` contains `Content-Encoding`,
- /// `Content-Length`, or `Transfer-Encoding`. The SDK controls body framing.
+ /// Returns an error if `default_headers` contains `Content-Type`,
+ /// `Content-Encoding`, `Content-Length`, or `Transfer-Encoding`.
+ /// The SDK controls request format and body framing.
/// Use [`Self::with_compression`] to configure request body encoding.
#[cfg(feature = "_hidden")]
#[doc(hidden)]
@@ -581,12 +582,13 @@ impl S2Config {
));
}
for name in [
+ http::header::CONTENT_TYPE,
http::header::CONTENT_LENGTH,
http::header::TRANSFER_ENCODING,
] {
if default_headers.contains_key(&name) {
return Err(ValidationError(format!(
- "{name} cannot be set in default headers; the SDK controls request body framing"
+ "{name} cannot be set in default headers; the SDK controls request format and body framing"
)));
}
}
@@ -4094,6 +4096,11 @@ mod tests {
#[cfg(feature = "_hidden")]
#[rstest]
+ #[case::content_type_s2s("content-type", "s2s/proto")]
+ #[case::content_type_protobuf("content-type", "application/protobuf")]
+ #[case::content_type_json("content-type", "application/json")]
+ #[case::content_type_mixed_case("Content-Type", "s2s/proto")]
+ #[case::content_type_empty("content-type", "")]
#[case::content_length("content-length", "123")]
#[case::content_length_mixed_case("Content-Length", "0")]
#[case::content_length_empty("content-length", "")]
From 1841dafda0a085235279c1889bb8bc473cddc6c3 Mon Sep 17 00:00:00 2001
From: Shikhar Bhushan
Date: Tue, 15 Sep 2026 23:03:11 -0700
Subject: [PATCH 12/41] ci: allow exact-version security exceptions to
dependency cooldown (#744)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The publication cooldown can reject the first release fixing a security
advisory while `cargo deny` rejects the older vulnerable release. This
adds exceptions for reviewed security updates, matched to exact
crate/version pairs, and updates S2 to the approved Rustls release.
`security-exceptions.toml` records the crate, exact version, advisory,
and reason. The gate validates entries, checks that publication metadata
exists, and prints the justification when it waives publication age.
Other versions retain the normal cooldown. The initial entry approves
`rustls 0.23.45` for
[RUSTSEC-2026-0285](https://rustsec.org/advisories/RUSTSEC-2026-0285.html).
Exceptions ship with the shared action; consumers pick them up by
updating their pinned action or reusable-workflow commit. The README
also documents the command-scoped Cargo resolver override needed to
select an approved fresh release. Existing first-party exemptions and
other dependency checks continue to apply.
### Dependency changes
The targeted nightly Cargo update changes four transitive package
versions, with no manifest changes:
| Crate | Before → after | Upstream changes and risk |
| --- | --- | --- |
| rustls | 0.23.43 → 0.23.45 | Fixes accepting TLS 1.3 handshake
messages at the wrong encryption level; raises AWS-LC and webpki
dependency floors. [Release
notes](https://github.com/rustls/rustls/releases/tag/v/0.23.45). |
| aws-lc-rs | 1.17.3 → 1.18.1 | Stabilizes ML-DSA APIs and tightens
validation of invalid crypto inputs.
[1.18.0](https://github.com/aws/aws-lc-rs/releases/tag/v1.18.0),
[1.18.1](https://github.com/aws/aws-lc-rs/releases/tag/v1.18.1). |
| aws-lc-sys | 0.43.0 → 0.45.0 | Updates bundled AWS-LC from 5.2 to 5.7,
including native crypto/build changes and padded-decryption output
handling. Largest runtime/build change in this update. [Wrapper
notes](https://github.com/aws/aws-lc-rs/releases/tag/v1.18.1), [AWS-LC
5.7](https://github.com/aws/aws-lc/releases/tag/v5.7.0). |
| rustls-webpki | 0.103.13 → 0.103.15 | Uses stabilized ML-DSA APIs; the
final patch fixes documentation builds.
[.14](https://github.com/rustls/webpki/releases/tag/v/0.103.14),
[.15](https://github.com/rustls/webpki/releases/tag/v/0.103.15). |
Rustls requires the newer AWS-LC/webpki dependency lines. The selected
transitive versions have already completed the cooldown; only Rustls
needs the exception. Cargo also re-resolves some Windows and tempfile
dependency edges to versions already present in the lockfile.
### Validation
- 15 deterministic Python tests pass, covering exact matching, other
fresh dependencies, malformed and duplicate entries, publication
metadata, and existing cooldown behavior. A dedicated workflow runs
them.
- The gate passes against S2's four new package versions and against
cachey PR #147; in both cases only `rustls 0.23.45` uses the exception,
with the advisory and reason printed.
- After the dependency update: `just fmt`, `just test` (796 passed),
locked workspace Clippy with all features/targets and warnings denied,
`cargo deny check`, and `git diff --check` pass.
Related: https://github.com/s2-streamstore/cachey/pull/147
---
.../rust-dependency-cooldown/.gitignore | 1 +
.../rust-dependency-cooldown/README.md | 51 +++
.../actions/rust-dependency-cooldown/check.py | 40 ++-
.../security-exceptions.toml | 5 +
.../rust-dependency-cooldown/test_check.py | 290 ++++++++++++++++++
.../rust-dependency-cooldown-tests.yml | 22 ++
Cargo.lock | 32 +-
7 files changed, 424 insertions(+), 17 deletions(-)
create mode 100644 .github/actions/rust-dependency-cooldown/.gitignore
create mode 100644 .github/actions/rust-dependency-cooldown/README.md
create mode 100644 .github/actions/rust-dependency-cooldown/security-exceptions.toml
create mode 100644 .github/actions/rust-dependency-cooldown/test_check.py
create mode 100644 .github/workflows/rust-dependency-cooldown-tests.yml
diff --git a/.github/actions/rust-dependency-cooldown/.gitignore b/.github/actions/rust-dependency-cooldown/.gitignore
new file mode 100644
index 00000000..c18dd8d8
--- /dev/null
+++ b/.github/actions/rust-dependency-cooldown/.gitignore
@@ -0,0 +1 @@
+__pycache__/
diff --git a/.github/actions/rust-dependency-cooldown/README.md b/.github/actions/rust-dependency-cooldown/README.md
new file mode 100644
index 00000000..c36831f3
--- /dev/null
+++ b/.github/actions/rust-dependency-cooldown/README.md
@@ -0,0 +1,51 @@
+# Rust dependency cooldown
+
+This action checks newly locked crates.io versions against the caller's
+`registry.global-min-publish-age` setting. S2-owned crates listed in
+`first-party-crates.txt` are exempt from the publication-age check.
+
+## Security exceptions
+
+`security-exceptions.toml` records reviewed exceptions for individual security
+releases. Each entry requires an exact crate name and version, an advisory
+identifier or URL, and a reason:
+
+```toml
+[[exception]]
+crate = "rustls"
+version = "0.23.45"
+advisory = "RUSTSEC-2026-0285"
+reason = "Fixes TLS 1.3 handshake messages accepted at the wrong encryption level"
+```
+
+Review the advisory and confirm that the exact release fixes it before adding an
+entry. Ranges, wildcards, duplicate entries, and malformed configuration are
+rejected. The action prints the crate, version, advisory, and reason whenever it
+uses an exception. The exception waives only publication age; other dependency
+checks, including `cargo deny`, continue to apply.
+
+The exception file is distributed with this action. After merging an exception,
+update consuming repositories' pinned action or reusable-workflow commit to pick
+it up. Future releases of the same crate still have to satisfy the cooldown.
+
+Entries need no expiry field: an approved release follows the normal age check
+once it is old enough. Old entries may be removed in a later cleanup. An empty
+file or `exception = []` means there are no security exceptions.
+
+Cargo also enforces publication age during dependency resolution. To select an
+approved release, override that resolver check for the targeted update command:
+
+```sh
+CARGO_RESOLVER_INCOMPATIBLE_PUBLISH_AGE=allow cargo +nightly update -p rustls --precise 0.23.45
+```
+
+Run the cooldown action on the resulting lockfile to check every newly selected
+version against the exception list and normal age requirement.
+
+## Tests
+
+Run from the repository root:
+
+```sh
+python3 -m unittest discover -s .github/actions/rust-dependency-cooldown -p 'test_*.py' -v
+```
diff --git a/.github/actions/rust-dependency-cooldown/check.py b/.github/actions/rust-dependency-cooldown/check.py
index 535e2884..53f7d0b0 100644
--- a/.github/actions/rust-dependency-cooldown/check.py
+++ b/.github/actions/rust-dependency-cooldown/check.py
@@ -20,6 +20,7 @@ ACTION_ROOT = Path(__file__).resolve().parent
REPO_ROOT = Path.cwd()
CONFIG = REPO_ROOT / ".cargo" / "config.toml"
ALLOWLIST = ACTION_ROOT / "first-party-crates.txt"
+SECURITY_EXCEPTIONS = ACTION_ROOT / "security-exceptions.toml"
CRATES_IO_SOURCE = "registry+https://github.com/rust-lang/crates.io-index"
INDEX_BASE = "https://index.crates.io"
USER_AGENT = "s2 minimum-publish-age check (github.com/s2-streamstore/s2)"
@@ -69,6 +70,36 @@ def allowed_crates() -> set[str]:
}
+def security_exceptions() -> dict[tuple[str, str], dict[str, str]]:
+ with SECURITY_EXCEPTIONS.open("rb") as exceptions_file:
+ document = tomllib.load(exceptions_file)
+ if document.keys() - {"exception"}:
+ raise ValueError(f"unexpected fields in {SECURITY_EXCEPTIONS}")
+ entries = document.get("exception", [])
+ if not isinstance(entries, list):
+ raise ValueError(f"expected [[exception]] entries in {SECURITY_EXCEPTIONS}")
+ required_fields = {"crate", "version", "advisory", "reason"}
+ exceptions: dict[tuple[str, str], dict[str, str]] = {}
+ for index, entry in enumerate(entries, start=1):
+ if not isinstance(entry, dict) or entry.keys() != required_fields:
+ raise ValueError(f"security exception {index} must contain {sorted(required_fields)}")
+ for field, value in entry.items():
+ if not isinstance(value, str) or not value or value != value.strip():
+ raise ValueError(f"security exception {index} has invalid {field}")
+ if re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9_-]*", entry["crate"]) is None:
+ raise ValueError(f"security exception {index} requires an exact crate name")
+ if re.fullmatch(
+ r"[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?",
+ entry["version"],
+ ) is None:
+ raise ValueError(f"security exception {index} requires an exact version")
+ key = (entry["crate"], entry["version"])
+ if key in exceptions:
+ raise ValueError(f"duplicate security exception for {key[0]} {key[1]}")
+ exceptions[key] = entry
+ return exceptions
+
+
def crates_io_versions(lock_text: str) -> set[tuple[str, str]]:
packages = tomllib.loads(lock_text).get("package", [])
return {
@@ -196,6 +227,7 @@ def main() -> int:
try:
if args.base_ref and run_git("rev-parse", "--verify", "--quiet", args.base_ref).returncode:
raise ValueError(f"base ref {args.base_ref!r} is not available")
+ approved_exceptions = security_exceptions()
if args.base_ref and not has_relevant_changes(args.base_ref):
print("No Rust dependency cooldown files changed; check skipped.")
return 0
@@ -231,6 +263,12 @@ def main() -> int:
checked += 1
crate_age = now - pubtime
if crate_age < age_limit:
+ if exception := approved_exceptions.get((name, version)):
+ print(
+ f"Publication cooldown waived for {name} {version} ({lockfile}): "
+ f"{exception['advisory']} — {exception['reason']}"
+ )
+ continue
violations.append(
f"{name} {version} ({lockfile}): published "
f"{crate_age.total_seconds() / 86400:.1f} days ago; "
@@ -246,7 +284,7 @@ def main() -> int:
print(f" - {violation}", file=sys.stderr)
return 1
- print(f"Checked {checked} new crates.io version(s); all are at least {age_text} old.")
+ print(f"Checked {checked} new crates.io version(s); publication cooldown policy satisfied.")
return 0
diff --git a/.github/actions/rust-dependency-cooldown/security-exceptions.toml b/.github/actions/rust-dependency-cooldown/security-exceptions.toml
new file mode 100644
index 00000000..c0e3a044
--- /dev/null
+++ b/.github/actions/rust-dependency-cooldown/security-exceptions.toml
@@ -0,0 +1,5 @@
+[[exception]]
+crate = "rustls"
+version = "0.23.45"
+advisory = "RUSTSEC-2026-0285"
+reason = "Fixes TLS 1.3 handshake messages accepted at the wrong encryption level"
diff --git a/.github/actions/rust-dependency-cooldown/test_check.py b/.github/actions/rust-dependency-cooldown/test_check.py
new file mode 100644
index 00000000..313e59a5
--- /dev/null
+++ b/.github/actions/rust-dependency-cooldown/test_check.py
@@ -0,0 +1,290 @@
+from __future__ import annotations
+
+import importlib.util
+import io
+import json
+import subprocess
+import sys
+import tempfile
+import unittest
+from contextlib import ExitStack, redirect_stderr, redirect_stdout
+from datetime import datetime, timedelta, timezone
+from pathlib import Path
+from unittest.mock import patch
+
+
+SPEC = importlib.util.spec_from_file_location(
+ "cooldown_check", Path(__file__).with_name("check.py")
+)
+assert SPEC is not None and SPEC.loader is not None
+check = importlib.util.module_from_spec(SPEC)
+SPEC.loader.exec_module(check)
+
+NOW = datetime(2026, 9, 16, tzinfo=timezone.utc)
+APPROVED_EXCEPTION = {
+ "crate": "rustls",
+ "version": "0.23.45",
+ "advisory": "https://rustsec.org/advisories/RUSTSEC-2026-0285.html",
+ "reason": "First release fixing TLS 1.3 handshake encryption-level validation.",
+}
+
+
+def exception_document(*entries: dict[str, object]) -> str:
+ return "\n".join(
+ "[[exception]]\n"
+ + "\n".join(f"{name} = {json.dumps(value)}" for name, value in entry.items())
+ + "\n"
+ for entry in entries
+ )
+
+
+class CooldownSecurityExceptionTests(unittest.TestCase):
+ def setUp(self) -> None:
+ temporary_directory = tempfile.TemporaryDirectory()
+ self.addCleanup(temporary_directory.cleanup)
+ temporary_root = Path(temporary_directory.name)
+ self.repo = temporary_root / "consumer"
+ self.repo.mkdir()
+ self.action = temporary_root / "trusted-action"
+ self.action.mkdir()
+ self.exceptions = self.action / "security-exceptions.toml"
+ self.exceptions.write_text(exception_document(APPROVED_EXCEPTION))
+ (self.action / "first-party-crates.txt").write_text("s2-api\n")
+ (self.repo / ".cargo").mkdir()
+ (self.repo / ".cargo" / "config.toml").write_text(
+ '[registry]\nglobal-min-publish-age = "7 days"\n'
+ )
+ self.write_lock()
+ self.git("init", "--quiet")
+ self.commit()
+
+ def git(self, *arguments: str) -> None:
+ subprocess.run(
+ [
+ "git",
+ "-c",
+ "user.name=Cooldown Tests",
+ "-c",
+ "user.email=cooldown-tests@example.invalid",
+ "-c",
+ "commit.gpgsign=false",
+ "-c",
+ "core.hooksPath=/dev/null",
+ *arguments,
+ ],
+ cwd=self.repo,
+ check=True,
+ capture_output=True,
+ text=True,
+ )
+
+ def commit(self) -> None:
+ self.git("add", ".")
+ self.git("commit", "--quiet", "-m", "test: record baseline")
+
+ def write_lock(self, *packages: tuple[str, str, str]) -> None:
+ contents = "version = 3\n"
+ for name, version, source in packages:
+ contents += (
+ "\n[[package]]\n"
+ f"name = {json.dumps(name)}\n"
+ f"version = {json.dumps(version)}\n"
+ f"source = {json.dumps(source)}\n"
+ )
+ (self.repo / "Cargo.lock").write_text(contents)
+
+ def run_gate(
+ self,
+ publication_times: dict[str, dict[str, datetime]] | None = None,
+ ) -> tuple[int, str, str, list[str]]:
+ published = publication_times or {
+ "rustls": {"0.23.45": NOW - timedelta(days=1)},
+ }
+ output, errors = io.StringIO(), io.StringIO()
+ with ExitStack() as stack:
+ for name, value in {
+ "ACTION_ROOT": self.action,
+ "REPO_ROOT": self.repo,
+ "CONFIG": self.repo / ".cargo" / "config.toml",
+ "ALLOWLIST": self.action / "first-party-crates.txt",
+ "SECURITY_EXCEPTIONS": self.exceptions,
+ }.items():
+ stack.enter_context(patch.object(check, name, value))
+ stack.enter_context(
+ patch.object(sys, "argv", ["check.py", "--repo-root", str(self.repo), "HEAD"])
+ )
+ clock = stack.enter_context(patch.object(check, "datetime", wraps=datetime))
+ clock.now.return_value = NOW
+ lookup = stack.enter_context(
+ patch.object(check, "publication_times", side_effect=published.__getitem__)
+ )
+ stack.enter_context(redirect_stdout(output))
+ stack.enter_context(redirect_stderr(errors))
+ status = check.main()
+ lookups = [call.args[0] for call in lookup.call_args_list]
+ return status, output.getvalue(), errors.getvalue(), lookups
+
+ def test_exact_security_exception_passes_and_reports_justification(self) -> None:
+ self.write_lock(("rustls", "0.23.45", check.CRATES_IO_SOURCE))
+ status, output, errors, _ = self.run_gate()
+ self.assertEqual(status, 0, errors)
+ for field in APPROVED_EXCEPTION.values():
+ self.assertIn(field, output)
+
+ def test_exception_does_not_cover_another_version_or_crate(self) -> None:
+ for name, version in [("rustls", "0.23.46"), ("another-crate", "0.23.45")]:
+ with self.subTest(crate=name, version=version):
+ self.write_lock((name, version, check.CRATES_IO_SOURCE))
+ status, _, errors, _ = self.run_gate(
+ {name: {version: NOW - timedelta(days=1)}}
+ )
+ self.assertEqual(status, 1)
+ self.assertIn(f"{name} {version}", errors)
+
+ def test_exception_does_not_hide_an_unrelated_young_dependency(self) -> None:
+ self.write_lock(
+ ("rustls", "0.23.45", check.CRATES_IO_SOURCE),
+ ("another-crate", "1.0.0", check.CRATES_IO_SOURCE),
+ )
+ status, output, errors, _ = self.run_gate(
+ {
+ "rustls": {"0.23.45": NOW - timedelta(days=1)},
+ "another-crate": {"1.0.0": NOW - timedelta(days=1)},
+ }
+ )
+ self.assertEqual(status, 1)
+ self.assertIn(APPROVED_EXCEPTION["advisory"], output)
+ self.assertIn("another-crate 1.0.0", errors)
+ self.assertNotIn("rustls 0.23.45", errors)
+
+ def test_consumer_repository_cannot_supply_its_own_exception(self) -> None:
+ unapproved = {**APPROVED_EXCEPTION, "version": "0.23.46"}
+ consumer_action = self.repo / ".github" / "actions" / "rust-dependency-cooldown"
+ consumer_action.mkdir(parents=True)
+ for directory in [self.repo, self.repo / ".cargo", consumer_action]:
+ (directory / "security-exceptions.toml").write_text(exception_document(unapproved))
+ self.write_lock(("rustls", "0.23.46", check.CRATES_IO_SOURCE))
+ status, _, errors, _ = self.run_gate(
+ {"rustls": {"0.23.46": NOW - timedelta(days=1)}}
+ )
+ self.assertEqual(status, 1)
+ self.assertIn("rustls 0.23.46", errors)
+
+ def test_malformed_security_exceptions_fail_closed(self) -> None:
+ documents = {
+ "invalid TOML": "[[exception]",
+ "not an array": "exception = 1\n",
+ "not a table": "exception = [1]\n",
+ "unknown top-level field": "exceptions = []\n",
+ }
+ for field in APPROVED_EXCEPTION:
+ missing = {name: value for name, value in APPROVED_EXCEPTION.items() if name != field}
+ documents[f"missing {field}"] = exception_document(missing)
+ for value in ["", " ", 123, f" {APPROVED_EXCEPTION[field]}"]:
+ documents[f"invalid {field}: {value!r}"] = exception_document(
+ {**APPROVED_EXCEPTION, field: value}
+ )
+ documents["unknown field"] = exception_document({**APPROVED_EXCEPTION, "extra": "value"})
+ documents["duplicate pair"] = exception_document(APPROVED_EXCEPTION, APPROVED_EXCEPTION)
+ self.write_lock(("rustls", "0.23.45", check.CRATES_IO_SOURCE))
+ for description, document in documents.items():
+ with self.subTest(description=description):
+ self.exceptions.write_text(document)
+ status, _, errors, _ = self.run_gate()
+ self.assertEqual(status, 2)
+ self.assertIn("minimum-publish-age error", errors)
+
+ def test_missing_action_exception_file_fails_closed(self) -> None:
+ self.exceptions.unlink()
+ self.write_lock(("rustls", "0.23.45", check.CRATES_IO_SOURCE))
+ status, _, errors, _ = self.run_gate()
+ self.assertEqual(status, 2)
+ self.assertIn("minimum-publish-age error", errors)
+
+ def test_malformed_exceptions_fail_even_when_no_lockfile_changed(self) -> None:
+ self.exceptions.write_text("[[exception]")
+ status, _, errors, _ = self.run_gate()
+ self.assertEqual(status, 2)
+ self.assertIn("minimum-publish-age error", errors)
+
+ def test_empty_exception_lists_do_not_exempt_young_dependencies(self) -> None:
+ self.write_lock(("rustls", "0.23.45", check.CRATES_IO_SOURCE))
+ for document in ["", "exception = []\n"]:
+ with self.subTest(document=document):
+ self.exceptions.write_text(document)
+ status, _, errors, _ = self.run_gate()
+ self.assertEqual(status, 1)
+ self.assertIn("rustls 0.23.45", errors)
+
+ def test_exception_ranges_and_wildcards_are_rejected(self) -> None:
+ self.write_lock(("rustls", "0.23.45", check.CRATES_IO_SOURCE))
+ for field, value in [
+ ("crate", "rustls*"),
+ ("version", "*"),
+ ("version", "0.23.*"),
+ ("version", ">=0.23.45"),
+ ]:
+ with self.subTest(field=field, value=value):
+ self.exceptions.write_text(
+ exception_document({**APPROVED_EXCEPTION, field: value})
+ )
+ status, _, errors, _ = self.run_gate()
+ self.assertEqual(status, 2)
+ self.assertIn("minimum-publish-age error", errors)
+
+ def test_approved_version_still_requires_a_publication_time(self) -> None:
+ self.write_lock(("rustls", "0.23.45", check.CRATES_IO_SOURCE))
+ status, _, errors, _ = self.run_gate({"rustls": {}})
+ self.assertEqual(status, 2)
+ self.assertIn("no publication time for rustls 0.23.45", errors)
+
+ def test_mature_approved_version_uses_normal_age_check(self) -> None:
+ self.write_lock(("rustls", "0.23.45", check.CRATES_IO_SOURCE))
+ status, output, errors, lookups = self.run_gate(
+ {"rustls": {"0.23.45": NOW - timedelta(days=7)}}
+ )
+ self.assertEqual(status, 0, errors)
+ self.assertNotIn(APPROVED_EXCEPTION["advisory"], output)
+ self.assertEqual(lookups, ["rustls"])
+
+ def test_age_boundary_for_ordinary_dependencies_is_unchanged(self) -> None:
+ self.write_lock(("another-crate", "1.0.0", check.CRATES_IO_SOURCE))
+ for age, expected_status in [(timedelta(days=7), 0), (timedelta(days=7, seconds=-1), 1)]:
+ with self.subTest(age=age):
+ status, _, errors, _ = self.run_gate({"another-crate": {"1.0.0": NOW - age}})
+ self.assertEqual(status, expected_status, errors)
+
+ def test_first_party_allowlist_still_exempts_new_versions(self) -> None:
+ self.write_lock(("s2-api", "99.0.0", check.CRATES_IO_SOURCE))
+ status, _, errors, lookups = self.run_gate()
+ self.assertEqual(status, 0, errors)
+ self.assertEqual(lookups, [])
+
+ def test_other_sources_are_not_processed_as_security_exceptions(self) -> None:
+ for source in [
+ "registry+https://example.invalid/index",
+ "git+https://example.invalid/rustls",
+ ]:
+ with self.subTest(source=source):
+ self.write_lock(("rustls", "0.23.45", source))
+ status, output, errors, lookups = self.run_gate()
+ self.assertEqual(status, 0, errors)
+ self.assertNotIn(APPROVED_EXCEPTION["advisory"], output)
+ self.assertEqual(lookups, [])
+
+ def test_existing_locked_versions_are_not_rechecked(self) -> None:
+ self.write_lock(("already-locked", "1.0.0", check.CRATES_IO_SOURCE))
+ self.commit()
+ self.write_lock(
+ ("already-locked", "1.0.0", check.CRATES_IO_SOURCE),
+ ("another-crate", "1.0.0", check.CRATES_IO_SOURCE),
+ )
+ status, _, errors, lookups = self.run_gate(
+ {"another-crate": {"1.0.0": NOW - timedelta(days=8)}}
+ )
+ self.assertEqual(status, 0, errors)
+ self.assertEqual(lookups, ["another-crate"])
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/.github/workflows/rust-dependency-cooldown-tests.yml b/.github/workflows/rust-dependency-cooldown-tests.yml
new file mode 100644
index 00000000..76f56ecc
--- /dev/null
+++ b/.github/workflows/rust-dependency-cooldown-tests.yml
@@ -0,0 +1,22 @@
+name: Rust dependency cooldown tests
+
+on:
+ pull_request:
+ paths:
+ - .github/actions/rust-dependency-cooldown/**
+ - .github/workflows/rust-dependency-cooldown-tests.yml
+ push:
+ branches: [main]
+ paths:
+ - .github/actions/rust-dependency-cooldown/**
+ - .github/workflows/rust-dependency-cooldown-tests.yml
+
+permissions:
+ contents: read
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ - run: python3 -m unittest discover -s .github/actions/rust-dependency-cooldown -p 'test_*.py' -v
diff --git a/Cargo.lock b/Cargo.lock
index 9a5b2d9f..ee239f35 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -505,9 +505,9 @@ dependencies = [
[[package]]
name = "aws-lc-rs"
-version = "1.17.3"
+version = "1.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "00bdb5da18dac48ca2cc7cd4a98e533e8635a58e2361d13a1a4ee3888e0d72f1"
+checksum = "b281d307588d634de920874890732659e2e7672f72b5e10e81badc1a8a83621e"
dependencies = [
"aws-lc-sys",
"untrusted 0.7.1",
@@ -516,9 +516,9 @@ dependencies = [
[[package]]
name = "aws-lc-sys"
-version = "0.43.0"
+version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43103168cc76fe62678a375e722fc9cb3a0146159ac5828bc4f0dfd755c2224c"
+checksum = "9bff6c3b54fad79a2e60b8102caf565819711497c1f5f092f49508e2f5c31b27"
dependencies = [
"cc",
"cmake",
@@ -1445,7 +1445,7 @@ version = "3.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34"
dependencies = [
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -2030,7 +2030,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -4340,7 +4340,7 @@ dependencies = [
"once_cell",
"socket2",
"tracing",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -4772,14 +4772,14 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys 0.12.1",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
name = "rustls"
-version = "0.23.43"
+version = "0.23.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06"
+checksum = "0d41d731c7d2f962d1ccc364cec258de3c0e93b38c2fb3ba97ac74513048d634"
dependencies = [
"aws-lc-rs",
"log",
@@ -4831,7 +4831,7 @@ dependencies = [
"security-framework",
"security-framework-sys",
"webpki-root-certs",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -4842,9 +4842,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
[[package]]
name = "rustls-webpki"
-version = "0.103.13"
+version = "0.103.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e"
+checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2"
dependencies = [
"aws-lc-rs",
"ring",
@@ -5820,10 +5820,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
dependencies = [
"fastrand",
- "getrandom 0.4.3",
+ "getrandom 0.3.4",
"once_cell",
"rustix 1.1.4",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -6826,7 +6826,7 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
From 52b6e2e8fb7b8b7b13a34a8b7b149429ea9e726a Mon Sep 17 00:00:00 2001
From: "release-pleaze[bot]"
<262023388+release-pleaze[bot]@users.noreply.github.com>
Date: Thu, 17 Sep 2026 20:55:25 -0700
Subject: [PATCH 13/41] chore: release (#742)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## 🤖 New release
* `s2-api`: 0.31.3 -> 0.31.4 (✓ API compatible changes)
* `s2-lite`: 0.42.11 -> 0.42.12 (✓ API compatible changes)
* `s2-sdk`: 0.34.7 -> 0.34.8 (✓ API compatible changes)
* `s2-cli`: 0.42.11 -> 0.42.12
* `s2-testcontainers`: 0.42.11 -> 0.42.12
Changelog
## `s2-api`
## [0.31.4] - 2026-09-16
### Miscellaneous Tasks
- Sync specs submodule
([#739](https://github.com/s2-streamstore/s2/issues/739))
## `s2-lite`
## [0.42.12] - 2026-09-16
### Miscellaneous Tasks
- Update Cargo.lock dependencies
## `s2-sdk`
## [0.34.8] - 2026-09-16
### Bug Fixes
- Reject Content-Type in default headers
([#740](https://github.com/s2-streamstore/s2/issues/740))
## `s2-cli`
## [0.42.12] - 2026-09-16
### Miscellaneous Tasks
- Update Cargo.lock dependencies
## `s2-testcontainers`
## [0.42.12] - 2026-09-16
---
This PR was generated with
[release-plz](https://github.com/release-plz/release-plz/).
Co-authored-by: release-pleaze[bot] <262023388+release-pleaze[bot]@users.noreply.github.com>
---
Cargo.lock | 10 +++++-----
api/CHANGELOG.md | 8 ++++++++
api/Cargo.toml | 2 +-
cli/CHANGELOG.md | 8 ++++++++
cli/Cargo.toml | 2 +-
lite/CHANGELOG.md | 8 ++++++++
lite/Cargo.toml | 2 +-
sdk/CHANGELOG.md | 8 ++++++++
sdk/Cargo.toml | 2 +-
testcontainers/CHANGELOG.md | 4 ++++
testcontainers/Cargo.toml | 2 +-
11 files changed, 46 insertions(+), 10 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index ee239f35..c3f99fa9 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4878,7 +4878,7 @@ checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
[[package]]
name = "s2-api"
-version = "0.31.3"
+version = "0.31.4"
dependencies = [
"axum",
"base64ct",
@@ -4908,7 +4908,7 @@ dependencies = [
[[package]]
name = "s2-cli"
-version = "0.42.11"
+version = "0.42.12"
dependencies = [
"assert_cmd",
"async-stream",
@@ -4992,7 +4992,7 @@ dependencies = [
[[package]]
name = "s2-lite"
-version = "0.42.11"
+version = "0.42.12"
dependencies = [
"async-stream",
"async-trait",
@@ -5051,7 +5051,7 @@ dependencies = [
[[package]]
name = "s2-sdk"
-version = "0.34.7"
+version = "0.34.8"
dependencies = [
"assert_matches",
"async-compression",
@@ -5111,7 +5111,7 @@ dependencies = [
[[package]]
name = "s2-testcontainers"
-version = "0.42.11"
+version = "0.42.12"
dependencies = [
"reqwest",
"s2-sdk",
diff --git a/api/CHANGELOG.md b/api/CHANGELOG.md
index 70473d74..cf39588b 100644
--- a/api/CHANGELOG.md
+++ b/api/CHANGELOG.md
@@ -2,6 +2,14 @@
All notable changes to this project will be documented in this file.
+## [0.31.4] - 2026-09-16
+
+### Miscellaneous Tasks
+
+- Sync specs submodule ([#739](https://github.com/s2-streamstore/s2/issues/739))
+
+
+
## [0.31.3] - 2026-09-11
### Features
diff --git a/api/Cargo.toml b/api/Cargo.toml
index 3615870a..ed5cb33c 100644
--- a/api/Cargo.toml
+++ b/api/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "s2-api"
-version = "0.31.3"
+version = "0.31.4"
description = "API types for S2, the durable streams API"
edition.workspace = true
license.workspace = true
diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md
index 9f50daa7..6697e40d 100644
--- a/cli/CHANGELOG.md
+++ b/cli/CHANGELOG.md
@@ -2,6 +2,14 @@
All notable changes to this project will be documented in this file.
+## [0.42.12] - 2026-09-16
+
+### Miscellaneous Tasks
+
+- Update Cargo.lock dependencies
+
+
+
## [0.42.11] - 2026-09-11
### Features
diff --git a/cli/Cargo.toml b/cli/Cargo.toml
index 7904e85d..b4abf635 100644
--- a/cli/Cargo.toml
+++ b/cli/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "s2-cli"
-version = "0.42.11"
+version = "0.42.12"
description = "CLI for S2"
edition.workspace = true
license.workspace = true
diff --git a/lite/CHANGELOG.md b/lite/CHANGELOG.md
index 9a55a37e..082bea58 100644
--- a/lite/CHANGELOG.md
+++ b/lite/CHANGELOG.md
@@ -2,6 +2,14 @@
All notable changes to this project will be documented in this file.
+## [0.42.12] - 2026-09-16
+
+### Miscellaneous Tasks
+
+- Update Cargo.lock dependencies
+
+
+
## [0.42.11] - 2026-09-11
### Features
diff --git a/lite/Cargo.toml b/lite/Cargo.toml
index 9f6790cb..fae14721 100644
--- a/lite/Cargo.toml
+++ b/lite/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "s2-lite"
-version = "0.42.11"
+version = "0.42.12"
description = "Lightweight server implementation of S2, the durable streams API, backed by object storage"
edition.workspace = true
license.workspace = true
diff --git a/sdk/CHANGELOG.md b/sdk/CHANGELOG.md
index bf935a06..2c9d748f 100644
--- a/sdk/CHANGELOG.md
+++ b/sdk/CHANGELOG.md
@@ -2,6 +2,14 @@
All notable changes to this project will be documented in this file.
+## [0.34.8] - 2026-09-16
+
+### Bug Fixes
+
+- Reject Content-Type in default headers ([#740](https://github.com/s2-streamstore/s2/issues/740))
+
+
+
## [0.34.7] - 2026-09-11
### Features
diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml
index 94dfb1e6..ffab8fe2 100644
--- a/sdk/Cargo.toml
+++ b/sdk/Cargo.toml
@@ -1,7 +1,7 @@
[package]
name = "s2-sdk"
description = "Rust SDK for S2"
-version = "0.34.7"
+version = "0.34.8"
edition.workspace = true
license.workspace = true
repository = "https://github.com/s2-streamstore/s2/tree/main/sdk"
diff --git a/testcontainers/CHANGELOG.md b/testcontainers/CHANGELOG.md
index 2fa76cdc..8585945b 100644
--- a/testcontainers/CHANGELOG.md
+++ b/testcontainers/CHANGELOG.md
@@ -2,6 +2,10 @@
All notable changes to this project will be documented in this file.
+## [0.42.12] - 2026-09-16
+
+
+
## [0.42.11] - 2026-09-11
diff --git a/testcontainers/Cargo.toml b/testcontainers/Cargo.toml
index 68a46e6b..f22a7ff7 100644
--- a/testcontainers/Cargo.toml
+++ b/testcontainers/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "s2-testcontainers"
-version = "0.42.11"
+version = "0.42.12"
description = "Testcontainers helpers for the S2 Docker image"
edition.workspace = true
license.workspace = true
From 17156f6e10d3707c6672a9e6ab44ad0be6f374ab Mon Sep 17 00:00:00 2001
From: "release-pleaze[bot]"
Date: Fri, 18 Sep 2026 04:30:10 +0000
Subject: [PATCH 14/41] Bump s2-lite-helm chart to appVersion 0.42.12
---
charts/s2-lite-helm/Chart.yaml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/charts/s2-lite-helm/Chart.yaml b/charts/s2-lite-helm/Chart.yaml
index 8481fb0f..74743646 100644
--- a/charts/s2-lite-helm/Chart.yaml
+++ b/charts/s2-lite-helm/Chart.yaml
@@ -2,8 +2,8 @@ apiVersion: v2
name: s2-lite-helm
description: Self-hostable S2 streaming datastore using SlateDB on object storage
type: application
-version: 0.1.67
-appVersion: "0.42.11"
+version: 0.1.68
+appVersion: "0.42.12"
keywords:
- s2
- streaming
From 69919e9d99a952ad815490e2995b565c93add04c Mon Sep 17 00:00:00 2001
From: Shikhar Bhushan
Date: Thu, 17 Sep 2026 22:09:54 -0700
Subject: [PATCH 15/41] chore(deps): upgrade SlateDB to 0.16 and refresh
dependencies (#755)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Summary
Upgrade SlateDB from 0.15 to 0.16 and refresh both Rust workspaces to
the latest releases allowed by the seven-day publication cooldown,
including incompatible direct dependency upgrades.
SlateDB writes now return before object-storage durability. Explicitly
await durability for metadata transactions, background deletion/trim
work, and test fixtures; retain the append pipeline's existing
sequence-based durability notifier. Rename the TTL option to
`ExpireAfterMillis` without changing retention units. Basin provisioning
also waits for the existing metadata row's sequence before returning an
unchanged Ensure result, an idempotent create result, or an
already-exists error, matching stream provisioning. Control-plane
regression tests share a fixture with automatic flushing disabled and
verify that basin/stream creation and concurrent basin retries wait for
durability. They synchronize through public SlateDB snapshot sequences
and check visibility through the backend APIs, without depending on
private metadata keys. A shared test-only `assert_durable()` helper
removes the repeated unwrap-and-wait chains from 53 fixture writes.
Remove the `proc-macro-error2` advisory exception after the tabled
upgrade removes that dependency.
## Changelog notes
| Dependency | Review and adaptation |
| --- | --- |
| `slatedb`, `slatedb-common`, `slatedb-txn-obj` **0.15.0 → 0.16.0** |
Adapt to [explicit write
durability](https://github.com/slatedb/slatedb/pull/1985) and
[millisecond TTL names](https://github.com/slatedb/slatedb/pull/1989).
The release also fixes compaction resurrection and sequence-tracker
deserialization, writes ManifestV2 universally while retaining V1 reads,
and increases the default GC interval to ten minutes. Existing
byte-based scan options remain valid.
[Release](https://github.com/slatedb/slatedb/releases/tag/v0.16.0). |
| `zstd` **0.13.3 → 0.14.0** | Prepared dictionaries must outlive their
streams, fixing a safe-code dangling-pointer issue; `Decoder::finish`
now consumes the remaining frame. Moves to `zstd-safe` 8 and
BSD-3-Clause licensing. Our streaming calls compile unchanged. SlateDB
still brings a separate 0.13.3 copy.
[Release](https://github.com/gyscos/zstd-rs/releases/tag/v0.14.0). |
| `dirs` **6.0.0 → 7.0.0** | Windows `preference_dir` moves from
LocalAppData to RoamingAppData. The CLI uses `config_dir`, `home_dir`,
and `cache_dir`, so its paths do not require migration.
[Changelog](https://docs.rs/crate/dirs/7.0.0#changelog). |
| `tabled` **0.21.0 → 0.22.0**, `tabled_derive` **0.11.0 → 0.12.0**,
`json_to_table` **0.13.0 → 0.14.0** | Derive diagnostics move from the
unmaintained `proc-macro-error2` to `syn::Error`; adds compact-table row
skipping and text attributes. `json_to_table` follows the tabled
version; no separate release notes were found for that wrapper.
[Changelog](https://github.com/zhiburt/tabled/blob/master/CHANGELOG.md),
[wrapper
comparison](https://github.com/zhiburt/tabled/compare/1b537fecdcd498b5d495c442646754013f771d35...7c1141044a395e7390222d4345837b9b62acc032).
|
| `rstest` / `rstest_macros` **0.26.1 → 0.27.0** | Raises MSRV to 1.85;
fixes traced mutable arguments and generated imports, and disables
unused futures-util defaults. Existing test attributes compile
unchanged.
[Release](https://github.com/la10736/rstest/releases/tag/v0.27.0). |
| `testcontainers` **0.27.3 → 0.28.0** | Updates the public Bollard
dependency to 0.21 and parse-display to 0.11. Helper code compiles
unchanged; see Docker validation below.
[Release](https://github.com/testcontainers/testcontainers-rs/releases/tag/0.28.0).
|
| `bytesize` **2.6.0 → 2.7.0** | Restores `display()` availability by
removing the problematic no-alloc support. Align the simulator
requirement with 2.7.
[Release](https://github.com/bytesize-rs/bytesize/releases/tag/bytesize-v2.7.0).
|
| `keyring` **4.1.6 → 4.2.0** | Refreshes platform credential-store
dependencies, including restored Android CLI support and 64-bit
restrictions for the DB keystore. Existing platform integration compiles
unchanged.
[Release](https://github.com/open-source-cooperative/keyring-rs/releases/tag/v4.2.0).
|
| `uuid` **1.24.0 → 1.26.1** | Fixes V7 counter placement and
overflowing Timestamp-to-SystemTime conversion; adds V7 precision
configuration. Our V4 generation calls are unchanged.
[1.26.1](https://github.com/uuid-rs/uuid/releases/tag/v1.26.1),
[1.26.0](https://github.com/uuid-rs/uuid/releases/tag/v1.26.0). |
| Simulator `s3s` **0.14.1 → 0.15.0** | Constant-time signature
comparison, tighter SigV4 region/expiry validation, and
streaming/checksum fixes; MSRV is 1.96. The mock S3 implementation needs
no API edits. 0.16 is still inside the cooldown.
[Changelog](https://github.com/s3s-project/s3s/blob/v0.15.0/CHANGELOG.md).
|
| Simulator `bytes` **1.12.0 → 1.12.1**, requirement **1.11 → 1.12** |
Fixes handling of a panicking `Box::new`; the main workspace already
used 1.12.1.
[Release](https://github.com/tokio-rs/bytes/releases/tag/v1.12.1). |
| Simulator `http` **1.4.2 → 1.5.0**, requirement **1.4 → 1.5** | Adds
QUERY and fixes URI builder/length validation.
[Release](https://github.com/hyperium/http/releases/tag/v1.5.0). |
| `hyper` **1.11.0 → 1.11.1**; simulator **1.10.1 → 1.11.1**,
requirement **1.9 → 1.11** | Fixes HTTP/1 trailer recognition, pooled
`Connection: close` handling, and flushing before yielding.
[Release](https://github.com/hyperium/hyper/releases/tag/v1.11.1). |
| SDK `tokio` requirement **1.6 → 1.53**, simulator **1.52 → 1.53** |
Align declared minimums with the already-locked 1.53.1 runtime; the
resolved Tokio version does not change.
[Release](https://github.com/tokio-rs/tokio/releases/tag/tokio-1.53.1).
|
Runtime-sensitive and incompatible transitive updates
- **Storage/cache:** `foyer` and its common/memory/storage/tokio crates
**0.22.3 → 0.22.6** fix stale cache entries after rejected admission,
in-flight entry membership checks, and musl ioctl types. Its
runtime/sketch refresh introduces `asyncband`, `datasketches`, and Jiff.
[0.22.4](https://github.com/foyer-rs/foyer/releases/tag/v0.22.4),
[0.22.6](https://github.com/foyer-rs/foyer/releases/tag/v0.22.6).
- **AWS:** `aws-config` **1.10.1 → 1.12.0** (simulator **1.8.15 →
1.12.0**), `aws-runtime` **1.9.1 → 1.9.2**, `aws-types` **1.5.0 →
1.6.0**, and the SSO/SSOOIDC/STS clients advance to **1.109.0 / 1.111.0
/ 1.114.0**. Clock-skew correction is now enabled by default; Smithy
adds pool controls and opt-in telemetry capture. The selected AWS
runtime requires Rust 1.94.1. [AWS
release](https://github.com/awslabs/aws-sdk-rust/releases/tag/release-2026-09-04),
[Smithy pool
controls](https://github.com/smithy-lang/smithy-rs/releases/tag/release-2026-08-19).
- **Smithy:** main `aws-smithy-http-client` **1.2.0 → 1.4.0**, runtime
**1.12.1 → 1.14.0**, runtime-api **1.14.0 → 1.16.0**, and types **1.6.1
→ 1.6.3**. Refreshing the older simulator lock also advances HTTP
**0.63.6 → 0.64.0**, JSON **0.62.7 → 0.63.0**, observability **0.2.6 →
0.3.0**, query/XML **0.60.15 → 0.62.0**, and schema **0.1.0 → 0.2.0**,
with the corresponding credential/SigV4/async crates. These align it
with the main workspace's runtime and protocol families; S3 smoke and
deterministic packet-loss scenarios pass. [Release
history](https://github.com/smithy-lang/smithy-rs/releases), [selected
package
versions](https://github.com/awslabs/aws-sdk-rust/releases/tag/release-2026-09-10).
- **HTTP/TLS:** `reqwest` **0.13.4 → 0.13.5** fixes proxy credential
selection and wrapped timeout recognition; `h2` **0.4.16 → 0.4.19**
fixes DATA-frame accounting and caps the encoder table; `tokio-rustls`
**0.26.4 → 0.26.5** can return more bytes per read. The graph no longer
requires `ureq` or `ureq-proto`.
[Reqwest](https://github.com/seanmonstar/reqwest/releases/tag/v0.13.5),
[h2](https://github.com/hyperium/h2/releases/tag/v0.4.19),
[tokio-rustls](https://github.com/rustls/tokio-rustls/releases/tag/v/0.26.5).
- **Certificate parsing/encoding:** `rcgen` **0.14.8 → 0.14.10** fixes
DER/spec compliance and uses stable AWS-LC ML-DSA. `pem` **3.0.6 →
4.0.0** adopts `base64` 0.23 and raises MSRV to 1.71; no formal PEM 4
release notes were found. `base64` **0.22.1 → 0.23.1** adds default SIMD
engines and changes `InvalidLastSymbol` information; 0.22 remains for
other consumers.
[rcgen](https://github.com/rustls/rcgen/releases/tag/v0.14.10), [PEM
comparison](https://github.com/jcreekmore/pem-rs/compare/7d6157704805dcedf703229926938a71b1ddd0b1...99c15c08f1389153ed037c33750f8605bbf8bd55),
[base64
notes](https://docs.rs/crate/base64/0.23.1/source/RELEASE-NOTES.md).
- **Crypto:** `blake3` **1.8.5 → 1.8.7** removes `arrayref` following an
upstream account compromise. `aes` **0.9.2 → 0.9.3** enables VAES
backends by default and raises MSRV to 1.89; `aes-gcm` **0.11.0 →
0.11.1** replaces subtle with ctutils. The Linux secret-service stack
moves `cbc` **0.1.2 → 0.2.1**, `hkdf` **0.12.4 → 0.13.0**, and
`block-padding` **0.3.3 → 0.4.2** to the Rust 2024/cipher 0.5/hmac 0.13
APIs, eliminating the older AES/cipher/HMAC/SHA2 copies.
[BLAKE3](https://github.com/BLAKE3-team/BLAKE3/releases/tag/1.8.7),
[AES](https://docs.rs/crate/aes/0.9.3/source/CHANGELOG.md),
[AES-GCM](https://docs.rs/crate/aes-gcm/0.11.1/source/CHANGELOG.md),
[CBC](https://docs.rs/crate/cbc/0.2.1/source/CHANGELOG.md),
[HKDF](https://docs.rs/crate/hkdf/0.13.0/source/CHANGELOG.md),
[padding](https://docs.rs/crate/block-padding/0.4.2/source/CHANGELOG.md).
- **Simulator crypto:** `aws-lc-rs` **1.17.0 → 1.18.1**, `aws-lc-sys`
**0.41.0 → 0.45.0**, and `rustls` **0.23.40 → 0.23.45** align with the
main workspace. AWS-LC tightens buffer/IV/key API contracts; Rustls
0.23.45 is the repository-approved security exception for TLS handshake
encryption-level validation. The getrandom fork remains pinned. [AWS-LC
release](https://github.com/aws/aws-lc-rs/releases/tag/v1.18.1), [Rustls
release](https://github.com/rustls/rustls/releases/tag/v/0.23.45).
- **Compression:** `async-compression` **0.4.43 → 0.4.46** (simulator
**0.4.42 → 0.4.46**), codecs **0.4.38 → 0.4.41**, and core **0.4.32 →
0.4.33** adopt zstd 0.14 and reject stalled deflate64 input. `flate2`
**1.1.9 → 1.1.10** fixes gzip write loops and rejects truncated deflate
streams. `miniz_oxide` **0.8.9 → 0.9.1** adds partial flushing, fixes
incomplete Huffman-tree acceptance, and makes several status/config
enums non-exhaustive. `zlib-rs` **0.6.6 → 0.6.7** fixes a use-after-free
in `set_level`. `zstd-safe` adds **8.0.0** alongside **7.3.0** and
`zstd-sys` becomes **2.1.0+zstd.1.5.7**. [Async
compression](https://docs.rs/crate/async-compression/0.4.46/source/CHANGELOG.md),
[flate2](https://github.com/rust-lang/flate2-rs/releases/tag/1.1.10),
[miniz
comparison](https://github.com/Frommi/miniz_oxide/compare/44e43c7786e379b2b1a7fde4aa0e63be719e583d...4e582392df3a739d2b0dfd2c537dc33e8942be38),
[zlib-rs](https://github.com/trifectatechfoundation/zlib-rs/releases/tag/v0.6.7),
[zstd-safe](https://github.com/gyscos/zstd-rs/releases/tag/zstd-safe-8.0.0).
- **Async/OS:** the futures family **0.3.33 → 0.3.34** (simulator
**0.3.32 → 0.3.34**) preserves cloned waker identity and updates its
macro parser. `mio` **1.2.2 → 1.2.3** fixes Unix-domain listener
readiness and Wine support. Related crossbeam, io-uring, libc, and
simulator Tokio utility updates require no owned API migration; the
simulator's trace-level determinism tests cover clock/RNG scheduling
behavior.
[Futures](https://github.com/rust-lang/futures-rs/releases/tag/0.3.34),
[Mio](https://docs.rs/crate/mio/1.2.3/source/CHANGELOG.md).
- **Derive/parser APIs:** `darling`/core/macro **0.23.0 → 0.24.1**,
`zvariant_utils` **3.5.0 → 4.2.0**, simulator `serde_derive_internals`
**0.29.1 → 0.30.0**, and simulator `syn` **2.0.118 → 2.0.119 + 3.0.5**
move transitive macro APIs to syn 3 (the main workspace already used syn
3). Darling fixes custom parsing and skipped-variant diagnostics;
zvariant_utils moves derive generation internally and deprecates
GVariant support. No application macro migration is needed.
[Darling](https://docs.rs/crate/darling/0.24.1/source/CHANGELOG.md),
[zvariant_utils](https://docs.rs/crate/zvariant_utils/4.2.0/source/CHANGELOG.md),
[Serde change](https://github.com/serde-rs/serde/pull/3085).
- **Docker helpers:** `bollard` **0.20.2 → 0.21.1** and buildkit-proto
**0.7.0 → 0.8.1** refresh Docker API models, add Podman support, and fix
logs without trailing newlines. `parse-display`/derive **0.9.1 →
0.11.0** change combined display/regex handling, add optional-field
parsing, and use Rust 2024. No separate formal release notes were found
for parse-display. [Bollard
comparison](https://github.com/fussybeaver/bollard/compare/ddd21715ac76ccaf83db1b5a346c014e1fa83b64...f9ec79e7546edfb674b1066c44a68815cb52251c),
[parse-display
comparison](https://github.com/frozenlib/parse-display/compare/2fd6c6ed8e737f3dfefae0442dafb06e6d3ff1d1...3a91021cd3e79c915fb72002bdc09f4d2966a9c5).
- **QUIC:** `quinn-proto` **0.11.16 → 0.11.17** fixes three remotely
triggered memory-exhaustion bugs and a CUBIC congestion-window overflow.
[Release](https://github.com/quinn-rs/quinn/releases/tag/quinn-proto-0.11.17).
Version 0.11.18 contains further panic fixes but is still inside the
publication cooldown and has no repository exception.
- **Serialization:** `serde_with`/macros **3.21.0 → 3.23.0** cap
attacker-controlled allocation hints for duplicate-key collection
adapters, add optional Jiff adapters, and update syn/base64.
`zvariant`/derive **5.13.1 → 5.15.0** correct fixed-size
struct/dictionary padding and deprecate GVariant support. [Serde-with
changelog](https://docs.rs/crate/serde_with/3.23.0/source/CHANGELOG.md),
[Zvariant
changelog](https://docs.rs/crate/zvariant/5.15.0/source/CHANGELOG.md).
- **Credential transport:** `zbus`/macros **5.18.0 → 5.19.0** make Tokio
and async-io features additive and surface connection failures as
`Error::Connection`; the secret-service dependency refresh uses the
updated crypto family above.
[Changelog](https://docs.rs/crate/zbus/5.19.0/source/CHANGELOG.md).
- **Unicode:** ICU collections/locale/normalization/property crates
**2.2.0 → 2.3.0**, provider **2.2.0 → 2.3.1**, and new segmenter
dependencies bring Unicode 17 property and line-segmentation updates.
[Release](https://github.com/unicode-org/icu4x/releases/tag/icu%402.3.0).
- **Concurrency and telemetry:** `crossbeam-epoch` **0.9.20 → 0.9.21**
and `crossbeam-utils` **0.8.22 → 0.8.23** improve ThreadSanitizer
compatibility and fix a leaked `ShardedLockWriteGuard` aliasing
violation. `portable-atomic` **1.14.0 → 1.15.0** fixes missing memory
barriers on older ARM targets. `log` **0.4.33 → 0.4.34** adds boxed
loggers with alloc support.
[Epoch](https://docs.rs/crate/crossbeam-epoch/0.9.21/source/CHANGELOG.md),
[Utils](https://docs.rs/crate/crossbeam-utils/0.8.23/source/CHANGELOG.md),
[Portable
atomic](https://docs.rs/crate/portable-atomic/1.15.0/source/CHANGELOG.md),
[Log](https://docs.rs/crate/log/0.4.34/source/CHANGELOG.md).
The complete lockfile delta below includes maintenance updates to the
serialization, Unicode, credential-store, QUIC, and platform-support
families. The main workspace and simulator build without further owned
API changes; Linux and other target-specific behavior is covered by CI
rather than the local macOS runs.
## Risk notes
- SlateDB changes persistence timing and writes ManifestV2. The
migration preserves durable acknowledgements and the append pipeline's
batching; upgrades also adopt the upstream ten-minute garbage-collection
default.
- Public APIs exposing SlateDB or testcontainers types now use their new
incompatible versions. The SDK Tokio requirement is explicitly raised to
1.53; the simulator now requires Rust 1.96 through s3s.
- Preserve the utoipa exact-version/git patch, simulator getrandom fork,
s2-verification revision, and existing Rustls 0.23.45 security
exception. Newer releases inside the seven-day cooldown remain deferred.
## Validation
- `just fmt`, `cargo sort --workspace --check`, and `git diff --check`:
pass.
- `cargo metadata --locked --format-version 1` for both workspaces:
pass.
- `just clippy`: pass; simulator Clippy with `--locked`, all targets,
and `RUSTFLAGS="--cfg tokio_unstable"`: pass.
- `just test`: **802 passed**. All four basin retry regression cases
were also verified to fail when the retry durability waits were
temporarily removed, then pass with the waits restored.
- `cargo nextest run --locked -p s2-testcontainers`: **4 passed**
against published image 0.42.11 before rebasing onto the release commit.
After rebase, **3 passed / 1 blocked** because main now selects image
0.42.12, which returns `manifest unknown` from GHCR. CI builds the
matching image from the PR source before running these tests.
- Simulator smoke seed 1 and trace-level determinism checks for smoke
seed 1, linearizable seed 1, and linearizable seed 2 with `--fail-rate
0.005`: pass. The linearizable scenarios each produce 300 matching
history records across repeated runs; the separate Go Porcupine checker
was not run locally.
- Publication cooldown check: **372 new crate versions pass**, retaining
the exact Rustls security exception; `just deny`: pass with existing
unrelated warnings.
- Live cloud SDK/CLI integration tests were not run locally.
- [PR CI](https://github.com/s2-streamstore/s2/pull/755/checks): all
required checks passed for the basin durability fix; rerunning for the
control-plane test reorganization.
Complete registry lockfile version changes
### `Cargo.lock`
| Package | Before | After |
| --- | --- | --- |
| `aes` | 0.8.4, 0.9.2 | 0.9.3 |
| `aes-gcm` | 0.11.0 | 0.11.1 |
| `aho-corasick` | 1.1.4 | 1.1.5 |
| `android_system_properties` | 0.1.5 | 0.1.6 |
| `apple-native-keyring-store` | 1.0.1 | 1.0.2 |
| `arrayref` | 0.3.9 | — |
| `async-compression` | 0.4.43 | 0.4.46 |
| `async-trait` | 0.1.91 | 0.1.92 |
| `asyncband` | — | 0.7.2 |
| `aws-config` | 1.10.1 | 1.12.0 |
| `aws-runtime` | 1.9.1 | 1.9.2 |
| `aws-sdk-sso` | 1.105.0 | 1.109.0 |
| `aws-sdk-ssooidc` | 1.107.0 | 1.111.0 |
| `aws-sdk-sts` | 1.110.0 | 1.114.0 |
| `aws-smithy-http-client` | 1.2.0 | 1.4.0 |
| `aws-smithy-runtime` | 1.12.1 | 1.14.0 |
| `aws-smithy-runtime-api` | 1.14.0 | 1.16.0 |
| `aws-smithy-types` | 1.6.1 | 1.6.3 |
| `aws-types` | 1.5.0 | 1.6.0 |
| `base64` | 0.22.1 | 0.22.1, 0.23.1 |
| `bitflags` | 2.13.1 | 1.3.2, 2.13.2 |
| `blake3` | 1.8.5 | 1.8.7 |
| `block-padding` | 0.3.3 | 0.4.2 |
| `blocking` | 1.6.2 | 1.7.0 |
| `bollard` | 0.20.2 | 0.21.1 |
| `bollard-buildkit-proto` | 0.7.0 | 0.8.1 |
| `bollard-stubs` | 1.52.1-rc.29.1.3 | 1.53.1-rc.29.3.1 |
| `bstr` | 1.13.0 | 1.13.1 |
| `bytecheck` | 0.8.2 | 0.8.3 |
| `bytecheck_derive` | 0.8.2 | 0.8.3 |
| `bytesize` | 2.6.0 | 2.7.0 |
| `cbc` | 0.1.2 | 0.2.1 |
| `cc` | 1.4.0 | 1.4.5 |
| `chacha20` | 0.10.1 | 0.10.2 |
| `cipher` | 0.4.4, 0.5.2 | 0.5.2 |
| `clap` | 4.6.5 | 4.6.6 |
| `clap_builder` | 4.6.5 | 4.6.6 |
| `cmsketch` | 0.2.4 | — |
| `combine` | 4.6.7 | 4.6.8 |
| `compression-codecs` | 0.4.38 | 0.4.41 |
| `compression-core` | 0.4.32 | 0.4.33 |
| `console` | 0.16.4 | 0.16.6 |
| `core_detect` | — | 1.0.0 |
| `cpufeatures` | 0.2.17, 0.3.0 | 0.2.17, 0.3.1 |
| `crc32fast` | 1.5.0 | 1.5.1 |
| `crossbeam-epoch` | 0.9.20 | 0.9.21 |
| `crossbeam-utils` | 0.8.22 | 0.8.23 |
| `darling` | 0.21.3, 0.23.0 | 0.21.3, 0.24.1 |
| `darling_core` | 0.21.3, 0.23.0 | 0.21.3, 0.24.1 |
| `darling_macro` | 0.21.3, 0.23.0 | 0.21.3, 0.24.1 |
| `data-encoding` | 2.11.0 | 2.11.1 |
| `datasketches` | — | 0.3.0 |
| `defmt` | — | 1.1.1 |
| `defmt-macros` | — | 1.1.1 |
| `defmt-parser` | — | 1.0.0 |
| `dirs` | 6.0.0 | 7.0.0 |
| `either` | 1.17.0 | 1.18.0 |
| `encoding_rs` | 0.8.35 | 0.8.41 |
| `eyre` | 0.6.12 | 0.6.14 |
| `find-msvc-tools` | 0.1.9 | 0.1.12 |
| `flate2` | 1.1.9 | 1.1.10 |
| `foyer` | 0.22.3 | 0.22.6 |
| `foyer-common` | 0.22.3 | 0.22.6 |
| `foyer-memory` | 0.22.3 | 0.22.6 |
| `foyer-storage` | 0.22.3 | 0.22.6 |
| `foyer-tokio` | 0.22.3 | 0.22.6 |
| `futures` | 0.3.33 | 0.3.34 |
| `futures-channel` | 0.3.33 | 0.3.34 |
| `futures-core` | 0.3.33 | 0.3.34 |
| `futures-executor` | 0.3.33 | 0.3.34 |
| `futures-io` | 0.3.33 | 0.3.34 |
| `futures-macro` | 0.3.33 | 0.3.34 |
| `futures-sink` | 0.3.33 | 0.3.34 |
| `futures-task` | 0.3.33 | 0.3.34 |
| `futures-util` | 0.3.33 | 0.3.34 |
| `h2` | 0.4.16 | 0.4.19 |
| `hermit-abi` | 0.5.2 | 0.5.3 |
| `hkdf` | 0.12.4 | 0.13.0 |
| `hmac` | 0.12.1, 0.13.0 | 0.13.0 |
| `http-body-util` | 0.1.4 | 0.1.5 |
| `hybrid-array` | 0.4.14 | 0.4.15 |
| `hyper` | 1.11.0 | 1.11.1 |
| `icu_collections` | 2.2.0 | 2.3.0 |
| `icu_locale_core` | 2.2.0 | 2.3.0 |
| `icu_locale_fallback` | — | 2.3.0 |
| `icu_locale_fallback_data` | — | 2.3.0 |
| `icu_normalizer` | 2.2.0 | 2.3.0 |
| `icu_normalizer_data` | 2.2.0 | 2.3.0 |
| `icu_properties` | 2.2.0 | 2.3.0 |
| `icu_properties_data` | 2.2.0 | 2.3.0 |
| `icu_provider` | 2.2.0 | 2.3.1 |
| `icu_segmenter` | — | 2.3.0 |
| `icu_segmenter_data` | — | 2.3.0 |
| `indexmap` | 1.9.3, 2.14.0 | 1.9.3, 2.14.2 |
| `inout` | 0.1.4, 0.2.2 | 0.2.2 |
| `io-uring` | 0.7.13 | 0.7.15 |
| `ipnet` | 2.12.0 | 2.12.2 |
| `jiff` | — | 0.2.35 |
| `jiff-core` | — | 0.1.0 |
| `jiff-static` | — | 0.2.35 |
| `jiff-tzdb` | — | 0.1.8 |
| `jiff-tzdb-platform` | — | 0.1.3 |
| `js-sys` | 0.3.103 | 0.3.105 |
| `json_to_table` | 0.13.0 | 0.14.0 |
| `keyring` | 4.1.6 | 4.2.0 |
| `libredox` | 0.1.18 | 0.1.23 |
| `litemap` | 0.8.2 | 0.8.3 |
| `log` | 0.4.33 | 0.4.34 |
| `lru` | 0.18.2 | 0.18.4 |
| `mea` | 0.6.5 | — |
| `miniz_oxide` | 0.8.9 | 0.8.9, 0.9.1 |
| `mio` | 1.2.2 | 1.2.3 |
| `multiversion` | — | 0.9.0 |
| `multiversion-macros` | — | 0.9.0 |
| `multiversion_no_op` | — | 1.0.0 |
| `num-integer` | 0.1.46 | 0.1.47 |
| `owo-colors` | 4.3.0 | 4.4.0 |
| `parse-display` | 0.9.1 | 0.11.0 |
| `parse-display-derive` | 0.9.1 | 0.11.0 |
| `pem` | 3.0.6 | 4.0.0 |
| `pest` | 2.8.8 | 2.9.1 |
| `pest_derive` | 2.8.8 | 2.9.1 |
| `pest_generator` | 2.8.8 | 2.9.1 |
| `pest_meta` | 2.8.8 | 2.9.1 |
| `pkg-config` | 0.3.33 | 0.3.34 |
| `portable-atomic` | 1.14.0 | 1.15.0 |
| `portable-atomic-util` | — | 0.2.8 |
| `potential_utf` | 0.1.5 | 0.1.6 |
| `proc-macro-error-attr2` | 2.0.0 | — |
| `proc-macro-error2` | 2.0.1 | — |
| `ptr_meta` | 0.3.1 | 0.3.2 |
| `ptr_meta_derive` | 0.3.1 | 0.3.2 |
| `quinn-proto` | 0.11.16 | 0.11.17 |
| `rancor` | 0.1.2 | 0.1.3 |
| `rcgen` | 0.14.8 | 0.14.10 |
| `ref-cast` | 1.0.26 | 1.0.27 |
| `ref-cast-impl` | 1.0.26 | 1.0.27 |
| `regex-automata` | 0.4.16 | 0.4.18 |
| `reqwest` | 0.13.4 | 0.13.5 |
| `rkyv` | 0.8.17 | 0.8.18 |
| `rkyv_derive` | 0.8.17 | 0.8.18 |
| `rstest` | 0.26.1 | 0.27.0 |
| `rstest_macros` | 0.26.1 | 0.27.0 |
| `rtoolbox` | 0.0.5 | 0.0.6 |
| `rust_decimal` | 1.42.1 | 1.43.0 |
| `secret-service` | 5.1.0 | 5.2.0 |
| `serde_with` | 3.21.0 | 3.23.0 |
| `serde_with_macros` | 3.21.0 | 3.23.0 |
| `sha2` | 0.10.9, 0.11.0 | 0.11.0 |
| `slatedb` | 0.15.0 | 0.16.0 |
| `slatedb-common` | 0.15.0 | 0.16.0 |
| `slatedb-txn-obj` | 0.15.0 | 0.16.0 |
| `smallvec` | 1.15.2 | 1.16.0 |
| `syn` | 2.0.119, 3.0.3 | 2.0.119, 3.0.5 |
| `tabled` | 0.21.0 | 0.22.0 |
| `tabled_derive` | 0.11.0 | 0.12.0 |
| `testcontainers` | 0.27.3 | 0.28.0 |
| `textwrap` | 0.16.2 | 0.16.3 |
| `thiserror` | 1.0.69, 2.0.19 | 1.0.69, 2.0.20 |
| `thiserror-impl` | 1.0.69, 2.0.19 | 1.0.69, 2.0.20 |
| `time` | 0.3.54 | 0.3.55 |
| `tinystr` | 0.8.3 | 0.8.4 |
| `tinyvec` | 1.12.0 | 1.13.2 |
| `tokio-rustls` | 0.26.4 | 0.26.5 |
| `toml` | 0.8.23, 1.1.4+spec-1.1.0 | 0.8.23, 1.1.6+spec-1.1.0 |
| `toml_edit` | 0.22.27, 0.25.13+spec-1.1.0 | 0.22.27,
0.25.15+spec-1.1.0 |
| `tower-http` | 0.6.11, 0.7.0 | 0.6.11, 0.7.1 |
| `twox-hash` | 2.1.3 | 2.1.4 |
| `unicode-linebreak` | 0.1.5 | — |
| `ureq` | 3.3.0 | — |
| `ureq-proto` | 0.6.0 | — |
| `utf8-zero` | 0.8.1 | — |
| `uuid` | 1.24.0 | 1.26.1 |
| `wasm-bindgen` | 0.2.126 | 0.2.128 |
| `wasm-bindgen-futures` | 0.4.76 | 0.4.78 |
| `wasm-bindgen-macro` | 0.2.126 | 0.2.128 |
| `wasm-bindgen-macro-support` | 0.2.126 | 0.2.128 |
| `wasm-bindgen-shared` | 0.2.126 | 0.2.128 |
| `web-sys` | 0.3.103 | 0.3.105 |
| `writeable` | 0.6.3 | 0.6.4 |
| `yaml-rust2` | 0.11.0 | 0.11.1 |
| `zbus` | 5.18.0 | 5.19.0 |
| `zbus-secret-service-keyring-store` | 1.0.0 | 1.0.1 |
| `zbus_macros` | 5.18.0 | 5.19.0 |
| `zcheapstr` | — | 1.1.0 |
| `zerocopy` | 0.8.55 | 0.8.57 |
| `zerocopy-derive` | 0.8.55 | 0.8.57 |
| `zerotrie` | 0.2.4 | 0.2.5 |
| `zerovec` | 0.11.6 | 0.11.8 |
| `zerovec-derive` | 0.11.3 | 0.11.6 |
| `zlib-rs` | 0.6.6 | 0.6.7 |
| `zstd` | 0.13.3 | 0.13.3, 0.14.0 |
| `zstd-safe` | 7.2.4 | 7.3.0, 8.0.0 |
| `zstd-sys` | 2.0.16+zstd.1.5.7 | 2.1.0+zstd.1.5.7 |
| `zvariant` | 5.13.1 | 5.15.0 |
| `zvariant_derive` | 5.13.1 | 5.15.0 |
| `zvariant_utils` | 3.5.0 | 4.2.0 |
### `sim/Cargo.lock`
| Package | Before | After |
| --- | --- | --- |
| `aegis` | 0.9.12 | 0.9.15 |
| `aes` | 0.9.1 | 0.9.3 |
| `aes-gcm` | 0.11.0 | 0.11.1 |
| `ahash` | 0.8.12 | — |
| `aho-corasick` | 1.1.4 | 1.1.5 |
| `android_system_properties` | 0.1.5 | 0.1.6 |
| `anyhow` | 1.0.102 | 1.0.104 |
| `arc-swap` | 1.9.1 | 1.9.2 |
| `arrayref` | 0.3.9 | — |
| `arrayvec` | 0.7.6 | 0.7.8 |
| `async-compression` | 0.4.42 | 0.4.46 |
| `async-trait` | 0.1.89 | 0.1.92 |
| `asyncband` | — | 0.7.2 |
| `aws-config` | 1.8.15 | 1.12.0 |
| `aws-credential-types` | 1.2.14 | 1.3.0 |
| `aws-lc-rs` | 1.17.0 | 1.18.1 |
| `aws-lc-sys` | 0.41.0 | 0.45.0 |
| `aws-runtime` | 1.7.2 | 1.9.2 |
| `aws-sdk-sso` | 1.97.0 | 1.109.0 |
| `aws-sdk-ssooidc` | 1.99.0 | 1.111.0 |
| `aws-sdk-sts` | 1.102.0 | 1.114.0 |
| `aws-sigv4` | 1.4.2 | 1.5.1 |
| `aws-smithy-async` | 1.2.14 | 1.3.0 |
| `aws-smithy-http` | 0.63.6 | 0.64.0 |
| `aws-smithy-http-client` | 1.1.13 | 1.4.0 |
| `aws-smithy-json` | 0.62.7 | 0.63.0 |
| `aws-smithy-observability` | 0.2.6 | 0.3.0 |
| `aws-smithy-query` | 0.60.15 | 0.62.0 |
| `aws-smithy-runtime` | 1.11.3 | 1.14.0 |
| `aws-smithy-runtime-api` | 1.12.3 | 1.16.0 |
| `aws-smithy-runtime-api-macros` | 1.0.0 | 1.1.0 |
| `aws-smithy-schema` | 0.1.0 | 0.2.0 |
| `aws-smithy-types` | 1.5.0 | 1.6.3 |
| `aws-smithy-xml` | 0.60.15 | 0.62.0 |
| `aws-types` | 1.3.16 | 1.6.0 |
| `base64` | 0.22.1 | 0.22.1, 0.23.1 |
| `bitflags` | 2.13.0 | 2.13.2 |
| `blake3` | 1.8.5 | 1.8.7 |
| `bytemuck` | 1.25.0 | 1.25.2 |
| `bytes` | 1.12.0 | 1.12.1 |
| `bytesize` | 2.6.0 | 2.7.0 |
| `cc` | 1.2.64 | 1.4.5 |
| `cfg_aliases` | 0.2.1 | 0.2.2 |
| `chacha20` | 0.10.0 | 0.10.2 |
| `clap` | 4.6.1 | 4.6.6 |
| `clap_builder` | 4.6.0 | 4.6.6 |
| `clap_derive` | 4.6.1 | 4.6.4 |
| `cmsketch` | 0.2.4 | — |
| `combine` | 4.6.7 | 4.6.8 |
| `compression-codecs` | 0.4.38 | 0.4.41 |
| `compression-core` | 0.4.32 | 0.4.33 |
| `cpufeatures` | 0.2.17, 0.3.0 | 0.2.17, 0.3.1 |
| `crc32fast` | 1.5.0 | 1.5.1 |
| `crossbeam-epoch` | 0.9.18 | 0.9.21 |
| `crossbeam-utils` | 0.8.21 | 0.8.23 |
| `data-encoding` | 2.11.0 | 2.11.1 |
| `datasketches` | — | 0.3.0 |
| `displaydoc` | 0.2.6 | 0.2.7 |
| `either` | 1.16.0 | 1.18.0 |
| `enumset` | 1.1.13 | 1.1.14 |
| `event-listener` | 5.4.1 | 5.4.2 |
| `eyre` | 0.6.12 | 0.6.14 |
| `fastrand` | 2.4.1 | 2.5.0 |
| `faststr` | 0.2.34 | — |
| `find-msvc-tools` | 0.1.9 | 0.1.12 |
| `flate2` | 1.1.9 | 1.1.10 |
| `foyer` | 0.22.3 | 0.22.6 |
| `foyer-common` | 0.22.3 | 0.22.6 |
| `foyer-memory` | 0.22.3 | 0.22.6 |
| `foyer-storage` | 0.22.3 | 0.22.6 |
| `foyer-tokio` | 0.22.3 | 0.22.6 |
| `fs-err` | 3.3.0 | 3.3.1 |
| `futures` | 0.3.32 | 0.3.34 |
| `futures-channel` | 0.3.32 | 0.3.34 |
| `futures-core` | 0.3.32 | 0.3.34 |
| `futures-executor` | 0.3.32 | 0.3.34 |
| `futures-io` | 0.3.32 | 0.3.34 |
| `futures-macro` | 0.3.32 | 0.3.34 |
| `futures-sink` | 0.3.32 | 0.3.34 |
| `futures-task` | 0.3.32 | 0.3.34 |
| `futures-util` | 0.3.32 | 0.3.34 |
| `h2` | 0.4.16 | 0.4.19 |
| `hashbrown` | 0.14.5, 0.15.5, 0.16.1, 0.17.1 | 0.14.5, 0.15.5, 0.17.1
|
| `hermit-abi` | 0.5.2 | 0.5.3 |
| `hmac` | 0.12.1, 0.13.0 | 0.13.0 |
| `http` | 0.2.12, 1.4.2 | 0.2.12, 1.5.0 |
| `http-body` | 0.4.6, 1.0.1 | 0.4.6, 1.1.0 |
| `http-body-util` | 0.1.3 | 0.1.5 |
| `hybrid-array` | 0.4.12 | 0.4.15 |
| `hyper` | 1.10.1 | 1.11.1 |
| `icu_collections` | 2.2.0 | 2.3.0 |
| `icu_locale_core` | 2.2.0 | 2.3.0 |
| `icu_normalizer` | 2.2.0 | 2.3.0 |
| `icu_normalizer_data` | 2.2.0 | 2.3.0 |
| `icu_properties` | 2.2.0 | 2.3.0 |
| `icu_properties_data` | 2.2.0 | 2.3.0 |
| `icu_provider` | 2.2.0 | 2.3.1 |
| `indexmap` | 2.14.0 | 2.14.2 |
| `io-uring` | 0.7.12 | 0.7.15 |
| `ipnet` | 2.12.0 | 2.12.2 |
| `jobserver` | 0.1.34 | 0.1.35 |
| `js-sys` | 0.3.102 | 0.3.105 |
| `libc` | 0.2.186 | 0.2.189 |
| `linkme` | 0.3.36 | 0.3.37 |
| `linkme-impl` | 0.3.36 | 0.3.37 |
| `litemap` | 0.8.2 | 0.8.3 |
| `log` | 0.4.32 | 0.4.34 |
| `lru` | 0.18.2 | 0.18.4 |
| `mea` | 0.6.4 | — |
| `memchr` | 2.8.2 | 2.8.3 |
| `miniz_oxide` | 0.8.9 | 0.9.1 |
| `mio` | 1.2.1 | 1.2.3 |
| `munge` | 0.4.7 | — |
| `munge_macro` | 0.4.7 | — |
| `num-bigint` | 0.4.6 | 0.4.8 |
| `num-integer` | 0.1.46 | 0.1.47 |
| `object_store` | 0.14.0 | 0.14.1 |
| `pem` | 3.0.6 | 4.0.0 |
| `pkg-config` | 0.3.33 | 0.3.34 |
| `polyval` | 0.7.1 | 0.7.3 |
| `potential_utf` | 0.1.5 | 0.1.6 |
| `proc-macro2` | 1.0.106 | 1.0.107 |
| `ptr_meta` | 0.3.1 | — |
| `ptr_meta_derive` | 0.3.1 | — |
| `quick-xml` | 0.40.1, 0.41.0 | 0.41.0 |
| `quinn` | 0.11.9 | 0.11.11 |
| `quinn-proto` | 0.11.14 | 0.11.17 |
| `quinn-udp` | 0.5.14 | 0.5.15 |
| `quote` | 1.0.45 | 1.0.47 |
| `rancor` | 0.1.1 | — |
| `rand` | 0.10.1, 0.8.6, 0.9.4 | 0.10.2, 0.8.8, 0.9.5 |
| `rand_pcg` | — | 0.10.2 |
| `rcgen` | 0.14.8 | 0.14.10 |
| `ref-cast` | 1.0.25 | 1.0.27 |
| `ref-cast-impl` | 1.0.25 | 1.0.27 |
| `regex` | — | 1.13.1 |
| `regex-automata` | 0.4.14 | 0.4.18 |
| `rend` | 0.5.3 | — |
| `reqwest` | 0.13.4 | 0.13.5 |
| `rkyv` | 0.8.16 | — |
| `rkyv_derive` | 0.8.16 | — |
| `rust_decimal` | 1.42.1 | 1.43.0 |
| `rustc-hash` | 2.1.2 | 2.1.3 |
| `rustls` | 0.23.40 | 0.23.45 |
| `rustls-pki-types` | 1.14.1 | 1.15.1 |
| `rustls-webpki` | 0.103.13 | 0.103.15 |
| `rustversion` | 1.0.22 | 1.0.23 |
| `s3s` | 0.14.1 | 0.15.0 |
| `schemars` | 1.2.1 | 1.2.2 |
| `schemars_derive` | 1.2.1 | 1.2.2 |
| `serde` | 1.0.228 | 1.0.229 |
| `serde_core` | 1.0.228 | 1.0.229 |
| `serde_derive` | 1.0.228 | 1.0.229 |
| `serde_derive_internals` | 0.29.1 | 0.30.0 |
| `serde_json` | 1.0.150 | 1.0.151 |
| `sha1` | 0.10.6, 0.11.0 | 0.10.7, 0.11.0 |
| `sha2` | 0.10.9, 0.11.0 | 0.11.0 |
| `simd-adler32` | 0.3.9 | 0.3.10 |
| `simd_cesu8` | 1.1.1 | 1.2.0 |
| `slatedb` | 0.15.0 | 0.16.0 |
| `slatedb-common` | 0.15.0 | 0.16.0 |
| `slatedb-txn-obj` | 0.15.0 | 0.16.0 |
| `smallvec` | 1.15.2 | 1.16.0 |
| `socket2` | 0.6.4 | 0.6.5 |
| `sonic-number` | 0.1.2 | — |
| `sonic-rs` | 0.5.8 | — |
| `sonic-simd` | 0.1.4 | — |
| `spin` | 0.10.0 | 0.10.1 |
| `syn` | 2.0.118 | 2.0.119, 3.0.5 |
| `thiserror` | 1.0.69, 2.0.18 | 1.0.69, 2.0.20 |
| `thiserror-impl` | 1.0.69, 2.0.18 | 1.0.69, 2.0.20 |
| `thread_local` | 1.1.9 | 1.1.10 |
| `time` | 0.3.49 | 0.3.55 |
| `time-macros` | 0.2.29 | 0.2.32 |
| `tinystr` | 0.8.3 | 0.8.4 |
| `tinyvec` | 1.11.0 | 1.13.2 |
| `tokio-macros` | 2.7.0 | 2.7.2 |
| `tokio-rustls` | 0.26.4 | 0.26.5 |
| `tokio-stream` | 0.1.18 | 0.1.19 |
| `tokio-util` | 0.7.18 | 0.7.19 |
| `tower-http` | 0.6.11, 0.7.0 | 0.6.11, 0.7.1 |
| `twox-hash` | 2.1.2 | 2.1.4 |
| `uuid` | 1.23.3 | 1.26.1 |
| `wasm-bindgen` | 0.2.125 | 0.2.128 |
| `wasm-bindgen-futures` | 0.4.75 | 0.4.78 |
| `wasm-bindgen-macro` | 0.2.125 | 0.2.128 |
| `wasm-bindgen-macro-support` | 0.2.125 | 0.2.128 |
| `wasm-bindgen-shared` | 0.2.125 | 0.2.128 |
| `web-sys` | 0.3.102 | 0.3.105 |
| `webpki-root-certs` | 1.0.8 | 1.0.9 |
| `windows-sys` | 0.52.0, 0.59.0, 0.60.2, 0.61.2 | 0.52.0, 0.59.0,
0.61.2 |
| `windows-targets` | 0.52.6, 0.53.5 | 0.52.6 |
| `windows_aarch64_gnullvm` | 0.52.6, 0.53.1 | 0.52.6 |
| `windows_aarch64_msvc` | 0.52.6, 0.53.1 | 0.52.6 |
| `windows_i686_gnu` | 0.52.6, 0.53.1 | 0.52.6 |
| `windows_i686_gnullvm` | 0.52.6, 0.53.1 | 0.52.6 |
| `windows_i686_msvc` | 0.52.6, 0.53.1 | 0.52.6 |
| `windows_x86_64_gnu` | 0.52.6, 0.53.1 | 0.52.6 |
| `windows_x86_64_gnullvm` | 0.52.6, 0.53.1 | 0.52.6 |
| `windows_x86_64_msvc` | 0.52.6, 0.53.1 | 0.52.6 |
| `writeable` | 0.6.3 | 0.6.4 |
| `xxhash-rust` | 0.8.15 | 0.8.18 |
| `zerocopy` | 0.8.52 | 0.8.57 |
| `zerocopy-derive` | 0.8.52 | 0.8.57 |
| `zerotrie` | 0.2.4 | 0.2.5 |
| `zerovec` | 0.11.6 | 0.11.8 |
| `zerovec-derive` | 0.11.3 | 0.11.6 |
| `zlib-rs` | — | 0.6.7 |
| `zmij` | 1.0.21 | 1.0.23 |
| `zstd` | 0.13.3 | 0.13.3, 0.14.0 |
| `zstd-safe` | 7.2.4 | 7.3.0, 8.0.0 |
| `zstd-sys` | 2.0.16+zstd.1.5.7 | 2.1.0+zstd.1.5.7 |
---
Cargo.lock | 1322 ++++++++++--------
Cargo.toml | 20 +-
deny.toml | 2 -
lite/src/backend/basins.rs | 35 +-
lite/src/backend/bgtasks/basin_deletion.rs | 48 +-
lite/src/backend/bgtasks/stream_doe.rs | 66 +-
lite/src/backend/bgtasks/stream_trim.rs | 102 +-
lite/src/backend/core.rs | 3 +-
lite/src/backend/mod.rs | 3 +
lite/src/backend/read.rs | 16 +-
lite/src/backend/store.rs | 8 +
lite/src/backend/streamer.rs | 11 +-
lite/src/backend/streams.rs | 8 +-
lite/src/backend/test_util.rs | 18 +
lite/tests/backend/common/setup.rs | 52 +-
lite/tests/backend/control_plane/basin.rs | 74 +
lite/tests/backend/control_plane/stream.rs | 28 +
sdk/Cargo.toml | 2 +-
sim/Cargo.lock | 1445 +++++++++-----------
sim/Cargo.toml | 14 +-
20 files changed, 1700 insertions(+), 1577 deletions(-)
create mode 100644 lite/src/backend/test_util.rs
diff --git a/Cargo.lock b/Cargo.lock
index c3f99fa9..10792de7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -24,7 +24,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1973cfbc1a2daf9cf550e74e1f088c28e7f7d8c1e1418fb6c9dc5184b7e84c99"
dependencies = [
"crypto-common 0.2.2",
- "inout 0.2.2",
+ "inout",
]
[[package]]
@@ -39,46 +39,36 @@ dependencies = [
[[package]]
name = "aes"
-version = "0.8.4"
+version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
+checksum = "35f0f96ce78e38c3dc6d8948aa8163d06385be74000f3c7a95bf1eef35d3ea32"
dependencies = [
- "cfg-if",
- "cipher 0.4.4",
- "cpufeatures 0.2.17",
-]
-
-[[package]]
-name = "aes"
-version = "0.9.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8eb277bec05f56a0e0591f155a484cbd0f4f07ff2905051a48c72f004f7ed58"
-dependencies = [
- "cipher 0.5.2",
+ "cipher",
"cpubits",
- "cpufeatures 0.3.0",
+ "cpufeatures 0.3.1",
+ "zeroize",
]
[[package]]
name = "aes-gcm"
-version = "0.11.0"
+version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fdf011db2e21ce0d575593d749db5554b47fed37aff429e4dc50bc91ac93a028"
+checksum = "7f2b8006a0c83f52b62ba44a97b58bf76fe2f70a329e588f67f89691d93d498f"
dependencies = [
"aead",
- "aes 0.9.2",
- "cipher 0.5.2",
+ "aes",
+ "cipher",
"ctr",
+ "ctutils",
"ghash",
- "subtle",
"zeroize",
]
[[package]]
name = "aho-corasick"
-version = "1.1.4"
+version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
+checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba"
dependencies = [
"memchr",
]
@@ -97,9 +87,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "android_system_properties"
-version = "0.1.5"
+version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc"
dependencies = [
"libc",
]
@@ -162,9 +152,9 @@ checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470"
[[package]]
name = "apple-native-keyring-store"
-version = "1.0.1"
+version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "797f94b6a53d7d10b56dc18290e0d40a2158352f108bb4ff32350825081a9f29"
+checksum = "2b350bfd03649e07aa05c0a81b3e15934374e585c98204a57e20b9d49f49bb9a"
dependencies = [
"keyring-core",
"log",
@@ -186,12 +176,6 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236"
-[[package]]
-name = "arrayref"
-version = "0.3.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb"
-
[[package]]
name = "arrayvec"
version = "0.7.8"
@@ -210,7 +194,7 @@ dependencies = [
"nom",
"num-traits",
"rusticata-macros",
- "thiserror 2.0.19",
+ "thiserror 2.0.20",
"time",
]
@@ -300,9 +284,9 @@ dependencies = [
[[package]]
name = "async-compression"
-version = "0.4.43"
+version = "0.4.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3976abdc8fe7d1133d43d304afd42abdf5bc3e1319d263d223bde07b5efc4be8"
+checksum = "4f10dafd0c8d2e51ae9a748805777613ed0bbe17bf586b76c8311f45c020a32f"
dependencies = [
"compression-codecs",
"compression-core",
@@ -430,15 +414,21 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
[[package]]
name = "async-trait"
-version = "0.1.91"
+version = "0.1.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae36dc4177970ef04fde5178d3e2429882def40e57a451f919c098f72baa6cec"
+checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667"
dependencies = [
"proc-macro2",
"quote",
- "syn 3.0.3",
+ "syn 3.0.5",
]
+[[package]]
+name = "asyncband"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2d85fd3d291fabcc40c7232c92c280ec1754fd7b5d7ea769f143222143e179a"
+
[[package]]
name = "atomic"
version = "0.6.1"
@@ -462,9 +452,9 @@ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
[[package]]
name = "aws-config"
-version = "1.10.1"
+version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b180a3c8b55960db3426d8964b8745e652466a1a49fe1a2eda828046d30b5e4"
+checksum = "b8d7b388a9fc3a6db15a5ec778c38b354eff1364882c94d08e0252f7a47dcaa4"
dependencies = [
"aws-credential-types",
"aws-runtime",
@@ -529,9 +519,9 @@ dependencies = [
[[package]]
name = "aws-runtime"
-version = "1.9.1"
+version = "1.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c9007227e10b5fed2f3e0a2beff489211e2b5604c400b7a9d5d81ca9d64c24bb"
+checksum = "ef47857a1d4488b528f4a5d5715fa7c3300820897824152234d3fa22b1426657"
dependencies = [
"aws-credential-types",
"aws-sigv4",
@@ -554,9 +544,9 @@ dependencies = [
[[package]]
name = "aws-sdk-sso"
-version = "1.105.0"
+version = "1.109.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ffd0fbe7873cb548a7aa60f9573c268fff94155397fd4f14dc9f1ecaaab8516"
+checksum = "c3cfe74df5d9ad2fedd691973ad3521ebf4f27a3c68c792556686aedb5519bab"
dependencies = [
"arc-swap",
"aws-credential-types",
@@ -580,9 +570,9 @@ dependencies = [
[[package]]
name = "aws-sdk-ssooidc"
-version = "1.107.0"
+version = "1.111.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "175763eb222a46377df7aa257a3bca980ab3e96703fefc8f4d0b8da6ad2e254c"
+checksum = "81b0ec31ed6191bd11350aae4b2004198f2db21350cb0a20c57e0a92e55dd161"
dependencies = [
"arc-swap",
"aws-credential-types",
@@ -606,9 +596,9 @@ dependencies = [
[[package]]
name = "aws-sdk-sts"
-version = "1.110.0"
+version = "1.114.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd8b14781dfbff48984017d57167b6ea0b6471c6920ec52b44a2677c7feb3c13"
+checksum = "ef45745026107ec30c4ef86bd8ae4b002e7e5f6a86e4225240bdf6b06a0b944a"
dependencies = [
"arc-swap",
"aws-credential-types",
@@ -644,11 +634,11 @@ dependencies = [
"bytes",
"form_urlencoded",
"hex",
- "hmac 0.13.0",
+ "hmac",
"http 0.2.12",
"http 1.5.0",
"percent-encoding",
- "sha2 0.11.0",
+ "sha2",
"time",
"tracing",
]
@@ -687,9 +677,9 @@ dependencies = [
[[package]]
name = "aws-smithy-http-client"
-version = "1.2.0"
+version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "635d23afda0a6ab48d666c4d447c4873e8d1e83518a2be2093122397e50b838e"
+checksum = "ebfd138fac0337cee7516c352757ea73b9f2266e57d0bcb5bc70e9547e45aef1"
dependencies = [
"aws-smithy-async",
"aws-smithy-runtime-api",
@@ -744,9 +734,9 @@ dependencies = [
[[package]]
name = "aws-smithy-runtime"
-version = "1.12.1"
+version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07505b34e8f4b3591a4fa69e9792b52289b95488dbbc68c3c0075b7bedb245e1"
+checksum = "b82e438d30e02a825d363bd639a9efaed68a8089d86101054b0081e7e0d3e606"
dependencies = [
"aws-smithy-async",
"aws-smithy-http",
@@ -770,9 +760,9 @@ dependencies = [
[[package]]
name = "aws-smithy-runtime-api"
-version = "1.14.0"
+version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b98f2e1fd67ec06618f9c291e5e495a468e60519e44c9c1979cd0521f3affdb"
+checksum = "9c054752dd9e4dc73d0b75748c99ac2d0feafbf2f25c7b0516f03a3534161223"
dependencies = [
"aws-smithy-async",
"aws-smithy-runtime-api-macros",
@@ -810,9 +800,9 @@ dependencies = [
[[package]]
name = "aws-smithy-types"
-version = "1.6.1"
+version = "1.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d6dc683efb34b9e755675b37fedbe0103141e5b6df7bdc9eb6967756a8c167d8"
+checksum = "8f94d16e797ec62cd999fc9d5942b48fa7050c3093ddadff48e4d7528d16fcb9"
dependencies = [
"base64-simd",
"bytes",
@@ -845,9 +835,9 @@ dependencies = [
[[package]]
name = "aws-types"
-version = "1.5.0"
+version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eec1cd5469f328c782dc3e33d4153cf118a54e33cbb3356d60d16f89883e1f94"
+checksum = "209f3a6d82a6e9e5f94abbed94c7a26e1c052341002bf57a5fb5481f625896fc"
dependencies = [
"aws-credential-types",
"aws-smithy-async",
@@ -964,7 +954,7 @@ dependencies = [
"addr2line",
"cfg-if",
"libc",
- "miniz_oxide",
+ "miniz_oxide 0.8.9",
"object",
"rustc-demangle",
"windows-link 0.2.1",
@@ -985,6 +975,12 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+[[package]]
+name = "base64"
+version = "0.23.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5"
+
[[package]]
name = "base64-simd"
version = "0.8.0"
@@ -1036,25 +1032,30 @@ dependencies = [
[[package]]
name = "bitflags"
-version = "2.13.1"
+version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06"
dependencies = [
"serde_core",
]
[[package]]
name = "blake3"
-version = "1.8.5"
+version = "1.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0aa83c34e62843d924f905e0f5c866eb1dd6545fc4d719e803d9ba6030371fce"
+checksum = "6d9e454fc11f76977dc803893aff6304ed33d6a26efae8696573bea74baa27ae"
dependencies = [
- "arrayref",
"arrayvec",
"cc",
"cfg-if",
"constant_time_eq",
- "cpufeatures 0.3.0",
+ "cpufeatures 0.3.1",
]
[[package]]
@@ -1077,18 +1078,18 @@ dependencies = [
[[package]]
name = "block-padding"
-version = "0.3.3"
+version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93"
+checksum = "710f1dd022ef4e93f8a438b4ba958de7f64308434fa6a87104481645cc30068b"
dependencies = [
- "generic-array",
+ "hybrid-array",
]
[[package]]
name = "blocking"
-version = "1.6.2"
+version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21"
+checksum = "a70e4329df6cb94385eed412ec92375c3cdd8a6e502493d1229b6414e4036dfa"
dependencies = [
"async-channel",
"async-task",
@@ -1099,13 +1100,13 @@ dependencies = [
[[package]]
name = "bollard"
-version = "0.20.2"
+version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee04c4c84f1f811b017f2fbb7dd8815c976e7ca98593de9c1e2afad0f636bff4"
+checksum = "dbe8358268799ebb3e4df23cb9d47f4c72bbc4f5247e2fa6a1bf7b6c0baea220"
dependencies = [
"async-stream",
- "base64",
- "bitflags",
+ "base64 0.22.1",
+ "bitflags 2.13.2",
"bollard-buildkit-proto",
"bollard-stubs",
"bytes",
@@ -1123,7 +1124,7 @@ dependencies = [
"log",
"num",
"pin-project-lite",
- "rand 0.9.5",
+ "rand 0.10.2",
"rustls",
"rustls-native-certs",
"rustls-pki-types",
@@ -1131,7 +1132,7 @@ dependencies = [
"serde_derive",
"serde_json",
"serde_urlencoded",
- "thiserror 2.0.19",
+ "thiserror 2.0.20",
"time",
"tokio",
"tokio-stream",
@@ -1144,24 +1145,23 @@ dependencies = [
[[package]]
name = "bollard-buildkit-proto"
-version = "0.7.0"
+version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85a885520bf6249ab931a764ffdb87b0ceef48e6e7d807cfdb21b751e086e1ad"
+checksum = "b5c97450e79c7c565302dd92e86b08823b47550fcb4fc5ce910194d1b087a1a3"
dependencies = [
"prost",
"prost-types",
"tonic",
"tonic-prost",
- "ureq",
]
[[package]]
name = "bollard-stubs"
-version = "1.52.1-rc.29.1.3"
+version = "1.53.1-rc.29.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f0a8ca8799131c1837d1282c3f81f31e76ceb0ce426e04a7fe1ccee3287c066"
+checksum = "ce412eb6f7096743011dc3cb5c674caeb24ced61d8c498fe07cf7998a4fea889"
dependencies = [
- "base64",
+ "base64 0.22.1",
"bollard-buildkit-proto",
"bytes",
"prost",
@@ -1182,9 +1182,9 @@ dependencies = [
[[package]]
name = "bstr"
-version = "1.13.0"
+version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f7dc094d718f2e1c1559ad110e27eeaae14a5465d3d56dd6dbd793079fbd530"
+checksum = "6bb31b46c14244e20ee9984b11bf5c992b91fb6939fea616e3512c8baecdbe5f"
dependencies = [
"memchr",
"regex-automata",
@@ -1199,9 +1199,9 @@ checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
[[package]]
name = "bytecheck"
-version = "0.8.2"
+version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0caa33a2c0edca0419d15ac723dff03f1956f7978329b1e3b5fdaaaed9d3ca8b"
+checksum = "26333eeac754f0ad8a6bcd0eb0ac012156302e4e16b852b72ee399aea4f12c29"
dependencies = [
"bytecheck_derive",
"ptr_meta",
@@ -1211,13 +1211,13 @@ dependencies = [
[[package]]
name = "bytecheck_derive"
-version = "0.8.2"
+version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89385e82b5d1821d2219e0b095efa2cc1f246cbf99080f3be46a1a85c0d392d9"
+checksum = "46d07918caa9eeaaf06b7873925c53a61daac173539b4f7715090745e44e4e69"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.119",
+ "syn 3.0.5",
]
[[package]]
@@ -1259,9 +1259,9 @@ dependencies = [
[[package]]
name = "bytesize"
-version = "2.6.0"
+version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "351a3e803ee3c6eaeee6b00076b767514b37c32a73d326c3ec7abddb7d6c3493"
+checksum = "7354288c522e7e980fafd2075d63d1285794c3a6a16cdd492f189ea406e5f18b"
[[package]]
name = "castaway"
@@ -1274,18 +1274,18 @@ dependencies = [
[[package]]
name = "cbc"
-version = "0.1.2"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6"
+checksum = "ce2dc9ee5f88d11e0beb842c88b33c8a5cf0d1329c4b19494af42b07dbfe8896"
dependencies = [
- "cipher 0.4.4",
+ "cipher",
]
[[package]]
name = "cc"
-version = "1.4.0"
+version = "1.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
+checksum = "005ec2760ca554fae18df7a11195552ec576cd665632a881bc011d5bb2fd4d80"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -1307,12 +1307,12 @@ checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527"
[[package]]
name = "chacha20"
-version = "0.10.1"
+version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81"
+checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06"
dependencies = [
"cfg-if",
- "cpufeatures 0.3.0",
+ "cpufeatures 0.3.1",
"rand_core 0.10.1",
]
@@ -1330,16 +1330,6 @@ dependencies = [
"windows-link 0.2.1",
]
-[[package]]
-name = "cipher"
-version = "0.4.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
-dependencies = [
- "crypto-common 0.1.7",
- "inout 0.1.4",
-]
-
[[package]]
name = "cipher"
version = "0.5.2"
@@ -1348,14 +1338,14 @@ checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c"
dependencies = [
"block-buffer 0.12.1",
"crypto-common 0.2.2",
- "inout 0.2.2",
+ "inout",
]
[[package]]
name = "clap"
-version = "4.6.5"
+version = "4.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "301b56658598e48f3648647ac6fc887be7e7108eddfa4e9b63fcf3ec58c0cadf"
+checksum = "473c7e07f409a8d772161724aa8db6a765a2532a70f9667eeb7b49d3d02fbdca"
dependencies = [
"clap_builder",
"clap_derive",
@@ -1363,9 +1353,9 @@ dependencies = [
[[package]]
name = "clap_builder"
-version = "4.6.5"
+version = "4.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94a65403d1a1bd28f7dc68eb8506e8874808ee5eecb59298de588e2e1407a078"
+checksum = "7b48fea5a88e9ae728a2dcbedbfc0e730f7d60da42e1cb049a83c9fb8b789889"
dependencies = [
"anstream",
"anstyle",
@@ -1382,7 +1372,7 @@ dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote",
- "syn 3.0.3",
+ "syn 3.0.5",
]
[[package]]
@@ -1406,12 +1396,6 @@ version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a"
-[[package]]
-name = "cmsketch"
-version = "0.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7ee2cfacbd29706479902b06d75ad8f1362900836aa32799eabc7e004bfd854"
-
[[package]]
name = "color-print"
version = "0.3.7"
@@ -1445,14 +1429,14 @@ version = "3.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34"
dependencies = [
- "windows-sys 0.59.0",
+ "windows-sys 0.61.2",
]
[[package]]
name = "combine"
-version = "4.6.7"
+version = "4.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
+checksum = "cfc320937d09e6de266b31b9afb480f197d7a861be86be7cb2ea7e5d1bfffc5e"
dependencies = [
"bytes",
"memchr",
@@ -1475,22 +1459,22 @@ dependencies = [
[[package]]
name = "compression-codecs"
-version = "0.4.38"
+version = "0.4.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce2548391e9c1929c21bf6aa2680af86fe4c1b33e6cea9ac1cfeec0bd11218cf"
+checksum = "58a6d0db8759036a783bc7c3f7a07f8cef3bf9470eb1db3bc86e8bcd1c5d0fe8"
dependencies = [
"compression-core",
"flate2",
"memchr",
- "zstd",
- "zstd-safe",
+ "zstd 0.14.0",
+ "zstd-safe 8.0.0",
]
[[package]]
name = "compression-core"
-version = "0.4.32"
+version = "0.4.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc14f565cf027a105f7a44ccf9e5b424348421a1d8952a8fc9d499d313107789"
+checksum = "6e8ccc4ea9f6acc32d102c0f6d471d11d913ad15f20c04de743374861fa1d414"
[[package]]
name = "concurrent-queue"
@@ -1516,16 +1500,16 @@ dependencies = [
"serde-untagged",
"serde_core",
"serde_json",
- "toml 1.1.4+spec-1.1.0",
+ "toml 1.1.6+spec-1.1.0",
"winnow 1.0.4",
"yaml-rust2",
]
[[package]]
name = "console"
-version = "0.16.4"
+version = "0.16.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4fe5f465a4f6fee88fad41b85d990f84c835335e85b5d9e6e63e0d06d28cba7c"
+checksum = "e96a4956774c13c126a8b5af4daa79384f4d826534c95a02d76afb39e2ab64e3"
dependencies = [
"encode_unicode",
"libc",
@@ -1601,6 +1585,12 @@ dependencies = [
"winapi",
]
+[[package]]
+name = "core_detect"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f8f80099a98041a3d1622845c271458a2d73e688351bf3cb999266764b81d48"
+
[[package]]
name = "cpubits"
version = "0.1.1"
@@ -1618,9 +1608,9 @@ dependencies = [
[[package]]
name = "cpufeatures"
-version = "0.3.0"
+version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
+checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566"
dependencies = [
"libc",
]
@@ -1637,18 +1627,18 @@ dependencies = [
[[package]]
name = "crc32fast"
-version = "1.5.0"
+version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
+checksum = "8498c871161e1742aaa9d52551b2d6ebdd4c3d45a3be423e3728f33b955be550"
dependencies = [
"cfg-if",
]
[[package]]
name = "crossbeam-epoch"
-version = "0.9.20"
+version = "0.9.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f"
+checksum = "dc74980687109a3b14c72fd458107bf0baa1da1a1a805e178d15501ba9b86d9d"
dependencies = [
"crossbeam-utils",
]
@@ -1665,9 +1655,9 @@ dependencies = [
[[package]]
name = "crossbeam-utils"
-version = "0.8.22"
+version = "0.8.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17"
+checksum = "a31eee39dddec8330830986fcd7625edb5a24ec90ea038215273bbc3adb08ac6"
[[package]]
name = "crunchy"
@@ -1702,7 +1692,7 @@ version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baaca1c4b237092596f64d571e9db6ce4109c4ef9742e27590f1709594461f21"
dependencies = [
- "cipher 0.5.2",
+ "cipher",
]
[[package]]
@@ -1726,12 +1716,12 @@ dependencies = [
[[package]]
name = "darling"
-version = "0.23.0"
+version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d"
+checksum = "ed17f5901b6630b993ca003def43f2f8ef4014fc13b047b57aad617ff32bc2ec"
dependencies = [
- "darling_core 0.23.0",
- "darling_macro 0.23.0",
+ "darling_core 0.24.1",
+ "darling_macro 0.24.1",
]
[[package]]
@@ -1749,15 +1739,15 @@ dependencies = [
[[package]]
name = "darling_core"
-version = "0.23.0"
+version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0"
+checksum = "6837e2cf7485aaae18f86181d2f0e9a7ed297a025e220aeabf63fdebd3a2ddff"
dependencies = [
"ident_case",
"proc-macro2",
"quote",
"strsim",
- "syn 2.0.119",
+ "syn 3.0.5",
]
[[package]]
@@ -1773,13 +1763,13 @@ dependencies = [
[[package]]
name = "darling_macro"
-version = "0.23.0"
+version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d"
+checksum = "2ac7135c3ef02b2f7833bbeb1be5ba7f966dcde8a87c6b87f65a778d71a02785"
dependencies = [
- "darling_core 0.23.0",
+ "darling_core 0.24.1",
"quote",
- "syn 2.0.119",
+ "syn 3.0.5",
]
[[package]]
@@ -1798,9 +1788,46 @@ dependencies = [
[[package]]
name = "data-encoding"
-version = "2.11.0"
+version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8"
+checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06"
+
+[[package]]
+name = "datasketches"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46c4cf71a36b46dcfc00e5014c0c20ccad2b1b6a008304d7d57d2749b2d41b3d"
+
+[[package]]
+name = "defmt"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2953bfe4f93bbd20cc71198842756f77d161884c99ebbabc41d80231ded88d1"
+dependencies = [
+ "bitflags 1.3.2",
+ "defmt-macros",
+]
+
+[[package]]
+name = "defmt-macros"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bad9c72e7ca2137e0dc3813245a0d282fd6daad32fd800af018306a9169b5fe8"
+dependencies = [
+ "defmt-parser",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "defmt-parser"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e"
+dependencies = [
+ "thiserror 2.0.20",
+]
[[package]]
name = "der-parser"
@@ -1839,7 +1866,6 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer 0.10.4",
"crypto-common 0.1.7",
- "subtle",
]
[[package]]
@@ -1856,9 +1882,9 @@ dependencies = [
[[package]]
name = "dirs"
-version = "6.0.0"
+version = "7.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
+checksum = "8d57d423b3c82e89b9a24ca3091fee61f456a26edbd28d26c65906f4bc1dcd8f"
dependencies = [
"dirs-sys",
]
@@ -1872,7 +1898,7 @@ dependencies = [
"libc",
"option-ext",
"redox_users",
- "windows-sys 0.59.0",
+ "windows-sys 0.61.2",
]
[[package]]
@@ -1883,7 +1909,7 @@ checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8"
dependencies = [
"proc-macro2",
"quote",
- "syn 3.0.3",
+ "syn 3.0.5",
]
[[package]]
@@ -1901,7 +1927,7 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29547a1dc60885a552306986316bc9701ba120c1a8db6769fa68691529ad373d"
dependencies = [
- "base64",
+ "base64 0.22.1",
"serde",
"serde_json",
]
@@ -1926,7 +1952,7 @@ checksum = "f88959de2d447fd3eddcf1909d1f19fe084e27a056a6904203dc5d8b9e771c1e"
dependencies = [
"rust_decimal",
"serde",
- "thiserror 2.0.19",
+ "thiserror 2.0.20",
"time",
"winnow 0.6.26",
]
@@ -1939,9 +1965,9 @@ checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555"
[[package]]
name = "either"
-version = "1.17.0"
+version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d"
+checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34"
[[package]]
name = "encode_unicode"
@@ -1951,11 +1977,17 @@ checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
[[package]]
name = "encoding_rs"
-version = "0.8.35"
+version = "0.8.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
+checksum = "7b5ef0006ac9ab233c38522f5ae99cae3625151de8f706cacee1cba4b8e2832a"
dependencies = [
"cfg-if",
+ "core_detect",
+ "multiversion",
+ "multiversion_no_op",
+ "rustversion",
+ "scopeguard",
+ "simdutf8",
]
[[package]]
@@ -2030,7 +2062,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
- "windows-sys 0.59.0",
+ "windows-sys 0.61.2",
]
[[package]]
@@ -2065,10 +2097,11 @@ dependencies = [
[[package]]
name = "eyre"
-version = "0.6.12"
+version = "0.6.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec"
+checksum = "c08309dbcc659c5549a24ddb9b27027640641b282ef5768267c7e675558986a3"
dependencies = [
+ "autocfg",
"indenter",
"once_cell",
]
@@ -2130,9 +2163,9 @@ dependencies = [
[[package]]
name = "find-msvc-tools"
-version = "0.1.9"
+version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+checksum = "3e0f1c7c3a72c66fd80abe965175f7523475c0489a87d3ff9d6e8c87d87a9d2d"
[[package]]
name = "fixedbitset"
@@ -2146,18 +2179,18 @@ version = "25.12.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35f6839d7b3b98adde531effaf34f0c2badc6f4735d26fe74709d8e513a96ef3"
dependencies = [
- "bitflags",
+ "bitflags 2.13.2",
"rustc_version",
]
[[package]]
name = "flate2"
-version = "1.1.9"
+version = "1.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
+checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb"
dependencies = [
"crc32fast",
- "miniz_oxide",
+ "miniz_oxide 0.9.1",
"zlib-rs",
]
@@ -2199,18 +2232,18 @@ dependencies = [
[[package]]
name = "foyer"
-version = "0.22.3"
+version = "0.22.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b0abc0b87814989efa711f9becd9f26969820e2d3905db27d10969c4bd45890"
+checksum = "6a59f42276891c0a4ce683fcda1aa1b4fd1065d68116c264e4bf49b9d318a0ed"
dependencies = [
"anyhow",
+ "asyncband",
"equivalent",
"foyer-common",
"foyer-memory",
"foyer-storage",
"foyer-tokio",
"futures-util",
- "mea",
"mixtrics",
"pin-project",
"serde",
@@ -2219,9 +2252,9 @@ dependencies = [
[[package]]
name = "foyer-common"
-version = "0.22.3"
+version = "0.22.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3db80d5dece93adb7ad709c84578794724a9cba342a7e566c3551c7ec626789"
+checksum = "643b47d510032a01e5af70dca288b0c5ec531646c1a8392e793fb5b0c13bef30"
dependencies = [
"anyhow",
"bincode",
@@ -2246,21 +2279,21 @@ dependencies = [
[[package]]
name = "foyer-memory"
-version = "0.22.3"
+version = "0.22.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db907f40a527ca2aa2f40a5f68b32ea58aa70f050cd233518e9ffd402cfba6ce"
+checksum = "ae51f5089f3d9025ae77f17ea66d2489ac0f82fbb1d91462807bea174d486d82"
dependencies = [
"anyhow",
- "bitflags",
- "cmsketch",
+ "asyncband",
+ "bitflags 2.13.2",
+ "datasketches",
"equivalent",
"foyer-common",
"foyer-intrusive-collections",
"foyer-tokio",
"futures-util",
- "hashbrown 0.16.1",
- "itertools 0.14.0",
- "mea",
+ "hashbrown 0.17.1",
+ "itertools 0.15.0",
"mixtrics",
"parking_lot",
"paste",
@@ -2271,12 +2304,13 @@ dependencies = [
[[package]]
name = "foyer-storage"
-version = "0.22.3"
+version = "0.22.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1983f1db3d0710e9c9d5fc116d9202dccd41a2d1e032572224f1aff5520aa958"
+checksum = "118192f532ba013f0cdc607efc22324b9ed855baed185608af0daa986e835c6b"
dependencies = [
"allocator-api2",
"anyhow",
+ "asyncband",
"bytes",
"core_affinity",
"equivalent",
@@ -2287,26 +2321,25 @@ dependencies = [
"fs4",
"futures-core",
"futures-util",
- "hashbrown 0.16.1",
+ "hashbrown 0.17.1",
"io-uring",
- "itertools 0.14.0",
+ "itertools 0.15.0",
"libc",
"lz4",
- "mea",
"parking_lot",
"pin-project",
- "rand 0.9.5",
+ "rand 0.10.2",
"serde",
"tracing",
"twox-hash",
- "zstd",
+ "zstd 0.13.3",
]
[[package]]
name = "foyer-tokio"
-version = "0.22.3"
+version = "0.22.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f6577b05a7ffad0db555aedf00bfe52af818220fc4c1c3a7a12520896fc38627"
+checksum = "6741d1133dfaab64d3f8a9290bbfed3685084add28f8b6ee7a6264aa4dc26918"
dependencies = [
"tokio",
]
@@ -2349,9 +2382,9 @@ checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
[[package]]
name = "futures"
-version = "0.3.33"
+version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a88cf1f829d945f548cf8fec32c61b1f202b6d93b45848602fc02af4b12ad218"
+checksum = "9a31d2a3fbaaeb2af2368bbdd904aa8e812d3c04a1ee10d3171f52d556e5d0a3"
dependencies = [
"futures-channel",
"futures-core",
@@ -2364,9 +2397,9 @@ dependencies = [
[[package]]
name = "futures-channel"
-version = "0.3.33"
+version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "262590f4fe6afeb0bc83be1daa64e52657fe185690a958af7f3ad0e92085c5ae"
+checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4"
dependencies = [
"futures-core",
"futures-sink",
@@ -2374,15 +2407,15 @@ dependencies = [
[[package]]
name = "futures-core"
-version = "0.3.33"
+version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7"
+checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e"
[[package]]
name = "futures-executor"
-version = "0.3.33"
+version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6754879cc9f2c66f88c6e5c35344bb0bdb0708b0352b1201815667c7eabc7458"
+checksum = "031b47cf1a3c6cc8bc2fc76cd437f521619387907d469316e7c0bc278f1f5432"
dependencies = [
"futures-core",
"futures-task",
@@ -2391,9 +2424,9 @@ dependencies = [
[[package]]
name = "futures-io"
-version = "0.3.33"
+version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a"
+checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed"
[[package]]
name = "futures-lite"
@@ -2410,26 +2443,26 @@ dependencies = [
[[package]]
name = "futures-macro"
-version = "0.3.33"
+version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2d6d3cde68c518367be28956066ddfef33813991b77a55005a69dae04bf3b10b"
+checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.119",
+ "syn 3.0.5",
]
[[package]]
name = "futures-sink"
-version = "0.3.33"
+version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e34418ac499d6305c2fb5ad0ed2f6ac998c5f8ca209b4510f7f94242c647e307"
+checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d"
[[package]]
name = "futures-task"
-version = "0.3.33"
+version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109"
+checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd"
[[package]]
name = "futures-timer"
@@ -2439,9 +2472,9 @@ checksum = "af43fadb8a98512d547e37b4e92e0ced13e205c061b87b4623eff01d918d6968"
[[package]]
name = "futures-util"
-version = "0.3.33"
+version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa"
+checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc"
dependencies = [
"futures-channel",
"futures-core",
@@ -2510,6 +2543,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2eecf2d5dc9b66b732b97707a0210906b1d30523eb773193ab777c0c84b3e8d5"
dependencies = [
"polyval",
+ "zeroize",
]
[[package]]
@@ -2538,9 +2572,9 @@ dependencies = [
[[package]]
name = "h2"
-version = "0.4.16"
+version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9f37a958b41b3b19ee2707c06439c0e9e547e847223eb791ecb0cb821c65e27"
+checksum = "ef8e5e5a340588f4452631496976cf8636d4a7ecf600239fdc27615d2530bc16"
dependencies = [
"atomic-waker",
"bytes",
@@ -2548,7 +2582,7 @@ dependencies = [
"futures-core",
"futures-sink",
"http 1.5.0",
- "indexmap 2.14.0",
+ "indexmap 2.14.2",
"slab",
"tokio",
"tokio-util",
@@ -2582,8 +2616,6 @@ version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
dependencies = [
- "allocator-api2",
- "equivalent",
"foldhash 0.2.0",
]
@@ -2621,9 +2653,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hermit-abi"
-version = "0.5.2"
+version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
+checksum = "e17592d60ebacc7d5e169f4663c5f84f9161cc90328abcfe8456f41e4dfcb284"
[[package]]
name = "hex"
@@ -2633,20 +2665,11 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "hkdf"
-version = "0.12.4"
+version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7"
+checksum = "4aaa26c720c68b866f2c96ef5c1264b3e6f473fe5d4ce61cd44bbe913e553018"
dependencies = [
- "hmac 0.12.1",
-]
-
-[[package]]
-name = "hmac"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
-dependencies = [
- "digest 0.10.7",
+ "hmac",
]
[[package]]
@@ -2711,9 +2734,9 @@ dependencies = [
[[package]]
name = "http-body-util"
-version = "0.1.4"
+version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e9f41fd6a08e4d4ec69df65976da761afd5ad5e58a9d4acb46bd1c953a9e3ff2"
+checksum = "23169fe34a5fbcdd3f3862e78fb9b6fccd5f02a6dc6f732547005d45631ce71c"
dependencies = [
"bytes",
"futures-core",
@@ -2742,18 +2765,18 @@ checksum = "15cdd26707701c53297e2fa6afb323d55fbc1d0810c3aec078ae3ef0424c3c15"
[[package]]
name = "hybrid-array"
-version = "0.4.14"
+version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "707114b52a152fa7bdb290cd7cd5912d9467273b6d74e21b8d81aca1f8533f6b"
+checksum = "27f864f10dfb56725ce5ce5472bc52252c8f93a4ab86327122cebf62c5f59a17"
dependencies = [
"typenum",
]
[[package]]
name = "hyper"
-version = "1.11.0"
+version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d22053281f852e11534f5198498373cbb59295120a20771d90f7ed1897490a72"
+checksum = "27b501faa50e7a26c3d3560ca625132f4078a17771f4810baf70475ae48cbe43"
dependencies = [
"atomic-waker",
"bytes",
@@ -2820,7 +2843,7 @@ version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0"
dependencies = [
- "base64",
+ "base64 0.22.1",
"bytes",
"futures-channel",
"futures-util",
@@ -2878,9 +2901,9 @@ dependencies = [
[[package]]
name = "icu_collections"
-version = "2.2.0"
+version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c"
+checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513"
dependencies = [
"displaydoc",
"potential_utf",
@@ -2892,22 +2915,43 @@ dependencies = [
[[package]]
name = "icu_locale_core"
-version = "2.2.0"
+version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29"
+checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb"
dependencies = [
"displaydoc",
"litemap",
+ "serde",
"tinystr",
"writeable",
"zerovec",
]
[[package]]
-name = "icu_normalizer"
-version = "2.2.0"
+name = "icu_locale_fallback"
+version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4"
+checksum = "251af8e57c9400e3eb58242fe5b8b1152b2a64fdf4cf632f923c38ccee6f2fa9"
+dependencies = [
+ "icu_locale_core",
+ "icu_locale_fallback_data",
+ "icu_provider",
+ "potential_utf",
+ "tinystr",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locale_fallback_data"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "decf2a22ec8fa68f1a0c1129a3f8583f8f8bc24e8b9ccbe98ead99f62a4dc3a8"
+
+[[package]]
+name = "icu_normalizer"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f"
dependencies = [
"icu_collections",
"icu_normalizer_data",
@@ -2919,16 +2963,17 @@ dependencies = [
[[package]]
name = "icu_normalizer_data"
-version = "2.2.0"
+version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38"
+checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0"
[[package]]
name = "icu_properties"
-version = "2.2.0"
+version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de"
+checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148"
dependencies = [
+ "displaydoc",
"icu_collections",
"icu_locale_core",
"icu_properties_data",
@@ -2939,18 +2984,20 @@ dependencies = [
[[package]]
name = "icu_properties_data"
-version = "2.2.0"
+version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14"
+checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa"
[[package]]
name = "icu_provider"
-version = "2.2.0"
+version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421"
+checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73"
dependencies = [
"displaydoc",
"icu_locale_core",
+ "serde",
+ "stable_deref_trait",
"writeable",
"yoke",
"zerofrom",
@@ -2958,6 +3005,28 @@ dependencies = [
"zerovec",
]
+[[package]]
+name = "icu_segmenter"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82d07aafccd67af15d02512a6adf5896fbc5ed00f2e99b471d2efa14016db3db"
+dependencies = [
+ "icu_collections",
+ "icu_locale_fallback",
+ "icu_provider",
+ "icu_segmenter_data",
+ "potential_utf",
+ "smallvec",
+ "utf8_iter",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_segmenter_data"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae293c039020f9ec10710af98d29ce6aa2051486638b49c9a6409f3b4a9e98ad"
+
[[package]]
name = "ident_case"
version = "1.0.1"
@@ -3004,9 +3073,9 @@ dependencies = [
[[package]]
name = "indexmap"
-version = "2.14.0"
+version = "2.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
+checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855"
dependencies = [
"equivalent",
"hashbrown 0.17.1",
@@ -3033,41 +3102,32 @@ version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb"
-[[package]]
-name = "inout"
-version = "0.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
-dependencies = [
- "block-padding",
- "generic-array",
-]
-
[[package]]
name = "inout"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7"
dependencies = [
+ "block-padding",
"hybrid-array",
]
[[package]]
name = "io-uring"
-version = "0.7.13"
+version = "0.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9080b15e63775b9a2ac7dca720f7050a8b955e092ea0f6020a4a80f69998cdc0"
+checksum = "ed3bd0ecfbb87805f538bb7b32e5239ca0763890c623e349860ecba69469f2bb"
dependencies = [
- "bitflags",
+ "bitflags 2.13.2",
"cfg-if",
"libc",
]
[[package]]
name = "ipnet"
-version = "2.12.0"
+version = "2.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2"
+checksum = "791930b43c0d5973160d90a8f3894509f2b273430f5c5c73b668636d0287c5c0"
[[package]]
name = "is_ci"
@@ -3105,6 +3165,59 @@ version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
+[[package]]
+name = "jiff"
+version = "0.2.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "668b7183bd07af9a4885f5c35b0cc5c83c4607a913c16b7e17291832910d2dcc"
+dependencies = [
+ "defmt",
+ "jiff-core",
+ "jiff-static",
+ "jiff-tzdb-platform",
+ "log",
+ "portable-atomic",
+ "portable-atomic-util",
+ "serde_core",
+ "windows-link 0.2.1",
+]
+
+[[package]]
+name = "jiff-core"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7feca88439efe53da3754500c1851dedf3cb36c524dd5cf8225cc0794de95d09"
+dependencies = [
+ "defmt",
+]
+
+[[package]]
+name = "jiff-static"
+version = "0.2.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a69dcb3a21cfb32ce1cd056169337ca284af0766dd766e7878819b251a49204"
+dependencies = [
+ "jiff-core",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "jiff-tzdb"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "142bd39932ad231f10513df9ab62661fead8719872150b7ad02a2df79f4e141e"
+
+[[package]]
+name = "jiff-tzdb-platform"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8"
+dependencies = [
+ "jiff-tzdb",
+]
+
[[package]]
name = "jni"
version = "0.22.4"
@@ -3117,7 +3230,7 @@ dependencies = [
"jni-sys",
"log",
"simd_cesu8",
- "thiserror 2.0.19",
+ "thiserror 2.0.20",
"walkdir",
"windows-link 0.2.1",
]
@@ -3166,9 +3279,9 @@ dependencies = [
[[package]]
name = "js-sys"
-version = "0.3.103"
+version = "0.3.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102"
+checksum = "ce57d20d1ea864ce2ac172ab472d409214f4fd359f0b2a2775abdf522e2af99e"
dependencies = [
"cfg-if",
"futures-util",
@@ -3188,9 +3301,9 @@ dependencies = [
[[package]]
name = "json_to_table"
-version = "0.13.0"
+version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f91246cf42b255a705436e360ca5894e376a50fdf696e4e60cb1bb6dd648e21"
+checksum = "0ec64c9908ffa97b6ef6044d96c9f56de1c81643d3b9fbc2823e8847ab11b467"
dependencies = [
"serde_json",
"tabled",
@@ -3198,9 +3311,9 @@ dependencies = [
[[package]]
name = "keyring"
-version = "4.1.6"
+version = "4.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72585bb6cc9bc370d1d545b7e23fcce71dfd4461c5e15275e3cf51bdfd9a980a"
+checksum = "2270074a3d26bcac93c1dc5d2845eb4c089e8d761ccf6e0ea266a16004640627"
dependencies = [
"apple-native-keyring-store",
"keyring-core",
@@ -3231,9 +3344,9 @@ checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
[[package]]
name = "libredox"
-version = "0.1.18"
+version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652"
+checksum = "8d8f1ea3f21fd3405dcaf6c9b5c1630af9afc422d9073ea39c5f6d6c772e08ed"
dependencies = [
"libc",
]
@@ -3252,9 +3365,9 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
[[package]]
name = "litemap"
-version = "0.8.2"
+version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0"
+checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae"
[[package]]
name = "lock_api"
@@ -3267,15 +3380,15 @@ dependencies = [
[[package]]
name = "log"
-version = "0.4.33"
+version = "0.4.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
+checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6"
[[package]]
name = "lru"
-version = "0.18.2"
+version = "0.18.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5d2f2f9b4ba7e6b24d95e7e899329d35be83bcded72c8540cdd5368932d1d90a"
+checksum = "ff9840bcc50b71349309900da0ce7279aa336ae71d73250b07998932c7d97c25"
dependencies = [
"hashbrown 0.17.1",
]
@@ -3339,15 +3452,6 @@ dependencies = [
"digest 0.11.3",
]
-[[package]]
-name = "mea"
-version = "0.6.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31fc7d159de0085ab6dd7ff145a9819442cfd3d098f783263120503c3f3e58b0"
-dependencies = [
- "slab",
-]
-
[[package]]
name = "memchr"
version = "2.8.3"
@@ -3410,6 +3514,15 @@ name = "miniz_oxide"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
+dependencies = [
+ "adler2",
+]
+
+[[package]]
+name = "miniz_oxide"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b63fbc4a50860e98e7b2aa7804ded1db5cbc3aff9193adaff57a6931bf7c4b4c"
dependencies = [
"adler2",
"simd-adler32",
@@ -3417,9 +3530,9 @@ dependencies = [
[[package]]
name = "mio"
-version = "1.2.2"
+version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427"
+checksum = "4b18443e9c262bfe8fa82f51666e2642c53393f7e5c27b3e1aeab922cff5b9d8"
dependencies = [
"libc",
"wasi",
@@ -3442,6 +3555,33 @@ version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084"
+[[package]]
+name = "multiversion"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4ca4bea16ffc3f443cf7d866912118196bfef4c6a1556ca00f9f9b00bb43f7c"
+dependencies = [
+ "multiversion-macros",
+]
+
+[[package]]
+name = "multiversion-macros"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d416831a7317ef4b08bee00b69cbbb9c8763da7959a7026244d6266869f9c83"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "rustversion",
+ "syn 3.0.5",
+]
+
+[[package]]
+name = "multiversion_no_op"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "743fb55ba31b18fb1ecef6bdc9aa2743314978ac084044301a7eee33fb99a20d"
+
[[package]]
name = "munge"
version = "0.4.7"
@@ -3468,7 +3608,7 @@ version = "0.31.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d"
dependencies = [
- "bitflags",
+ "bitflags 2.13.2",
"cfg-if",
"cfg_aliases",
"libc",
@@ -3505,7 +3645,7 @@ version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
dependencies = [
- "windows-sys 0.59.0",
+ "windows-sys 0.61.2",
]
[[package]]
@@ -3549,9 +3689,9 @@ checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441"
[[package]]
name = "num-integer"
-version = "0.1.46"
+version = "0.1.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
+checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b"
dependencies = [
"num-traits",
]
@@ -3602,7 +3742,7 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
dependencies = [
- "bitflags",
+ "bitflags 2.13.2",
]
[[package]]
@@ -3632,7 +3772,7 @@ checksum = "d354792e39fa5f0009e47623cf8b15b099bf9a652fa55c6f817fe28ac84fea50"
dependencies = [
"async-trait",
"aws-lc-rs",
- "base64",
+ "base64 0.22.1",
"bytes",
"chrono",
"crc-fast",
@@ -3656,7 +3796,7 @@ dependencies = [
"serde",
"serde_json",
"serde_urlencoded",
- "thiserror 2.0.19",
+ "thiserror 2.0.20",
"tokio",
"tracing",
"url",
@@ -3751,9 +3891,9 @@ checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e"
[[package]]
name = "owo-colors"
-version = "4.3.0"
+version = "4.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d"
+checksum = "13c45bb4a6ae1280ec0803b1ef9d3455eb50f01efbbe1447ab020f1d54fba9d8"
[[package]]
name = "papergrid"
@@ -3797,9 +3937,9 @@ dependencies = [
[[package]]
name = "parse-display"
-version = "0.9.1"
+version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "914a1c2265c98e2446911282c6ac86d8524f495792c38c5bd884f80499c7538a"
+checksum = "e78deb158fb1d73b29efb4b7e9b9860b78059c670de06bd28df8d0b458ded0eb"
dependencies = [
"parse-display-derive",
"regex",
@@ -3808,9 +3948,9 @@ dependencies = [
[[package]]
name = "parse-display-derive"
-version = "0.9.1"
+version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2ae7800a4c974efd12df917266338e79a7a74415173caf7e70aa0a0707345281"
+checksum = "8e95a50d1084dab562913062c4c34bb204b68fc6ec38a1395909ff5aaaf4f10a"
dependencies = [
"proc-macro2",
"quote",
@@ -3857,11 +3997,11 @@ dependencies = [
[[package]]
name = "pem"
-version = "3.0.6"
+version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be"
+checksum = "d354a98a3d1251555de99e8fdd8afda05573c31b82f59063a7b0a29b5527f120"
dependencies = [
- "base64",
+ "base64 0.23.1",
"serde_core",
]
@@ -3873,9 +4013,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
name = "pest"
-version = "2.8.8"
+version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7df728be843c7070fab6ab7c328c4e9e9d78e23bf749c0669c86ee7ebfa050a2"
+checksum = "6d45aeb61b4bf818e12d4205f2466f8c4748f85f4fce0146d1c03d69d753f0ad"
dependencies = [
"memchr",
"ucd-trie",
@@ -3883,9 +4023,9 @@ dependencies = [
[[package]]
name = "pest_derive"
-version = "2.8.8"
+version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e2dd6fc3b26b3462ee188aac870f5a41d398f1cd5e2408d16531bd71c9591fd"
+checksum = "89cc5a242e25ed4e7704d0be240f2cfbe20a8c27e7e252d94835be93d92dc39f"
dependencies = [
"pest",
"pest_generator",
@@ -3893,9 +4033,9 @@ dependencies = [
[[package]]
name = "pest_generator"
-version = "2.8.8"
+version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a7a9205cfb6f596a9e8b689c0a15f9ceb7a1aafae7aaf788150ac65b29975b6"
+checksum = "7abf21475cc3820fe4b2ca2dc2142902f67a02189f3b5b3a229f4febc01a43e5"
dependencies = [
"pest",
"pest_meta",
@@ -3906,9 +4046,9 @@ dependencies = [
[[package]]
name = "pest_meta"
-version = "2.8.8"
+version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85abd351c0de1e8384fc791a0737111a350394937e92b956b743dac12429f57c"
+checksum = "adba4db388f687393c18c51348d44a41d870ca9df71a2c98172ea3035dc6936e"
dependencies = [
"pest",
]
@@ -3921,7 +4061,7 @@ checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455"
dependencies = [
"fixedbitset",
"hashbrown 0.15.5",
- "indexmap 2.14.0",
+ "indexmap 2.14.2",
]
[[package]]
@@ -3969,9 +4109,9 @@ dependencies = [
[[package]]
name = "pkg-config"
-version = "0.3.33"
+version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
+checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548"
[[package]]
name = "polling"
@@ -3994,22 +4134,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0fa31d631f2b2cb2a544d0aa321ce847a94764d701ca2becc411138b93d49cd"
dependencies = [
"cpubits",
- "cpufeatures 0.3.0",
+ "cpufeatures 0.3.1",
"universal-hash",
+ "zeroize",
]
[[package]]
name = "portable-atomic"
-version = "1.14.0"
+version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d20d5497ef88037a52ff98267d066e7f11fcc5e99bbfbd58a42336193aacec3"
+checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85"
+
+[[package]]
+name = "portable-atomic-util"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10ab3eb7f3becc3a1cbc4f2c6f20267996cfc1a6467a873763411b136a122715"
+dependencies = [
+ "portable-atomic",
+]
[[package]]
name = "potential_utf"
-version = "0.1.5"
+version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564"
+checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661"
dependencies = [
+ "serde_core",
+ "writeable",
"zerovec",
]
@@ -4074,29 +4226,7 @@ version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f"
dependencies = [
- "toml_edit 0.25.13+spec-1.1.0",
-]
-
-[[package]]
-name = "proc-macro-error-attr2"
-version = "2.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
-dependencies = [
- "proc-macro2",
- "quote",
-]
-
-[[package]]
-name = "proc-macro-error2"
-version = "2.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
-dependencies = [
- "proc-macro-error-attr2",
- "proc-macro2",
- "quote",
- "syn 2.0.119",
+ "toml_edit 0.25.15+spec-1.1.0",
]
[[package]]
@@ -4127,7 +4257,7 @@ version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc5b72d8145275d844d4b5f6d4e1eef00c8cd889edb6035c21675d1bb1f45c9f"
dependencies = [
- "bitflags",
+ "bitflags 2.13.2",
"hex",
"procfs-core",
"rustix 0.38.44",
@@ -4139,7 +4269,7 @@ version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "239df02d8349b06fc07398a3a1697b06418223b1c7725085e801e7c0fc6a12ec"
dependencies = [
- "bitflags",
+ "bitflags 2.13.2",
"hex",
]
@@ -4157,7 +4287,7 @@ dependencies = [
"parking_lot",
"procfs",
"protobuf",
- "thiserror 2.0.19",
+ "thiserror 2.0.20",
]
[[package]]
@@ -4168,7 +4298,7 @@ checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744"
dependencies = [
"bit-set",
"bit-vec 0.8.0",
- "bitflags",
+ "bitflags 2.13.2",
"num-traits",
"rand 0.9.5",
"rand_chacha",
@@ -4252,22 +4382,22 @@ dependencies = [
[[package]]
name = "ptr_meta"
-version = "0.3.1"
+version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b9a0cf95a1196af61d4f1cbdab967179516d9a4a4312af1f31948f8f6224a79"
+checksum = "743da816b98c921cdbe8628ef7381b76f25ecf4da599fc80aca90eae7ef70cc0"
dependencies = [
"ptr_meta_derive",
]
[[package]]
name = "ptr_meta_derive"
-version = "0.3.1"
+version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7347867d0a7e1208d93b46767be83e2b8f978c3dad35f775ac8d8847551d6fe1"
+checksum = "1c8d9ca532f185d5d4db7a7c9d51420b452168ea1c2b913953281bd6fe1fcbd0"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.119",
+ "syn 3.0.5",
]
[[package]]
@@ -4300,7 +4430,7 @@ dependencies = [
"rustc-hash",
"rustls",
"socket2",
- "thiserror 2.0.19",
+ "thiserror 2.0.20",
"tokio",
"tracing",
"web-time",
@@ -4308,9 +4438,9 @@ dependencies = [
[[package]]
name = "quinn-proto"
-version = "0.11.16"
+version = "0.11.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2f4bfc015262b9df63c8845072ce59068853ff5872180c2ce2f13038b970e560"
+checksum = "04759210543be93709136e28212294a659ef5001836ff4eab4d663e4529bba83"
dependencies = [
"aws-lc-rs",
"bytes",
@@ -4323,7 +4453,7 @@ dependencies = [
"rustls",
"rustls-pki-types",
"slab",
- "thiserror 2.0.19",
+ "thiserror 2.0.20",
"tinyvec",
"tracing",
"web-time",
@@ -4340,7 +4470,7 @@ dependencies = [
"once_cell",
"socket2",
"tracing",
- "windows-sys 0.59.0",
+ "windows-sys 0.61.2",
]
[[package]]
@@ -4366,9 +4496,9 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rancor"
-version = "0.1.2"
+version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "daff8b7b3ccf5f7ba270b3e7a0a4d4c701c5797e38dec27c7e2c3dbb830fed1c"
+checksum = "9b534442d0fcdb55d66f373d9cac6d33b6293a2335bc2136dbd06ce0e87d2572"
dependencies = [
"ptr_meta",
]
@@ -4448,9 +4578,9 @@ dependencies = [
[[package]]
name = "rcgen"
-version = "0.14.8"
+version = "0.14.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "57f6d249aad744e274e682777a50283a225a32705394ee6d5fcc01efa25e4055"
+checksum = "8774e05a7d0de114588e6a28fe7e71694b82614ed569d86d8b389dfbc98b8ad8"
dependencies = [
"aws-lc-rs",
"pem",
@@ -4466,7 +4596,7 @@ version = "0.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
dependencies = [
- "bitflags",
+ "bitflags 2.13.2",
]
[[package]]
@@ -4477,27 +4607,27 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
dependencies = [
"getrandom 0.2.17",
"libredox",
- "thiserror 2.0.19",
+ "thiserror 2.0.20",
]
[[package]]
name = "ref-cast"
-version = "1.0.26"
+version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "216e8f773d7923bcba9ceb86a86c93cabb3903a11872fc3f138c49630e50b96d"
+checksum = "7e440fb4e4b4147295338efb76001ab9e4efc0e5839df2c47fc5ac2381d365c3"
dependencies = [
"ref-cast-impl",
]
[[package]]
name = "ref-cast-impl"
-version = "1.0.26"
+version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2c9283685feec7d69af75fb0e858d5e7378f33fe4fc699383b2916ab9273e03c"
+checksum = "92ecd8964f8453721699a1ed72037b0db49ce2f5a5138486ee89bed6f67cdf3a"
dependencies = [
"proc-macro2",
"quote",
- "syn 3.0.3",
+ "syn 3.0.5",
]
[[package]]
@@ -4514,9 +4644,9 @@ dependencies = [
[[package]]
name = "regex-automata"
-version = "0.4.16"
+version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad"
+checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2"
dependencies = [
"aho-corasick",
"memchr",
@@ -4552,11 +4682,11 @@ dependencies = [
[[package]]
name = "reqwest"
-version = "0.13.4"
+version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3"
+checksum = "16a1cfa75cc186dd73d5818e510e042e40927bccc9c236b061cea97e1eb08029"
dependencies = [
- "base64",
+ "base64 0.23.1",
"bytes",
"futures-core",
"futures-util",
@@ -4607,14 +4737,14 @@ dependencies = [
[[package]]
name = "rkyv"
-version = "0.8.17"
+version = "0.8.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "815cc8a37159a463064825246cadb07961e25cd9885908606f6d08a98d8f8874"
+checksum = "d9776093b7ca170454ab1406954f7b7d97a57c51dc6c0642957fb2ef25c2d399"
dependencies = [
"bytecheck",
"bytes",
"hashbrown 0.17.1",
- "indexmap 2.14.0",
+ "indexmap 2.14.2",
"munge",
"ptr_meta",
"rancor",
@@ -4626,13 +4756,13 @@ dependencies = [
[[package]]
name = "rkyv_derive"
-version = "0.8.17"
+version = "0.8.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0ed1a78a1b19d184b0daa629dd9a024573173ec7d485b287cb369fb3607cc1c"
+checksum = "1c25ef604ac7dd839d44d64648952ea23c97866f124ff671b0ed2cf3ad9bb06e"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.119",
+ "syn 3.0.5",
]
[[package]]
@@ -4641,7 +4771,7 @@ version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81116b9531d61eabc41aeb228e4b6b2435bcca3233b98cf3b3077d4e6e9debb3"
dependencies = [
- "bitflags",
+ "bitflags 2.13.2",
"once_cell",
"serde",
"serde_derive",
@@ -4662,9 +4792,9 @@ dependencies = [
[[package]]
name = "rstest"
-version = "0.26.1"
+version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f5a3193c063baaa2a95a33f03035c8a72b83d97a54916055ba22d35ed3839d49"
+checksum = "948203e6d13b83e51a90d7cf236dd58a0065f23f4b902f00f306e7eb2bd09b9c"
dependencies = [
"futures-timer",
"futures-util",
@@ -4673,9 +4803,9 @@ dependencies = [
[[package]]
name = "rstest_macros"
-version = "0.26.1"
+version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c845311f0ff7951c5506121a9ad75aec44d083c31583b2ea5a30bcb0b0abba0"
+checksum = "a8d92eaf7b6e51e12471d71ddc72608e7151573de44099aacfbb1128177c0da4"
dependencies = [
"cfg-if",
"glob",
@@ -4691,12 +4821,12 @@ dependencies = [
[[package]]
name = "rtoolbox"
-version = "0.0.5"
+version = "0.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50a0e551c1e27e1731aba276dbeaeac73f53c7cd34d1bda485d02bd1e0f36844"
+checksum = "9a1efe12a1469752d0e6ff5ebec0b6ef4924cc5c4c71046b0ec730040535819d"
dependencies = [
"libc",
- "windows-sys 0.59.0",
+ "windows-sys 0.61.2",
]
[[package]]
@@ -4711,9 +4841,9 @@ dependencies = [
[[package]]
name = "rust_decimal"
-version = "1.42.1"
+version = "1.43.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "be2a24f50780bc85f09cc6ac299bdf1424302742d77221106859c9d8b102126a"
+checksum = "7653272e75dcac41dc199fbea6f5797633994fafd339943c06c9af16bf29cd3a"
dependencies = [
"arrayvec",
"num-traits",
@@ -4755,7 +4885,7 @@ version = "0.38.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
dependencies = [
- "bitflags",
+ "bitflags 2.13.2",
"errno",
"libc",
"linux-raw-sys 0.4.15",
@@ -4768,11 +4898,11 @@ version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
dependencies = [
- "bitflags",
+ "bitflags 2.13.2",
"errno",
"libc",
"linux-raw-sys 0.12.1",
- "windows-sys 0.59.0",
+ "windows-sys 0.61.2",
]
[[package]]
@@ -4831,7 +4961,7 @@ dependencies = [
"security-framework",
"security-framework-sys",
"webpki-root-certs",
- "windows-sys 0.59.0",
+ "windows-sys 0.61.2",
]
[[package]]
@@ -4899,11 +5029,11 @@ dependencies = [
"serde",
"serde_json",
"strum",
- "thiserror 2.0.19",
+ "thiserror 2.0.20",
"time",
"tokio-util",
"utoipa",
- "zstd",
+ "zstd 0.14.0",
]
[[package]]
@@ -4949,16 +5079,16 @@ dependencies = [
"serde",
"serde_json",
"serial_test",
- "sha2 0.11.0",
+ "sha2",
"strum",
"tabled",
"tempfile",
"terminal_size",
- "thiserror 2.0.19",
+ "thiserror 2.0.20",
"tikv-jemallocator",
"tokio",
"tokio-stream",
- "toml 1.1.4+spec-1.1.0",
+ "toml 1.1.6+spec-1.1.0",
"tracing",
"tracing-subscriber",
"uuid",
@@ -4985,7 +5115,7 @@ dependencies = [
"serde",
"serde_json",
"strum",
- "thiserror 2.0.19",
+ "thiserror 2.0.20",
"time",
"utoipa",
]
@@ -5007,7 +5137,7 @@ dependencies = [
"eyre",
"futures",
"http 1.5.0",
- "indexmap 2.14.0",
+ "indexmap 2.14.2",
"itertools 0.15.0",
"parking_lot",
"prometheus",
@@ -5025,13 +5155,13 @@ dependencies = [
"serde_json",
"slatedb",
"strum",
- "thiserror 2.0.19",
+ "thiserror 2.0.20",
"tikv-jemallocator",
"time",
"tokio",
"tokio-util",
"tower",
- "tower-http 0.7.0",
+ "tower-http 0.7.1",
"tracing",
"tracing-subscriber",
"utoipa",
@@ -5080,7 +5210,7 @@ dependencies = [
"serde_json",
"serde_urlencoded",
"test-context",
- "thiserror 2.0.19",
+ "thiserror 2.0.20",
"time",
"tokio",
"tokio-muxt",
@@ -5106,7 +5236,7 @@ dependencies = [
"s2-common",
"secrecy",
"serde",
- "thiserror 2.0.19",
+ "thiserror 2.0.20",
]
[[package]]
@@ -5116,7 +5246,7 @@ dependencies = [
"reqwest",
"s2-sdk",
"testcontainers",
- "thiserror 2.0.19",
+ "thiserror 2.0.20",
"tokio",
]
@@ -5172,7 +5302,7 @@ dependencies = [
"proc-macro2",
"quote",
"serde_derive_internals",
- "syn 3.0.3",
+ "syn 3.0.5",
]
[[package]]
@@ -5192,20 +5322,20 @@ dependencies = [
[[package]]
name = "secret-service"
-version = "5.1.0"
+version = "5.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a62d7f86047af0077255a29494136b9aaaf697c76ff70b8e49cded4e2623c14"
+checksum = "5107b24b91445dd2aa449a258a1807b63240942157292354dc5bfdbeb8bc6db8"
dependencies = [
- "aes 0.8.4",
+ "aes",
"cbc",
"futures-util",
- "generic-array",
- "getrandom 0.2.17",
+ "getrandom 0.4.3",
"hkdf",
+ "hybrid-array",
"num",
"once_cell",
"serde",
- "sha2 0.10.9",
+ "sha2",
"zbus",
]
@@ -5215,7 +5345,7 @@ version = "3.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d"
dependencies = [
- "bitflags",
+ "bitflags 2.13.2",
"core-foundation",
"core-foundation-sys",
"libc",
@@ -5288,7 +5418,7 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
dependencies = [
"proc-macro2",
"quote",
- "syn 3.0.3",
+ "syn 3.0.5",
]
[[package]]
@@ -5299,7 +5429,7 @@ checksum = "f852137cce035d6a4df67ccce505ff6b3e9fd3a10e3e52b24dc71e650bb1a9bd"
dependencies = [
"proc-macro2",
"quote",
- "syn 3.0.3",
+ "syn 3.0.5",
]
[[package]]
@@ -5308,7 +5438,7 @@ version = "1.0.151"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
dependencies = [
- "indexmap 2.14.0",
+ "indexmap 2.14.2",
"itoa",
"memchr",
"serde",
@@ -5335,7 +5465,7 @@ checksum = "8d3b1629de253c70a0508c3899572da79ca359fdab27c7920ff00406df418906"
dependencies = [
"proc-macro2",
"quote",
- "syn 3.0.3",
+ "syn 3.0.5",
]
[[package]]
@@ -5370,16 +5500,17 @@ dependencies = [
[[package]]
name = "serde_with"
-version = "3.21.0"
+version = "3.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c"
+checksum = "935177bb8c0cd8ca1a4e6d1a2ac8988bea69cab4f9d3a31311e012ad27868ea4"
dependencies = [
- "base64",
+ "base64 0.23.1",
"bs58",
"chrono",
"hex",
"indexmap 1.9.3",
- "indexmap 2.14.0",
+ "indexmap 2.14.2",
+ "jiff",
"schemars 0.9.0",
"schemars 1.2.2",
"serde_core",
@@ -5390,14 +5521,14 @@ dependencies = [
[[package]]
name = "serde_with_macros"
-version = "3.21.0"
+version = "3.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660"
+checksum = "1d607aa01a3cb0ad757d6fd216136910db3c97b102fe686585689615a02dbcdc"
dependencies = [
- "darling 0.23.0",
+ "darling 0.24.1",
"proc-macro2",
"quote",
- "syn 2.0.119",
+ "syn 3.0.5",
]
[[package]]
@@ -5406,7 +5537,7 @@ version = "0.9.34+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
dependencies = [
- "indexmap 2.14.0",
+ "indexmap 2.14.2",
"itoa",
"ryu",
"serde",
@@ -5435,7 +5566,7 @@ checksum = "a22144e767da4ddd8416dbf383700542ffd8a5dc493dfecedfe1fe3ad03c98ae"
dependencies = [
"proc-macro2",
"quote",
- "syn 3.0.3",
+ "syn 3.0.5",
]
[[package]]
@@ -5449,17 +5580,6 @@ dependencies = [
"digest 0.10.7",
]
-[[package]]
-name = "sha2"
-version = "0.10.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
-dependencies = [
- "cfg-if",
- "cpufeatures 0.2.17",
- "digest 0.10.7",
-]
-
[[package]]
name = "sha2"
version = "0.11.0"
@@ -5467,7 +5587,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4"
dependencies = [
"cfg-if",
- "cpufeatures 0.3.0",
+ "cpufeatures 0.3.1",
"digest 0.11.3",
]
@@ -5532,15 +5652,15 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
[[package]]
name = "slatedb"
-version = "0.15.0"
+version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "35ca56b01922b15aa69fe3abb62cadc985d86032c9647e4606e211c4da751a76"
+checksum = "fb40332f41e231926df3a0ef742c05316b43368ba4b520433d2a1eba1ab00f0f"
dependencies = [
"async-channel",
"async-trait",
"atomic",
"backon",
- "bitflags",
+ "bitflags 2.13.2",
"bytes",
"chrono",
"crc32fast",
@@ -5574,14 +5694,14 @@ dependencies = [
"url",
"uuid",
"walkdir",
- "zstd",
+ "zstd 0.13.3",
]
[[package]]
name = "slatedb-common"
-version = "0.15.0"
+version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0aa8de522ff46a0f9b5a66f45e650d125421d4d91990933591092f9d010c40d1"
+checksum = "6b77f238f348e95e1653a5235f880ac703011b82582bd556f2aba405abf8180e"
dependencies = [
"chrono",
"log",
@@ -5595,9 +5715,9 @@ dependencies = [
[[package]]
name = "slatedb-txn-obj"
-version = "0.15.0"
+version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d85fd9c0c86dd4954524fa8238d0548bd80f4a9a66983cbde3728402d339993e"
+checksum = "1ad63b6c250219e26d71f497820f970274c301f896366daa56915f2a05df0e0a"
dependencies = [
"async-trait",
"bytes",
@@ -5618,9 +5738,9 @@ checksum = "88414a5ca1f85d82cc34471e975f0f74f6aa54c40f062efa42c0080e7f763f81"
[[package]]
name = "smallvec"
-version = "1.15.2"
+version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
+checksum = "b9be42f50aa861c555654aa3a37f52f4b1074bacf4e48fe0ef7fa584e80f1f0f"
[[package]]
name = "socket2"
@@ -5746,9 +5866,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "3.0.3"
+version = "3.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3"
+checksum = "12df2e0110f65b775f769bb17ef989067a1d931b2eb822bd4346631eeada89f9"
dependencies = [
"proc-macro2",
"quote",
@@ -5791,9 +5911,9 @@ dependencies = [
[[package]]
name = "tabled"
-version = "0.21.0"
+version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5dc662e6da844ad6e428ad16b57967c9d33c82e16bb1c258326c0c078605dff"
+checksum = "2d2596a104db1900b943f97d793a6c99addca918c4525c999f751a0f17d472eb"
dependencies = [
"papergrid",
"tabled_derive",
@@ -5802,12 +5922,11 @@ dependencies = [
[[package]]
name = "tabled_derive"
-version = "0.11.0"
+version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ea5d1b13ca6cff1f9231ffd62f15eefd72543dab5e468735f1a456728a02846"
+checksum = "7dca1937322a1e892b1a65f6a6736183bb0a29d3b4234d1d53bc436dff9beb76"
dependencies = [
"heck 0.5.0",
- "proc-macro-error2",
"proc-macro2",
"quote",
"syn 2.0.119",
@@ -5823,7 +5942,7 @@ dependencies = [
"getrandom 0.3.4",
"once_cell",
"rustix 1.1.4",
- "windows-sys 0.59.0",
+ "windows-sys 0.61.2",
]
[[package]]
@@ -5833,7 +5952,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874"
dependencies = [
"rustix 1.1.4",
- "windows-sys 0.59.0",
+ "windows-sys 0.61.2",
]
[[package]]
@@ -5865,9 +5984,9 @@ dependencies = [
[[package]]
name = "testcontainers"
-version = "0.27.3"
+version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bfd5785b5483672915ed5fe3cddf9f546802779fc1eceff0a6fb7321fac81c1e"
+checksum = "6e2bbe381afaaa58ea610c5fc3ffb2184063a32b3e358a179f0b4865dd59934a"
dependencies = [
"astral-tokio-tar",
"async-trait",
@@ -5887,7 +6006,7 @@ dependencies = [
"serde",
"serde_json",
"serde_with",
- "thiserror 2.0.19",
+ "thiserror 2.0.20",
"tokio",
"tokio-stream",
"tokio-util",
@@ -5905,11 +6024,11 @@ dependencies = [
[[package]]
name = "textwrap"
-version = "0.16.2"
+version = "0.16.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057"
+checksum = "b81c0cb5fce14f53e49c1d4da0c508334ff12040221bb8ab01b2dabd91d04b6e"
dependencies = [
- "unicode-linebreak",
+ "icu_segmenter",
"unicode-width 0.2.2",
]
@@ -5924,11 +6043,11 @@ dependencies = [
[[package]]
name = "thiserror"
-version = "2.0.19"
+version = "2.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9"
+checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f"
dependencies = [
- "thiserror-impl 2.0.19",
+ "thiserror-impl 2.0.20",
]
[[package]]
@@ -5944,13 +6063,13 @@ dependencies = [
[[package]]
name = "thiserror-impl"
-version = "2.0.19"
+version = "2.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd"
+checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af"
dependencies = [
"proc-macro2",
"quote",
- "syn 3.0.3",
+ "syn 3.0.5",
]
[[package]]
@@ -5984,9 +6103,9 @@ dependencies = [
[[package]]
name = "time"
-version = "0.3.54"
+version = "0.3.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3e1d5e639ff6bab73cb6885cc7e7b1de96c3f32c68ec55f3952614bec1092244"
+checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134"
dependencies = [
"deranged",
"num-conv",
@@ -6023,19 +6142,20 @@ dependencies = [
[[package]]
name = "tinystr"
-version = "0.8.3"
+version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d"
+checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643"
dependencies = [
"displaydoc",
+ "serde_core",
"zerovec",
]
[[package]]
name = "tinyvec"
-version = "1.12.0"
+version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f"
+checksum = "4cf0ded5c4e56918d8f8a339e1bb67d038d3bc6d144ac407904015ba2e4cde9b"
dependencies = [
"tinyvec_macros",
]
@@ -6071,7 +6191,7 @@ checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e"
dependencies = [
"proc-macro2",
"quote",
- "syn 3.0.3",
+ "syn 3.0.5",
]
[[package]]
@@ -6086,9 +6206,9 @@ dependencies = [
[[package]]
name = "tokio-rustls"
-version = "0.26.4"
+version = "0.26.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61"
+checksum = "b0c85f2c3ef0b1cd58b36682f4b17aaa995f0e5db534d85692b4903abce21f67"
dependencies = [
"rustls",
"tokio",
@@ -6157,11 +6277,11 @@ dependencies = [
[[package]]
name = "toml"
-version = "1.1.4+spec-1.1.0"
+version = "1.1.6+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3aace63f4bbcdfc2c965b059de67119c89c4017a70d633be6c104910f67056f5"
+checksum = "920602543f0911ab71da12c50d59701da54c196d1a2bf5cb4b75667f137a406a"
dependencies = [
- "indexmap 2.14.0",
+ "indexmap 2.14.2",
"serde_core",
"serde_spanned 1.1.1",
"toml_datetime 1.1.1+spec-1.1.0",
@@ -6194,7 +6314,7 @@ version = "0.22.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
dependencies = [
- "indexmap 2.14.0",
+ "indexmap 2.14.2",
"serde",
"serde_spanned 0.6.9",
"toml_datetime 0.6.11",
@@ -6204,11 +6324,11 @@ dependencies = [
[[package]]
name = "toml_edit"
-version = "0.25.13+spec-1.1.0"
+version = "0.25.15+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6975367e4d2ef766d86af01ffad14b622fecc8d4357a998fbc4deb6e9bacaf9b"
+checksum = "1340ea94a5856333492c9064b02c778b191dd2c853778d9609debdcdfea3a614"
dependencies = [
- "indexmap 2.14.0",
+ "indexmap 2.14.2",
"toml_datetime 1.1.1+spec-1.1.0",
"toml_parser",
"winnow 1.0.4",
@@ -6243,7 +6363,7 @@ checksum = "ac2a5518c70fa84342385732db33fb3f44bc4cc748936eb5833d2df34d6445ef"
dependencies = [
"async-trait",
"axum",
- "base64",
+ "base64 0.22.1",
"bytes",
"h2",
"http 1.5.0",
@@ -6283,7 +6403,7 @@ checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4"
dependencies = [
"futures-core",
"futures-util",
- "indexmap 2.14.0",
+ "indexmap 2.14.2",
"pin-project-lite",
"slab",
"sync_wrapper",
@@ -6300,7 +6420,7 @@ version = "0.6.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840"
dependencies = [
- "bitflags",
+ "bitflags 2.13.2",
"bytes",
"futures-util",
"http 1.5.0",
@@ -6314,12 +6434,12 @@ dependencies = [
[[package]]
name = "tower-http"
-version = "0.7.0"
+version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b11f75e912b0c2be01b63d8cf8057b8c3f97cf34abb3d431a3a4c8675498e233"
+checksum = "08a05a66a4fdd61cbbe0a1d755ffe0ca6aba159dd4820936a0ff8a8278245b9c"
dependencies = [
"async-compression",
- "bitflags",
+ "bitflags 2.13.2",
"bytes",
"futures-core",
"http 1.5.0",
@@ -6416,12 +6536,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]]
name = "twox-hash"
-version = "2.1.3"
+version = "2.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8464ec13c3691491391d9fce00f6416c9a48e46972f72d7865688be2080192c9"
-dependencies = [
- "rand 0.10.2",
-]
+checksum = "5283634e518fe9e82c7b20520bb4bc209009fd16c82077c802f8111ecbb0117a"
[[package]]
name = "typed-path"
@@ -6490,12 +6607,6 @@ version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
-[[package]]
-name = "unicode-linebreak"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f"
-
[[package]]
name = "unicode-segmentation"
version = "1.13.3"
@@ -6548,33 +6659,6 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
-[[package]]
-name = "ureq"
-version = "3.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dea7109cdcd5864d4eeb1b58a1648dc9bf520360d7af16ec26d0a9354bafcfc0"
-dependencies = [
- "base64",
- "log",
- "percent-encoding",
- "rustls",
- "rustls-pki-types",
- "ureq-proto",
- "utf8-zero",
-]
-
-[[package]]
-name = "ureq-proto"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e994ba84b0bd1b1b0cf92878b7ef898a5c1760108fe7b6010327e274917a808c"
-dependencies = [
- "base64",
- "http 1.5.0",
- "httparse",
- "log",
-]
-
[[package]]
name = "url"
version = "2.5.8"
@@ -6594,12 +6678,6 @@ version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
-[[package]]
-name = "utf8-zero"
-version = "0.8.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8c0a043c9540bae7c578c88f91dda8bd82e59ae27c21baca69c8b191aaf5a6e"
-
[[package]]
name = "utf8_iter"
version = "1.0.4"
@@ -6617,7 +6695,7 @@ name = "utoipa"
version = "5.4.0"
source = "git+https://github.com/infiniteregrets/utoipa?rev=82bcb28a792ba9a0d29963827ec473823099fc94#82bcb28a792ba9a0d29963827ec473823099fc94"
dependencies = [
- "indexmap 2.14.0",
+ "indexmap 2.14.2",
"serde",
"serde_json",
"utoipa-gen",
@@ -6635,9 +6713,9 @@ dependencies = [
[[package]]
name = "uuid"
-version = "1.24.0"
+version = "1.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239"
+checksum = "2ef6dac1e96601b4fb3acccccff2139741fcb757cb9a36089bf5be91cfb285ce"
dependencies = [
"getrandom 0.4.3",
"js-sys",
@@ -6709,9 +6787,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen"
-version = "0.2.126"
+version = "0.2.128"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4"
+checksum = "aecb87a33d3b0c5e3b7aa46336eaf486cffafbd281b195e4c8b80d50df2351bf"
dependencies = [
"cfg-if",
"once_cell",
@@ -6722,9 +6800,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-futures"
-version = "0.4.76"
+version = "0.4.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d"
+checksum = "6ef4c5d3d2cdf5c54f4231181768f5510842e350db025faf1f7163b1030ed928"
dependencies = [
"js-sys",
"wasm-bindgen",
@@ -6732,9 +6810,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
-version = "0.2.126"
+version = "0.2.128"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1"
+checksum = "a690d511e3c1a8b3a55e33511e3c2c00c78415cd23650f32b808627f5696b9ed"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -6742,22 +6820,22 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
-version = "0.2.126"
+version = "0.2.128"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e"
+checksum = "411e4887f0071ef2d2164a9d5fdf2d20efbef78fccd3a78b0c10a1dc5295e48a"
dependencies = [
"bumpalo",
"proc-macro2",
"quote",
- "syn 2.0.119",
+ "syn 3.0.5",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
-version = "0.2.126"
+version = "0.2.128"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24"
+checksum = "81941cd78d0c92026c33e5e01312845a4cb1e9af3407f9134b100dd03144103e"
dependencies = [
"unicode-ident",
]
@@ -6777,9 +6855,9 @@ dependencies = [
[[package]]
name = "web-sys"
-version = "0.3.103"
+version = "0.3.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141"
+checksum = "9fbddc4a036f00ec4f18c83445bd3115cb306a91da554919a099d9222fe4a7f8"
dependencies = [
"js-sys",
"wasm-bindgen",
@@ -6826,7 +6904,7 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
- "windows-sys 0.59.0",
+ "windows-sys 0.61.2",
]
[[package]]
@@ -7122,9 +7200,9 @@ checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
[[package]]
name = "writeable"
-version = "0.6.3"
+version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4"
+checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc"
[[package]]
name = "x509-parser"
@@ -7140,7 +7218,7 @@ dependencies = [
"nom",
"oid-registry",
"rusticata-macros",
- "thiserror 2.0.19",
+ "thiserror 2.0.20",
"time",
]
@@ -7168,9 +7246,9 @@ checksum = "aee1b19627c7c60102ab80d3a9cbe18de90bfe03bfa6c3715447681f0e8c8af6"
[[package]]
name = "yaml-rust2"
-version = "0.11.0"
+version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "631a50d867fafb7093e709d75aaee9e0e0d5deb934021fcea25ac2fe09edc51e"
+checksum = "b36710ce3a279cfce8465dbab826f161675a262950b922cb2c3663852dfe9eb0"
dependencies = [
"arraydeque",
"encoding_rs",
@@ -7218,9 +7296,9 @@ dependencies = [
[[package]]
name = "zbus"
-version = "5.18.0"
+version = "5.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe18fb60dc696039e738717b76eaea21e7a4489bbb1885020b43c94236d7e98a"
+checksum = "5db4be7c075cb421e4b7ee645541604239bd243ba7c357511f4ff3a74b555907"
dependencies = [
"async-broadcast",
"async-executor",
@@ -7253,9 +7331,9 @@ dependencies = [
[[package]]
name = "zbus-secret-service-keyring-store"
-version = "1.0.0"
+version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ccede190ba363386a24e8021c7f3848393976609ec9f5d1f8c6c09ef37075b4"
+checksum = "74801d001b9e7729adb4f1825b67b398185fed424749aa3d8bacf70417137d9a"
dependencies = [
"keyring-core",
"secret-service",
@@ -7264,14 +7342,14 @@ dependencies = [
[[package]]
name = "zbus_macros"
-version = "5.18.0"
+version = "5.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe96480bed92df2b442a1a30df364e12d08eed03aeb061f2b8dc6afb2be91119"
+checksum = "2990635d09ade6df1868f72f8cac69a876a90981e8bd3c40b1be413f8dc88f40"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
- "syn 2.0.119",
+ "syn 3.0.5",
"zbus_names",
"zvariant",
"zvariant_utils",
@@ -7289,19 +7367,28 @@ dependencies = [
]
[[package]]
-name = "zerocopy"
-version = "0.8.55"
+name = "zcheapstr"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5a105cd7b140f6eeec8acff2ea38135d3cab283ada58540f629fe51e46696eb"
+checksum = "d1afec51604565183aeb5c54c20aeab286120d4e4460f7f76e3e8bb8c0d99473"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "zerocopy"
+version = "0.8.57"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d35102a9f36d089ccae9e4c6802bc118be4487b80aaffc0ab4e0cf5ce92d2873"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
-version = "0.8.55"
+version = "0.8.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fe976fb70c78cd64cccfe3a6fc142244e8a77b70959b30faf9d0ac37ee228eb"
+checksum = "146c01f5ab44258da43cf276c74a2763db2ff3969c9c652c3f2de07041d0b2bc"
dependencies = [
"proc-macro2",
"quote",
@@ -7337,21 +7424,23 @@ checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e"
[[package]]
name = "zerotrie"
-version = "0.2.4"
+version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf"
+checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f"
dependencies = [
"displaydoc",
"yoke",
"zerofrom",
+ "zerovec",
]
[[package]]
name = "zerovec"
-version = "0.11.6"
+version = "0.11.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239"
+checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8"
dependencies = [
+ "serde",
"yoke",
"zerofrom",
"zerovec-derive",
@@ -7359,13 +7448,13 @@ dependencies = [
[[package]]
name = "zerovec-derive"
-version = "0.11.3"
+version = "0.11.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555"
+checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.119",
+ "syn 3.0.5",
]
[[package]]
@@ -7376,7 +7465,7 @@ checksum = "2d04a6b5381502aa6087c94c669499eb1602eb9c5e8198e534de571f7154809b"
dependencies = [
"crc32fast",
"flate2",
- "indexmap 2.14.0",
+ "indexmap 2.14.2",
"memchr",
"typed-path",
"zopfli",
@@ -7384,9 +7473,9 @@ dependencies = [
[[package]]
name = "zlib-rs"
-version = "0.6.6"
+version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b142a20ec14a91d5bc708c1dc21b080c550113d8aa77afa29635673a65dd02c5"
+checksum = "34b31d188d9d685a4f9c7b46d6e36631b07058d2cfe190267adce54dc230bf12"
[[package]]
name = "zmij"
@@ -7412,23 +7501,41 @@ version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a"
dependencies = [
- "zstd-safe",
+ "zstd-safe 7.3.0",
+]
+
+[[package]]
+name = "zstd"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf06bd8162af0734b344780deb55b42a2429ae430870d13fcc12f238e880fe6e"
+dependencies = [
+ "zstd-safe 8.0.0",
]
[[package]]
name = "zstd-safe"
-version = "7.2.4"
+version = "7.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d"
+checksum = "64d80649ab6db9d9f6f9c80a40becd948eda4714a0a5ac8c4d157a32231c7882"
+dependencies = [
+ "zstd-sys",
+]
+
+[[package]]
+name = "zstd-safe"
+version = "8.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae42c0555055784c70058d19ba8e275528e8a99a706684868ace5da4e716a4ab"
dependencies = [
"zstd-sys",
]
[[package]]
name = "zstd-sys"
-version = "2.0.16+zstd.1.5.7"
+version = "2.1.0+zstd.1.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748"
+checksum = "0ef0a8027ec3ee71300ab3bcbcd0393f434aa72b91ca6d635a39941deae8eea0"
dependencies = [
"cc",
"pkg-config",
@@ -7436,40 +7543,41 @@ dependencies = [
[[package]]
name = "zvariant"
-version = "5.13.1"
+version = "5.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bee2a0bcd2a907786a456fff45aaaaf54c9ba5f50b71ae9ec1a4edd200c94911"
+checksum = "c1d34c27cc6cdd1f458427519dd6b8612f7b7e3f7b9a0b2355d041dda9869147"
dependencies = [
"endi",
"enumflags2",
"serde",
"winnow 1.0.4",
+ "zcheapstr",
"zvariant_derive",
"zvariant_utils",
]
[[package]]
name = "zvariant_derive"
-version = "5.13.1"
+version = "5.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38a708216a18780796770bfe3f4739c7c83a3e8f789b755534bbbc06e4e23e12"
+checksum = "864155e69b4352db0c7f374917bf45d1e0c8d17659c8b3dbf9795f3673f8c497"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
- "syn 2.0.119",
+ "syn 3.0.5",
"zvariant_utils",
]
[[package]]
name = "zvariant_utils"
-version = "3.5.0"
+version = "4.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90cb9383f9b45290407a1258b202d3f8f01db719eb60b4e4055c6375af4fc7c7"
+checksum = "bad0294361a320b694a328460dc73add56c306150f5cb6bfafc44446120008a3"
dependencies = [
"proc-macro2",
"quote",
"serde",
- "syn 2.0.119",
+ "syn 3.0.5",
"winnow 1.0.4",
]
diff --git a/Cargo.toml b/Cargo.toml
index d6801da5..31b11445 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -29,14 +29,14 @@ axum-server = "0.8"
base64ct = "1.8"
blake3 = "1.8"
bytes = "1"
-bytesize = "2.6"
+bytesize = "2.7"
clap = "4.6"
color-print = "0.3"
colored = "3.1"
compact_str = "0.10"
config = "0.15"
dashmap = "6.2"
-dirs = "6.0"
+dirs = "7.0"
enumset = "1.1"
eyre = "0.6"
flate2 = "1.1"
@@ -49,8 +49,8 @@ humantime = "2.4"
indexmap = "2.14"
indicatif = "0.18"
itertools = "0.15"
-json_to_table = "0.13"
-keyring = "4.1"
+json_to_table = "0.14"
+keyring = "4.2"
miette = "7.6"
mime = "0.3"
parking_lot = "0.12"
@@ -63,7 +63,7 @@ rcgen = { version = "0.14", default-features = false, features = ["crypto", "pem
reqwest = { version = "0.13", default-features = false, features = ["rustls"] }
rkyv = "0.8"
rpassword = "7.5"
-rstest = "0.26"
+rstest = "0.27"
rustls = { version = "0.23", default-features = false, features = ["logging", "std", "tls12"] }
s2-api = { path = "api", version = "0.31" }
s2-common = { path = "common", version = "0.41" }
@@ -76,11 +76,11 @@ secrecy = "0.10.3"
semver = "1.0"
serde = "1.0"
serde_json = "1.0"
-slatedb = "0.15.0"
+slatedb = "0.16.0"
strum = "0.28"
-tabled = "0.21"
+tabled = "0.22"
tempfile = "3.27"
-testcontainers = "0.27"
+testcontainers = "0.28"
thiserror = "2.0"
tikv-jemallocator = { version = "0.7", features = ["unprefixed_malloc_on_supported_platforms"] }
time = "0.3"
@@ -92,9 +92,9 @@ tower-http = "0.7"
tracing = "0.1"
tracing-subscriber = "0.3"
utoipa = "=5.4"
-uuid = "1.24"
+uuid = "1.26"
xxhash-rust = "0.8"
-zstd = "0.13"
+zstd = "0.14"
[patch.crates-io]
utoipa = { git = "https://github.com/infiniteregrets/utoipa", rev = "82bcb28a792ba9a0d29963827ec473823099fc94" }
diff --git a/deny.toml b/deny.toml
index 48b28369..24aac6b4 100644
--- a/deny.toml
+++ b/deny.toml
@@ -6,8 +6,6 @@ ignore = [
# Transitive deps via slatedb/foyer — not actionable.
"RUSTSEC-2024-0436", # paste unmaintained
"RUSTSEC-2025-0141", # bincode unmaintained
- # Transitive dep via tabled_derive — no fixed release available.
- "RUSTSEC-2026-0173", # proc-macro-error2 unmaintained
# Transitive dep via object_store (slatedb) — fix requires object_store >=0.13.
"RUSTSEC-2026-0194", # quick-xml quadratic attribute check
"RUSTSEC-2026-0195", # quick-xml unbounded namespace allocation
diff --git a/lite/src/backend/basins.rs b/lite/src/backend/basins.rs
index c37511af..211c056e 100644
--- a/lite/src/backend/basins.rs
+++ b/lite/src/backend/basins.rs
@@ -11,11 +11,16 @@ use slatedb::{
};
use time::OffsetDateTime;
-use super::{Backend, bgtasks::BgtaskTrigger, store::db_txn_get};
+use super::{
+ Backend,
+ bgtasks::BgtaskTrigger,
+ store::{db_txn_commit_durable, db_txn_get},
+};
use crate::backend::{
error::{
BasinAlreadyExistsError, BasinDeletionPendingError, BasinNotFoundError, DeleteBasinError,
GetBasinConfigError, ListBasinsError, ProvisionBasinError, ReconfigureBasinError,
+ StorageError,
},
kv,
};
@@ -63,6 +68,8 @@ impl Backend {
Ok(Page::new(basins, has_more))
}
+ /// Any outcome asserting the basin exists — `Created`, `Updated`, `Noop`,
+ /// or `BasinAlreadyExists` — is readable at `DurabilityLevel::Remote`.
pub async fn provision_basin(
&self,
basin: BasinName,
@@ -73,7 +80,13 @@ impl Backend {
let txn = self.db.begin(IsolationLevel::SerializableSnapshot).await?;
- let existing_meta = db_txn_get(&txn, &meta_key, kv::basin_meta::deser_value).await?;
+ // A transaction can see metadata that has not been flushed yet.
+ let existing_entry = txn.get_key_value(&meta_key).await?;
+ let existing_seq = existing_entry.as_ref().map(|kv| kv.seq);
+ let existing_meta = existing_entry
+ .map(|kv| kv::basin_meta::deser_value(kv.value))
+ .transpose()
+ .map_err(StorageError::from)?;
if let Some(existing_meta) = &existing_meta
&& existing_meta.deleted_at.is_some()
{
@@ -85,7 +98,7 @@ impl Backend {
let new_creation_idempotency_key = request_token
.as_ref()
.map(|req_token| creation_idempotency_key(req_token, &config));
- return if new_creation_idempotency_key.is_some()
+ let result = if new_creation_idempotency_key.is_some()
&& existing.creation_idempotency_key == new_creation_idempotency_key
{
Ok(ProvisionResult::Noop(BasinInfo {
@@ -97,6 +110,10 @@ impl Backend {
} else {
Err(BasinAlreadyExistsError { basin }.into())
};
+ drop(txn);
+ self.await_durable_seq(existing_seq.expect("existing meta was read"))
+ .await?;
+ return result;
}
(Some(existing), ProvisionMode::Ensure) => {
let meta = kv::basin_meta::BasinMeta {
@@ -130,11 +147,15 @@ impl Backend {
}),
};
- if !matches!(&outcome, ProvisionResult::Noop(_)) {
+ if matches!(&outcome, ProvisionResult::Noop(_)) {
+ drop(txn);
+ self.await_durable_seq(existing_seq.expect("noop implies existing meta"))
+ .await?;
+ } else {
let meta = outcome.inner();
txn.put(&meta_key, kv::basin_meta::ser_value(meta))?;
- txn.commit().await?;
+ db_txn_commit_durable(txn).await?;
}
Ok(outcome.map(|meta| BasinInfo {
@@ -179,7 +200,7 @@ impl Backend {
txn.put(&meta_key, kv::basin_meta::ser_value(&meta))?;
- txn.commit().await?;
+ db_txn_commit_durable(txn).await?;
Ok(meta.config)
}
@@ -197,7 +218,7 @@ impl Backend {
kv::basin_deletion_pending::ser_key(&basin),
kv::basin_deletion_pending::ser_value(&StreamNameStartAfter::default()),
)?;
- txn.commit().await?;
+ db_txn_commit_durable(txn).await?;
self.bgtask_trigger(BgtaskTrigger::BasinDeletion);
}
Ok(())
diff --git a/lite/src/backend/bgtasks/basin_deletion.rs b/lite/src/backend/bgtasks/basin_deletion.rs
index e11563be..8fdda524 100644
--- a/lite/src/backend/bgtasks/basin_deletion.rs
+++ b/lite/src/backend/bgtasks/basin_deletion.rs
@@ -122,7 +122,7 @@ impl Backend {
kv::basin_deletion_pending::ser_key(basin),
kv::basin_deletion_pending::ser_value(cursor),
);
- self.db.write(batch).await?;
+ self.db.write(batch).await?.await_durable().await?;
Ok(())
}
@@ -131,7 +131,7 @@ impl Backend {
let mut batch = WriteBatch::new();
batch.delete(kv::basin_meta::ser_key(basin));
batch.delete(kv::basin_deletion_pending::ser_key(basin));
- self.db.write(batch).await?;
+ self.db.write(batch).await?.await_durable().await?;
Ok(())
}
}
@@ -149,7 +149,7 @@ mod tests {
use time::OffsetDateTime;
use super::super::tests::test_backend;
- use crate::backend::{Backend, kv};
+ use crate::backend::{Backend, kv, test_util::DbWriteTestExt as _};
fn basin_meta(deleted_at: Option) -> kv::basin_meta::BasinMeta {
kv::basin_meta::BasinMeta {
@@ -185,16 +185,16 @@ mod tests {
kv::basin_meta::ser_key(basin),
kv::basin_meta::ser_value(&basin_meta(Some(OffsetDateTime::now_utc()))),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
backend
.db
.put(
kv::basin_deletion_pending::ser_key(basin),
kv::basin_deletion_pending::ser_value(&StreamNameStartAfter::default()),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
}
async fn seed_tombstoned_streams(backend: &Backend, basin: &BasinName, count: usize) {
@@ -207,7 +207,7 @@ mod tests {
kv::stream_meta::ser_value(&stream_meta(Some(deleted_at))),
);
}
- backend.db.write(batch).await.unwrap();
+ backend.db.write(batch).assert_durable().await;
}
#[tokio::test]
@@ -221,16 +221,16 @@ mod tests {
kv::basin_meta::ser_key(&basin),
kv::basin_meta::ser_value(&basin_meta(Some(OffsetDateTime::now_utc()))),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
backend
.db
.put(
kv::basin_deletion_pending::ser_key(&basin),
kv::basin_deletion_pending::ser_value(&StreamNameStartAfter::default()),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
let has_more = backend.clone().tick_basin_deletion().await.unwrap();
assert!(!has_more);
@@ -266,8 +266,8 @@ mod tests {
kv::stream_meta::ser_key(&basin, &stream),
kv::stream_meta::ser_value(&stream_meta(None)),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
let has_more = backend.clone().tick_basin_deletion().await.unwrap();
assert!(!has_more);
@@ -384,16 +384,16 @@ mod tests {
kv::basin_meta::ser_key(&basin),
kv::basin_meta::ser_value(&basin_meta(Some(OffsetDateTime::now_utc()))),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
backend
.db
.put(
kv::basin_deletion_pending::ser_key(&basin),
kv::basin_deletion_pending::ser_value(&cursor),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
// First tick resets cursor from past-end back to the beginning.
let has_more = backend.clone().tick_basin_deletion().await.unwrap();
@@ -435,8 +435,8 @@ mod tests {
kv::stream_meta::ser_key(&basin, &stream),
kv::stream_meta::ser_value(&stream_meta(Some(deleted_at))),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
let has_more = backend.clone().tick_basin_deletion().await.unwrap();
assert!(!has_more);
@@ -474,8 +474,8 @@ mod tests {
kv::stream_meta::ser_key(&basin, &stream),
kv::stream_meta::ser_value(&stream_meta(Some(deleted_at))),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
// First tick: blocked by tombstoned stream.
let has_more = backend.clone().tick_basin_deletion().await.unwrap();
@@ -493,8 +493,8 @@ mod tests {
backend
.db
.delete(kv::stream_meta::ser_key(&basin, &stream))
- .await
- .unwrap();
+ .assert_durable()
+ .await;
// Second tick: no streams remain, completes.
let has_more = backend.clone().tick_basin_deletion().await.unwrap();
diff --git a/lite/src/backend/bgtasks/stream_doe.rs b/lite/src/backend/bgtasks/stream_doe.rs
index d09b108d..91d3a0ab 100644
--- a/lite/src/backend/bgtasks/stream_doe.rs
+++ b/lite/src/backend/bgtasks/stream_doe.rs
@@ -138,7 +138,7 @@ impl Backend {
for entry in pending {
batch.delete(kv::stream_doe_deadline::ser_key(entry.deadline, stream_id));
}
- self.db.write(batch).await?;
+ self.db.write(batch).await?.await_durable().await?;
Ok(())
}
@@ -170,6 +170,8 @@ impl Backend {
kv::stream_doe_deadline::ser_key(deadline, stream_id),
kv::stream_doe_deadline::ser_value(min_age),
)
+ .await?
+ .await_durable()
.await?;
Ok(())
}
@@ -194,7 +196,7 @@ mod tests {
use super::{super::tests::test_backend, PendingDoeBatch, TimestampSecs};
use crate::{
- backend::{Backend, kv},
+ backend::{Backend, kv, test_util::DbWriteTestExt as _},
stream_id::StreamId,
};
@@ -237,24 +239,24 @@ mod tests {
creation_idempotency_key: None,
}),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
backend
.db
.put(
kv::stream_meta::ser_key(basin, stream),
kv::stream_meta::ser_value(&meta),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
backend
.db
.put(
kv::stream_id_mapping::ser_key(stream_id),
kv::stream_id_mapping::ser_value(basin, stream),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
stream_id
}
@@ -289,8 +291,8 @@ mod tests {
backend
.db
.put(key.clone(), kv::stream_tail_position::ser_value(position))
- .await
- .unwrap();
+ .assert_durable()
+ .await;
let kv = backend
.db
.get_key_value(key)
@@ -353,8 +355,8 @@ mod tests {
kv::stream_doe_deadline::ser_key(deadline, stream_id),
kv::stream_doe_deadline::ser_value(MIN_AGE),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
process_pending_stream_doe_at(&backend, stream_id, deadline).await;
@@ -394,8 +396,8 @@ mod tests {
kv::stream_doe_deadline::ser_key(deadline, stream_id),
kv::stream_doe_deadline::ser_value(min_age),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
process_pending_stream_doe_at(&backend, stream_id, deadline).await;
@@ -445,8 +447,8 @@ mod tests {
kv::stream_doe_deadline::ser_key(deadline, stream_id),
kv::stream_doe_deadline::ser_value(MIN_AGE),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
process_pending_stream_doe_at(&backend, stream_id, deadline).await;
@@ -491,16 +493,16 @@ mod tests {
kv::stream_record_timestamp::ser_key(stream_id, pos),
kv::stream_record_timestamp::ser_value(),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
backend
.db
.put(
kv::stream_doe_deadline::ser_key(deadline, stream_id),
kv::stream_doe_deadline::ser_value(MIN_AGE),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
let has_more = backend.clone().tick_stream_doe().await.unwrap();
assert!(!has_more);
@@ -549,8 +551,8 @@ mod tests {
kv::stream_doe_deadline::ser_key(deadline, stream_id),
kv::stream_doe_deadline::ser_value(MIN_AGE),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
let has_more = backend.clone().tick_stream_doe().await.unwrap();
assert!(!has_more);
@@ -594,16 +596,16 @@ mod tests {
kv::stream_doe_deadline::ser_key(deadline_a, stream_id),
kv::stream_doe_deadline::ser_value(MIN_AGE),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
backend
.db
.put(
kv::stream_doe_deadline::ser_key(deadline_b, stream_id),
kv::stream_doe_deadline::ser_value(MIN_AGE),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
let page = backend.list_pending_stream_doe(far_future).await.unwrap();
assert!(!page.has_more);
@@ -711,16 +713,16 @@ mod tests {
kv::stream_tail_position::ser_key(stream_id),
kv::stream_tail_position::ser_value(pos),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
backend
.db
.put(
kv::stream_record_timestamp::ser_key(stream_id, pos),
kv::stream_record_timestamp::ser_value(),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
let min_age = Duration::from_secs(30);
let expected_delay =
@@ -776,8 +778,8 @@ mod tests {
kv::stream_doe_deadline::ser_key(existing_deadline, stream_id),
kv::stream_doe_deadline::ser_value(initial_min_age),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
backend
.reconfigure_stream(
diff --git a/lite/src/backend/bgtasks/stream_trim.rs b/lite/src/backend/bgtasks/stream_trim.rs
index 2a1f332d..8cee7a21 100644
--- a/lite/src/backend/bgtasks/stream_trim.rs
+++ b/lite/src/backend/bgtasks/stream_trim.rs
@@ -9,7 +9,12 @@ use slatedb::{
use tracing::instrument;
use crate::{
- backend::{Backend, error::StorageError, kv, store::db_txn_get},
+ backend::{
+ Backend,
+ error::StorageError,
+ kv,
+ store::{db_txn_commit_durable, db_txn_get},
+ },
stream_id::StreamId,
};
@@ -100,13 +105,13 @@ impl Backend {
batch.delete(kv::stream_record_data::ser_key(stream_id, pos));
batch_size += 1;
if batch_size >= DELETE_BATCH_SIZE {
- self.db.write(batch).await?;
+ self.db.write(batch).await?.await_durable().await?;
batch = WriteBatch::new();
batch_size = 0;
}
}
if batch_size > 0 {
- self.db.write(batch).await?;
+ self.db.write(batch).await?.await_durable().await?;
}
Ok(has_remaining_records)
}
@@ -147,7 +152,7 @@ impl Backend {
txn.delete(kv::stream_tail_position::ser_key(stream_id))?;
txn.delete(kv::stream_fencing_token::ser_key(stream_id))?;
}
- txn.commit().await?;
+ db_txn_commit_durable(txn).await?;
Ok(())
}
}
@@ -170,7 +175,10 @@ mod tests {
use time::OffsetDateTime;
use super::super::tests::test_backend;
- use crate::{backend::kv, stream_id::StreamId};
+ use crate::{
+ backend::{kv, test_util::DbWriteTestExt as _},
+ stream_id::StreamId,
+ };
fn test_record() -> Metered {
let record = Record::try_from_parts(vec![], Bytes::from_static(b"trim-test")).unwrap();
@@ -198,16 +206,16 @@ mod tests {
kv::stream_record_data::ser_key(stream_id, pos),
kv::stream_record_data::ser_value(metered.as_ref()),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
backend
.db
.put(
kv::stream_record_timestamp::ser_key(stream_id, pos),
kv::stream_record_timestamp::ser_value(),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
}
backend
@@ -216,8 +224,8 @@ mod tests {
kv::stream_trim_point::ser_key(stream_id),
kv::stream_trim_point::ser_value(trim_point(3)),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
backend.clone().tick_stream_trim().await.unwrap();
@@ -275,16 +283,16 @@ mod tests {
kv::stream_meta::ser_key(&basin, &stream),
kv::stream_meta::ser_value(&meta),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
backend
.db
.put(
kv::stream_id_mapping::ser_key(stream_id),
kv::stream_id_mapping::ser_value(&basin, &stream),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
backend
.db
.put(
@@ -294,8 +302,8 @@ mod tests {
timestamp: 1234,
}),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
let token = FencingToken::from_str("token-1").unwrap();
backend
.db
@@ -303,8 +311,8 @@ mod tests {
kv::stream_fencing_token::ser_key(stream_id),
kv::stream_fencing_token::ser_value(&token),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
for seq in 0..3 {
let pos = StreamPosition {
@@ -317,16 +325,16 @@ mod tests {
kv::stream_record_data::ser_key(stream_id, pos),
kv::stream_record_data::ser_value(metered.as_ref()),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
backend
.db
.put(
kv::stream_record_timestamp::ser_key(stream_id, pos),
kv::stream_record_timestamp::ser_value(),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
}
backend
@@ -335,8 +343,8 @@ mod tests {
kv::stream_trim_point::ser_key(stream_id),
kv::stream_trim_point::ser_value(trim_point(SeqNum::MAX)),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
backend.clone().tick_stream_trim().await.unwrap();
@@ -402,8 +410,8 @@ mod tests {
kv::stream_trim_point::ser_key(stream_id),
kv::stream_trim_point::ser_value(trim_point(10)),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
backend
.finalize_trim(stream_id, trim_point(5))
@@ -435,7 +443,7 @@ mod tests {
kv::stream_trim_point::ser_value(trim_point(1)),
);
}
- backend.db.write(batch).await.unwrap();
+ backend.db.write(batch).assert_durable().await;
let has_more = backend.clone().tick_stream_trim().await.unwrap();
assert!(has_more);
@@ -472,24 +480,24 @@ mod tests {
kv::stream_record_data::ser_key(stream_id, pos),
kv::stream_record_data::ser_value(metered.as_ref()),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
backend
.db
.put(
kv::stream_record_timestamp::ser_key(stream_id, pos),
kv::stream_record_timestamp::ser_value(),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
backend
.db
.put(
kv::stream_trim_point::ser_key(stream_id),
kv::stream_trim_point::ser_value(trim_point(1)),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
backend.clone().tick_stream_trim().await.unwrap();
@@ -532,16 +540,16 @@ mod tests {
kv::stream_record_data::ser_key(stream_id_a, pos_a),
kv::stream_record_data::ser_value(metered.as_ref()),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
backend
.db
.put(
kv::stream_record_timestamp::ser_key(stream_id_a, pos_a),
kv::stream_record_timestamp::ser_value(),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
let pos_b = StreamPosition {
seq_num: seq,
@@ -553,16 +561,16 @@ mod tests {
kv::stream_record_data::ser_key(stream_id_b, pos_b),
kv::stream_record_data::ser_value(metered.as_ref()),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
backend
.db
.put(
kv::stream_record_timestamp::ser_key(stream_id_b, pos_b),
kv::stream_record_timestamp::ser_value(),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
}
backend
@@ -571,8 +579,8 @@ mod tests {
kv::stream_trim_point::ser_key(stream_id_a),
kv::stream_trim_point::ser_value(trim_point(2)),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
backend.clone().tick_stream_trim().await.unwrap();
@@ -645,7 +653,7 @@ mod tests {
kv::stream_trim_point::ser_key(stream_id),
kv::stream_trim_point::ser_value(trim_point(total)),
);
- backend.db.write(batch).await.unwrap();
+ backend.db.write(batch).assert_durable().await;
backend.clone().tick_stream_trim().await.unwrap();
@@ -706,8 +714,8 @@ mod tests {
kv::stream_trim_point::ser_key(stream_id),
kv::stream_trim_point::ser_value(trim_point(5)),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
backend
.finalize_trim(stream_id, trim_point(5))
diff --git a/lite/src/backend/core.rs b/lite/src/backend/core.rs
index cf7558ad..e291833c 100644
--- a/lite/src/backend/core.rs
+++ b/lite/src/backend/core.rs
@@ -431,6 +431,7 @@ mod tests {
use time::OffsetDateTime;
use super::*;
+ use crate::backend::test_util::DbWriteTestExt as _;
async fn new_test_backend() -> Backend {
let object_store: Arc =
@@ -484,7 +485,7 @@ mod tests {
kv::stream_record_data::ser_key(stream_id, record_pos),
kv::stream_record_data::ser_value(metered_record.as_ref()),
);
- backend.db.write(wb).await.unwrap();
+ backend.db.write(wb).assert_durable().await;
backend
.start_streamer(StreamerGenerationId::next(), basin.clone(), stream.clone())
diff --git a/lite/src/backend/mod.rs b/lite/src/backend/mod.rs
index be8db24f..f3dd8acb 100644
--- a/lite/src/backend/mod.rs
+++ b/lite/src/backend/mod.rs
@@ -11,6 +11,9 @@ mod store;
mod streamer;
mod streams;
+#[cfg(test)]
+mod test_util;
+
mod append;
mod kv;
diff --git a/lite/src/backend/read.rs b/lite/src/backend/read.rs
index c4e0df3d..b0c8ece9 100644
--- a/lite/src/backend/read.rs
+++ b/lite/src/backend/read.rs
@@ -430,7 +430,9 @@ mod tests {
use super::*;
use crate::{
- backend::{FOLLOWER_MAX_LAG, kv, streamer::DORMANT_TIMEOUT},
+ backend::{
+ FOLLOWER_MAX_LAG, kv, streamer::DORMANT_TIMEOUT, test_util::DbWriteTestExt as _,
+ },
stream_id::StreamId,
};
@@ -505,8 +507,8 @@ mod tests {
),
kv::stream_record_timestamp::ser_value(),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
backend
.db
.put(
@@ -519,8 +521,8 @@ mod tests {
),
kv::stream_record_timestamp::ser_value(),
)
- .await
- .unwrap();
+ .assert_durable()
+ .await;
// Should find record in stream_a
let result = resolve_timestamp(&backend.db, stream_a, 500).await.unwrap();
@@ -582,7 +584,7 @@ mod tests {
let stream_id = StreamId::new(&basin, &stream);
let mut batch = WriteBatch::new();
batch.delete(kv::stream_record_data::ser_key(stream_id, ack.start));
- backend.db.write(batch).await.unwrap();
+ backend.db.write(batch).assert_durable().await;
let start = ReadStart {
from: ReadFrom::SeqNum(0),
@@ -924,7 +926,7 @@ mod tests {
delete_batch.delete(kv::stream_record_data::ser_key(stream_id, ack.start));
}
- backend.db.write(delete_batch).await.unwrap();
+ backend.db.write(delete_batch).assert_durable().await;
tokio::time::advance(wait + Duration::from_secs(1)).await;
tokio::task::yield_now().await;
diff --git a/lite/src/backend/store.rs b/lite/src/backend/store.rs
index 791bc561..30212f62 100644
--- a/lite/src/backend/store.rs
+++ b/lite/src/backend/store.rs
@@ -42,3 +42,11 @@ pub(super) async fn db_txn_get + Send, V>(
let value = txn.get(key).await?.map(deser).transpose()?;
Ok(value)
}
+
+/// Commit metadata changes and wait until remote reads can observe them.
+pub(super) async fn db_txn_commit_durable(txn: DbTransaction) -> Result<(), slatedb::Error> {
+ if let Some(handle) = txn.commit().await? {
+ handle.await_durable().await?;
+ }
+ Ok(())
+}
diff --git a/lite/src/backend/streamer.rs b/lite/src/backend/streamer.rs
index d8acb6a3..553705b1 100644
--- a/lite/src/backend/streamer.rs
+++ b/lite/src/backend/streamer.rs
@@ -28,7 +28,7 @@ use s2_storage::record::{
};
use slatedb::{
IterationOrder, WriteBatch,
- config::{PutOptions, ScanOptions, Ttl, WriteOptions},
+ config::{PutOptions, ScanOptions, Ttl},
};
use tokio::{
sync::{Semaphore, SemaphorePermit, broadcast, mpsc, oneshot},
@@ -1019,7 +1019,7 @@ async fn db_submit_append(
}: DbSubmitAppendOptions,
) -> Result {
let ttl = match retention {
- RetentionPolicy::Age(age) => Ttl::ExpireAfter(age.as_millis() as u64),
+ RetentionPolicy::Age(age) => Ttl::ExpireAfterMillis(age.as_millis() as u64),
RetentionPolicy::Infinite() => Ttl::NoExpiry,
};
let ttl_put_opts = PutOptions { ttl };
@@ -1058,11 +1058,8 @@ async fn db_submit_append(
kv::stream_tail_position::ser_key(stream_id),
kv::stream_tail_position::ser_value(next_pos(&records)),
);
- let write_opts = WriteOptions {
- await_durable: false,
- ..Default::default()
- };
- let write_handle = db.write_with_options(wb, &write_opts).await?;
+ // The durability notifier tracks this sequence and acknowledges the append after flush.
+ let write_handle = db.write(wb).await?;
Ok(InFlightAppend {
db_seq: write_handle.seqnum(),
records,
diff --git a/lite/src/backend/streams.rs b/lite/src/backend/streams.rs
index b59d8032..a9542c9e 100644
--- a/lite/src/backend/streams.rs
+++ b/lite/src/backend/streams.rs
@@ -15,7 +15,7 @@ use tracing::instrument;
use super::{
Backend,
- store::db_txn_get,
+ store::{db_txn_commit_durable, db_txn_get},
streamer::{TerminalTrimCondition, TerminalTrimOutcome, doe_arm_delay},
};
use crate::{
@@ -226,7 +226,7 @@ impl Backend {
)?;
}
- txn.commit().await?;
+ db_txn_commit_durable(txn).await?;
}
if let ProvisionResult::Updated(meta) = &outcome
@@ -333,7 +333,7 @@ impl Backend {
)?;
}
- txn.commit().await?;
+ db_txn_commit_durable(txn).await?;
if let Some(client) = self.streamer_client_if_active(&basin, &stream) {
client.advise_reconfig(meta.config.clone());
@@ -390,7 +390,7 @@ impl Backend {
if meta.deleted_at.is_none() {
meta.deleted_at = Some(OffsetDateTime::now_utc());
txn.put(&meta_key, kv::stream_meta::ser_value(&meta))?;
- txn.commit().await?;
+ db_txn_commit_durable(txn).await?;
}
Ok(())
}
diff --git a/lite/src/backend/test_util.rs b/lite/src/backend/test_util.rs
new file mode 100644
index 00000000..13994a23
--- /dev/null
+++ b/lite/src/backend/test_util.rs
@@ -0,0 +1,18 @@
+use std::future::Future;
+
+use slatedb::{Error, WriteHandle};
+
+/// Finish a fixture write and assert that remote reads can observe it.
+pub(super) trait DbWriteTestExt:
+ Future