trove

package module
v1.8.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 5, 2026 License: MIT Imports: 15 Imported by: 4

README

Trove, a Go object storage library for local disk, S3, Google Cloud Storage, Azure Blob Storage and SFTP

Trove: object storage for Go over S3, GCS, Azure Blob and local disk

Trove is an open-source Go library for object and file storage. You write against one API (Put, Get, List, Copy, buckets) and pick the backend at startup: local disk, memory, Amazon S3 and S3-compatible stores like MinIO, Google Cloud Storage, Azure Blob Storage or SFTP. On top of that you get a middleware pipeline for encryption, compression, virus scanning and deduplication, routing of keys to different backends, content-addressable storage, and an io/fs view of a bucket. It runs on its own or as a Forge extension.

CI Go Reference GitHub Stars

Documentation · Quick start · Drivers · Middleware · Routing · CAS and VFS · Forge · FAQ

Quick start

You need Go 1.25 or later. The local disk driver lives in the root module, so one go get is enough to try it:

mkdir hello-trove
cd hello-trove
go mod init example.com/hello-trove
go get github.com/xraph/trove@latest

Save this as main.go:

package main

import (
	"context"
	"errors"
	"fmt"
	"io"
	"log"
	"strings"

	"github.com/xraph/trove"
	"github.com/xraph/trove/driver"
	"github.com/xraph/trove/drivers/localdriver"
)

func main() {
	ctx := context.Background()

	local := localdriver.New()
	if err := local.Open(ctx, "local://./storage"); err != nil {
		log.Fatal(err)
	}

	t, err := trove.Open(local, trove.WithDefaultBucket("uploads"))
	if err != nil {
		log.Fatal(err)
	}
	defer t.Close(ctx)

	if err := t.CreateBucket(ctx, "uploads"); err != nil && !errors.Is(err, trove.ErrBucketExists) {
		log.Fatal(err)
	}

	info, err := t.Put(ctx, "uploads", "notes/hello.txt", strings.NewReader("Hello, Trove!"),
		driver.WithContentType("text/plain"))
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("stored", info.Key, info.Size, "bytes")

	obj, err := t.Get(ctx, "uploads", "notes/hello.txt")
	if err != nil {
		log.Fatal(err)
	}
	defer obj.Close()

	body, err := io.ReadAll(obj)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(string(body))

	it, err := t.List(ctx, "uploads", driver.WithPrefix("notes/"))
	if err != nil {
		log.Fatal(err)
	}
	objects, err := it.All(ctx)
	if err != nil {
		log.Fatal(err)
	}
	for _, o := range objects {
		fmt.Println("listed", o.Key)
	}
}

Run it:

go run .
# stored notes/hello.txt 13 bytes
# Hello, Trove!
# listed notes/hello.txt

Look in ./storage/uploads/notes/ and you'll find hello.txt next to a hello.txt.meta.json sidecar, which is where the local driver keeps the content type and metadata. To move the same code to S3, import github.com/xraph/trove/drivers/s3driver, call s3driver.New() and open it with an s3:// DSN. Nothing else changes.

Why Trove?

If you've written an upload service, you've probably written the same wrapper twice: once over the AWS SDK for production and once over the filesystem for tests and local dev. Trove is that wrapper, with the parts you'd bolt on later (encryption at rest, compression, scanning, routing old files to cheap storage) already built as middleware you can scope to a bucket or a key pattern.

You need Trove gives you
One storage API across clouds Put, Get, Head, Delete, List, Copy and bucket calls on every driver
Fast tests without a cloud account memdriver and localdriver in the root module, plus trovetest.RunDriverSuite for driver conformance
Large uploads and direct browser downloads Multipart uploads, pre-signed URLs and range reads on the S3, GCS and Azure drivers
Encryption at rest AES-256-GCM middleware with a pluggable key provider
Smaller objects Zstandard compression middleware with a minimum size and extension exclusions
Malware checks on upload A scan middleware with a ClamAV provider
Hot and cold storage in one app Routing rules that send keys to named backends by glob pattern or function
Deduplicated content Content-addressable storage keyed by SHA-256, BLAKE3 or XXHash, with pinning and GC
A filesystem view Stat, ReadDir, Walk, Open, Create and Rename over a bucket, plus an io/fs.FS adapter for http.FileServer
To see what storage is doing An OpenTelemetry span per operation and six metrics, with no keys, bucket names or error text on either

The architecture guide shows how the facade, the router, the middleware resolver and the drivers fit together.

