AGENTS.md #
Keld is a friendly TOML-configured wrapper around restic (a backup tool). It provides:
- Layered configuration with presets and command-specific overrides
- Interactive menu when invoked without arguments (BubbleTea v2)
- Split preset syntax (
home@cloud) for composable configurations - Passthrough of arbitrary flags to restic
This project uses jujutsu for version control. Before starting work, check jj st. If there's existing work in progress, run jj new -m "..." to create a new
working copy with a good, imperative, kernel-commit-style description. DO NOT
follow or read ANY skills or use ANY tools related to Conventional Commits.
Essential Commands #
All tasks are managed via mise (see mise.toml):
mise run vuln
mise run vet
mise run install
# Check formatting without modifying
mise run fmt:check
# Run full check suite (fmt:check, vet, lint, vuln, build, test)
mise run check
Freely check the --help output of various restic commands as you work, so
we can be absolutely certain we're writing a good, up-to-date wrapper. Any time
our code differs from restic proper, PLEASE FLAG IT TO ME!
Config System #
Section Merge Order #
Config sections are merged in ascending priority order. For
keld home@cloud backup:
[global] → [global.backup] → [@cloud] → [@cloud.backup] →
[home@] → [home@.backup] → [home@cloud] → [home@cloud.backup] →
CLI overrides
Config File Discovery #
- Default dirs:
/usr/share/keld,/etc/keld,~/.config/keld - In each dir:
config.tomlthen sortedconf.d/*.toml KELD_CONFIG_PATHSenv var (colon-separated, supports globs)KELD_CONFIG_FILEreplaces all above if set
Special Config Keys #
| Key | Purpose |
|---|---|
_arguments |
Positional args passed to restic (array or space-separated string) |
_workdir |
Directory to chdir before exec |
_command |
Restic subcommand (allows aliasing) |
_pre_hooks |
Backup-only shell commands run before restic backup |
_post_hooks |
Backup-only shell commands run after restic backup is attempted |
*.environ |
Section suffix for environment variables |
FOO_COMMAND |
In .environ: executed via sh -c, stdout sets FOO |
Interpolation #
Values can reference other sections: cache-dir = "${vars.cache-root}/cache"
Split Presets #
Presets with @ are split: home@cloud → applies [@cloud], [home@],
then [home@cloud]
See examples/keld/config.toml for comprehensive examples.
Code Patterns & Conventions #
Test Patterns #
- Table-driven tests with
t.Parallel() - Testdata embedded as string constants (see
resolveFixtureTOML) tt := ttcopy before parallel subtest- Use
t.TempDir()andt.Setenv()for isolation
Important Gotchas #
Config Merge Behavior #
- Later files override earlier at section granularity
- Within a file, later sections override earlier
- Nested tables (sub-commands) are not merged across sections—only leaf keys
- Multi-line strings become repeated flags (split on
\n)
Restic Process Supervision #
restic.Run() supervises restic as a child process. This allows Keld to run
post-backup hooks after restic exits and preserve restic's numeric exit code for
callers.
Test Isolation #
Tests modify DefaultConfigDirs global. Always restore with t.Cleanup():
original := DefaultConfigDirs
DefaultConfigDirs = []string{tmpDir}
t.Cleanup(func() { DefaultConfigDirs = original })
BubbleTea v2 #
Using v2 API (not v1). Key differences:
tea.NewProgram(m)instead oftea.NewProgram(m, opts...)tea.RequestBackgroundColorfor theme detectiontea.NewView()instead of returning strings
Environment Variables #
| Variable | Purpose |
|---|---|
KELD_CONFIG_FILE |
Single config file (highest priority) |
KELD_CONFIG_PATHS |
Colon-separated additional config paths |
KELD_DRYRUN |
Set to enable dry-run mode |
KELD_EXECUTABLE |
Override restic binary path |
Development Workflow #
- Make changes
mise run check- full validation- Test manually:
keld --show-command --preset <preset> <command>