Repository: benbjohnson/litestream
Pull request #636 (2025-04-21 16:06:09)

Remove generations
This pull request removes the concept of generations from Litestream for the upcoming v0.5.0 release which will use LTX as the backing storage format. Previously, generations were a way to represent an unbroken chain of a snapshot and subsequent WAL segments. If Litestream became out of sync with the SQLite database, a new generation could be started.

v0.5.0 will use a single chain of files to represent this history of a database. If Litestream gets out of sync, it will calculate the difference from the previous index and upload the next replication file based on that.


Patch:
diff --git a/abs/replica_client.go b/abs/replica_client.go
index 4d5e00e..6ece18e 100644
--- a/abs/replica_client.go
+++ b/abs/replica_client.go
@@ -7,7 +7,6 @@ import (
 	"net/url"
 	"os"
 	"path"
-	"strings"
 	"sync"
 	"time"
 
@@ -90,53 +89,17 @@ func (c *ReplicaClient) Init(ctx context.Context) (err error) {
 	return nil
 }
 
-// Generations returns a list of available generation names.
-func (c *ReplicaClient) Generations(ctx context.Context) ([]string, error) {
-	if err := c.Init(ctx); err != nil {
-		return nil, err
-	}
-
-	var generations []string
-	var marker azblob.Marker
-	for marker.NotDone() {
-		internal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, "LIST").Inc()
-
-		resp, err := c.containerURL.ListBlobsHierarchySegment(ctx, marker, "/", azblob.ListBlobsSegmentOptions{
-			Prefix: litestream.GenerationsPath(c.Path) + "/",
-		})
-		if err != nil {
-			return nil, err
-		}
-		marker = resp.NextMarker
-
-		for _, prefix := range resp.Segment.BlobPrefixes {
-			name := path.Base(strings.TrimSuffix(prefix.Name, "/"))
-			if !litestream.IsGenerationName(name) {
-				continue
-			}
-			generations = append(generations, name)
-		}
-	}
-
-	return generations, nil
-}
-
-// DeleteGeneration deletes all snapshots & WAL segments within a generation.
-func (c *ReplicaClient) DeleteGeneration(ctx context.Context, generation string) error {
+// DeleteAll deletes all snapshots & WAL segments .
+func (c *ReplicaClient) DeleteAll(ctx context.Context) error {
 	if err := c.Init(ctx); err != nil {
 		return err
 	}
 
-	dir, err := litestream.GenerationPath(c.Path, generation)
-	if err != nil {
-		return fmt.Errorf("cannot determine generation path: %w", err)
-	}
-
 	var marker azblob.Marker
 	for marker.NotDone() {
 		internal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, "LIST").Inc()
 
-		resp, err := c.containerURL.ListBlobsFlatSegment(ctx, marker, azblob.ListBlobsSegmentOptions{Prefix: dir + "/"})
+		resp, err := c.containerURL.ListBlobsFlatSegment(ctx, marker, azblob.ListBlobsSegmentOptions{Prefix: "/"})
 		if err != nil {
 			return err
 		}
@@ -154,26 +117,26 @@ func (c *ReplicaClient) DeleteGeneration(ctx context.Context, generation string)
 		}
 	}
 
-	// log.Printf("%s(%s): retainer: deleting generation: %s", r.db.Path(), r.Name(), generation)
+	// log.Printf("%s(%s): retainer: deleting: %s", r.db.Path(), r.Name())
 
 	return nil
 }
 
