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
| Requirement | Check | Note |
|---|---|---|
| Python 3.9+ | python3 --version | The sync layer is pure stdlib — nothing to pip install. |
| git | git --version | Recall runs straight from a clone of its repo; updating is git pull. |
| At least one AI coding tool | ls ~/.claude/projects | Claude 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.
git clone https://github.com/attilabalogh/pibmo-recall.git ~/code/pibmo-recallThis 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.
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. aconsolidated root under ~/Library/CloudStorage), the first launchdrun 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. Amachine-wide multi-user sweep is a separate, explicit opt-in (currentlyin 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 scheduleboth -- 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 carriedonly 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 denieslaunchd and cron access to ~/Library/CloudStorage) -> total, silent outage- the cloud client can evict files to dataless placeholder stubs thesync cannot read- client rewrites and conflict copies violate append-only preservation- when the client is not running, nothing is preserved at allThe local root must be a plain local path -- a hard rule of thepreservation design. A cloud-synced folder is welcome as the CONSOLIDATEDroot 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
gaviin{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) andprofiles.json(output profiles for the processing layer) come from the repo’s*.example.jsontemplates; 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:
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:
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/recallClaude: new -Users-jo-code-acme-app/9c2f51db-4f76-4f89-9e56-1d1f37c9a1b4.jsonl new -Users-jo-code-acme-app/d81a2f66-0c3e-47c5-8b3a-52aa9f14e7c2.jsonlGemini: new-version settings.json new-version tmp/6f1acb2d90e4/chats/session-2026-07-21.json new-version tmp/6f1acb2d90e4/logs.jsonCodex: 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:
ls "$ROOT/gavi/claude/projects/-Users-jo-code-acme-app/"9c2f51db-4f76-4f89-9e56-1d1f37c9a1b4.jsonld81a2f66-0c3e-47c5-8b3a-52aa9f14e7c2.jsonlRun 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": {}}ls "$ROOT/gavi/claude/projects/-Users-jo-code-acme-app/"9c2f51db-4f76-4f89-9e56-1d1f37c9a1b4.fork-93107689.jsonl9c2f51db-4f76-4f89-9e56-1d1f37c9a1b4.jsonlThe 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:
launchctl bootout gui/$UID com.pibmo.recall.sync 2>/dev/null || truelaunchctl bootstrap gui/$UID ~/Library/LaunchAgents/com.pibmo.recall.sync.plistOn Linux (the installer writes pibmo-recall-sync.service and .timer
under ~/.config/systemd/user/; add loginctl enable-linger $USER on a
headless box):
systemctl --user daemon-reloadsystemctl --user enable --now pibmo-recall-sync.timerThe 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 completeWhat 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:
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 insteadSummary: 7 ok, 1 warn, 0 failThe 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.
- Configuration —
machines.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.