config

package
v1.18.0 Latest Latest
Warning

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

Go to latest
Published: Aug 2, 2026 License: MIT Imports: 6 Imported by: 0

Documentation

Overview

Package config holds the resolved configuration for opening a Cloudstic repository: which store to talk to, which credentials unlock it, and how the client should behave.

It is the boundary between how configuration is *expressed* — command-line flags, environment variables, a profiles YAML file — and how a repository is *opened*. Values here are fully resolved: a store URI has been chosen, and secret references have already been read into the credentials they name. Nothing in this package performs I/O against a store or a cloud provider; pkg/open does that, and takes these values as input.

The split matters for what it costs to import. This package depends only on pkg/profile and pkg/secretref, so reading a profiles file and resolving it into a configuration — to validate it, display it, or hand it to something else — pulls in no cloud SDK. Constructing a store from that configuration necessarily does, which is why construction lives in pkg/open (RFC 0022 §7).

Zero values are the correct defaults. A Client{Store: …} with nothing else set behaves the way the cloudstic CLI behaves with no flags passed, which is why packfiles are expressed as DisablePackfile rather than Packfile: the client enables them by default, so a positive field would silently turn them off for every caller who did not think to mention them.

Index

Constants

View Source
const (
	ProviderGoogle   = "google"
	ProviderOneDrive = "onedrive"
)

Auth providers, as written in a profiles file's `provider:` field.

Variables

This section is empty.

Functions

func DefaultAuthTokenRef

func DefaultAuthTokenRef(provider, name string) string

DefaultAuthTokenRef returns the secret reference an auth entry's OAuth token is stored under when the user names no location: a token in the config directory's managed store, keyed by provider and entry name.

An empty name yields the "default" entry, which is what an auth entry created implicitly by `cloudstic backup gdrive` uses.

func ResolveValue

func ResolveValue(ctx context.Context, r *secretref.Resolver, key, inline, ref string) (string, error)

ResolveValue returns the effective value of a profile field that may be given either inline or as a scheme://path secret reference.

An inline value wins and short-circuits: the reference is not consulted, and a broken reference alongside an inline value is not an error. When neither is set the result is the empty string, which callers read as "the profile says nothing about this field".

key names the profiles-file entry and appears in the error, so a failure says which entry to go fix rather than only which backend refused.

func SourceURIForProvider

func SourceURIForProvider(provider string) (string, error)

SourceURIForProvider returns the source URI that authenticates a provider without addressing any particular content.

The provider name and the source scheme are not the same word — Google's provider is "google" but its scheme is "gdrive" — and the URI deliberately carries no drive name or path. Both cloud sources resolve a drive name eagerly during construction, so naming one here would make authenticating depend on a drive existing, which is backwards: you authenticate in order to find out which drives there are.

Types

type B2

type B2 struct {
	KeyID  string
	AppKey string
}

B2 holds Backblaze B2 application key credentials.

type Backup

type Backup struct {
	Source Source

	// Tags are recorded on the snapshot and are what `forget -tag` filters on.
	Tags []string

	// IgnoreEmpty skips writing a snapshot when nothing changed.
	IgnoreEmpty bool

	// AuthRef names the profiles-file auth entry that supplied the cloud
	// credentials in Source, or that should. MergeProfileBackup reads it and
	// records the entry it used, so the resolved configuration says where the
	// credentials came from.
	AuthRef string

	DryRun  bool
	Verbose bool
}

Backup is the resolved configuration for one backup run: what to read, and how to record it.

Zero values are the correct defaults, as elsewhere in this package: a Backup with only Source set behaves the way `cloudstic backup` behaves with no flags beyond -source.

func ApplyProfileAuth

func ApplyProfileAuth(base Backup, decided FieldSet, auth profile.Auth) (Backup, error)

ApplyProfileAuth folds a named auth entry's credentials into base, after checking the entry's provider against what the source's scheme requires.

The provider rule is the reason this is exported rather than left inside MergeProfileBackup: a gdrive source needs a "google" entry and a onedrive source a "onedrive" one, and a caller pairing them the other way should get an error rather than an unauthenticated run. Anything acting on a profiles file needs that check, including code that writes an auth entry before using it.

func MergeProfileBackup

func MergeProfileBackup(base Backup, decided FieldSet, name string, cfg *profile.Config) (Backup, error)

MergeProfileBackup returns base with profile name's backup settings folded in underneath it: every field the caller has already decided is left alone, and the rest come from the profile.

