Skip to content

Data storage & logs

Everything the bridge writes lives in two directories — data under ~/.painapple-code/, credentials under ~/.config/painapple-code/ — never inside your project folders.

Data directory: ~/.painapple-code/

Override with PAINAPPLE_CODE_HOME. The Docker image sets it to /data (a named volume by default).

~/.painapple-code/
├── config.json              # Global settings
├── tab-state.json           # Open tabs + active tab (server-side, survives PWA quirks)
├── shortcuts.json           # Keyboard-shortcut overrides
├── favorites.json           # Favorited sessions
├── prompt-favorites.json    # Favorited prompts
├── presets/                 # Prompt/config presets (one JSON per preset)
├── shadow.duckdb            # Turn metadata database (DuckDB, + .wal sidecar)
├── logs/                    # Server logs (auto-rotating, see below)
└── projects/
    └── {hash}/              # 12-char SHA256 of the resolved project path
        ├── path             # Original project path (reverse lookup)
        ├── config.json      # Project-specific settings
        ├── sessions/
        │   └── {id}/
        │       ├── meta.json      # Session metadata (~350 bytes)
        │       ├── messages.jsonl # Parsed conversation + cost (append-only)
        │       ├── raw.jsonl      # Raw Claude I/O (debugging/audit)
        │       ├── tools/         # Large tool outputs (Read_xxx.txt, Bash_xxx.txt, …)
        │       ├── stash.json     # Comments-stash items
        │       └── uploads/       # Uploaded images/files
        └── shadow-git/      # Per-project shadow git repository (auto-journal)

Notes:

  • One project per path. The {hash} is derived from the absolute project path, so two clones of the same repo at different paths are separate projects.
  • UI-state files are per-tier. With --state-suffix dev, the top-level state files become tab-state-dev.json, presets-dev/, and so on; project and session history stays shared. See the Server CLI reference.
  • Uploads before a session exists land in uploads/tmp/ at the top level.

Config directory: ~/.config/painapple-code/

Override with PAINAPPLE_CODE_CONFIG. Kept separate so credentials survive wiping the data directory.

Path Purpose
config.yaml Auth password (mode 0600, parent 0700)
tokens/ Optional token-profile files (alternate API credentials selectable per session)
cert.pem, key.pem Auto-generated self-signed TLS cert/key (when TLS is enabled)
fingerprint Cert fingerprint sidecar (only with --tls-fp-url)

Log files

All in ~/.painapple-code/logs/ (override with --log-dir):

File Content Rotation
server.log All events, INFO and up 10 MB, 5 backups
access.log HTTP/WebSocket requests with timing 10 MB, 3 backups
error.log Errors with tracebacks 5 MB, 10 backups
crash.log Redirected stderr — uvicorn errors, unhandled exceptions Append-only, no rotation
server.pid PID of the running server (for post-mortem analysis) Removed on clean exit
cat ~/.painapple-code/logs/error.log
tail -f ~/.painapple-code/logs/access.log

Docker

In the container, PAINAPPLE_CODE_HOME=/data and /data is a named volume (or a bind mount, depending on your DATA_VOLUME setting). To copy the volume out to the host:

painapple docker extract ./painapple-data-export

See the Docker CLI reference.

Backup, cleanup, and wipe

# Back up everything (sessions, journal, DB, settings)
tar czf painapple-backup.tgz -C ~ .painapple-code

# Find which project a hash belongs to
cat ~/.painapple-code/projects/{hash}/path

# Remove one project's history (sessions + shadow git)
rm -rf ~/.painapple-code/projects/{hash}

# Full wipe — auth config survives (it lives in ~/.config/painapple-code/)
rm -rf ~/.painapple-code

Stop the server before touching shadow.duckdb

DuckDB is single-writer and keeps a .wal sidecar. Back up or move the database only while the bridge is stopped, and copy the .wal file together with it.

Resuming sessions in the plain CLI

Sessions created through the bridge are regular Claude Code sessions. The provider session ID is stored in each session's meta.json (provider_session_id), and you can pick any conversation up from a terminal:

claude --resume <session-id>