Storage drivers

localdriver and memdriver ship inside the root module. The cloud and SFTP drivers are separate Go modules, so you only pull the AWS, Google or Azure SDK into your build when you import that driver, and a service that only ever writes to local disk keeps a short go.sum.

Backend Package DSN Extra capabilities Docs
Local disk github.com/xraph/trove/drivers/localdriver file:///abs/path, local://./rel/path Local driver
Memory github.com/xraph/trove/drivers/memdriver mem:// Memory driver
Amazon S3, MinIO github.com/xraph/trove/drivers/s3driver s3://REGION/BUCKET, with ?endpoint=...&path_style=true for MinIO multipart, presign, range S3 driver
Google Cloud Storage github.com/xraph/trove/drivers/gcsdriver gcs://PROJECT_ID/BUCKET multipart, presign, range GCS driver
Azure Blob Storage github.com/xraph/trove/drivers/azuredriver azure://ACCOUNT_NAME/CONTAINER?key=ACCESS_KEY multipart, presign, range Azure driver
SFTP github.com/xraph/trove/drivers/sftpdriver sftp://user:pass@host:22/basepath SFTP driver

Not every backend can do everything. Capabilities are separate interfaces in the driver package, and you check for one with a type assertion:

if p, ok := t.Driver().(driver.PresignDriver); ok {
	url, err := p.PresignGet(ctx, "uploads", "report.pdf", 15*time.Minute)
	// ...
}

Errors are classified the same way across drivers. errors.Is(err, trove.ErrObjectNotFound) works whether the object was missing on disk or in S3, and trove.Permanent(err) tells you whether a retry has any chance of working.

Middleware

Middleware wraps the write path, the read path, or both. You can register it for everything, or give it a scope so it only touches the buckets and keys you choose:

t, err := trove.Open(drv,
	trove.WithMiddleware(encrypt.New(
		encrypt.WithKeyProvider(encrypt.NewStaticKeyProvider(key)),
	)),
	trove.WithScopedMiddleware(
		&middleware.ScopeBucket{Buckets: []string{"logs"}},
		compress.New(compress.WithMinSize(4096)),
	),
)
Package What it does
middleware/encrypt AES-256-GCM. The key comes from a KeyProvider, so you can serve it from a secrets manager
middleware/compress Zstandard, skipping objects under WithMinSize and extensions in WithExclude
middleware/scan Scans writes through a Provider. scan.NewClamAVProvider(addr) talks to clamd
middleware/dedup Detects duplicate content by hash, with an in-memory store or your own
middleware/watermark Writes a text mark into file metadata: PNG tEXt, JPEG COM and the PDF info dictionary, without image processing

Scopes are ScopeBucket, ScopeKeyPattern, ScopeContentType and ScopeFunc, and ScopeAnd, ScopeOr and ScopeNot combine them. You can also add and remove middleware at runtime with UseMiddleware and RemoveMiddleware. One thing to plan for: the middleware buffers the whole object in memory while it works, so if you accept uploads of a few gigabytes through an encrypted or compressed path, size your memory and your upload limits for that. The middleware guide has the details.

Route keys to different backends

One Trove can hold several named backends. Routing rules decide which one a key lands on, and anything no rule claims goes to the driver you passed to Open:

t, err := trove.Open(primary,
	trove.WithBackend("archive", archive),
	trove.WithRoute("*.log", "archive"),
	trove.WithRouteFunc(func(bucket, key string) string {
		if strings.HasPrefix(key, "tmp/") {
			return "archive"
		}
		return ""
	}),
)

Patterns use filepath.Match, so * stops at a / and *.log matches app.log but not 2026/app.log. t.Backend("archive") gives you a Trove bound to that backend if you need to reach it directly. See multi-backend routing.

Content-addressable storage

Turn on CAS with trove.WithCAS(cas.AlgSHA256) (or cas.AlgBlake3, cas.AlgXXHash) and t.CAS() stores content under its hash. Writing the same bytes twice keeps one copy:

hash, info, err := t.CAS().Store(ctx, strings.NewReader("same bytes, stored once"))

Retrieve, Exists and Stat read it back, Pin and Unpin protect an entry, and GC removes what nothing references. More in the CAS docs.

Virtual filesystem

An object store has no folders, only keys. t.VFS("uploads") gives you a directory-shaped view anyway, built from the / in your keys, and vfs.NewIOFS adapts it to io/fs.FS so the standard library can serve it:

