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_tois set andageis 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
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file claude_backup_cron-0.1.1.tar.gz.
File metadata
- Download URL: claude_backup_cron-0.1.1.tar.gz
- Upload date:
- Size: 35.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e4ba49a4d551f401acd1021a404b4b249becb958b207a5c3ea821b50ee69a0d3
|
|
| MD5 |
281c82be4efd099f08f20a810c332759
|
|
| BLAKE2b-256 |
9b93f2e4c05df1cea78705131f76bcd0f81033b420255fe7432ade324ff71f7b
|
Provenance
The following attestation bundles were made for claude_backup_cron-0.1.1.tar.gz:
Publisher:
release.yml on hinanohart/claude-backup-cron
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
claude_backup_cron-0.1.1.tar.gz -
Subject digest:
e4ba49a4d551f401acd1021a404b4b249becb958b207a5c3ea821b50ee69a0d3 - Sigstore transparency entry: 1330035213
- Sigstore integration time:
-
Permalink:
hinanohart/claude-backup-cron@dd20b88effa7b330b836a99421b0488a083f98eb -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/hinanohart
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@dd20b88effa7b330b836a99421b0488a083f98eb -
Trigger Event:
push
-
Statement type:
File details
Details for the file claude_backup_cron-0.1.1-py3-none-any.whl.
File metadata
- Download URL: claude_backup_cron-0.1.1-py3-none-any.whl
- Upload date:
- Size: 29.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0c366a66cbe7ba00e4a8589de233e3041758b8634242221db872bc30b489e9c6
|
|
| MD5 |
408230d0e87da605b1e03de260d34464
|
|
| BLAKE2b-256 |
14dcb79d6340cf0dcdfc58af072836e35d01a8954e6deec5cbb4478b9f88de3d
|
Provenance
The following attestation bundles were made for claude_backup_cron-0.1.1-py3-none-any.whl:
Publisher:
release.yml on hinanohart/claude-backup-cron
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
claude_backup_cron-0.1.1-py3-none-any.whl -
Subject digest:
0c366a66cbe7ba00e4a8589de233e3041758b8634242221db872bc30b489e9c6 - Sigstore transparency entry: 1330035310
- Sigstore integration time:
-
Permalink:
hinanohart/claude-backup-cron@dd20b88effa7b330b836a99421b0488a083f98eb -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/hinanohart
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@dd20b88effa7b330b836a99421b0488a083f98eb -
Trigger Event:
push
-
Statement type: