Go library

Using it as a Go library

ImgYoung's storage layer ships as three public packages you can go get — a driver interface, an rclone implementation, and multi-store failover primitives.

ImgYoung's multi-store reads and writes, channel encryption, and failover are exposed as public Go packages, and the application itself reaches storage through these same packages. If your own Go project needs to write one object to several stores, fall back automatically when reading, or check whether a store is usable, you can reuse them directly.

The three packages

PackageWhat it providesDepends on
store/driverThe Driver interface (Put / Open / Stat / Delete / List), ObjectInfo, and the sentinel ErrNotFoundStandard library only
store/rcloneA Driver backed by any rclone backend, with an optional crypt layer, OAuth token write-back, password parameter encoding, and export of an rclone configuration file equivalent to the driverstore/driver and rclone
store/failoverStateless failover primitives over a list of Drivers: write until N copies succeed, read from the first readable one, light and deep health checksstore/driver only
store/failover ──→ store/driver ←── store/rclone ──→ github.com/rclone/rclone
  • store/driver imports neither rclone nor any concrete backend. You can implement Driver yourself and never touch rclone.
  • store/rclone is an optional implementation: rclone is compiled in only if you import it. It registers every rclone backend, so Backend accepts any backend name rclone supports.
  • store/failover works purely against the driver.Driver interface and treats rclone drivers and your own drivers the same way.

None of the three packages depend on the application's internal/ code; a lint rule (depguard) enforces that boundary.

Installation

Terminal
# Interface and failover primitives only (bring your own driver)
go get github.com/x-haose/imgyoung/store/failover@latest

# Plus the rclone implementation
go get github.com/x-haose/imgyoung/store/rclone@latest

The module's go.mod declares Go 1.26.9; your toolchain must be at least that version.

One module, only the packages you use get compiled

store/ lives in the same module as the application, github.com/x-haose/imgyoung; it is not split into a separate module, because a separate module would need replace directives to tie the two together during development in the repository. Go compiles only imported packages: if you import just store/driver and store/failover, neither rclone nor any application code ends up in your binary.

The size difference is substantial. A minimal program that prints one line, built on darwin/arm64 (not stripped):

Imported packageBinary size
store/failover (pulls in store/driver)about 2.7 MB
store/rcloneabout 100 MB

The second one is large because store/rclone imports rclone's backend/all, which registers every backend at once.

Dependency versions can be raised

Once you import any store package, this module joins your dependency graph, and the module versions listed in its go.mod take part in Go's minimal version selection (MVS). If your project also depends on one of those modules (the AWS SDK, golang.org/x/..., and so on), its version may be raised to at least what this module requires. This only affects version selection; what gets compiled, and how large the binary is, still depends only on the packages you import.

Compatibility promise

Exported identifiers in all three packages stay backward compatible within major version v1: types, functions, methods, fields, and sentinel errors will not be removed or change signature.

  • Error message text is written in Chinese; do not depend on its content. Classify errors with errors.Is against the exported sentinels (driver.ErrNotFound, failover.ErrUnavailable, and so on).
  • Backend parameter names and behavior in store/rclone come from rclone itself and follow the rclone version pinned in this module's go.mod.
  • Packages under internal/ cannot be imported from outside the module (a Go rule) and are not covered.

What the library does not include

store only performs one operation against an ordered list of drivers. It keeps no state and runs nothing in the background. The following pieces are implemented in the application, layered on top of the same primitives; if your project needs them, you build them yourself:

CapabilityDescriptionWhere the application implements it
Replica state persistenceWhich drivers hold a copy of which object. failover.Write only returns the outcome of each attempt; recording it is up to youblob_channels table
Asynchronous replicationReturn once N copies are written and fill in the remaining drivers in the background. See Failover · Building your own replicationinternal/core/replication
ReconciliationPeriodically List each store, compare against your records in both directions, delete orphans, and re-copy missing replicasinternal/core/replication
SchedulingHealth check intervals, retries with backoff, a task queuecmd/imgyoung, the River task queue
Driver instance managementRebuild a driver and Close the old one when its configuration changes; keep health state and switching policyinternal/core/channel
Cloud drive authorizationThe library only writes back refreshed tokens; obtain the initial token with rclone authorizeinternal/core/channel

For how the application puts these pieces together, see Architecture.

Next steps

On this page