V2 is the recommended architecture: one persistent per-user Agent on each monitored computer and one Node.js 24.15+ Server providing both the Dashboard and versioned API. Devices operate concurrently; there is no lease and no invented token quota. The Agent counts newly observed token_count.lastUsage events from installation onward and delivers a privacy-allowlisted numeric event through a crash-safe SQLite outbox.
Status: the V2 MVP implementation, migrations, Dashboard, installers, Docker deployment, and release automation are present. Before a production rollout, run the two-real-machine validation in your own Codex environment.
cp compose.v2.example.yml compose.yml
mkdir -p releases
# Download manifest.json, SHA256SUMS, and all three native Agent artifacts
# from one v2-agent-* GitHub Release into ./releases/, then verify them:
(cd releases && sha256sum --check SHA256SUMS)
export CODEX_METER_ADMIN_PASSWORD='replace-with-a-long-random-password'
export CODEX_METER_SERVER_URL='https://meter.example.com'
export CODEX_METER_TRUSTED_PROXIES='127.0.0.1,::1' # exact reverse-proxy backend source IP(s)
docker compose up -d --build
curl -fsS http://127.0.0.1:3000/api/v1/health
curl -fsS http://127.0.0.1:3000/api/v1/agent/releases/manifest.jsonThe Compose example sets CODEX_METER_RELEASE_DIR=/releases and mounts host ./releases at /releases:ro; Dashboard one-line installers depend on those release assets. The complete first-release tagging, asset download, checksum, and endpoint-verification procedure is in V2 Server deployment.
Put an HTTPS reverse proxy in front of the loopback-bound port. Preserve Host, set exactly X-Forwarded-Proto: https, and list the proxy's exact backend source IP in CODEX_METER_TRUSTED_PROXIES (Docker bridge/NAT deployments may not appear as loopback). Plaintext enrollment and Agent sync are rejected with HTTP 426. The single service stores its WAL-mode SQLite database at /data/meter.db; back up that file using a SQLite-safe backup or a stopped-container copy. Open the Dashboard, create Groups, choose Add Device, and run the displayed one-line installer. Released Agents for Linux x64, Windows x64, and macOS arm64 are self-contained and require no global Node.js or npm on monitored computers.
- Account quota reporting is read-only, optional, and may be stale or unavailable. It is never estimated from token counts.
- Group percentage is the share of locally measured token usage, not exact OpenAI quota attribution or billing.
- Never upload Codex rollout JSONL or
auth.json, including in support requests. - Recognized inherited fork/subagent/revert history is skipped. Ambiguous inherited files are baselined, so they undercount safely rather than risk double-counting.
- SQLite is a single-service MVP. Do not run multiple Server replicas against
/data/meter.db; no Redis, PostgreSQL, or queue is required.
V2 documentation: architecture, installation, deployment, validation, privacy, and troubleshooting.
Everything below describes V1. V1 remains tested for compatibility but is legacy: it requires launching Codex through a wrapper, assumes exactly three users, and uses leases/operator-defined quota behavior that V2 deliberately does not use.
Codex Meter is a small, dependency-free quota and usage meter for exactly three people who each run and authenticate their own local OpenAI Codex CLI.
It is not an OpenAI plugin, OAuth proxy, or official billing tool. It is a cooperative local wrapper plus a central Node.js server.
ํ๊ตญ์ด ์์ฝ: ์ธ ์ฌ๋์ด ๊ฐ์ ๋ณธ์ธ์ Codex CLI์ ๊ณ์ ์ ๊ทธ๋๋ก ์ฌ์ฉํ๋ฉด์, ์ค์ ์๋ฒ์์ ์ฌ์ฉ๋์ ์ง๊ณํ๊ณ ํ์ํ ๋๋ง ๊ฐ์ ํ๋๋ฅผ ์ ์ฉํ๋ ๋๊ตฌ์ ๋๋ค. ํ๋กฌํํธยท์๋ตยท์์ค ์ฝ๋ยทCodex ์ธ์ฆ์ ๋ณด๋ ์๋ฒ๋ก ๋ณด๋ด์ง ์๊ณ ์ซ์ ํ ํฐ ์นด์ดํฐ 5๊ฐ๋ง ์ ์กํฉ๋๋ค.
Codex Meter๋ ์ธ ๋ช ์ด ๊ฐ์ ๊ฐ์ธ ์ปดํจํฐ์ ํฐ๋ฏธ๋์์ Codex CLI๋ฅผ ์คํํ๋, ์ฌ์ฉ๋์ ์ค์์์ ์ฌ์ฉ์๋ณ๋ก ์ง๊ณํ๊ณ ์ ํ์ ์ผ๋ก ๊ฐ์ ์ฟผํฐ๋ฅผ ์ ์ฉํ๊ธฐ ์ํ ๋๊ตฌ์ ๋๋ค.
์ค์ ๊ตฌ์ฑ์ ๋ค์ ๋ ๋ถ๋ถ์ผ๋ก ๋๋ฉ๋๋ค.
- ๊ฐ์ธ ์ปดํจํฐ์ ๋ํผ๊ฐ Codex CLI๋ฅผ ๋์ ์คํํฉ๋๋ค.
- ์ค์ Meter ์๋ฒ๊ฐ ์ธ ์ฌ์ฉ์์ ์ฌ์ฉ๋, ์ฟผํฐ, ์คํ ์ํ๋ฅผ ๊ด๋ฆฌํฉ๋๋ค.
์ฌ์ฉ์ A/B/C์ ๊ฐ์ธ ํฐ๋ฏธ๋
โโ Codex Meter ๋ํผ
โโ ์ค์ ์๋ฒ์ ์คํ ํ๊ฐ ์์ฒญ
โโ ๋ณธ์ธ ์ปดํจํฐ์ Codex CLI ์คํ
โโ ๋ก์ปฌ ์ธ์
์์ token_count๋ง ์ง๊ณ
โโ ์ธ์
์์ ์ถ์ถํ ์ซ์ ์ฌ์ฉ๋ 5๊ฐ๋ง ์ค์ ์๋ฒ์ ๋ณด๊ณ
์ค์ Meter ์๋ฒ
โโ ์ฌ์ฉ์๋ณ ๋์ ์ฌ์ฉ๋
โโ ๊ด์ฐฐ ๋ชจ๋ ๋๋ ์ธ ๋ช
์๊ฒ ๋์ผํ ์ฟผํฐ ์ ์ฉ
โโ ์ฌ์ฉ์๋น ๋์ ์คํ 1๊ฐ ์ ํ
โโ ๊ฐ์ ๋ชจ๋์ ์ฟผํฐ ์ด๊ณผยท๋ชจ๋ ๋ชจ๋์ ๋นํ์ฑ ์ฌ์ฉ์ ์ฐจ๋จ
โโ ์ฌ์ฉ์์ฉ ์กฐํ API์ ๊ด๋ฆฌ์ ๋์๋ณด๋
์ด ํ๋ก์ ํธ๋ OpenAI ๊ณต์ ํ๋ฌ๊ทธ์ธ์ด๋ ๊ณต์ ์ฌ์ฉ๋ยท๊ณผ๊ธ ๋๊ตฌ๊ฐ ์๋๋๋ค. Codex ์์์ ์คํ๋๋ ํ๋ ฅํ ๋ก์ปฌ ๋ํผ์ ๋ณ๋์ ์ค์ ๊ด๋ฆฌ ์๋ฒ์ ๋๋ค.
์ฌ์ฉ์๊ฐ ํ ํฐ ์์น๋ ์ฌ์ฉ ๋ด์ญ์ ์ง์ ์
๋ ฅํ ํ์๋ ์์ต๋๋ค. ์ฌ์ฉ์์๊ฒ ํ์ํ ์๋ ์ค์ ์ ์ต์ด ํ ๋ฒ client.json์ ์ค์ ์๋ฒ ์ฃผ์์ ๋ณธ์ธ์ Meter ํ ํฐ์ ์ ์ฅํ๋ ๊ฒ๋ฟ์
๋๋ค.
์ดํ Codex Meter ๋ํผ๋ฅผ ์คํํ ๋๋ง๋ค ๋ค์ ๊ณผ์ ์ด ์๋์ผ๋ก ์งํ๋ฉ๋๋ค.
- ์ค์ ์๋ฒ์ ์คํ ๊ฐ๋ฅ ์ฌ๋ถ๋ฅผ ํ์ธํ๊ณ ์ฌ์ฉ์ lease๋ฅผ ๋ฐ์ต๋๋ค.
- ๋ํผ๊ฐ ๊ฐ์ ํฐ๋ฏธ๋์์ ์ฌ์ฉ์์ ๋ก์ปฌ Codex CLI๋ฅผ ์คํํฉ๋๋ค.
- ๊ธฐ๋ณธ 5์ด ๊ฐ๊ฒฉ์ผ๋ก ๋ก์ปฌ Codex ์ธ์ JSONL์ ๋ค์ ํ์ธํฉ๋๋ค.
- ์๋ก ๋ฐ์ํ
token_count์ ๋ค์ฏ ์นด์ดํฐ ์ฐจ์ด๋ฅผ ์๋ ๊ณ์ฐํด ์ค์ ์๋ฒ์ ์ ์กํฉ๋๋ค. - ๊ฐ์ ๋ชจ๋์์๋ ์ฟผํฐ ์ด๊ณผ ์๋ต์ ๋ฐ์ผ๋ฉด ์คํ ์ค์ธ Codex๋ฅผ ์ค์งํฉ๋๋ค. ๊ด์ฐฐ ๋ชจ๋์์๋ ์ฌ์ฉ๋๋ง ๊ธฐ๋กํฉ๋๋ค.
- Codex๊ฐ ๋๋๋ฉด ์ต์ข ์ฌ์ฉ๋๊ณผ ์คํ ์ข ๋ฃ๋ฅผ ์๋ ๋ณด๊ณ ํฉ๋๋ค.
- ์ผ์์ ์ธ ์ ์ก ์คํจ๋ ๋ก์ปฌ spool์ ๋ณด๊ดํ๋ค๊ฐ ๋ค์ ์คํ ๋ ์๋ ์ฌ์ ์กํฉ๋๋ค.
๋ฐ๋ผ์ /v1/usage ํธ์ถ์ด๋ ๊ด๋ฆฌ์ ๋์๋ณด๋๋ ์ฌ์ฉ๋์ ์
๋ ฅํ๋ ๊ธฐ๋ฅ์ด ์๋๋ผ ์ด๋ฏธ ์๋ ์์ง๋ ๊ฒฐ๊ณผ๋ฅผ ์กฐํํ๋ ๊ธฐ๋ฅ์
๋๋ค. ๋ค๋ง ๊ณ๋์ด ์ ์ฉ๋๋ ค๋ฉด ์ฌ์ฉ์๊ฐ ์๋ณธ codex ๋์ Codex Meter ๋ํผ๋ฅผ ํตํด ์คํํด์ผ ํฉ๋๋ค.
๊ฐ ์ฌ์ฉ์๋ ์์ ์ ์ปดํจํฐ์ Codex CLI๋ฅผ ์ค์นํ๊ณ ์ง์ ์ธ์ฆํฉ๋๋ค. Codex Meter๋ Codex OAuth ํ ํฐ๊ณผ auth.json์ ์ฝ๊ฑฐ๋ ๋ณต์ฌํ๊ฑฐ๋ ์๋ฒ๋ก ๋ณด๋ด์ง ์์ต๋๋ค.
๋ํผ๋ ๋ก์ปฌ Codex ์ธ์
JSONL์ ํ ์ค์ฉ ์ฝ๊ณ ๊ฐ ๋ ์ฝ๋๋ฅผ ํ์ฑํด token_count ์ด๋ฒคํธ์ธ์ง ํ์ธํฉ๋๋ค. ํ๋กฌํํธยท์๋ตยท๋๊ตฌ ์คํยท์์ค ๋ด์ฉ ๋ฑ token_count๊ฐ ์๋ ๋ ์ฝ๋๋ ๋ก์ปฌ์์ ์ฆ์ ๋ฒ๋ฆฌ๋ฉฐ, ์ ์ฅํ๊ฑฐ๋ ์ค์ ์๋ฒ๋ก ์ ์กํ์ง ์์ต๋๋ค. ์ธ์
์์ ์ถ์ถํด ์ ์กํ๋ ์ฌ์ฉ๋ ์ ๋ณด๋ ๋ค์ ์ซ์ 5๊ฐ๋ฟ์
๋๋ค.
input_tokenscached_input_tokensoutput_tokensreasoning_output_tokenstotal_tokens
Meter ์๋ฒ์ฉ ์ฌ์ฉ์ ํ ํฐ์ Codex ์ธ์ฆ์ ๋ณด์ ์์ ํ ๋ณ๊ฐ์ ๋๋ค. ์๋ฒ ์ํ์๋ ํ๋ฌธ ํ ํฐ ๋์ SHA-256 ํด์๋ง ์ ์ฅ๋ฉ๋๋ค.
- ์ค์ ์๋ฒ์ ๊ฐ ์ฌ์ฉ์ ์ปดํจํฐ์ Node.js 22 ์ด์
- ๊ฐ ์ฌ์ฉ์ ์ปดํจํฐ์ ๊ณต์ Codex CLI ์ค์น ๋ฐ ๋ก์ปฌ ์ธ์ฆ
- ์๊ฒฉ ์ฐ๊ฒฐ ์ HTTPS ๋ฆฌ๋ฒ์ค ํ๋ก์, VPN ๋๋ SSH ํฐ๋
- ์ ํํ ์ธ ๊ฐ์ ๊ณ ์ ํ Meter ์ฌ์ฉ์ ID
์ธ๋ถ ๋ฐํ์ ํจํค์ง๊ฐ ์์ผ๋ฏ๋ก npm install์ ํ์ํ์ง ์์ต๋๋ค.
git clone https://github.com/SANGDNOG/codex-meter.git
cd codex-meter
node --version
npm test์ธ ๋ช ์ ์ฌ์ฉ๋์ ๋๋ต ๋น๊ตํ๋ ๋ชฉ์ ์ด๋ผ๋ฉด ๊ด์ฐฐ ์ ์ฉ ๋ชจ๋๋ก ์ต์ด ํ ๋ฒ ์ด๊ธฐํํฉ๋๋ค. ์ด ๋ชจ๋๋ ํ ํฐ์ ๊ธฐ๋กํ์ง๋ง ์์์ ํ ํฐ ํ๋๋ก Codex๋ฅผ ์ค์งํ์ง ์์ต๋๋ค.
export CODEX_METER_STATE="$HOME/.codex-meter-server/state.json"
umask 077
node bin/admin.js init \
--users=alice,bob,carol \
--observe-only \
--reset-ms=2592000000 \
--max-leases=1 \
--lease-ttl-ms=120000 \
> meter-tokens-once.json์์๋ 30์ผ๋ง๋ค ์ธก์ ์นด์ดํฐ๋ง ์ด๊ธฐํํฉ๋๋ค. ๊ฐ์ ํ ํฐ ์ ์ฑ
์ด ํ์ํ ๋๋ง --observe-only ๋์ --quota=์ํ๋_์์_์ ์๋ฅผ ์ฌ์ฉํ์ธ์. ์ด ๊ฐ์ OpenAI ํ๋ ํ๋๊ฐ ์๋๋ผ ์ด์์๊ฐ ์ ํ๋ ๋ก์ปฌ ์ ์ฑ
์
๋๋ค.
meter-tokens-once.json์๋ ๊ด๋ฆฌ์ ํ ํฐ 1๊ฐ์ ์ฌ์ฉ์ ํ ํฐ 3๊ฐ๊ฐ ์ต์ด ํ ๋ฒ๋ง ํ๋ฌธ์ผ๋ก ์ถ๋ ฅ๋ฉ๋๋ค. ๊ฐ ์ฌ์ฉ์์๊ฒ ๋ณธ์ธ์ ํ ํฐ๋ง ์์ ํ ๋ฐฉ๋ฒ์ผ๋ก ์ ๋ฌํ ๋ค ํ์ผ์ ์์ ํ๊ฒ ์ญ์ ํ์ธ์. ํ ํฐ์ ์์ด๋ฒ๋ฆฌ๋ฉด ํด์์์ ๋ณต๊ตฌํ ์ ์์ผ๋ฏ๋ก ์ ์ํ๋ฅผ ์ด๊ธฐํํด์ผ ํฉ๋๋ค.
์๋ฒ๋ฅผ ๋ก์ปฌ ์ฃผ์์์ ์คํํฉ๋๋ค.
CODEX_METER_HOST=127.0.0.1 \
CODEX_METER_PORT=8787 \
node bin/server.js์ํ ํ์ธ:
curl http://127.0.0.1:8787/healthํ๋ฌธ HTTP ์๋ฒ๋ฅผ ์ ๋ขฐํ ์ ์๋ ๋คํธ์ํฌ์ ์ง์ ๊ณต๊ฐํ์ง ๋ง์ธ์. ์๊ฒฉ ์ฌ์ฉ์๋ HTTPS, VPN ๋๋ SSH ํฐ๋์ ํตํด ์ ์ํด์ผ ํฉ๋๋ค.
๊ฐ ์ฌ์ฉ์ ์ปดํจํฐ์์ ์ ์ฅ์๋ฅผ ๋ฐ๊ณ ๊ฐ์ธ ์ค์ ํ์ผ์ ๋ง๋ญ๋๋ค.
git clone https://github.com/SANGDNOG/codex-meter.git
cd codex-meter
mkdir -p "$HOME/.codex-meter"
chmod 700 "$HOME/.codex-meter"
cat > "$HOME/.codex-meter/client.json" <<'JSON'
{
"serverUrl": "https://meter.example.internal/",
"meterToken": "๋ณธ์ธ์๊ฒ_๋ฐ๊ธ๋_METER_TOKEN",
"pollIntervalMs": 5000
}
JSON
chmod 600 "$HOME/.codex-meter/client.json"
chmod +x clients/unix/codex-meter
mkdir -p "$HOME/.local/bin"
ln -s "$(pwd)/clients/unix/codex-meter" "$HOME/.local/bin/codex-meter"์ด์ ์๋ codex๋ฅผ ์คํํ๋ ์๋ฆฌ์ ๋ํผ๋ฅผ ์ฌ์ฉํฉ๋๋ค.
/path/to/codex-meter/clients/unix/codex-meter
/path/to/codex-meter/clients/unix/codex-meter --model MODEL_NAME "์์
๋ด์ฉ"
codex-metergit clone https://github.com/SANGDNOG/codex-meter.git
cd codex-meter
New-Item -ItemType Directory -Force "$HOME\.codex-meter" | Out-Null
@'
{
"serverUrl": "https://meter.example.internal/",
"meterToken": "๋ณธ์ธ์๊ฒ_๋ฐ๊ธ๋_METER_TOKEN",
"pollIntervalMs": 5000
}
'@ | Set-Content -Encoding utf8 "$HOME\.codex-meter\client.json"PowerShell ๋ํผ๋ก Codex๋ฅผ ์คํํฉ๋๋ค.
powershell -NoProfile -File .\clients\windows\codex-meter.ps1
powershell -NoProfile -File .\clients\windows\codex-meter.ps1 --model MODEL_NAME "์์
๋ด์ฉ"PowerShell ๋ํผ์ Node.js ๋ํผ๋ ์ธ์๋ฅผ ๋ฐฐ์ด๋ก ์ ๋ฌํ๋ฉฐ cmd.exe๋ฅผ ํธ์ถํ์ง ์์ต๋๋ค. ๊ธฐ๋ณธ์ ์ผ๋ก ๋ค์ดํฐ๋ธ codex.exe๋ฅผ ์ฐ์ ์ฌ์ฉํฉ๋๋ค. ํ์ค npm ์ค์น์ codex.cmd๋ง ์์ผ๋ฉด ์ธ์ ํ ๊ณต์ @openai/codex/bin/codex.js๋ฅผ Node.js๋ก ์ง์ ์คํํฉ๋๋ค. ์ค์น ์์น๊ฐ ํน์ํ๋ฉด CODEX_METER_CODEX์ .cmd๊ฐ ์๋ ๋ค์ดํฐ๋ธ codex.exe ์ ์ฒด ๊ฒฝ๋ก๋ฅผ ์ง์ ํ์ธ์.
๋ธ๋ผ์ฐ์ ์์ ์๋ฒ ๋ฃจํธ ์ฃผ์๋ฅผ ์ด๊ณ ์์ ์ Meter ํ ํฐ์ ์ ๋ ฅํ๋ฉด ๋ณธ์ธ ์ฌ์ฉ๋๋ง ์กฐํํ ์ ์์ต๋๋ค. ์ด ํ ํฐ์ OpenAI API ํค๊ฐ ์๋๋ผ ์๋ฒ ์ด๊ธฐํ ๋ ๋ณ๋๋ก ๋ฐ๊ธ๋๋ Meter ์ ์ฉ ์๊ฒฉ ์ฆ๋ช ์ด๋ฉฐ, ์น ํ๋ฉด์ ํ ํฐ์ URLยท์ฟ ํคยท๋ธ๋ผ์ฐ์ ์ ์ฅ์์ ๋ณด๊ดํ์ง ์์ต๋๋ค.
curl --oauth2-bearer USER_METER_TOKEN \
https://meter.example.internal/v1/usage๊ด๋ฆฌ์๋ ๊ฐ์ ๋ฃจํธ ํ๋ฉด์ ๊ด๋ฆฌ์ ๋ก๊ทธ์ธ์ Admin Meter ํ ํฐ์ ์ ๋ ฅํด ์ ์ฒด ๋์๋ณด๋๋ฅผ ์กฐํํ๊ฑฐ๋, ์ธ์ฆ๋ JSON API๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
curl --oauth2-bearer ADMIN_METER_TOKEN \
https://meter.example.internal/admin.json์ฌ์ฉ์๋ฅผ ๋นํ์ฑํํ๊ฑฐ๋ ๋ค์ ํ์ฑํํ ๋๋ ๊ฒฝ์ ์ํ๋ฅผ ํผํ๊ธฐ ์ํด ๋จผ์ ์๋ฒ๋ฅผ ์ค์งํ ๋ค ์คํํฉ๋๋ค.
export CODEX_METER_STATE="$HOME/.codex-meter-server/state.json"
node bin/admin.js set-enabled alice false
node bin/admin.js set-enabled alice true- ์คํ ์ ์ค์ ์๋ฒ์ ์ฐ๊ฒฐํ ์ ์์ผ๋ฉด Codex ์คํ์ ์์ํ์ง ์์ต๋๋ค.
- ์ ์์ ์ผ๋ก ์์ํ ๋ค ์ผ์์ ์ธ ๋คํธ์ํฌ ์ฅ์ ๊ฐ ๋ฐ์ํ๋ฉด ๋ก์ปฌ Codex๋ ๊ณ์ ์คํ๋ฉ๋๋ค.
- ์ ์กํ์ง ๋ชปํ ์ซ์ ์ฌ์ฉ๋์ ๊ฐ์ธ ์ปดํจํฐ์ ๋น๊ณต๊ฐ spool ํ์ผ์ ์ ์ฅํ๋ค๊ฐ ๋ค์ ์คํ ๋ ๋ค์ ์ ์กํฉ๋๋ค.
- ์ธ์ฆ ์คํจ๋ ์๋ชป๋ ์์ฒญ ๊ฐ์ ์๊ตฌ์ ์ธ HTTP 4xx ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด ๋ํผ๊ฐ Codex ์คํ์ ์ค์งํฉ๋๋ค.
- ์จ๋ผ์ธ ์ํ์์๋ ํ์ผ ํ์ธ ์ฃผ๊ธฐ๋งํผ ์ฟผํฐ๋ฅผ ์กฐ๊ธ ์ด๊ณผํ ์ ์์ต๋๋ค.
- ์๋ฒ๋ ๋คํธ์ํฌ๊ฐ ๋๊ธด ๋์์๋ ์ค์ ์ฐจ๋จ์ ์ ์ฉํ ์ ์์ด ์ด๊ณผ๋์ด ์ปค์ง ์ ์์ต๋๋ค.
- ์ฌ์ฉ์๊ฐ ์๋ณธ
codex๋ฅผ ์ง์ ์คํํ๊ฑฐ๋ ๋ก์ปฌ ํ๋ก๊ทธ๋จ์ ์์ ํ๋ฉด ๊ณ๋์ ์ฐํํ ์ ์์ต๋๋ค. ๋ฐ๋ผ์ ์ด ๋๊ตฌ๋ ์ธ ์ฌ์ฉ์๊ฐ ๋ํผ ์ฌ์ฉ์ ๋์ํ๋ ํ๋ ฅํ ๊ณ๋ ๋ฐฉ์์ ๋๋ค. - Codex ์ธ์ ์ญ์ ยท์์, ๊ฐ์์ค๋ฌ์ด ์ ์ ์ฐจ๋จ, ํฅํ ์ธ์ ํ์ ๋ณ๊ฒฝ์ ์ธก์ ์ ํ๋๋ฅผ ๋ฎ์ถ ์ ์์ต๋๋ค.
- ํ์๋๋ ์ฌ์ฉ๋์ OpenAI์ ๊ณต์ Pro/Codex ์์ฌ๋์ด๋ ์ฒญ๊ตฌ ์ฌ์ฉ๋์ด ์๋๋๋ค.
Each user's computer Central meter server
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ own Codex CLI + own OAuth โ โ three meter identities โ
โ โ โ โ equal shared policy โ
โ Codex Meter wrapper โโโ 5 countersโโ quota + active lease โ
โ reads local token_count onlyโโ allow/stop โโ usage API + admin view โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- Requires exactly three unique meter users.
- Supports observe-only measurement or one configurable quota applied equally to all three users; both modes use the same reset period.
- Allows one active wrapper per user.
- Denies a new run when the user is disabled or already active; enforcement mode also denies users who are out of quota.
- In enforcement mode, stops a connected run after its measured usage crosses the quota.
- Expires stale leases after a crashed or disconnected client.
- Spools numeric updates during transient network failures and replays them idempotently.
- Gives each user an authenticated own-usage endpoint and gives the administrator aggregate JSON/HTML views.
- Uses only Node.js 22 built-ins; no
npm installis required.
Codex Meter locally streams Codex session JSONL line by line only to find token_count records. It discards every other record and never retains or transmits prompts, responses, tool calls, source content, or other session content.
The only usage values accepted by the server are these five nonnegative safe-integer counters:
input_tokenscached_input_tokensoutput_tokensreasoning_output_tokenstotal_tokens
Codex Meter does not read, copy, proxy, distribute, or store Codex OAuth credentials or auth.json. Each person installs and authenticates Codex locally. Separate random meter tokens authorize only this meter, and the server stores their SHA-256 hashes rather than plaintext tokens.
- Linux, macOS, or Windows with Node.js 22+
- A trusted HTTPS reverse proxy, VPN, or SSH tunnel if clients connect remotely
- Node.js 22+
- Official Codex CLI installed and authenticated locally
- A distinct meter token issued by the server administrator
Clone the repository on the server:
git clone https://github.com/SANGDNOG/codex-meter.git
cd codex-meter
node --version
npm testFor rough relative measurement, initialize the state once in observe-only mode. This records tokens but never stops Codex at an arbitrary token threshold. Exactly three unique user IDs are mandatory:
export CODEX_METER_STATE="$HOME/.codex-meter-server/state.json"
umask 077
node bin/admin.js init \
--users=alice,bob,carol \
--observe-only \
--reset-ms=2592000000 \
--max-leases=1 \
--lease-ttl-ms=120000 \
> meter-tokens-once.jsonThe example resets only the measurement counters every 30 days. If you intentionally want local enforcement, replace --observe-only with --quota=YOUR_POSITIVE_INTEGER. That value is an operator-defined local policy, not an OpenAI plan limit.
meter-tokens-once.json contains the admin token and three user tokens in plaintext only once. Give each person only their own token through a secure channel, then securely remove the file. If you lose the tokens, initialize a new state instead of trying to recover them from the hash-only state file.
Start the server on localhost:
CODEX_METER_HOST=127.0.0.1 \
CODEX_METER_PORT=8787 \
node bin/server.jsHealth check:
curl http://127.0.0.1:8787/healthDo not expose this plain HTTP listener directly to an untrusted network. Use HTTPS through a trusted reverse proxy or access it only through a VPN/SSH tunnel. Bearer tokens are credentials and plain HTTP exposes them in transit.
Clone or copy this repository to each user's computer. Never place Codex auth.json inside this project.
Each user creates a private client.json containing the reachable server URL and only that user's meter token.
git clone https://github.com/SANGDNOG/codex-meter.git
cd codex-meter
mkdir -p "$HOME/.codex-meter"
chmod 700 "$HOME/.codex-meter"
cat > "$HOME/.codex-meter/client.json" <<'JSON'
{
"serverUrl": "https://meter.example.internal/",
"meterToken": "PASTE_ONLY_THIS_USERS_METER_TOKEN",
"pollIntervalMs": 5000
}
JSON
chmod 600 "$HOME/.codex-meter/client.json"
chmod +x clients/unix/codex-meter
mkdir -p "$HOME/.local/bin"
ln -s "$(pwd)/clients/unix/codex-meter" "$HOME/.local/bin/codex-meter"Use the wrapper anywhere you would normally use codex:
/path/to/codex-meter/clients/unix/codex-meter
/path/to/codex-meter/clients/unix/codex-meter --model MODEL_NAME "your prompt"
codex-meterOptional shell alias:
alias codex-meter="/path/to/codex-meter/clients/unix/codex-meter"git clone https://github.com/SANGDNOG/codex-meter.git
cd codex-meter
New-Item -ItemType Directory -Force "$HOME\.codex-meter" | Out-Null
@'
{
"serverUrl": "https://meter.example.internal/",
"meterToken": "PASTE_ONLY_THIS_USERS_METER_TOKEN",
"pollIntervalMs": 5000
}
'@ | Set-Content -Encoding utf8 "$HOME\.codex-meter\client.json"Run Codex through the PowerShell wrapper:
powershell -NoProfile -File .\clients\windows\codex-meter.ps1
powershell -NoProfile -File .\clients\windows\codex-meter.ps1 --model MODEL_NAME "your prompt"The wrapper forwards arguments as an array and never invokes cmd.exe. It prefers a native codex.exe; for the standard npm codex.cmd shim, it executes the adjacent official @openai/codex/bin/codex.js with Node. For a nonstandard installation, set CODEX_METER_CODEX to the full native codex.exe path, not a .cmd file.
| Variable | Default | Purpose |
|---|---|---|
CODEX_METER_STATE |
~/.codex-meter-server/state.json |
Server state file |
CODEX_METER_HOST |
127.0.0.1 |
Listen address |
CODEX_METER_PORT |
8787 |
Listen port |
| Setting / variable | Default | Purpose |
|---|---|---|
serverUrl |
required | Reachable meter server base URL |
meterToken |
required | This user's separate meter credential |
pollIntervalMs |
5000 |
Polling interval, from 1000 to 60000 ms |
CODEX_METER_HOME |
~/.codex-meter |
Client config, lock, and spool directory |
CODEX_HOME |
~/.codex |
Local Codex home; sessions are read below sessions/ |
CODEX_METER_CODEX |
auto-detected | Explicit Codex executable path |
A user can open the server root URL and enter their Meter token to see only their own usage. This is a meter-specific credential issued during server initialization, not an OpenAI API key. The page does not store it in a URL, cookie, local storage, or session storage. The authenticated JSON API remains available:
curl --oauth2-bearer USER_METER_TOKEN \
https://meter.example.internal/v1/usageThe administrator enters the Admin Meter token in the root page to open the aggregate dashboard, or reads the authenticated JSON API:
curl --oauth2-bearer ADMIN_METER_TOKEN \
https://meter.example.internal/admin.jsonTo disable or re-enable a user, stop the server first so there are no competing state writers:
export CODEX_METER_STATE="$HOME/.codex-meter-server/state.json"
node bin/admin.js set-enabled alice false
node bin/admin.js set-enabled alice true| Code | Meaning |
|---|---|
0 or Codex code |
Normal Codex exit |
69 |
Meter unavailable at startup |
73 |
Another local wrapper holds the client lock |
74 |
Local Codex sessions could not be scanned |
75 |
Quota/disable stop or permanent HTTP 4xx meter failure |
77 |
Start denied because of quota, disable, or active lease |
78 |
Missing or invalid local configuration |
127 |
Codex executable could not be started |
- After a successful start, transient network/server failures fail open so local Codex can continue.
- Numeric absolute updates are written to a private local spool and replayed on the next run.
- Duplicate replay does not double-count because updates use absolute per-lease high-water values.
- HTTP 4xx authentication/protocol failures stop the wrapped Codex process.
- Connected quota enforcement may overshoot by roughly one polling interval.
- During an outage, central disable/quota enforcement is unavailable and overshoot can be unbounded until connectivity and replay return.
- This is cooperative metering. A user can bypass it by launching
codexdirectly or altering local software. - It is not official OpenAI/Codex quota or billing accounting and may differ from provider totals.
- One meter user may run only one wrapper at a time; overlapping scans could double-count one user's session directory.
- Local session deletion/truncation, abrupt power loss, filesystem failure, multiple independent client homes, or future Codex session-format changes can reduce accuracy.
SIGKILLor sudden power loss can happen before the final local scan. Stale leases prevent permanent lockout but cannot recover events that were never observed.
npm testThe deterministic node:test suite covers local parser filtering, strict request schemas, delta calculation, idempotent updates/replay, authentication failures, exhausted starts, quota crossing, stale leases, hash-only token storage, local locking, literal shell-free arguments, and the real wrapper/server stop path with a fake Codex process.
bin/ server, admin CLI, and Codex wrapper entry points
clients/unix/ macOS/Linux launcher
clients/windows/ PowerShell launcher
lib/ server, store, client, command, and usage modules
test/ deterministic Node.js tests and fixtures
MIT License. See LICENSE.
This is an independent community project and is not affiliated with, endorsed by, or supported by OpenAI. โOpenAIโ and โCodexโ are trademarks of their respective owners.