Durable Streams API
734
stars
559
commits
Rust
primary language
Sep 11, 2026
updated
s2.dev is a serverless datastore for real-time, streaming data.
This repository contains:
Use the nightly Cargo dependency commands so that the repository publication cooldown applies:
cargo +nightly add <crate>
cargo +nightly update
cargo +nightly update -p <crate>
cargo +nightly remove <crate>
cargo +nightly generate-lockfile
Use --locked with normal build, check, test, run, document, fetch, and metadata commands. The simulator is temporarily exempt until its separate lockfile is regenerated. The pull request dependency check verifies every proposed lock-file change before Rust build jobs start.
Install the repository Cargo tools from Homebrew bottles:
brew install cargo-deny cargo-nextest
brew install s2-streamstore/s2/s2
cargo install --locked s2-cli
curl -fsSL https://raw.githubusercontent.com/s2-streamstore/s2/main/install.sh | bash
Or specify a version with VERSION=x.y.z before the command. See all releases.
docker pull ghcr.io/s2-streamstore/s2
Store an access token in the OS credential store:
s2 auth access-token set
Or pipe it in from a script or a secret manager:
op read 'op://S2/CLI/access-token' | s2 auth access-token set --stdin
For CI and other ephemeral environments, set S2_ACCESS_TOKEN in the environment. On a headless host with no credential store, opt into a private plaintext file instead (mode 0600 on Unix):
printf '%s' "$S2_ACCESS_TOKEN" |
s2 auth access-token set --stdin --insecure-storage
unset S2_ACCESS_TOKEN
Plaintext access_token values in config.toml are deprecated.
Migrate one with s2 auth access-token migrate, or re-store it with --insecure-storage on a
headless host that has no credential store.
s2-lite is embedded as the s2 lite subcommand of the CLI. It's a self-hostable server implementation of the S2 API.
It uses SlateDB as its storage engine, which relies entirely on object storage for durability.
It is easy to run s2 lite against object stores like AWS S3 and Tigris. It is a single-node binary with no other external dependencies.
You can also simply not specify a --bucket, which makes it operate entirely in-memory (or use --local-root to persist to local disk instead).
[!TIP] When you point lite at a
--bucket, data is always durable on object storage before being acknowledged or returned to readers — just like s2.dev.The optional in-memory mode (no
--bucketor--local-rootspecified) just makes it an effective S2 emulator for integration tests.
Here's how you can run in-memory without any external dependency:
# Using Docker
docker run -p 8080:80 ghcr.io/s2-streamstore/s2 lite
# Or directly with the CLI
s2 lite --port 8080
docker run -p 8080:80 \
-e AWS_PROFILE=${AWS_PROFILE} \
-v ~/.aws:/home/nonroot/.aws:ro \
ghcr.io/s2-streamstore/s2 lite \
--bucket ${S3_BUCKET} \
--path s2lite
docker run -p 8080:80 \
-e AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} \
-e AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} \
-e AWS_ENDPOINT_URL_S3=${AWS_ENDPOINT_URL_S3} \
ghcr.io/s2-streamstore/s2 lite \
--bucket ${S3_BUCKET} \
--path s2lite
[!NOTE] Point the S2 CLI or SDKs at your lite instance like this:
export S2_ACCOUNT_ENDPOINT="http://localhost:8080" export S2_BASIN_ENDPOINT="http://localhost:8080" export S2_ACCESS_TOKEN="ignored"
Let's make sure the server is ready:
while ! curl -sf ${S2_ACCOUNT_ENDPOINT}/health -o /dev/null; do echo Waiting...; sleep 2; done && echo Up!
Install the CLI (see Installation above) or upgrade if s2 --version is older than 0.26
Let's create a basin with auto-creation of streams enabled:
s2 create-basin liteness --create-stream-on-append --create-stream-on-read
Test your performance:
s2 bench liteness --target-mibps 10 --duration 5s --catchup-delay 0s

Now let's try streaming sessions. In one or more new terminals (make sure you re-export the env vars noted above),
s2 read s2://liteness/starwars 2> /dev/null
Now back from your original terminal, let's write to the stream:
nc starwars.s2.dev 23 | s2 append s2://liteness/starwars

