Stream Model
A stream is an append-only sequence of bytes with durability guarantees. This page describes the stream model in detail.
Properties
Every stream has the following properties:
| Property | Type | Description |
|---|---|---|
| URL | string | Unique identifier for the stream |
| Content-Type | string | MIME type of stream data |
| Current Offset | string | Position after last byte (tail) |
| Created At | timestamp | When the stream was created |
| TTL | seconds | Optional time-to-live |
| Expires At | timestamp | Optional absolute expiry |
Durability Guarantees
Once a write is acknowledged (server returns success), the data is durable:
┌────────┐ ┌────────┐ ┌─────────┐│ Client │ │ Server │ │ Storage │└───┬────┘ └───┬────┘ └────┬────┘ │ │ │ │ POST (data) │ │ │──────────────────▶│ │ │ │ write + fsync │ │ │───────────────────▶│ │ │ │ │ │ ack │ │ │◀───────────────────│ │ │ │ │ 204 No Content │ │ │◀──────────────────│ │ │ │ │ ▼ ▼ ▼ Data is now durable - survives server restartDurability Semantics
- Write Acknowledgment: Server MUST only return success after data is durable
- Read Consistency: Any data returned by a read MUST be durable
- Ordering: Writes are strictly ordered by offset
- Atomicity: Each append is atomic - either fully written or not at all
Immutability
Streams are immutable by position:
Offset: 0 10 20 30 40 50 │ │ │ │ │ │Stream: ───┴────┴────┴────┴────┴────┴───▶ ▲ ▲ ▲ ▲ │ │ │ │ Message boundaries (never change)- Data at a given offset MUST NOT change after being written
- New data can only be appended to the end
- The only mutation allowed is deletion of the entire stream
Stream Lifecycle
Creation
Streams are created with a PUT request:
PUT /streams/my-stream HTTP/1.1Host: api.example.comContent-Type: application/jsonStream-TTL: 86400
{"initial": "data"}HTTP/1.1 201 CreatedLocation: /streams/my-streamContent-Type: application/jsonStream-Next-Offset: 0000000000000017Idempotent Creation
Creating an existing stream with matching configuration returns success:
PUT /streams/my-stream HTTP/1.1Content-Type: application/jsonHTTP/1.1 200 OKContent-Type: application/jsonStream-Next-Offset: 0000000000000042Creating with different configuration returns conflict:
PUT /streams/my-stream HTTP/1.1Content-Type: text/plainHTTP/1.1 409 ConflictActive State
Active streams accept reads and writes:
POST /streams/my-stream HTTP/1.1Content-Type: application/json
{"event": "update"}HTTP/1.1 204 No ContentStream-Next-Offset: 0000000000000059Deletion
Streams are deleted with a DELETE request:
DELETE /streams/my-stream HTTP/1.1HTTP/1.1 204 No ContentAfter deletion:
- Reads return
404 Not Found - The URL SHOULD NOT be reused for new streams
Expiration
Streams with TTL or expiry are automatically deleted:
PUT /streams/temporary HTTP/1.1Stream-TTL: 3600After 3600 seconds:
- Stream is automatically deleted
- Reads return
404 Not Found
Retention
Servers MAY implement retention policies that drop data older than a certain age while keeping the stream active:
Full Stream:├────────────────────────────────────────────┤│ Old data │ Retained data │├───────────────┼────────────────────────────┤ Dropped Still readable
Reading dropped offsets returns:HTTP/1.1 410 GoneRetention differs from expiration:
- Retention: Drops old data, stream continues
- Expiration: Deletes entire stream
Metadata
Stream metadata is available via HEAD request:
HEAD /streams/my-stream HTTP/1.1HTTP/1.1 200 OKContent-Type: application/jsonStream-Next-Offset: 0000000000000059Stream-TTL: 3542Cache-Control: no-storeAvailable Metadata
| Header | Description |
|---|---|
Content-Type | Stream’s content type |
Stream-Next-Offset | Current tail offset |
Stream-TTL | Remaining seconds until expiry |
Stream-Expires-At | Absolute expiry time (RFC 3339) |
Independent Read/Write Implementation
Servers MAY implement read and write paths independently:
| Implementation | Reads | Writes | Example Use Case |
|---|---|---|---|
| Full | Yes | Yes | General-purpose streaming |
| Read-only | Yes | No | Database change feeds |
| Write-only | No | Yes | Log aggregation |
For read-only streams, POST returns:
HTTP/1.1 405 Method Not AllowedFor write-only streams, GET returns:
HTTP/1.1 405 Method Not AllowedNext Steps
- Reading Operations - How to read from streams
- Writing Operations - How to create and append
- Offsets - Understanding offset tokens