fsys := t.VFS("uploads")
entries, err := fsys.ReadDir(ctx, "notes")

http.Handle("/files/", http.StripPrefix("/files/", http.FileServer(http.FS(vfs.NewIOFS(ctx, fsys)))))

The stream package handles chunked transfers with backpressure, resumable streams and progress hooks. Start with the VFS docs and the streaming engine.

Use it with Forge

The github.com/xraph/trove/extension module plugs Trove into a Forge app. It opens the storage driver from a DSN during Register, creates the default bucket, provides *trove.Trove through Forge's DI container, reports health and closes the driver on shutdown. It needs Go 1.26, the same as Forge.

go get github.com/xraph/trove/extension@latest
go get github.com/xraph/trove/drivers/s3driver@latest
import (
	"github.com/xraph/forge"
	"github.com/xraph/trove"
	_ "github.com/xraph/trove/drivers/s3driver"
	troveext "github.com/xraph/trove/extension"
)

app := forge.NewApp(forge.AppConfig{
	Name:        "files",
	HTTPAddress: ":8080",
	Extensions: []forge.Extension{
		troveext.New(troveext.WithConfig(troveext.Config{
			StorageDriver: "s3://us-east-1/my-bucket",
			DefaultBucket: "uploads",
		})),
	},
})

err := app.Router().POST("/notes", func(ctx forge.Context) error {
	t, err := forge.Inject[*trove.Trove](app.Container())
	if err != nil {
		return err
	}

	info, err := t.Put(ctx.Context(), "uploads", "notes/hello.txt", strings.NewReader("hello"))
	if err != nil {
		return err
	}

	return ctx.JSON(http.StatusCreated, info)
})

The extension registers the file, local and mem schemes itself. For S3, GCS, Azure or SFTP you blank-import the driver package so its scheme is registered. With no storage_driver at all, you get an in-memory store, which is handy in tests and a surprise in production.

If the container holds a *grove.DB (from the Grove extension, on Postgres, SQLite or MongoDB), the Trove extension keeps object and bucket metadata there and runs its own migrations during Register. It looks the database up while it registers, so list the Grove extension before Trove. It also registers a contributor for the Forge Dashboard, with a content route at /dashboard/trove/content that serves previews, downloads and uploads through short-lived signed tickets.

The REST handlers in extension/handler (buckets, objects, multipart uploads, CAS) form a plain http.Handler that you build with handler.New and mount yourself. Integrations with Chronicle, Vault, Warden and Dispatch are in extension/hooks, and you wire those yourself too.

Telemetry needs no wiring. Every storage operation records a span under your request's span, and the extension puts forge_trove_operation_duration_seconds, forge_trove_object_bytes_total and the scan and CAS counters on /_/metrics. Outside Forge, pass an observe.Recorder to trove.WithMetrics. The observability page lists every span and metric.

Configuration

The extension reads extensions.trove from your Forge config, then falls back to a top-level trove key. YAML wins for string fields, and a true you set in code turns a flag on even when YAML leaves it off.

extensions:
  trove:
    storage_driver: s3://us-east-1/my-bucket
    default_bucket: uploads
    enable_compression: true
Key Option Default What it does
storage_driver WithConfig in-memory Storage DSN, resolved through the driver registry
default_bucket WithConfig default Bucket used when you don't name one, created at startup
grove_database WithGroveDatabase(name) unnamed *grove.DB Which named Grove database holds the metadata
disable_migrate WithDisableMigrate() false Skips the metadata migrations
enable_compression WithConfig false Adds the Zstandard middleware
enable_cas WithConfig false Turns on CAS with SHA-256
stores (list of name, storage_driver, grove_database, default_bucket, enable_cas, enable_compression) WithFileStore, WithFileStoreDSN Several named stores, each provided by name, plus a *TroveManager
default WithDefaultFileStore(name) first store The store you get as the unnamed *trove.Trove
dashboard_content_path WithConfig /dashboard/trove/content Where the dashboard content route mounts
dashboard_max_upload_bytes WithConfig 64 MiB Largest upload the dashboard accepts
dashboard_content_secret WithConfig random per process Signs content links, at least 32 bytes. Set it when you run more than one instance

WithTroveOption and WithTroveOptionFor(store, opt) pass any trove.Option, such as your own middleware or routing rules, through to the instances the extension builds. The Forge extension guide and the configuration reference go further.

Documentation

