Chronica is a local, continuously updated memory of your AI conversations.
It can reclaim ChatGPT history directly, import official exports, keep the archive incrementally current, search it lexically or semantically, assemble historical context, surface unfinished threads, and follow related conversation context. It is one application with one executable: chronica.
The older GPT-Export-Parser, ml_trainer, knowledge_graph, Beacon, and Ephemeris repositories contributed mechanisms. They are provenance, not bundled applications or runtime dependencies. See docs/ARCHITECTURE.md.
The default install is deliberately small and CPU-only:
python -m pip install --no-cache-dir -e .Default runtime dependencies are only PyYAML, Requests, and NumPy. Chronica does not install PyTorch, CUDA/NVIDIA wheels, FAISS, SciPy, scikit-learn, Playwright, Selenium, ChromeDriver, a browser, or an embedding model.
Optional neural semantic embeddings are explicit and CPU-only:
python -m pip install --no-cache-dir -e '.[semantic-cpu]'Leave the default retrieval.embedding_backend: hash on a constrained system unless you specifically want the optional FastEmbed model.
Do not merge a new release over an old source tree. Stale Python modules can survive a directory overlay even when pip reports the new distribution version. Extract the new release to a separate directory, then let its release applicator replace only Chronica-managed source:
python /path/to/new/Chronica/tools/apply_release.py /path/to/existing/ChronicaThe applicator replaces src/, tests/, docs/, and release-owned root/tool files while preserving local state such as data-root/, chronica.yml, .git/, and local virtual environments. It verifies the release runtime manifest before and after replacement, rolls back managed files if replacement fails, and removes only disposable packaging artifacts (build/, dist/, egg-info, pytest cache) so setuptools cannot reuse stale built Python files from an older release.
Then install from the existing repository. On systems where the boot-drive temporary area is constrained, point Python/pip temporary work at the configured data volume:
mkdir -p /path/to/large-storage/chronica/tmp
TMPDIR=/path/to/large-storage/chronica/tmp python -m pip install --no-cache-dir --upgrade .Every public chronica/python -m chronica invocation verifies the installed runtime file inventory and hashes against the packaged release manifest before executing a command. A mixed or stale installation fails before archive/index/browser mutation. Wheel metadata and runtime version derive from the same _version.py authority.
Choose bulk storage explicitly:
chronica init --data-root /path/to/large-storage/chronicaChronica will not silently choose your home/boot drive for the archive, index, browser profile, run evidence, or model cache.
chronica import /path/to/chatgpt-export.zip
chronica search "what was I working on"Import streams conversations.json into Chronica's transactional archive instead of adopting the large aggregate JSON file as steady-state authority.
Chronica v0.3.8 prefers the Chrome session you already have open. Enable Chrome's local remote-debugging server, keep a logged-in https://chatgpt.com/ tab open, and run the update directly:
chronica updateThe default chatgpt.browser_session_mode: auto first probes the configured loopback-only chatgpt.browser_debug_url (http://127.0.0.1:9222 by default). Chronica supports both classic DevTools discovery and Chrome 144+ approval-only remote debugging. In approval-only mode, /json/* discovery may intentionally return HTTP 404; Chronica opens one browser-level /devtools/browser WebSocket, waits for Chrome approval, and keeps that same approved connection for the entire synchronization run. It does not launch, navigate, or close the user's browser. Local DevTools HTTP/WebSocket traffic bypasses proxy environment variables.
Live provider requests are executed from inside the authenticated ChatGPT tab, so Chrome retains ownership of cookies, Cloudflare/browser state, TLS identity, and the user's existing external network/proxy path. Chronica retains ownership of request pacing, response validation, transactional archive writes, and failure semantics. The short-lived bearer token remains process-memory-only and is used inside that browser transport; it is never written to configuration, logs, provider pacing state, or retained run artifacts.
If no existing DevTools endpoint is actually listening, auto falls back to the older dedicated-profile path under data_root. A live endpoint that denies approval, times out waiting for approval, or is protocol-incompatible is reported explicitly and does not trigger a second browser launch. browser_session_mode: existing makes attachment mandatory and never launches a browser; browser_session_mode: dedicated always uses the Chronica-owned profile. chronica login follows the same configured ownership rule and is only needed when the selected browser session is not already authenticated. Chronica does not fill forms or use WebDriver/Playwright.
chronica browser-check remains a standalone diagnostic. On approval-only Chrome it opens its own short-lived CDP connection and can therefore cause a separate approval prompt; it is not a required pre-step for chronica update.
If upgrading from a challenged v0.2.1 Playwright profile, chronica login --reset-profile deletes only the Chronica-owned fallback browser profile and refuses paths outside data_root.
sync_archive
fetch only new/updated conversations
commit every successful conversation transactionally
|
v
normalize_archive
normalize only changed conversation revisions
|
v
rebuild_index
reindex only changed conversations
One Chronica mutation lock protects update, import, explicit indexing, browser-profile login, and lazy related-edge writes. A timer cannot overlap a manual update, and an import/index/login cannot race an update. If individual conversation downloads fail after other downloads succeeded, those successful commits still normalize/index in the same run; the update is reported as partial and returns non-success so the misses remain visible. Authentication/list failures remain fatal.
Large first backfills use adaptive provider pacing with separate metadata and conversation-detail lanes. Both start conservatively at one request per second, share provider-wide cooldown evidence, and persist learned safe spacing in archive/chatgpt/.provider_pacing.json. HTTP 429 responses honor both forms of Retry-After; repeated depletion estimates the provider's apparent refill interval and applies a safety margin instead of restarting the same burst on every process. Learned pacing, next-safe-request time, and recovery progress survive manual or scheduled restarts. Per-request no-progress retries, cumulative provider-cooldown waiting, and an internal sync deadline bound recovery without discarding already committed conversations. A bounded run exits partial before the outer subprocess timeout when the next safe request no longer fits, and the next update resumes from the transactional archive. Pre-v0.3.4 pacing keys such as request_interval_ms, inter_request_delay_ms, and the old event-budget settings remain parseable for upgrade compatibility but do not control the new adaptive policy.
Older Chronica releases used archive/chatgpt/conversations.json. On first update after upgrading, v0.3 streams that file into archive/chatgpt/archive.sqlite3 conversation-by-conversation and leaves the original file untouched. It does not redownload conversations already represented there. Update startup also removes legacy pre-v0.3 token.txt copies only from Chronica-owned run-evidence directories and records that redaction without retaining the secret.
chronica search "how did I solve the async bug"
chronica search '"exact phrase" async' --mode lexical
chronica context "continue the Chronica architecture work" --max-tokens 3000
chronica threads --days 30
chronica related 42 --hops 2A normal chronica search returns all three retrieval views at once: hybrid, lexical, and semantic. Chronica performs one lexical pass and one semantic pass, then derives the hybrid RRF ranking from those same candidates; it does not repeat the semantic scan. --mode hybrid, --mode lexical, and --mode semantic remain available as diagnostic/override views rather than required everyday choices. JSON output preserves the three rankings under hybrid, lexical, and semantic keys.
Hybrid search combines FTS5/BM25 with vector ranking. The default hash vectors are zero-download and CPU-only. Exact semantic search scans stored embeddings in bounded batches rather than loading a corpus-wide vector matrix into RAM. If you deliberately change the embedding backend/model, read commands do not silently download/re-encode the corpus: run chronica index explicitly to migrate the retrieval projection first. Lexical search remains available without that migration.
Related edges are lazy: ordinary updates do not perform an all-pairs graph rebuild. When chronica related needs relationships for a chunk, Chronica computes a bounded neighbor set, persists it, and reuses it.
Chronica is designed to remain usable on limited consumer hardware:
- no GPU execution path;
- no Torch/CUDA/NVIDIA dependency tree;
- no bundled Chromium;
- an already-running browser can be reused through a loopback-only DevTools endpoint;
- when the dedicated fallback is used, browser profile and browser/Mesa caches live below the explicit
data_root; - no large aggregate archive rewrite during daily sync;
- no full aggregate JSON load during normal daily normalization;
- successful remote fetches are committed independently, so interruption does not discard prior progress;
- update indexing touches only changed conversations;
- no O(n²) corpus-wide related-edge rebuild;
- semantic scan uses bounded batches instead of a persistent full in-memory matrix;
- optional neural embeddings default to two CPU threads and batch size eight;
- pipeline stages are bounded by typed timeout configuration;
- update/index preflight configured disk/RAM floors before expensive work;
- imports additionally reserve disk proportional to the uncompressed export before archive/index mutation.
- long imports, normalization passes, and index updates emit bounded progress instead of appearing hung.
chronica status reports archive counts, retrieval counts, active embedding backend, and accelerator.
chronica init
chronica browser-check
chronica login
chronica import <export.zip|directory|conversations.json>
chronica update
chronica plan
chronica index
chronica search|query <text>
chronica context <text>
chronica related <chunk-id>
chronica threads
chronica status
chronica history
chronica schedule show
chronica schedule install --enable
chronica convert <conversation.json> <conversation.md>
chronica schedule show
chronica schedule install --enableThe systemd user timer re-enters the same chronica update command used manually. The application-level update lock prevents overlap.
.
├── README.md
├── LICENSE
├── pyproject.toml
├── chronica.example.yml
├── docs/ARCHITECTURE.md
├── src/chronica/
│ ├── cli.py
│ ├── config.py
│ ├── archive.py
│ ├── memory.py
│ ├── pipeline.py
│ ├── scheduler.py
│ ├── ingestion/chatgpt/
│ ├── retrieval/
│ └── automation/
└── tests/
No active source file is intended to exceed 500 logical lines. Raw archive state remains authoritative; normalized messages, indexes, embeddings, related edges, and run evidence cannot silently become archive truth.