decided names the fields the caller owns, using the same FieldSet as MergeProfileStore — one set may name both store and backup fields, since a caller usually resolves both from one mechanism.

An auth_ref on the profile, or FieldAuthRef among the decided fields with base naming one, supplies the cloud credentials. Its provider must match the source's scheme: pointing a Google Drive source at a OneDrive auth entry is an error rather than a silently unauthenticated run.

This is the counterpart to MergeProfileStore and applies the same precedence. The two are separate functions because a profile's store is shared between profiles by name while its backup settings are its own, so a caller resolving a store by name (`cloudstic store verify`) needs the first without the second.

type Client

type Client struct {
	Store           Store
	Unlock          Unlock
	DisablePackfile bool
	Quiet           bool
	JSON            bool

	// Verbose asks the reporter for per-item detail. It is a presentation
	// choice, which is why it lives here and on the reporter rather than as an
	// option on each operation.
	Verbose bool
}

Client is the resolved configuration for opening a repository client.

func FromProfileStore

func FromProfileStore(ctx context.Context, s profile.Store, r *secretref.Resolver) (Client, error)

FromProfileStore resolves a profile's store definition into a complete client configuration, reading every secret reference it names through r.

This is the whole of what a profiles file says about reaching a repository: where the store is, and which credentials open it. Fields the profile does not mention are left at their zero value, which is the correct default (see the package comment), so the result is directly usable — pass it to pkg/open, or inspect it, without further filling in.

Secret references are resolved here rather than at connect time, so a profile naming a secret that cannot be read fails while it is still obvious which profile and which field are at fault, instead of surfacing later as an authentication failure from a cloud provider.

Use MergeProfileStore when you have configuration of your own to layer over the profile. This is that function with nothing decided, and is defined that way rather than reimplemented: against a zero Client the two field groups it distinguishes collapse into the same behaviour, so a separate implementation would be a second copy of the field list waiting to disagree with the first.

func MergeProfileStore

func MergeProfileStore(ctx context.Context, base Client, decided FieldSet, s profile.Store, r *secretref.Resolver) (Client, error)

MergeProfileStore returns base with the profile's store definition folded in underneath it: every field the caller has already decided is left alone, and the rest come from the profile.

decided names the fields the caller owns. Pass FieldsSetIn(base) when a non-empty value is what "I decided this" means for your mechanism, or an explicit NewFieldSet when empty is a choice you need to keep. A nil set decides nothing, making this equivalent to FromProfileStore.

The two groups of fields deliberately behave differently, and the difference is load-bearing:

  • Location and KMS settings are taken only when the profile actually names one, so a profile that is silent about them leaves whatever base had.
  • Credentials are taken whenever the caller has not decided them, which clears a value base carried. That is what makes selecting a profile override an ambient credential from the environment: a profile is an explicit choice of *which* store to talk to, so reaching it with half a credential set inherited from the environment would be worse than failing to reach it at all.

A decided field is never resolved, so a broken secret reference on a field that is about to be replaced is not an error.

type Field

type Field string

Field names one setting a profile can supply, for callers that layer their own configuration mechanism over a profile.

It exists because "which fields have you already decided?" cannot be expressed by a Client alone: a plain string field cannot distinguish "unset" from "deliberately empty", and the difference decides whether a profile's value applies. Naming each field as a constant makes that question answerable without a bare string, which silently meant "not decided" whenever it was misspelled — and there were two plausible spellings of every field, since the profiles file writes s3_access_key where the cloudstic flag is -s3-access-key.

A Field's string value is the cloudstic flag that carries it, so a program layering flags of its own can use the flag name directly; ProfileKey gives the profiles-file spelling of the same field.

const (
	FieldSourceURI         Field = "source"
	FieldTags              Field = "tag"
	FieldExcludes          Field = "exclude"
	FieldExcludeFile       Field = "exclude-file"
	FieldIgnoreEmpty       Field = "ignore-empty-snapshot"
	FieldSkipNativeFiles   Field = "skip-native-files"
	FieldVolumeUUID        Field = "volume-uuid"
	FieldGoogleCreds       Field = "google-credentials"
	FieldGoogleCredsRef    Field = "google-credentials-ref"
	FieldGoogleCredsJSON   Field = "google-credentials-json"
	FieldGoogleTokenFile   Field = "google-token-file"
	FieldGoogleTokenRef    Field = "google-token-ref"
	FieldOneDriveClientID  Field = "onedrive-client-id"
	FieldOneDriveTokenFile Field = "onedrive-token-file"
	FieldOneDriveTokenRef  Field = "onedrive-token-ref"
	FieldAuthRef           Field = "auth-ref"
)

