Skip to content

Latest commit

 

History

History
97 lines (73 loc) · 7.52 KB

File metadata and controls

97 lines (73 loc) · 7.52 KB

Privacy and Retention

Codexa is offline-first, but it still writes local operator data: logs, saved chats, pending chat-save WAL records, pilot-run batches, benchmark artifacts, and optional JSON exports. Keep config.yml local and use config-example.yml as the shareable template.

No operator-data egress. Corpus content, queries, embeddings, and the index never leave the host, and there is no telemetry. Online Wikipedia enrichment and LLM provider calls are strictly opt-in, and the default index→extract→chunk→sparse-search path opens no network sockets. This is enforced as an executable invariant in tests/test_todo_rspurp1_no_egress_2026_06_24.py (RS-PURP-1), which blocks every outbound socket and exercises both the default path and Paddle construction. The decision record — including the rejected cloud-default and telemetry options — is ADR 0004.

One inbound exception: the first-run model fetch. allow_model_download (top level) defaults to true, so an embedding model missing from <persist_dir>/hf_cache is fetched once from the Hugging Face Hub. The fetch is inbound-only and carries no operator data; a warm cache opens no socket, because loads run under HF_HUB_OFFLINE=1. To prove zero egress, set allow_model_download: false and pre-cache with codexa fetch embedding — a cache miss then raises ModelNotCachedError naming that command instead of reaching the network. See ADR 0014.

PaddleOCR also starts with PaddleX model-source connectivity checks disabled: its models must already exist in the local cache, otherwise the engine soft-fails with a remediation event and Tesseract remains available.

Retention knobs

All knobs live under privacy: in config.yml.

knob default effect
log_retention_days 7 Rotating JSONL logs under <store.persist_dir>/logs/; 0 keeps indefinitely.
pending_wal_retention_days 30 Pending chat-save WAL replay/cleanup window.
pending_wal_max_bytes 1048576 Safety cap for pending chat WAL storage. Records evicted to stay under it are appended to <wal>.dropped and logged as session_wal_records_evicted, never discarded silently.
chat_retention_days 0 Saved chat retention; 0 keeps indefinitely.
pilot_retention_days 0 Pilot-run retention; 0 keeps indefinitely.
query_history_enabled true Browser localStorage recent-query history. Set false to clear and stop writing it.
query_history_retention_days 0 Browser recent-query history retention; 0 keeps indefinitely. Positive values clear expired local history on page load.
redact_log_paths false When enabled, absolute POSIX, Windows, and UNC paths are reduced to basenames in structured log/export path fields.
redact_export_content false When enabled, JSON exports keep metadata/provenance but set session name, query, answer, and passage body fields to null.

Purge paths

Use these when a workstation changes owner, a corpus is removed, or you need to minimize local operator data:

codexa sessions purge --config config.yml --dry-run
codexa pilot purge --config config.yml --dry-run

Both commands use their configured retention period when --older-than N is omitted (privacy.chat_retention_days and privacy.pilot_retention_days, respectively). Review the dry-run count, then repeat the command with --yes to purge without an interactive confirmation.

Logs are plain files under <store.persist_dir>/logs/ — beside the index they describe, so removing a store removes its logs with it. CODEXA_LOG_DIR overrides the location, and with no store configured they fall back to $XDG_STATE_HOME/codexa/logs (else ~/.local/state/codexa/logs). Benchmark output is separate and unaffected: scripts/bench_indexer.py still writes logs/bench/<timestamp>/ under the repository root. Remove old run directories when they are no longer needed.

Browser recent-query history is held in localStorage["codexa_query_history_v1"]. Set privacy.query_history_enabled: false and reload the UI once to remove it from that browser profile, or clear the site's storage in the browser.

Turn snapshot identity metadata

In generation-published mode, each persisted assistant extra carries generation_id and root_binding_digest as both keys or neither; flat-mode turns omit both. The forward-compatible extra envelope preserves this pair through the local JSON and SQLite session stores, pending-save WAL records and replay, and JSON session export. These are metadata fields, so redact_export_content does not remove them.

The persistence builder validates only that both values are nonempty strings; a real generation lease supplies the canonical form. Its root digest is 64 lowercase hexadecimal characters: SHA-256 of a domain-separated byte string containing the decimal device and inode numbers from the canonical (st_dev, st_ino) root binding. It contains no corpus path or corpus content. It is not secret and not anonymous merely because it is path-free. The digest is not portable across a copied/recreated root and is not a content hash.

The pair is matching-only metadata. Its only persisted consumer is prior-pin snapshot matching, which rejects reuse when the saved pair differs from the active snapshot. The values cannot reopen a generation, recreate a read lease, or authorize access. They are not durable authority, not user content, and must not be displayed as assistant text or treated as a transcript field. The saved-turn render and persistence path does not emit the pair as log fields; other generation lifecycle logs can intentionally name a generation. A later exact-generation read still acquires and validates a fresh filesystem lease.

The local session and WAL files can still contain raw queries, answers, passages, and paths. For saved-session export, content and path redaction retains the identity pair because it is path-free metadata; that does not make an export share-safe by itself. Review the remaining metadata before sharing; result-only exports never invent the pair because they do not serialize assistant extra.

The executable contract covers the path-free digest, both-or-none turn persistence, actual WAL journal/replay and result-only export exclusion, JSON/SQLite session round trips, and privacy-aware session export.

Exports

Saved-session JSON exports and result JSON exports include full passage text by default. With privacy.redact_log_paths: true, source paths are minimized. With privacy.redact_export_content: true, session name, query, answer, and passage body fields are set to null so operators can share metadata/provenance snapshots without raw corpus content.

Wikipedia/ZIM passages carry license, license_url, and attribution fields in rendered/search export surfaces so CC BY-SA provenance survives outside the app. See Wikipedia integration.

Benchmark artifacts

scripts/bench_indexer.py redacts sampled corpus paths by default: sample_paths.txt stores corpus-relative paths when possible, and generated per-rung configs are minimized after each rung. Pass --keep-absolute-paths only when you explicitly need reusable absolute-path artifacts for a private local investigation.