Local Usage turns the Codex session metadata stored on your computer into a fast, privacy-conscious usage dashboard. It can run entirely on one machine, or several machines can send signed, minimized usage snapshots to an optional central dashboard hosted with OpenAI Sites or on your own server.
Stable release 1.5.5 · Docker image 1.5.5 · AGPL-3.0-or-later · Changelog · CI status
Setup guides: deploy the central dashboard with OpenAI Sites · deploy the public Mesh ingress · install a reporting agent
If you like this project and want to support its ongoing development, you can buy me a coffee. Donations are entirely optional; the dashboard remains free and open source.
- API-equivalent cost using the observed Standard or Fast service tier, split between fresh input, cached input, and output;
- estimated ChatGPT Codex credits, including the observed Fast-mode multiplier for each call;
- projects, conversations, model calls, turns, duration, tokens, cache rate, and cost share;
- hourly, daily, and monthly activity charts with click-to-zoom drill-down;
- a transient Custom date range while drilling into a chart, applied to the rest of the page without overwriting the user's saved Custom range;
- current and historical weekly quota periods, including the subscription tier observed during each period;
- early or free-reset boundaries, hourly activity bars, cumulative quota consumption, and a projection to the end of the current quota period;
- filters by project, model, period, usage, and conversation name.
The interface is available in French, English, German, Spanish, Italian, Portuguese, Japanese, Russian, and Simplified Chinese. The browser language is used on first visit when supported. Explicit language choices, date preferences, and custom pricing remain in browser storage.
Choose Settings → Appearance for the original green theme or the blue, violet and amber palettes. The choice applies immediately to the dashboard and its charts, persists in this browser and synchronizes across tabs on the same origin. Local and hosted dashboards share the same themes. See UI themes to add a palette.
The server indexes sessions incrementally and persists a derived snapshot. An open page checks for newer data every 15 seconds, while Refresh forces an immediate source check.
The same dashboard supports four complementary modes:
| Mode | Start it with | Interface | Data destination |
|---|---|---|---|
| Local GUI | npm start, the launch scripts, or the dashboard Docker image |
Local web GUI on port 4317 | This machine only |
| Local GUI + reporting agent | Local GUI with MESH_HUB_URL configured |
Local and Centralized views | Signed minimized snapshots are sent to the selected hub |
| Headless reporting agent | npm run start:agent or the Docker agent target |
None; no HTTP port | Signed minimized snapshots are sent to the selected hub |
| Central hub | sites-hub/ on OpenAI Sites, or DASHBOARD_MODE=hub locally |
Aggregated web GUI and machine administration | D1 on Sites, or the self-hosted hub store |
The “agent” in this README is a collector and reporter belonging to this project. It is not an autonomous Codex AI agent: it cannot execute tasks, read arbitrary files, or receive commands from the hub.
flowchart LR
subgraph A["Windows, macOS, or Linux machine"]
LogsA["Local Codex session metadata"] --> CollectorA["Local collector"]
CollectorA --> GuiA["Optional local GUI"]
end
subgraph B["Another machine"]
LogsB["Local Codex session metadata"] --> CollectorB["Headless or GUI collector"]
end
CollectorA -->|"signed minimized snapshots"| Hub["OpenAI Sites or self-hosted hub"]
CollectorB -->|"signed minimized snapshots"| Hub
Hub --> Central["Centralized dashboard"]
Local mode is always the default. Nothing is sent to a hub unless MESH_HUB_URL is explicitly configured.
- a persistent Custom date-time range, including an unbounded Now end;
- chart click drill-down that temporarily filters the entire page to the visible range;
- current and navigable historical weekly quota periods, with observed plan tiers and correct early-reset boundaries;
- responsive hourly historical charts and cumulative current-period quota projections;
- Standard/Fast API-equivalent pricing and separate ChatGPT Codex credit calculations;
- one deterministic browser bundle shared by local Node, Docker, and Sites deployments;
- versioned
/api/capabilitiesand/api/usagecontracts across all runtimes; - an optional signed multi-machine Usage Mesh;
- a reusable collector, a headless agent entrypoint, and a dedicated Docker target;
- an authenticated OpenAI Sites hub backed by D1, with enrollment and node revocation.
- Node.js 22.13 or newer for the local dashboard/agent, or Docker;
- a local Codex installation with session files under the Codex home directory;
- Node.js 22.13 or newer when developing or testing
sites-hub/locally.
The root application has no npm runtime dependencies. Windows, macOS, and Linux are supported anywhere Node.js or Docker can access the user's Codex data directory.
Usage is stored relationally in embedded SQLite locally and in Docker, and D1 on Sites. No database server or SQL container is needed. Existing JSON caches are imported automatically without deleting the originals. See storage, migrations, backups and performance.
Clone the repository:
git clone https://github.com/capisoft-lib/codex_usage.git
cd codex_usageOn Windows, double-click start-dashboard.cmd, or run:
.\start-dashboard.cmdOn macOS or Linux:
chmod +x start-dashboard.sh
./start-dashboard.shThe equivalent direct command on every OS is:
npm startOpen http://127.0.0.1:4317. With no MESH_HUB_URL, the GUI stays local and does not start outbound reporting.
Direct mode reads only these paths under the current user's Codex directory:
~/.codex/sessions/
~/.codex/archived_sessions/
~/.codex/session_index.jsonl
It does not authenticate with OpenAI and never opens auth.json.
In dashboard Settings, choose the five-hour quota, weekly quota, or both, then open the compact window. It uses the selected data source, language and palette. The browser popup does not guarantee always-on-top behavior.
For a native always-on-top window, install the separate desktop helper with Node.js 22.13 or newer:
npm ci --prefix desktop
npm run start:desktopElectron is confined to desktop/; npm start, the existing platform launchers, Docker and the headless agent keep their dependency-free behavior. The Electron binary downloads on first launch if necessary.
The desktop helper opens the miniature immediately and respects HOST and PORT. It reuses a compatible dashboard already running at that address, or starts its own server. Closing the window keeps the tray menu available; Quit Codex Usage desktop stops only a server started by this helper. The application menu provides the same actions if the tray is unavailable.
To display quotas from an existing local server, Docker deployment or hosted Site, run the helper on your desktop computer with the dashboard URL:
npm run start:desktop -- --url https://your-dashboard.example/This mode starts no local collector or server. The miniature is bundled locally; the remote deployment does not need to provide mini.html. An accessible dashboard API works directly. For a private Mesh hub, open Configure access, create a one-time association code in the site's administration (the same operation used for an agent), and enter the supplied hub address and code. Use the public Mesh ingress address supplied by the administration, which can differ from the dashboard URL. No OpenAI login runs inside Electron.
The helper stores its own Mesh identity in its operating-system user-data directory and reconnects on subsequent launches with the same dashboard URL. It only sends signed read requests, never collects or uploads sessions, and never shares the sending agent's identity. The current Mesh protocol grants the same node permissions as an agent; it does not issue a read-only credential. Revoke the Quota desktop node in the administration to withdraw access. The existing sending agent remains responsible for fresh observations.
When the helper starts the server, the dashboard Settings button opens the native window and passes its preferences. When reusing an independently started server, that button remains a browser popup; use the desktop menu to reopen the native window. Browser and Electron preferences live in separate storage, so existing native windows do not live-sync preferences from an external browser. Reopening through Settings passes the latest selection to a helper-owned server.
The miniature polls every 15 seconds and updates countdowns every second. It displays the last observation time separately from the last successful check, labels observations older than five minutes, and keeps the last values visibly marked on connection loss. Expired balances are not reused for a new quota window. A hosted dashboard selects its centralized source automatically.
Desktop validation: after npm run build:ui, run npm test --prefix desktop in a graphical desktop session (or under Xvfb on Linux). The smoke tests use isolated profiles and empty Codex directories. Windows CI runs them alongside the existing checks; screenshots are saved in the temporary directory printed by the test. Native behavior on macOS and Linux still requires platform-specific verification.
The dashboard image runs as a non-root user with all Linux capabilities removed. Only the three required Codex sources are mounted read-only; the .codex root and auth.json are never mounted. A named volume preserves the derived snapshot and, when enabled, the reporting agent's device identity.
The public Linux AMD64/ARM64 image is:
capitaine/codex-usage-dashboard:1.5.5
The 1.0.2 release is also mirrored at ghcr.io/capisoft-lib/codex-usage-dashboard:1.0.2.
On Windows PowerShell:
$codexData = Join-Path $env:USERPROFILE ".codex"
$image = "capitaine/codex-usage-dashboard:1.5.5"
docker pull $image
docker volume create codex-usage-dashboard-storage
docker run -d `
--name codex-usage-dashboard `
--restart unless-stopped `
--init `
--read-only `
--security-opt no-new-privileges:true `
--cap-drop ALL `
--pids-limit 128 `
-p 127.0.0.1:4317:4317 `
--mount "type=bind,source=$codexData\sessions,target=/codex-data/sessions,readonly" `
--mount "type=bind,source=$codexData\archived_sessions,target=/codex-data/archived_sessions,readonly" `
--mount "type=bind,source=$codexData\session_index.jsonl,target=/codex-data/session_index.jsonl,readonly" `
--mount "type=volume,source=codex-usage-dashboard-storage,target=/app-cache" `
--tmpfs /tmp `
$imageOn macOS or Linux:
IMAGE="capitaine/codex-usage-dashboard:1.5.5"
docker pull "$IMAGE"
docker volume create codex-usage-dashboard-storage
docker run -d \
--name codex-usage-dashboard \
--restart unless-stopped \
--init \
--read-only \
--security-opt no-new-privileges:true \
--cap-drop ALL \
--pids-limit 128 \
-p 127.0.0.1:4317:4317 \
--mount "type=bind,source=$HOME/.codex/sessions,target=/codex-data/sessions,readonly" \
--mount "type=bind,source=$HOME/.codex/archived_sessions,target=/codex-data/archived_sessions,readonly" \
--mount "type=bind,source=$HOME/.codex/session_index.jsonl,target=/codex-data/session_index.jsonl,readonly" \
--mount "type=volume,source=codex-usage-dashboard-storage,target=/app-cache" \
--tmpfs /tmp \
"$IMAGE"Then open http://127.0.0.1:4317.
docker logs -f codex-usage-dashboard
docker stop codex-usage-dashboardCopy .env.example to .env, then set CODEX_DATA_PATH to the absolute path of the local .codex directory.
Windows:
Copy-Item .env.example .env
# Set CODEX_DATA_PATH=C:/Users/your-name/.codex in .env
docker compose up -d --buildmacOS or Linux:
cp .env.example .env
# Set CODEX_DATA_PATH=/home/your-name/.codex in .env
docker compose up -d --buildThe following sources must exist before Compose starts:
$CODEX_DATA_PATH/sessions/
$CODEX_DATA_PATH/archived_sessions/
$CODEX_DATA_PATH/session_index.jsonl
Compose deliberately refuses to create missing host paths. docker compose down keeps the named storage volume; avoid docker compose down -v unless you intend to delete the cached analysis and agent identity.
An agent can be added to any Windows, macOS, or Linux computer that runs Codex and can run Node.js or Docker. Each machine remains authoritative for its own logs: it analyzes locally, removes disallowed fields, signs the result with an Ed25519 key created on that machine, and pushes the minimized snapshot to the hub.
For a complete installation procedure, including Node.js and Docker operation, verification, updates, revocation, and recovery, read Install a reporting agent. Windows laptops should use the official Task Scheduler supervisor for sleep/resume recovery and automatic restart.
For a Sites hub, sign in with ChatGPT, open /admin, and select Add a machine. The page generates a ready-to-copy command containing the public ingress address and a code that expires after ten minutes and can be used once.
For a self-hosted hub, create the same code through its administration endpoint as described below.
From the checked-out project folder on the new machine, run the command copied from /admin:
npm run start:agent -- --hub-url "https://your-mesh-ingress.example" --associate "AAAA-BBBB-CCCC-DDDD-EEEE-FFFF-0000-1111"The first successful synchronization persists the non-secret ingress address and the machine's private Ed25519 identity in .cache/mesh-agent.json. Later starts need only npm run start:agent; neither the one-time code nor any shared infrastructure token remains configured.
Optional but recommended settings are:
MESH_NODE_ALIAS=Office PC
MESH_AGENT_STATE_PATH=.cache/mesh-agent.json
MESH_PROJECT_MODE=hash
MESH_INCLUDE_TITLES=false
For a private Sites hub, MESH_HUB_URL is the dedicated public Mesh ingress, not the private Site. Reporting machines never receive a Site bypass credential.
To run the local GUI and the reporting agent together:
npm startBecause the persistent association is present, the GUI exposes both Local and Centralized sources. The browser requests centralized data through its local server; the enrolled agent signs that read request and the hub returns only the current owner's aggregate.
To report without serving a GUI:
npm run start:agentThe headless process uses the same incremental collector but opens no HTTP port. The Dockerfile also exposes a dedicated target:
docker build --target agent -t codex-usage-agent .Run it with the same three read-only Codex mounts, the /app-cache volume, and the MESH_* environment variables used by the dashboard container. Keep MESH_AGENT_STATE_PATH persistent: it contains the machine's private signing key and enrollment identity. Remove MESH_ENROLLMENT_CODE after the first successful enrollment.
OpenAI Sites can build, host, refine, and share web applications from ChatGPT. Sites is currently documented as a public beta; availability and limits depend on plan, region, and workspace settings. Site management happens in ChatGPT on the web or desktop, rather than from the standalone Codex CLI or IDE extension.
You do not install this Site inside Codex. Open this repository in the desktop app and give the deployment request directly to Codex in a conversation where Sites is available. Mentioning @Sites starts that workflow explicitly. ChatGPT on the web can then manage the existing Site; a web conversation can update source only when that source is available to it. The detailed procedure is in Deploy the central dashboard with OpenAI Sites.
For a first deployment or an update, give Codex this request first:
@Sites Prepare the Sites application in sites-hub/. If the ignored
sites-hub/.openai/hosting.json exists, reuse only that linked Site. Otherwise,
create a new private Site in my account with access limited to me. Never commit
the project ID or production URL. Run the relevant tests and build, then save a
new version without deploying it. Show me the result before changing access,
secrets, or production.
After reviewing the saved version, ask:
Deploy the approved saved version. Keep the current access policy, database
binding, and runtime settings unchanged, then give me the production URL and
deployment status.
Saving and deploying are deliberately separate: every Sites deployment URL is a production URL. If immediate publication is intended, Codex can perform both operations in one request, but the two-step prompt above is the safer default.
This repository's Sites application lives in sites-hub/. It provides:
- the same generated dashboard UI as the local server and Docker image;
- ChatGPT identity for browser visitors;
- an
/adminpage for one-time enrollment codes, machine status, and revocation; - D1 storage for owners, enrollments, machines, sanitized snapshots, and quota history;
- signed endpoints for enrollment, ingestion, and owner-scoped reads;
- a separate stateless public ingress that keeps private-Site authorization server-side.
The repository contains no production project ID or private Site URL. Every user creates their own private Site, D1 database, ingress configuration, and server-side upstream secret. Do not share deployment files between users. This keeps deployments as separate data silos, while authenticated Mesh reads are also scoped to the owning Site user.
The local GUI and the Site are separate deployments, but both use the bundle generated from root public/. npm run build:ui writes a deterministic bundle and SHA-256 manifest under dist/dashboard/; a Sites build regenerates and copies that exact bundle. Do not edit generated dashboard copies.
cd sites-hub
npm install
npm test
npm run lintRun npm run db:generate only after changing the D1 schema, then review the
generated migration before deployment. The committed
sites-hub/.openai/hosting.example.json declares the DB binding shape. The
ignored hosting.json links only the local checkout to that user's private Site
and must never contain secrets.
Use the Sites workflow in ChatGPT desktop or web to open the existing project, save a version, and deploy it. The official documentation separates saving a version from deployment and notes that every deployment URL is a production URL, so verify the saved version before publishing.
In the Sites settings:
- bind the D1 database declared as
DBand apply the checked-in migrations; - configure any secret or environment value in Sites settings, not in
.openai/hosting.json; - keep access private and limited to the deploying user's account unless other trusted users are deliberately authorized;
- deploy
mesh-ingress/and store the Site bypass value only as its encryptedSITES_UPSTREAM_AUTH_TOKENserver-side secret; - configure machines with the public ingress URL and a one-time enrollment code only.
After deployment, sign in to /admin, create a one-time code, enroll each machine, and use /dashboard/index.html for the aggregate. Keep Sites and reporting agents on compatible Mesh protocol versions when deploying schema or protocol changes.
Sites is optional. A local or private-network hub can use the same root server in hub mode.
Create a long random administrator token in a non-versioned environment and start it:
$env:MESH_ADMIN_TOKEN = '<long-random-secret>'
docker compose -f compose.mesh-hub.yaml up -d --buildCreate a ten-minute enrollment code:
Invoke-RestMethod -Method Post -Uri http://127.0.0.1:4318/api/mesh/enrollments `
-Headers @{ Authorization = "Bearer $env:MESH_ADMIN_TOKEN" }Configure each machine with:
MESH_HUB_URL=http://private-hub-address:4318
MESH_ENROLLMENT_CODE=AAAA-BBBB-CCCC-DDDD-EEEE-FFFF-0000-1111
If the hub is reachable outside a trusted private network, place it behind HTTPS and protect its browser interface. Mesh requests remain protected by signatures, timestamps, monotonic sequence numbers, and node revocation.
The default transmitted snapshot contains counters, tokens, models, timestamps, duration, state, a machine name or explicit alias, and minimized project identity. A canonical GitHub URL already present in session metadata may identify the same project across machines; credentials and query parameters are removed. Otherwise, the working path is hashed by default.
The agent never sends:
auth.jsonor any OpenAI/Codex credential;- raw JSONL logs;
- prompts, responses, reasoning, tool output, or commands;
- file contents, secrets, usernames, or full working paths in the default mode.
Additional guarantees:
MESH_PROJECT_MODE=hashis the default;basenameandfullare explicit, more revealing choices;MESH_INCLUDE_TITLES=falseremoves conversation titles by default;- the hub is push-only and cannot browse the machine or request extra fields;
- revoking a node immediately blocks future uploads and signed reads;
- sequence numbers prevent replay and duplicate processing from the same node;
- quota is account-level: the hub keeps the newest observation and never adds percentages from multiple machines.
public/ is the only editable browser UI source. The local server, dashboard container, self-hosted hub, and Sites adapter all implement the same versioned contract:
GET /api/capabilitiesdescribes runtime, available sources, default source, and refresh support;GET /api/usage?source=local|centralizedreturns the public usage snapshot.
The browser maintains separate Local and Centralized caches. The local view is not replaced by enrolling a machine.
While the dashboard tab is visible, it checks for updated data every 15 seconds in both modes and refreshes immediately when you return to the tab. Failed requests retry automatically, including after a failed first load. This reads the latest available snapshot; source collection and Mesh synchronization still follow the agent's configured interval (REFRESH_INTERVAL_MS, 60 seconds by default).
| Variable | Default | Description |
|---|---|---|
HOST |
127.0.0.1 |
Interface used by the local HTTP server. |
PORT |
4317 |
Local dashboard port. |
CODEX_HOME |
$HOME/.codex |
Codex data directory. |
CODEX_SESSIONS_PATH |
$CODEX_HOME/sessions |
Explicit session directory, used by scoped Docker mode. |
CODEX_ARCHIVED_SESSIONS_PATH |
$CODEX_HOME/archived_sessions |
Explicit archived-session directory. |
CODEX_SESSION_INDEX_PATH |
$CODEX_HOME/session_index.jsonl |
Explicit conversation-title index. |
REFRESH_INTERVAL_MS |
60000 |
Source reindex interval in milliseconds, minimum 1000. |
SNAPSHOT_PATH |
.cache/usage-snapshot.json |
Legacy import source; SQLite defaults to this path plus .sqlite. Empty uses in-memory SQLite unless USAGE_DATABASE_PATH is set. |
USAGE_DATABASE_PATH |
${SNAPSHOT_PATH}.sqlite |
Optional embedded SQLite path; use a persistent Docker volume. |
MESH_HUB_PATH |
.cache/mesh-hub.json |
Legacy hub import source; SQLite defaults to this path plus .sqlite. |
MESH_DATABASE_PATH |
${MESH_HUB_PATH}.sqlite |
Optional embedded SQLite path for self-hosted hubs. |
DASHBOARD_ASSETS_PATH |
dist/dashboard |
Generated UI bundle served locally. |
DASHBOARD_MODE |
local |
local analyzes this machine; hub accepts and aggregates Mesh snapshots. |
MESH_HUB_URL |
empty | Enables the outbound reporting agent and Centralized source. |
MESH_NODE_ALIAS |
system machine name | Optional display-name override. |
MESH_ENROLLMENT_CODE |
empty | One-time code required only for initial enrollment. |
MESH_AGENT_STATE_PATH |
.cache/mesh-agent.json |
Persistent enrollment identity and private key. |
MESH_BATCH_SIZE |
25 (100 in Compose) |
Maximum minimized sessions per request. |
MESH_PROJECT_MODE |
hash |
Project privacy mode: hash, basename, or full. |
MESH_INCLUDE_TITLES |
false |
Explicitly allow sanitized conversation titles. |
Windows PowerShell example:
$env:PORT = "8080"
$env:CODEX_HOME = "D:\CodexData"
npm startmacOS/Linux example:
PORT=8080 CODEX_HOME=/path/to/.codex npm startThe dashboard derives two separate estimates from locally observed model calls. It does not require an API key or actual API billing data.
- Codex credits use the published ChatGPT Codex rate card. Each call inherits its recorded service tier, and known Fast/Priority calls receive the documented credit multiplier.
- API-equivalent cost estimates what the same calls would have cost through the API. Standard prices, published API Fast rates, and long-context adjustments are applied independently from ChatGPT credit multipliers.
- Both estimates select the rate applicable at each call's original timestamp from a shared, versioned catalog. Later price cuts do not reduce earlier consumption; late imports retain their historical rate.
- GPT-6 Astra, Sol, Luna and GPT-6.1 Sol are supported with distinct model IDs, dated rates and API long-context rules. Sol 6.1 cached input costs $0.10 or 2.5 Codex credits per million tokens; its Fast purchased-credit billing is 2x and included-subscription quota weighting is 2.5x. Earlier periods retain their recorded factors.
- Unknown models, dates outside documented coverage and unsupported tiers remain visibly unrated and are excluded from totals. A reference API price is available only in explicitly selected custom simulation.
- The pricing dialog includes historical/current/custom modes, sourced rate history, catalog verification status and an export of the applied calculation. Legacy custom browser prices are preserved for custom simulation.
See Dated pricing research and maintenance for the August 2025–September 2026 research ledger, evidence gaps, UTC day-boundary convention and update procedure. Historical Codex credit coverage begins at the documented observation on August 11, 2026; older credit rates are not invented.
Weekly quota history is reconstructed only from observed 10,080-minute rate-limit windows. Reset timestamps within five minutes are grouped. If a free or early reset starts a new quota before the previous nominal reset, the new start becomes the previous period's effective end.
Each historical period displays the plan code observed at that time, hourly activity bars, and cumulative usage through its effective end. Capacity is calibrated from rated credits and the peak observed quota percentage; it is an estimate, not an official plan allowance, and no plan-specific capacity is invented. The current-period forecast projects the observed consumption curve to the effective reset using a 24-hour EMA.
Pricing references: ChatGPT Codex plans and credits, Codex Fast multipliers, API pricing, and API Fast mode.
The displayed dollar amount is theoretical API-equivalent cost, not a bill or the subscription price. Observed cache writes are included with their dated rates. Tool fees and unobserved charges remain excluded. Analyzer v8 preserves optional cache-write counters; deploy the receiving hub before updating reporting agents.
- The local server listens on
127.0.0.1by default. - Session files are opened read-only.
- The dashboard never needs an OpenAI API credential and never reads
auth.json. - Docker mounts only the two session directories and title index, not the
.codexroot. - Browser API responses use an explicit allowlist and exclude analyzer paths, modification times, parse details, message text, and file contents.
- There is no analytics, telemetry, external font, or CDN asset.
- In local-only mode, no Codex usage data leaves the machine.
- In Mesh mode, only the documented minimized snapshot is sent to the explicitly configured hub.
Do not change HOST to 0.0.0.0 unless you intend to expose the local dashboard and its metadata to other reachable devices. Protect MESH_AGENT_STATE_PATH and administrator tokens as secrets. A reporting machine must never hold the private Site's bypass credential.
The dashboard reads:
$CODEX_HOME/sessions/
$CODEX_HOME/archived_sessions/
$CODEX_HOME/session_index.jsonl
Codex session logs are an internal format and may change. Malformed or partially written JSONL lines are ignored so an active session does not break the dashboard.
A user turn can trigger multiple model calls, especially when tools are used. Input usage may include instructions, repository context, prior messages, and tool results—not only text typed by the user.
Root application:
npm run build
npm test
npm run checkSites application:
cd sites-hub
npm test
npm run lintThe root runtime uses Node.js built-ins and browser-native HTML, CSS, and JavaScript. The Sites adapter uses React/vinext and D1.
Contributions are welcome. Read CONTRIBUTING.md before submitting changes, especially the privacy requirements for fixtures, logs, and local Codex data.
Local Usage is an independent free-software project and is not affiliated with, endorsed by, or sponsored by OpenAI. “Codex” and “OpenAI” describe compatibility; their trademarks remain the property of their respective owners. The project does not include OpenAI logo artwork.
The complete project, including its original icon and documentation, is free software licensed under GNU AGPL version 3 or any later version. Modified distributed versions remain under the same license, and a modified version used through a computer network must offer its corresponding source code to its users.
Copyright © 2026 capisoft-lib and contributors.
public/ Editable source for the shared browser interface
dist/dashboard/ Generated UI bundle for local, Docker, and Sites builds
docs/ Sites deployment and reporting-agent guides
mesh-ingress/ Stateless public gateway for signed machine traffic
scripts/ Deterministic UI build and synchronization tools
src/analyzer.mjs Read-only Codex session parser
src/usage-collector.mjs Reusable local collector and optional Mesh sender
src/public-usage.mjs Browser API privacy boundary and allowlist
src/mesh-*.mjs Signing, privacy, agent, protocol, and self-hosted storage
agent.mjs Headless reporting-agent entrypoint
server.mjs Local dashboard and self-hosted hub HTTP adapter
sites-hub/ Authenticated OpenAI Sites adapter and D1 aggregation
test/ Parser, UI, pricing, privacy, and Mesh regression tests
start-dashboard.* Direct Windows/macOS/Linux launchers
Confirm that CODEX_HOME contains sessions or archived_sessions, then restart. For Docker, also confirm that all three scoped sources exist below CODEX_DATA_PATH.
The local server only advertises Centralized mode after MESH_HUB_URL is configured and the machine has valid persistent enrollment state. Provide a fresh one-time code for initial enrollment, then keep the state file and remove the code.
Confirm that MESH_HUB_URL is the ingress origin and that /healthz returns HTTP 200. Do not give the Site bypass token to the machine. Follow the Mesh ingress guide to verify server-side configuration.
Open the pricing dialog with the $ button, choose Custom simulation, and enter that model's input, cached-input, and output prices per million tokens. Historical mode always uses the dated official catalog; custom rates do not rewrite it.
PORT=8080 npm startPowerShell:
$env:PORT = "8080"
npm startOpen Projects → Project groups (also available from Settings) to combine two or more projects under a custom name. Edit a group to rename it or change its members; Ungroup restores the original projects. Groups affect project totals, model breakdowns and conversation labels without modifying source sessions. The editor lists projects across all dates. Each project can belong to one custom group. Preferences are saved in this browser only and are not synchronized between browsers or devices.
Exact-name duplicates with a single matching GitHub repository are automatically combined by default. Expand Automatic grouping options on the grouping page to inspect the matches or disable this browser preference. Ambiguous repositories and explicit manual memberships are never combined automatically.