The fields a profile can supply about what to back up, as distinct from where the repository is (see StoreFields). A field's string value is the cloudstic flag that carries it.

const (
	FieldStoreURI          Field = "store"
	FieldS3Endpoint        Field = "s3-endpoint"
	FieldS3Region          Field = "s3-region"
	FieldS3Profile         Field = "s3-profile"
	FieldS3AccessKey       Field = "s3-access-key"
	FieldS3SecretKey       Field = "s3-secret-key"
	FieldB2KeyID           Field = "b2-key-id"
	FieldB2AppKey          Field = "b2-app-key"
	FieldStoreSFTPPassword Field = "store-sftp-password"
	FieldStoreSFTPKey      Field = "store-sftp-key"
	FieldPassword          Field = "password"
	FieldEncryptionKey     Field = "encryption-key"
	FieldRecoveryKey       Field = "recovery-key"
	FieldKMSKeyARN         Field = "kms-key-arn"
	FieldKMSRegion         Field = "kms-region"
	FieldKMSEndpoint       Field = "kms-endpoint"
)

The fields a profile can supply. MergeProfileStore arbitrates between these and a caller's own values; Fields returns the complete set.

func BackupFields

func BackupFields() []Field

BackupFields returns every field a profile can supply about what to back up, in a fresh slice. See StoreFields for the ones describing where it goes.

func StoreFields

func StoreFields() []Field

StoreFields returns every store-related field a profile can supply, in a fresh slice. See BackupFields for the ones that describe what to back up.

Iterate it to build a FieldSet from whatever "the user gave me this" means in your own configuration mechanism — a set of parsed flags, keys present in a TOML file, non-empty form inputs. Doing so keeps the set complete as fields are added, where a hand-written list would quietly fall behind.

func (Field) ProfileKey

func (f Field) ProfileKey() string

ProfileKey returns the profiles-file key that states this field, which is not always the field's own string: the file writes s3_access_key where the cloudstic flag is -s3-access-key. It is the spelling to show a user who has to go edit their profiles file.

An unrecognized Field returns the empty string.

type FieldSet

type FieldSet map[Field]struct{}

FieldSet is a set of fields the caller has already decided, and which a profile therefore must not supply.

func FieldsSetIn

func FieldsSetIn(cfg Client) FieldSet

FieldsSetIn reports which fields cfg holds a non-empty value for.

This is the FieldSet to pass when your own configuration mechanism has no notion of "present but empty" — reading a struct you filled in is then exactly as good as tracking which keys you filled, and cannot drift from it.

Use an explicit NewFieldSet instead when empty means something: the cloudstic CLI does, because `-password ""` is a deliberate choice of no password and must still beat the profile's.

func NewFieldSet

func NewFieldSet(fields ...Field) FieldSet

NewFieldSet collects fields into a set. It is valid to pass none.

func (FieldSet) Has

func (s FieldSet) Has(f Field) bool

Has reports whether f is in the set. The zero FieldSet is empty, so a nil set means "nothing decided" rather than being an error.

type Google

type Google struct {
	CredsPath string
	CredsRef  string
	CredsJSON string
	TokenPath string
	TokenRef  string
}

Google holds the credentials for a Google Drive source. Credentials may be given as a file path, a secret reference, or inline JSON; the source tries them in that order.

type KMS

type KMS struct {
	KeyARN   string
	Region   string
	Endpoint string
}

KMS holds AWS KMS settings for kms-platform key slots.

type OneDrive

type OneDrive struct {
	ClientID  string
	TokenPath string
	TokenRef  string
}

OneDrive holds the credentials for a OneDrive source.

type S3

type S3 struct {
	Endpoint  string
	Region    string
	Profile   string
	AccessKey string
	SecretKey string
}

S3 holds the credentials and endpoint settings for an S3 store.

type SFTP

type SFTP struct {
	Password   string
	Key        string
	KnownHosts string
	Insecure   bool
}

SFTP holds SFTP authentication and host-key settings. The same shape serves both a store and a backup source, which are configured independently.

type Source

