nessi 58eab24258 chore: initial project setup with AI usage dashboard for Codex and Devin
Add FastAPI-based monitoring dashboard with support for Codex CLI and Devin API v3 Enterprise consumption tracking. Includes Docker deployment, systemd service configuration, kiosk mode launcher, comprehensive documentation, and demo mode for testing without credentials.
2026-09-18 11:34:33 +02:00

AI Usage Dashboard

AI Usage Dashboard is a small FastAPI monitoring kiosk for the official local Codex app-server mechanism and Devin API v3 Enterprise consumption endpoints. Provider refreshes are independent: an unavailable provider never hides a successful result from the other provider.

Requirements

  • Python 3.12
  • Codex CLI installed and authenticated locally when Codex data is enabled
  • Devin API v3 Enterprise access and a service user with ViewAccountConsumption when Devin data is enabled
  • Chromium/Chrome only for the optional kiosk launcher
  • Docker and Compose are optional

Install and run directly

Unix:

python3.12 -m venv .venv
. .venv/bin/activate
python -m pip install -r requirements.txt
cp .env.example .env
python -m app.main

python -m app.main is the simplest direct command and honors HOST and PORT from .env. The explicit Uvicorn alternative is:

python -m uvicorn app.main:app --host 0.0.0.0 --port 8080

Windows PowerShell:

py -3.12 -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install -r requirements.txt
Copy-Item .env.example .env
python -m app.main

python -m app.main honors HOST and PORT from .env. The explicit Uvicorn alternative is:

python -m uvicorn app.main:app --host 0.0.0.0 --port 8080

Open http://localhost:8080. The application also runs in deterministic demo mode with DEMO_MODE=true and no provider credentials.

Configuration

Copy .env.example to .env. Unknown environment variables are ignored and names are case-insensitive.

Variable Default Meaning
HOST 0.0.0.0 Bind host when using a launcher that reads settings
PORT 8080 Application port
CODEX_ENABLED true Enable the local Codex provider
CODEX_COMMAND codex One executable path; it is never shell-split
CODEX_TIMEOUT 8 Codex request timeout in seconds
DEVIN_ENABLED true Enable Devin
DEVIN_API_KEY empty Devin Bearer token, kept as a secret value
DEVIN_API_URL https://api.devin.ai Devin API base URL
DEVIN_ORG_ID empty Organization scope, used when no user scope is configured
DEVIN_USER_ID empty User scope, takes precedence over organization
DEVIN_TIMEOUT 8 Devin HTTP timeout in seconds
PROVIDER_CACHE_TTL 8 Successful provider cache duration in seconds
REFRESH_INTERVAL 10 Browser refresh period in seconds
TIMEZONE Europe/Vienna ZoneInfo name used for reset timestamps
DEMO_MODE false Use deterministic local demo values for both cards

Provider mechanisms

Codex

The dashboard invokes the official local CLI exactly as codex app-server --listen stdio://, without a shell. It sends the official line-delimited initialize, initialized, and account/rateLimits/read messages and parses primary and secondary windows, credits, and individual limits. Install and log in to the official CLI first:

codex login

The app-server interface is official, but is documented as a development/debug interface and may change. If the executable is not on the service PATH, configure CODEX_COMMAND with its absolute path. ChatGPT plan rate limits are distinct from OpenAI API billing. CODEX_API_KEY is intentionally not used or included because an API key does not expose ChatGPT Codex plan limits.

Devin

The provider uses the official Devin API v3 Enterprise consumption endpoints. It selects the active cycle from /v3/enterprise/consumption/cycles, then requests daily consumption using user scope, organization scope, or the account endpoint in that precedence order. User and organization IDs are URL-quoted. Organization usage additionally attempts the organization max cycle ACU limit; an unavailable optional organization-details request does not discard consumption.

Devin v3 consumption is Enterprise-only and requires a service user with ViewAccountConsumption. Reading the organization max limit may require extra organization-read permissions. Self-serve has no supported machine-readable provider endpoint; use demo mode until Devin publishes an official endpoint. Do not claim real-time freshness beyond vendor behavior: the dashboard cache and refresh schedule only control when it asks the vendors again.

Security and behavior

  • The Devin Bearer token is read from environment settings. Codex uses the official CLI's local credential store instead of an API key setting. Neither credential reaches the browser, and neither is returned, logged, or included in error messages.
  • Upstream error bodies and process stderr are discarded; users see only a generic provider error.
  • Provider calls have independent timeouts and successful results use a monotonic TTL cache. Only successful results are cached.
  • The browser makes one /api/usage request immediately and repeats using the response refresh interval. A network failure keeps the previous cards visible and marks the header connection state.
  • GET /health returns exactly {"status":"ok"}.
  • GET /api/usage returns normalized Codex and Devin usage, server_time, demo_mode, and refresh_interval; it sends Cache-Control: no-store.
  • GET / serves the dashboard HTML with Cache-Control: no-store. Static assets are under /static.

