Documentation
¶
Overview ¶
Package objstore is a minimal object-store boundary for the few things this service stores next to — but not inside — a kopia repository.
kopia owns everything under a Space's repo prefix; nothing here ever writes there (see snapshot.RepoPrefix vs snapshot.EnvelopeKey and snapshot.ServerEnvelopeKey). The two users are:
- the backup worker, publishing a Space's two key envelopes to the target: the RK-wrapped one so a Take-Out is self-contained (Phase 5, Path A) and the SRW-wrapped one so the state Space is not the only copy of the service's own (R1 amendment), and
- the Take-Out CLI, copying a Space's ciphertext objects out of S3 with no OpenCloud dependency and no key input at all.
It is an interface so both can be exercised against a local directory in unit tests, with S3 reserved for integration tests (AGENTS.md testability rule).
Index ¶
- Constants
- Variables
- func EndpointURL(endpoint string, disableTLS bool) string
- func RequireCredentials(accessKeyID, secretAccessKey string) error
- type Capabilities
- type CheckOutcome
- type Checker
- type CheckerFunc
- type DirStore
- type ObjectInfo
- type ObjectLockSupport
- type Prober
- type S3Checker
- type S3Config
- type S3Prober
- type S3Store
- type Store
Constants ¶
const DefaultCheckTimeout = 15 * time.Second
DefaultCheckTimeout bounds a check against an unresponsive endpoint. It is short because a human is waiting on the answer.
const DefaultProbeTimeout = 30 * time.Second
DefaultProbeTimeout bounds a capability probe against an unresponsive target.
Variables ¶
var ErrMissingCredentials = errors.New("objstore: S3 access key id and secret access key are both required")
ErrMissingCredentials means an S3 access was attempted without both halves of a static credential.
var ErrNotFound = errors.New("objstore: object not found")
ErrNotFound is returned when no object exists at a key.
Functions ¶
func EndpointURL ¶
EndpointURL normalises a bare host:port into a URL, leaving an explicit scheme untouched.
func RequireCredentials ¶
RequireCredentials refuses an incomplete static credential. Every S3 client in this module goes through it, so none of them can fall back to ambient credentials. The error never echoes either value.
Types ¶
type Capabilities ¶
type Capabilities struct {
// ObjectLock is whether the backend implements Object Lock.
ObjectLock ObjectLockSupport
// ObjectLockEnabled is true only when the bucket reports an active Object
// Lock configuration. It is meaningful only when ObjectLock is
// ObjectLockSupported.
ObjectLockEnabled bool
// VersioningEnabled reports the bucket's versioning status.
//
// False does not mean "the backend cannot do versioning". Garage answers
// GetBucketVersioning successfully with an empty status — byte for byte what
// a real S3 bucket that has never had versioning enabled returns — so the
// two are indistinguishable without attempting to enable it, which is a
// write this probe will not make against somebody's backup bucket. Pinned by
// TestGarageCapabilitySignals.
VersioningEnabled bool
}
Capabilities is what one probe observed about a bucket.
func (Capabilities) Immutable ¶
func (c Capabilities) Immutable() bool
Immutable reports whether this bucket actually makes objects undeletable today. It is deliberately strict: only an enabled Object Lock configuration counts, because versioning alone does not stop a credential that may delete from deleting versions too.
type CheckOutcome ¶
type CheckOutcome string
CheckOutcome is the coarse verdict of a reachability check.
const ( // CheckOK means the credentials listed the bucket. CheckOK CheckOutcome = "ok" // CheckUnreachable means nothing answered: DNS, connection refused, TLS. CheckUnreachable CheckOutcome = "unreachable" // CheckTimeout means something answered too slowly, or not at all in time. CheckTimeout CheckOutcome = "timeout" // CheckAuthFailed means the credentials were rejected as credentials — an // unknown key id or a bad signature. Typically a typo in the secret. CheckAuthFailed CheckOutcome = "auth_failed" // CheckDenied means the credentials are valid but may not read this bucket. CheckDenied CheckOutcome = "denied" // CheckBucketMissing means the endpoint has no such bucket. CheckBucketMissing CheckOutcome = "bucket_missing" // CheckUnknown means the endpoint answered something this code does not // recognise. The honest answer is that we do not know, exactly as with an // unreadable Object Lock configuration — inventing a nicer verdict would // send an admin looking in the wrong place. CheckUnknown CheckOutcome = "unknown" )
type Checker ¶
type Checker interface {
Check(ctx context.Context, cfg S3Config, prefix string) CheckOutcome
}
Checker reports whether a target is usable with the given configuration. It is an interface for the same reason Prober is: it is an S3 boundary, and callers take it as a dependency so their tests do not reach for a network.
type CheckerFunc ¶
type CheckerFunc func(ctx context.Context, cfg S3Config, prefix string) CheckOutcome
CheckerFunc adapts a function to Checker.
func (CheckerFunc) Check ¶
func (f CheckerFunc) Check(ctx context.Context, cfg S3Config, prefix string) CheckOutcome
Check implements Checker.
type DirStore ¶
type DirStore struct {
// Root is the directory that plays the role of the bucket.
Root string
}
DirStore is a Store over a local directory, mapping an object key onto a relative file path. It backs unit tests and local development, and gives the offline decrypt CLI a way to read a Take-Out that was produced as a plain directory tree.
type ObjectInfo ¶
type ObjectInfo struct {
// Key is the full object key, including any prefix.
Key string
// Size is the object size in bytes.
Size int64
}
ObjectInfo describes one stored object. It carries no credentials and no key material.
type ObjectLockSupport ¶
type ObjectLockSupport int
ObjectLockSupport is what a probe was able to establish about a bucket's support for S3 Object Lock.
const ( // ObjectLockUnknown means the probe could not tell — typically because the // credential it used may not read the bucket's configuration. ObjectLockUnknown ObjectLockSupport = iota // ObjectLockUnsupported means the backend answered that it does not // implement Object Lock at all. ObjectLockUnsupported // ObjectLockSupported means the backend implements Object Lock. It says // nothing about whether the bucket has it enabled; see Capabilities. ObjectLockSupported )
func (ObjectLockSupport) String ¶
func (s ObjectLockSupport) String() string
String renders the support level for logs.
type Prober ¶
type Prober interface {
Probe(ctx context.Context, cfg S3Config) (Capabilities, error)
}
Prober observes a target's immutability capabilities. It is an interface because it is an S3 boundary: callers take it as a dependency so their tests do not reach for a network.
type S3Checker ¶
type S3Checker struct {
// Timeout bounds the check; zero means DefaultCheckTimeout.
Timeout time.Duration
// Logger receives the cause of every failed check; nil logs nothing.
Logger *slog.Logger
}
S3Checker is the real Checker.
type S3Config ¶
type S3Config struct {
// Endpoint is a URL or a bare host:port.
Endpoint string
Region string
Bucket string
// AccessKeyID / SecretAccessKey are the target's S3 credentials. Both are
// required: there is no fallback to ambient AWS credentials (see NewS3).
AccessKeyID string
SecretAccessKey string
// UsePathStyle forces path-style addressing (required for Garage).
UsePathStyle bool
// DisableTLS talks plain HTTP; it also decides the scheme when Endpoint
// carries none.
DisableTLS bool
}
S3Config describes how to reach an S3 target. Credentials are plaintext and therefore live only in process memory for the duration of a command or run; they are never logged and never serialized (decisions.md #14).
type S3Prober ¶
type S3Prober struct {
// Timeout bounds the probe. It is a diagnostic, so it must never be able to
// spend a caller's whole budget; zero means DefaultProbeTimeout.
Timeout time.Duration
}
S3Prober is the real Prober.
func (S3Prober) Probe ¶
Probe asks a bucket what it supports. It makes read-only calls and changes nothing.
An error means the probe itself could not be run (the endpoint was unreachable, the client could not be built). A backend that answers "I do not implement this" is not an error — it is the answer.
type S3Store ¶
type S3Store struct {
// contains filtered or unexported fields
}
S3Store is the S3-backed Store.
func NewS3 ¶
NewS3 builds an S3-backed store.
The SDK's default checksum behaviour (WhenSupported) breaks multipart GET against Garage, which returns no SDK-verifiable composite checksum (phase-0-findings.md Spike 1, gotcha 4), so both checksum knobs are pinned to WhenRequired.
Credentials are static and explicit. Without them the SDK would walk its default chain — environment, shared files, and finally the cloud metadata service, which an admin's laptop or a home server answers by retrying against 169.254.169.254 (review-2026-10.md F5). Missing credentials are refused here, before any request is made.
type Store ¶
type Store interface {
// Get streams the object at key. The caller closes the reader.
// It returns ErrNotFound when the key does not exist.
Get(ctx context.Context, key string) (io.ReadCloser, error)
// Put stores body at key. Implementations read body to EOF.
Put(ctx context.Context, key string, body []byte) error
// List returns every object whose key starts with prefix.
List(ctx context.Context, prefix string) ([]ObjectInfo, error)
}
Store is the read/write object boundary.
Implementations must be safe for concurrent use.