type Source struct {
	URI string

	// ConfigDir locates the token files a cloud source falls back to when
	// Google.TokenPath or OneDrive.TokenPath is empty, and the managed store
	// behind config-token:// references. It carries paths.ConfigDir's meaning:
	// empty means CLOUDSTIC_CONFIG_DIR or the platform default.
	ConfigDir string

	SFTP     SFTP
	Google   Google
	OneDrive OneDrive

	// VolumeUUID overrides the detected volume identity of a local source,
	// which is what lets a portable drive back up incrementally from more than
	// one machine.
	VolumeUUID string

	// SkipNativeFiles excludes Google-native documents, which have no byte
	// stream to download.
	SkipNativeFiles bool

	SkipMode        bool
	SkipFlags       bool
	SkipXattrs      bool
	XattrNamespaces []string

	// Excludes are gitignore-syntax patterns. ExcludeFile names a file holding
	// more of them, one per line; open.Backup reads it and appends to Excludes.
	Excludes    []string
	ExcludeFile string
}

Source is everything needed to construct a backup source.

Which fields matter depends on the URI's scheme, the same way config.Store works: a local source reads VolumeUUID and the metadata switches, an SFTP source reads SFTP, and the cloud sources read Google or OneDrive.

func SourceForAuth

func SourceForAuth(auth profile.Auth) (Source, error)

SourceForAuth returns the source configuration that authenticates auth's provider with auth's credentials.

Use it to act on a profiles file's auth entry — signing in, or checking which account an entry belongs to — without addressing any content: pass the result to open.Source and read the resulting source's Info.

Only the credentials belonging to auth's provider are carried over. An entry holding both providers' fields (which a hand-edited file may) does not produce a source that would try both.

type SourceURI

type SourceURI struct {
	Scheme string // "local", "sftp", "gdrive", "gdrive-changes", "onedrive", "onedrive-changes"
	// local/sftp fields
	Path string
	// sftp-specific fields
	Host string
	Port string
	User string
}

SourceURI holds the parsed components of a source URI or keyword.

Which fields are populated depends on Scheme: local uses Path, sftp uses Host, Port, User and Path. For the cloud schemes (gdrive, onedrive, and their -changes variants) Host carries the *drive name* rather than a network host, and Path the sub-path within that drive.

func ParseSourceURI

func ParseSourceURI(raw string) (*SourceURI, error)

ParseSourceURI parses a source URI or keyword into its components.

Supported formats:

local:<path>                        e.g. local:./documents
sftp://[user@]host[:port]/<path>    e.g. sftp://backup@host.com/data
gdrive
gdrive-changes
onedrive
onedrive-changes

type Store

type Store struct {
	URI   string
	S3    S3
	B2    B2
	SFTP  SFTP
	Debug bool
}

Store is everything needed to construct an object store.

Not to be confused with profile.Store, which is the same configuration as *declared* in a profiles file, secret references and all. Store is what that resolves to. config.FromProfileStore converts one into the other.

type StoreURI

type StoreURI struct {
	Scheme string // "local", "s3", "b2", "sftp"
	// S3/B2 fields
	Bucket string
	Prefix string
	// local field
	Path string
	// SFTP fields
	Host string
	Port string
	User string
}

StoreURI holds the parsed components of a store URI.

Which fields are populated depends on Scheme: local uses Path, s3 and b2 use Bucket and Prefix, sftp uses Host, Port, User and Path.

func ParseStoreURI

func ParseStoreURI(raw string) (*StoreURI, error)

ParseStoreURI parses a store URI into its components.

Supported formats:

local:<path>                        e.g. local:./backup_store
s3:<bucket>[/<prefix>]              e.g. s3:my-bucket or s3:my-bucket/prod
b2:<bucket>[/<prefix>]              e.g. b2:my-bucket or b2:my-bucket/prod
sftp://[user@]host[:port]/<path>    e.g. sftp://backup@host.com/backups

type Unlock

type Unlock struct {
	Password      string
	EncryptionKey string
	RecoveryKey   string
	KMS           KMS
	Prompt        bool
	NoPrompt      bool
}

Unlock is everything needed to build the keychain that unlocks a repository.

The credentials are tried in a fixed order — KMS, then EncryptionKey, then Password, then RecoveryKey — so supplying more than one is not ambiguous. Prompt and NoPrompt govern the interactive fallback, which is only ever reachable when the caller opts into it.

Jump to

Keyboard shortcuts

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