Topic Docs
Install and store a first object Getting started, basic storage
Pick and configure a backend Driver system, capabilities, driver interface
Encrypt, compress or scan objects Middleware pipeline, middleware setup
Split storage across backends Multi-backend setup
Upload large files Streaming uploads
Handle errors and tenants Errors, multi-tenancy
Run it inside Forge Forge extension, Grove store, dashboard
Trace and measure it Observability

The exported Go API is on pkg.go.dev, and the docs source is in docs/.

FAQ

How do I use S3 and local storage with the same Go code?

Write your code against *trove.Trove and choose the driver when you start up: localdriver with a file:// DSN in development, s3driver with an s3:// DSN in production. The calls and the error values stay the same.

Does Trove work with MinIO, Cloudflare R2 or other S3-compatible storage?

The S3 driver takes an endpoint and path_style parameter in its DSN, as in s3://KEY:SECRET@us-east-1/bucket?endpoint=http://localhost:9000&path_style=true, and the integration tests run against MinIO. Other S3-compatible services work the same way if they accept the S3 API at a custom endpoint.

How do I encrypt files at rest in Go?

Add encrypt.New(encrypt.WithKeyProvider(...)) with trove.WithMiddleware. Objects are sealed with AES-256-GCM before they reach the driver and opened again on Get, and the key provider can fetch the 32-byte key from wherever you keep secrets.

Can I use Trove without Forge?

Yes. The root module has no xraph dependencies at all, so you can drop it into any Go service, and you only need Forge if you import extension.

Contributing

Issues and focused pull requests are welcome on GitHub. Build with GOWORK=off, and test each module from its own directory, since ./... stops at module boundaries:

git clone https://github.com/xraph/trove.git
cd trove
GOWORK=off go build ./...
make test-race
make lint
(cd extension && GOWORK=off go test -race -count=1 ./...)
(cd drivers/s3driver && GOWORK=off go test -race -count=1 ./...)

The extension, gcsdriver and sftpdriver modules need Go 1.26. The driver integration tests sit behind the integration build tag and need a local MinIO, Azurite, fake-gcs-server or SFTP container, which the top of each integration_test.go shows how to start. Benchmarks need -tags bench. Several -all Makefile targets don't work in this repo, and AGENTS.md lists which ones, along with the CI jobs and the release workflow. Commits follow Conventional Commits.

License

MIT. See LICENSE.

Built by Rex Raphael and contributors, as part of the Forge forgery stack.

Documentation

Overview

Package trove provides a multi-backend object storage engine with bi-directional streaming, composable middleware, and first-class Forge transport integration.

Trove is to object/file storage what Grove is to databases — a polyglot, driver-based abstraction that generates native operations per storage backend while delivering a unified, composable API surface.

Quick Start

drv := localdriver.New()
drv.Open(ctx, "file:///tmp/storage")

t, err := trove.Open(drv)
if err != nil {
    log.Fatal(err)
}
defer t.Close(ctx)

// Upload an object.
err = t.Put(ctx, "my-bucket", "hello.txt", strings.NewReader("hello world"))

// Download an object.
reader, err := t.Get(ctx, "my-bucket", "hello.txt")
defer reader.Close()

Drivers

Every storage backend registers as a driver — the same pattern Grove uses for pgdriver, mongodriver, etc. Available drivers:

  • localdriver: Local filesystem storage
  • memdriver: In-memory storage (testing)
  • s3driver: AWS S3, MinIO, R2, DigitalOcean Spaces (Phase 3)
  • gcsdriver: Google Cloud Storage (Phase 3)
  • azuredriver: Azure Blob Storage (Phase 3)

Multi-Backend Routing

Trove supports multiple named backends simultaneously:

t, err := trove.Open(primaryDriver,
    trove.WithBackend("archive", archiveDriver),
    trove.WithRoute("*.log", "archive"),
)

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrNotFound is returned when a requested resource cannot be found.
	// It is the parent of ErrBucketNotFound and ErrObjectNotFound.
	ErrNotFound = driver.ErrNotFound

	// ErrBucketNotFound is returned when a bucket cannot be found.
	ErrBucketNotFound = driver.ErrBucketNotFound

	// ErrBucketExists is returned when creating a bucket that already exists.
	ErrBucketExists = driver.ErrBucketExists

	// ErrObjectNotFound is returned when an object cannot be found.
	ErrObjectNotFound = driver.ErrObjectNotFound

	// ErrPermissionDenied is returned when the backend rejects the operation
	// as unauthorized. Retrying will not help until access is granted.
	ErrPermissionDenied = driver.ErrPermissionDenied

	// ErrQuotaExceeded is returned when a storage quota or rate limit is
	// exceeded. Retrying later may succeed.
	ErrQuotaExceeded = driver.ErrQuotaExceeded

	// ErrInvalidPath is returned when a bucket name or object key is not
	// addressable — empty, containing a NUL byte, rooted, or resolving outside
	// the container it was addressed to. It does not unwrap to ErrNotFound:
	// nothing was looked up, so the request is malformed rather than the
	// resource missing, and an HTTP caller should answer 400 rather than 404.
	ErrInvalidPath = driver.ErrInvalidPath
)

