novadocs
Concepts

Concepts

Trimming

Trim advances a monotonic watermark — sequence numbers survive, and reads below it fail at once

Trim is the explicit way history ends: a per-stream watermark that only advances. Everything below it is gone the moment it moves; the auditor reclaims the bytes later. The two speeds are on the retention page, which also covers the age-based way the watermark moves for you.

Trim advances a watermark

Trim names a point (a sequence number, or a time resolved to the first record stamped at or after it) and advances the stream's trim watermark to it. Records below the watermark are logically deleted.

The watermark is monotonic: it only ever moves forward. The point applied is max(current, min(requested, tail)), so a trim behind the current watermark is a no-op and a trim past the tail trims everything retained so far. Trimming is instant and metadata-only; the stream's leader serves it when one holds the stream (a non-holder redirects, and the SDK follows), and it goes straight to committed state otherwise.

Trim in Go
err := c.Trim(ctx, addr, 1000)          // drop records 0…999
err = c.TrimFrom(ctx, addr, cutoffMs)   // drop everything stamped before cutoffMs
Trim from the shell and over HTTP
nova stream trim nova://demo/events --entry 1000
nova stream trim nova://demo/events --at 2026-08-01T00:00:00Z
curl -s -X POST localhost:8080/v1/streams/demo/events/trim -d '{"seq_num":1000}'
curl -s -X POST localhost:8080/v1/streams/demo/events/trim -d '{"timestamp_ms":1722470400000}'

Trim is its own action in scoped tokens, never bundled with append or read: it is irreversible data loss, and a credential must be granted it explicitly.

Sequence numbers survive a trim

Trimming never renumbers. After trimming below 1000, record 1000 is still record 1000, the tail is unchanged, and CheckTail still reports the same next sequence number. A sequence number is a permanent name; a trim only changes which names are readable.

CheckTail reports timestamp_ms: 0 when the whole history has been trimmed away, because no committed record's time is known any more.

Reads below the watermark fail

A read or subscription that starts below the watermark fails as OutOfRange (client.ErrTrimmed in Go, 410 Gone over HTTP), distinct from the 400 of a malformed request so a client can tell the two apart. A subscription that is trimmed under mid-life ends with the same error.

To read from "the earliest still retained" instead of erroring, pass clamp: the start is pulled up to the watermark.

r, err := c.Read(ctx, addr, 0, 100, client.WithClamp())

nova read does this by default when you give no --from.

Source documents

  • docs/adr/0002-trim-watermark-retention.md
  • proto/nova/v1/dataplane.proto
  • client/client.go

On this page