vault

package
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: Apr 11, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package vault provides encrypted, append-only snapshot storage using age encryption.

Index

Constants

View Source
const (
	// LegacyDefaultName is the synthetic name assigned to the legacy
	// single-vault entry when only vault_dir: is set.
	LegacyDefaultName = "default"
)

Variables

This section is empty.

Functions

func DefaultConfigDir

func DefaultConfigDir() (string, error)

DefaultConfigDir returns the default configuration directory for locksmith.

func DefaultConfigPath

func DefaultConfigPath() (string, error)

DefaultConfigPath returns the default path to the vault config file.

func SaveConfig

func SaveConfig(path string, cfg *Config) error

SaveConfig writes the vault configuration to the given path. If path is empty, it uses the default config path.

func SecureTmpDir

func SecureTmpDir() (string, error)

SecureTmpDir creates a temporary directory in /dev/shm (RAM-backed) on Linux. Falls back to os.TempDir() if /dev/shm is not available.

Types

type Config

type Config struct {
	// Legacy single-vault fields. Still honored for backward compatibility.
	VaultDir  string `yaml:"vault_dir,omitempty"`
	Identity  string `yaml:"identity,omitempty"`
	GPGBinary string `yaml:"gpg_binary,omitempty"`

	// Multi-vault registry. Each entry can override identity and gpg_binary.
	Vaults  []Entry `yaml:"vaults,omitempty"`
	Default string  `yaml:"default,omitempty"`
}

Config holds machine-local vault configuration stored at ~/.config/locksmith/config.yaml.

Two forms are supported:

  1. Legacy single-vault form (still works forever):

    vault_dir: ~/Dropbox/Private/vault identity: ~/.config/locksmith/age.key gpg_binary: gpg

  2. Multi-vault registry form:

    vaults: - name: personal path: ~/Dropbox/Private/vault identity: ~/.config/locksmith/personal.age - name: work path: ~/work/vault default: personal

Both forms may coexist; the legacy single-vault entry is treated as a synthetic registry entry named "default". Use Resolve() to look up a vault by name with the correct precedence.

func LoadConfig

func LoadConfig(path string) (*Config, error)

LoadConfig reads the vault configuration from the given path. If path is empty, it uses the default config path.

All path-like fields (vault_dir, identity, vaults[].path, vaults[].identity) have a leading "~/" expanded to the user's home directory.

func (*Config) AddVault added in v0.4.0

func (c *Config) AddVault(entry Entry) error

AddVault appends a new entry to the registry. Returns an error if a vault with the same name already exists. Path is required, identity is optional. If this is the first registry entry, it also becomes the default.

func (*Config) Resolve added in v0.4.0

func (c *Config) Resolve(name string) (*Entry, error)

Resolve looks up a vault by name and returns its effective entry. The resolution rules are:

  • If name is "" and Default is set, look up Default.
  • If name is "" and Default is empty, fall back to LegacyDefaultName.
  • If a matching entry exists in Vaults, return it (per-entry fields win).
  • Otherwise, if a legacy single-vault config (VaultDir set) is present and the requested name is "" or LegacyDefaultName, synthesize a LegacyDefaultName entry from the top-level fields.
  • Otherwise return an error.

Top-level Identity and GPGBinary act as defaults that fill in any per-entry fields left blank.

func (*Config) VaultNames added in v0.4.0

func (c *Config) VaultNames() []string

VaultNames returns the names of all known vaults, including the synthetic legacy entry when only VaultDir is set. Useful for the web UI's chooser screen and for shell completion.

type Entry added in v0.4.0

type Entry struct {
	Name      string `yaml:"name"`
	Path      string `yaml:"path"`
	Identity  string `yaml:"identity,omitempty"`
	GPGBinary string `yaml:"gpg_binary,omitempty"`

	// TrustedMasterFP is the GPG master key fingerprint that this vault
	// is expected to contain (40 hex chars). Populated on first use
	// (TOFU): the kernel reads gpgsmith.yaml from the decrypted vault on
	// the first OpenSession and records what it found. On subsequent
	// opens the kernel verifies the embedded master_fp matches this
	// value and refuses with a loud error on mismatch — defending
	// against snapshot substitution attacks. Empty until first use.
	TrustedMasterFP string `yaml:"trusted_master_fp,omitempty"`
}