Errors that drivers classify. These are aliases of the sentinels defined in the driver package — the canonical values live there so that drivers in their own modules can wrap them without importing this package. Because errors.Is compares by identity, trove.ErrObjectNotFound and driver.ErrObjectNotFound match each other.

ErrObjectNotFound and ErrBucketNotFound unwrap to ErrNotFound, so a caller that only needs to know "this is permanently missing" can match ErrNotFound and catch both.

View Source
var (
	// ErrKeyEmpty is returned when an object key is empty.
	ErrKeyEmpty = errors.New("trove: key is required")

	// ErrBucketEmpty is returned when a bucket name is empty.
	ErrBucketEmpty = errors.New("trove: bucket name is required")

	// ErrDriverClosed is returned when an operation is attempted on a closed driver.
	ErrDriverClosed = errors.New("trove: driver is closed")

	// ErrNilDriver is returned when Open is called with a nil driver.
	ErrNilDriver = errors.New("trove: driver is required")

	// ErrInvalidDSN is returned when a DSN string cannot be parsed.
	ErrInvalidDSN = errors.New("trove: invalid DSN")

	// ErrChecksumMismatch is returned when a checksum verification fails.
	ErrChecksumMismatch = errors.New("trove: checksum mismatch")

	// ErrContentBlocked is returned when content is rejected by scanning middleware.
	ErrContentBlocked = errors.New("trove: content blocked")

	// ErrStreamClosed is returned when writing to a closed stream.
	ErrStreamClosed = errors.New("trove: stream is closed")

	// ErrUploadExpired is returned when a resumable upload session has expired.
	ErrUploadExpired = errors.New("trove: upload session expired")

	// ErrBackendNotFound is returned when a named backend cannot be found.
	ErrBackendNotFound = errors.New("trove: backend not found")

	// ErrPoolClosed is returned when an operation targets a closed stream pool.
	ErrPoolClosed = errors.New("trove: stream pool is closed")

	// ErrStreamNotActive is returned when an operation requires an active stream.
	ErrStreamNotActive = errors.New("trove: stream is not active")

	// ErrMaxStreamsReached is returned when the pool's concurrency limit is hit.
	ErrMaxStreamsReached = errors.New("trove: maximum concurrent streams reached")
)

Sentinel errors returned by Trove operations.

Functions

func Permanent

func Permanent(err error) bool

Permanent reports whether err describes a condition that retrying cannot resolve, so the caller should fail the operation now rather than spend its backoff budget on attempts certain to fail the same way.

A missing object, a permission failure, and an already-existing bucket are permanent. An exceeded quota is not: it may be granted later. Neither is an unclassified error — a condition Trove has not mapped is assumed transient, because treating it as permanent would silently discard work whenever a backend grows a failure mode the drivers do not yet recognize.

reader, err := t.Get(ctx, "artifacts", key)
if err != nil {
    if trove.Permanent(err) {
        return job.Fail(err) // straight to the dead letter queue
    }
    return job.Retry(err) // transient — back off and try again
}

See the driver package for the full classification.

Types

type ChecksumAlgorithm

type ChecksumAlgorithm int

ChecksumAlgorithm identifies the hash algorithm used for integrity verification.

const (
	// SHA256 uses the SHA-256 hash algorithm (default).
	SHA256 ChecksumAlgorithm = iota
	// Blake3 uses the BLAKE3 hash algorithm (faster, modern).
	Blake3
	// XXHash uses the XXHash algorithm (non-cryptographic, fastest).
	XXHash
)

func (ChecksumAlgorithm) String

func (a ChecksumAlgorithm) String() string

String returns the algorithm name.

type Config