Docker Compose

Create .env, then build and run:

cp .env.example .env
docker compose up --build -d
curl http://localhost:8080/health

The container uses Python 3.12 slim, a non-root user, read-only root storage, a /tmp tmpfs, no-new-privileges, and a healthcheck. docker-compose.yml uses restart: unless-stopped and maps port 8080. The image does not bundle the Codex CLI or a personal Codex credential store: Docker works out of the box for demo mode and Devin, while real Codex should normally run directly on the authenticated host. An operator may deliberately build and securely provision the official CLI and credential store, but credentials should never be baked into an image.

Kiosk mode

start-kiosk.sh accepts DASHBOARD_URL (default http://localhost:8080) and searches in this order: chromium, chromium-browser, google-chrome, google-chrome-stable. It launches the first available browser with:

--kiosk --noerrdialogs --disable-infobars --disable-session-crashed-bubble

The script exits clearly if no supported browser is installed.

systemd dashboard service

Install the project at /opt/usage-kiosk, create /opt/usage-kiosk/.env, and adjust User if necessary:

[Unit]
Description=AI Usage Dashboard
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=usagekiosk
Group=usagekiosk
WorkingDirectory=/opt/usage-kiosk
EnvironmentFile=/opt/usage-kiosk/.env
ExecStart=/opt/usage-kiosk/.venv/bin/uvicorn app.main:app --host 0.0.0.0 --port 8080
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true

[Install]
WantedBy=multi-user.target

Save as /etc/systemd/system/usage-kiosk.service, then run sudo systemctl daemon-reload, sudo systemctl enable --now usage-kiosk, and inspect sudo journalctl -u usage-kiosk if needed.

systemd browser launch

For systems where the display manager and X authority are ready before the browser unit starts, save this as /etc/systemd/system/usage-kiosk-browser.service:

[Unit]
Description=AI Usage Dashboard Chromium Kiosk
After=display-manager.service usage-kiosk.service
Requires=usage-kiosk.service

[Service]
Type=simple
User=usagekiosk
WorkingDirectory=/opt/usage-kiosk
Environment=DISPLAY=:0
Environment=XAUTHORITY=/home/usagekiosk/.Xauthority
ExecStart=/opt/usage-kiosk/start-kiosk.sh
Restart=on-failure
RestartSec=5

[Install]
WantedBy=graphical.target

Enable it with sudo systemctl daemon-reload and sudo systemctl enable --now usage-kiosk-browser. The DISPLAY, XAUTHORITY, user home, and display-manager readiness vary by distribution and graphical setup; a desktop-session autostart entry is preferable when systemd starts too early for the X session.

Kiosk X session/autostart

For a dedicated graphical user, install the launcher at /opt/usage-kiosk/start-kiosk.sh, make it executable, and use an X session autostart entry such as ~/.config/autostart/usage-kiosk.desktop:

[Desktop Entry]
Type=Application
Name=Usage Dashboard Kiosk
Exec=/opt/usage-kiosk/start-kiosk.sh
Terminal=false
X-GNOME-Autostart-enabled=true

A window-manager session may instead run /opt/usage-kiosk/start-kiosk.sh from its session startup. Configure automatic graphical login for the kiosk user only on a physically secured display. Boot ordering is: network, usage-kiosk.service, graphical session, then Chromium kiosk. Keep the dashboard service bound to the required network interfaces and use a firewall or reverse proxy when it is not on a trusted display network.

Troubleshooting

  • Codex error: verify codex is installed, codex login completed, and CODEX_COMMAND points to one executable. Run the CLI manually outside the dashboard to confirm its availability.
  • Devin error: verify Enterprise entitlement, service-user permissions, token scope, API URL, and configured user or organization scope. A 401/403/404 on optional organization metadata only removes the max-limit fields.
  • No Devin limit: the consumption endpoint remains valid, but no numeric limit is invented when the organization max limit is unavailable.
  • Connection error in the header: the browser could not complete the dashboard request; existing provider rows remain on screen and retries continue on the configured interval.
  • Stale-looking values: successful provider results are cached for PROVIDER_CACHE_TTL; vendor processing and reporting delays are outside this dashboard's control.
  • Container health failure: inspect docker compose logs dashboard, verify .env, and test http://localhost:8080/health.
  • Kiosk failure: confirm one of the four supported Chromium executable names is on PATH, then check DASHBOARD_URL and the graphical session environment.

Development checks

python -m pytest -q
python -m compileall app
docker compose config
S
Description
No description provided
Readme
64 KiB
Languages
Python 81.1%
JavaScript 13.7%
HTML 3%
Dockerfile 1.1%
Shell 1.1%