Documentation
¶
Overview ¶
Package backend defines where ciphertext lives. The Backend interface is the seam between client-side-crypto and provider-holds-plaintext storage models: RcloneStorage is the only MVP implementation. A future KeyVaultBackend can implement the same interface and simply ignore the crypto layer.
Index ¶
- Variables
- func CreateRemote(ctx context.Context, name, kind string, params map[string]string) error
- func ListRemotes(ctx context.Context) ([]string, error)
- func RcloneInstalled() bool
- func RemoteType(ctx context.Context, name string) (string, error)
- type Backend
- type HeaderStore
- type RcloneStorage
- func (s *RcloneStorage) Get(ctx context.Context, namespace string) ([]byte, error)
- func (s *RcloneStorage) GetHeader(ctx context.Context) ([]byte, error)
- func (s *RcloneStorage) List(ctx context.Context) ([]string, error)
- func (s *RcloneStorage) Preflight(ctx context.Context) error
- func (s *RcloneStorage) Probe(ctx context.Context) error
- func (s *RcloneStorage) Put(ctx context.Context, namespace string, ciphertext []byte) error
- func (s *RcloneStorage) PutHeader(ctx context.Context, raw []byte) error
Constants ¶
This section is empty.
Variables ¶
var ErrNotFound = errors.New("namespace not found")
ErrNotFound is returned by Get when no blob exists for the namespace.
var ErrRcloneMissing = errors.New("rclone not found in PATH")
ErrRcloneMissing is returned when no rclone binary is on PATH.
Functions ¶
func CreateRemote ¶
CreateRemote drives `rclone config create` into the user's global rclone config. Params pass via argv: briefly visible in /proc, but with no shell nothing lands in history. This is acceptable because storage credentials only ever guard ciphertext. Revisit if rclone grows a stdin-based config path.
func ListRemotes ¶
ListRemotes returns the names of the user's configured rclone remotes.
func RcloneInstalled ¶
func RcloneInstalled() bool
RcloneInstalled reports whether an rclone binary is available.
Types ¶
type Backend ¶
type Backend interface {
// Get returns the stored ciphertext for a namespace (or ErrNotFound).
Get(ctx context.Context, namespace string) ([]byte, error)
// Put stores ciphertext. Implementations SHOULD retain prior versions.
Put(ctx context.Context, namespace string, ciphertext []byte) error
// List returns known namespaces.
List(ctx context.Context) ([]string, error)
}
type HeaderStore ¶
type HeaderStore interface {
// GetHeader returns the raw header object (or ErrNotFound).
GetHeader(ctx context.Context) ([]byte, error)
// PutHeader stores the raw header object.
PutHeader(ctx context.Context, raw []byte) error
}
HeaderStore is implemented by client-side-crypto backends, which keep the key-slot header next to the namespace blobs (see internal/crypto: LUKS2-style wrapped master key). Backends where the provider holds plaintext have no key material and won't implement it.
type RcloneStorage ¶
type RcloneStorage struct {
Remote string // rclone remote name, e.g. "b2"
Base string // path within the remote, e.g. "my-bucket/notenv"
// Versioned: the remote retains old versions on overwrite (B2 does
// natively), so the .prev backup copy (~3s server-side on B2) is
// redundant and skipped.
Versioned bool
}
RcloneStorage implements Backend by shelling out to a system rclone. This keeps the binary small and the dependency explicit; embedding the library is a possible later optimization.
func (*RcloneStorage) GetHeader ¶
func (s *RcloneStorage) GetHeader(ctx context.Context) ([]byte, error)
func (*RcloneStorage) Preflight ¶
func (s *RcloneStorage) Preflight(ctx context.Context) error
Preflight verifies rclone is installed and the remote exists.
func (*RcloneStorage) Probe ¶
func (s *RcloneStorage) Probe(ctx context.Context) error
Probe round-trips a marker object through the configured base path so a bad credential or bucket fails here, with context, not at the first real `set` days later.
func (*RcloneStorage) PutHeader ¶
func (s *RcloneStorage) PutHeader(ctx context.Context, raw []byte) error
PutHeader writes the header object. Only called on creation today; when rotation lands (`notenv key ...`) it MUST grow a previous-version backup first, because a clobbered header locks the user out of every blob under it.