Persistent token usage service for Codex sessions
Turn one-shot Codex token reports into a small service with incremental sync, multi-device aggregation, and a live HTML dashboard.
Click the preview image to open the demo video. The demo uses fabricated sample data.
token-account converts the original static HTML script into a long-lived FastAPI service backed by SQLite.
It accepts incremental sync uploads from one or more machines, deduplicates events by event_id, stores source status, and keeps the legacy report UI available as a live dashboard page.
- Long-lived HTTP service for token usage reporting
- Incremental sync from local Codex session logs
- Idempotent event ingestion with
event_iddeduplication - Aggregated reporting across multiple devices
- Dashboard APIs plus server-rendered legacy HTML report
- Local sync state tracking for efficient repeated uploads
- API:
FastAPI,Pydantic,Uvicorn - Storage:
SQLite,sqlite3 - Sync client:
urllib.request, local JSON state - Reporting UI: server-rendered HTML, legacy report renderer
- Packaging and deployment:
Python 3.11+,Docker,Docker Compose
src/token_account/
cli.py CLI entry for serve / sync / sync-loop
service.py FastAPI routes and service wiring
syncer.py Incremental sync client
storage.py SQLite schema and ingestion
reporting.py Report aggregation and payload building
legacy_report.py Legacy HTML report renderer
src/codex_token_report.py Thin executable entrypoint
Dockerfile Container image
docker-compose.yml Compose service definition
pricing.json Optional pricing overrides
- Install dependencies.
pip install -r requirements.txt- Start the service.
python3 src/codex_token_report.py serve --host 0.0.0.0 --port 8000 --db-file data/token-account.db- Sync local Codex events once.
python3 src/codex_token_report.py sync --service-url http://127.0.0.1:8000- Keep syncing in the background.
python3 src/codex_token_report.py sync-loop --service-url http://127.0.0.1:8000 --interval 60Default URLs after the service starts:
- Report page:
http://127.0.0.1:8000/ - Dashboard API:
http://127.0.0.1:8000/api/dashboard - Report API:
http://127.0.0.1:8000/api/report - Sources API:
http://127.0.0.1:8000/api/sources - Health API:
http://127.0.0.1:8000/api/health
On Windows, you can also double-click open-report.bat to start the local service in the background and open the browser automatically.
Start the FastAPI service.
Common options:
--host: bind address, default127.0.0.1--port: bind port, default8000--db-file: SQLite file path, defaultdata/token-account.db--pricing-file: optional pricing override file
Scan local .codex/sessions data and push normalized token events to the service.
Common options:
--service-url: service base URL, defaulthttp://127.0.0.1:8000--codex-home: custom.codexroot--sessions-root: direct path to thesessionsdirectory--state-file: local sync state file--source-id: source device identifier--hostname: source host name--batch-size: upload batch size, default1000--timeout: HTTP timeout in seconds, default30
Run sync repeatedly for daemon, scheduler, or systemd usage.
Extra option:
--interval: sync interval in seconds, default60
- Events are stored individually and deduplicated by
event_id - Device metadata is preserved in the
sourcestable - Sync runs are recorded for troubleshooting and status display
- The HTML report polls fresh report data periodically from the service
The repository includes Dockerfile and docker-compose.yml.
Default container command:
python src/codex_token_report.py serve --host 0.0.0.0 --port 8000 --db-file /data/token-account.dbStart it with:
docker compose up -d --buildThe local ./data directory is mounted to /data for SQLite persistence.
Thanks to the Linux.do community for the ongoing discussion, sharing, and practical feedback around Codex workflows, self-hosted tooling, and deployment experience.
Projects like this benefit a lot from communities that openly exchange scripts, ideas, and real-world usage patterns.
The current service does not add authentication.
If you expose it to a public network, anyone who can reach the service can read report data, inspect source status, and submit sync payloads. Put it behind a reverse proxy, access control, or add an auth layer before wider exposure.