-// Snapshots returns an iterator over all available snapshots for a generation.
-func (c *ReplicaClient) Snapshots(ctx context.Context, generation string) (litestream.SnapshotIterator, error) {
+// Snapshots returns an iterator over all available snapshots.
+func (c *ReplicaClient) Snapshots(ctx context.Context) (litestream.SnapshotIterator, error) {
 	if err := c.Init(ctx); err != nil {
 		return nil, err
 	}
-	return newSnapshotIterator(ctx, generation, c), nil
+	return newSnapshotIterator(ctx, c), nil
 }
 
 // WriteSnapshot writes LZ4 compressed data from rd to the object storage.
-func (c *ReplicaClient) WriteSnapshot(ctx context.Context, generation string, index int, rd io.Reader) (info litestream.SnapshotInfo, err error) {
+func (c *ReplicaClient) WriteSnapshot(ctx context.Context, index int, rd io.Reader) (info litestream.SnapshotInfo, err error) {
 	if err := c.Init(ctx); err != nil {
 		return info, err
 	}
 
-	key, err := litestream.SnapshotPath(c.Path, generation, index)
+	key, err := litestream.SnapshotPath(c.Path, index)
 	if err != nil {
 		return info, fmt.Errorf("cannot determine snapshot path: %w", err)
 	}
@@ -192,23 +155,22 @@ func (c *ReplicaClient) WriteSnapshot(ctx context.Context, generation string, in
 	internal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, "PUT").Inc()
 	internal.OperationBytesCounterVec.WithLabelValues(ReplicaClientType, "PUT").Add(float64(rc.N()))
 
-	// log.Printf("%s(%s): snapshot: creating %s/%08x t=%s", r.db.Path(), r.Name(), generation, index, time.Since(startTime).Truncate(time.Millisecond))
+	// log.Printf("%s(%s): snapshot: creating %s/%08x t=%s", r.db.Path(), r.Name(),  index, time.Since(startTime).Truncate(time.Millisecond))
 
 	return litestream.SnapshotInfo{
-		Generation: generation,
-		Index:      index,
-		Size:       rc.N(),
-		CreatedAt:  startTime.UTC(),
+		Index:     index,
+		Size:      rc.N(),
+		CreatedAt: startTime.UTC(),
 	}, nil
 }
 
-// SnapshotReader returns a reader for snapshot data at the given generation/index.
-func (c *ReplicaClient) SnapshotReader(ctx context.Context, generation string, index int) (io.ReadCloser, error) {
+// SnapshotReader returns a reader for snapshot data at the given index.
+func (c *ReplicaClient) SnapshotReader(ctx context.Context, index int) (io.ReadCloser, error) {
 	if err := c.Init(ctx); err != nil {
 		return nil, err
 	}
 
-	key, err := litestream.SnapshotPath(c.Path, generation, index)
+	key, err := litestream.SnapshotPath(c.Path, index)
 	if err != nil {
 		return nil, fmt.Errorf("cannot determine snapshot path: %w", err)
 	}
@@ -227,13 +189,13 @@ func (c *ReplicaClient) SnapshotReader(ctx context.Context, generation string, i
 	return resp.Body(azblob.RetryReaderOptions{}), nil
 }
 
-// DeleteSnapshot deletes a snapshot with the given generation & index.
-func (c *ReplicaClient) DeleteSnapshot(ctx context.Context, generation string, index int) error {
+// DeleteSnapshot deletes a snapshot with the given index.
+func (c *ReplicaClient) DeleteSnapshot(ctx context.Context, index int) error {
 	if err := c.Init(ctx); err != nil {
 		return err
 	}
 
-	key, err := litestream.SnapshotPath(c.Path, generation, index)
+	key, err := litestream.SnapshotPath(c.Path, index)
 	if err != nil {
 		return fmt.Errorf("cannot determine snapshot path: %w", err)
 	}
@@ -249,12 +211,12 @@ func (c *ReplicaClient) DeleteSnapshot(ctx context.Context, generation string, i
 	return nil
 }
 
-// WALSegments returns an iterator over all available WAL files for a generation.
-func (c *ReplicaClient) WALSegments(ctx context.Context, generation string) (litestream.WALSegmentIterator, error) {
+// WALSegments returns an iterator over all available WAL files.
+func (c *ReplicaClient) WALSegments(ctx context.Context) (litestream.WALSegmentIterator, error) {
 	if err := c.Init(ctx); err != nil {
 		return nil, err
 	}
-	return newWALSegmentIterator(ctx, generation, c), nil
+	return newWALSegmentIterator(ctx, c), nil
 }
 
 // WriteWALSegment writes LZ4 compressed data from rd into a file on disk.
@@ -263,7 +225,7 @@ func (c *ReplicaClient) WriteWALSegment(ctx context.Context, pos litestream.Pos,
 		return info, err
 	}
 
-	key, err := litestream.WALSegmentPath(c.Path, pos.Generation, pos.Index, pos.Offset)
+	key, err := litestream.WALSegmentPath(c.Path, pos.Index, pos.Offset)
 	if err != nil {
 		return info, fmt.Errorf("cannot determine wal segment path: %w", err)
 	}
@@ -283,11 +245,10 @@ func (c *ReplicaClient) WriteWALSegment(ctx context.Context, pos litestream.Pos,
 	internal.OperationBytesCounterVec.WithLabelValues(ReplicaClientType, "PUT").Add(float64(rc.N()))
 
 	return litestream.WALSegmentInfo{
-		Generation: pos.Generation,
-		Index:      pos.Index,
-		Offset:     pos.Offset,
-		Size:       rc.N(),
-		CreatedAt:  startTime.UTC(),
+		Index:     pos.Index,
+		Offset:    pos.Offset,
+		Size:      rc.N(),
+		CreatedAt: startTime.UTC(),
 	}, nil
 }
 
@@ -298,7 +259,7 @@ func (c *ReplicaClient) WALSegmentReader(ctx context.Context, pos litestream.Pos
 		return nil, err
 	}
 
-	key, err := litestream.WALSegmentPath(c.Path, pos.Generation, pos.Index, pos.Offset)
+	key, err := litestream.WALSegmentPath(c.Path, pos.Index, pos.Offset)
 	if err != nil {
 		return nil, fmt.Errorf("cannot determine wal segment path: %w", err)
 	}
@@ -324,7 +285,7 @@ func (c *ReplicaClient) DeleteWALSegments(ctx context.Context, a []litestream.Po
 	}
 
 	for _, pos := range a {
-		key, err := litestream.WALSegmentPath(c.Path, pos.Generation, pos.Index, pos.Offset)
+		key, err := litestream.WALSegmentPath(c.Path, pos.Index, pos.Offset)
 		if err != nil {
 			return fmt.Errorf("cannot determine wal segment path: %w", err)
 		}
@@ -343,8 +304,7 @@ func (c *ReplicaClient) DeleteWALSegments(ctx context.Context, a []litestream.Po
 }
 
 type snapshotIterator struct {
-	client     *ReplicaClient
-	generation string
+	client *ReplicaClient
 
 	ch     chan litestream.SnapshotInfo
 	g      errgroup.Group
@@ -355,11 +315,10 @@ type snapshotIterator struct {
 	err  error
 }
 
-func newSnapshotIterator(ctx context.Context, generation string, client *ReplicaClient) *snapshotIterator {
+func newSnapshotIterator(ctx context.Context, client *ReplicaClient) *snapshotIterator {
 	itr := &snapshotIterator{
-		client:     client,
-		generation: generation,
-		ch:         make(chan litestream.SnapshotInfo),
+		client: client,
+		ch:     make(chan litestream.SnapshotInfo),
 	}
 
 	itr.ctx, itr.cancel = context.WithCancel(ctx)
@@ -372,7 +331,7 @@ func newSnapshotIterator(ctx context.Context, generation string, client *Replica
 func (itr *snapshotIterator) fetch() error {
 	defer close(itr.ch)
 
-	dir, err := litestream.SnapshotsPath(itr.client.Path, itr.generation)
+	dir, err := litestream.SnapshotsPath(itr.client.Path)
 	if err != nil {
 		return fmt.Errorf("cannot determine snapshots path: %w", err)
 	}
@@ -395,10 +354,9 @@ func (itr *snapshotIterator) fetch() error {
 			}
 
 			info := litestream.SnapshotInfo{
-				Generation: itr.generation,
-				Index:      index,
-				Size:       *item.Properties.ContentLength,
-				CreatedAt:  item.Properties.CreationTime.UTC(),
+				Index:     index,
+				Size:      *item.Properties.ContentLength,
+				CreatedAt: item.Properties.CreationTime.UTC(),
 			}
 
 			select {
@@ -449,8 +407,7 @@ func (itr *snapshotIterator) Snapshot() litestream.SnapshotInfo {
 }
 
 type walSegmentIterator struct {
-	client     *ReplicaClient
-	generation string
+	client *ReplicaClient
 
 	ch     chan litestream.WALSegmentInfo
 	g      errgroup.Group
@@ -461,11 +418,10 @@ type walSegmentIterator struct {
 	err  error
 }
 
-func newWALSegmentIterator(ctx context.Context, generation string, client *ReplicaClient) *walSegmentIterator {
+func newWALSegmentIterator(ctx context.Context, client *ReplicaClient) *walSegmentIterator {
 	itr := &walSegmentIterator{
-		client:     client,
-		generation: generation,
-		ch:         make(chan litestream.WALSegmentInfo),
+		client: client,
+		ch:     make(chan litestream.WALSegmentInfo),
 	}
 
 	itr.ctx, itr.cancel = context.WithCancel(ctx)
@@ -478,7 +434,7 @@ func newWALSegmentIterator(ctx context.Context, generation string, client *Repli
 func (itr *walSegmentIterator) fetch() error {
 	defer close(itr.ch)
 
-	dir, err := litestream.WALPath(itr.client.Path, itr.generation)
+	dir, err := litestream.WALPath(itr.client.Path)
 	if err != nil {
 		return fmt.Errorf("cannot determine wal path: %w", err)
 	}
@@ -501,11 +457,10 @@ func (itr *walSegmentIterator) fetch() error {
 			}
 
 			info := litestream.WALSegmentInfo{
-				Generation: itr.generation,
-				Index:      index,
-				Offset:     offset,
-				Size:       *item.Properties.ContentLength,
-				CreatedAt:  item.Properties.CreationTime.UTC(),
+				Index:     index,
+				Offset:    offset,
+				Size:      *item.Properties.ContentLength,
+				CreatedAt: item.Properties.CreationTime.UTC(),
 			}
 
 			select {
diff --git a/db.go b/db.go
index debf5a5..4ba518f 100644
--- a/db.go
+++ b/db.go
@@ -5,17 +5,14 @@ import (
 	"context"
 	"database/sql"
 	"encoding/binary"
-	"encoding/hex"
 	"errors"
 	"fmt"
 	"hash/crc64"
 	"io"
 	"log/slog"
 	"math"
-	"math/rand"
 	"os"
 	"path/filepath"
-	"strings"
 	"sync"
 	"time"
 
@@ -35,7 +32,6 @@ const (
 )
 
 // MaxIndex is the maximum possible WAL index.
-// If this index is reached then a new generation will be started.
 const MaxIndex = 0x7FFFFFFF
 
 // DB represents a managed instance of a SQLite database in the file system.
@@ -172,45 +168,35 @@ func (db *DB) SetMetaPath(mp string) {
 	db.metaPath = mp
 }
 
-// GenerationNamePath returns the path of the name of the current generation.
-func (db *DB) GenerationNamePath() string {
-	return filepath.Join(db.metaPath, "generation")
-}
-
-// GenerationPath returns the path of a single generation.
-// Panics if generation is blank.
-func (db *DB) GenerationPath(generation string) string {
-	assert(generation != "", "generation name required")
-	return filepath.Join(db.metaPath, "generations", generation)
-}
+// TODO(gen): Refactor to simply be metaPath. Was GenerationPath().
+// func (db *DB) MetaPath2() string {	return db.metaPath}
 
 // ShadowWALDir returns the path of the shadow wal directory.
-// Panics if generation is blank.
-func (db *DB) ShadowWALDir(generation string) string {
-	return filepath.Join(db.GenerationPath(generation), "wal")
+func (db *DB) ShadowWALDir() string {
+	return filepath.Join(db.metaPath, "wal")
 }
 
 // ShadowWALPath returns the path of a single shadow WAL file.
-// Panics if generation is blank or index is negative.
-func (db *DB) ShadowWALPath(generation string, index int) string {
+// Panics if index is negative.
+func (db *DB) ShadowWALPath(index int) string {
 	assert(index >= 0, "shadow wal index cannot be negative")
-	return filepath.Join(db.ShadowWALDir(generation), FormatWALPath(index))
+	return filepath.Join(db.ShadowWALDir(), FormatWALPath(index))
 }
 
-// CurrentShadowWALPath returns the path to the last shadow WAL in a generation.
-func (db *DB) CurrentShadowWALPath(generation string) (string, error) {
-	index, _, err := db.CurrentShadowWALIndex(generation)
+// CurrentShadowWALPath returns the path to the last shadow WAL.
+func (db *DB) CurrentShadowWALPath() (string, error) {
+	index, _, err := db.CurrentShadowWALIndex()
 	if err != nil {
 		return "", err
 	}
-	return db.ShadowWALPath(generation, index), nil
+	return db.ShadowWALPath(index), nil
 }
 
 // CurrentShadowWALIndex returns the current WAL index & total size.
-func (db *DB) CurrentShadowWALIndex(generation string) (index int, size int64, err error) {
-	des, err := os.ReadDir(filepath.Join(db.GenerationPath(generation), "wal"))
+func (db *DB) CurrentShadowWALIndex() (index int, size int64, err error) {
+	des, err := os.ReadDir(filepath.Join(db.metaPath, "wal"))
 	if os.IsNotExist(err) {
-		return 0, 0, nil // no wal files written for generation
+		return 0, 0, nil // no wal files written
 	} else if err != nil {
 		return 0, 0, err
 	}
@@ -257,26 +243,19 @@ func (db *DB) Replica(name string) *Replica {
 
 // Pos returns the current position of the database.
 func (db *DB) Pos() (Pos, error) {
-	generation, err := db.CurrentGeneration()
-	if err != nil {
-		return Pos{}, err
-	} else if generation == "" {
-		return Pos{}, nil
-	}
-
-	index, _, err := db.CurrentShadowWALIndex(generation)
+	index, _, err := db.CurrentShadowWALIndex()
 	if err != nil {
 		return Pos{}, err
 	}
 
-	fi, err := os.Stat(db.ShadowWALPath(generation, index))
+	fi, err := os.Stat(db.ShadowWALPath(index))
 	if os.IsNotExist(err) {
-		return Pos{Generation: generation, Index: index}, nil
+		return Pos{Index: index}, nil
 	} else if err != nil {
 		return Pos{}, err
 	}
 
-	return Pos{Generation: generation, Index: index, Offset: frameAlign(fi.Size(), db.pageSize)}, nil
+	return Pos{Index: index, Offset: frameAlign(fi.Size(), db.pageSize)}, nil
 }
 
 // Notify returns a channel that closes when the shadow WAL changes.
@@ -332,7 +311,7 @@ func (db *DB) Close(ctx context.Context) (err error) {
 
 	// Perform a final db sync, if initialized.
 	if db.db != nil {
-		if e := db.Sync(ctx); e != nil && err == nil {
+		if e := db.Sync(ctx); e != nil {
 			err = e
 		}
 	}
@@ -483,13 +462,11 @@ func (db *DB) init() (err error) {
 
 	// If we have an existing shadow WAL, ensure the headers match.
 	if err := db.verifyHeadersMatch(); err != nil {
-		db.Logger.Warn("init: cannot determine last wal position, clearing generation", "error", err)
-		if err := os.Remove(db.GenerationNamePath()); err != nil && !os.IsNotExist(err) {
-			return fmt.Errorf("remove generation name: %w", err)
-		}
+		// TODO(gen): Handle header mismatch
+		panic(fmt.Sprintf("init: cannot determine last wal position, clearing generation: %s", err))
 	}
 
-	// Clean up previous generations.
+	// Clean up replicated WAL files.
 	if err := db.clean(); err != nil {
 		return fmt.Errorf("clean: %w", err)
 	}
@@ -504,16 +481,8 @@ func (db *DB) init() (err error) {
 
 // verifyHeadersMatch returns true if the primary WAL and last shadow WAL header match.
 func (db *DB) verifyHeadersMatch() error {
-	// Determine current generation.
-	generation, err := db.CurrentGeneration()
-	if err != nil {
-		return err
-	} else if generation == "" {
-		return nil
-	}
-
-	// Find current generation & latest shadow WAL.
-	shadowWALPath, err := db.CurrentShadowWALPath(generation)
+	// Find latest shadow WAL.
+	shadowWALPath, err := db.CurrentShadowWALPath()
 	if err != nil {
 		return fmt.Errorf("cannot determine current shadow wal path: %w", err)
 	}
@@ -538,56 +507,17 @@ func (db *DB) verifyHeadersMatch() error {
 	return nil
 }
 
-// clean removes old generations & WAL files.
+// clean removes old WAL files.
 func (db *DB) clean() error {
-	if err := db.cleanGenerations(); err != nil {
-		return err
-	}
 	return db.cleanWAL()
 }
 
-// cleanGenerations removes old generations.
-func (db *DB) cleanGenerations() error {
-	generation, err := db.CurrentGeneration()
-	if err != nil {
-		return err
-	}
-
-	dir := filepath.Join(db.metaPath, "generations")
-	fis, err := os.ReadDir(dir)
-	if os.IsNotExist(err) {
-		return nil
-	} else if err != nil {
-		return err
-	}
-	for _, fi := range fis {
-		// Skip the current generation.
-		if filepath.Base(fi.Name()) == generation {
-			continue
-		}
-
-		// Delete all other generations.
-		if err := os.RemoveAll(filepath.Join(dir, fi.Name())); err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
 // cleanWAL removes WAL files that have been replicated.
 func (db *DB) cleanWAL() error {
-	generation, err := db.CurrentGeneration()
-	if err != nil {
-		return err
-	}
-
 	// Determine lowest index that's been replicated to all replicas.
 	min := -1
 	for _, r := range db.Replicas {
 		pos := r.Pos()
-		if pos.Generation != generation {
-			pos = Pos{} // different generation, reset index to zero
-		}
 		if min == -1 || pos.Index < min {
 			min = pos.Index
 		}
@@ -599,8 +529,8 @@ func (db *DB) cleanWAL() error {
 	}
 	min-- // Keep an extra WAL file.
 
-	// Remove all WAL files for the generation before the lowest index.
-	dir := db.ShadowWALDir(generation)
+	// Remove all WAL files before the lowest index.
+	dir := db.ShadowWALDir()
 	fis, err := os.ReadDir(dir)
 	if os.IsNotExist(err) {
 		return nil
@@ -654,68 +584,6 @@ func (db *DB) releaseReadLock() error {
 	return err
 }
 
-// CurrentGeneration returns the name of the generation saved to the "generation"
-// file in the meta data directory. Returns empty string if none exists.
-func (db *DB) CurrentGeneration() (string, error) {
-	buf, err := os.ReadFile(db.GenerationNamePath())
-	if os.IsNotExist(err) {
-		return "", nil
-	} else if err != nil {
-		return "", err
-	}
-
-	// TODO: Verify if generation directory exists. If not, delete name file.
-
-	generation := strings.TrimSpace(string(buf))
-	if len(generation) != GenerationNameLen {
-		return "", nil
-	}
-	return generation, nil
-}
-
-// createGeneration starts a new generation by creating the generation
-// directory, snapshotting to each replica, and updating the current
-// generation name.
-func (db *DB) createGeneration() (string, error) {
-	// Generate random generation hex name.
-	buf := make([]byte, GenerationNameLen/2)
-	_, _ = rand.New(rand.NewSource(time.Now().UnixNano())).Read(buf)
-	generation := hex.EncodeToString(buf)
-
-	// Generate new directory.
-	dir := filepath.Join(db.metaPath, "generations", generation)
-	if err := internal.MkdirAll(dir, db.dirInfo); err != nil {
-		return "", err
-	}
-
-	// Initialize shadow WAL with copy of header.
-	if _, err := db.initShadowWALFile(db.ShadowWALPath(generation, 0)); err != nil {
-		return "", fmt.Errorf("initialize shadow wal: %w", err)
-	}
-
-	// Atomically write generation name as current generation.
-	generationNamePath := db.GenerationNamePath()
-	mode := os.FileMode(0600)
-	if db.fileInfo != nil {
-		mode = db.fileInfo.Mode()
-	}
-	if err := os.WriteFile(generationNamePath+".tmp", []byte(generation+"\n"), mode); err != nil {
-		return "", fmt.Errorf("write generation temp file: %w", err)
-	}
-	uid, gid := internal.Fileinfo(db.fileInfo)
-	_ = os.Chown(generationNamePath+".tmp", uid, gid)
-	if err := os.Rename(generationNamePath+".tmp", generationNamePath); err != nil {
-		return "", fmt.Errorf("rename generation file: %w", err)
-	}
-
-	// Remove old generations.
-	if err := db.clean(); err != nil {
-		return "", err
-	}
-
-	return generation, nil
-}
-
 // Sync copies pending data from the WAL to the shadow WAL.
 func (db *DB) Sync(ctx context.Context) (err error) {
 	db.mu.Lock()
@@ -745,8 +613,8 @@ func (db *DB) Sync(ctx context.Context) (err error) {
 	}
 
 	// Verify our last sync matches the current state of the WAL.
-	// This ensures that we have an existing generation & that the last sync
-	// position of the real WAL hasn't been overwritten by another process.
+	// This ensures that the last sync position of the real WAL hasn't
+	// been overwritten by another process.
 	info, err := db.verify()
 	if err != nil {
 		return fmt.Errorf("cannot verify wal state: %w", err)
@@ -758,18 +626,14 @@ func (db *DB) Sync(ctx context.Context) (err error) {
 
 	// If we are unable to verify the WAL state then we start a new generation.
 	if info.reason != "" {
-		// Start new generation & notify user via log message.
-		if info.generation, err = db.createGeneration(); err != nil {
-			return fmt.Errorf("create generation: %w", err)
-		}
-		db.Logger.Info("sync: new generation", "generation", info.generation, "reason", info.reason)
+		// TODO(gen): Handle sync issue that previously restarted generation
+		panic(fmt.Sprintf("unable to verify wal state: %q", info.reason))
 
 		// Clear shadow wal info.
-		info.shadowWALPath = db.ShadowWALPath(info.generation, 0)
+		info.shadowWALPath = db.ShadowWALPath(0)
 		info.shadowWALSize = WALHeaderSize
 		info.restart = false
 		info.reason = ""
-
 	}
 
 	// Synchronize real WAL with current shadow WAL.
@@ -796,7 +660,7 @@ func (db *DB) Sync(ctx context.Context) (err error) {
 	if checkpoint {
 		changed = true
 
-		if err := db.checkpoint(ctx, info.generation, checkpointMode); err != nil {
+		if err := db.checkpoint(ctx, checkpointMode); err != nil {
 			return fmt.Errorf("checkpoint: mode=%v err=%w", checkpointMode, err)
 		}
 	}
@@ -808,7 +672,7 @@ func (db *DB) Sync(ctx context.Context) (err error) {
 
 	// Compute current index and total shadow WAL size.
 	// This is only for metrics so we ignore any errors that occur.
-	index, size, _ := db.CurrentShadowWALIndex(info.generation)
+	index, size, _ := db.CurrentShadowWALIndex()
 	db.shadowWALIndexGauge.Set(float64(index))
 	db.shadowWALSizeGauge.Set(float64(size))
 
@@ -836,19 +700,9 @@ func (db *DB) ensureWALExists() (err error) {
 }
 
 // verify ensures the current shadow WAL state matches where it left off from
-// the real WAL. Returns generation & WAL sync information. If info.reason is
-// not blank, verification failed and a new generation should be started.
+// the real WAL. Returns WAL sync information. If info.reason is not blank,
+// verification failed.
 func (db *DB) verify() (info syncInfo, err error) {
-	// Look up existing generation.
-	generation, err := db.CurrentGeneration()
-	if err != nil {
-		return info, fmt.Errorf("cannot find current generation: %w", err)
-	} else if generation == "" {
-		info.reason = "no generation exists"
-		return info, nil
-	}
-	info.generation = generation
-
 	// Determine total bytes of real DB for metrics.
 	fi, err := os.Stat(db.Path())
 	if err != nil {
@@ -867,14 +721,14 @@ func (db *DB) verify() (info syncInfo, err error) {
 	db.walSizeGauge.Set(float64(fi.Size()))
 
 	// Open shadow WAL to copy append to.
-	index, _, err := db.CurrentShadowWALIndex(info.generation)
+	index, _, err := db.CurrentShadowWALIndex()
 	if err != nil {
 		return info, fmt.Errorf("cannot determine shadow WAL index: %w", err)
 	} else if index >= MaxIndex {
 		info.reason = "max index exceeded"
 		return info, nil
 	}
-	info.shadowWALPath = db.ShadowWALPath(generation, index)
+	info.shadowWALPath = db.ShadowWALPath(index)
 
 	// Determine shadow WAL current size.
 	fi, err = os.Stat(info.shadowWALPath)
@@ -909,7 +763,6 @@ func (db *DB) verify() (info syncInfo, err error) {
 	}
 
 	// If we only have a header then ensure header matches.
-	// Otherwise we need to start a new generation.
 	if info.shadowWALSize == WALHeaderSize && info.restart {
 		info.reason = "wal header only, mismatched"
 		return info, nil
@@ -932,7 +785,6 @@ func (db *DB) verify() (info syncInfo, err error) {
 }
 
 type syncInfo struct {
-	generation    string    // generation name
 	dbModTime     time.Time // last modified date of real DB file
 	walSize       int64     // size of real WAL file
 	walModTime    time.Time // last modified date of real WAL file
@@ -1183,7 +1035,7 @@ func (db *DB) ShadowWALReader(pos Pos) (r *ShadowWALReader, err error) {
 
 // shadowWALReader opens a file reader for a shadow WAL file at a given position.
 func (db *DB) shadowWALReader(pos Pos) (r *ShadowWALReader, err error) {
-	filename := db.ShadowWALPath(pos.Generation, pos.Index)
+	filename := db.ShadowWALPath(pos.Index)
 
 	f, err := os.Open(filename)
 	if err != nil {
@@ -1299,24 +1151,19 @@ func readLastChecksumFrom(f *os.File, pageSize int) (uint32, uint32, error) {
 func (db *DB) Checkpoint(ctx context.Context, mode string) (err error) {
 	db.mu.Lock()
 	defer db.mu.Unlock()
-
-	generation, err := db.CurrentGeneration()
-	if err != nil {
-		return fmt.Errorf("cannot determine generation: %w", err)
-	}
-	return db.checkpoint(ctx, generation, mode)
+	return db.checkpoint(ctx, mode)
 }
 
 // checkpoint performs a checkpoint on the WAL file and initializes a
 // new shadow WAL file.
-func (db *DB) checkpoint(ctx context.Context, generation, mode string) error {
+func (db *DB) checkpoint(ctx context.Context, mode string) error {
 	// Try getting a checkpoint lock, will fail during snapshots.
 	if !db.chkMu.TryLock() {
 		return nil
 	}
 	defer db.chkMu.Unlock()
 
-	shadowWALPath, err := db.CurrentShadowWALPath(generation)
+	shadowWALPath, err := db.CurrentShadowWALPath()
 	if err != nil {
 		return err
 	}
@@ -1452,12 +1299,11 @@ func (db *DB) monitor() {
 	}
 }
 
-// CalcRestoreTarget returns a replica & generation to restore from based on opt criteria.
-func (db *DB) CalcRestoreTarget(ctx context.Context, opt RestoreOptions) (*Replica, string, error) {
+// CalcRestoreTarget returns a replica to restore from based on opt criteria.
+func (db *DB) CalcRestoreTarget(ctx context.Context, opt RestoreOptions) (*Replica, error) {
 	var target struct {
-		replica    *Replica
-		generation string
-		updatedAt  time.Time
+		replica   *Replica
+		updatedAt time.Time
 	}
 
 	for _, r := range db.Replicas {
@@ -1466,9 +1312,9 @@ func (db *DB) CalcRestoreTarget(ctx context.Context, opt RestoreOptions) (*Repli
 			continue
 		}
 
-		generation, updatedAt, err := r.CalcRestoreTarget(ctx, opt)
+		updatedAt, err := r.CalcRestoreTarget(ctx, opt)
 		if err != nil {
-			return nil, "", err
+			return nil, err
 		}
 
 		// Use the latest replica if we have multiple candidates.
@@ -1476,13 +1322,13 @@ func (db *DB) CalcRestoreTarget(ctx context.Context, opt RestoreOptions) (*Repli
 			continue
 		}
 
-		target.replica, target.generation, target.updatedAt = r, generation, updatedAt
+		target.replica, target.updatedAt = r, updatedAt
 	}
-	return target.replica, target.generation, nil
+	return target.replica, nil
 }
 
 // applyWAL performs a truncating checkpoint on the given database.
-func applyWAL(ctx context.Context, index int, dbPath string) error {
+func applyWAL(_ context.Context, index int, dbPath string) error {
 	// Copy WAL file from it's staging path to the correct "-wal" location.
 	if err := os.Rename(fmt.Sprintf("%s-%08x-wal", dbPath, index), dbPath+"-wal"); err != nil {
 		return err
@@ -1521,15 +1367,8 @@ func (db *DB) CRC64(ctx context.Context) (uint64, Pos, error) {
 		return 0, Pos{}, os.ErrNotExist
 	}
 
-	generation, err := db.CurrentGeneration()
-	if err != nil {
-		return 0, Pos{}, fmt.Errorf("cannot find current generation: %w", err)
-	} else if generation == "" {
-		return 0, Pos{}, fmt.Errorf("no current generation")
-	}
-
 	// Force a RESTART checkpoint to ensure the database is at the start of the WAL.
-	if err := db.checkpoint(ctx, generation, CheckpointModeRestart); err != nil {
+	if err := db.checkpoint(ctx, CheckpointModeRestart); err != nil {
 		return 0, Pos{}, err
 	}
 
@@ -1577,10 +1416,6 @@ type RestoreOptions struct {
 	// If blank, all replicas are considered.
 	ReplicaName string
 
-	// Specific generation to restore from.
-	// If blank, all generations considered.
-	Generation string
-
 	// Specific index to restore from.
 	// Set to math.MaxInt32 to ignore index.
 	Index int
diff --git a/file/replica_client.go b/file/replica_client.go
index adbdbf0..9aab06d 100644
--- a/file/replica_client.go
+++ b/file/replica_client.go
@@ -48,103 +48,45 @@ func (c *ReplicaClient) Path() string {
 	return c.path
 }
 
-// GenerationsDir returns the path to a generation root directory.
-func (c *ReplicaClient) GenerationsDir() (string, error) {
-	if c.path == "" {
-		return "", fmt.Errorf("file replica path required")
-	}
-	return filepath.Join(c.path, "generations"), nil
-}
-
-// GenerationDir returns the path to a generation's root directory.
-func (c *ReplicaClient) GenerationDir(generation string) (string, error) {
-	dir, err := c.GenerationsDir()
-	if err != nil {
-		return "", err
-	} else if generation == "" {
-		return "", fmt.Errorf("generation required")
-	}
-	return filepath.Join(dir, generation), nil
-}
-
-// SnapshotsDir returns the path to a generation's snapshot directory.
-func (c *ReplicaClient) SnapshotsDir(generation string) (string, error) {
-	dir, err := c.GenerationDir(generation)
-	if err != nil {
-		return "", err
-	}
-	return filepath.Join(dir, "snapshots"), nil
+// SnapshotsDir returns the path to the snapshot directory.
+func (c *ReplicaClient) SnapshotsDir() (string, error) {
+	return filepath.Join(c.path, "snapshots"), nil
 }
 
 // SnapshotPath returns the path to an uncompressed snapshot file.
-func (c *ReplicaClient) SnapshotPath(generation string, index int) (string, error) {
-	dir, err := c.SnapshotsDir(generation)
+func (c *ReplicaClient) SnapshotPath(index int) (string, error) {
+	dir, err := c.SnapshotsDir()
 	if err != nil {
 		return "", err
 	}
 	return filepath.Join(dir, litestream.FormatSnapshotPath(index)), nil
 }
 
-// WALDir returns the path to a generation's WAL directory
-func (c *ReplicaClient) WALDir(generation string) (string, error) {
-	dir, err := c.GenerationDir(generation)
-	if err != nil {
-		return "", err
-	}
-	return filepath.Join(dir, "wal"), nil
+// WALDir returns the path to the WAL directory
+func (c *ReplicaClient) WALDir() (string, error) {
+	return filepath.Join(c.path, "wal"), nil
 }
 
 // WALSegmentPath returns the path to a WAL segment file.
-func (c *ReplicaClient) WALSegmentPath(generation string, index int, offset int64) (string, error) {
-	dir, err := c.WALDir(generation)
+func (c *ReplicaClient) WALSegmentPath(index int, offset int64) (string, error) {
+	dir, err := c.WALDir()
 	if err != nil {
 		return "", err
 	}
 	return filepath.Join(dir, litestream.FormatWALSegmentPath(index, offset)), nil
 }
 
-// Generations returns a list of available generation names.
-func (c *ReplicaClient) Generations(ctx context.Context) ([]string, error) {
-	root, err := c.GenerationsDir()
-	if err != nil {
-		return nil, fmt.Errorf("cannot determine generations path: %w", err)
-	}
-
-	fis, err := os.ReadDir(root)
-	if os.IsNotExist(err) {
-		return nil, nil
-	} else if err != nil {
-		return nil, err
-	}
-
-	var generations []string
-	for _, fi := range fis {
-		if !litestream.IsGenerationName(fi.Name()) {
-			continue
-		} else if !fi.IsDir() {
-			continue
-		}
-		generations = append(generations, fi.Name())
-	}
-	return generations, nil
-}
-
-// DeleteGeneration deletes all snapshots & WAL segments within a generation.
-func (c *ReplicaClient) DeleteGeneration(ctx context.Context, generation string) error {
-	dir, err := c.GenerationDir(generation)
-	if err != nil {
-		return fmt.Errorf("cannot determine generation path: %w", err)
-	}
-
-	if err := os.RemoveAll(dir); err != nil && !os.IsNotExist(err) {
+// DeleteAll deletes all snapshots & WAL segments.
+func (c *ReplicaClient) DeleteAll(ctx context.Context) error {
+	if err := os.RemoveAll(c.path); err != nil && !os.IsNotExist(err) {
 		return err
 	}
 	return nil
 }
 
-// Snapshots returns an iterator over all available snapshots for a generation.
-func (c *ReplicaClient) Snapshots(ctx context.Context, generation string) (litestream.SnapshotIterator, error) {
-	dir, err := c.SnapshotsDir(generation)
+// Snapshots returns an iterator over all available snapshots.
+func (c *ReplicaClient) Snapshots(ctx context.Context) (litestream.SnapshotIterator, error) {
+	dir, err := c.SnapshotsDir()
 	if err != nil {
 		return nil, fmt.Errorf("cannot determine snapshots path: %w", err)
 	}
@@ -172,10 +114,9 @@ func (c *ReplicaClient) Snapshots(ctx context.Context, generation string) (lites
 		}
 
 		infos = append(infos, litestream.SnapshotInfo{
-			Generation: generation,
-			Index:      index,
-			Size:       fi.Size(),
-			CreatedAt:  fi.ModTime().UTC(),
+			Index:     index,
+			Size:      fi.Size(),
+			CreatedAt: fi.ModTime().UTC(),
 		})
 	}
 
@@ -183,8 +124,8 @@ func (c *ReplicaClient) Snapshots(ctx context.Context, generation string) (lites
 }
 
 // WriteSnapshot writes LZ4 compressed data from rd into a file on disk.
-func (c *ReplicaClient) WriteSnapshot(ctx context.Context, generation string, index int, rd io.Reader) (info litestream.SnapshotInfo, err error) {
-	filename, err := c.SnapshotPath(generation, index)
+func (c *ReplicaClient) WriteSnapshot(ctx context.Context, index int, rd io.Reader) (info litestream.SnapshotInfo, err error) {
+	filename, err := c.SnapshotPath(index)
 	if err != nil {
 		return info, fmt.Errorf("cannot determine snapshot path: %w", err)
 	}
@@ -220,10 +161,9 @@ func (c *ReplicaClient) WriteSnapshot(ctx context.Context, generation string, in
 		return info, err
 	}
 	info = litestream.SnapshotInfo{
-		Generation: generation,
-		Index:      index,
-		Size:       fi.Size(),
-		CreatedAt:  fi.ModTime().UTC(),
+		Index:     index,
+		Size:      fi.Size(),
+		CreatedAt: fi.ModTime().UTC(),
 	}
 
 	// Move snapshot to final path when it has been fully written & synced to disk.
@@ -234,19 +174,19 @@ func (c *ReplicaClient) WriteSnapshot(ctx context.Context, generation string, in
 	return info, nil
 }
 
-// SnapshotReader returns a reader for snapshot data at the given generation/index.
+// SnapshotReader returns a reader for snapshot data at the given index.
 // Returns os.ErrNotExist if no matching index is found.
-func (c *ReplicaClient) SnapshotReader(ctx context.Context, generation string, index int) (io.ReadCloser, error) {
-	filename, err := c.SnapshotPath(generation, index)
+func (c *ReplicaClient) SnapshotReader(ctx context.Context, index int) (io.ReadCloser, error) {
+	filename, err := c.SnapshotPath(index)
 	if err != nil {
 		return nil, fmt.Errorf("cannot determine snapshot path: %w", err)
 	}
 	return os.Open(filename)
 }
 
-// DeleteSnapshot deletes a snapshot with the given generation & index.
-func (c *ReplicaClient) DeleteSnapshot(ctx context.Context, generation string, index int) error {
-	filename, err := c.SnapshotPath(generation, index)
+// DeleteSnapshot deletes a snapshot with the given index.
+func (c *ReplicaClient) DeleteSnapshot(ctx context.Context, index int) error {
+	filename, err := c.SnapshotPath(index)
 	if err != nil {
 		return fmt.Errorf("cannot determine snapshot path: %w", err)
 	}
@@ -256,9 +196,9 @@ func (c *ReplicaClient) DeleteSnapshot(ctx context.Context, generation string, i
 	return nil
 }
 
-// WALSegments returns an iterator over all available WAL files for a generation.
-func (c *ReplicaClient) WALSegments(ctx context.Context, generation string) (litestream.WALSegmentIterator, error) {
-	dir, err := c.WALDir(generation)
+// WALSegments returns an iterator over all available WAL files.
+func (c *ReplicaClient) WALSegments(ctx context.Context) (litestream.WALSegmentIterator, error) {
+	dir, err := c.WALDir()
 	if err != nil {
 		return nil, fmt.Errorf("cannot determine wal path: %w", err)
 	}
@@ -286,11 +226,10 @@ func (c *ReplicaClient) WALSegments(ctx context.Context, generation string) (lit
 		}
 
 		infos = append(infos, litestream.WALSegmentInfo{
-			Generation: generation,
-			Index:      index,
-			Offset:     offset,
-			Size:       fi.Size(),
-			CreatedAt:  fi.ModTime().UTC(),
+			Index:     index,
+			Offset:    offset,
+			Size:      fi.Size(),
+			CreatedAt: fi.ModTime().UTC(),
 		})
 	}
 
@@ -299,7 +238,7 @@ func (c *ReplicaClient) WALSegments(ctx context.Context, generation string) (lit
 
 // WriteWALSegment writes LZ4 compressed data from rd into a file on disk.
 func (c *ReplicaClient) WriteWALSegment(ctx context.Context, pos litestream.Pos, rd io.Reader) (info litestream.WALSegmentInfo, err error) {
-	filename, err := c.WALSegmentPath(pos.Generation, pos.Index, pos.Offset)
+	filename, err := c.WALSegmentPath(pos.Index, pos.Offset)
 	if err != nil {
 		return info, fmt.Errorf("cannot determine wal segment path: %w", err)
 	}
@@ -335,11 +274,10 @@ func (c *ReplicaClient) WriteWALSegment(ctx context.Context, pos litestream.Pos,
 		return info, err
 	}
 	info = litestream.WALSegmentInfo{
-		Generation: pos.Generation,
-		Index:      pos.Index,
-		Offset:     pos.Offset,
-		Size:       fi.Size(),
-		CreatedAt:  fi.ModTime().UTC(),
+		Index:     pos.Index,
+		Offset:    pos.Offset,
+		Size:      fi.Size(),
+		CreatedAt: fi.ModTime().UTC(),
 	}
 
 	// Move WAL segment to final path when it has been written & synced to disk.
@@ -353,7 +291,7 @@ func (c *ReplicaClient) WriteWALSegment(ctx context.Context, pos litestream.Pos,
 // WALSegmentReader returns a reader for a section of WAL data at the given position.
 // Returns os.ErrNotExist if no matching index/offset is found.
 func (c *ReplicaClient) WALSegmentReader(ctx context.Context, pos litestream.Pos) (io.ReadCloser, error) {
-	filename, err := c.WALSegmentPath(pos.Generation, pos.Index, pos.Offset)
+	filename, err := c.WALSegmentPath(pos.Index, pos.Offset)
 	if err != nil {
 		return nil, fmt.Errorf("cannot determine wal segment path: %w", err)
 	}
@@ -363,7 +301,7 @@ func (c *ReplicaClient) WALSegmentReader(ctx context.Context, pos litestream.Pos
 // DeleteWALSegments deletes WAL segments at the given positions.
 func (c *ReplicaClient) DeleteWALSegments(ctx context.Context, a []litestream.Pos) error {
 	for _, pos := range a {
-		filename, err := c.WALSegmentPath(pos.Generation, pos.Index, pos.Offset)
+		filename, err := c.WALSegmentPath(pos.Index, pos.Offset)
 		if err != nil {
 			return fmt.Errorf("cannot determine wal segment path: %w", err)
 		}
diff --git a/gcs/replica_client.go b/gcs/replica_client.go
index 7b2b2c6..82c345f 100644
--- a/gcs/replica_client.go
+++ b/gcs/replica_client.go
@@ -6,7 +6,6 @@ import (
 	"io"
 	"os"
 	"path"
-	"strings"
 	"sync"
 	"time"
 
@@ -59,53 +58,15 @@ func (c *ReplicaClient) Init(ctx context.Context) (err error) {
 	return nil
 }
 
-// Generations returns a list of available generation names.
-func (c *ReplicaClient) Generations(ctx context.Context) ([]string, error) {
-	if err := c.Init(ctx); err != nil {
-		return nil, err
-	}
-
-	// Construct query to only pull generation directory names.
-	query := &storage.Query{
-		Delimiter: "/",
-		Prefix:    litestream.GenerationsPath(c.Path) + "/",
-	}
-
-	// Loop over results and only build list of generation-formatted names.
-	it := c.bkt.Objects(ctx, query)
-	var generations []string
-	for {
-		attrs, err := it.Next()
-		if err == iterator.Done {
-			break
-		} else if err != nil {
-			return nil, err
-		}
-
-		name := path.Base(strings.TrimSuffix(attrs.Prefix, "/"))
-		if !litestream.IsGenerationName(name) {
-			continue
-		}
-		generations = append(generations, name)
-	}
-
-	return generations, nil
-}
-
-// DeleteGeneration deletes all snapshots & WAL segments within a generation.
-func (c *ReplicaClient) DeleteGeneration(ctx context.Context, generation string) error {
+// DeleteAll deletes all snapshots & WAL segments.
+func (c *ReplicaClient) DeleteAll(ctx context.Context) error {
 	if err := c.Init(ctx); err != nil {
 		return err
 	}
 
-	dir, err := litestream.GenerationPath(c.Path, generation)
-	if err != nil {
-		return fmt.Errorf("cannot determine generation path: %w", err)
-	}
-
-	// Iterate over every object in generation and delete it.
+	// Iterate over every object and delete it.
 	internal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, "LIST").Inc()
-	for it := c.bkt.Objects(ctx, &storage.Query{Prefix: dir + "/"}); ; {
+	for it := c.bkt.Objects(ctx, &storage.Query{Prefix: c.Path + "/"}); ; {
 		attrs, err := it.Next()
 		if err == iterator.Done {
 			break
@@ -121,30 +82,30 @@ func (c *ReplicaClient) DeleteGeneration(ctx context.Context, generation string)
 		internal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, "DELETE").Inc()
 	}
 
-	// log.Printf("%s(%s): retainer: deleting generation: %s", r.db.Path(), r.Name(), generation)
+	// log.Printf("%s(%s): retainer: deleting", r.db.Path(), r.Name())
 
 	return nil
 }
 
-// Snapshots returns an iterator over all available snapshots for a generation.
-func (c *ReplicaClient) Snapshots(ctx context.Context, generation string) (litestream.SnapshotIterator, error) {
+// Snapshots returns an iterator over all available snapshots.
+func (c *ReplicaClient) Snapshots(ctx context.Context) (litestream.SnapshotIterator, error) {
 	if err := c.Init(ctx); err != nil {
 		return nil, err
 	}
-	dir, err := litestream.SnapshotsPath(c.Path, generation)
+	dir, err := litestream.SnapshotsPath(c.Path)
 	if err != nil {
 		return nil, fmt.Errorf("cannot determine snapshots path: %w", err)
 	}
-	return newSnapshotIterator(generation, c.bkt.Objects(ctx, &storage.Query{Prefix: dir + "/"})), nil
+	return newSnapshotIterator(c.bkt.Objects(ctx, &storage.Query{Prefix: dir + "/"})), nil
 }
 
 // WriteSnapshot writes LZ4 compressed data from rd to the object storage.
-func (c *ReplicaClient) WriteSnapshot(ctx context.Context, generation string, index int, rd io.Reader) (info litestream.SnapshotInfo, err error) {
+func (c *ReplicaClient) WriteSnapshot(ctx context.Context, index int, rd io.Reader) (info litestream.SnapshotInfo, err error) {
 	if err := c.Init(ctx); err != nil {
 		return info, err
 	}
 
-	key, err := litestream.SnapshotPath(c.Path, generation, index)
+	key, err := litestream.SnapshotPath(c.Path, index)
 	if err != nil {
 		return info, fmt.Errorf("cannot determine snapshot path: %w", err)
 	}
@@ -163,23 +124,22 @@ func (c *ReplicaClient) WriteSnapshot(ctx context.Context, generation string, in
 	internal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, "PUT").Inc()
 	internal.OperationBytesCounterVec.WithLabelValues(ReplicaClientType, "PUT").Add(float64(n))
 
-	// log.Printf("%s(%s): snapshot: creating %s/%08x t=%s", r.db.Path(), r.Name(), generation, index, time.Since(startTime).Truncate(time.Millisecond))
+	// log.Printf("%s(%s): snapshot: creating %08x t=%s", r.db.Path(), r.Name(),  index, time.Since(startTime).Truncate(time.Millisecond))
 
 	return litestream.SnapshotInfo{
-		Generation: generation,
-		Index:      index,
-		Size:       n,
-		CreatedAt:  startTime.UTC(),
+		Index:     index,
+		Size:      n,
+		CreatedAt: startTime.UTC(),
 	}, nil
 }
 
-// SnapshotReader returns a reader for snapshot data at the given generation/index.
-func (c *ReplicaClient) SnapshotReader(ctx context.Context, generation string, index int) (io.ReadCloser, error) {
+// SnapshotReader returns a reader for snapshot data at the given index.
+func (c *ReplicaClient) SnapshotReader(ctx context.Context, index int) (io.ReadCloser, error) {
 	if err := c.Init(ctx); err != nil {
 		return nil, err
 	}
 
-	key, err := litestream.SnapshotPath(c.Path, generation, index)
+	key, err := litestream.SnapshotPath(c.Path, index)
 	if err != nil {
 		return nil, fmt.Errorf("cannot determine snapshot path: %w", err)
 	}
@@ -197,13 +157,13 @@ func (c *ReplicaClient) SnapshotReader(ctx context.Context, generation string, i
 	return r, nil
 }
 
-// DeleteSnapshot deletes a snapshot with the given generation & index.
-func (c *ReplicaClient) DeleteSnapshot(ctx context.Context, generation string, index int) error {
+// DeleteSnapshot deletes a snapshot with the given index.
+func (c *ReplicaClient) DeleteSnapshot(ctx context.Context, index int) error {
 	if err := c.Init(ctx); err != nil {
 		return err
 	}
 
-	key, err := litestream.SnapshotPath(c.Path, generation, index)
+	key, err := litestream.SnapshotPath(c.Path, index)
 	if err != nil {
 		return fmt.Errorf("cannot determine snapshot path: %w", err)
 	}
@@ -216,16 +176,16 @@ func (c *ReplicaClient) DeleteSnapshot(ctx context.Context, generation string, i
 	return nil
 }
 
-// WALSegments returns an iterator over all available WAL files for a generation.
-func (c *ReplicaClient) WALSegments(ctx context.Context, generation string) (litestream.WALSegmentIterator, error) {
+// WALSegments returns an iterator over all available WAL files.
+func (c *ReplicaClient) WALSegments(ctx context.Context) (litestream.WALSegmentIterator, error) {
 	if err := c.Init(ctx); err != nil {
 		return nil, err
 	}
-	dir, err := litestream.WALPath(c.Path, generation)
+	dir, err := litestream.WALPath(c.Path)
 	if err != nil {
 		return nil, fmt.Errorf("cannot determine wal path: %w", err)
 	}
-	return newWALSegmentIterator(generation, c.bkt.Objects(ctx, &storage.Query{Prefix: dir + "/"})), nil
+	return newWALSegmentIterator(c.bkt.Objects(ctx, &storage.Query{Prefix: dir + "/"})), nil
 }
 
 // WriteWALSegment writes LZ4 compressed data from rd into a file on disk.
@@ -234,7 +194,7 @@ func (c *ReplicaClient) WriteWALSegment(ctx context.Context, pos litestream.Pos,
 		return info, err
 	}
 
-	key, err := litestream.WALSegmentPath(c.Path, pos.Generation, pos.Index, pos.Offset)
+	key, err := litestream.WALSegmentPath(c.Path, pos.Index, pos.Offset)
 	if err != nil {
 		return info, fmt.Errorf("cannot determine wal segment path: %w", err)
 	}
@@ -254,11 +214,10 @@ func (c *ReplicaClient) WriteWALSegment(ctx context.Context, pos litestream.Pos,
 	internal.OperationBytesCounterVec.WithLabelValues(ReplicaClientType, "PUT").Add(float64(n))
 
 	return litestream.WALSegmentInfo{
-		Generation: pos.Generation,
-		Index:      pos.Index,
-		Offset:     pos.Offset,
-		Size:       n,
-		CreatedAt:  startTime.UTC(),
+		Index:     pos.Index,
+		Offset:    pos.Offset,
+		Size:      n,
+		CreatedAt: startTime.UTC(),
 	}, nil
 }
 
@@ -269,7 +228,7 @@ func (c *ReplicaClient) WALSegmentReader(ctx context.Context, pos litestream.Pos
 		return nil, err
 	}
 
-	key, err := litestream.WALSegmentPath(c.Path, pos.Generation, pos.Index, pos.Offset)
+	key, err := litestream.WALSegmentPath(c.Path, pos.Index, pos.Offset)
 	if err != nil {
 		return nil, fmt.Errorf("cannot determine wal segment path: %w", err)
 	}
@@ -294,7 +253,7 @@ func (c *ReplicaClient) DeleteWALSegments(ctx context.Context, a []litestream.Po
 	}
 
 	for _, pos := range a {
-		key, err := litestream.WALSegmentPath(c.Path, pos.Generation, pos.Index, pos.Offset)
+		key, err := litestream.WALSegmentPath(c.Path, pos.Index, pos.Offset)
 		if err != nil {
 			return fmt.Errorf("cannot determine wal segment path: %w", err)
 		}
@@ -309,17 +268,14 @@ func (c *ReplicaClient) DeleteWALSegments(ctx context.Context, a []litestream.Po
 }
 
 type snapshotIterator struct {
-	generation string
-
 	it   *storage.ObjectIterator
 	info litestream.SnapshotInfo
 	err  error
 }
 
-func newSnapshotIterator(generation string, it *storage.ObjectIterator) *snapshotIterator {
+func newSnapshotIterator(it *storage.ObjectIterator) *snapshotIterator {
 	return &snapshotIterator{
-		generation: generation,
-		it:         it,
+		it: it,
 	}
 }
 
@@ -351,10 +307,9 @@ func (itr *snapshotIterator) Next() bool {
 
 		// Store current snapshot and return.
 		itr.info = litestream.SnapshotInfo{
-			Generation: itr.generation,
-			Index:      index,
-			Size:       attrs.Size,
-			CreatedAt:  attrs.Created.UTC(),
+			Index:     index,
+			Size:      attrs.Size,
+			CreatedAt: attrs.Created.UTC(),
 		}
 		return true
 	}
@@ -365,17 +320,14 @@ func (itr *snapshotIterator) Err() error { return itr.err }
 func (itr *snapshotIterator) Snapshot() litestream.SnapshotInfo { return itr.info }
 
 type walSegmentIterator struct {
-	generation string
-
 	it   *storage.ObjectIterator
 	info litestream.WALSegmentInfo
 	err  error
 }
 
-func newWALSegmentIterator(generation string, it *storage.ObjectIterator) *walSegmentIterator {
+func newWALSegmentIterator(it *storage.ObjectIterator) *walSegmentIterator {
 	return &walSegmentIterator{
-		generation: generation,
-		it:         it,
+		it: it,
 	}
 }
 
@@ -407,11 +359,10 @@ func (itr *walSegmentIterator) Next() bool {
 
 		// Store current snapshot and return.
 		itr.info = litestream.WALSegmentInfo{
-			Generation: itr.generation,
-			Index:      index,
-			Offset:     offset,
-			Size:       attrs.Size,
-			CreatedAt:  attrs.Created.UTC(),
+			Index:     index,
+			Offset:    offset,
+			Size:      attrs.Size,
+			CreatedAt: attrs.Created.UTC(),
 		}
 		return true
 	}
diff --git a/mock/replica_client.go b/mock/replica_client.go
index a8bd998..d4ff10f 100644
--- a/mock/replica_client.go
+++ b/mock/replica_client.go
@@ -10,13 +10,12 @@ import (
 var _ litestream.ReplicaClient = (*ReplicaClient)(nil)
 
 type ReplicaClient struct {
-	GenerationsFunc       func(ctx context.Context) ([]string, error)
-	DeleteGenerationFunc  func(ctx context.Context, generation string) error
-	SnapshotsFunc         func(ctx context.Context, generation string) (litestream.SnapshotIterator, error)
-	WriteSnapshotFunc     func(ctx context.Context, generation string, index int, r io.Reader) (litestream.SnapshotInfo, error)
-	DeleteSnapshotFunc    func(ctx context.Context, generation string, index int) error
-	SnapshotReaderFunc    func(ctx context.Context, generation string, index int) (io.ReadCloser, error)
-	WALSegmentsFunc       func(ctx context.Context, generation string) (litestream.WALSegmentIterator, error)
+	DeleteAllFunc         func(ctx context.Context) error
+	SnapshotsFunc         func(ctx context.Context) (litestream.SnapshotIterator, error)
+	WriteSnapshotFunc     func(ctx context.Context, index int, r io.Reader) (litestream.SnapshotInfo, error)
+	DeleteSnapshotFunc    func(ctx context.Context, index int) error
+	SnapshotReaderFunc    func(ctx context.Context, index int) (io.ReadCloser, error)
+	WALSegmentsFunc       func(ctx context.Context) (litestream.WALSegmentIterator, error)
 	WriteWALSegmentFunc   func(ctx context.Context, pos litestream.Pos, r io.Reader) (litestream.WALSegmentInfo, error)
 	DeleteWALSegmentsFunc func(ctx context.Context, a []litestream.Pos) error
 	WALSegmentReaderFunc  func(ctx context.Context, pos litestream.Pos) (io.ReadCloser, error)
@@ -24,32 +23,28 @@ type ReplicaClient struct {
 
 func (c *ReplicaClient) Type() string { return "mock" }
 
-func (c *ReplicaClient) Generations(ctx context.Context) ([]string, error) {
-	return c.GenerationsFunc(ctx)
+func (c *ReplicaClient) DeleteAll(ctx context.Context) error {
+	return c.DeleteAllFunc(ctx)
 }
 
-func (c *ReplicaClient) DeleteGeneration(ctx context.Context, generation string) error {
-	return c.DeleteGenerationFunc(ctx, generation)
+func (c *ReplicaClient) Snapshots(ctx context.Context) (litestream.SnapshotIterator, error) {
+	return c.SnapshotsFunc(ctx)
 }
 
-func (c *ReplicaClient) Snapshots(ctx context.Context, generation string) (litestream.SnapshotIterator, error) {
-	return c.SnapshotsFunc(ctx, generation)
+func (c *ReplicaClient) WriteSnapshot(ctx context.Context, index int, r io.Reader) (litestream.SnapshotInfo, error) {
+	return c.WriteSnapshotFunc(ctx, index, r)
 }
 
-func (c *ReplicaClient) WriteSnapshot(ctx context.Context, generation string, index int, r io.Reader) (litestream.SnapshotInfo, error) {
-	return c.WriteSnapshotFunc(ctx, generation, index, r)
+func (c *ReplicaClient) DeleteSnapshot(ctx context.Context, index int) error {
+	return c.DeleteSnapshotFunc(ctx, index)
 }
 
-func (c *ReplicaClient) DeleteSnapshot(ctx context.Context, generation string, index int) error {
-	return c.DeleteSnapshotFunc(ctx, generation, index)
+func (c *ReplicaClient) SnapshotReader(ctx context.Context, index int) (io.ReadCloser, error) {
+	return c.SnapshotReaderFunc(ctx, index)
 }
 
-func (c *ReplicaClient) SnapshotReader(ctx context.Context, generation string, index int) (io.ReadCloser, error) {
-	return c.SnapshotReaderFunc(ctx, generation, index)
-}
-
-func (c *ReplicaClient) WALSegments(ctx context.Context, generation string) (litestream.WALSegmentIterator, error) {
-	return c.WALSegmentsFunc(ctx, generation)
+func (c *ReplicaClient) WALSegments(ctx context.Context) (litestream.WALSegmentIterator, error) {
+	return c.WALSegmentsFunc(ctx)
 }
 
 func (c *ReplicaClient) WriteWALSegment(ctx context.Context, pos litestream.Pos, r io.Reader) (litestream.WALSegmentInfo, error) {
diff --git a/replica.go b/replica.go
index a5daf2d..3d34e3a 100644
--- a/replica.go
+++ b/replica.go
@@ -164,39 +164,41 @@ func (r *Replica) Sync(ctx context.Context) (err error) {
 	// Find current position of database.
 	dpos, err := r.db.Pos()
 	if err != nil {
-		return fmt.Errorf("cannot determine current generation: %w", err)
+		return fmt.Errorf("cannot determine current position: %w", err)
 	} else if dpos.IsZero() {
-		return fmt.Errorf("no generation, waiting for data")
+		return fmt.Errorf("no position, waiting for data")
 	}
-	generation := dpos.Generation
 
 	r.Logger().Debug("replica sync", "position", dpos.String())
 
-	// Create a new snapshot and update the current replica position if
-	// the generation on the database has changed.
-	if r.Pos().Generation != generation {
-		// Create snapshot if no snapshots exist for generation.
-		snapshotN, err := r.snapshotN(ctx, generation)
-		if err != nil {
-			return err
-		} else if snapshotN == 0 {
-			if info, err := r.Snapshot(ctx); err != nil {
+	// TODO(gen): Figure out how to resnapshot
+	/*
+		// Create a new snapshot and update the current replica position if
+		// the generation on the database has changed.
+		if r.Pos().Generation != generation {
+			// Create snapshot if no snapshots exist for generation.
+			snapshotN, err := r.snapshotN(ctx, generation)
+			if err != nil {
 				return err
-			} else if info.Generation != generation {
-				return fmt.Errorf("generation changed during snapshot, exiting sync")
+			} else if snapshotN == 0 {
+				if info, err := r.Snapshot(ctx); err != nil {
+					return err
+				} else if info.Generation != generation {
+					return fmt.Errorf("generation changed during snapshot, exiting sync")
+				}
 			}
-		}
 
-		pos, err := r.calcPos(ctx, generation)
-		if err != nil {
-			return fmt.Errorf("cannot determine replica position: %s", err)
-		}
+			pos, err := r.calcPos(ctx)
+			if err != nil {
+				return fmt.Errorf("cannot determine replica position: %s", err)
+			}
 
-		r.Logger().Debug("replica sync: calc new pos", "position", pos.String())
-		r.mu.Lock()
-		r.pos = pos
-		r.mu.Unlock()
-	}
+			r.Logger().Debug("replica sync: calc new pos", "position", pos.String())
+			r.mu.Lock()
+			r.pos = pos
+			r.mu.Unlock()
+		}
+	*/
 
 	// Read all WAL files since the last position.
 	for {
@@ -336,9 +338,9 @@ func (r *Replica) syncWAL(ctx context.Context) (err error) {
 	return nil
 }
 
-// snapshotN returns the number of snapshots for a generation.
-func (r *Replica) snapshotN(ctx context.Context, generation string) (int, error) {
-	itr, err := r.Client.Snapshots(ctx, generation)
+// snapshotN returns the number of snapshots.
+func (r *Replica) snapshotN(ctx context.Context) (int, error) {
+	itr, err := r.Client.Snapshots(ctx)
 	if err != nil {
 		return 0, err
 	}
@@ -352,21 +354,21 @@ func (r *Replica) snapshotN(ctx context.Context, generation string) (int, error)
 }
 
 // calcPos returns the last position for the given generation.
-func (r *Replica) calcPos(ctx context.Context, generation string) (pos Pos, err error) {
+func (r *Replica) calcPos(ctx context.Context) (pos Pos, err error) {
 	// Fetch last snapshot. Return error if no snapshots exist.
-	snapshot, err := r.maxSnapshot(ctx, generation)
+	snapshot, err := r.maxSnapshot(ctx)
 	if err != nil {
 		return pos, fmt.Errorf("max snapshot: %w", err)
 	} else if snapshot == nil {
-		return pos, fmt.Errorf("no snapshot available: generation=%s", generation)
+		return pos, fmt.Errorf("no snapshot available")
 	}
 
 	// Determine last WAL segment available. Use snapshot if none exist.
-	segment, err := r.maxWALSegment(ctx, generation)
+	segment, err := r.maxWALSegment(ctx)
 	if err != nil {
 		return pos, fmt.Errorf("max wal segment: %w", err)
 	} else if segment == nil {
-		return Pos{Generation: snapshot.Generation, Index: snapshot.Index}, nil
+		return Pos{Index: snapshot.Index}, nil
 	}
 
 	// Read segment to determine size to add to offset.
@@ -392,15 +394,14 @@ func (r *Replica) calcPos(ctx context.Context, generation string) (pos Pos, err
 
 	// Return the position at the end of the last WAL segment.
 	return Pos{
-		Generation: segment.Generation,
-		Index:      segment.Index,
-		Offset:     segment.Offset + n,
+		Index:  segment.Index,
+		Offset: segment.Offset + n,
 	}, nil
 }
 
-// maxSnapshot returns the last snapshot in a generation.
-func (r *Replica) maxSnapshot(ctx context.Context, generation string) (*SnapshotInfo, error) {
-	itr, err := r.Client.Snapshots(ctx, generation)
+// maxSnapshot returns the last snapshot.
+func (r *Replica) maxSnapshot(ctx context.Context) (*SnapshotInfo, error) {
+	itr, err := r.Client.Snapshots(ctx)
 	if err != nil {
 		return nil, err
 	}
@@ -415,9 +416,9 @@ func (r *Replica) maxSnapshot(ctx context.Context, generation string) (*Snapshot
 	return max, itr.Close()
 }
 
-// maxWALSegment returns the highest WAL segment in a generation.
-func (r *Replica) maxWALSegment(ctx context.Context, generation string) (*WALSegmentInfo, error) {
-	itr, err := r.Client.WALSegments(ctx, generation)
+// maxWALSegment returns the highest WAL segment.
+func (r *Replica) maxWALSegment(ctx context.Context) (*WALSegmentInfo, error) {
+	itr, err := r.Client.WALSegments(ctx)
 	if err != nil {
 		return nil, err
 	}
@@ -440,32 +441,24 @@ func (r *Replica) Pos() Pos {
 	return r.pos
 }
 
-// Snapshots returns a list of all snapshots across all generations.
+// Snapshots returns a list of all snapshots.
 func (r *Replica) Snapshots(ctx context.Context) ([]SnapshotInfo, error) {
-	generations, err := r.Client.Generations(ctx)
+	var a []SnapshotInfo
+
+	itr, err := r.Client.Snapshots(ctx)
 	if err != nil {
-		return nil, fmt.Errorf("cannot fetch generations: %w", err)
+		return nil, err
 	}
+	defer itr.Close()
 
-	var a []SnapshotInfo
-	for _, generation := range generations {
-		if err := func() error {
-			itr, err := r.Client.Snapshots(ctx, generation)
-			if err != nil {
-				return err
-			}
-			defer itr.Close()
-
-			other, err := SliceSnapshotIterator(itr)
-			if err != nil {
-				return err
-			}
-			a = append(a, other...)
+	other, err := SliceSnapshotIterator(itr)
+	if err != nil {
+		return nil, err
+	}
+	a = append(a, other...)
 
-			return itr.Close()
-		}(); err != nil {
-			return a, err
-		}
+	if err := itr.Close(); err != nil {
+		return nil, err
 	}
 
 	sort.Sort(SnapshotInfoSlice(a))
@@ -505,9 +498,11 @@ func (r *Replica) Snapshot(ctx context.Context) (info SnapshotInfo, err error) {
 	pos, err := r.db.Pos()
 	if err != nil {
 		return info, fmt.Errorf("cannot determine db position: %w", err)
-	} else if pos.IsZero() {
-		return info, ErrNoGeneration
 	}
+	// TODO(gen): ???
+	//else if pos.IsZero() {
+	//	return info, ErrNoGeneration
+	//}
 
 	// Open db file descriptor, if not already open, & position at beginning.
 	if r.f == nil {
@@ -559,7 +554,7 @@ func (r *Replica) Snapshot(ctx context.Context) (info SnapshotInfo, err error) {
 
 	startTime := time.Now()
 	// Delegate write to client & wait for writer goroutine to finish.
-	if info, err = r.Client.WriteSnapshot(ctx, pos.Generation, pos.Index, pr); err != nil {
+	if info, err = r.Client.WriteSnapshot(ctx, pos.Index, pr); err != nil {
 		return info, err
 	} else if err := g.Wait(); err != nil {
 		return info, err
@@ -588,36 +583,21 @@ func (r *Replica) EnforceRetention(ctx context.Context) (err error) {
 		retained = append(retained, snapshot)
 	}
 
-	// Loop over generations and delete unretained snapshots & WAL files.
-	generations, err := r.Client.Generations(ctx)
-	if err != nil {
-		return fmt.Errorf("generations: %w", err)
-	}
-	for _, generation := range generations {
-		// Find earliest retained snapshot for this generation.
-		snapshot := FindMinSnapshotByGeneration(retained, generation)
-
-		// Delete entire generation if no snapshots are being retained.
-		if snapshot == nil {
-			if err := r.Client.DeleteGeneration(ctx, generation); err != nil {
-				return fmt.Errorf("delete generation: %w", err)
-			}
-			continue
-		}
+	// Delete unretained snapshots & WAL files.
+	snapshot := FindMinSnapshot(retained)
 
-		// Otherwise remove all earlier snapshots & WAL segments.
-		if err := r.deleteSnapshotsBeforeIndex(ctx, generation, snapshot.Index); err != nil {
-			return fmt.Errorf("delete snapshots before index: %w", err)
-		} else if err := r.deleteWALSegmentsBeforeIndex(ctx, generation, snapshot.Index); err != nil {
-			return fmt.Errorf("delete wal segments before index: %w", err)
-		}
+	// Otherwise remove all earlier snapshots & WAL segments.
+	if err := r.deleteSnapshotsBeforeIndex(ctx, snapshot.Index); err != nil {
+		return fmt.Errorf("delete snapshots before index: %w", err)
+	} else if err := r.deleteWALSegmentsBeforeIndex(ctx, snapshot.Index); err != nil {
+		return fmt.Errorf("delete wal segments before index: %w", err)
 	}
 
 	return nil
 }
 
-func (r *Replica) deleteSnapshotsBeforeIndex(ctx context.Context, generation string, index int) error {
-	itr, err := r.Client.Snapshots(ctx, generation)
+func (r *Replica) deleteSnapshotsBeforeIndex(ctx context.Context, index int) error {
+	itr, err := r.Client.Snapshots(ctx)
 	if err != nil {
 		return fmt.Errorf("fetch snapshots: %w", err)
 	}
@@ -629,17 +609,17 @@ func (r *Replica) deleteSnapshotsBeforeIndex(ctx context.Context, generation str
 			continue
 		}
 
-		if err := r.Client.DeleteSnapshot(ctx, info.Generation, info.Index); err != nil {
-			return fmt.Errorf("delete snapshot %s/%08x: %w", info.Generation, info.Index, err)
+		if err := r.Client.DeleteSnapshot(ctx, info.Index); err != nil {
+			return fmt.Errorf("delete snapshot %08x: %w", info.Index, err)
 		}
-		r.Logger().Info("snapshot deleted", "generation", generation, "index", index)
+		r.Logger().Info("snapshot deleted", "index", index)
 	}
 
 	return itr.Close()
 }
 
-func (r *Replica) deleteWALSegmentsBeforeIndex(ctx context.Context, generation string, index int) error {
-	itr, err := r.Client.WALSegments(ctx, generation)
+func (r *Replica) deleteWALSegmentsBeforeIndex(ctx context.Context, index int) error {
+	itr, err := r.Client.WALSegments(ctx)
 	if err != nil {
 		return fmt.Errorf("fetch wal segments: %w", err)
 	}
@@ -665,7 +645,7 @@ func (r *Replica) deleteWALSegmentsBeforeIndex(ctx context.Context, generation s
 		return fmt.Errorf("delete wal segments: %w", err)
 	}
 
-	r.Logger().Info("wal segmented deleted before", "generation", generation, "index", index, "n", len(a))
+	r.Logger().Info("wal segmented deleted before", "index", index, "n", len(a))
 	return nil
 }
 
@@ -744,9 +724,9 @@ func (r *Replica) snapshotter(ctx context.Context) {
 
 	logger := r.Logger()
 	if pos, err := r.db.Pos(); err != nil {
-		logger.Error("snapshotter cannot determine generation", "error", err)
+		logger.Error("snapshotter cannot determine position", "error", err)
 	} else if !pos.IsZero() {
-		if snapshot, err := r.maxSnapshot(ctx, pos.Generation); err != nil {
+		if snapshot, err := r.maxSnapshot(ctx); err != nil {
 			logger.Error("snapshotter cannot determine latest snapshot", "error", err)
 		} else if snapshot != nil {
 			nextSnapshot := r.SnapshotInterval - time.Since(snapshot.CreatedAt)
@@ -760,7 +740,7 @@ func (r *Replica) snapshotter(ctx context.Context) {
 			case <-ctx.Done():
 				return
 			case <-time.After(nextSnapshot):
-				if _, err := r.Snapshot(ctx); err != nil && err != ErrNoGeneration {
+				if _, err := r.Snapshot(ctx); err != nil {
 					logger.Error("snapshotter error", "error", err)
 				}
 			}
@@ -775,7 +755,7 @@ func (r *Replica) snapshotter(ctx context.Context) {
 		case <-ctx.Done():
 			return
 		case <-ticker.C:
-			if _, err := r.Snapshot(ctx); err != nil && err != ErrNoGeneration {
+			if _, err := r.Snapshot(ctx); err != nil {
 				r.Logger().Error("snapshotter error", "error", err)
 				continue
 			}
@@ -839,7 +819,6 @@ func (r *Replica) Validate(ctx context.Context) error {
 	if err := r.Restore(ctx, RestoreOptions{
 		OutputPath:  restorePath,
 		ReplicaName: r.Name(),
-		Generation:  pos.Generation,
 		Index:       pos.Index - 1,
 	}); err != nil {
 		return fmt.Errorf("cannot restore: %w", err)
@@ -909,11 +888,14 @@ func (r *Replica) waitForReplica(ctx context.Context, pos Pos) error {
 			continue
 		}
 
-		// Exit if the generation has changed while waiting as there will be
-		// no further progress on the old generation.
-		if curr.Generation != pos.Generation {
-			return fmt.Errorf("generation changed")
-		}
+		// TODO(gen): ???
+		/*
+			// Exit if the generation has changed while waiting as there will be
+			// no further progress on the old generation.
+			if curr.Generation != pos.Generation {
+				return fmt.Errorf("generation changed")
+			}
+		*/
 
 		ready := true
 		if curr.Index < pos.Index {
@@ -932,12 +914,12 @@ func (r *Replica) waitForReplica(ctx context.Context, pos Pos) error {
 	}
 }
 
-// GenerationCreatedAt returns the earliest creation time of any snapshot.
+// CreatedAt returns the earliest creation time of any snapshot.
 // Returns zero time if no snapshots exist.
-func (r *Replica) GenerationCreatedAt(ctx context.Context, generation string) (time.Time, error) {
+func (r *Replica) CreatedAt(ctx context.Context) (time.Time, error) {
 	var min time.Time
 
-	itr, err := r.Client.Snapshots(ctx, generation)
+	itr, err := r.Client.Snapshots(ctx)
 	if err != nil {
 		return min, err
 	}
@@ -951,11 +933,11 @@ func (r *Replica) GenerationCreatedAt(ctx context.Context, generation string) (t
 	return min, itr.Close()
 }
 
-// GenerationTimeBounds returns the creation time & last updated time of a generation.
+// TimeBounds returns the creation time & last updated time.
 // Returns zero time if no snapshots or WAL segments exist.
-func (r *Replica) GenerationTimeBounds(ctx context.Context, generation string) (createdAt, updatedAt time.Time, err error) {
+func (r *Replica) TimeBounds(ctx context.Context) (createdAt, updatedAt time.Time, err error) {
 	// Iterate over snapshots.
-	sitr, err := r.Client.Snapshots(ctx, generation)
+	sitr, err := r.Client.Snapshots(ctx)
 	if err != nil {
 		return createdAt, updatedAt, err
 	}
@@ -982,7 +964,7 @@ func (r *Replica) GenerationTimeBounds(ctx context.Context, generation string) (
 	}
 
 	// Iterate over WAL segments.
-	witr, err := r.Client.WALSegments(ctx, generation)
+	witr, err := r.Client.WALSegments(ctx)
 	if err != nil {
 		return createdAt, updatedAt, err
 	}
@@ -1007,62 +989,34 @@ func (r *Replica) GenerationTimeBounds(ctx context.Context, generation string) (
 	return createdAt, updatedAt, nil
 }
 
-// CalcRestoreTarget returns a generation to restore from.
-func (r *Replica) CalcRestoreTarget(ctx context.Context, opt RestoreOptions) (generation string, updatedAt time.Time, err error) {
-	var target struct {
-		generation string
-		updatedAt  time.Time
-	}
-
-	generations, err := r.Client.Generations(ctx)
+// CalcRestoreTarget returns a target time restore from.
+func (r *Replica) CalcRestoreTarget(ctx context.Context, opt RestoreOptions) (updatedAt time.Time, err error) {
+	// Determine the replicated time bounds.
+	createdAt, updatedAt, err := r.TimeBounds(ctx)
 	if err != nil {
-		return "", time.Time{}, fmt.Errorf("cannot fetch generations: %w", err)
+		return time.Time{}, fmt.Errorf("created at: %w", err)
 	}
 
-	// Search generations for one that contains the requested timestamp.
-	for _, generation := range generations {
-		// Skip generation if it does not match filter.
-		if opt.Generation != "" && generation != opt.Generation {
-			continue
+	// Skip if it does not contain timestamp.
+	if !opt.Timestamp.IsZero() {
+		if opt.Timestamp.Before(createdAt) || opt.Timestamp.After(updatedAt) {
+			return time.Time{}, fmt.Errorf("timestamp does not exist")
 		}
-
-		// Determine the time bounds for the generation.
-		createdAt, updatedAt, err := r.GenerationTimeBounds(ctx, generation)
-		if err != nil {
-			return "", time.Time{}, fmt.Errorf("generation created at: %w", err)
-		}
-
-		// Skip if it does not contain timestamp.
-		if !opt.Timestamp.IsZero() {
-			if opt.Timestamp.Before(createdAt) || opt.Timestamp.After(updatedAt) {
-				continue
-			}
-		}
-
-		// Use the latest replica if we have multiple candidates.
-		if !updatedAt.After(target.updatedAt) {
-			continue
-		}
-
-		target.generation = generation
-		target.updatedAt = updatedAt
 	}
 
-	return target.generation, target.updatedAt, nil
+	return updatedAt, nil
 }
 
 // Replica restores the database from a replica based on the options given.
 // This method will restore into opt.OutputPath, if specified, or into the
 // DB's original database path. It can optionally restore from a specific
-// replica or generation or it will automatically choose the best one. Finally,
+// replica or it will automatically choose the best one. Finally,
 // a timestamp can be specified to restore the database to a specific
 // point-in-time.
 func (r *Replica) Restore(ctx context.Context, opt RestoreOptions) (err error) {
 	// Validate options.
 	if opt.OutputPath == "" {
 		return fmt.Errorf("output path required")
-	} else if opt.Generation == "" && opt.Index != math.MaxInt32 {
-		return fmt.Errorf("must specify generation when restoring to index")
 	} else if opt.Index != math.MaxInt32 && !opt.Timestamp.IsZero() {
 		return fmt.Errorf("cannot specify index & timestamp to restore")
 	}
@@ -1077,17 +1031,17 @@ func (r *Replica) Restore(ctx context.Context, opt RestoreOptions) (err error) {
 	// Find lastest snapshot that occurs before timestamp or index.
 	var minWALIndex int
 	if opt.Index < math.MaxInt32 {
-		if minWALIndex, err = r.SnapshotIndexByIndex(ctx, opt.Generation, opt.Index); err != nil {
+		if minWALIndex, err = r.SnapshotIndexByIndex(ctx, opt.Index); err != nil {
 			return fmt.Errorf("cannot find snapshot index: %w", err)
 		}
 	} else {
-		if minWALIndex, err = r.SnapshotIndexAt(ctx, opt.Generation, opt.Timestamp); err != nil {
+		if minWALIndex, err = r.SnapshotIndexAt(ctx, opt.Timestamp); err != nil {
 			return fmt.Errorf("cannot find snapshot index by timestamp: %w", err)
 		}
 	}
 
 	// Compute list of offsets for each WAL index.
-	walSegmentMap, err := r.walSegmentMap(ctx, opt.Generation, minWALIndex, opt.Index, opt.Timestamp)
+	walSegmentMap, err := r.walSegmentMap(ctx, minWALIndex, opt.Index, opt.Timestamp)
 	if err != nil {
 		return fmt.Errorf("cannot find max wal index for restore: %w", err)
 	}
@@ -1102,19 +1056,19 @@ func (r *Replica) Restore(ctx context.Context, opt RestoreOptions) (err error) {
 
 	// Ensure that we found the specific index, if one was specified.
 	if opt.Index != math.MaxInt32 && opt.Index != maxWALIndex {
-		return fmt.Errorf("unable to locate index %d in generation %q, highest index was %d", opt.Index, opt.Generation, maxWALIndex)
+		return fmt.Errorf("unable to locate index %d, highest index was %d", opt.Index, maxWALIndex)
 	}
 
 	// If no WAL files were found, mark this as a snapshot-only restore.
 	snapshotOnly := maxWALIndex == -1
 
 	// Initialize starting position.
-	pos := Pos{Generation: opt.Generation, Index: minWALIndex}
+	pos := Pos{Index: minWALIndex}
 	tmpPath := opt.OutputPath + ".tmp"
 
 	// Copy snapshot to output path.
-	r.Logger().Info("restoring snapshot", "generation", opt.Generation, "index", minWALIndex, "path", tmpPath)
-	if err := r.restoreSnapshot(ctx, pos.Generation, pos.Index, tmpPath); err != nil {
+	r.Logger().Info("restoring snapshot", "index", minWALIndex, "path", tmpPath)
+	if err := r.restoreSnapshot(ctx, pos.Index, tmpPath); err != nil {
 		return fmt.Errorf("cannot restore snapshot: %w", err)
 	}
 
@@ -1125,14 +1079,14 @@ func (r *Replica) Restore(ctx context.Context, opt RestoreOptions) (err error) {
 	}
 
 	// Begin processing WAL files.
-	r.Logger().Info("restoring wal files", "generation", opt.Generation, "index_min", minWALIndex, "index_max", maxWALIndex)
+	r.Logger().Info("restoring wal files", "index_min", minWALIndex, "index_max", maxWALIndex)
 
 	// Fill input channel with all WAL indexes to be loaded in order.
 	// Verify every index has at least one offset.
 	ch := make(chan int, maxWALIndex-minWALIndex+1)
 	for index := minWALIndex; index <= maxWALIndex; index++ {
 		if len(walSegmentMap[index]) == 0 {
-			return fmt.Errorf("missing WAL index: %s/%08x", opt.Generation, index)
+			return fmt.Errorf("missing WAL index: %08x", index)
 		}
 		ch <- index
 	}
@@ -1165,9 +1119,9 @@ func (r *Replica) Restore(ctx context.Context, opt RestoreOptions) (err error) {
 
 					startTime := time.Now()
 
-					err := r.downloadWAL(ctx, opt.Generation, index, walSegmentMap[index], tmpPath)
+					err := r.downloadWAL(ctx, index, walSegmentMap[index], tmpPath)
 					if err != nil {
-						err = fmt.Errorf("cannot download wal %s/%08x: %w", opt.Generation, index, err)
+						err = fmt.Errorf("cannot download wal %08x: %w", index, err)
 					}
 
 					// Mark index as ready-to-apply and notify applying code.
@@ -1182,7 +1136,7 @@ func (r *Replica) Restore(ctx context.Context, opt RestoreOptions) (err error) {
 					}
 
 					r.Logger().Info("downloaded wal",
-						"generation", opt.Generation, "index", index,
+						"index", index,
 						"elapsed", time.Since(startTime).String(),
 					)
 				}
@@ -1210,7 +1164,7 @@ func (r *Replica) Restore(ctx context.Context, opt RestoreOptions) (err error) {
 		if err = applyWAL(ctx, index, tmpPath); err != nil {
 			return fmt.Errorf("cannot apply wal: %w", err)
 		}
-		r.Logger().Info("applied wal", "generation", opt.Generation, "index", index, "elapsed", time.Since(startTime).String())
+		r.Logger().Info("applied wal", "index", index, "elapsed", time.Since(startTime).String())
 	}
 
 	// Ensure all goroutines finish. All errors should have been handled during
@@ -1233,10 +1187,10 @@ type walRestoreState struct {
 	err   error
 }
 
-// SnapshotIndexAt returns the highest index for a snapshot within a generation
-// that occurs before timestamp. If timestamp is zero, returns the latest snapshot.
-func (r *Replica) SnapshotIndexAt(ctx context.Context, generation string, timestamp time.Time) (int, error) {
-	itr, err := r.Client.Snapshots(ctx, generation)
+// SnapshotIndexAt returns the highest index for a snapshot that occurs before timestamp.
+// If timestamp is zero, returns the latest snapshot.
+func (r *Replica) SnapshotIndexAt(ctx context.Context, timestamp time.Time) (int, error) {
+	itr, err := r.Client.Snapshots(ctx)
 	if err != nil {
 		return 0, err
 	}
@@ -1263,10 +1217,10 @@ func (r *Replica) SnapshotIndexAt(ctx context.Context, generation string, timest
 	return snapshotIndex, nil
 }
 
-// SnapshotIndexbyIndex returns the highest index for a snapshot within a generation
-// that occurs before a given index. If index is MaxInt32, returns the latest snapshot.
-func (r *Replica) SnapshotIndexByIndex(ctx context.Context, generation string, index int) (int, error) {
-	itr, err := r.Client.Snapshots(ctx, generation)
+// SnapshotIndexbyIndex returns the highest index for a snapshot that occurs
+// before a given index. If index is MaxInt32, returns the latest snapshot.
+func (r *Replica) SnapshotIndexByIndex(ctx context.Context, index int) (int, error) {
+	itr, err := r.Client.Snapshots(ctx)
 	if err != nil {
 		return 0, err
 	}
@@ -1295,8 +1249,8 @@ func (r *Replica) SnapshotIndexByIndex(ctx context.Context, generation string, i
 
 // walSegmentMap returns a map of WAL indices to their segments.
 // Filters by a max timestamp or a max index.
-func (r *Replica) walSegmentMap(ctx context.Context, generation string, minIndex, maxIndex int, maxTimestamp time.Time) (map[int][]int64, error) {
-	itr, err := r.Client.WALSegments(ctx, generation)
+func (r *Replica) walSegmentMap(ctx context.Context, minIndex, maxIndex int, maxTimestamp time.Time) (map[int][]int64, error) {
+	itr, err := r.Client.WALSegments(ctx)
 	if err != nil {
 		return nil, err
 	}
@@ -1323,9 +1277,9 @@ func (r *Replica) walSegmentMap(ctx context.Context, generation string, minIndex
 		// Verify offsets are added in order.
 		offsets := m[info.Index]
 		if len(offsets) == 0 && info.Offset != 0 {
-			return nil, fmt.Errorf("missing initial wal segment: generation=%s index=%08x offset=%d", generation, info.Index, info.Offset)
+			return nil, fmt.Errorf("missing initial wal segment: index=%08x offset=%d", info.Index, info.Offset)
 		} else if len(offsets) > 0 && offsets[len(offsets)-1] >= info.Offset {
-			return nil, fmt.Errorf("wal segments out of order: generation=%s index=%08x offsets=(%d,%d)", generation, info.Index, offsets[len(offsets)-1], info.Offset)
+			return nil, fmt.Errorf("wal segments out of order: index=%08x offsets=(%d,%d)", info.Index, offsets[len(offsets)-1], info.Offset)
 		}
 
 		// Append to the end of the WAL file.
@@ -1335,7 +1289,7 @@ func (r *Replica) walSegmentMap(ctx context.Context, generation string, minIndex
 }
 
 // restoreSnapshot copies a snapshot from the replica to a file.
-func (r *Replica) restoreSnapshot(ctx context.Context, generation string, index int, filename string) error {
+func (r *Replica) restoreSnapshot(ctx context.Context, index int, filename string) error {
 	// Determine the user/group & mode based on the DB, if available.
 	var fileInfo, dirInfo os.FileInfo
 	if db := r.DB(); db != nil {
@@ -1352,7 +1306,7 @@ func (r *Replica) restoreSnapshot(ctx context.Context, generation string, index
 	}
 	defer f.Close()
 
-	rd, err := r.Client.SnapshotReader(ctx, generation, index)
+	rd, err := r.Client.SnapshotReader(ctx, index)
 	if err != nil {
 		return err
 	}
@@ -1378,7 +1332,7 @@ func (r *Replica) restoreSnapshot(ctx context.Context, generation string, index
 // downloadWAL copies a WAL file from the replica to a local copy next to the DB.
 // The WAL is later applied by applyWAL(). This function can be run in parallel
 // to download multiple WAL files simultaneously.
-func (r *Replica) downloadWAL(ctx context.Context, generation string, index int, offsets []int64, dbPath string) (err error) {
+func (r *Replica) downloadWAL(ctx context.Context, index int, offsets []int64, dbPath string) (err error) {
 	// Determine the user/group & mode based on the DB, if available.
 	var fileInfo os.FileInfo
 	if db := r.DB(); db != nil {
@@ -1388,7 +1342,7 @@ func (r *Replica) downloadWAL(ctx context.Context, generation string, index int,
 	// Open readers for every segment in the WAL file, in order.
 	var readers []io.Reader
 	for _, offset := range offsets {
-		rd, err := r.Client.WALSegmentReader(ctx, Pos{Generation: generation, Index: index, Offset: offset})
+		rd, err := r.Client.WALSegmentReader(ctx, Pos{Index: index, Offset: offset})
 		if err != nil {
 			return err
 		}
diff --git a/replica_client.go b/replica_client.go
index 3d3f508..c3c1025 100644
--- a/replica_client.go
+++ b/replica_client.go
@@ -10,29 +10,25 @@ type ReplicaClient interface {
 	// Returns the type of client.
 	Type() string
 
-	// Returns a list of available generations. Order is undefined.
-	Generations(ctx context.Context) ([]string, error)
+	// Deletes all snapshots & WAL segments.
+	DeleteAll(ctx context.Context) error
 
-	// Deletes all snapshots & WAL segments within a generation.
-	DeleteGeneration(ctx context.Context, generation string) error
+	// Returns an iterator of all snapshots on the replica. Order is undefined.
+	Snapshots(ctx context.Context) (SnapshotIterator, error)
 
-	// Returns an iterator of all snapshots within a generation on the replica. Order is undefined.
-	Snapshots(ctx context.Context, generation string) (SnapshotIterator, error)
+	// Writes LZ4 compressed snapshot data to the replica at a given index.
+	// Returns metadata for the snapshot.
+	WriteSnapshot(ctx context.Context, index int, r io.Reader) (SnapshotInfo, error)
 
-	// Writes LZ4 compressed snapshot data to the replica at a given index
-	// within a generation. Returns metadata for the snapshot.
-	WriteSnapshot(ctx context.Context, generation string, index int, r io.Reader) (SnapshotInfo, error)
-
-	// Deletes a snapshot with the given generation & index.
-	DeleteSnapshot(ctx context.Context, generation string, index int) error
+	// Deletes a snapshot with the given index.
+	DeleteSnapshot(ctx context.Context, index int) error
 
 	// Returns a reader that contains LZ4 compressed snapshot data for a
-	// given index within a generation. Returns an os.ErrNotFound error if
-	// the snapshot does not exist.
-	SnapshotReader(ctx context.Context, generation string, index int) (io.ReadCloser, error)
+	// given index. Returns an os.ErrNotFound error if the snapshot does not exist.
+	SnapshotReader(ctx context.Context, index int) (io.ReadCloser, error)
 
-	// Returns an iterator of all WAL segments within a generation on the replica. Order is undefined.
-	WALSegments(ctx context.Context, generation string) (WALSegmentIterator, error)
+	// Returns an iterator of all WAL segments on the replica. Order is undefined.
+	WALSegments(ctx context.Context) (WALSegmentIterator, error)
 
 	// Writes an LZ4 compressed WAL segment at a given position.
 	// Returns metadata for the written segment.
@@ -42,7 +38,6 @@ type ReplicaClient interface {
 	DeleteWALSegments(ctx context.Context, a []Pos) error
 
 	// Returns a reader that contains an LZ4 compressed WAL segment at a given
-	// index/offset within a generation. Returns an os.ErrNotFound error if the
-	// WAL segment does not exist.
+	// index/offset. Returns an os.ErrNotFound error if the WAL segment does not exist.
 	WALSegmentReader(ctx context.Context, pos Pos) (io.ReadCloser, error)
 }
diff --git a/s3/replica_client.go b/s3/replica_client.go
index 7b51375..a1cc67e 100644
--- a/s3/replica_client.go
+++ b/s3/replica_client.go
@@ -146,51 +146,17 @@ func (c *ReplicaClient) findBucketRegion(ctx context.Context, bucket string) (st
 	return DefaultRegion, nil
 }
 
-// Generations returns a list of available generation names.
-func (c *ReplicaClient) Generations(ctx context.Context) ([]string, error) {
-	if err := c.Init(ctx); err != nil {
-		return nil, err
-	}
-
-	var generations []string
-	if err := c.s3.ListObjectsPagesWithContext(ctx, &s3.ListObjectsInput{
-		Bucket:    aws.String(c.Bucket),
-		Prefix:    aws.String(litestream.GenerationsPath(c.Path) + "/"),
-		Delimiter: aws.String("/"),
-	}, func(page *s3.ListObjectsOutput, lastPage bool) bool {
-		internal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, "LIST").Inc()
-
-		for _, prefix := range page.CommonPrefixes {
-			name := path.Base(aws.StringValue(prefix.Prefix))
-			if !litestream.IsGenerationName(name) {
-				continue
-			}
-			generations = append(generations, name)
-		}
-		return true
-	}); err != nil {
-		return nil, err
-	}
-
-	return generations, nil
-}
-
-// DeleteGeneration deletes all snapshots & WAL segments within a generation.
-func (c *ReplicaClient) DeleteGeneration(ctx context.Context, generation string) error {
+// DeleteAll deletes all snapshots & WAL segments.
+func (c *ReplicaClient) DeleteAll(ctx context.Context) error {
 	if err := c.Init(ctx); err != nil {
 		return err
 	}
 
-	dir, err := litestream.GenerationPath(c.Path, generation)
-	if err != nil {
-		return fmt.Errorf("cannot determine generation path: %w", err)
-	}
-
-	// Collect all files for the generation.
+	// Collect all files.
 	var objIDs []*s3.ObjectIdentifier
 	if err := c.s3.ListObjectsPagesWithContext(ctx, &s3.ListObjectsInput{
 		Bucket: aws.String(c.Bucket),
-		Prefix: aws.String(dir),
+		Prefix: aws.String(c.Path),
 	}, func(page *s3.ListObjectsOutput, lastPage bool) bool {
 		internal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, "LIST").Inc()
 
@@ -224,26 +190,26 @@ func (c *ReplicaClient) DeleteGeneration(ctx context.Context, generation string)
 		objIDs = objIDs[n:]
 	}
 
-	// log.Printf("%s(%s): retainer: deleting generation: %s", r.db.Path(), r.Name(), generation)
+	// log.Printf("%s(%s): retainer: deleting: %s", r.db.Path(), r.Name())
 
 	return nil
 }
 
-// Snapshots returns an iterator over all available snapshots for a generation.
-func (c *ReplicaClient) Snapshots(ctx context.Context, generation string) (litestream.SnapshotIterator, error) {
+// Snapshots returns an iterator over all available snapshots.
+func (c *ReplicaClient) Snapshots(ctx context.Context) (litestream.SnapshotIterator, error) {
 	if err := c.Init(ctx); err != nil {
 		return nil, err
 	}
-	return newSnapshotIterator(ctx, c, generation), nil
+	return newSnapshotIterator(ctx, c), nil
 }
 
 // WriteSnapshot writes LZ4 compressed data from rd into a file on disk.
-func (c *ReplicaClient) WriteSnapshot(ctx context.Context, generation string, index int, rd io.Reader) (info litestream.SnapshotInfo, err error) {
+func (c *ReplicaClient) WriteSnapshot(ctx context.Context, index int, rd io.Reader) (info litestream.SnapshotInfo, err error) {
 	if err := c.Init(ctx); err != nil {
 		return info, err
 	}
 
-	key, err := litestream.SnapshotPath(c.Path, generation, index)
+	key, err := litestream.SnapshotPath(c.Path, index)
 	if err != nil {
 		return info, fmt.Errorf("cannot determine snapshot path: %w", err)
 	}
@@ -261,23 +227,22 @@ func (c *ReplicaClient) WriteSnapshot(ctx context.Context, generation string, in
 	internal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, "PUT").Inc()
 	internal.OperationBytesCounterVec.WithLabelValues(ReplicaClientType, "PUT").Add(float64(rc.N()))
 
-	// log.Printf("%s(%s): snapshot: creating %s/%08x t=%s", r.db.Path(), r.Name(), generation, index, time.Since(startTime).Truncate(time.Millisecond))
+	// log.Printf("%s(%s): snapshot: creating %08x t=%s", r.db.Path(), r.Name(),  index, time.Since(startTime).Truncate(time.Millisecond))
 
 	return litestream.SnapshotInfo{
-		Generation: generation,
-		Index:      index,
-		Size:       rc.N(),
-		CreatedAt:  startTime.UTC(),
+		Index:     index,
+		Size:      rc.N(),
+		CreatedAt: startTime.UTC(),
 	}, nil
 }
 
-// SnapshotReader returns a reader for snapshot data at the given generation/index.
-func (c *ReplicaClient) SnapshotReader(ctx context.Context, generation string, index int) (io.ReadCloser, error) {
+// SnapshotReader returns a reader for snapshot data at the given index.
+func (c *ReplicaClient) SnapshotReader(ctx context.Context, index int) (io.ReadCloser, error) {
 	if err := c.Init(ctx); err != nil {
 		return nil, err
 	}
 
-	key, err := litestream.SnapshotPath(c.Path, generation, index)
+	key, err := litestream.SnapshotPath(c.Path, index)
 	if err != nil {
 		return nil, fmt.Errorf("cannot determine snapshot path: %w", err)
 	}
@@ -297,13 +262,13 @@ func (c *ReplicaClient) SnapshotReader(ctx context.Context, generation string, i
 	return out.Body, nil
 }
 
-// DeleteSnapshot deletes a snapshot with the given generation & index.
-func (c *ReplicaClient) DeleteSnapshot(ctx context.Context, generation string, index int) error {
+// DeleteSnapshot deletes a snapshot with the given index.
+func (c *ReplicaClient) DeleteSnapshot(ctx context.Context, index int) error {
 	if err := c.Init(ctx); err != nil {
 		return err
 	}
 
-	key, err := litestream.SnapshotPath(c.Path, generation, index)
+	key, err := litestream.SnapshotPath(c.Path, index)
 	if err != nil {
 		return fmt.Errorf("cannot determine snapshot path: %w", err)
 	}
@@ -323,12 +288,12 @@ func (c *ReplicaClient) DeleteSnapshot(ctx context.Context, generation string, i
 	return nil
 }
 
-// WALSegments returns an iterator over all available WAL files for a generation.
-func (c *ReplicaClient) WALSegments(ctx context.Context, generation string) (litestream.WALSegmentIterator, error) {
+// WALSegments returns an iterator over all available WAL files.
+func (c *ReplicaClient) WALSegments(ctx context.Context) (litestream.WALSegmentIterator, error) {
 	if err := c.Init(ctx); err != nil {
 		return nil, err
 	}
-	return newWALSegmentIterator(ctx, c, generation), nil
+	return newWALSegmentIterator(ctx, c), nil
 }
 
 // WriteWALSegment writes LZ4 compressed data from rd into a file on disk.
@@ -337,7 +302,7 @@ func (c *ReplicaClient) WriteWALSegment(ctx context.Context, pos litestream.Pos,
 		return info, err
 	}
 
-	key, err := litestream.WALSegmentPath(c.Path, pos.Generation, pos.Index, pos.Offset)
+	key, err := litestream.WALSegmentPath(c.Path, pos.Index, pos.Offset)
 	if err != nil {
 		return info, fmt.Errorf("cannot determine wal segment path: %w", err)
 	}
@@ -356,11 +321,10 @@ func (c *ReplicaClient) WriteWALSegment(ctx context.Context, pos litestream.Pos,
 	internal.OperationBytesCounterVec.WithLabelValues(ReplicaClientType, "PUT").Add(float64(rc.N()))
 
 	return litestream.WALSegmentInfo{
-		Generation: pos.Generation,
-		Index:      pos.Index,
-		Offset:     pos.Offset,
-		Size:       rc.N(),
-		CreatedAt:  startTime.UTC(),
+		Index:     pos.Index,
+		Offset:    pos.Offset,
+		Size:      rc.N(),
+		CreatedAt: startTime.UTC(),
 	}, nil
 }
 
@@ -371,7 +335,7 @@ func (c *ReplicaClient) WALSegmentReader(ctx context.Context, pos litestream.Pos
 		return nil, err
 	}
 
-	key, err := litestream.WALSegmentPath(c.Path, pos.Generation, pos.Index, pos.Offset)
+	key, err := litestream.WALSegmentPath(c.Path, pos.Index, pos.Offset)
 	if err != nil {
 		return nil, fmt.Errorf("cannot determine wal segment path: %w", err)
 	}
@@ -406,7 +370,7 @@ func (c *ReplicaClient) DeleteWALSegments(ctx context.Context, a []litestream.Po
 
 		// Generate a batch of object IDs for deleting the WAL segments.
 		for i, pos := range a[:n] {
-			key, err := litestream.WALSegmentPath(c.Path, pos.Generation, pos.Index, pos.Offset)
+			key, err := litestream.WALSegmentPath(c.Path, pos.Index, pos.Offset)
 			if err != nil {
 				return fmt.Errorf("cannot determine wal segment path: %w", err)
 			}
@@ -432,61 +396,8 @@ func (c *ReplicaClient) DeleteWALSegments(ctx context.Context, a []litestream.Po
 	return nil
 }
 
-// DeleteAll deletes everything on the remote path. Mainly used for testing.
-func (c *ReplicaClient) DeleteAll(ctx context.Context) error {
-	if err := c.Init(ctx); err != nil {
-		return err
-	}
-
-	prefix := c.Path
-	if prefix != "" {
-		prefix += "/"
-	}
-
-	// Collect all files for the generation.
-	var objIDs []*s3.ObjectIdentifier
-	if err := c.s3.ListObjectsPagesWithContext(ctx, &s3.ListObjectsInput{
-		Bucket: aws.String(c.Bucket),
-		Prefix: aws.String(prefix),
-	}, func(page *s3.ListObjectsOutput, lastPage bool) bool {
-		internal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, "LIST").Inc()
-
-		for _, obj := range page.Contents {
-			objIDs = append(objIDs, &s3.ObjectIdentifier{Key: obj.Key})
-		}
-		return true
-	}); err != nil {
-		return err
-	}
-
-	// Delete all files in batches.
-	for len(objIDs) > 0 {
-		n := MaxKeys
-		if len(objIDs) < n {
-			n = len(objIDs)
-		}
-
-		out, err := c.s3.DeleteObjectsWithContext(ctx, &s3.DeleteObjectsInput{
-			Bucket: aws.String(c.Bucket),
-			Delete: &s3.Delete{Objects: objIDs[:n], Quiet: aws.Bool(true)},
-		})
-		if err != nil {
-			return err
-		}
-		if err := deleteOutputError(out); err != nil {
-			return err
-		}
-		internal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, "DELETE").Inc()
-
-		objIDs = objIDs[n:]
-	}
-
-	return nil
-}
-
 type snapshotIterator struct {
-	client     *ReplicaClient
-	generation string
+	client *ReplicaClient
 
 	ch     chan litestream.SnapshotInfo
 	g      errgroup.Group
@@ -497,11 +408,10 @@ type snapshotIterator struct {
 	err  error
 }
 
-func newSnapshotIterator(ctx context.Context, client *ReplicaClient, generation string) *snapshotIterator {
+func newSnapshotIterator(ctx context.Context, client *ReplicaClient) *snapshotIterator {
 	itr := &snapshotIterator{
-		client:     client,
-		generation: generation,
-		ch:         make(chan litestream.SnapshotInfo),
+		client: client,
+		ch:     make(chan litestream.SnapshotInfo),
 	}
 
 	itr.ctx, itr.cancel = context.WithCancel(ctx)
@@ -514,7 +424,7 @@ func newSnapshotIterator(ctx context.Context, client *ReplicaClient, generation
 func (itr *snapshotIterator) fetch() error {
 	defer close(itr.ch)
 
-	dir, err := litestream.SnapshotsPath(itr.client.Path, itr.generation)
+	dir, err := litestream.SnapshotsPath(itr.client.Path)
 	if err != nil {
 		return fmt.Errorf("cannot determine snapshots path: %w", err)
 	}
@@ -534,10 +444,9 @@ func (itr *snapshotIterator) fetch() error {
 			}
 
 			info := litestream.SnapshotInfo{
-				Generation: itr.generation,
-				Index:      index,
-				Size:       aws.Int64Value(obj.Size),
-				CreatedAt:  obj.LastModified.UTC(),
+				Index:     index,
+				Size:      aws.Int64Value(obj.Size),
+				CreatedAt: obj.LastModified.UTC(),
 			}
 
 			select {
@@ -588,8 +497,7 @@ func (itr *snapshotIterator) Snapshot() litestream.SnapshotInfo {
 }
 
 type walSegmentIterator struct {
-	client     *ReplicaClient
-	generation string
+	client *ReplicaClient
 
 	ch     chan litestream.WALSegmentInfo
 	g      errgroup.Group
@@ -600,11 +508,10 @@ type walSegmentIterator struct {
 	err  error
 }
 
-func newWALSegmentIterator(ctx context.Context, client *ReplicaClient, generation string) *walSegmentIterator {
+func newWALSegmentIterator(ctx context.Context, client *ReplicaClient) *walSegmentIterator {
 	itr := &walSegmentIterator{
-		client:     client,
-		generation: generation,
-		ch:         make(chan litestream.WALSegmentInfo),
+		client: client,
+		ch:     make(chan litestream.WALSegmentInfo),
 	}
 
 	itr.ctx, itr.cancel = context.WithCancel(ctx)
@@ -617,7 +524,7 @@ func newWALSegmentIterator(ctx context.Context, client *ReplicaClient, generatio
 func (itr *walSegmentIterator) fetch() error {
 	defer close(itr.ch)
 
-	dir, err := litestream.WALPath(itr.client.Path, itr.generation)
+	dir, err := litestream.WALPath(itr.client.Path)
 	if err != nil {
 		return fmt.Errorf("cannot determine wal path: %w", err)
 	}
@@ -637,11 +544,10 @@ func (itr *walSegmentIterator) fetch() error {
 			}
 
 			info := litestream.WALSegmentInfo{
-				Generation: itr.generation,
-				Index:      index,
-				Offset:     offset,
-				Size:       aws.Int64Value(obj.Size),
-				CreatedAt:  obj.LastModified.UTC(),
+				Index:     index,
+				Offset:    offset,
+				Size:      aws.Int64Value(obj.Size),
+				CreatedAt: obj.LastModified.UTC(),
 			}
 
 			select {
diff --git a/sftp/replica_client.go b/sftp/replica_client.go
index 1b8168c..a5d8518 100644
--- a/sftp/replica_client.go
+++ b/sftp/replica_client.go
@@ -111,40 +111,8 @@ func (c *ReplicaClient) Init(ctx context.Context) (_ *sftp.Client, err error) {
 	return c.sftpClient, nil
 }
 
-// Generations returns a list of available generation names.
-func (c *ReplicaClient) Generations(ctx context.Context) (_ []string, err error) {
-	defer func() { c.resetOnConnError(err) }()
-
-	sftpClient, err := c.Init(ctx)
-	if err != nil {
-		return nil, err
-	}
-
-	fis, err := sftpClient.ReadDir(litestream.GenerationsPath(c.Path))
-	if os.IsNotExist(err) {
-		return nil, nil
-	} else if err != nil {
-		return nil, err
-	}
-
-	var generations []string
-	for _, fi := range fis {
-		if !fi.IsDir() {
-			continue
-		}
-
-		name := path.Base(fi.Name())
-		if !litestream.IsGenerationName(name) {
-			continue
-		}
-		generations = append(generations, name)
-	}
-
-	return generations, nil
-}
-
-// DeleteGeneration deletes all snapshots & WAL segments within a generation.
-func (c *ReplicaClient) DeleteGeneration(ctx context.Context, generation string) (err error) {
+// DeleteAll deletes all snapshots & WAL segments.
+func (c *ReplicaClient) DeleteAll(ctx context.Context) (err error) {
 	defer func() { c.resetOnConnError(err) }()
 
 	sftpClient, err := c.Init(ctx)
@@ -152,13 +120,8 @@ func (c *ReplicaClient) DeleteGeneration(ctx context.Context, generation string)
 		return err
 	}
 
-	dir, err := litestream.GenerationPath(c.Path, generation)
-	if err != nil {
-		return fmt.Errorf("cannot determine generation path: %w", err)
-	}
-
 	var dirs []string
-	walker := sftpClient.Walk(dir)
+	walker := sftpClient.Walk(c.Path)
 	for walker.Step() {
 		if err := walker.Err(); err != nil {
 			return fmt.Errorf("cannot walk path %q: %w", walker.Path(), err)
@@ -183,13 +146,13 @@ func (c *ReplicaClient) DeleteGeneration(ctx context.Context, generation string)
 		}
 	}
 
-	// log.Printf("%s(%s): retainer: deleting generation: %s", r.db.Path(), r.Name(), generation)
+	// log.Printf("%s(%s): retainer: deleting all", r.db.Path(), r.Name())
 
 	return nil
 }
 
-// Snapshots returns an iterator over all available snapshots for a generation.
-func (c *ReplicaClient) Snapshots(ctx context.Context, generation string) (_ litestream.SnapshotIterator, err error) {
+// Snapshots returns an iterator over all available snapshots.
+func (c *ReplicaClient) Snapshots(ctx context.Context) (_ litestream.SnapshotIterator, err error) {
 	defer func() { c.resetOnConnError(err) }()
 
 	sftpClient, err := c.Init(ctx)
@@ -197,7 +160,7 @@ func (c *ReplicaClient) Snapshots(ctx context.Context, generation string) (_ lit
 		return nil, err
 	}
 
-	dir, err := litestream.SnapshotsPath(c.Path, generation)
+	dir, err := litestream.SnapshotsPath(c.Path)
 	if err != nil {
 		return nil, fmt.Errorf("cannot determine snapshots path: %w", err)
 	}
@@ -219,10 +182,9 @@ func (c *ReplicaClient) Snapshots(ctx context.Context, generation string) (_ lit
 		}
 
 		infos = append(infos, litestream.SnapshotInfo{
-			Generation: generation,
-			Index:      index,
-			Size:       fi.Size(),
-			CreatedAt:  fi.ModTime().UTC(),
+			Index:     index,
+			Size:      fi.Size(),
+			CreatedAt: fi.ModTime().UTC(),
 		})
 	}
 
@@ -230,7 +192,7 @@ func (c *ReplicaClient) Snapshots(ctx context.Context, generation string) (_ lit
 }
 
 // WriteSnapshot writes LZ4 compressed data from rd to the object storage.
-func (c *ReplicaClient) WriteSnapshot(ctx context.Context, generation string, index int, rd io.Reader) (info litestream.SnapshotInfo, err error) {
+func (c *ReplicaClient) WriteSnapshot(ctx context.Context, index int, rd io.Reader) (info litestream.SnapshotInfo, err error) {
 	defer func() { c.resetOnConnError(err) }()
 
 	sftpClient, err := c.Init(ctx)
@@ -238,7 +200,7 @@ func (c *ReplicaClient) WriteSnapshot(ctx context.Context, generation string, in
 		return info, err
 	}
 
-	filename, err := litestream.SnapshotPath(c.Path, generation, index)
+	filename, err := litestream.SnapshotPath(c.Path, index)
 	if err != nil {
 		return info, fmt.Errorf("cannot determine snapshot path: %w", err)
 	}
@@ -264,18 +226,17 @@ func (c *ReplicaClient) WriteSnapshot(ctx context.Context, generation string, in
 	internal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, "PUT").Inc()
 	internal.OperationBytesCounterVec.WithLabelValues(ReplicaClientType, "PUT").Add(float64(n))
 
-	// log.Printf("%s(%s): snapshot: creating %s/%08x t=%s", r.db.Path(), r.Name(), generation, index, time.Since(startTime).Truncate(time.Millisecond))
+	// log.Printf("%s(%s): snapshot: creating %08x t=%s", r.db.Path(), r.Name(), index, time.Since(startTime).Truncate(time.Millisecond))
 
 	return litestream.SnapshotInfo{
-		Generation: generation,
-		Index:      index,
-		Size:       n,
-		CreatedAt:  startTime.UTC(),
+		Index:     index,
+		Size:      n,
+		CreatedAt: startTime.UTC(),
 	}, nil
 }
 
-// SnapshotReader returns a reader for snapshot data at the given generation/index.
-func (c *ReplicaClient) SnapshotReader(ctx context.Context, generation string, index int) (_ io.ReadCloser, err error) {
+// SnapshotReader returns a reader for snapshot data at the given index.
+func (c *ReplicaClient) SnapshotReader(ctx context.Context, index int) (_ io.ReadCloser, err error) {
 	defer func() { c.resetOnConnError(err) }()
 
 	sftpClient, err := c.Init(ctx)
@@ -283,7 +244,7 @@ func (c *ReplicaClient) SnapshotReader(ctx context.Context, generation string, i
 		return nil, err
 	}
 
-	filename, err := litestream.SnapshotPath(c.Path, generation, index)
+	filename, err := litestream.SnapshotPath(c.Path, index)
 	if err != nil {
 		return nil, fmt.Errorf("cannot determine snapshot path: %w", err)
 	}
@@ -298,8 +259,8 @@ func (c *ReplicaClient) SnapshotReader(ctx context.Context, generation string, i
 	return f, nil
 }
 
-// DeleteSnapshot deletes a snapshot with the given generation & index.
-func (c *ReplicaClient) DeleteSnapshot(ctx context.Context, generation string, index int) (err error) {
+// DeleteSnapshot deletes a snapshot with the given index.
+func (c *ReplicaClient) DeleteSnapshot(ctx context.Context, index int) (err error) {
 	defer func() { c.resetOnConnError(err) }()
 
 	sftpClient, err := c.Init(ctx)
@@ -307,7 +268,7 @@ func (c *ReplicaClient) DeleteSnapshot(ctx context.Context, generation string, i
 		return err
 	}
 
-	filename, err := litestream.SnapshotPath(c.Path, generation, index)
+	filename, err := litestream.SnapshotPath(c.Path, index)
 	if err != nil {
 		return fmt.Errorf("cannot determine snapshot path: %w", err)
 	}
@@ -320,8 +281,8 @@ func (c *ReplicaClient) DeleteSnapshot(ctx context.Context, generation string, i
 	return nil
 }
 
-// WALSegments returns an iterator over all available WAL files for a generation.
-func (c *ReplicaClient) WALSegments(ctx context.Context, generation string) (_ litestream.WALSegmentIterator, err error) {
+// WALSegments returns an iterator over all available WAL files.
+func (c *ReplicaClient) WALSegments(ctx context.Context) (_ litestream.WALSegmentIterator, err error) {
 	defer func() { c.resetOnConnError(err) }()
 
 	sftpClient, err := c.Init(ctx)
@@ -329,7 +290,7 @@ func (c *ReplicaClient) WALSegments(ctx context.Context, generation string) (_ l
 		return nil, err
 	}
 
-	dir, err := litestream.WALPath(c.Path, generation)
+	dir, err := litestream.WALPath(c.Path)
 	if err != nil {
 		return nil, fmt.Errorf("cannot determine wal path: %w", err)
 	}
@@ -350,11 +311,10 @@ func (c *ReplicaClient) WALSegments(ctx context.Context, generation string) (_ l
 		}
 
 		infos = append(infos, litestream.WALSegmentInfo{
-			Generation: generation,
-			Index:      index,
-			Offset:     offset,
-			Size:       fi.Size(),
-			CreatedAt:  fi.ModTime().UTC(),
+			Index:     index,
+			Offset:    offset,
+			Size:      fi.Size(),
+			CreatedAt: fi.ModTime().UTC(),
 		})
 	}
 
@@ -370,7 +330,7 @@ func (c *ReplicaClient) WriteWALSegment(ctx context.Context, pos litestream.Pos,
 		return info, err
 	}
 
-	filename, err := litestream.WALSegmentPath(c.Path, pos.Generation, pos.Index, pos.Offset)
+	filename, err := litestream.WALSegmentPath(c.Path, pos.Index, pos.Offset)
 	if err != nil {
 		return info, fmt.Errorf("cannot determine wal segment path: %w", err)
 	}
@@ -397,11 +357,10 @@ func (c *ReplicaClient) WriteWALSegment(ctx context.Context, pos litestream.Pos,
 	internal.OperationBytesCounterVec.WithLabelValues(ReplicaClientType, "PUT").Add(float64(n))
 
 	return litestream.WALSegmentInfo{
-		Generation: pos.Generation,
-		Index:      pos.Index,
-		Offset:     pos.Offset,
-		Size:       n,
-		CreatedAt:  startTime.UTC(),
+		Index:     pos.Index,
+		Offset:    pos.Offset,
+		Size:      n,
+		CreatedAt: startTime.UTC(),
 	}, nil
 }
 
@@ -415,7 +374,7 @@ func (c *ReplicaClient) WALSegmentReader(ctx context.Context, pos litestream.Pos
 		return nil, err
 	}
 
-	filename, err := litestream.WALSegmentPath(c.Path, pos.Generation, pos.Index, pos.Offset)
+	filename, err := litestream.WALSegmentPath(c.Path, pos.Index, pos.Offset)
 	if err != nil {
 		return nil, fmt.Errorf("cannot determine wal segment path: %w", err)
 	}
@@ -440,7 +399,7 @@ func (c *ReplicaClient) DeleteWALSegments(ctx context.Context, a []litestream.Po
 	}
 
 	for _, pos := range a {
-		filename, err := litestream.WALSegmentPath(c.Path, pos.Generation, pos.Index, pos.Offset)
+		filename, err := litestream.WALSegmentPath(c.Path, pos.Index, pos.Offset)
 		if err != nil {
 			return fmt.Errorf("cannot determine wal segment path: %w", err)
 		}
@@ -454,7 +413,7 @@ func (c *ReplicaClient) DeleteWALSegments(ctx context.Context, a []litestream.Po
 	return nil
 }
 
-// Cleanup deletes path & generations directories after empty.
+// Cleanup deletes path & directories after empty.
 func (c *ReplicaClient) Cleanup(ctx context.Context) (err error) {
 	defer func() { c.resetOnConnError(err) }()
 
@@ -463,9 +422,7 @@ func (c *ReplicaClient) Cleanup(ctx context.Context) (err error) {
 		return err
 	}
 
-	if err := sftpClient.RemoveDirectory(litestream.GenerationsPath(c.Path)); err != nil && !os.IsNotExist(err) {
-		return fmt.Errorf("cannot delete generations path: %w", err)
-	} else if err := sftpClient.RemoveDirectory(c.Path); err != nil && !os.IsNotExist(err) {
+	if err := sftpClient.RemoveDirectory(c.Path); err != nil && !os.IsNotExist(err) {
 		return fmt.Errorf("cannot delete path: %w", err)
 	}
 	return nil


Test patch:
diff --git a/cmd/litestream/generations.go b/cmd/litestream/generations.go
deleted file mode 100644
index aceaafe..0000000
--- a/cmd/litestream/generations.go
+++ /dev/null
@@ -1,171 +0,0 @@
-package main
-
-import (
-	"context"
-	"flag"
-	"fmt"
-	"os"
-	"sort"
-	"text/tabwriter"
-	"time"
-
-	"github.com/benbjohnson/litestream"
-)
-
-// GenerationsCommand represents a command to list all generations for a database.
-type GenerationsCommand struct{}
-
-// Run executes the command.
-func (c *GenerationsCommand) Run(ctx context.Context, args []string) (err error) {
-	fs := flag.NewFlagSet("litestream-generations", flag.ContinueOnError)
-	configPath, noExpandEnv := registerConfigFlag(fs)
-	replicaName := fs.String("replica", "", "replica name")
-	fs.Usage = c.Usage
-	if err := fs.Parse(args); err != nil {
-		return err
-	} else if fs.NArg() == 0 || fs.Arg(0) == "" {
-		return fmt.Errorf("database path or replica URL required")
-	} else if fs.NArg() > 1 {
-		return fmt.Errorf("too many arguments")
-	}
-
-	var db *litestream.DB
-	var r *litestream.Replica
-	dbUpdatedAt := time.Now()
-	if isURL(fs.Arg(0)) {
-		if *configPath != "" {
-			return fmt.Errorf("cannot specify a replica URL and the -config flag")
-		}
-		if r, err = NewReplicaFromConfig(&ReplicaConfig{URL: fs.Arg(0)}, nil); err != nil {
-			return err
-		}
-	} else {
-		if *configPath == "" {
-			*configPath = DefaultConfigPath()
-		}
-
-		// Load configuration.
-		config, err := ReadConfigFile(*configPath, !*noExpandEnv)
-		if err != nil {
-			return err
-		}
-
-		// Lookup database from configuration file by path.
-		if path, err := expand(fs.Arg(0)); err != nil {
-			return err
-		} else if dbc := config.DBConfig(path); dbc == nil {
-			return fmt.Errorf("database not found in config: %s", path)
-		} else if db, err = NewDBFromConfig(dbc); err != nil {
-			return err
-		}
-
-		// Filter by replica, if specified.
-		if *replicaName != "" {
-			if r = db.Replica(*replicaName); r == nil {
-				return fmt.Errorf("replica %q not found for database %q", *replicaName, db.Path())
-			}
-		}
-
-		// Determine last time database or WAL was updated.
-		if dbUpdatedAt, err = db.UpdatedAt(); err != nil {
-			return err
-		}
-	}
-
-	var replicas []*litestream.Replica
-	if r != nil {
-		replicas = []*litestream.Replica{r}
-	} else {
-		replicas = db.Replicas
-	}
-
-	// List each generation.
-	w := tabwriter.NewWriter(os.Stdout, 0, 8, 2, ' ', 0)
-	defer w.Flush()
-
-	fmt.Fprintln(w, "name\tgeneration\tlag\tstart\tend")
-	for _, r := range replicas {
-		generations, err := r.Client.Generations(ctx)
-		if err != nil {
-			r.Logger().Error("cannot list generations", "error", err)
-			continue
-		}
-
-		sort.Strings(generations)
-
-		// Iterate over each generation for the replica.
-		for _, generation := range generations {
-			createdAt, updatedAt, err := r.GenerationTimeBounds(ctx, generation)
-			if err != nil {
-				r.Logger().Error("cannot determine generation time bounds", "error", err)
-				continue
-			}
-
-			fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n",
-				r.Name(),
-				generation,
-				truncateDuration(dbUpdatedAt.Sub(updatedAt)).String(),
-				createdAt.Format(time.RFC3339),
-				updatedAt.Format(time.RFC3339),
-			)
-		}
-	}
-
-	return nil
-}
-
-// Usage prints the help message to STDOUT.
-func (c *GenerationsCommand) Usage() {
-	fmt.Printf(`
-The generations command lists all generations for a database or replica. It also
-lists stats about their lag behind the primary database and the time range they
-cover.
-
-Usage:
-
-	litestream generations [arguments] DB_PATH
-
-	litestream generations [arguments] REPLICA_URL
-
-Arguments:
-
-	-config PATH
-	    Specifies the configuration file.
-	    Defaults to %s
-
-	-no-expand-env
-	    Disables environment variable expansion in configuration file.
-
-	-replica NAME
-	    Optional, filters by replica.
-
-`[1:],
-		DefaultConfigPath(),
-	)
-}
-
-func truncateDuration(d time.Duration) time.Duration {
-	if d < 0 {
-		if d < -10*time.Second {
-			return d.Truncate(time.Second)
-		} else if d < -time.Second {
-			return d.Truncate(time.Second / 10)
-		} else if d < -time.Millisecond {
-			return d.Truncate(time.Millisecond)
-		} else if d < -time.Microsecond {
-			return d.Truncate(time.Microsecond)
-		}
-		return d
-	}
-
-	if d > 10*time.Second {
-		return d.Truncate(time.Second)
-	} else if d > time.Second {
-		return d.Truncate(time.Second / 10)
-	} else if d > time.Millisecond {
-		return d.Truncate(time.Millisecond)
-	} else if d > time.Microsecond {
-		return d.Truncate(time.Microsecond)
-	}
-	return d
-}
diff --git a/cmd/litestream/main.go b/cmd/litestream/main.go
index 0115e55..084011c 100644
--- a/cmd/litestream/main.go
+++ b/cmd/litestream/main.go
@@ -74,8 +74,6 @@ func (m *Main) Run(ctx context.Context, args []string) (err error) {
 	switch cmd {
 	case "databases":
 		return (&DatabasesCommand{}).Run(ctx, args)
-	case "generations":
-		return (&GenerationsCommand{}).Run(ctx, args)
 	case "replicate":
 		c := NewReplicateCommand()
 		if err := c.ParseFlags(ctx, args); err != nil {
diff --git a/cmd/litestream/restore.go b/cmd/litestream/restore.go
index d2f0507..667d08d 100644
--- a/cmd/litestream/restore.go
+++ b/cmd/litestream/restore.go
@@ -24,7 +24,6 @@ func (c *RestoreCommand) Run(ctx context.Context, args []string) (err error) {
 	configPath, noExpandEnv := registerConfigFlag(fs)
 	fs.StringVar(&opt.OutputPath, "o", "", "output path")
 	fs.StringVar(&opt.ReplicaName, "replica", "", "replica name")
-	fs.StringVar(&opt.Generation, "generation", "", "generation name")
 	fs.Var((*indexVar)(&opt.Index), "index", "wal index")
 	fs.IntVar(&opt.Parallelism, "parallelism", opt.Parallelism, "parallelism")
 	ifDBNotExists := fs.Bool("if-db-not-exists", false, "")
@@ -46,7 +45,7 @@ func (c *RestoreCommand) Run(ctx context.Context, args []string) (err error) {
 		}
 	}
 
-	// Determine replica & generation to restore from.
+	// Determine replica to restore from.
 	var r *litestream.Replica
 	if isURL(fs.Arg(0)) {
 		if *configPath != "" {
@@ -72,13 +71,11 @@ func (c *RestoreCommand) Run(ctx context.Context, args []string) (err error) {
 
 	// Return an error if no matching targets found.
 	// If optional flag set, return success. Useful for automated recovery.
-	if opt.Generation == "" {
-		if *ifReplicaExists {
-			slog.Info("no matching backups found")
-			return nil
-		}
-		return fmt.Errorf("no matching backups found")
+	if *ifReplicaExists {
+		slog.Info("no matching backups found")
+		return nil
 	}
+	return fmt.Errorf("no matching backups found")
 
 	return r.Restore(ctx, opt)
 }
@@ -102,7 +99,7 @@ func (c *RestoreCommand) loadFromURL(ctx context.Context, replicaURL string, ifD
 	if err != nil {
 		return nil, err
 	}
-	opt.Generation, _, err = r.CalcRestoreTarget(ctx, *opt)
+	_, err = r.CalcRestoreTarget(ctx, *opt)
 	return r, err
 }
 
@@ -137,12 +134,11 @@ func (c *RestoreCommand) loadFromConfig(ctx context.Context, dbPath, configPath
 		return nil, errSkipDBExists
 	}
 
-	// Determine the appropriate replica & generation to restore from,
-	r, generation, err := db.CalcRestoreTarget(ctx, *opt)
+	// Determine the appropriate replica to restore from,
+	r, err := db.CalcRestoreTarget(ctx, *opt)
 	if err != nil {
 		return nil, err
 	}
-	opt.Generation = generation
 
 	return r, nil
 }
@@ -171,10 +167,6 @@ Arguments:
 	    Restore from a specific replica.
 	    Defaults to replica with latest data.
 
-	-generation NAME
-	    Restore from a specific generation.
-	    Defaults to generation with latest data.
-
 	-index NUM
 	    Restore up to a specific hex-encoded WAL index (inclusive).
 	    Defaults to use the highest available index.
@@ -209,12 +201,9 @@ Examples:
 	# Restore latest replica for database to new /tmp directory
 	$ litestream restore -o /tmp/db /path/to/db
 
-	# Restore database from latest generation on S3.
+	# Restore database from S3.
 	$ litestream restore -replica s3 /path/to/db
 
-	# Restore database from specific generation on S3.
-	$ litestream restore -replica s3 -generation xxxxxxxx /path/to/db
-
 `[1:],
 		DefaultConfigPath(),
 	)
diff --git a/cmd/litestream/snapshots.go b/cmd/litestream/snapshots.go
index 25d83e3..ca0de44 100644
--- a/cmd/litestream/snapshots.go
+++ b/cmd/litestream/snapshots.go
@@ -78,7 +78,7 @@ func (c *SnapshotsCommand) Run(ctx context.Context, args []string) (err error) {
 	w := tabwriter.NewWriter(os.Stdout, 0, 8, 2, ' ', 0)
 	defer w.Flush()
 
-	fmt.Fprintln(w, "replica\tgeneration\tindex\tsize\tcreated")
+	fmt.Fprintln(w, "replica\tindex\tsize\tcreated")
 	for _, r := range replicas {
 		infos, err := r.Snapshots(ctx)
 		if err != nil {
@@ -86,9 +86,8 @@ func (c *SnapshotsCommand) Run(ctx context.Context, args []string) (err error) {
 			continue
 		}
 		for _, info := range infos {
-			fmt.Fprintf(w, "%s\t%s\t%d\t%d\t%s\n",
+			fmt.Fprintf(w, "%s\t%d\t%d\t%s\n",
 				r.Name(),
-				info.Generation,
 				info.Index,
 				info.Size,
 				info.CreatedAt.Format(time.RFC3339),
diff --git a/cmd/litestream/wal.go b/cmd/litestream/wal.go
index e56b69c..72eaa07 100644
--- a/cmd/litestream/wal.go
+++ b/cmd/litestream/wal.go
@@ -19,7 +19,6 @@ func (c *WALCommand) Run(ctx context.Context, args []string) (err error) {
 	fs := flag.NewFlagSet("litestream-wal", flag.ContinueOnError)
 	configPath, noExpandEnv := registerConfigFlag(fs)
 	replicaName := fs.String("replica", "", "replica name")
-	generation := fs.String("generation", "", "generation name")
 	fs.Usage = c.Usage
 	if err := fs.Parse(args); err != nil {
 		return err
@@ -78,43 +77,30 @@ func (c *WALCommand) Run(ctx context.Context, args []string) (err error) {
 	w := tabwriter.NewWriter(os.Stdout, 0, 8, 2, ' ', 0)
 	defer w.Flush()
 
-	fmt.Fprintln(w, "replica\tgeneration\tindex\toffset\tsize\tcreated")
+	fmt.Fprintln(w, "replica\tindex\toffset\tsize\tcreated")
 	for _, r := range replicas {
-		var generations []string
-		if *generation != "" {
-			generations = []string{*generation}
-		} else {
-			if generations, err = r.Client.Generations(ctx); err != nil {
-				r.Logger().Error("cannot determine generations", "error", err)
-				continue
+		if err := func() error {
+			itr, err := r.Client.WALSegments(ctx)
+			if err != nil {
+				return err
 			}
-		}
-
-		for _, generation := range generations {
-			if err := func() error {
-				itr, err := r.Client.WALSegments(ctx, generation)
-				if err != nil {
-					return err
-				}
-				defer itr.Close()
-
-				for itr.Next() {
-					info := itr.WALSegment()
-
-					fmt.Fprintf(w, "%s\t%s\t%x\t%d\t%d\t%s\n",
-						r.Name(),
-						info.Generation,
-						info.Index,
-						info.Offset,
-						info.Size,
-						info.CreatedAt.Format(time.RFC3339),
-					)
-				}
-				return itr.Close()
-			}(); err != nil {
-				r.Logger().Error("cannot fetch wal segments", "error", err)
-				continue
+			defer itr.Close()
+
+			for itr.Next() {
+				info := itr.WALSegment()
+
+				fmt.Fprintf(w, "%s\t%x\t%d\t%d\t%s\n",
+					r.Name(),
+					info.Index,
+					info.Offset,
+					info.Size,
+					info.CreatedAt.Format(time.RFC3339),
+				)
 			}
+			return itr.Close()
+		}(); err != nil {
+			r.Logger().Error("cannot fetch wal segments", "error", err)
+			continue
 		}
 	}
 
@@ -144,16 +130,13 @@ Arguments:
 	-replica NAME
 	    Optional, filter by a specific replica.
 
-	-generation NAME
-	    Optional, filter by a specific generation.
-
 Examples:
 
 	# List all WAL segments for a database.
 	$ litestream wal /path/to/db
 
-	# List all WAL segments on S3 for a specific generation.
-	$ litestream wal -replica s3 -generation xxxxxxxx /path/to/db
+	# List all WAL segments on S3
+	$ litestream wal -replica s3 /path/to/db
 
 	# List all WAL segments for replica URL.
 	$ litestream wal s3://mybkt/db
diff --git a/db_test.go b/db_test.go
index 4e1c494..dca742d 100644
--- a/db_test.go
+++ b/db_test.go
@@ -41,30 +41,16 @@ func TestDB_MetaPath(t *testing.T) {
 	})
 }
 
-func TestDB_GenerationNamePath(t *testing.T) {
-	db := litestream.NewDB("/tmp/db")
-	if got, want := db.GenerationNamePath(), `/tmp/.db-litestream/generation`; got != want {
-		t.Fatalf("GenerationNamePath()=%v, want %v", got, want)
-	}
-}
-
-func TestDB_GenerationPath(t *testing.T) {
-	db := litestream.NewDB("/tmp/db")
-	if got, want := db.GenerationPath("xxxx"), `/tmp/.db-litestream/generations/xxxx`; got != want {
-		t.Fatalf("GenerationPath()=%v, want %v", got, want)
-	}
-}
-
 func TestDB_ShadowWALDir(t *testing.T) {
 	db := litestream.NewDB("/tmp/db")
-	if got, want := db.ShadowWALDir("xxxx"), `/tmp/.db-litestream/generations/xxxx/wal`; got != want {
+	if got, want := db.ShadowWALDir(), `/tmp/.db-litestream/wal`; got != want {
 		t.Fatalf("ShadowWALDir()=%v, want %v", got, want)
 	}
 }
 
 func TestDB_ShadowWALPath(t *testing.T) {
 	db := litestream.NewDB("/tmp/db")
-	if got, want := db.ShadowWALPath("xxxx", 1000), `/tmp/.db-litestream/generations/xxxx/wal/000003e8.wal`; got != want {
+	if got, want := db.ShadowWALPath(1000), `/tmp/.db-litestream/wal/000003e8.wal`; got != want {
 		t.Fatalf("ShadowWALPath()=%v, want %v", got, want)
 	}
 }
@@ -196,8 +182,6 @@ func TestDB_Sync(t *testing.T) {
 		// Ensure position now available.
 		if pos, err := db.Pos(); err != nil {
 			t.Fatal(err)
-		} else if pos.Generation == "" {
-			t.Fatal("expected generation")
 		} else if got, want := pos.Index, 0; got != want {
 			t.Fatalf("pos.Index=%v, want %v", got, want)
 		} else if got, want := pos.Offset, fi.Size(); got != want {
@@ -235,8 +219,6 @@ func TestDB_Sync(t *testing.T) {
 			t.Fatal(err)
 		} else if pos1, err := db.Pos(); err != nil {
 			t.Fatal(err)
-		} else if pos0.Generation != pos1.Generation {
-			t.Fatal("expected the same generation")
 		} else if got, want := pos1.Index, pos0.Index; got != want {
 			t.Fatalf("Index=%v, want %v", got, want)
 		} else if got, want := pos1.Offset, pos0.Offset+4096+litestream.WALFrameHeaderSize; got != want {
@@ -255,8 +237,7 @@ func TestDB_Sync(t *testing.T) {
 		}
 
 		// Obtain initial position.
-		pos0, err := db.Pos()
-		if err != nil {
+		if _, err := db.Pos(); err != nil {
 			t.Fatal(err)
 		}
 
@@ -284,10 +265,8 @@ func TestDB_Sync(t *testing.T) {
 		}
 
 		// Obtain initial position.
-		if pos1, err := db.Pos(); err != nil {
+		if _, err := db.Pos(); err != nil {
 			t.Fatal(err)
-		} else if pos0.Generation == pos1.Generation {
-			t.Fatal("expected new generation after truncation")
 		}
 	})
 
@@ -307,8 +286,7 @@ func TestDB_Sync(t *testing.T) {
 		}
 
 		// Obtain initial position.
-		pos0, err := db.Pos()
-		if err != nil {
+		if _, err := db.Pos(); err != nil {
 			t.Fatal(err)
 		}
 
@@ -343,15 +321,15 @@ func TestDB_Sync(t *testing.T) {
 		}
 
 		// Obtain initial position.
-		if pos1, err := db.Pos(); err != nil {
+		if _, err := db.Pos(); err != nil {
 			t.Fatal(err)
-		} else if pos0.Generation == pos1.Generation {
-			t.Fatal("expected new generation after truncation")
 		}
 	})
 
 	// Ensure DB can handle a mismatched header-only and start new generation.
 	t.Run("WALHeaderMismatch", func(t *testing.T) {
+		t.Skip("TODO(gen)")
+
 		db, sqldb := MustOpenDBs(t)
 		defer MustCloseDBs(t, db, sqldb)
 
@@ -372,7 +350,7 @@ func TestDB_Sync(t *testing.T) {
 
 		// Read existing file, update header checksum, and write back only header
 		// to simulate a header with a mismatched checksum.
-		shadowWALPath := db.ShadowWALPath(pos0.Generation, pos0.Index)
+		shadowWALPath := db.ShadowWALPath(pos0.Index)
 		if buf, err := os.ReadFile(shadowWALPath); err != nil {
 			t.Fatal(err)
 		} else if err := os.WriteFile(shadowWALPath, append(buf[:litestream.WALHeaderSize-8], 0, 0, 0, 0, 0, 0, 0, 0), 0600); err != nil {
@@ -386,16 +364,20 @@ func TestDB_Sync(t *testing.T) {
 			t.Fatal(err)
 		}
 
-		// Verify a new generation was started.
-		if pos1, err := db.Pos(); err != nil {
-			t.Fatal(err)
-		} else if pos0.Generation == pos1.Generation {
-			t.Fatal("expected new generation")
-		}
+		/*
+			// Verify a new generation was started.
+			if pos1, err := db.Pos(); err != nil {
+				t.Fatal(err)
+			} else if pos0.Generation == pos1.Generation {
+				t.Fatal("expected new generation")
+			}
+		*/
 	})
 
 	// Ensure DB can handle partial shadow WAL header write.
 	t.Run("PartialShadowWALHeader", func(t *testing.T) {
+		t.Skip("TODO(gen): LTX will be atomically written")
+
 		db, sqldb := MustOpenDBs(t)
 		defer MustCloseDBs(t, db, sqldb)
 
@@ -414,7 +396,7 @@ func TestDB_Sync(t *testing.T) {
 		// Close & truncate shadow WAL to simulate a partial header write.
 		if err := db.Close(context.Background()); err != nil {
 			t.Fatal(err)
-		} else if err := os.Truncate(db.ShadowWALPath(pos0.Generation, pos0.Index), litestream.WALHeaderSize-1); err != nil {
+		} else if err := os.Truncate(db.ShadowWALPath(pos0.Index), litestream.WALHeaderSize-1); err != nil {
 			t.Fatal(err)
 		}
 
@@ -425,16 +407,20 @@ func TestDB_Sync(t *testing.T) {
 			t.Fatal(err)
 		}
 
-		// Verify a new generation was started.
-		if pos1, err := db.Pos(); err != nil {
-			t.Fatal(err)
-		} else if pos0.Generation == pos1.Generation {
-			t.Fatal("expected new generation")
-		}
+		/*
+			// Verify a new generation was started.
+			if pos1, err := db.Pos(); err != nil {
+				t.Fatal(err)
+			} else if pos0.Generation == pos1.Generation {
+				t.Fatal("expected new generation")
+			}
+		*/
 	})
 
 	// Ensure DB can handle partial shadow WAL writes.
 	t.Run("PartialShadowWALFrame", func(t *testing.T) {
+		t.Skip("TODO(gen): LTX will be atomically written")
+
 		db, sqldb := MustOpenDBs(t)
 		defer MustCloseDBs(t, db, sqldb)
 
@@ -451,7 +437,7 @@ func TestDB_Sync(t *testing.T) {
 		}
 
 		// Obtain current shadow WAL size.
-		fi, err := os.Stat(db.ShadowWALPath(pos0.Generation, pos0.Index))
+		fi, err := os.Stat(db.ShadowWALPath(pos0.Index))
 		if err != nil {
 			t.Fatal(err)
 		}
@@ -459,7 +445,7 @@ func TestDB_Sync(t *testing.T) {
 		// Close & truncate shadow WAL to simulate a partial frame write.
 		if err := db.Close(context.Background()); err != nil {
 			t.Fatal(err)
-		} else if err := os.Truncate(db.ShadowWALPath(pos0.Generation, pos0.Index), fi.Size()-1); err != nil {
+		} else if err := os.Truncate(db.ShadowWALPath(pos0.Index), fi.Size()-1); err != nil {
 			t.Fatal(err)
 		}
 
@@ -478,7 +464,7 @@ func TestDB_Sync(t *testing.T) {
 		}
 
 		// Ensure shadow WAL has recovered.
-		if fi0, err := os.Stat(db.ShadowWALPath(pos0.Generation, pos0.Index)); err != nil {
+		if fi0, err := os.Stat(db.ShadowWALPath(pos0.Index)); err != nil {
 			t.Fatal(err)
 		} else if got, want := fi0.Size(), fi.Size(); got != want {
 			t.Fatalf("Size()=%v, want %v", got, want)
@@ -487,6 +473,8 @@ func TestDB_Sync(t *testing.T) {
 
 	// Ensure DB can handle a generation directory with a missing shadow WAL.
 	t.Run("NoShadowWAL", func(t *testing.T) {
+		t.Skip("TODO(gen): Implement with LTX")
+
 		db, sqldb := MustOpenDBs(t)
 		defer MustCloseDBs(t, db, sqldb)
 
@@ -505,7 +493,7 @@ func TestDB_Sync(t *testing.T) {
 		// Close & delete shadow WAL to simulate dir created but not WAL.
 		if err := db.Close(context.Background()); err != nil {
 			t.Fatal(err)
-		} else if err := os.Remove(db.ShadowWALPath(pos0.Generation, pos0.Index)); err != nil {
+		} else if err := os.Remove(db.ShadowWALPath(pos0.Index)); err != nil {
 			t.Fatal(err)
 		}
 
@@ -519,8 +507,6 @@ func TestDB_Sync(t *testing.T) {
 		// Verify new generation created but index/offset the same.
 		if pos1, err := db.Pos(); err != nil {
 			t.Fatal(err)
-		} else if pos0.Generation == pos1.Generation {
-			t.Fatal("expected new generation")
 		} else if got, want := pos1.Index, pos0.Index; got != want {
 			t.Fatalf("Index=%v want %v", got, want)
 		} else if got, want := pos1.Offset, pos0.Offset; got != want {
diff --git a/file/replica_client_test.go b/file/replica_client_test.go
index 94d2e44..ea0d51d 100644
--- a/file/replica_client_test.go
+++ b/file/replica_client_test.go
@@ -19,56 +19,16 @@ func TestReplicaClient_Type(t *testing.T) {
 	}
 }
 
-func TestReplicaClient_GenerationsDir(t *testing.T) {
-	t.Run("OK", func(t *testing.T) {
-		if got, err := file.NewReplicaClient("/foo").GenerationsDir(); err != nil {
-			t.Fatal(err)
-		} else if want := "/foo/generations"; got != want {
-			t.Fatalf("GenerationsDir()=%v, want %v", got, want)
-		}
-	})
-	t.Run("ErrNoPath", func(t *testing.T) {
-		if _, err := file.NewReplicaClient("").GenerationsDir(); err == nil || err.Error() != `file replica path required` {
-			t.Fatalf("unexpected error: %v", err)
-		}
-	})
-}
-
-func TestReplicaClient_GenerationDir(t *testing.T) {
-	t.Run("OK", func(t *testing.T) {
-		if got, err := file.NewReplicaClient("/foo").GenerationDir("0123456701234567"); err != nil {
-			t.Fatal(err)
-		} else if want := "/foo/generations/0123456701234567"; got != want {
-			t.Fatalf("GenerationDir()=%v, want %v", got, want)
-		}
-	})
-	t.Run("ErrNoPath", func(t *testing.T) {
-		if _, err := file.NewReplicaClient("").GenerationDir("0123456701234567"); err == nil || err.Error() != `file replica path required` {
-			t.Fatalf("expected error: %v", err)
-		}
-	})
-	t.Run("ErrNoGeneration", func(t *testing.T) {
-		if _, err := file.NewReplicaClient("/foo").GenerationDir(""); err == nil || err.Error() != `generation required` {
-			t.Fatalf("expected error: %v", err)
-		}
-	})
-}
-
 func TestReplicaClient_SnapshotsDir(t *testing.T) {
 	t.Run("OK", func(t *testing.T) {
-		if got, err := file.NewReplicaClient("/foo").SnapshotsDir("0123456701234567"); err != nil {
+		if got, err := file.NewReplicaClient("/foo").SnapshotsDir(); err != nil {
 			t.Fatal(err)
 		} else if want := "/foo/generations/0123456701234567/snapshots"; got != want {
 			t.Fatalf("SnapshotsDir()=%v, want %v", got, want)
 		}
 	})
 	t.Run("ErrNoPath", func(t *testing.T) {
-		if _, err := file.NewReplicaClient("").SnapshotsDir("0123456701234567"); err == nil || err.Error() != `file replica path required` {
-			t.Fatalf("unexpected error: %v", err)
-		}
-	})
-	t.Run("ErrNoGeneration", func(t *testing.T) {
-		if _, err := file.NewReplicaClient("/foo").SnapshotsDir(""); err == nil || err.Error() != `generation required` {
+		if _, err := file.NewReplicaClient("").SnapshotsDir(); err == nil || err.Error() != `file replica path required` {
 			t.Fatalf("unexpected error: %v", err)
 		}
 	})
@@ -76,19 +36,14 @@ func TestReplicaClient_SnapshotsDir(t *testing.T) {
 
 func TestReplicaClient_SnapshotPath(t *testing.T) {
 	t.Run("OK", func(t *testing.T) {
-		if got, err := file.NewReplicaClient("/foo").SnapshotPath("0123456701234567", 1000); err != nil {
+		if got, err := file.NewReplicaClient("/foo").SnapshotPath(1000); err != nil {
 			t.Fatal(err)
-		} else if want := "/foo/generations/0123456701234567/snapshots/000003e8.snapshot.lz4"; got != want {
+		} else if want := "/foo/snapshots/000003e8.snapshot.lz4"; got != want {
 			t.Fatalf("SnapshotPath()=%v, want %v", got, want)
 		}
 	})
 	t.Run("ErrNoPath", func(t *testing.T) {
-		if _, err := file.NewReplicaClient("").SnapshotPath("0123456701234567", 1000); err == nil || err.Error() != `file replica path required` {
-			t.Fatalf("unexpected error: %v", err)
-		}
-	})
-	t.Run("ErrNoGeneration", func(t *testing.T) {
-		if _, err := file.NewReplicaClient("/foo").SnapshotPath("", 1000); err == nil || err.Error() != `generation required` {
+		if _, err := file.NewReplicaClient("").SnapshotPath(1000); err == nil || err.Error() != `file replica path required` {
 			t.Fatalf("unexpected error: %v", err)
 		}
 	})
@@ -96,19 +51,14 @@ func TestReplicaClient_SnapshotPath(t *testing.T) {
 
 func TestReplicaClient_WALDir(t *testing.T) {
 	t.Run("OK", func(t *testing.T) {
-		if got, err := file.NewReplicaClient("/foo").WALDir("0123456701234567"); err != nil {
+		if got, err := file.NewReplicaClient("/foo").WALDir(); err != nil {
 			t.Fatal(err)
-		} else if want := "/foo/generations/0123456701234567/wal"; got != want {
+		} else if want := "/foo/wal"; got != want {
 			t.Fatalf("WALDir()=%v, want %v", got, want)
 		}
 	})
 	t.Run("ErrNoPath", func(t *testing.T) {
-		if _, err := file.NewReplicaClient("").WALDir("0123456701234567"); err == nil || err.Error() != `file replica path required` {
-			t.Fatalf("unexpected error: %v", err)
-		}
-	})
-	t.Run("ErrNoGeneration", func(t *testing.T) {
-		if _, err := file.NewReplicaClient("/foo").WALDir(""); err == nil || err.Error() != `generation required` {
+		if _, err := file.NewReplicaClient("").WALDir(); err == nil || err.Error() != `file replica path required` {
 			t.Fatalf("unexpected error: %v", err)
 		}
 	})
@@ -116,19 +66,14 @@ func TestReplicaClient_WALDir(t *testing.T) {
 
 func TestReplicaClient_WALSegmentPath(t *testing.T) {
 	t.Run("OK", func(t *testing.T) {
-		if got, err := file.NewReplicaClient("/foo").WALSegmentPath("0123456701234567", 1000, 1001); err != nil {
+		if got, err := file.NewReplicaClient("/foo").WALSegmentPath(1000, 1001); err != nil {
 			t.Fatal(err)
-		} else if want := "/foo/generations/0123456701234567/wal/000003e8_000003e9.wal.lz4"; got != want {
+		} else if want := "/foo/wal/000003e8_000003e9.wal.lz4"; got != want {
 			t.Fatalf("WALPath()=%v, want %v", got, want)
 		}
 	})
 	t.Run("ErrNoPath", func(t *testing.T) {
-		if _, err := file.NewReplicaClient("").WALSegmentPath("0123456701234567", 1000, 0); err == nil || err.Error() != `file replica path required` {
-			t.Fatalf("unexpected error: %v", err)
-		}
-	})
-	t.Run("ErrNoGeneration", func(t *testing.T) {
-		if _, err := file.NewReplicaClient("/foo").WALSegmentPath("", 1000, 0); err == nil || err.Error() != `generation required` {
+		if _, err := file.NewReplicaClient("").WALSegmentPath(1000, 0); err == nil || err.Error() != `file replica path required` {
 			t.Fatalf("unexpected error: %v", err)
 		}
 	})
diff --git a/litestream.go b/litestream.go
index 95e97d5..9bd87ec 100644
--- a/litestream.go
+++ b/litestream.go
@@ -25,8 +25,6 @@ const (
 	WALExt        = ".wal"
 	WALSegmentExt = ".wal.lz4"
 	SnapshotExt   = ".snapshot.lz4"
-
-	GenerationNameLen = 16
 )
 
 // SQLite checkpoint modes.
@@ -39,7 +37,6 @@ const (
 
 // Litestream errors.
 var (
-	ErrNoGeneration     = errors.New("no generation available")
 	ErrNoSnapshots      = errors.New("no snapshots available")
 	ErrChecksumMismatch = errors.New("invalid replica, checksum mismatch")
 )
@@ -189,15 +186,14 @@ func (itr *WALSegmentInfoSliceIterator) WALSegment() WALSegmentInfo {
 
 // SnapshotInfo represents file information about a snapshot.
 type SnapshotInfo struct {
-	Generation string
-	Index      int
-	Size       int64
-	CreatedAt  time.Time
+	Index     int
+	Size      int64
+	CreatedAt time.Time
 }
 
 // Pos returns the WAL position when the snapshot was made.
 func (info *SnapshotInfo) Pos() Pos {
-	return Pos{Generation: info.Generation, Index: info.Index}
+	return Pos{Index: info.Index}
 }
 
 // SnapshotInfoSlice represents a slice of snapshot metadata.
@@ -208,9 +204,6 @@ func (a SnapshotInfoSlice) Len() int { return len(a) }
 func (a SnapshotInfoSlice) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
 
 func (a SnapshotInfoSlice) Less(i, j int) bool {
-	if a[i].Generation != a[j].Generation {
-		return a[i].Generation < a[j].Generation
-	}
 	return a[i].Index < a[j].Index
 }
 
@@ -225,15 +218,13 @@ func FilterSnapshotsAfter(a []SnapshotInfo, t time.Time) []SnapshotInfo {
 	return other
 }
 
-// FindMinSnapshotByGeneration finds the snapshot with the lowest index in a generation.
-func FindMinSnapshotByGeneration(a []SnapshotInfo, generation string) *SnapshotInfo {
+// FindMinSnapshot finds the snapshot with the lowest index.
+func FindMinSnapshot(a []SnapshotInfo) *SnapshotInfo {
 	var min *SnapshotInfo
 	for i := range a {
 		snapshot := &a[i]
 
-		if snapshot.Generation != generation {
-			continue
-		} else if min == nil || snapshot.Index < min.Index {
+		if min == nil || snapshot.Index < min.Index {
 			min = snapshot
 		}
 	}
@@ -242,9 +233,8 @@ func FindMinSnapshotByGeneration(a []SnapshotInfo, generation string) *SnapshotI
 
 // WALInfo represents file information about a WAL file.
 type WALInfo struct {
-	Generation string
-	Index      int
-	CreatedAt  time.Time
+	Index     int
+	CreatedAt time.Time
 }
 
 // WALInfoSlice represents a slice of WAL metadata.
@@ -255,24 +245,20 @@ func (a WALInfoSlice) Len() int { return len(a) }
 func (a WALInfoSlice) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
 
 func (a WALInfoSlice) Less(i, j int) bool {
-	if a[i].Generation != a[j].Generation {
-		return a[i].Generation < a[j].Generation
-	}
 	return a[i].Index < a[j].Index
 }
 
 // WALSegmentInfo represents file information about a WAL segment file.
 type WALSegmentInfo struct {
-	Generation string
-	Index      int
-	Offset     int64
-	Size       int64
-	CreatedAt  time.Time
+	Index     int
+	Offset    int64
+	Size      int64
+	CreatedAt time.Time
 }
 
 // Pos returns the WAL position when the segment was made.
 func (info *WALSegmentInfo) Pos() Pos {
-	return Pos{Generation: info.Generation, Index: info.Index, Offset: info.Offset}
+	return Pos{Index: info.Index, Offset: info.Offset}
 }
 
 // WALSegmentInfoSlice represents a slice of WAL segment metadata.
@@ -283,19 +269,16 @@ func (a WALSegmentInfoSlice) Len() int { return len(a) }
 func (a WALSegmentInfoSlice) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
 
 func (a WALSegmentInfoSlice) Less(i, j int) bool {
-	if a[i].Generation != a[j].Generation {
-		return a[i].Generation < a[j].Generation
-	} else if a[i].Index != a[j].Index {
+	if a[i].Index != a[j].Index {
 		return a[i].Index < a[j].Index
 	}
 	return a[i].Offset < a[j].Offset
 }
 
-// Pos is a position in the WAL for a generation.
+// Pos is a position in the WAL.
 type Pos struct {
-	Generation string // generation name
-	Index      int    // wal file index
-	Offset     int64  // offset within wal file
+	Index  int   // wal file index
+	Offset int64 // offset within wal file
 }
 
 // String returns a string representation.
@@ -303,7 +286,7 @@ func (p Pos) String() string {
 	if p.IsZero() {
 		return ""
 	}
-	return fmt.Sprintf("%s/%08x:%d", p.Generation, p.Index, p.Offset)
+	return fmt.Sprintf("%08x:%d", p.Index, p.Offset)
 }
 
 // IsZero returns true if p is the zero value.
@@ -313,7 +296,7 @@ func (p Pos) IsZero() bool {
 
 // Truncate returns p with the offset truncated to zero.
 func (p Pos) Truncate() Pos {
-	return Pos{Generation: p.Generation, Index: p.Index}
+	return Pos{Index: p.Index}
 }
 
 // Checksum computes a running SQLite checksum over a byte slice.
@@ -394,63 +377,28 @@ func removeTmpFiles(root string) error {
 	})
 }
 
-// IsGenerationName returns true if s is the correct length and is only lowercase hex characters.
-func IsGenerationName(s string) bool {
-	if len(s) != GenerationNameLen {
-		return false
-	}
-	for _, ch := range s {
-		if !isHexChar(ch) {
-			return false
-		}
-	}
-	return true
-}
-
-// GenerationsPath returns the path to a generation root directory.
-func GenerationsPath(root string) string {
-	return path.Join(root, "generations")
-}
-
-// GenerationPath returns the path to a generation's root directory.
-func GenerationPath(root, generation string) (string, error) {
-	dir := GenerationsPath(root)
-	if generation == "" {
-		return "", fmt.Errorf("generation required")
-	}
-	return path.Join(dir, generation), nil
-}
-
-// SnapshotsPath returns the path to a generation's snapshot directory.
-func SnapshotsPath(root, generation string) (string, error) {
-	dir, err := GenerationPath(root, generation)
-	if err != nil {
-		return "", err
-	}
-	return path.Join(dir, "snapshots"), nil
+// SnapshotsPath returns the path to a snapshot directory.
+func SnapshotsPath(root string) (string, error) {
+	return path.Join(root, "snapshots"), nil
 }
 
 // SnapshotPath returns the path to an uncompressed snapshot file.
-func SnapshotPath(root, generation string, index int) (string, error) {
-	dir, err := SnapshotsPath(root, generation)
+func SnapshotPath(root string, index int) (string, error) {
+	dir, err := SnapshotsPath(root)
 	if err != nil {
 		return "", err
 	}
 	return path.Join(dir, FormatSnapshotPath(index)), nil
 }
 
-// WALPath returns the path to a generation's WAL directory
-func WALPath(root, generation string) (string, error) {
-	dir, err := GenerationPath(root, generation)
-	if err != nil {
-		return "", err
-	}
-	return path.Join(dir, "wal"), nil
+// WALPath returns the path to a WAL directory
+func WALPath(root string) (string, error) {
+	return path.Join(root, "wal"), nil
 }
 
 // WALSegmentPath returns the path to a WAL segment file.
-func WALSegmentPath(root, generation string, index int, offset int64) (string, error) {
-	dir, err := WALPath(root, generation)
+func WALSegmentPath(root string, index int, offset int64) (string, error) {
+	dir, err := WALPath(root)
 	if err != nil {
 		return "", err
 	}
@@ -535,11 +483,6 @@ func FormatWALSegmentPath(index int, offset int64) string {
 
 var walSegmentPathRegex = regexp.MustCompile(`^([0-9a-f]{8})(?:_([0-9a-f]{8}))\.wal\.lz4$`)
 
-// isHexChar returns true if ch is a lowercase hex character.
-func isHexChar(ch rune) bool {
-	return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f')
-}
-
 func assert(condition bool, message string) {
 	if !condition {
 		panic("assertion failed: " + message)
diff --git a/litestream_test.go b/litestream_test.go
index b4f7d3e..2d1abb5 100644
--- a/litestream_test.go
+++ b/litestream_test.go
@@ -40,100 +40,52 @@ func TestChecksum(t *testing.T) {
 	})
 }
 
-func TestGenerationsPath(t *testing.T) {
-	t.Run("OK", func(t *testing.T) {
-		if got, want := litestream.GenerationsPath("foo"), "foo/generations"; got != want {
-			t.Fatalf("GenerationsPath()=%v, want %v", got, want)
-		}
-	})
-	t.Run("NoPath", func(t *testing.T) {
-		if got, want := litestream.GenerationsPath(""), "generations"; got != want {
-			t.Fatalf("GenerationsPath()=%v, want %v", got, want)
-		}
-	})
-}
-
-func TestGenerationPath(t *testing.T) {
-	t.Run("OK", func(t *testing.T) {
-		if got, err := litestream.GenerationPath("foo", "0123456701234567"); err != nil {
-			t.Fatal(err)
-		} else if want := "foo/generations/0123456701234567"; got != want {
-			t.Fatalf("GenerationPath()=%v, want %v", got, want)
-		}
-	})
-	t.Run("ErrNoGeneration", func(t *testing.T) {
-		if _, err := litestream.GenerationPath("foo", ""); err == nil || err.Error() != `generation required` {
-			t.Fatalf("expected error: %v", err)
-		}
-	})
-}
-
 func TestSnapshotsPath(t *testing.T) {
 	t.Run("OK", func(t *testing.T) {
-		if got, err := litestream.SnapshotsPath("foo", "0123456701234567"); err != nil {
+		if got, err := litestream.SnapshotsPath("foo"); err != nil {
 			t.Fatal(err)
-		} else if want := "foo/generations/0123456701234567/snapshots"; got != want {
+		} else if want := "foo/snapshots"; got != want {
 			t.Fatalf("SnapshotsPath()=%v, want %v", got, want)
 		}
 	})
-	t.Run("ErrNoGeneration", func(t *testing.T) {
-		if _, err := litestream.SnapshotsPath("foo", ""); err == nil || err.Error() != `generation required` {
-			t.Fatalf("unexpected error: %v", err)
-		}
-	})
 }
 
 func TestSnapshotPath(t *testing.T) {
 	t.Run("OK", func(t *testing.T) {
-		if got, err := litestream.SnapshotPath("foo", "0123456701234567", 1000); err != nil {
+		if got, err := litestream.SnapshotPath("foo", 1000); err != nil {
 			t.Fatal(err)
-		} else if want := "foo/generations/0123456701234567/snapshots/000003e8.snapshot.lz4"; got != want {
+		} else if want := "foo/snapshots/000003e8.snapshot.lz4"; got != want {
 			t.Fatalf("SnapshotPath()=%v, want %v", got, want)
 		}
 	})
-	t.Run("ErrNoGeneration", func(t *testing.T) {
-		if _, err := litestream.SnapshotPath("foo", "", 1000); err == nil || err.Error() != `generation required` {
-			t.Fatalf("unexpected error: %v", err)
-		}
-	})
 }
 
 func TestWALPath(t *testing.T) {
 	t.Run("OK", func(t *testing.T) {
-		if got, err := litestream.WALPath("foo", "0123456701234567"); err != nil {
+		if got, err := litestream.WALPath("foo"); err != nil {
 			t.Fatal(err)
-		} else if want := "foo/generations/0123456701234567/wal"; got != want {
+		} else if want := "foo/wal"; got != want {
 			t.Fatalf("WALPath()=%v, want %v", got, want)
 		}
 	})
-	t.Run("ErrNoGeneration", func(t *testing.T) {
-		if _, err := litestream.WALPath("foo", ""); err == nil || err.Error() != `generation required` {
-			t.Fatalf("unexpected error: %v", err)
-		}
-	})
 }
 
 func TestWALSegmentPath(t *testing.T) {
 	t.Run("OK", func(t *testing.T) {
-		if got, err := litestream.WALSegmentPath("foo", "0123456701234567", 1000, 1001); err != nil {
+		if got, err := litestream.WALSegmentPath("foo", 1000, 1001); err != nil {
 			t.Fatal(err)
-		} else if want := "foo/generations/0123456701234567/wal/000003e8_000003e9.wal.lz4"; got != want {
+		} else if want := "foo/wal/000003e8_000003e9.wal.lz4"; got != want {
 			t.Fatalf("WALPath()=%v, want %v", got, want)
 		}
 	})
-	t.Run("ErrNoGeneration", func(t *testing.T) {
-		if _, err := litestream.WALSegmentPath("foo", "", 1000, 0); err == nil || err.Error() != `generation required` {
-			t.Fatalf("unexpected error: %v", err)
-		}
-	})
 }
 
-func TestFindMinSnapshotByGeneration(t *testing.T) {
+func TestFindMinSnapshot(t *testing.T) {
 	infos := []litestream.SnapshotInfo{
-		{Generation: "29cf4bced74e92ab", Index: 0},
-		{Generation: "5dfeb4aa03232553", Index: 24},
+		{Index: 0},
+		{Index: 24},
 	}
-	if got, want := litestream.FindMinSnapshotByGeneration(infos, "29cf4bced74e92ab"), &infos[0]; got != want {
+	if got, want := litestream.FindMinSnapshot(infos), &infos[0]; got != want {
 		t.Fatalf("info=%#v, want %#v", got, want)
 	}
 }
diff --git a/replica_client_test.go b/replica_client_test.go
index cf6079b..73ba612 100644
--- a/replica_client_test.go
+++ b/replica_client_test.go
@@ -8,7 +8,6 @@ import (
 	"math/rand"
 	"os"
 	"path"
-	"reflect"
 	"sort"
 	"strings"
 	"testing"
@@ -62,57 +61,21 @@ var (
 	sftpPath     = flag.String("sftp-path", os.Getenv("LITESTREAM_SFTP_PATH"), "")
 )
 
-func TestReplicaClient_Generations(t *testing.T) {
-	RunWithReplicaClient(t, "OK", func(t *testing.T, c litestream.ReplicaClient) {
-		t.Parallel()
-
-		// Write snapshots.
-		if _, err := c.WriteSnapshot(context.Background(), "5efbd8d042012dca", 0, strings.NewReader(`foo`)); err != nil {
-			t.Fatal(err)
-		} else if _, err := c.WriteSnapshot(context.Background(), "b16ddcf5c697540f", 0, strings.NewReader(`bar`)); err != nil {
-			t.Fatal(err)
-		} else if _, err := c.WriteSnapshot(context.Background(), "155fe292f8333c72", 0, strings.NewReader(`baz`)); err != nil {
-			t.Fatal(err)
-		}
-
-		// Fetch and sort generations.
-		got, err := c.Generations(context.Background())
-		if err != nil {
-			t.Fatal(err)
-		}
-		sort.Strings(got)
-
-		if want := []string{"155fe292f8333c72", "5efbd8d042012dca", "b16ddcf5c697540f"}; !reflect.DeepEqual(got, want) {
-			t.Fatalf("Generations()=%v, want %v", got, want)
-		}
-	})
-
-	RunWithReplicaClient(t, "NoGenerationsDir", func(t *testing.T, c litestream.ReplicaClient) {
-		t.Parallel()
-
-		if generations, err := c.Generations(context.Background()); err != nil {
-			t.Fatal(err)
-		} else if got, want := len(generations), 0; got != want {
-			t.Fatalf("len(Generations())=%v, want %v", got, want)
-		}
-	})
-}
-
 func TestReplicaClient_Snapshots(t *testing.T) {
 	RunWithReplicaClient(t, "OK", func(t *testing.T, c litestream.ReplicaClient) {
 		t.Parallel()
 
 		// Write snapshots.
-		if _, err := c.WriteSnapshot(context.Background(), "5efbd8d042012dca", 1, strings.NewReader(``)); err != nil {
+		if _, err := c.WriteSnapshot(context.Background(), 1, strings.NewReader(``)); err != nil {
 			t.Fatal(err)
-		} else if _, err := c.WriteSnapshot(context.Background(), "b16ddcf5c697540f", 5, strings.NewReader(`x`)); err != nil {
+		} else if _, err := c.WriteSnapshot(context.Background(), 5, strings.NewReader(`x`)); err != nil {
 			t.Fatal(err)
-		} else if _, err := c.WriteSnapshot(context.Background(), "b16ddcf5c697540f", 10, strings.NewReader(`xyz`)); err != nil {
+		} else if _, err := c.WriteSnapshot(context.Background(), 10, strings.NewReader(`xyz`)); err != nil {
 			t.Fatal(err)
 		}
 
 		// Fetch all snapshots by generation.
-		itr, err := c.Snapshots(context.Background(), "b16ddcf5c697540f")
+		itr, err := c.Snapshots(context.Background())
 		if err != nil {
 			t.Fatal(err)
 		}
@@ -128,9 +91,7 @@ func TestReplicaClient_Snapshots(t *testing.T) {
 		sort.Sort(litestream.SnapshotInfoSlice(a))
 
 		// Verify first snapshot metadata.
-		if got, want := a[0].Generation, "b16ddcf5c697540f"; got != want {
-			t.Fatalf("Generation=%v, want %v", got, want)
-		} else if got, want := a[0].Index, 5; got != want {
+		if got, want := a[0].Index, 5; got != want {
 			t.Fatalf("Index=%v, want %v", got, want)
 		} else if got, want := a[0].Size, int64(1); got != want {
 			t.Fatalf("Size=%v, want %v", got, want)
@@ -139,9 +100,7 @@ func TestReplicaClient_Snapshots(t *testing.T) {
 		}
 
 		// Verify second snapshot metadata.
-		if got, want := a[1].Generation, "b16ddcf5c697540f"; got != want {
-			t.Fatalf("Generation=%v, want %v", got, want)
-		} else if got, want := a[1].Index, 0xA; got != want {
+		if got, want := a[1].Index, 0xA; got != want {
 			t.Fatalf("Index=%v, want %v", got, want)
 		} else if got, want := a[1].Size, int64(3); got != want {
 			t.Fatalf("Size=%v, want %v", got, want)
@@ -154,43 +113,17 @@ func TestReplicaClient_Snapshots(t *testing.T) {
 			t.Fatal(err)
 		}
 	})
-
-	RunWithReplicaClient(t, "NoGenerationDir", func(t *testing.T, c litestream.ReplicaClient) {
-		t.Parallel()
-
-		itr, err := c.Snapshots(context.Background(), "5efbd8d042012dca")
-		if err != nil {
-			t.Fatal(err)
-		}
-		defer itr.Close()
-
-		if itr.Next() {
-			t.Fatal("expected no snapshots")
-		}
-	})
-
-	RunWithReplicaClient(t, "ErrNoGeneration", func(t *testing.T, c litestream.ReplicaClient) {
-		t.Parallel()
-
-		itr, err := c.Snapshots(context.Background(), "")
-		if err == nil {
-			err = itr.Close()
-		}
-		if err == nil || err.Error() != `cannot determine snapshots path: generation required` {
-			t.Fatalf("unexpected error: %v", err)
-		}
-	})
 }
 
 func TestReplicaClient_WriteSnapshot(t *testing.T) {
 	RunWithReplicaClient(t, "OK", func(t *testing.T, c litestream.ReplicaClient) {
 		t.Parallel()
 
-		if _, err := c.WriteSnapshot(context.Background(), "b16ddcf5c697540f", 1000, strings.NewReader(`foobar`)); err != nil {
+		if _, err := c.WriteSnapshot(context.Background(), 1000, strings.NewReader(`foobar`)); err != nil {
 			t.Fatal(err)
 		}
 
-		if r, err := c.SnapshotReader(context.Background(), "b16ddcf5c697540f", 1000); err != nil {
+		if r, err := c.SnapshotReader(context.Background(), 1000); err != nil {
 			t.Fatal(err)
 		} else if buf, err := io.ReadAll(r); err != nil {
 			t.Fatal(err)
@@ -203,7 +136,7 @@ func TestReplicaClient_WriteSnapshot(t *testing.T) {
 
 	RunWithReplicaClient(t, "ErrNoGeneration", func(t *testing.T, c litestream.ReplicaClient) {
 		t.Parallel()
-		if _, err := c.WriteSnapshot(context.Background(), "", 0, nil); err == nil || err.Error() != `cannot determine snapshot path: generation required` {
+		if _, err := c.WriteSnapshot(context.Background(), 0, nil); err == nil || err.Error() != `cannot determine snapshot path: generation required` {
 			t.Fatalf("unexpected error: %v", err)
 		}
 	})
@@ -213,11 +146,11 @@ func TestReplicaClient_SnapshotReader(t *testing.T) {
 	RunWithReplicaClient(t, "OK", func(t *testing.T, c litestream.ReplicaClient) {
 		t.Parallel()
 
-		if _, err := c.WriteSnapshot(context.Background(), "5efbd8d042012dca", 10, strings.NewReader(`foo`)); err != nil {
+		if _, err := c.WriteSnapshot(context.Background(), 10, strings.NewReader(`foo`)); err != nil {
 			t.Fatal(err)
 		}
 
-		r, err := c.SnapshotReader(context.Background(), "5efbd8d042012dca", 10)
+		r, err := c.SnapshotReader(context.Background(), 10)
 		if err != nil {
 			t.Fatal(err)
 		}
@@ -233,7 +166,7 @@ func TestReplicaClient_SnapshotReader(t *testing.T) {
 	RunWithReplicaClient(t, "ErrNotFound", func(t *testing.T, c litestream.ReplicaClient) {
 		t.Parallel()
 
-		if _, err := c.SnapshotReader(context.Background(), "5efbd8d042012dca", 1); !os.IsNotExist(err) {
+		if _, err := c.SnapshotReader(context.Background(), 1); !os.IsNotExist(err) {
 			t.Fatalf("expected not exist, got %#v", err)
 		}
 	})
@@ -241,7 +174,7 @@ func TestReplicaClient_SnapshotReader(t *testing.T) {
 	RunWithReplicaClient(t, "ErrNoGeneration", func(t *testing.T, c litestream.ReplicaClient) {
 		t.Parallel()
 
-		if _, err := c.SnapshotReader(context.Background(), "", 1); err == nil || err.Error() != `cannot determine snapshot path: generation required` {
+		if _, err := c.SnapshotReader(context.Background(), 1); err == nil || err.Error() != `cannot determine snapshot path: generation required` {
 			t.Fatalf("unexpected error: %v", err)
 		}
 	})
@@ -251,18 +184,18 @@ func TestReplicaClient_WALs(t *testing.T) {
 	RunWithReplicaClient(t, "OK", func(t *testing.T, c litestream.ReplicaClient) {
 		t.Parallel()
 
-		if _, err := c.WriteWALSegment(context.Background(), litestream.Pos{Generation: "5efbd8d042012dca", Index: 1, Offset: 0}, strings.NewReader(``)); err != nil {
+		if _, err := c.WriteWALSegment(context.Background(), litestream.Pos{Index: 1, Offset: 0}, strings.NewReader(``)); err != nil {
 			t.Fatal(err)
 		}
-		if _, err := c.WriteWALSegment(context.Background(), litestream.Pos{Generation: "b16ddcf5c697540f", Index: 2, Offset: 0}, strings.NewReader(`12345`)); err != nil {
+		if _, err := c.WriteWALSegment(context.Background(), litestream.Pos{Index: 2, Offset: 0}, strings.NewReader(`12345`)); err != nil {
 			t.Fatal(err)
-		} else if _, err := c.WriteWALSegment(context.Background(), litestream.Pos{Generation: "b16ddcf5c697540f", Index: 2, Offset: 5}, strings.NewReader(`67`)); err != nil {
+		} else if _, err := c.WriteWALSegment(context.Background(), litestream.Pos{Index: 2, Offset: 5}, strings.NewReader(`67`)); err != nil {
 			t.Fatal(err)
-		} else if _, err := c.WriteWALSegment(context.Background(), litestream.Pos{Generation: "b16ddcf5c697540f", Index: 3, Offset: 0}, strings.NewReader(`xyz`)); err != nil {
+		} else if _, err := c.WriteWALSegment(context.Background(), litestream.Pos{Index: 3, Offset: 0}, strings.NewReader(`xyz`)); err != nil {
 			t.Fatal(err)
 		}
 
-		itr, err := c.WALSegments(context.Background(), "b16ddcf5c697540f")
+		itr, err := c.WALSegments(context.Background())
 		if err != nil {
 			t.Fatal(err)
 		}
@@ -278,9 +211,7 @@ func TestReplicaClient_WALs(t *testing.T) {
 		sort.Sort(litestream.WALSegmentInfoSlice(a))
 
 		// Verify first WAL segment metadata.
-		if got, want := a[0].Generation, "b16ddcf5c697540f"; got != want {
-			t.Fatalf("Generation=%v, want %v", got, want)
-		} else if got, want := a[0].Index, 2; got != want {
+		if got, want := a[0].Index, 2; got != want {
 			t.Fatalf("Index=%v, want %v", got, want)
 		} else if got, want := a[0].Offset, int64(0); got != want {
 			t.Fatalf("Offset=%v, want %v", got, want)
@@ -291,9 +222,7 @@ func TestReplicaClient_WALs(t *testing.T) {
 		}
 
 		// Verify first WAL segment metadata.
-		if got, want := a[1].Generation, "b16ddcf5c697540f"; got != want {
-			t.Fatalf("Generation=%v, want %v", got, want)
-		} else if got, want := a[1].Index, 2; got != want {
+		if got, want := a[1].Index, 2; got != want {
 			t.Fatalf("Index=%v, want %v", got, want)
 		} else if got, want := a[1].Offset, int64(5); got != want {
 			t.Fatalf("Offset=%v, want %v", got, want)
@@ -304,9 +233,7 @@ func TestReplicaClient_WALs(t *testing.T) {
 		}
 
 		// Verify third WAL segment metadata.
-		if got, want := a[2].Generation, "b16ddcf5c697540f"; got != want {
-			t.Fatalf("Generation=%v, want %v", got, want)
-		} else if got, want := a[2].Index, 3; got != want {
+		if got, want := a[2].Index, 3; got != want {
 			t.Fatalf("Index=%v, want %v", got, want)
 		} else if got, want := a[2].Offset, int64(0); got != want {
 			t.Fatalf("Offset=%v, want %v", got, want)
@@ -322,28 +249,14 @@ func TestReplicaClient_WALs(t *testing.T) {
 		}
 	})
 
-	RunWithReplicaClient(t, "NoGenerationDir", func(t *testing.T, c litestream.ReplicaClient) {
-		t.Parallel()
-
-		itr, err := c.WALSegments(context.Background(), "5efbd8d042012dca")
-		if err != nil {
-			t.Fatal(err)
-		}
-		defer itr.Close()
-
-		if itr.Next() {
-			t.Fatal("expected no wal files")
-		}
-	})
-
 	RunWithReplicaClient(t, "NoWALs", func(t *testing.T, c litestream.ReplicaClient) {
 		t.Parallel()
 
-		if _, err := c.WriteSnapshot(context.Background(), "5efbd8d042012dca", 0, strings.NewReader(`foo`)); err != nil {
+		if _, err := c.WriteSnapshot(context.Background(), 0, strings.NewReader(`foo`)); err != nil {
 			t.Fatal(err)
 		}
 
-		itr, err := c.WALSegments(context.Background(), "5efbd8d042012dca")
+		itr, err := c.WALSegments(context.Background())
 		if err != nil {
 			t.Fatal(err)
 		}
@@ -357,7 +270,7 @@ func TestReplicaClient_WALs(t *testing.T) {
 	RunWithReplicaClient(t, "ErrNoGeneration", func(t *testing.T, c litestream.ReplicaClient) {
 		t.Parallel()
 
-		itr, err := c.WALSegments(context.Background(), "")
+		itr, err := c.WALSegments(context.Background())
 		if err == nil {
 			err = itr.Close()
 		}
@@ -371,11 +284,11 @@ func TestReplicaClient_WriteWALSegment(t *testing.T) {
 	RunWithReplicaClient(t, "OK", func(t *testing.T, c litestream.ReplicaClient) {
 		t.Parallel()
 
-		if _, err := c.WriteWALSegment(context.Background(), litestream.Pos{Generation: "b16ddcf5c697540f", Index: 1000, Offset: 2000}, strings.NewReader(`foobar`)); err != nil {
+		if _, err := c.WriteWALSegment(context.Background(), litestream.Pos{Index: 1000, Offset: 2000}, strings.NewReader(`foobar`)); err != nil {
 			t.Fatal(err)
 		}
 
-		if r, err := c.WALSegmentReader(context.Background(), litestream.Pos{Generation: "b16ddcf5c697540f", Index: 1000, Offset: 2000}); err != nil {
+		if r, err := c.WALSegmentReader(context.Background(), litestream.Pos{Index: 1000, Offset: 2000}); err != nil {
 			t.Fatal(err)
 		} else if buf, err := io.ReadAll(r); err != nil {
 			t.Fatal(err)
@@ -388,7 +301,7 @@ func TestReplicaClient_WriteWALSegment(t *testing.T) {
 
 	RunWithReplicaClient(t, "ErrNoGeneration", func(t *testing.T, c litestream.ReplicaClient) {
 		t.Parallel()
-		if _, err := c.WriteWALSegment(context.Background(), litestream.Pos{Generation: "", Index: 0, Offset: 0}, nil); err == nil || err.Error() != `cannot determine wal segment path: generation required` {
+		if _, err := c.WriteWALSegment(context.Background(), litestream.Pos{Index: 0, Offset: 0}, nil); err == nil || err.Error() != `cannot determine wal segment path: generation required` {
 			t.Fatalf("unexpected error: %v", err)
 		}
 	})
@@ -398,11 +311,11 @@ func TestReplicaClient_WALReader(t *testing.T) {
 
 	RunWithReplicaClient(t, "OK", func(t *testing.T, c litestream.ReplicaClient) {
 		t.Parallel()
-		if _, err := c.WriteWALSegment(context.Background(), litestream.Pos{Generation: "5efbd8d042012dca", Index: 10, Offset: 5}, strings.NewReader(`foobar`)); err != nil {
+		if _, err := c.WriteWALSegment(context.Background(), litestream.Pos{Index: 10, Offset: 5}, strings.NewReader(`foobar`)); err != nil {
 			t.Fatal(err)
 		}
 
-		r, err := c.WALSegmentReader(context.Background(), litestream.Pos{Generation: "5efbd8d042012dca", Index: 10, Offset: 5})
+		r, err := c.WALSegmentReader(context.Background(), litestream.Pos{Index: 10, Offset: 5})
 		if err != nil {
 			t.Fatal(err)
 		}
@@ -418,7 +331,7 @@ func TestReplicaClient_WALReader(t *testing.T) {
 	RunWithReplicaClient(t, "ErrNotFound", func(t *testing.T, c litestream.ReplicaClient) {
 		t.Parallel()
 
-		if _, err := c.WALSegmentReader(context.Background(), litestream.Pos{Generation: "5efbd8d042012dca", Index: 1, Offset: 0}); !os.IsNotExist(err) {
+		if _, err := c.WALSegmentReader(context.Background(), litestream.Pos{Index: 1, Offset: 0}); !os.IsNotExist(err) {
 			t.Fatalf("expected not exist, got %#v", err)
 		}
 	})
@@ -428,22 +341,22 @@ func TestReplicaClient_DeleteWALSegments(t *testing.T) {
 	RunWithReplicaClient(t, "OK", func(t *testing.T, c litestream.ReplicaClient) {
 		t.Parallel()
 
-		if _, err := c.WriteWALSegment(context.Background(), litestream.Pos{Generation: "b16ddcf5c697540f", Index: 1, Offset: 2}, strings.NewReader(`foo`)); err != nil {
+		if _, err := c.WriteWALSegment(context.Background(), litestream.Pos{Index: 1, Offset: 2}, strings.NewReader(`foo`)); err != nil {
 			t.Fatal(err)
-		} else if _, err := c.WriteWALSegment(context.Background(), litestream.Pos{Generation: "5efbd8d042012dca", Index: 3, Offset: 4}, strings.NewReader(`bar`)); err != nil {
+		} else if _, err := c.WriteWALSegment(context.Background(), litestream.Pos{Index: 3, Offset: 4}, strings.NewReader(`bar`)); err != nil {
 			t.Fatal(err)
 		}
 
 		if err := c.DeleteWALSegments(context.Background(), []litestream.Pos{
-			{Generation: "b16ddcf5c697540f", Index: 1, Offset: 2},
-			{Generation: "5efbd8d042012dca", Index: 3, Offset: 4},
+			{Index: 1, Offset: 2},
+			{Index: 3, Offset: 4},
 		}); err != nil {
 			t.Fatal(err)
 		}
 
-		if _, err := c.WALSegmentReader(context.Background(), litestream.Pos{Generation: "b16ddcf5c697540f", Index: 1, Offset: 2}); !os.IsNotExist(err) {
+		if _, err := c.WALSegmentReader(context.Background(), litestream.Pos{Index: 1, Offset: 2}); !os.IsNotExist(err) {
 			t.Fatalf("expected not exist, got %#v", err)
-		} else if _, err := c.WALSegmentReader(context.Background(), litestream.Pos{Generation: "5efbd8d042012dca", Index: 3, Offset: 4}); !os.IsNotExist(err) {
+		} else if _, err := c.WALSegmentReader(context.Background(), litestream.Pos{Index: 3, Offset: 4}); !os.IsNotExist(err) {
 			t.Fatalf("expected not exist, got %#v", err)
 		}
 	})
@@ -552,15 +465,8 @@ func NewSFTPReplicaClient(tb testing.TB) *sftp.ReplicaClient {
 func MustDeleteAll(tb testing.TB, c litestream.ReplicaClient) {
 	tb.Helper()
 
-	generations, err := c.Generations(context.Background())
-	if err != nil {
-		tb.Fatalf("cannot list generations for deletion: %s", err)
-	}
-
-	for _, generation := range generations {
-		if err := c.DeleteGeneration(context.Background(), generation); err != nil {
-			tb.Fatalf("cannot delete generation: %s", err)
-		}
+	if err := c.DeleteAll(context.Background()); err != nil {
+		tb.Fatalf("cannot delete all: %s", err)
 	}
 
 	switch c := c.(type) {
diff --git a/replica_test.go b/replica_test.go
index 78ba620..f39df9d 100644
--- a/replica_test.go
+++ b/replica_test.go
@@ -15,8 +15,7 @@ import (
 
 func nextIndex(pos litestream.Pos) litestream.Pos {
 	return litestream.Pos{
-		Generation: pos.Generation,
-		Index:      pos.Index + 1,
+		Index: pos.Index + 1,
 	}
 }
 
@@ -39,7 +38,7 @@ func TestReplica_Sync(t *testing.T) {
 	db, sqldb := MustOpenDBs(t)
 	defer MustCloseDBs(t, db, sqldb)
 
-	// Issue initial database sync to setup generation.
+	// Issue initial database sync.
 	if err := db.Sync(context.Background()); err != nil {
 		t.Fatal(err)
 	}
@@ -58,16 +57,6 @@ func TestReplica_Sync(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	// Verify client generation matches database.
-	generations, err := c.Generations(context.Background())
-	if err != nil {
-		t.Fatal(err)
-	} else if got, want := len(generations), 1; got != want {
-		t.Fatalf("len(generations)=%v, want %v", got, want)
-	} else if got, want := generations[0], dpos.Generation; got != want {
-		t.Fatalf("generations[0]=%v, want %v", got, want)
-	}
-
 	// Verify we synced checkpoint page to WAL.
 	if r, err := c.WALSegmentReader(context.Background(), nextIndex(dpos)); err != nil {
 		t.Fatal(err)