Claude Code + Codex Transcript Sync

Central repository for Claude Code and Codex session transcripts from multiple machines

Setup Instructions

1 Set environment variables

Add these to your shell profile (~/.bashrc, ~/.zshrc, etc.):

# Claude Sync configuration
export CLAUDE_SYNC_URL="https://claude.cee.wtf"
export CLAUDE_SYNC_KEY="your-api-key-here"
export CLAUDE_MACHINE_ID="$(hostname)"  # or a custom name

Then reload your shell: source ~/.bashrc (or restart your terminal)

2 Create the hook script

Save this to ~/.claude/hooks/sync-session.sh (on Windows: C:\Users\<you>\.claude\hooks\sync-session.sh):

#!/bin/bash
# Claude Code session sync hook
# Uploads transcript to central server on session end and before compacts

: "${CLAUDE_SYNC_URL:=https://claude.cee.wtf}"
: "${CLAUDE_SYNC_KEY:=}"
: "${CLAUDE_MACHINE_ID:=$(hostname)}"

# Skip if no API key configured
[ -z "$CLAUDE_SYNC_KEY" ] && exit 0

# Read hook input from stdin
INPUT=$(cat)
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // empty')
TRANSCRIPT_PATH=$(echo "$INPUT" | jq -r '.transcript_path // empty')

# Validate required fields
[ -z "$SESSION_ID" ] || [ -z "$TRANSCRIPT_PATH" ] && exit 0

# Convert Windows path to WSL path if running under WSL
if command -v wslpath >/dev/null 2>&1; then
    TRANSCRIPT_PATH=$(wslpath "$TRANSCRIPT_PATH" 2>/dev/null || echo "$TRANSCRIPT_PATH")
fi

# Expand ~ in path
TRANSCRIPT_PATH="${TRANSCRIPT_PATH/#\~/$HOME}"
[ -f "$TRANSCRIPT_PATH" ] || exit 0

# Upload in background (don't block session exit)
curl -s -X POST "$CLAUDE_SYNC_URL/api/sessions" \
    -H "X-API-Key: $CLAUDE_SYNC_KEY" \
    -F "machine_id=$CLAUDE_MACHINE_ID" \
    -F "session_id=$SESSION_ID" \
    -F "transcript=@$TRANSCRIPT_PATH" \
    --max-time 10 >/dev/null 2>&1 &

exit 0

Make it executable:

mkdir -p ~/.claude/hooks
chmod +x ~/.claude/hooks/sync-session.sh

Windows (WSL): Hardcode CLAUDE_SYNC_KEY and CLAUDE_MACHINE_ID directly in the script — shell profile env vars won't be inherited when Claude Code invokes WSL.

3 Configure Claude Code hooks

Edit ~/.claude/settings.json to add the hooks:

{
  "hooks": {
    "SessionEnd": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "~/.claude/hooks/sync-session.sh"
          }
        ]
      }
    ],
    "PreCompact": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "~/.claude/hooks/sync-session.sh"
          }
        ]
      }
    ]
  }
}

Why PreCompact? Long sessions compact multiple times. Uploading on each compact captures intermediate states, so if a session crashes you still have the last pre-compact snapshot. Same session_id overwrites previous upload.

Windows (WSL): Git Bash converts /mnt/c/... paths incorrectly. Use this command instead:
MSYS_NO_PATHCONV=1 wsl bash /mnt/c/Users/<you>/.claude/hooks/sync-session.sh

4 Verify setup

Test the upload manually:

# Create a test transcript
echo '{"test":true}' > /tmp/test.jsonl

# Upload it
curl -X POST https://claude.cee.wtf/api/sessions \
    -H "X-API-Key: $CLAUDE_SYNC_KEY" \
    -F "machine_id=test" \
    -F "session_id=test-$(date +%s)" \
    -F "transcript=@/tmp/test.jsonl"

# List sessions to confirm (auth required)
curl -H "X-API-Key: $CLAUDE_SYNC_KEY" \
    https://claude.cee.wtf/api/sessions
5 Upload previous sessions (optional)

The hook only uploads sessions when they end. To bulk upload existing sessions from ~/.claude/projects/:

# Upload all existing sessions
for file in ~/.claude/projects/*/*.jsonl; do
    [ -f "$file" ] || continue
    session_id=$(basename "$file" .jsonl)
    echo "Uploading $session_id..."
    curl -s -X POST "$CLAUDE_SYNC_URL/api/sessions" \
        -H "X-API-Key: $CLAUDE_SYNC_KEY" \
        -F "machine_id=$CLAUDE_MACHINE_ID" \
        -F "session_id=$session_id" \
        -F "transcript=@$file"
done

This is only needed once per machine to backfill historical sessions. Future sessions will sync automatically.

Codex / ChatGPT desktop

1 Install the Codex client

Copy client/sync_codex.py from the service repository to ~/.codex/hooks/sync_codex.py, then create ~/.codex/claude-sync.json:

{
  "url": "https://claude.cee.wtf",
  "api_key": "your-api-key-here",
  "machine_id": "my-computer-codex"
}
2 Configure Codex lifecycle hooks

Copy client/hooks.json.example to ~/.codex/hooks.json, fix the Windows username/Python path if needed, then review and trust the hook with /hooks. It syncs Stop, PreCompact, SubagentStop, and SessionEnd.

# Optional: backfill existing local Codex rollouts
python3 ~/.codex/hooks/sync_codex.py --all

This covers local Codex tasks in ChatGPT desktop, Codex CLI, and the IDE extension. Ordinary ChatGPT web conversations are not local Codex rollouts.

API Reference

Authentication: All API endpoints (except / and /api/healthz) require the X-API-Key header. Do NOT use Bearer authentication - use the header directly: X-API-Key: your-key

Method Endpoint Description
POST /api/sessions Upload transcript
GET /api/sessions List sessions (?machine_id=xxx&provider=codex&limit=100)
GET /api/sessions/{id}/transcript Download raw JSONL
GET /api/healthz Health check (no auth required)

Upload Request

POST /api/sessions
Content-Type: multipart/form-data
X-API-Key: your-api-key

machine_id: laptop-home
session_id: abc123-def456
provider: codex (optional; defaults to claude)
transcript: (file)

Response

{
  "status": "ok",
  "session_id": "abc123-def456",
  "machine_id": "laptop-home",
  "provider": "codex",
  "file_size": 12345
}