Skip to content

Getting started

This page takes you from nothing to one machine archiving its AI coding sessions every hour, with no consolidation and no cloud — the simplest configuration that is still a real Recall install, and a complete product on its own. Everything below was run against the repo as shipped; the command output is reproduced as printed, with only machine-specific paths and hardware identifiers shortened for readability.

For a multi-machine fleet — where each machine archives to its own local root and pushes its subtree into a shared consolidated root — use the full Guided install guide instead. This page exists to show the moving parts on a single box first.

Prerequisites

RequirementCheckNote
Python 3.9+python3 --versionThe sync layer is pure stdlib — nothing to pip install.
gitgit --versionRecall runs straight from a clone of its repo; updating is git pull.
At least one AI coding toolls ~/.claude/projectsClaude Code shown here; ~/.gemini (Gemini CLI, Antigravity) and ~/.codex (Codex CLI) are archived whenever present.

1. Clone the repo

There is no package and no deployed copy: the scheduler unit the installer writes points into this checkout, so the clone’s location should be durable, and git pull is the entire update mechanism.

Terminal window
git clone https://github.com/attilabalogh/pibmo-recall.git ~/code/pibmo-recall

This provides the four entry points you will meet below: install.sh (the per-machine installer), scripts/sync/sync-transcripts.py (the hourly sync), scripts/sync/doctor.py (the health check), and scripts/process/process_root.py (the processing layer — not needed today).

2. Run the installer

The installer asks three questions; on a single computer, Enter answers all three. It proposes the platform-default local dataset root, registers this machine’s hardware UUID under a friendly name, seeds the shared config, and writes the native scheduler unit — a LaunchAgent on macOS, a systemd user timer on Linux. It never edits your crontab, and it writes nothing into ~/.claude or ~/.pibmo.

