append and read. S2 stores the stream’s algorithm in metadata, but not the key material itself.
Determined at stream creation
A stream captures its
cipher when it is created, through its basin’s configuration.Key supplied on data plane requests
Encrypted streams require the
s2-encryption-key header on REST requests, or pass it using the corresponding CLI flags.Key custody stays with you
S2 does not persist or log the encryption key.
How it works
New streams inherit thestream_cipher configured on the basin at the moment they are created. Reconfiguring the basin later only affects streams created after that change.
Supported ciphers
| Cipher | API / CLI value | Key length |
|---|---|---|
| AEGIS-256 (recommended) | aegis-256 | 32 bytes |
| AES-256-GCM (NIST-approved) | aes-256-gcm | 32 bytes |
s2-encryption-key header or to the CLI. SDKs allow providing either a string or bytes.
A stream configured with
aes-256-gcm can have up to 2^32 records (~4.3 billion), and subsequent appends will be rejected. Otherwise, a stream can have up to 2^64 records (effectively unlimited) – so aegis-256 is generally preferable.Where keys are required
| Operation | Key required? | Notes |
|---|---|---|
| Create or reconfigure basin | No | Set stream_cipher here for future streams. |
| Create stream | No | The stream inherits the basin’s current stream_cipher. |
| Append | Yes, if the stream has a cipher | Provide the key on every request or session. |
| Read | Yes, if the stream has a cipher | Use the same key that encrypted the records you want to read. |
| Check tail | No | Tail only returns positions, not payloads. |
| List streams | No | Each stream’s cipher is returned in metadata. |
Generate a key
Store the resulting value as a secret to be used for one or more streams. Maintain a mapping of which key was used for which stream, so that it may be specified consistently for appends and reads.
Configure encryption for new streams
- CLI
- REST
- Rust SDK
Stream create endpoints do not take a per-stream encryption field. A stream inherits the basin default when it is created, and that
cipher is immutable for the lifetime of the stream.Append and read with the key
- CLI
- REST
- Rust SDK
The same keying pattern applies to unary operations, append/read sessions, SSE reads, and S2S sessions. The encryption key is just an HTTP header on the wire.
Inspect cipher config
- Get basin config to see the basin’s current
stream_cipherfor new streams. - List streams to inspect current streams’
cipher.
Key rotation and errors
If you need to rotate the key, create a new stream and cut writers and readers over deliberately. Similarly, to change the cipher used, update the basin configuration and then create a new stream.| Situation | Result |
|---|---|
| Missing key on an encrypted stream | 422 invalid |
| Key is not valid base64 or not 32 bytes | 422 invalid |
| Wrong key | 400 decryption_failed |
Encrypted record payloads are authenticated to the stream they were written to.