Deploy s2-lite to Kubernetes using Helm. See the Helm chart documentation for installation instructions and configuration options.
/health will return 200 on success for readiness and liveness checks
/metrics returns Prometheus text format
Use SL8_ prefixed environment variables, e.g.:
# Defaults to 50ms for remote bucket / 5ms in-memory
SL8_FLUSH_INTERVAL=10ms
streamer that owns the current tail position, serializes appends, and broadcasts acknowledged records to followerslite::backend::kv::Key documents the data modeling in SlateDBComplete specs are available:
[!IMPORTANT] Unlike the cloud service where the basin is implicit as a subdomain,
/streams/*requests must specify the basin using theS2-Basinheader. The SDKs take care of this automatically.
| Endpoint | Support |
|---|---|
/basins | Supported |
/streams | Supported |
/streams/{stream}/records | Supported |
/access-tokens | Not supported https://github.com/s2-streamstore/s2/issues/28 |
/metrics | Not supported |
Rust
99.3%
Durable Streams API
734
stars
559
commits
Rust
primary language
Sep 11, 2026
updated
s2.dev is a serverless datastore for real-time, streaming data.
This repository contains:
Use the nightly Cargo dependency commands so that the repository publication cooldown applies:
cargo +nightly add <crate>
cargo +nightly update
cargo +nightly update -p <crate>
cargo +nightly remove <crate>
cargo +nightly generate-lockfile
Use --locked with normal build, check, test, run, document, fetch, and metadata commands. The simulator is temporarily exempt until its separate lockfile is regenerated. The pull request dependency check verifies every proposed lock-file change before Rust build jobs start.
Install the repository Cargo tools from Homebrew bottles:
brew install cargo-deny cargo-nextest
brew install s2-streamstore/s2/s2
cargo install --locked s2-cli
curl -fsSL https://raw.githubusercontent.com/s2-streamstore/s2/main/install.sh | bash
Or specify a version with VERSION=x.y.z before the command. See all releases.
docker pull ghcr.io/s2-streamstore/s2
Store an access token in the OS credential store:
s2 auth access-token set
Or pipe it in from a script or a secret manager:
op read 'op://S2/CLI/access-token' | s2 auth access-token set --stdin
For CI and other ephemeral environments, set S2_ACCESS_TOKEN in the environment. On a headless host with no credential store, opt into a private plaintext file instead (mode 0600 on Unix):
printf '%s' "$S2_ACCESS_TOKEN" |
s2 auth access-token set --stdin --insecure-storage
unset S2_ACCESS_TOKEN
Plaintext access_token values in config.toml are deprecated.
Migrate one with s2 auth access-token migrate, or re-store it with --insecure-storage on a
headless host that has no credential store.
s2-lite is embedded as the s2 lite subcommand of the CLI. It's a self-hostable server implementation of the S2 API.
It uses SlateDB as its storage engine, which relies entirely on object storage for durability.
It is easy to run s2 lite against object stores like AWS S3 and Tigris. It is a single-node binary with no other external dependencies.
You can also simply not specify a --bucket, which makes it operate entirely in-memory (or use --local-root to persist to local disk instead).
[!TIP] When you point lite at a
--bucket, data is always durable on object storage before being acknowledged or returned to readers — just like s2.dev.The optional in-memory mode (no
--bucketor--local-rootspecified) just makes it an effective S2 emulator for integration tests.
Here's how you can run in-memory without any external dependency:
# Using Docker
docker run -p 8080:80 ghcr.io/s2-streamstore/s2 lite
# Or directly with the CLI
s2 lite --port 8080
docker run -p 8080:80 \
-e AWS_PROFILE=${AWS_PROFILE} \
-v ~/.aws:/home/nonroot/.aws:ro \
ghcr.io/s2-streamstore/s2 lite \
--bucket ${S3_BUCKET} \
--path s2lite
docker run -p 8080:80 \
-e AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} \
-e AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} \
-e AWS_ENDPOINT_URL_S3=${AWS_ENDPOINT_URL_S3} \
ghcr.io/s2-streamstore/s2 lite \
--bucket ${S3_BUCKET} \
--path s2lite
[!NOTE] Point the S2 CLI or SDKs at your lite instance like this:
export S2_ACCOUNT_ENDPOINT="http://localhost:8080" export S2_BASIN_ENDPOINT="http://localhost:8080" export S2_ACCESS_TOKEN="ignored"
Let's make sure the server is ready:
while ! curl -sf ${S2_ACCOUNT_ENDPOINT}/health -o /dev/null; do echo Waiting...; sleep 2; done && echo Up!
Install the CLI (see Installation above) or upgrade if s2 --version is older than 0.26
Let's create a basin with auto-creation of streams enabled:
s2 create-basin liteness --create-stream-on-append --create-stream-on-read
Test your performance:
s2 bench liteness --target-mibps 10 --duration 5s --catchup-delay 0s

Now let's try streaming sessions. In one or more new terminals (make sure you re-export the env vars noted above),
s2 read s2://liteness/starwars 2> /dev/null
Now back from your original terminal, let's write to the stream:
nc starwars.s2.dev 23 | s2 append s2://liteness/starwars

Deploy s2-lite to Kubernetes using Helm. See the Helm chart documentation for installation instructions and configuration options.
/health will return 200 on success for readiness and liveness checks
/metrics returns Prometheus text format
Use SL8_ prefixed environment variables, e.g.:
# Defaults to 50ms for remote bucket / 5ms in-memory
SL8_FLUSH_INTERVAL=10ms
streamer that owns the current tail position, serializes appends, and broadcasts acknowledged records to followerslite::backend::kv::Key documents the data modeling in SlateDBComplete specs are available:
[!IMPORTANT] Unlike the cloud service where the basin is implicit as a subdomain,
/streams/*requests must specify the basin using theS2-Basinheader. The SDKs take care of this automatically.
| Endpoint | Support |
|---|---|
/basins | Supported |
/streams | Supported |
/streams/{stream}/records | Supported |
/access-tokens | Not supported https://github.com/s2-streamstore/s2/issues/28 |
/metrics | Not supported |
Rust
99.3%