Skip to main content

Scheduled encrypted backups for Claude Code memory and similar small directories.

Reason this release was yanked:

0.1.2 includes security fixes. Previous version had regressions; please upgrade.

Project description

claude-backup-cron

Scheduled, encrypted backups for small-but-high-value directories — Claude Code's memory/ folder in particular, but anything the shape would fit.

What it does

  • Tars each configured source directory into a deterministic artefact.
  • Skips the run entirely if nothing has changed since last time (content hash, not mtime — mtime lies).
  • Optionally encrypts the artefact with age before it ever reaches a destination.
  • Fans out to one or more destinations — a private git remote, an S3 bucket, a spare local drive — with independent success/failure per destination.
  • On any failure, posts a short message to an optional webhook (Discord/Slack shape).
  • Installs itself as a cron job with a one-liner.

Zero runtime dependencies on Python 3.11+; tomli backport on 3.10. External binaries (git, aws, age, crontab) are invoked only when the corresponding feature is actually used.

Why

The existing memory-backup.sh that ships in many Claude Code setups is ~20 lines of bash: great for one machine, brittle in practice — silent on failure, no encryption, single destination, no change detection. This package is the "I want it to keep working while I'm not watching" version: strict typing, deterministic artefacts, structured failures, audit-friendly CLI.

Install

pipx install claude-backup-cron

Or in a venv:

python3 -m venv ~/.local/venvs/claude-backup-cron
~/.local/venvs/claude-backup-cron/bin/pip install claude-backup-cron

See docs/INSTALL.md for setup on each destination kind (git remote, S3 bucket, local path).

Configure

Default config location: ~/.config/claude-backup-cron/config.toml. Override with $CLAUDE_BACKUP_CRON_CONFIG.

Minimal example:

[global]
alert_webhook = "https://discord.com/api/webhooks/..."

[[sources]]
id = "claude-memory"
path = "~/.claude/projects/-home-runza/memory"
exclude = [".git/*", "*.swp"]

[[destinations]]
id = "offsite-git"
kind = "git"
remote = "git@github.com:me/claude-memory-backup.git"
branch = "main"
encrypt_to = "age1abc..."   # omit to upload plaintext

Verify it parses:

claude-backup-cron show-config

A fuller example covering all three destination kinds lives in examples/config.toml.

Run

claude-backup-cron run              # do the thing
claude-backup-cron run --dry-run    # package sources but don't upload
claude-backup-cron run --json       # machine-readable report on stdout

Exit codes:

Code Meaning
0 All steps succeeded (or nothing to do).
2 At least one source→destination step failed. Others may have succeeded.
3 Config or encryption setup error — nothing was uploaded.

Schedule

claude-backup-cron install-cron --schedule "0 3 * * *"   # daily 03:00
claude-backup-cron uninstall-cron

The installer manages a single block in your user crontab, marked with # claude-backup-cron managed entry. Re-running install-cron replaces the previous block in place.

Encryption

Set encrypt_to = "age1..." on a destination. The artefact is piped through age --recipient <...> before upload. We refuse to fall back to plaintext on encryption failure — that's exactly the degradation you don't want in a backup tool.

To decrypt later:

age --decrypt -i ~/.ssh/my-age-key claude-memory-abc123.tar.age \
  | tar -xvf -

Library API

from claude_backup_cron import load, run

config = load()
report = run(config, dry_run=False)
if not report.ok:
    for failure in report.failed:
        print(failure.source_id, "→", failure.destination_id, failure.message)

All public types are frozen dataclasses (Config, SourceSpec, DestinationSpec, RunReport, StepResult, Artefact, Upload). Typed Python (py.typed); passes mypy --strict.

Design commitments

  • Never silently downgrade encryption. If encrypt_to is set and age is missing, the destination fails — it does not upload plaintext.
  • Independent destination failures. A dead S3 bucket must not prevent the git push that follows.
  • Deterministic artefacts. The same tree produces the same tarball bytes (mtime/uid/gid zeroed) so destinations can no-op when nothing has changed.
  • Zero telemetry. No phone-home, no metrics. A backup tool that beacons is a backup tool that will be firewalled off.

Picking a destination kind

Quick rule of thumb:

Kind Good for Avoid when
git Small sources (< ~100 MB), full version history matters The source is large and churns; every run adds a blob to .git/objects that is never deleted.
s3 Offsite durability with a lifecycle rule for rotation You don't have (or don't want) an AWS-shaped account.
local Spare drive, second machine on LAN, bounded retention (retain = N) You need the backup to survive the laptop being stolen.

Mix and match — having one git destination for history and one local destination for recent rollback is a common shape.

Threat model & non-goals

  • In scope: accidental data loss (mistaken rm, disk failure, lost laptop, revoked cloud account). The remote copies let you rebuild.
  • Out of scope: targeted attack by someone with root on the host. If the attacker can read your config and your age identity file, they can already read your unencrypted source — this is a backup tool, not a sealing room.
  • Out of scope: real-time sync. The minimum meaningful schedule is whatever cron runs; continuous replication is a different product.

See SECURITY.md for the vulnerability reporting process.

License

Apache-2.0. See LICENSE and NOTICE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

claude_backup_cron-0.1.0.tar.gz (35.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

claude_backup_cron-0.1.0-py3-none-any.whl (29.3 kB view details)

Uploaded Python 3

File details

Details for the file claude_backup_cron-0.1.0.tar.gz.

File metadata

  • Download URL: claude_backup_cron-0.1.0.tar.gz
  • Upload date:
  • Size: 35.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for claude_backup_cron-0.1.0.tar.gz
Algorithm Hash digest
SHA256 edccf448e0685ea98c429ea2568a652cf73eed4007298f5374dd493dc9162395
MD5 164e109fae229256ec13a71ebd1d6964
BLAKE2b-256 058cae194f91aedafb808395cf5b4fd0a89c19bdd2d4aa81b1abce3d0400b573

See more details on using hashes here.

Provenance

The following attestation bundles were made for claude_backup_cron-0.1.0.tar.gz:

Publisher: release.yml on hinanohart/claude-backup-cron

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file claude_backup_cron-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for claude_backup_cron-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c7f98aedd10b47ba319aa7211ad847f5ab0955241bee25b35a303489ff8dbe8f
MD5 3f393f43a1d8bbed3e39b1bf69c66a50
BLAKE2b-256 1a258241577925719b42e5136a4ff8a5cce87bc335235aff0703b52c088de35a

See more details on using hashes here.

Provenance

The following attestation bundles were made for claude_backup_cron-0.1.0-py3-none-any.whl:

Publisher: release.yml on hinanohart/claude-backup-cron

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page