type Config struct {
	// DefaultBucket is the bucket used when none is specified.
	DefaultBucket string

	// ChunkSize is the size in bytes for streaming chunks.
	ChunkSize int64

	// PoolSize is the maximum number of concurrent streams.
	PoolSize int

	// ChecksumAlgorithm is the hash algorithm for integrity verification.
	ChecksumAlgorithm ChecksumAlgorithm

	// StreamBufferSize is the buffer size in bytes for stream operations.
	StreamBufferSize int

	// Retry configures retry behavior for storage operations.
	Retry RetryPolicy
}

Config holds the configuration for a Trove instance.

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns a Config with sensible defaults.

type Option

type Option func(*Trove) error

Option configures a Trove instance.

func WithBackend

func WithBackend(name string, drv driver.Driver) Option

WithBackend registers a named storage backend. Named backends can be selected explicitly via Backend() or automatically via routing rules.

func WithCAS

func WithCAS(alg cas.HashAlgorithm) Option

WithCAS enables content-addressable storage with the given hash algorithm. Open builds the CAS on the default driver once every option has run, so it records on the Recorder from WithMetrics wherever that option sits.

func WithChecksumAlgorithm

func WithChecksumAlgorithm(alg ChecksumAlgorithm) Option

WithChecksumAlgorithm sets the hash algorithm for integrity verification.

func WithChunkSize

func WithChunkSize(size int64) Option

WithChunkSize sets the size in bytes for streaming chunks.

func WithDefaultBucket

func WithDefaultBucket(name string) Option

WithDefaultBucket sets the bucket used when none is specified in operations.

func WithMetrics

func WithMetrics(r observe.Recorder) Option

WithMetrics records Trove's metrics on r: the duration of every facade operation, object bytes through Put and Get, scans by the scan middleware and, when CAS is enabled, CAS stores and garbage collection. Package observe lists each metric with its labels. A nil r, the default, records nothing.

Spans need no option. They go to the global OpenTelemetry tracer provider, which a Forge app installs.

func WithMiddleware

func WithMiddleware(mws ...middleware.Middleware) Option

WithMiddleware registers global middleware (read + write, all operations).

func WithMiddlewareAt

func WithMiddlewareAt(priority int, mws ...middleware.Middleware) Option

WithMiddlewareAt registers middleware with explicit priority (lower = runs first).

func WithPoolConfig

func WithPoolConfig(cfg stream.PoolConfig) Option

WithPoolConfig configures the default streaming pool. This overrides the pool created from PoolSize and ChunkSize in DefaultConfig.

func WithPoolSize

func WithPoolSize(n int) Option

WithPoolSize sets the maximum number of concurrent streams.

func WithReadMiddleware

func WithReadMiddleware(mws ...middleware.Middleware) Option

WithReadMiddleware registers middleware that only runs on the download/read path.

func WithRetry

func WithRetry(policy RetryPolicy) Option

WithRetry configures retry behavior for storage operations.

func WithRoute

func WithRoute(pattern, backend string) Option

WithRoute adds a pattern-based routing rule that directs matching keys to a named backend. Pattern uses filepath.Match syntax.

Example:

trove.WithRoute("*.log", "archive")   // All .log files → archive backend
trove.WithRoute("tmp/*", "local")     // All tmp/ keys → local backend

func WithRouteFunc

func WithRouteFunc(fn func(bucket, key string) string) Option

WithRouteFunc adds a custom routing function. The function receives the bucket and key and returns the backend name to use. Return empty string to fall through to the next rule or the default backend.

func WithScopedMiddleware

func WithScopedMiddleware(scope middleware.Scope, mws ...middleware.Middleware) Option

WithScopedMiddleware registers middleware with a composed scope (read + write).

func WithScopedReadMiddleware

func WithScopedReadMiddleware(scope middleware.Scope, mws ...middleware.Middleware) Option

WithScopedReadMiddleware registers read-only middleware with a composed scope.

func WithScopedWriteMiddleware

func WithScopedWriteMiddleware(scope middleware.Scope, mws ...middleware.Middleware) Option

WithScopedWriteMiddleware registers write-only middleware with a composed scope.

func WithStreamBufferSize

func WithStreamBufferSize(size int) Option

WithStreamBufferSize sets the buffer size in bytes for stream operations.

func WithVFS

func WithVFS(bucket string) Option

WithVFS sets the default bucket for VFS operations. When set, calling t.VFS() returns a filesystem view of this bucket.

func WithWriteMiddleware

func WithWriteMiddleware(mws ...middleware.Middleware) Option

WithWriteMiddleware registers middleware that only runs on the upload/write path.

