Trove

Capabilities

Optional driver capabilities via interface extension: multipart, presign, versioning, notifications, lifecycle, range reads.

The core driver.Driver interface is intentionally minimal. Advanced features are exposed through optional capability interfaces that a driver may or may not implement. This keeps the contract simple while allowing each backend to expose its native strengths.

Capability Check Pattern

Use a Go type assertion to check if a driver supports a capability at runtime:

drv := t.Driver()

if mp, ok := drv.(driver.MultipartDriver); ok {
    // Driver supports multipart uploads
    uploadID, err := mp.InitiateMultipart(ctx, "bucket", "large-file.bin")
    // ...
}

This pattern is safe -- if the driver does not implement the interface, the assertion returns false and no capability methods are called.

MultipartDriver

Enables chunked uploads where each part can be uploaded independently and assembled on the server.

type MultipartDriver interface {
    Driver
    InitiateMultipart(ctx context.Context, bucket, key string, opts ...PutOption) (uploadID string, err error)
    UploadPart(ctx context.Context, bucket, key, uploadID string, partNum int, r io.Reader) (*PartInfo, error)
    CompleteMultipart(ctx context.Context, bucket, key, uploadID string, parts []PartInfo) (*ObjectInfo, error)
    AbortMultipart(ctx context.Context, bucket, key, uploadID string) error
}

Each part returns a PartInfo with the part number, ETag, and size. Pass all part infos to CompleteMultipart to finalize the upload.

uploadID, _ := mp.InitiateMultipart(ctx, "data", "backup.tar.gz")

var parts []driver.PartInfo
for i, chunk := range chunks {
    part, _ := mp.UploadPart(ctx, "data", "backup.tar.gz", uploadID, i+1, chunk)
    parts = append(parts, *part)
}

info, _ := mp.CompleteMultipart(ctx, "data", "backup.tar.gz", uploadID, parts)

PresignDriver

Generates time-limited URLs for direct client access without proxying through your server.

type PresignDriver interface {
    Driver
    PresignGet(ctx context.Context, bucket, key string, expires time.Duration) (string, error)
    PresignPut(ctx context.Context, bucket, key string, expires time.Duration) (string, error)
}
if ps, ok := drv.(driver.PresignDriver); ok {
    downloadURL, _ := ps.PresignGet(ctx, "media", "video.mp4", 15*time.Minute)
    uploadURL, _ := ps.PresignPut(ctx, "uploads", "user-avatar.jpg", 5*time.Minute)
}

VersioningDriver

Manages object version history for audit trails and point-in-time recovery.

type VersioningDriver interface {
    Driver
    ListVersions(ctx context.Context, bucket, key string) ([]VersionInfo, error)
    GetVersion(ctx context.Context, bucket, key, versionID string) (*ObjectReader, error)
    DeleteVersion(ctx context.Context, bucket, key, versionID string) error
    RestoreVersion(ctx context.Context, bucket, key, versionID string) (*ObjectInfo, error)
}

VersionInfo includes the version ID, key, size, timestamp, and whether it is the latest version.

if vd, ok := drv.(driver.VersioningDriver); ok {
    versions, _ := vd.ListVersions(ctx, "docs", "contract.pdf")
    for _, v := range versions {
        fmt.Printf("Version %s, %d bytes, latest=%v\n", v.VersionID, v.Size, v.IsLatest)
    }

    // Restore a previous version
    vd.RestoreVersion(ctx, "docs", "contract.pdf", versions[1].VersionID)
}

NotificationDriver

Provides real-time change notifications via a Go channel.

type NotificationDriver interface {
    Driver
    Watch(ctx context.Context, bucket string, opts ...WatchOption) (<-chan ObjectEvent, error)
}

Event types:

ConstantValueDescription
EventCreatedobject.createdA new object was stored
EventDeletedobject.deletedAn object was removed
EventUpdatedobject.updatedAn existing object was overwritten
if nd, ok := drv.(driver.NotificationDriver); ok {
    events, _ := nd.Watch(ctx, "uploads")

    go func() {
        for event := range events {
            fmt.Printf("[%s] %s/%s\n", event.Type, event.Bucket, event.Key)
        }
    }()
}

LifecycleDriver

Configures automatic object expiration and storage class transitions.

type LifecycleDriver interface {
    Driver
    SetLifecycle(ctx context.Context, bucket string, rules []LifecycleRule) error
    GetLifecycle(ctx context.Context, bucket string) ([]LifecycleRule, error)
}
if lc, ok := drv.(driver.LifecycleDriver); ok {
    lc.SetLifecycle(ctx, "logs", []driver.LifecycleRule{
        {
            ID:             "expire-old-logs",
            Prefix:         "debug/",
            ExpirationDays: 30,
            Enabled:        true,
        },
        {
            ID:                "archive-access-logs",
            Prefix:            "access/",
            TransitionDays:    90,
            TransitionStorage: "GLACIER",
            Enabled:           true,
        },
    })
}

RangeDriver

Supports byte-range reads for resumable downloads and partial content retrieval.

type RangeDriver interface {
    Driver
    GetRange(ctx context.Context, bucket, key string, offset, length int64) (*ObjectReader, error)
}

A length of -1 reads from the offset to the end of the object.

if rd, ok := drv.(driver.RangeDriver); ok {
    // Read bytes 1000-1999
    reader, _ := rd.GetRange(ctx, "media", "movie.mp4", 1000, 1000)
    defer reader.Close()
}

ServerCopyDriver

Performs server-side copy to avoid downloading and re-uploading data.

type ServerCopyDriver interface {
    Driver
    ServerCopy(ctx context.Context, src, dst ObjectRef) (*ObjectInfo, error)
}

ObjectRef identifies an object by bucket and key:

if sc, ok := drv.(driver.ServerCopyDriver); ok {
    sc.ServerCopy(ctx,
        driver.ObjectRef{Bucket: "source", Key: "data.csv"},
        driver.ObjectRef{Bucket: "backup", Key: "data.csv"},
    )
}

Driver Capability Matrix

CapabilityLocalMemoryS3GCSAzureSFTP
MultipartDriver----YesYesYes--
PresignDriver----YesYesYes--
VersioningDriver----Yes------
NotificationDriver----YesYes----
LifecycleDriver----YesYes----
RangeDriver----YesYesYes--
ServerCopyDriver----YesYesYes--

On this page