Skip to content

harn backup

harn backup <subcommand> [options]

harn backup wraps restic for encrypted recovery snapshots of durable state under .harnery/. Harnery chooses the default paths from its storage catalog and refuses snapshots that exceed the configured size ceiling.

You still own the restic repository and its password. Harnery sets the repository, password file, target set, tags, and retention defaults, then invokes restic.

Terminal window
harn backup init
harn backup snapshot [--if-stale <duration>] [--tag <tag>] [--include-artifacts] [--allow-large]
harn backup list [--json]
harn backup restore <snapshot-id> [--target <path>]
harn backup prune [--dry-run]
harn backup check

All subcommands accept --repo <path> and --password-file <path>. The environment variables HARNERY_RESTIC_REPO and HARNERY_RESTIC_PASSWORD_FILE override project configuration.

Terminal window
harn backup init

init creates a password file with mode 0600 when the file does not exist, then creates the restic repository. A relative backup.password_file path resolves from the project root, not the shell’s current directory.

Run init only for a new repository. To join an existing repository from another machine, put the existing password in the configured file and run harn backup check. The same password opens every snapshot in one repository.

Filesystem repositories get their local parent directory automatically. Remote repository strings such as rclone:remote:path go straight to restic; Harnery does not try to create a local directory from the remote string or configure the remote provider.

The default set comes from storage catalog families classified as canonical-authority, durable-object-history, or recovery-state whose roots are inside .harnery/. Harnery then removes event ledger families and managed artifacts. Event ledgers can be much larger than the coordination records needed to recover a project, while artifacts are temporary workspaces with their own retention policy.

A Harnery test checks catalog coverage. A newly registered family must be selected, excluded by name, or rooted outside .harnery/; it cannot disappear from backups because someone forgot to update a second directory list.

Use backup.include to add .harnery-relative paths and backup.exclude to remove catalog family IDs or .harnery-relative paths. The size gate applies after those changes.

{
"backup": {
"repo": "rclone:archive:harnery/project-a",
"password_file": ".secrets/harnery-restic.password",
"include": [],
"exclude": [],
"max_bytes": 52428800,
"schedule": {
"if_stale": "24h",
"tags": ["daily"]
},
"keep_daily": 7,
"keep_weekly": 4,
"keep_monthly": 6
}
}
Setting Default Purpose
repo ~/.cache/harnery/restic-repo Restic repository path or URL.
password_file ~/.config/harnery/restic-password File containing the repository password.
include [] Extra paths below .harnery/.
exclude [] Catalog family IDs or paths removed from the default set.
max_bytes 52428800 Refuse a larger selection unless --allow-large is present.
schedule unset Optional session-start snapshot policy.
keep_daily, keep_weekly, keep_monthly 7, 4, 6 Retention used by prune.

When backup.schedule is present, the session-start hook launches snapshot --if-stale <duration> in the background with the configured tags. The child owns none of the hook’s stdio, so the harness never waits for restic or the remote provider. The hook stays quiet on success.

Three small files under .harnery/logs/ carry the schedule’s state:

File Writer Purpose
backup-schedule.log the background runner Timestamped output of every scheduled run; rotated when it passes 256 KiB.
backup-schedule.status.json the background runner Exit code and finish time of the last scheduled run.
backup-host-snapshot.json harn backup snapshot This host’s newest known snapshot time.

The hook reads the status file first. When the last scheduled run failed, it adds one line naming the exit code, the finish time, and the log path to the session’s start-up context, prints the same line on stderr, and relaunches the snapshot so a transient error heals on the next session. It then reads the host cache: when this host’s newest snapshot is younger than if_stale, the hook launches nothing, so a machine with many session starts a day asks the remote repository about once per freshness window rather than on every start. A missing, stale, or foreign-host cache always launches the command, and the command still asks restic before deciding.

Terminal window
harn backup snapshot --tag manual
harn backup snapshot --if-stale 24h --tag daily

--tag is repeatable. --if-stale exits successfully without creating a snapshot when this restic host already has one newer than the duration. Restic’s host filter keeps snapshots independent across machines. A cross-process lock prevents two concurrent hooks on one machine from both passing the same freshness check.

If the selected files exceed backup.max_bytes, the command reports the selected size and stops. --include-artifacts adds managed working artifacts for a deliberate one-off snapshot; the size gate still applies. --allow-large bypasses that guard for one invocation.

Terminal window
harn backup list --json
harn backup check
harn backup restore 1a2b3c4d --target ./recovered

restore writes to a new timestamped directory when --target is absent. It does not restore over the live .harnery/ tree.

Terminal window
harn backup prune --dry-run
harn backup prune

Pruning stays manual. --dry-run previews the configured 7 daily, 4 weekly, and 6 monthly retention policy. Running without --dry-run calls restic forget --prune.