Terminal window
cd ~/code/pibmo-recall && bash install.sh
=== Pibmo Recall install (per-machine setup) ===
Repo: ~/code/pibmo-recall
Interpreter: /usr/bin/python3
Local dataset root (this machine's archive) [~/Library/Application Support/pibmo/recall]:
Friendly name for THIS machine (e.g. moraba, gavi) [gavi]:
Multi-machine setup? Configure optional destinations [y/N]:
machines.json: registered 3F2504E0... as 'gavi'
projects.json: seeded from projects.example.json
profiles.json: seeded from profiles.example.json
wrote ~/Library/LaunchAgents/com.pibmo.recall.sync.plist
Activate (idempotent; bootout first when re-writing an active unit):
launchctl bootout gui/$UID com.pibmo.recall.sync 2>/dev/null || true
launchctl bootstrap gui/$UID "~/Library/LaunchAgents/com.pibmo.recall.sync.plist"
If a sync destination crosses a vendor cloud mount (e.g. a
consolidated root under ~/Library/CloudStorage), the first launchd
run may raise ONE macOS consent prompt for python3 -- grant it once.
=== Done ===
'gavi' is registered and the shared config is in place under the local root:
~/Library/Application Support/pibmo/recall
Archiving scope: THIS user's ~/.claude (+ ~/.gemini, ~/.codex) only. A
machine-wide multi-user sweep is a separate, explicit opt-in (currently
in design) -- it is never switched on silently.
Next -- run the activation commands printed above, then prove the loop:
1. First sync now, by hand (safe any time; add --dry-run to preview):
/usr/bin/python3 "~/code/pibmo-recall/scripts/sync/sync-transcripts.py" --root "~/Library/Application Support/pibmo/recall" -v
ls "~/Library/Application Support/pibmo/recall/gavi/claude/projects/" | head
2. Health check any time (each failure prints its exact fix):
/usr/bin/python3 "~/code/pibmo-recall/scripts/sync/doctor.py" --root "~/Library/Application Support/pibmo/recall" --machine "gavi" --python "/usr/bin/python3" --repo "~/code/pibmo-recall"
3. After the next full hour:
tail "~/Library/Application Support/pibmo/recall/gavi/logs/cron.log"
Prefer plain cron instead? Skip the activation commands -- never schedule
both -- and paste this via 'crontab -e' (check first for stale entries:
crontab -l | grep -n -E 'sync-transcripts|sync\.sh|pibmo'):
0 * * * * /usr/bin/python3 "~/code/pibmo-recall/scripts/sync/sync-transcripts.py" --root "~/Library/Application Support/pibmo/recall" >> "~/Library/Application Support/pibmo/recall/gavi/logs/cron.log" 2>&1
Nothing was written to ~/.claude or ~/.pibmo. The dataset-root path is carried
only on the unit / cron line (use --root, or export PIBMO_ROOT, for manual runs).

What the installer did:

  • Proposed a local root and refused the alternatives. The local root is where preservation happens, so it must be a path no cloud client, permission fence, or network can deny. Point the first prompt at a Dropbox folder and the installer fails closed rather than accept it:

    REFUSED as the local dataset root: ~/Dropbox/Datasets/pibmo
    - path component 'Dropbox' looks cloud-synced (dropbox)
    Risks of a cloud-synced local root (the 2026-06/07 fleet outage class):
    - schedulers can be permission-fenced off the path (macOS TCC denies
    launchd and cron access to ~/Library/CloudStorage) -> total, silent outage
    - the cloud client can evict files to dataless placeholder stubs the
    sync cannot read
    - client rewrites and conflict copies violate append-only preservation
    - when the client is not running, nothing is preserved at all
    The local root must be a plain local path -- a hard rule of the
    preservation design. A cloud-synced folder is welcome as the CONSOLIDATED
    root instead -- that is replication's job, not preservation's.
    Detection is heuristic: re-run with --force to override eyes-open.
  • Registered this machine. The hardware UUID maps to the friendly name gavi in {root}/machines.json, and everything this machine archives lands under its own {root}/gavi/ subtree.

  • Seeded the shared config. projects.json (process-time grouping of projects) and profiles.json (output profiles for the processing layer) come from the repo’s *.example.json templates; an existing file is never overwritten.

  • Wrote the scheduler unit, not your crontab. The dataset-root path travels on the unit’s command line — there is no dotfile and no config discovery. The crontab line is printed as a fallback, never pasted for you.

Re-running the installer with the same answers reports machines.json: 3F2504E0... already registered as 'gavi' and projects.json: kept (already present) — it is deterministic.

3. Sync, list, verify

The first sync is safe to run by hand at any time — concurrent runs exit immediately via a lock, and --dry-run previews without writing a byte:

Terminal window
ROOT="$HOME/Library/Application Support/pibmo/recall"
python3 ~/code/pibmo-recall/scripts/sync/sync-transcripts.py --root "$ROOT" --dry-run
=== Pibmo Recall sync [dry run] — 2026-07-22 06:00 ===
machine: gavi
root: ~/Library/Application Support/pibmo/recall
[dry] would add new project to projects.json: -Users-jo-code-acme-app
Done. 8 file action(s): {"claude": {"new": 2}, "gemini": {"new-version": 3}, "codex": {"new-version": 1, "new": 2}}

Now for real, verbose:

Terminal window
python3 ~/code/pibmo-recall/scripts/sync/sync-transcripts.py --root "$ROOT" -v
=== Pibmo Recall sync — 2026-07-22 06:00 ===
machine: gavi
root: ~/Library/Application Support/pibmo/recall
Claude:
new -Users-jo-code-acme-app/9c2f51db-4f76-4f89-9e56-1d1f37c9a1b4.jsonl
new -Users-jo-code-acme-app/d81a2f66-0c3e-47c5-8b3a-52aa9f14e7c2.jsonl
Gemini:
new-version settings.json
new-version tmp/6f1acb2d90e4/chats/session-2026-07-21.json
new-version tmp/6f1acb2d90e4/logs.json
Codex:
new-version config.toml
new history.jsonl
new sessions/2026/07/21/rollout-2026-07-21T16-20-31-1f9e8d7c-6b5a-4c3d-2e1f-0a9b8c7d6e5f.jsonl
adding new project to projects.json: -Users-jo-code-acme-app
Done. 8 file action(s): {"claude": {"new": 2}, "gemini": {"new-version": 3}, "codex": {"new-version": 1, "new": 2}}

Each file was routed to a preservation primitive by type: .jsonl transcripts go through lineage (new, later extend or fork), opaque files become content-addressed versions (new-version), and SQLite databases are logically snapshotted. The raw layout is mirrored exactly as the tools wrote it — no renames, no remapping; the projects.json row that was auto-appended defaults to process: false and only ever affects how the processing layer groups output, never where raw data lands.

The archive is now on disk, laid out per machine and per tool:

Terminal window
ls "$ROOT/gavi/claude/projects/-Users-jo-code-acme-app/"
9c2f51db-4f76-4f89-9e56-1d1f37c9a1b4.jsonl
d81a2f66-0c3e-47c5-8b3a-52aa9f14e7c2.jsonl

Run the sync again and nothing happens — a (size, mtime) fast path skips every unchanged file without reading it:

Done. 0 file action(s): {"claude": {}, "gemini": {}, "codex": {}}

4. Watch a rewrite fork instead of destroy

The reason Recall exists: transcripts get rotated, truncated, and rewritten by the tools that produce them. Two more sync runs against a live session show both halves of the append-only guarantee. First, the session grew two lines between runs — the archived lineage extends:

Claude:
extend -Users-jo-code-acme-app/9c2f51db-4f76-4f89-9e56-1d1f37c9a1b4.jsonl
Done. 1 file action(s): {"claude": {"extend": 1}, "gemini": {}, "codex": {}}

Then the session was compacted — Claude Code rewrote the file shorter, with different content after the first line. The archive is never overwritten or shrunk; the divergent rewrite lands as a sibling fork:

Claude:
fork -Users-jo-code-acme-app/9c2f51db-4f76-4f89-9e56-1d1f37c9a1b4.jsonl
Done. 1 file action(s): {"claude": {"fork": 1}, "gemini": {}, "codex": {}}
Terminal window
ls "$ROOT/gavi/claude/projects/-Users-jo-code-acme-app/"
9c2f51db-4f76-4f89-9e56-1d1f37c9a1b4.fork-93107689.jsonl
9c2f51db-4f76-4f89-9e56-1d1f37c9a1b4.jsonl

The original 8-line lineage is byte-for-byte untouched; the 3-line post-compaction rewrite lives beside it, keyed by a hash of the first divergent line (fork-93107689), so a growing fork extends in place instead of spawning duplicates. A source that merely shrank back to a prefix is skipped outright — the archive never regresses. Every one of these observations is also recorded in {root}/gavi/pibmo.db, the per-machine provenance database.

5. Activate the hourly schedule

The installer already printed these; they are idempotent. On macOS:

Terminal window
launchctl bootout gui/$UID com.pibmo.recall.sync 2>/dev/null || true
launchctl bootstrap gui/$UID ~/Library/LaunchAgents/com.pibmo.recall.sync.plist

On Linux (the installer writes pibmo-recall-sync.service and .timer under ~/.config/systemd/user/; add loginctl enable-linger $USER on a headless box):

Terminal window
systemctl --user daemon-reload
systemctl --user enable --now pibmo-recall-sync.timer

The unit fires at the top of every hour; ticks missed while the machine slept coalesce into one catch-up run on wake (RunAtLoad on macOS, Persistent=true on systemd). Prefer plain cron? Paste the printed fallback line instead — never schedule both. Each scheduled run appends two lines to {root}/gavi/logs/cron.log:

[2026-07-22 06:03] sync started
[2026-07-22 06:03] sync complete

What the doctor shows

One command answers “is this machine actually archiving?” — registration, root writability (a create-and-delete canary under gavi/logs/, never inside the raw archive), the scheduler unit (current bytes and embedded paths that still exist — the moved-repo class of outage), the heartbeat every run records in pibmo.db, and log recency. Run seconds after install, before the first scheduled run has landed, it reports:

Terminal window
python3 ~/code/pibmo-recall/scripts/sync/doctor.py --root "$ROOT"
Pibmo Recall doctor -- machine 'gavi', root ~/Library/Application Support/pibmo/recall
OK root ~/Library/Application Support/pibmo/recall/machines.json readable (1 machine(s) registered)
OK identity 3F2504E0...
OK registered 'gavi'
OK writable canary ok under gavi/logs/
OK unit 1 unit file(s) current
OK unit-cmd embedded interpreter and script exist
OK heartbeat last completed sync 2026-07-21T20:00:39.138250+00:00 (0.0h ago)
WARN log no ~/Library/Application Support/pibmo/recall/gavi/logs/cron.log yet
fix: expected after the first scheduled run; manual -v runs print to the terminal instead
Summary: 7 ok, 1 warn, 0 fail

The WARN clears once the first scheduled run lands (heartbeats are stored in UTC; the sync headers print local time). The exit code is 0 with warnings and 1 on any failure — and every failure prints its exact remediation in indented fix: lines, just like the warning above. On a multi-machine setup, --consolidate adds replication-lag rows per machine. See Troubleshooting.

Two things to know before you rely on it

A dead scheduler preserves nothing, and it fails silently. Recall only archives when the unit fires. Sleep is handled — missed ticks coalesce into a catch-up run on wake — but a unit whose embedded repo path moved, or a permission fence between the scheduler and the root, stops preservation without an error you would ever see. That failure class is why the doctor checks the unit’s embedded paths and the heartbeat’s age rather than trusting that the unit exists, and why the installer refuses root placements where those fences are known to appear. Run the doctor whenever you are unsure; believing is not a health check.

Exclusion is not redaction. The sync excludes credentials at the source (~/.codex/auth.json, ~/.gemini/oauth_creds.json, google_accounts.json, and friends), so stored secrets never enter the archive. Everything else in a session is preserved verbatim, and append-only means nothing is ever deleted — prompts, file contents quoted into context, command output, mistakes. The archive is exactly as sensitive as the sessions it preserves. Keep the local root local (the installer insists), and treat replication to cloud storage as a deliberate, separate decision: Recall delegates it to a tool you choose and never speaks a cloud API itself.

Next

  • Guided install — the full per-machine guide: WSL, the optional context banner, and the multi-machine tiers (consolidated root, delegated replication).
  • Concepts — roots, machines, lineage and forks, provenance, profiles.
  • Configurationmachines.json, projects.json, profiles.json, and --root / PIBMO_ROOT.
  • Processing — the deterministic, zero-LLM layer that projects the raw archive into per-project Markdown.
  • Troubleshooting — reading the doctor’s remediations when a row goes red.