# Config examples

```toml
# keld config merge order (lowest to highest precedence):
#   [global] -> [global.<command>] -> split preset parts -> full preset -> CLI overrides
#
# For a split preset like `home@cloud` and command `backup`, keld checks:
#   [global] -> [global.backup] -> [@cloud] -> [@cloud.backup] ->
#   [home@] -> [home@.backup] -> [home@cloud] -> [home@cloud.backup]

[global]
# Shared flags for every command and preset.
password-file = "~/.config/restic/password.txt"

[global.backup]
# Command-specific defaults.
exclude-file = "~/.config/restic/excludes.txt"
exclude-if-present = ".nobackup"

[home.backup]
# Simple preset (no @): `keld --preset home backup`
_arguments = ["/home/user"]
tag = ["home"]

# ── Suffixes: the "where" ───────────────────────────────────
#
# Suffix sections define backend-wide settings. Restic supports paths
# inside rclone remotes and S3 buckets, so one remote or bucket can hold
# multiple independent repositories. Credentials live here once.

["@nas"]
# Suffix: applies to any `*@nas` preset.
repository = "sftp:nas@example.org:/mnt/backups/restic"
tag = ["nas"]

["@cloud"]
# Suffix for S3-compatible cloud backups (B2, Wasabi, etc.).
# Note: no repository here — each full preset sets its own path
# within the shared bucket.
tag = ["cloud"]

# Keys ending in _COMMAND are executed by keld; stdout becomes the env
# var with the suffix removed. RESTIC_PASSWORD_COMMAND and
# RESTIC_FROM_PASSWORD_COMMAND are passed through to restic as-is.
["@cloud".environ]
AWS_ACCESS_KEY_ID_COMMAND = "op read 'op://Vault/Cloud/access-key-id'"
AWS_SECRET_ACCESS_KEY_COMMAND = "op read 'op://Vault/Cloud/secret-access-key'"

# ── Prefixes: the "what" ────────────────────────────────────
#
# Prefix sections define sources, excludes, and the restic encryption
# password. Each dataset gets its own password so repositories are
# independently encrypted, even when stored on the same backend.

["home@"]
# Prefix: applies to any `home@*` preset.
host = "my-laptop"
tag = ["home"]

["home@".environ]
RESTIC_PASSWORD_COMMAND = "pass show backups/restic-home"

["home@".backup]
# Prefix + command section.
_arguments = ["/home/user", "/etc"]
exclude-if-present = ".keld-skip"

["home@".forget]
# Prefix + different command.
keep-daily = 7
keep-weekly = 5
keep-monthly = 12

# ── Full presets: prefix + suffix with repo path ────────────
#
# Full preset sections set the repository path within each backend.
# This is where the prefix ("what") meets the suffix ("where").

["home@nas"]
# Uses the @nas suffix's sftp base; path set here.
repository = "sftp:nas@example.org:/mnt/backups/restic/home"

["home@cloud"]
# Path within the shared S3 bucket for this dataset.
repository = "s3:s3.us-east-1.amazonaws.com/my-restic-bucket/home"

# ── BorgBase: per-repo URLs ─────────────────────────────────
#
# BorgBase provides per-repository URLs with embedded credentials.
# Each repo is specific to one dataset — there's no shared @borgbase
# suffix to reuse. Keep the split form so the prefix's _arguments
# and password settings still apply through the merge chain.

["home@borgbase_home"]
repository = "rest:https://ab12cd34:s3cr3tp4ssw0rd@ab12cd34.repo.borgbase.com"
```

## Systemd timer setup

Example systemd user units for daily backups (with lightweight structural
check) and monthly full integrity verification, with optional healthchecks.io
integration via `runitor`. Also assume use via `mise` and that the user has
set up the `mise` config snippet for `keld`. This might not be the case;
you'll need to ask or figure out how they've installed `keld`.

### Unit files

Installed to `~/.config/systemd/user/` or `/etc/systemd/user/`.

**keld-backup@.service** — runs backup then a quick structural check. Both
are wrapped by a single runitor invocation so a failure in either reports
to the same healthcheck:

```ini
[Unit]
Description=keld %I backup

[Service]
Nice=19
IOSchedulingClass=idle
KillSignal=SIGINT
EnvironmentFile=-%h/.config/keld/timers/%I_backup.env
ExecStart=/bin/sh -c '%h/.local/bin/mise x github:bdd/runitor -- runitor -- /bin/sh -c "mise x http:keld -- keld --preset %I backup && mise x http:keld -- keld --preset %I check"'
```

**keld-backup@.timer**:

```ini
[Unit]
Description=Daily keld %I backup

[Timer]
OnCalendar=daily
AccuracySec=1m
RandomizedDelaySec=1h
Persistent=true

[Install]
WantedBy=timers.target
```

**keld-integrity@.service** — monthly full data integrity verification.
Downloads and verifies every pack file (`--read-data`):

```ini
[Unit]
Description=keld %I integrity check (read-data)

[Service]
Nice=19
IOSchedulingClass=idle
KillSignal=SIGINT
EnvironmentFile=-%h/.config/keld/timers/%I_integrity.env
ExecStart=%h/.local/bin/mise x github:bdd/runitor -- runitor -- mise x http:keld -- keld --preset %I check --read-data
```

**keld-integrity@.timer**:

```ini
[Unit]
Description=Monthly keld %I integrity check

[Timer]
OnCalendar=monthly
AccuracySec=1m
RandomizedDelaySec=1h
Persistent=true

[Install]
WantedBy=timers.target
```

### Enabling timers

```bash
# For each preset (e.g. media@borgbase_media):
systemctl --user enable --now keld-backup@media@borgbase_media.timer
systemctl --user enable --now keld-integrity@media@borgbase_media.timer

# Verify
systemctl --user list-timers
```

### Healthchecks env files

Create env files in `~/.config/keld/timers/` with a `CHECK_UUID` variable for each preset:

```
~/.config/keld/timers/media@borgbase_media_backup.env     → backup + check UUID
~/.config/keld/timers/media@borgbase_media_integrity.env  → monthly read-data UUID
```

The `EnvironmentFile=-` prefix (`-`) makes the env file optional — the timer still works without healthchecks.