Entry is a single named entry in the vault registry.

func (*Entry) ToConfig added in v0.4.0

func (e *Entry) ToConfig() *Config

ToConfig returns an ephemeral single-vault Config equivalent to this entry. Handy for passing the resolved entry to the existing Vault constructors, which still take a *Config.

type Snapshot

type Snapshot struct {
	Path      string
	Timestamp time.Time
	Message   string
}

Snapshot represents a single encrypted vault snapshot.

type Vault

type Vault struct {
	// contains filtered or unexported fields
}

Vault manages encrypted, append-only snapshots.

func New

func New(cfg *Config, logger *slog.Logger) (*Vault, error)

New creates a new Vault instance from the given configuration. If cfg.Identity is set, it reads the age key file; otherwise the caller must provide a passphrase-based identity via NewWithPassphrase.

func NewWithPassphrase

func NewWithPassphrase(cfg *Config, passphrase string, logger *slog.Logger) (*Vault, error)

NewWithPassphrase creates a new Vault that uses a passphrase for encryption.

func (*Vault) Create

func (v *Vault) Create(ctx context.Context) error

Create creates the vault directory if it does not already exist.

func (*Vault) DecryptFile added in v0.4.0

func (v *Vault) DecryptFile(_ context.Context, path string) (string, error)

DecryptFile decrypts an arbitrary .tar.age file (typically a .session-<host> ephemeral) into a fresh secure workdir and returns its path. The file must have been encrypted with this vault's identity. The returned workdir is the caller's to manage (Discard or Seal).

func (*Vault) Discard

func (v *Vault) Discard(ctx context.Context, workdir string) error

Discard removes the working directory without saving a new snapshot.

func (*Vault) Import

func (v *Vault) Import(ctx context.Context, sourcePath string) (Snapshot, error)

Import takes an existing directory, tars and encrypts it as the first snapshot.

func (*Vault) List

func (v *Vault) List(ctx context.Context) ([]Snapshot, error)

List scans the vault directory for .tar.age files and returns them sorted by timestamp.

func (*Vault) Open

func (v *Vault) Open(ctx context.Context) (string, Snapshot, error)

Open decrypts the latest snapshot into a secure temporary directory.

func (*Vault) Passphrase

func (v *Vault) Passphrase() string

Passphrase returns the passphrase used for encryption, or empty if key-file mode.

func (*Vault) Restore

func (v *Vault) Restore(ctx context.Context, ref string) (string, error)

Restore decrypts a specific snapshot (by filename or prefix) into a secure tmpdir.

func (*Vault) Seal

func (v *Vault) Seal(ctx context.Context, workdir string, message string) (Snapshot, error)

Seal tars and encrypts the workdir as a new snapshot, then removes the workdir.

func (*Vault) SealEphemeral added in v0.4.0

func (v *Vault) SealEphemeral(ctx context.Context, workdir, targetPath string) error

SealEphemeral tars and encrypts a directory into the supplied target path, reusing the vault's encryption identity. Unlike Seal, this does NOT generate a timestamped canonical filename — the caller specifies the destination path, which is typically an in-progress session file like "<canonical>.tar.age.session-<hostname>".

The write is atomic: a temp file is created in the same directory, the content is encrypted into it, and then the temp file is renamed over the target. This means concurrent readers always see either the previous version of the target or the new one, never a half-written file.

The workdir is NOT removed by this function — only by Seal/Discard. Use SealEphemeral when you want to flush the in-memory state to disk periodically (heartbeat) or once on idle-out, while keeping the workdir alive for further operations.

Jump to

Keyboard shortcuts

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