type RetryPolicy

type RetryPolicy struct {
	// Strategy is the backoff strategy.
	Strategy RetryStrategy

	// MaxAttempts is the maximum number of retry attempts.
	MaxAttempts int

	// BaseDelay is the initial delay between retries.
	BaseDelay time.Duration

	// MaxDelay is the maximum delay between retries (for exponential backoff).
	MaxDelay time.Duration
}

RetryPolicy configures retry behavior for storage operations.

type RetryStrategy

type RetryStrategy int

RetryStrategy identifies the retry backoff strategy.

const (
	// RetryNone disables retries.
	RetryNone RetryStrategy = iota
	// RetryFixed uses a fixed delay between retries.
	RetryFixed
	// RetryExponential uses exponential backoff.
	RetryExponential
)

type Trove

type Trove struct {
	// contains filtered or unexported fields
}

Trove is the root handle for object storage operations. It wraps one or more storage drivers, routing operations through the configured backend(s).

func Open

func Open(drv driver.Driver, opts ...Option) (*Trove, error)

Open creates a new Trove instance with the given default driver and options.

Example:

t, err := trove.Open(localdriver.New(),
    trove.WithDefaultBucket("primary"),
    trove.WithChunkSize(8 * 1024 * 1024),
)

func (*Trove) Backend

func (t *Trove) Backend(name string) (*Trove, error)

Backend returns a new Trove handle pinned to the named backend. All operations on the returned handle bypass routing and go directly to the specified backend.

func (*Trove) Backends

func (t *Trove) Backends() []string

Backends returns the names of the backends registered with WithBackend, sorted. The default driver has no name and is never in the list, so an empty list means every operation goes to the default. A handle returned by Backend is pinned to one driver and returns an empty list.

func (*Trove) CAS

func (t *Trove) CAS() *cas.CAS

CAS returns the content-addressable storage engine, or nil if CAS is not enabled.

func (*Trove) Close

func (t *Trove) Close(ctx context.Context) error

Close gracefully shuts down the streaming pool and all drivers.

func (*Trove) Config

func (t *Trove) Config() Config

Config returns a copy of the current configuration.

func (*Trove) Copy

func (t *Trove) Copy(ctx context.Context, srcBucket, srcKey, dstBucket, dstKey string, opts ...driver.CopyOption) (info *driver.ObjectInfo, err error)

Copy copies an object within or across buckets.

func (*Trove) CreateBucket

func (t *Trove) CreateBucket(ctx context.Context, name string, opts ...driver.BucketOption) (err error)

CreateBucket creates a new bucket on the default driver.

func (*Trove) Delete

func (t *Trove) Delete(ctx context.Context, bucket, key string, opts ...driver.DeleteOption) (err error)

Delete removes an object.

func (*Trove) DeleteBucket

func (t *Trove) DeleteBucket(ctx context.Context, name string) (err error)

DeleteBucket removes a bucket from the default driver.

func (*Trove) Driver

func (t *Trove) Driver() driver.Driver

Driver returns the primary (default) storage driver.

func (*Trove) DriverFor

func (t *Trove) DriverFor(bucket, key string) driver.Driver

DriverFor returns the driver that serves bucket and key, after routing. Driver returns only the default; an object routed elsewhere is served by this one.

func (*Trove) Get

func (t *Trove) Get(ctx context.Context, bucket, key string, opts ...driver.GetOption) (obj *driver.ObjectReader, err error)

Get retrieves an object, returning a ReadCloser for the content. Data passes through the read middleware pipeline (e.g., decryption, decompression) after being retrieved from the driver.

Its span ends when Get returns, so it covers the driver's request and the response headers but not the time the caller spends reading the body.

func (*Trove) Head

func (t *Trove) Head(ctx context.Context, bucket, key string) (info *driver.ObjectInfo, err error)

Head returns object metadata without content.

func (*Trove) Health

func (t *Trove) Health(ctx context.Context) error

Health checks the health of the Trove by pinging its primary driver.

func (*Trove) List

func (t *Trove) List(ctx context.Context, bucket string, opts ...driver.ListOption) (it *driver.ObjectIterator, err error)

List returns objects matching the given options.

func (*Trove) ListBuckets

func (t *Trove) ListBuckets(ctx context.Context) (buckets []driver.BucketInfo, err error)

ListBuckets returns all buckets from the default driver.

func (*Trove) Pool

func (t *Trove) Pool() *stream.Pool

Pool returns the default streaming pool.

