Docs / Operations

Back up & restore projects

Hydrate's backup / restore workflow moves a project's memory between machines without uploading anything. You get one encrypted file, a passphrase, and symmetric CLI commands.

Dehydrate is a separate feature, coming soon — it ingests your CLAUDE.md and docs into Hydrate and shrinks them. Backup / restore is about moving an existing Hydrate project between machines. The two don't overlap.

Pro tier only. Free users can still run Hydrate locally and use the blunt whole-DB backup; per-project bundles are the portable-memory feature Pro pays for.

The three commands

hydrate backup  --project=<slug> [--passphrase=<pass>] [--out=<path>]
hydrate restore <bundle> [--passphrase=<pass>] [--merge=<strategy>] [--dry-run] [--yes]
hydrate inspect <bundle>

hydrate backup

Produces a self-contained, passphrase-encrypted file (AES-256-GCM, Argon2id key derivation). Includes every fact, session summary, and retrieval metric tied to the project — but not your user-scope preferences, licenses, or sync state.

hydrate backup --project=pulse
passphrase:
passphrase (confirm):
backed up 247 facts, 34 sessions, 12 metrics → pulse.hyd.json (2.7 MiB)

hydrate inspect

Reads the unencrypted metadata trailer — no passphrase required. Useful to sanity-check a bundle before you restore it.

hydrate inspect pulse.hyd.json
bundle:         pulse.hyd.json
magic:          HYD1
format_version: 1
exported_at:    2026-04-18T06:15:00Z
project:        pulse
facts:          247
session_summaries: 34
metrics:        12
total_size:     2803671 bytes

hydrate restore

Decrypts, validates, shows a merge preview, and applies. Everything lands in a single SQLite transaction — a failure mid-restore rolls back to the prior state.

hydrate restore pulse.hyd.json --merge=merge-newer
passphrase:
Import preview for project "pulse" (--merge=merge-newer):
  + 47 new facts
  ~ 3 facts updated (newer content)
  = 18 facts unchanged (duplicates)
  + 5 new session summaries
Apply? [y/N]: y
restored project "pulse" via merge-newer: 47 new, 3 updated, 18 unchanged facts; 5 new, 0 updated sessions; 12 metrics

Merge strategies

Pick via --merge=<strategy>. Default is reject, the safest option.

  • reject (default) — aborts with "project already exists" if the slug is already on this machine. Zero writes. First use on a new machine, no collisions possible.
  • replace — drops the existing project and re-inserts everything from the bundle. Use when the bundle is the authoritative copy (e.g. restoring from an earlier backup).
  • merge-newer — for each (project_id, content_hash) pair, keeps whichever side has the higher updated_at timestamp. Ties go to the local copy. The day-to-day cross-machine sync strategy.
  • merge-union — adds any fact whose content_hash isn't already present locally; skips duplicates without touching local rows. Use when a teammate sends you their bundle and you want to fold in their facts without overwriting yours.

Preview before committing

For merge-newer and merge-union, the preview always runs and you confirm with y. Pass --yes to skip the prompt (useful in scripts) or --dry-run to print the preview and exit without writing.

Example: move Pulse from laptop A to laptop B

# On laptop A
hydrate backup --project=pulse
scp pulse.hyd.json laptopB:/tmp/

# On laptop B
hydrate restore /tmp/pulse.hyd.json

That's the whole flow. The bundle is passphrase-encrypted, so copying it over an insecure channel (email, USB stick, public Wi-Fi scp) is still safe — brute-forcing Argon2id+AES-GCM is not a weekend project.

Example: teammate shares their facts

# Teammate backs up
hydrate backup --project=pulse --out=alex-pulse.hyd.json

# You restore without overwriting your own work
hydrate restore alex-pulse.hyd.json --merge=merge-union

Your facts stay unchanged; any Alex fact that doesn't already exist on your machine lands.

Embedding dimension mismatches

Bundles record the embedding model + dim they were built with. If your local install uses a different embedder (e.g. bundle used 384-d all-MiniLM-L6-v2, your install uses 1536-d text-embedding-3-small), the restore still succeeds but:

  • The fact rows land with their metadata intact.
  • Their embeddings are discarded — silently storing mismatched-dim vectors would break retrieval in subtle ways.
  • Each such fact is queued for re-embedding with your local embedder.
  • A warning line tells you how many facts were queued.

Check the configured dim with hydrate config get embed.dim. Override it for a restore with HYDRATE_EMBED_DIM=1536 hydrate restore ....

Pre-flight check

hydrate doctor

Runs license / config / embed-dim / store / round-trip checks. Prints READY TO SHIP when green. Safe to run any time you're unsure a fresh clone is wired up correctly.

Troubleshooting

  • wrong passphrase — GCM authentication failed. Double-check capitalisation and trailing whitespace.
  • bundle is corrupted or incomplete — the file was truncated in transit. Re-copy.
  • this bundle was exported from a newer Hydrate; please upgradeformat_version exceeds what this build supports. Upgrade with brew upgrade hydrate (or run the installer again).
  • project 'pulse' already exists — with --merge=reject (the default) collisions abort. Rerun with --merge=replace, merge-newer, or merge-union depending on intent.
  • Did you mean `hydrate backup`? — you ran the pre-v0.2 name (hydrate export / hydrate import). The commands were renamed to backup / restore in v0.2. The bundle format is unchanged.
  • Free-tier cap reached — Free is capped at 2 active projects. Run hydrate project deactivate <slug> to make room, or upgrade to Pro for unlimited projects.

What's out of scope

  • No cloud sync. Ever. The whole point of per-project bundles is that the user controls the file.
  • No incremental / delta bundles in v0.3. Each backup is a full snapshot. Post-launch.
  • No team-tier shared memory — separate plan, separate pricing tier.