Compare commits

..

50 commits

Author SHA1 Message Date
detail-app[bot]
0e99007688
fix(lite): resolve AWS region from profile chain for static credentials (#780)
**Detail bug report:** [View on
Detail](https://app.detail.dev/org_89d327b3-b883-4365-b6a3-46b6701342a9/bugs/bug_8cac9425-ce79-4db4-b569-bf59c78d8d81)

Closes #778

## Bug

In `lite/src/server.rs`, `s3_builder()` builds the `object_store` AWS S3
client backing SlateDB. It branches on whether static env credentials
(`AWS_ACCESS_KEY_ID` + `AWS_SECRET_ACCESS_KEY`) are present:

- The **static-credentials arm** set credentials but never resolved the
region — it relied solely on `AmazonS3Builder::from_env()`, which reads
only `AWS_*` env vars and silently falls back to `us-east-1` when
`AWS_REGION`/`AWS_DEFAULT_REGION` are unset.
- The **sibling `_ =>` arm** resolved region from the full AWS default
chain (env → profile → IMDS) via `aws_config::load_defaults` and called
`with_region`.

The two arms of the same `match` resolved the same field (`region`) by
different chains. A deployment that supplied credentials via env but
region via `~/.aws/config` hit the static arm, dropped the profile
region, and targeted `us-east-1`. Against real AWS S3 for a bucket
elsewhere this produces a wrong-region `301`; `object_store` does not
disable reqwest's redirect following, and `remove_sensitive_headers`
strips the `Authorization` header on the cross-host redirect, so the
followed request to the correct-region endpoint is unauthenticated and
AWS rejects it with a non-retryable `403`.

## Fix

The static arm now resolves region env-first (`AWS_REGION` then
`AWS_DEFAULT_REGION`), and only when both env knobs are absent falls
back to `aws_config::load_defaults(...).await.region()` — the same
standard chain the `_ =>` arm uses — then applies
`builder.with_region(region)`. This makes both arms resolve region by
the same chain tiers while preserving the env-first fast path for the
common static-creds + `AWS_REGION` workflow (`load_defaults` is only
reached when env region is absent, i.e. the trigger intersection). The
credential wiring (`StaticCredentialProvider` + `AWS_SESSION_TOKEN`) is
unchanged; the change is purely additive region resolution.

## Testing

Added three hermetic `#[tokio::test]` regression tests in
`lite/src/server.rs` (`mod tests`) covering the static arm's new
contract:
- profile region is applied when env region is absent (the regression —
confirmed to fail on the pre-fix code with `left: None, right:
Some("eu-west-1")` and pass after the fix)
- env region takes precedence over profile (preserves the documented
fast path)
- no bogus region is synthesized when no region is available anywhere

Routine checks all pass: `cargo check --locked -p s2-lite` (default and
`--all-features`), `cargo +nightly fmt --all --check`, `cargo clippy
--locked -p s2-lite --all-targets -- -D warnings --allow deprecated`,
and the full s2-lite nextest suite (331/331).

End-to-end smoke (not versioned — ran against a containerized
S3-compatible backend during development): built the release `server`
binary and ran it against `adobe/s3mock` over HTTP with static env creds
and the region supplied only via `AWS_CONFIG_FILE` (the bug-trigger
intersection, `AWS_REGION`/`AWS_DEFAULT_REGION` unset). The server
logged the resolved `region=us-east-1` from the profile tier (previously
silently dropped), SlateDB wrote manifest/WAL/compaction objects to S3,
and the full basin/stream/append/tail API lifecycle succeeded (HTTP
201/200), with a clean SIGTERM shutdown and no `403`/`301`/redirect
errors. (LocalStack's latest image is license-gated; S3Mock was used as
the equivalent HTTP S3-compatible backend.) This confirms the
custom-endpoint static-creds path still works and the profile region is
now honored; it does not reproduce the live-AWS redirect/auth-strip
chain because S3Mock does not issue wrong-region 301s or validate SigV4
region.

Not verified: live AWS S3 end-to-end (a real bucket in a non-`us-east-1`
region with static keys). The environment has no AWS credentials (`aws
sts get-caller-identity` returns `Unable to locate credentials`, no
`~/.aws`, no IMDS), so the live-AWS 301 → auth-stripped 403 path could
not be captured directly. The hermetic regression test covers the
trigger (region misresolution), and the S3Mock smoke covers
no-regression plus profile-region resolution; the live-AWS HTTP-chain
leg is the only uncovered item.

---
_Automatic Fixes PRs can be [configured
here](https://app.detail.dev/org_89d327b3-b883-4365-b6a3-46b6701342a9/settings/repos/repo_c4bd6a47-9b7d-4b62-9c18-8cf0ac18a8f9/bugs)._

---------

Co-authored-by: detail-app[bot] <180357370+detail-app[bot]@users.noreply.github.com>
2026-09-27 09:18:22 -07:00
release-pleaze[bot]
3a292004d8 Bump s2-lite-helm chart to appVersion 0.43.0 2026-09-25 23:55:21 +00:00
release-pleaze[bot]
62af3892f3
chore: release (#783)
## 🤖 New release

* `s2-common`: 0.41.3 -> 0.42.0 (✓ API compatible changes)
* `s2-api`: 0.31.5 -> 0.32.0 (✓ API compatible changes)
* `s2-resource-spec`: 0.2.2 -> 0.3.0 (✓ API compatible changes)
* `s2-lite`: 0.42.14 -> 0.43.0 (✓ API compatible changes)
* `s2-sdk`: 0.34.10 -> 0.35.0 (✓ API compatible changes)
* `s2-cli`: 0.42.14 -> 0.43.0
* `s2-testcontainers`: 0.42.14 -> 0.43.0
* `s2-storage`: 0.2.5 -> 0.2.6

<details><summary><i><b>Changelog</b></i></summary><p>

## `s2-common`

<blockquote>

## [0.42.0] - 2026-09-25

### Features

- [**breaking**] Expose storage classes as strings and in location
responses ([#775](https://github.com/s2-streamstore/s2/issues/775))

<!-- generated by git-cliff -->
</blockquote>

## `s2-api`

<blockquote>

## [0.32.0] - 2026-09-25

### Features

- [**breaking**] Expose storage classes as strings and in location
responses ([#775](https://github.com/s2-streamstore/s2/issues/775))

### Bug Fixes

- Skip s2s response compression the client refused with q=0
([#781](https://github.com/s2-streamstore/s2/issues/781))

<!-- generated by git-cliff -->
</blockquote>

## `s2-resource-spec`

<blockquote>

## [0.3.0] - 2026-09-25

### Features

- [**breaking**] Expose storage classes as strings and in location
responses ([#775](https://github.com/s2-streamstore/s2/issues/775))

<!-- generated by git-cliff -->
</blockquote>

## `s2-lite`

<blockquote>

## [0.43.0] - 2026-09-25

### Features

- [**breaking**] Expose storage classes as strings and in location
responses ([#775](https://github.com/s2-streamstore/s2/issues/775))

### Bug Fixes

- Gate basin deletion draining on per-basin progress
([#779](https://github.com/s2-streamstore/s2/issues/779))

<!-- generated by git-cliff -->
</blockquote>

## `s2-sdk`

<blockquote>

## [0.35.0] - 2026-09-25

### Features

- [**breaking**] Expose storage classes as strings and in location
responses ([#775](https://github.com/s2-streamstore/s2/issues/775))

<!-- generated by git-cliff -->
</blockquote>

## `s2-cli`

<blockquote>

## [0.43.0] - 2026-09-25

### Features

- [**breaking**] Expose storage classes as strings and in location
responses ([#775](https://github.com/s2-streamstore/s2/issues/775))

### Bug Fixes

- Show match-none token scopes as no access, not as wildcards
([#782](https://github.com/s2-streamstore/s2/issues/782))

<!-- generated by git-cliff -->
</blockquote>

## `s2-testcontainers`

<blockquote>

## [0.43.0] - 2026-09-25

<!-- generated by git-cliff -->
</blockquote>

## `s2-storage`

<blockquote>

## [0.2.6] - 2026-09-25

### Miscellaneous Tasks

- Updated the following local packages: s2-common

<!-- generated by git-cliff -->
</blockquote>


</p></details>

---
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>
2026-09-26 04:47:13 +05:30
Mehul Arora
0c16d61ed7
feat!: expose storage classes as strings and in location responses (#775)
Expose available storage classes and the configured default in location
responses. Use `CompactString` for class names throughout the SDK, API,
common types, CLI, and resource specs, allowing future names to pass
through without a client release. Docs and the locations API describe
the available choices. The CLI displays available classes and the
configured default in `list-locations`, `get-default-location`, and
`set-default-location`; responses from older servers retain the previous
output when those fields are absent.

Remove OSS storage-class enums and the shared Express fallback. Omitted
classes stay unspecified until the server resolves them: cloud uses the
location default for basins and basin defaults for streams. CLI
apply/diff preserves unknown names. Dry-run reads the location default
once when needed and compares streams using the desired basin defaults.
If discovery is unavailable, it marks storage-class resolution as
uncertain instead of reporting a false change or no-op. Actual apply
leaves omitted classes for the server to resolve. Lite retains Express
as its own fallback when both the stream and basin omit a class,
including when reading older metadata. Explicit class names and basin
inheritance are preserved.

BREAKING CHANGE: remove `StorageClass` from `s2_sdk::types`,
`s2_api::v1::config`, `s2_common::config`, and `s2_resource_spec`.
Replace `.with_storage_class(StorageClass::Express)` with
`.with_storage_class("express")`, and enum matches with string
comparisons. Fields use `CompactString` with their existing
optional/patch wrappers; `s2_common::config::StreamConfig.storage_class`
is now `Option<CompactString>`. Omitted/null patch semantics are
preserved.

Validation: formatting, workspace Clippy with all features/targets, and
903 existing workspace tests passed. All 146 applicable integration
tests from unchanged Python SDK main passed against the local Lite
build. Temporary API checks verified the Express fallback, basin
inheritance, unknown class names, and null resets. Temporary CLI checks
verified all three location commands with future class names and
populated, empty, missing, and null discovery fields. Temporary dry-run
checks covered unchanged and changed defaults, future names, desired
basin inheritance, explicit overrides, unavailable discovery, error
propagation, and one lookup across multiple basins. A local Lite
create/preview/reapply check confirmed an uncertain preview and an
unchanged reapply. The generated CLI schema still matches. Existing Rust
tests were adapted; no new tests were added.

Related: [cloud](https://github.com/s2-streamstore/s2-cloud/pull/1836),
[specs](https://github.com/s2-streamstore/s2-specs/pull/24). Deploy [the
docs redirect](https://github.com/s2-streamstore/docs/pull/361) before
publishing the `/docs/storage-classes` links.
2026-09-26 04:29:54 +05:30
detail-app[bot]
bc762d0947
fix(lite): gate basin deletion draining on per-basin progress (#779)
`tick_basin_deletion` reported more work whenever its page of pending
basins was full, even if every basin on the page was blocked waiting for
`stream_trim` to purge tombstoned streams. With 32 or more basins
pending and the first page blocked, `run_tick` re-ran the tick back to
back without sleeping, rescanning the same basins until `stream_trim`
caught up.

`PageProgress` now records per-item outcomes through a shared
`ItemProgress`: an item can advance with more work ready, complete, or
be blocked. Basin deletion reports one outcome per basin, so the backlog
keeps draining while some basin makes progress and otherwise waits for
the next tick. Resetting a basin's cursor to the start counts as
blocked, since treating it as progress would rescan a multi-page basin
in a tight loop. Stream trim and delete-on-empty record `Ok` as
completed and transaction conflicts as blocked, as before.

Closes #777.

---------

Co-authored-by: detail-app[bot] <180357370+detail-app[bot]@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Shikhar Bhushan <shikhar@schmizz.net>
2026-09-24 23:55:12 -07:00
breken
113121b88a
fix(api): skip s2s response compression the client refused with q=0 (#781)
## Summary

`CompressionAlgorithm::from_accept_encoding` dropped everything after
`;` in each `Accept-Encoding` entry without reading it. As a result, a
coding the client explicitly refused with a zero weight was still
selected:

| `Accept-Encoding` | before | after |
| --- | --- | --- |
| `zstd;q=0, gzip` | `Zstd` | `Gzip` |
| `gzip;q=0` | `Gzip` | `None` |
| `zstd;q=0, gzip;q=0` | `Zstd` | `None` |

RFC 9110 §12.4.2 defines `q=0` as "not acceptable". Because of this bug,
s2s read and append session frames of 1 KiB or more were compressed with
the exact coding the client had refused. The Rust SDK decodes either
codec, so it isn't affected in practice. A client that leaves out a
decompressor and says so with `q=0` gets frames it cannot decode.

With this change, codings whose `q` parameter is zero are skipped.
Non-zero weights keep the existing zstd-over-gzip preference, and
`gzip;q=0.8, deflate` still selects gzip.

## Tests

- `api`: `from_accept_encoding_skips_refused_codings` (rstest, 5 cases).
Before the fix, 4 cases fail (for example `left: Zstd, right: Gzip`).
After the fix, all pass.
- `lite`: `s2s_read_does_not_compress_with_refused_encodings` appends a
4 KiB record, reads it over `s2s/proto` with `Accept-Encoding: zstd;q=0,
gzip;q=0`, and checks the compression bits of the frame flag byte.
Before the fix: `left: 1 (zstd), right: 0`. After the fix it passes, and
the record round-trips.
- `cargo test --locked -p s2-api -p s2-lite`: all pass.
- `cargo +nightly fmt --all` is clean. `cargo clippy -p s2-api -p
s2-lite --all-targets -- -D warnings --allow deprecated` is clean (run
without the `codegen` feature because `protoc` isn't installed locally).

Note: #550 also touches this function (feature-gating the codecs). The
two changes are independent, but one of them will need a trivial rebase.

This fix and its tests were prepared with AI assistance (Claude). I
reviewed and ran them locally.

---------

Co-authored-by: breken-ai <312387581+breken-ai@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-24 22:44:40 -07:00
breken
f686bf893c
fix(cli): show match-none token scopes as no access, not as wildcards (#782)
## Summary

The API defines a scope resource set of `{"exact": ""}` as "match no
resources". The SDK decodes it into `BasinMatcher::None`,
`StreamMatcher::None`, and `AccessTokenMatcher::None`. The CLI's SDK→CLI
conversions turned each of those into an empty **prefix**, which means
the opposite: "match everything". So for a token that grants no basin,
stream, or token access:

- `s2 list-access-tokens` printed `basins=*  streams=*  tokens=*`.
- The JSON output reported `{"prefix": ""}`.

This makes a no-access token look like a full-access one. `s2 apply`
already renders the same scope correctly as `none` (`cli/src/diff.rs`).

This change maps a match-none matcher to an unset matcher
(`Option::None`). The CLI already renders an unset matcher as `∅` in the
summary and serializes it as `null`. That is the same shape s2-lite
returns, since it omits a match-none resource set.

## Tests

- `types::tests::match_none_scope_matchers_render_as_no_access`
deserializes the wire scope
`{"basins":{"exact":""},"streams":{"exact":""},"access_tokens":{"exact":""}}`
through the SDK into the CLI's `AccessTokenInfo`. It then checks the
list summary line and the JSON.
  - Before the fix: `basins=*  streams=*  tokens=*  perms=none  ops=0`.
- After the fix: `basins=∅ streams=∅ tokens=∅ perms=none ops=0`, with
the three fields `null`.
- `cargo test --locked -p s2-cli --bin s2 --test cli`: all pass.
- `cargo +nightly fmt --all` is clean. `cargo clippy --locked -p s2-cli
--all-features --all-targets -- -D warnings --allow deprecated` is
clean.

This fix and its test were prepared with AI assistance (Claude). I
reviewed and ran them locally.

Co-authored-by: breken-ai <312387581+breken-ai@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-24 22:37:17 -07:00
release-pleaze[bot]
de7db559be Bump s2-lite-helm chart to appVersion 0.42.14 2026-09-24 14:54:21 +00:00
release-pleaze[bot]
73f0e5d78b
chore: release (#774)
## 🤖 New release

* `s2-lite`: 0.42.13 -> 0.42.14 (✓ API compatible changes)
* `s2-sdk`: 0.34.9 -> 0.34.10 (✓ API compatible changes)
* `s2-cli`: 0.42.13 -> 0.42.14
* `s2-testcontainers`: 0.42.13 -> 0.42.14

<details><summary><i><b>Changelog</b></i></summary><p>

## `s2-lite`

<blockquote>

## [0.42.14] - 2026-09-24

### Bug Fixes

- Coalesce delete-on-empty scheduling
([#772](https://github.com/s2-streamstore/s2/issues/772))

<!-- generated by git-cliff -->
</blockquote>

## `s2-sdk`

<blockquote>

## [0.34.10] - 2026-09-24

### Bug Fixes

- Preserve uncertainty across append retries
([#767](https://github.com/s2-streamstore/s2/issues/767))

<!-- generated by git-cliff -->
</blockquote>

## `s2-cli`

<blockquote>

## [0.42.14] - 2026-09-24

<!-- generated by git-cliff -->
</blockquote>

## `s2-testcontainers`

<blockquote>

## [0.42.14] - 2026-09-24

<!-- generated by git-cliff -->
</blockquote>


</p></details>

---
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>
2026-09-24 07:16:53 -07:00
Shikhar Bhushan
d5d46ac503
fix(lite): coalesce delete-on-empty scheduling (#772)
Delete-on-empty could delete a stream before an increased `min_age`
elapsed, or discard its last deadline while records were still alive
after a retention change. Retain one authoritative DOE schedule per
stream so deferred deletion always preserves future work.

- Add separate key spaces for per-stream `Scheduled`/`Parked` state and
the time-ordered check queue. Ordinary appends no longer read or write
DOE schedules, and the refresh bookkeeping and historical `min_age`
cutoffs are removed.
- Keep deletion serialized through the streamer, checking the current
minimum age, stream incarnation, configuration revision, and stable
tail. A nonempty observation remains useful despite pending or
concurrent appends: defer to a record's stored expiration or park behind
a record without a TTL. Empty results still require stable-tail checks
before deletion. Configuration-revision mismatches retry in ten minutes
without relying on a possibly stale minimum age.
- Observe state, mapping, and metadata through a read-only snapshot. A
normal deferred check uses one serializable completion transaction to
validate the revision, consume its ticket, and install its successor.
Successful deletion completion only needs to revalidate the scheduler
state. Obsolete-work cleanup also revalidates its snapshot before
removing state.
- DOE configuration changes and completed regular trims coalesce with an
earlier check while advancing the state's revision, so an in-flight
worker cannot consume or postpone a concurrent wake. Partial trims wake
parked streams too. Trim scheduling normally reads only DOE state; if a
full trim finds no state, it checks current metadata and initializes
scheduling for enabled streams. This preserves the upgrade path for
infinite-retention streams whose last legacy deadline was already
consumed.
- Process due checks and legacy migration in pages of up to 10,000 keys,
with four concurrent workers. Use legacy deadline keys only for bounded,
idempotent migration, including future deadlines. A consistent snapshot
identifies cleanup-only streams, whose legacy keys share one write batch
per page. Streams needing initialization revalidate eligibility
transactionally and install state atomically with legacy-key removal.
Existing new state and its revision are untouched.
- Expected transaction conflicts leave affected work pending while the
rest of the page finishes. Backlog processing continues when at least
one item succeeds; a fully conflicted page waits for the next tick
rather than retrying in a tight loop. Other storage errors still fail
the tick. Configuration APIs retain retryable transaction-conflict
responses.

Enabling DOE or changing `min_age` on an existing stream requests
evaluation at the next tick. An empty stream whose last write is already
old enough can therefore be deleted then, without the previous
retention-plus-age scheduling delay. With unchanged finite retention and
no trims, deletion eligibility is based on approximately `max(retention,
min_age)` since the last write, subject to the ten-minute minimum retry
interval and background-tick granularity.

Migration removes legacy deadlines, and older binaries do not consume
the new scheduler state: downgrading does not preserve migrated
schedules. There is no metadata-wide backfill. An upgraded stream
without any legacy deadline gains state through a full trim or a DOE
configuration change; otherwise it remains unscheduled.

Regression coverage includes both configuration APIs and maximum-age
boundaries, stored expirations across retention changes, pending and
concurrent appends after the minimum age has elapsed,
trim/configuration/recreation races, full-trim recovery without legacy
deadlines, transactional wake preservation, draining migration pages
through the background loop, batched cleanup and snapshot revalidation,
durable terminal trim, and encoding/queue-boundary properties.

Validation:

- `just fmt`
- `just test --status-level fail --final-status-level fail
--no-fail-fast` — 886 tests passed across 12 binaries; the recipe
excludes Docker-backed and live integration suites.
- `just clippy`

Fixes #639.
Supersedes #769.
2026-09-23 23:53:56 -07:00
devin-ai-integration[bot]
db563d19e4
fix(sdk): preserve uncertainty across append retries (#767)
## Summary

Consider:
- user is appending with retry policy that retries all failures (even
indefinite)
- attempt 1 fails, with indefinite error (e.g. unavailable)
- attempt 2 fails, this time with definite error (e.g. aborted)

Right now, we'd return the final error, which is definite. This gives
the impression that no side effect was possible from the entire op,
across all attempts, but actually is only about the final attempt.

The fix tracks uncertainty across attempts using shared internal
helpers. If the final error is definite but an earlier attempt may have
had side effects, return `IndefiniteFailure { final_attempt_error }`
holding the final definite error. The entire append operation remains
indeterminate. This preserves the final attempt's diagnostic and
retryability while keeping `has_no_side_effects()` false for the logical
append. An already indefinite final error is returned directly, and
successful retries still succeed. Sessions track uncertainty per
unresolved batch.

Helper unit tests cover uncertainty wrapping, error classification, and
access to the final attempt's error.

Link to Devin session:
https://app.devin.ai/sessions/cbd9b244893a445ea0a9a49dc0cd0fe9
Open in Devin Desktop:
https://app.devin.ai/desktop/session/cbd9b244893a445ea0a9a49dc0cd0fe9?variant=devin
Requested by: @sgbalogh

---------

Co-authored-by: Stephen Balogh <stephen@s2.dev>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-23 11:11:44 -07:00
detail-app[bot]
da425b1602
docs: correct lite flush interval default comment (#773)
The `SL8_FLUSH_INTERVAL` comment in README.md stated the default was
"50ms
for remote bucket / 5ms in-memory", but after the separate WAL object
store
landed, the default follows the WAL store type when one is configured. A
reader using `--bucket` (S3) with `--wal-local-root` (local) would
wrongly
assume a 50ms default when it is actually 5ms.

Introduced by commit 1e954f014a
(@infiniteregrets, #766)

---
_Doc Drift PRs can be [configured
here](https://app.detail.dev/org_89d327b3-b883-4365-b6a3-46b6701342a9/settings/repos/repo_c4bd6a47-9b7d-4b62-9c18-8cf0ac18a8f9/doc-drift)._

Co-authored-by: detail-app[bot] <180357370+detail-app[bot]@users.noreply.github.com>
2026-09-22 11:04:03 -07:00
release-pleaze[bot]
66899e659b Bump s2-lite-helm chart to appVersion 0.42.13 2026-09-22 17:10:02 +00:00
release-pleaze[bot]
d2cece9730
chore: release (#758)
## 🤖 New release

* `s2-common`: 0.41.2 -> 0.41.3 (✓ API compatible changes)
* `s2-api`: 0.31.4 -> 0.31.5 (✓ API compatible changes)
* `s2-storage`: 0.2.4 -> 0.2.5 (✓ API compatible changes)
* `s2-lite`: 0.42.12 -> 0.42.13 (✓ API compatible changes)
* `s2-sdk`: 0.34.8 -> 0.34.9 (✓ API compatible changes)
* `s2-cli`: 0.42.12 -> 0.42.13
* `s2-testcontainers`: 0.42.12 -> 0.42.13 (✓ API compatible changes)

<details><summary><i><b>Changelog</b></i></summary><p>

## `s2-common`

<blockquote>

## [0.41.3] - 2026-09-22

### Miscellaneous Tasks

- Update Cargo.toml dependencies

<!-- generated by git-cliff -->
</blockquote>

## `s2-api`

<blockquote>

## [0.31.5] - 2026-09-22

### Bug Fixes

- Preserve SSE read budgets across reconnects
([#727](https://github.com/s2-streamstore/s2/issues/727))

<!-- generated by git-cliff -->
</blockquote>

## `s2-storage`

<blockquote>

## [0.2.5] - 2026-09-22

### Miscellaneous Tasks

- Update Cargo.toml dependencies

<!-- generated by git-cliff -->
</blockquote>

## `s2-lite`

<blockquote>

## [0.42.13] - 2026-09-22

### Features

- Support a separate WAL object store
([#766](https://github.com/s2-streamstore/s2/issues/766))

### Bug Fixes

- Keep streamers alive until pending writes settle
([#757](https://github.com/s2-streamstore/s2/issues/757))
- Await durable deletion markers on retries
([#756](https://github.com/s2-streamstore/s2/issues/756))
- Prevent stale or missed stream config updates
([#759](https://github.com/s2-streamstore/s2/issues/759))
- Apply the at-tail read guard after resolving a timestamp start
([#762](https://github.com/s2-streamstore/s2/issues/762))
- Prevent deletion races when recreating streams
([#760](https://github.com/s2-streamstore/s2/issues/760))
- Preserve SSE read budgets across reconnects
([#727](https://github.com/s2-streamstore/s2/issues/727))

### Performance

- Avoid copying serialized append buffers
([#763](https://github.com/s2-streamstore/s2/issues/763))
- Reuse acknowledgement queue capacity
([#764](https://github.com/s2-streamstore/s2/issues/764))

<!-- generated by git-cliff -->
</blockquote>

## `s2-sdk`

<blockquote>

## [0.34.9] - 2026-09-22

<!-- generated by git-cliff -->
</blockquote>

## `s2-cli`

<blockquote>

## [0.42.13] - 2026-09-22

### Features

- Support a separate WAL object store
([#766](https://github.com/s2-streamstore/s2/issues/766))

<!-- generated by git-cliff -->
</blockquote>

## `s2-testcontainers`

<blockquote>

## [0.42.13] - 2026-09-22

### Miscellaneous Tasks

- Update Cargo.toml dependencies

<!-- generated by git-cliff -->
</blockquote>


</p></details>

---
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>
2026-09-22 22:02:24 +05:30
devin-ai-integration[bot]
4741e6d212
fix(lite): preserve SSE read budgets across reconnects (#727)
Bounded SSE reads could exceed their original count or byte limit after
repeated reconnects: the resume path subtracted cumulative progress from
the original budget, but emitted IDs reset progress on each connection.

Seed the emitted counters from `Last-Event-Id` when adjusting the read
bounds, and use saturating addition for client-supplied counters. This
matches the fix already on cloud main in
[s2-cloud#1778](https://github.com/s2-streamstore/s2-cloud/pull/1778).
Document the cumulative meaning of both counters on the shared API type.

Handler tests reconnect with unchanged bounds and actual emitted IDs
through three deliveries, compare against an uninterrupted read, and
verify that another resume delivers nothing after exhaustion. Coverage
includes count-only, bytes-only, both limits with either one reached
first, and near-maximum counters on bounded and unbounded reads.

Validation:
- `just fmt`
- `just test`: 843 passed
- `just clippy`
- Restoring the old counter reset makes all four reconnect regression
cases fail.

Closes #745.

---------

Co-authored-by: Stephen Balogh <stephen@s2.dev>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: shikhar <shikhar@s2.dev>
2026-09-22 09:19:26 -07:00
Mehul Arora
c512df1c08
docs: remove WAL location warning (#771)
Removes the WAL storage warning block from the README.

Made with [Cursor](https://cursor.com)

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-22 21:39:53 +05:30
Mehul Arora
a4d3eb309a
docs: trim WAL location warning (#770)
Drops the leading sentence of the WAL storage warning in the README and
rewords the remainder to stand alone.

Made with [Cursor](https://cursor.com)

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-22 21:32:00 +05:30
Mehul Arora
b45ca65d63
feat(helm): persistent volumes and separate WAL storage (#768)
Follow-up to #766. Exposes the `s2 lite` storage flags the chart was
missing, and folds the README feedback from that review into a combined
storage section.

**Chart.** The main store is now `objectStorage` (`--bucket`) or
`persistentVolume` (`--local-root` on a PVC), and the WAL is
`walStorage.bucket` (`--wal-bucket`) or `walStorage.persistentVolume`
(`--wal-local-root` on a PVC). The two volume blocks share one shape
(`enabled`, `mountPath`, `size`, `storageClass`, `existingClaim`) and
one `pvc.yaml` template; claims are named `<fullname>-data` and
`<fullname>-wal` and default to `/data` and `/wal`.
`walStorage.endpoint`/`region` map to
`S2LITE_WAL_AWS_ENDPOINT_URL_S3`/`S2LITE_WAL_AWS_REGION`; separate
credentials go through `env`, as for the main store. Templates fail on
`objectStorage` + `persistentVolume` both enabled, on `walStorage`
without a persistent main store, and on both WAL modes at once.
`NOTES.txt` reports the storage and WAL locations. The deployment
already uses `strategy: Recreate`, so `ReadWriteOnce` claims do not
block upgrades.

**README.** Replaces the "Separate WAL storage" section with a "Storage"
section presenting main-store and WAL settings side by side in one table
(bucket/directory, endpoint, region, credentials, session token). The
chart README links there instead of repeating it.

Both READMEs and `values.yaml` note that the WAL location must stay the
same across restarts, since changing it does not migrate data and the
server does not error.

**Merge after the next lite release.** `walStorage` passes
`--wal-bucket`/`--wal-local-root`, which the current default image
(`appVersion` 0.42.12) does not have; #766 ships in the next release.
Wait for its `appVersion` bump commit on `main` before merging.
`persistentVolume` alone works on 0.42.12.

Chart-created PVCs are deleted by `helm uninstall`; both volume blocks
expose `annotations` for `helm.sh/resource-policy: keep`, documented in
the chart README.

Chart `version` is not bumped; the `Bump Chart Version` workflow handles
that before release.

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-22 21:20:21 +05:30
Shikhar Bhushan
54413b050b
fix(lite): prevent deletion races when recreating streams (#760)
Deleting and recreating a stream under the same name could let a delayed
deletion request, background trim job, or delete-on-empty (DOE) deadline
modify the recreated stream. A crash between terminal trim and metadata
marking also allowed provisioning in ensure mode to acknowledge an
update that pending cleanup would erase, while independent
initialization reads could revive a deleted stream.

- Read initialization state from one durable snapshot and reject
deletion-pending metadata. Finish shared initialization even if every
caller cancels, so snapshots are released and failed initialization
slots are removed. Report a missing live-stream ID mapping as a storage
invariant error instead of aborting the process.
- Reject provisioning and configuration updates once terminal trim has
begun. Stream deletion uses the append path to persist the terminal trim
command and marker together. Repeated deletion requests, late DOE
checks, and append rejections reporting deletion pending wait for the
original terminal append to become durable through the same
acknowledgement queue. Dropped deletion replies report an indeterminate
outcome because the write may have completed. Recheck the terminal trim
marker transactionally before marking metadata, and wait for the
transaction's read sequence to become durable if the trim worker has
already removed it.
- Bound each trim job by the commit sequence of the marker it scanned.
Record deletion stops before records newer than that marker, and the
existing finalization transaction clears only that marker version. Stale
work cannot delete records from a recreated stream or finalize its
deletion. Bulk record deletes stay in ordinary write batches, with no
additional reads; finite trims that empty a stream arm DOE in the
finalization transaction.
- Use the ID mapping's creation sequence to reject earlier streams' DOE
deadlines, including recreation between eligibility lookup and streamer
delivery. Give every new DOE schedule a random 128-bit key suffix.
Cleanup deletes the exact scanned keys with an ordinary write batch,
bounded by the 10,000-row scan limit, without per-deadline rereads or
cleanup transactions. A later schedule survives even if it has the same
stream and deadline.

Existing deadline keys remain readable and need no rewrite; new
schedules never overwrite them. New deadline keys are 16 bytes longer
and cannot be decoded by older binaries. Other persisted formats are
unchanged. DOE changes only address lifecycle races; retention
scheduling and min_age policy are unchanged.

Regression tests cover the split deletion state, durability of deletion
replies and append rejections, delayed deletion completion, stale trim
work across recreation, and initializer cancellation. The lifecycle test
checks both the recreated stream's records and its terminal trim marker,
which has the same trim value as the original stream's marker. It fails
without the record sequence bound and without the marker version guard.
DOE tests cover stale work with both key formats and re-arming during
cleanup; codec tests cover both formats and expired-range boundaries.
The repeated deletion request regression fails before its fix. Mutation
checks also confirm the DOE tests catch stale-generation work and reused
scheduling keys.

Closes #627
Closes #628
Closes #734

Validation: `just fmt`, `just test` (833 passed), `just clippy`, and
`RUST_LOG=trace just sim meta smoke --seed 1` with DOE temporarily
enabled in the smoke fixture (both runs succeeded with identical traces;
fixture restored afterward).
2026-09-21 23:14:34 -07:00
Mehul Arora
1e954f014a
feat(lite): support a separate WAL object store (#766)
Lite stores WAL writes and LSM data in the same object store, so WAL
latency can be affected by memtable flushes and compaction output. Add
`--wal-bucket` and `--wal-local-root` to configure a dedicated WAL store
through SlateDB's existing builder API. The README leads with a local
filesystem WAL and a remote S3 bucket for the main database.

`--wal-bucket` reuses the main S3 connection configuration by default,
including its endpoint, region and credentials. Optional
`S2LITE_WAL_AWS_*` overrides select a different server or credentials;
the WAL endpoint takes precedence over an inherited
`AWS_ENDPOINT_URL_S3`. Both buckets use the same S3 builder; a
WAL-specific key pair replaces the complete credential set, including
its optional session token. Omitting both WAL selectors preserves the
shared-store default.

Filesystem WAL storage retains fsync. The default flush interval follows
the WAL store type, and `SL8_FLUSH_INTERVAL` still takes precedence.
Separate WAL storage requires an explicitly configured persistent main
store. Both stores use `--path` and must be reopened at the same
locations; these options do not migrate existing WAL data.

Validation: `just fmt`, `just test` (840 passed), and `just clippy`,
using locked dependencies. Tests cover CLI constraints, inherited S3
connection settings, generic and S3-specific endpoint inheritance,
endpoint-only and credential overrides, session-token isolation,
physical file routing, and recovery of acknowledged records after
killing and restarting S2. No dependency changes. No performance
improvement is claimed.

Fixes #673.
2026-09-22 10:54:27 +05:30
Deepak Modi
00d082d343
fix(lite): apply the at-tail read guard after resolving a timestamp start (#762)
## Problem

`read_start_seq_num` rejects a read that starts at the tail and cannot
follow, returning `UnwrittenError` (HTTP 416). The check matched only
`ReadPosition::SeqNum`, and it ran *before* the
`ReadPosition::Timestamp` arm resolved its start through
`resolve_timestamp(..).unwrap_or(tail)`. A timestamp start that resolves
to the tail therefore slipped past it.

On an empty stream, the two spellings of the same read disagree:

```
GET /v1/streams/{stream}/records?seq_num=0    ->  416, tail {0,0}
GET /v1/streams/{stream}/records?timestamp=0  ->  200, {"records":[]}
```

The `200` also contradicts the documented meaning of an empty unary
batch, which is that an explicit `count`, `bytes`, or `until` bound
could not be satisfied.

The same disagreement appears whenever a timestamp start resolves to the
tail, not only on an empty stream.

## Fix

Resolve the start position to a sequence number first, then apply one
at-tail check to the resolved value.

Clamped starts are unaffected: `clamp` already rewrites the position to
`tail.seq_num` before this point, so it reaches the check exactly as it
did before. Timestamp starts that resolve behind the tail are unaffected
too.

## Tests

`test_read_at_tail_of_empty_stream_returns_unwritten` covers both
spellings on an empty stream. With the fix reverted, the `seq_num` case
passes and the `timestamp` case fails, so it pins the behaviour rather
than restating it.

`cargo nextest run -p s2-lite` passes 255/255, and `cargo fmt --check`
plus `cargo clippy --all-targets -- -D warnings` are clean.

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: shikhar <shikhar@s2.dev>
2026-09-21 10:37:54 -07:00
Mehul Arora
0a1210af32
perf(lite): reuse acknowledgement queue capacity (#764)
`PendingAppends::on_stable` shrank the acknowledgement queue to zero
whenever it drained. A single append therefore freed a four-slot buffer
and forced the next append to allocate again. The streamer also advances
durability one append at a time, so a 16-append burst began shrinking
before it finished draining.

Keep small queue allocations for reuse, with a minimum shrink target of
16 senders. Queues still start unallocated, and larger backlogs retain
the existing policy of shrinking at one-quarter occupancy to twice the
remaining length. Once drained, they retain space for at most 16
senders.

Regression tests exercise repeated single-append and 16-append bursts,
reclamation after a 128-append burst, and acknowledgement delivery only
after durability advances. Against the original code, the reuse tests
fail with capacities `0` versus `4` and `8` versus `16`; all four tests
pass with the fix. This establishes allocation reuse; end-to-end
throughput impact has not been measured.

Validation:

- `just fmt`
- `RUSTUP_TOOLCHAIN=stable cargo test --locked -p s2-lite --lib
--all-features backend::append::tests` — 4 passed.
- `RUSTUP_TOOLCHAIN=stable just test` — 813 passed.
- `RUSTUP_TOOLCHAIN=stable just clippy` — passed with warnings denied.
2026-09-20 22:02:00 -07:00
Mehul Arora
4192c6241f
perf(lite): avoid copying serialized append buffers (#763)
Append persistence serializes record data, timestamp indexes, and
metadata into owned `Bytes`, then passes them through SlateDB's
`put`/`put_with_options` APIs, which copy both keys and values again.
Use `put_bytes`/`put_bytes_with_options` to transfer those buffers
directly into the write batch while preserving the existing TTL options
and durable acknowledgement path.

Validation:

- `just fmt` passed.
- `RUSTUP_TOOLCHAIN=stable just test` passed: 809 tests.

The redundant allocations and copies are removed; the end-to-end
throughput impact has not been measured.
2026-09-21 08:29:55 +05:30
Shikhar Bhushan
9163a9f5c3
fix(lite): prevent stale or missed stream config updates (#759)
Active streamers could apply configuration notifications out of order or
miss a committed update when its request was cancelled or the streamer
was still initializing. This could leave them enforcing outdated
retention or timestamping settings indefinitely.

Use durable metadata commit sequences to apply only newer
configurations, starting with the sequence read alongside the initial
config. An owned task completes each stream-config commit and its
notification even if the caller cancels. Initializing streamers retain
the newest pending configuration and enqueue it when publishing the
ready client, under the same slot lock. Metadata is durable before
acknowledgment, while streamer initialization and configuration
application remain asynchronous.

Shared read helpers centralize value, sequence, and timestamp decoding
while preserving durable reads and transaction snapshots. The existing
Ensure/PATCH integration tests now cover cancellation before flush, and
a focused initialization test verifies delivery of the newest queued
configuration. Removing the fixes makes all three regression cases fail;
the two normal update cases still pass. The retention regression also
checks that delayed notifications cannot restore an older TTL policy.

Validation: `just fmt`, `just test` (809 passed), `just clippy`, and
`RUST_LOG=trace just sim meta smoke --seed 1` (two successful runs with
identical traces).
2026-09-18 07:06:28 -07:00
Shikhar Bhushan
73519db845
fix(lite): await durable deletion markers on retries (#756)
A delete retry could observe an unflushed deletion marker and return
success while durable metadata still described the basin or stream as
active. Wait for the existing marker's commit sequence on the idempotent
path, and retrigger basin cleanup after that wait so a cancelled first
request does not suppress the notification.

For streams, the terminal trim was already durable; this also makes the
metadata marker durable before the delete response.

Validation: `just fmt`, `just test` (804 passed), and `just clippy`. Two
focused control-plane tests retry a cancelled basin or stream deletion
while its metadata marker remains unflushed, using disabled automatic
flushing and paused time. Both fail on the base commit and pass with
this fix.

Related independent durability fixes:
[#757](https://github.com/s2-streamstore/s2/pull/757),
[#759](https://github.com/s2-streamstore/s2/pull/759). All three combine
without conflicts; combined validation passes `just fmt`, `just test`
(810 passed), `just clippy`, and simulator smoke/trace determinism with
seed 1.
2026-09-17 22:47:22 -07:00
Shikhar Bhushan
75804295d1
fix(lite): keep streamers alive until pending writes settle (#757)
Cancelling an append after its write reaches memory can drop the
streamer's last client lease while that write is still awaiting
durability. After the idle timeout, a replacement streamer would recover
the older durable tail and reuse the cancelled append's sequence number.

Keep the streamer alive while writes are queued or in flight. Once those
writes settle, normal dormancy can resume and any replacement can
recover their positions from durable storage.

Validation: `just fmt`, `just test` (803 passed), and `just clippy`. A
focused streamer lifecycle test disables automatic flushing, cancels an
accepted append, and advances past the actual dormancy timeout with no
client leases. It verifies that the streamer stays alive until the write
is durable and exits normally after flushing. Restoring the original
idle-exit condition makes the test fail.

Related independent durability fixes:
[#756](https://github.com/s2-streamstore/s2/pull/756),
[#759](https://github.com/s2-streamstore/s2/pull/759). All three combine
without conflicts; combined validation passes `just fmt`, `just test`
(810 passed), `just clippy`, and simulator smoke/trace determinism with
seed 1.
2026-09-17 22:44:58 -07:00
Shikhar Bhushan
69919e9d99
chore(deps): upgrade SlateDB to 0.16 and refresh dependencies (#755)
## 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](1b537fecdc...7c1141044a).
|
| `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).
|

<details>
<summary>Runtime-sensitive and incompatible transitive updates</summary>

- **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](7d61577048...99c15c08f1),
[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](44e43c7786...4e582392df),
[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](ddd21715ac...f9ec79e754),
[parse-display
comparison](2fd6c6ed8e...3a91021cd3).

- **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.

</details>

## 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.

<details>
<summary>Complete registry lockfile version changes</summary>

### `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 |

</details>
2026-09-17 22:09:54 -07:00
release-pleaze[bot]
17156f6e10 Bump s2-lite-helm chart to appVersion 0.42.12 2026-09-18 04:30:10 +00:00
release-pleaze[bot]
52b6e2e8fb
chore: release (#742)
## 🤖 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

<details><summary><i><b>Changelog</b></i></summary><p>

## `s2-api`

<blockquote>

## [0.31.4] - 2026-09-16

### Miscellaneous Tasks

- Sync specs submodule
([#739](https://github.com/s2-streamstore/s2/issues/739))

<!-- generated by git-cliff -->
</blockquote>

## `s2-lite`

<blockquote>

## [0.42.12] - 2026-09-16

### Miscellaneous Tasks

- Update Cargo.lock dependencies

<!-- generated by git-cliff -->
</blockquote>

## `s2-sdk`

<blockquote>

## [0.34.8] - 2026-09-16

### Bug Fixes

- Reject Content-Type in default headers
([#740](https://github.com/s2-streamstore/s2/issues/740))

<!-- generated by git-cliff -->
</blockquote>

## `s2-cli`

<blockquote>

## [0.42.12] - 2026-09-16

### Miscellaneous Tasks

- Update Cargo.lock dependencies

<!-- generated by git-cliff -->
</blockquote>

## `s2-testcontainers`

<blockquote>

## [0.42.12] - 2026-09-16

<!-- generated by git-cliff -->
</blockquote>


</p></details>

---
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>
2026-09-17 20:55:25 -07:00
Shikhar Bhushan
1841dafda0
ci: allow exact-version security exceptions to dependency cooldown (#744)
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
2026-09-15 23:03:11 -07:00
devin-ai-integration[bot]
a271f7d9c4
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 <stephen@s2.dev>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-11 14:11:47 -07:00
github-actions[bot]
09b57bf77f
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](f29cbcaafe...edaa1fbcb2)
|
---

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>
2026-09-12 02:24:09 +05:30
release-pleaze[bot]
c364f506ee Bump s2-lite-helm chart to appVersion 0.42.11 2026-09-11 16:19:31 +00:00
release-pleaze[bot]
1a0312afa0
chore: release (#733)
## 🤖 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

<details><summary><i><b>Changelog</b></i></summary><p>

## `s2-api`

<blockquote>

## [0.31.3] - 2026-09-11

### Features

- `s2-stream-config` header for auto-created streams
([#718](https://github.com/s2-streamstore/s2/issues/718))

<!-- generated by git-cliff -->
</blockquote>

## `s2-lite`

<blockquote>

## [0.42.11] - 2026-09-11

### Features

- `s2-stream-config` header for auto-created streams
([#718](https://github.com/s2-streamstore/s2/issues/718))

<!-- generated by git-cliff -->
</blockquote>

## `s2-sdk`

<blockquote>

## [0.34.7] - 2026-09-11

### Features

- `s2-stream-config` header for auto-created streams
([#718](https://github.com/s2-streamstore/s2/issues/718))

<!-- generated by git-cliff -->
</blockquote>

## `s2-cli`

<blockquote>

## [0.42.11] - 2026-09-11

### Features

- `s2-stream-config` header for auto-created streams
([#718](https://github.com/s2-streamstore/s2/issues/718))

<!-- generated by git-cliff -->
</blockquote>

## `s2-testcontainers`

<blockquote>

## [0.42.11] - 2026-09-11

<!-- generated by git-cliff -->
</blockquote>


</p></details>

---
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>
2026-09-11 21:12:01 +05:30
Mehul Arora
a4f247aeed
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<StreamConfig> 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 <cursoragent@cursor.com>
2026-09-11 21:01:21 +05:30
release-pleaze[bot]
4aebddaba0 Bump s2-lite-helm chart to appVersion 0.42.10 2026-09-11 00:56:03 +00:00
release-pleaze[bot]
24b3e78620
chore: release (#732)
## 🤖 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

<details><summary><i><b>Changelog</b></i></summary><p>

## `s2-lite`

<blockquote>

## [0.42.10] - 2026-09-11

### Miscellaneous Tasks

- Update Cargo.lock dependencies

<!-- generated by git-cliff -->
</blockquote>

## `s2-sdk`

<blockquote>

## [0.34.6] - 2026-09-11

### Features

- Set default request headers (`_hidden` only)
([#731](https://github.com/s2-streamstore/s2/issues/731))

<!-- generated by git-cliff -->
</blockquote>

## `s2-cli`

<blockquote>

## [0.42.10] - 2026-09-11

<!-- generated by git-cliff -->
</blockquote>

## `s2-testcontainers`

<blockquote>

## [0.42.10] - 2026-09-11

<!-- generated by git-cliff -->
</blockquote>


</p></details>

---
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>
2026-09-10 17:17:29 -07:00
Stephen Balogh
7eb44972ed
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`.
2026-09-10 16:58:19 -07:00
release-pleaze[bot]
198640ea04 Bump s2-lite-helm chart to appVersion 0.42.9 2026-09-10 01:12:01 +00:00
release-pleaze[bot]
3f23989396
chore: release (#725)
## 🤖 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

<details><summary><i><b>Changelog</b></i></summary><p>

## `s2-common`

<blockquote>

## [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))

<!-- generated by git-cliff -->
</blockquote>

## `s2-api`

<blockquote>

## [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))

<!-- generated by git-cliff -->
</blockquote>

## `s2-lite`

<blockquote>

## [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))

<!-- generated by git-cliff -->
</blockquote>

## `s2-sdk`

<blockquote>

## [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))

<!-- generated by git-cliff -->
</blockquote>

## `s2-cli`

<blockquote>

## [0.42.9] - 2026-09-10

<!-- generated by git-cliff -->
</blockquote>

## `s2-testcontainers`

<blockquote>

## [0.42.9] - 2026-09-10

<!-- generated by git-cliff -->
</blockquote>


</p></details>

---
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>
2026-09-09 17:36:34 -07:00
devin-ai-integration[bot]
b3784c31e7
fix(api): switch JSON extractor from sonic-rs back to serde_json (#729)
## Summary

Reverts the `s2_api::data::Json` / `JsonOpt` axum extractor from
`sonic_rs::from_slice` (#383) back to `serde_json::from_slice`, and
drops the `sonic-rs` dependency.

**Why:** sonic-rs has no recursion bound on its *skip* path. When the
deserializer meets a value it doesn't want — an unknown field, or a
wrong-typed value such as `"body": [[[[…` where a `String` is expected —
it walks to the end of that value via `Parser::skip_one(true)` →
`skip_array`/`skip_object` → `skip_one` …, recursing once per nesting
level with no counter. (Its `MAX_ALLOWED_DEPTH = 255` guard from
cloudwego/sonic-rs#213 only covers the visitor path.) A request body
nested ~20k+ levels deep therefore overflows the tokio worker stack and
aborts the whole frontend process:

```
thread 'tokio-rt-worker' has overflowed its stack
fatal runtime error: stack overflow, aborting
```

Reported upstream as cloudwego/sonic-rs#232 (open, reproduces on
0.5.9/main).

serde_json is safe on every such path: type mismatches error out before
descending (`invalid type: sequence, expected a string` → 422),
`deserialize_ignored_any` skips iteratively with a heap stack, and
values that are actually deserialized hit the default 128-level
recursion limit (`recursion limit exceeded` → 400).

**Error classification** keeps the same 400/422/500 split:

```rust
serde_json::error::Category::Data          => DataError   (422)
serde_json::error::Category::Io            => Other       (500)
serde_json::error::Category::Syntax | Eof  => SyntaxError (400)
```

**Performance:** measured on `AppendInput` in release builds (x86-64,
sonic-rs built with `-C target-cpu=native` so it gets AVX2), serde_json
was on par or faster than sonic-rs for realistic append bodies — 371 vs
459 µs for 1000×100 B records with headers, 1.66 vs 2.09 ms for 1000×1
KiB records with escapes, 132 vs 105 µs for a single 1 MiB string.

**Tests:** `deeply_nested_json_does_not_overflow_stack` parses 50k-deep
nesting in `body`, in an unknown field, and at top level on a 2
MiB-stack thread; the old `serde_json_sonic_rs_roundtrip` differential
test is kept as a plain `serde_json_roundtrip`.

Companion change for s2-cloud: s2-streamstore/s2-cloud#1777 (will be
reduced to bumping `s2-api` and switching the OTLP extractor once this
is released).


Link to Devin session:
https://app.devin.ai/sessions/7c3250684bc84290abeeb13826313c4b
Open in Devin Desktop:
https://app.devin.ai/desktop/session/7c3250684bc84290abeeb13826313c4b?variant=devin
Requested by: @sgbalogh

---------

Co-authored-by: Stephen Balogh <stephen@s2.dev>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-09 17:15:23 -07:00
devin-ai-integration[bot]
741b0995fc
fix(common): reject NUL bytes in stream names and access token IDs (#728)
## Summary

Black-box fuzzing of an S2 sandbox found that a NUL byte (0x00) in a
stream name — or in the list `prefix` / `start_after` query params —
surfaces as a generic `500 {"code":"other","message":"Internal Server
Error"}` instead of a structured 400:

```
POST /v1/streams              {"stream":"a\u0000b"}   -> 500 other
GET  /v1/streams?prefix=a%00b                        -> 500 other
GET  /v1/streams?start_after=a%00b                   -> 500 other
```

Every other control char (`\t`, `\n`, `\r`, `\x01`), arbitrary Unicode,
emoji, `/`, `?`, `#`, `%20`, etc. is accepted and round-trips fine; only
NUL breaks.

**Root cause:** `StreamNameStr::<T>::validate_str` in
`common/src/stream.rs` only checks non-empty, not `.`/`..`, and `len <=
MAX_STREAM_NAME_LEN`. Nothing rejects an interior NUL, so it passes
validation and reaches the metastore, where Postgres/DSQL rejects NUL in
a `TEXT` column and the error isn't mapped to a client error. Because
`NameProps`, `PrefixProps`, and `StartAfterProps` all share
`validate_str`, the name, `prefix`, and `start_after` paths are all
affected.

**Fix** (minimal; no other character is newly rejected):

```rust
// StreamNameStr::<T>::validate_str
if name.contains('\0') {
    return Err(format!("stream {} must not contain NUL bytes", T::FIELD_NAME).into());
}
```

Applied identically to `AccessTokenIdStr::validate_str`
(`common/src/access.rs`), which mirrors the stream validator
rule-for-rule and had the same gap. `BasinNameStr` and `LocationName`
already enforce strict ASCII charsets that exclude NUL — no code change,
just added `nul` regression cases to lock it in.

Rejecting NUL narrows the documented contract ("between 1 and 512
bytes"), so the doc comments that feed the OpenAPI spec
(`CreateStreamRequest::stream`,
`ListStreamsRequest::{prefix,start_after}`,
`IssueAccessTokenRequest::id`,
`ListAccessTokensRequest::{prefix,start_after}`) and the SDK rustdoc on
the re-exported name/prefix/start-after types now say "must not contain
NUL bytes". The `api/specs` submodule is synced separately by the specs
workflow.

Since the fix is in the shared `s2-common` crate it covers both s2-cloud
and s2-lite. Verified against a locally built lite, which now returns
structured 400s:

```
create stream a\0b     -> 400 bad_json  "stream name must not contain NUL bytes ..."
?prefix=a%00b          -> 400 bad_query "prefix: stream prefix must not contain NUL bytes"
?start_after=a%00b     -> 400 bad_query "start_after: stream start-after must not contain NUL bytes"
create "a\tb/名前 😀?#"  -> 201 (unchanged)
```

**s2-lite behaviour before this fix:** it did *not* 500. Running `main`
lite, all three requests succeeded (`201`/`200`) and the NUL name
round-tripped through list. SlateDB keys are raw bytes, and although
lite uses `\0` as the basin/stream separator in the `StreamMeta` key and
`StreamIdMapping` value, `deser_key` splits on the *first* `\0` and
basin names can never contain NUL, so the encoding stayed unambiguous.
The 500 is specific to s2-cloud's SQL-backed metastore; lite just
becomes consistent with cloud in rejecting NUL up-front.

**Tests:**
- Unit: `rstest` `nul` cases added to `validate_name_err` /
`validate_prefix_err` / `validate_start_after_err` (stream, basin,
access token) and `LocationName::validate_name_err`, plus
`control_chars` / `unicode` `validate_name_ok` cases guarding the
"nothing else newly rejected" intent.
- HTTP: new `handlers::v1::streams::test` module in lite (in-process
`Router::oneshot`, same pattern as the records handler tests) asserting
NUL in the create body → 400 `bad_json`, NUL in `prefix`/`start_after` →
400 `bad_query`, and control-char/Unicode names → 201. These fail if the
`validate_str` guard is removed.

`just clippy` and `just test` pass.

No version bumps; release is left to the normal release flow.

Link to Devin session:
https://app.devin.ai/sessions/503132b92ae34635b700d43f406c0ccd
Open in Devin Desktop:
https://app.devin.ai/desktop/session/503132b92ae34635b700d43f406c0ccd?variant=devin
Requested by: @sgbalogh

---------

Co-authored-by: Stephen Balogh <stephen@s2.dev>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-09 17:13:37 -07:00
chewbaucke
10f011b625
fix(lite): close SlateDB on graceful shutdown
Lite previously returned after draining HTTP requests without closing SlateDB, leaving memtables to be recovered from the WAL on the next startup.

Call `Backend::close()` after HTTP shutdown, delegating to SlateDB 0.15's `Db::close()` to flush memtables to L0 and shut down the database. Log the elapsed close time and propagate close errors. This reduces WAL replay on restart; Lite's existing `manifest_poll_interval` startup wait remains.

Shutdown awaits the database close without an application timeout. Deployments should allow enough termination grace for the final flush; an interrupted close may still leave WAL data to replay on the next startup.

Validation:

- `just fmt` and `just test` (744 tests passed).
- Local filesystem SIGTERM/reopen checks over HTTP and self-signed HTTPS preserved all 512 acknowledged records per scenario and accepted subsequent appends.
- The contributor reported restart-to-ready times of 2–6 seconds on Fly.io/Tigris, down from 106 seconds when quiet and 251–332 seconds after a burst, with a 1–2 second close and all 352 acknowledged records plus the seed record present after reopening. These remote-store timings were not independently reproduced during review.
2026-09-07 10:32:37 -07:00
release-pleaze[bot]
3553cd9eea Bump s2-lite-helm chart to appVersion 0.42.8 2026-09-03 02:43:11 +00:00
release-pleaze[bot]
cabbd79ef9
chore: release (#711)
## 🤖 New release

* `s2-lite`: 0.42.7 -> 0.42.8 (✓ API compatible changes)
* `s2-sdk`: 0.34.3 -> 0.34.4 (✓ API compatible changes)
* `s2-cli`: 0.42.7 -> 0.42.8
* `s2-testcontainers`: 0.42.7 -> 0.42.8

<details><summary><i><b>Changelog</b></i></summary><p>

## `s2-lite`

<blockquote>

## [0.42.8] - 2026-09-01

### Miscellaneous Tasks

- Update Cargo.lock dependencies

<!-- generated by git-cliff -->
</blockquote>

## `s2-sdk`

<blockquote>

## [0.34.4] - 2026-09-01

### Features

- Act on s2s reconnect advice in append and read sessions
([#703](https://github.com/s2-streamstore/s2/issues/703))

### Bug Fixes

- Require http2
([#710](https://github.com/s2-streamstore/s2/issues/710))
- Classify h2 REFUSED_STREAM as retryable
([#716](https://github.com/s2-streamstore/s2/issues/716))

<!-- generated by git-cliff -->
</blockquote>

## `s2-cli`

<blockquote>

## [0.42.8] - 2026-09-01

### Bug Fixes

- Require http2
([#710](https://github.com/s2-streamstore/s2/issues/710))

<!-- generated by git-cliff -->
</blockquote>

## `s2-testcontainers`

<blockquote>

## [0.42.8] - 2026-09-01

<!-- generated by git-cliff -->
</blockquote>


</p></details>

---
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>
2026-09-03 07:36:07 +05:30
Mehul Arora
6bcc6a300d
feat(sdk): act on s2s reconnect advice in append and read sessions (#703) 2026-09-02 02:11:31 +05:30
detail-app[bot]
7689acd48d
fix(sdk): classify h2 REFUSED_STREAM as retryable (#716)
**Detail bug report:** [View on
Detail](https://app.detail.dev/org_89d327b3-b883-4365-b6a3-46b6701342a9/bugs/bug_1ccfeb5d-95f9-46a0-badf-3561947108b4)

Closes #715

## Bug

HTTP/2 `RST_STREAM` frames with `REFUSED_STREAM` were classified as
non-retryable `ClientError::Other` instead of retryable
`ClientError::ConnectionClosedEarly`. The h2 classification in
`classify_hyper_source` only handled `is_io() || is_go_away()`, missing
`RST_STREAM` resets entirely. Since RFC 9113 §8.7 guarantees a
`REFUSED_STREAM` is sent before any processing occurred (so the request,
including non-idempotent methods, can be safely retried), the client was
failing instead of retrying.

## Fix

Extracted the nested `h2::Error` handling in `sdk/src/error.rs` into a
`classify_h2_error` helper and added a branch that classifies any h2
error with `reason() == Some(h2::Reason::REFUSED_STREAM)` as
`ConnectionClosedEarly` (retryable). I/O and GOAWAY h2 errors keep their
existing classification; all other reasons (and unknown/future codes)
still fall through to non-retryable `Other`.

The check is **reason-based, not `is_reset()`-based**. h2 surfaces a
received `RST_STREAM(REFUSED_STREAM)` in two shapes: a `Reset`-kind
error while reading the response body (`is_reset()==true`), and a
`Reason`-kind error while sending the request body — hyper wraps the
reason from `SendStream::poll_reset` via `h2::Error::from(reason)`,
which has `is_reset()==false`. An `is_reset()`-only check would miss the
request-body path; keying on `reason()` covers both. Other `RST_STREAM`
reasons (`INTERNAL_ERROR`, `CANCEL`, `FLOW_CONTROL_ERROR`,
`STREAM_CLOSED`, …) may indicate partial processing and are
intentionally left non-retryable.

## Testing

- **Unit tests** (`sdk/src/error.rs`): 4 new tests covering
`classify_h2_error` — REFUSED_STREAM classifies as retryable
`ConnectionClosedEarly`; it does so even when `!is_io() && !is_go_away()
&& !is_reset()` (the request-body path); all other named reasons and
unknown codes stay non-retryable; and the `ConnectionClosedEarly` vs
`Other` retryability contract holds. The full SDK unit suite passes
(110/110).
- **End-to-end test** (`sdk/tests/refused_stream_retry.rs`): a
self-contained integration test that spins up an h2 prior-knowledge
server which `RST_STREAM(REFUSED_STREAM)`s the first request and serves
a valid `list_basins` response on the retry. The SDK retries and
succeeds, and the server is observed to receive ≥2 requests. This
exercises the full `HttpError → classify_hyper_source →
classify_h2_error → ClientError → retry loop` path that can't be
unit-tested (hyper/h2 errors have no public constructors). Reverting the
fix makes this test fail with the exact bug symptom `Client(Other("send
error: client error (SendRequest)"))`, confirming it guards against
regression.
- **Live server integration**: ran `stream_ops` + `basin_ops` +
`account_ops` (non-token) against a local `s2 lite` server (HTTP/2
prior-knowledge over cleartext) — all pass, including the
producer/append-session happy-path tests. `metrics_ops` and the
`account_ops` access-token management tests return `501 not_implemented`
from `s2 lite` (a known limitation the CI `sdk-tests.yml` skips via
`--skip access_token --skip metrics`); these run against a full S2
server in CI and are unrelated to this change.
- **Could not verify**: the bug report's Evidence 3 claims oversized
request headers trigger `REFUSED_STREAM`. In h2 0.4.16, oversized
headers actually yield an HTTP `431` response
(`proto/streams/recv.rs:207-234`), not a per-stream `REFUSED_STREAM`
(the cited `recv.rs:1041` is the `max_concurrent_streams` `refused`
path). The fix is reason-based and trigger-agnostic, so any genuine
`REFUSED_STREAM` source is covered identically by the unit and
end-to-end tests.
- Routine checks (typecheck, clippy with `-D warnings` across all
targets, `cargo +nightly fmt --all --check`, and `cargo doc` with `-D
warnings`) all pass.

---
_Automatic Fixes PRs can be [configured
here](https://app.detail.dev/org_89d327b3-b883-4365-b6a3-46b6701342a9/settings/repos/repo_c4bd6a47-9b7d-4b62-9c18-8cf0ac18a8f9/bugs)._

---------

Co-authored-by: detail-app[bot] <180357370+detail-app[bot]@users.noreply.github.com>
Co-authored-by: Mehul Arora <mehul@s2.dev>
2026-08-31 21:20:11 +05:30
Mehul Arora
91e1cf3fa3
fix(sdk): require http2 (#710) 2026-08-25 22:56:07 +05:30
Shikhar Bhushan
a5ac6ac5a0
docs: refine descriptions
Signed-off-by: Shikhar Bhushan <shikhar@s2.dev>
2026-08-25 07:09:14 -07:00
Cristian C
97411e9004
ci: add Rust dependency cooldown gate (#713)
Adds a seven-day publication cooldown for newly locked crates.io
versions. Runs it as a reusable, credential-free PR gate and documents
the dependency update workflow.


---------

Co-authored-by: Codex GPT-5.6 Sol <noreply@openai.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 21:23:18 +03:00
121 changed files with 9774 additions and 3948 deletions

1440
Cargo.lock generated

File diff suppressed because it is too large Load diff

Some files were not shown because too many files have changed in this diff Show more