func (*Trove) Put

func (t *Trove) Put(ctx context.Context, bucket, key string, r io.Reader, opts ...driver.PutOption) (info *driver.ObjectInfo, err error)

Put stores an object from a reader. Data passes through the write middleware pipeline (e.g., encryption, compression) before reaching the driver.

func (*Trove) RemoveMiddleware

func (t *Trove) RemoveMiddleware(name string, scope middleware.Scope)

RemoveMiddleware removes all registrations matching the given middleware name and scope. If scope is nil, removes all registrations with that name.

func (*Trove) Resolver

func (t *Trove) Resolver() *middleware.Resolver

Resolver returns the middleware pipeline resolver.

func (*Trove) Stream

func (t *Trove) Stream(ctx context.Context, bucket, key string, dir stream.Direction, opts ...stream.Option) (*stream.Stream, error)

Stream creates a new managed stream within the default pool. The stream provides chunked transfer with backpressure, resumability, and lifecycle hooks.

func (*Trove) UseMiddleware

func (t *Trove) UseMiddleware(reg middleware.Registration)

UseMiddleware registers a middleware at runtime. Thread-safe.

func (*Trove) VFS

func (t *Trove) VFS(bucket ...string) *vfs.FS

VFS returns a virtual filesystem view of the given bucket. If no bucket is specified, it uses the VFS bucket (set via WithVFS) or falls back to the default bucket.

Directories

Path Synopsis
Package cas provides content-addressable storage for Trove.
Package cas provides content-addressable storage for Trove.
Package driver defines the core interfaces and types that every Trove storage backend must implement.
Package driver defines the core interfaces and types that every Trove storage backend must implement.
drivers
localdriver
Package localdriver provides a local filesystem storage driver.
Package localdriver provides a local filesystem storage driver.
memdriver
Package memdriver provides an in-memory storage driver for testing.
Package memdriver provides an in-memory storage driver for testing.
azuredriver module
gcsdriver module
s3driver module
sftpdriver module
extension module
Package id defines TypeID-based identity types for all Trove entities.
Package id defines TypeID-based identity types for all Trove entities.
Package internal contains core domain logic with zero Forge imports.
Package internal contains core domain logic with zero Forge imports.
telemetry
Package telemetry is the tracing and metrics plumbing Trove's packages share: the tracer every span comes from, span names and attribute keys, how a failed span is marked, and how a measurement reaches the caller's observe.Recorder.
Package telemetry is the tracing and metrics plumbing Trove's packages share: the tracer every span comes from, span names and attribute keys, how a failed span is marked, and how a measurement reaches the caller's observe.Recorder.
telemetrytest
Package telemetrytest records what Trove's telemetry emits, for tests: a span recorder installed as the global tracer provider, and an observe.Recorder that remembers every measurement and checks it against the metric definitions in package observe.
Package telemetrytest records what Trove's telemetry emits, for tests: a span recorder installed as the global tracer provider, and an observe.Recorder that remembers every measurement and checks it against the metric definitions in package observe.
Package middleware provides a composable, scope-aware interceptor pipeline for Trove's read/write data path.
Package middleware provides a composable, scope-aware interceptor pipeline for Trove's read/write data path.
compress
Package compress provides Zstd compression middleware for Trove.
Package compress provides Zstd compression middleware for Trove.
dedup
Package dedup provides content-hash deduplication middleware for Trove.
Package dedup provides content-hash deduplication middleware for Trove.
encrypt
Package encrypt provides AES-256-GCM encryption middleware for Trove.
Package encrypt provides AES-256-GCM encryption middleware for Trove.
scan
Package scan provides content scanning middleware for Trove.
Package scan provides content scanning middleware for Trove.
watermark
Package watermark provides invisible metadata watermarking middleware for Trove.
Package watermark provides invisible metadata watermarking middleware for Trove.
Package observe defines the metrics Trove records and the small interface it records them through.
Package observe defines the metrics Trove records and the small interface it records them through.
Package stream provides a managed streaming engine for Trove with backpressure, chunked transfers, buffer pooling, and concurrency control.
Package stream provides a managed streaming engine for Trove with backpressure, chunked transfers, buffer pooling, and concurrency control.
Package trovetest provides testing utilities for Trove storage drivers.
Package trovetest provides testing utilities for Trove storage drivers.
Package vfs provides a virtual filesystem abstraction over Trove object storage.
Package vfs provides a virtual filesystem abstraction over Trove object storage.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL