Install
openclaw skills install clawtel-setupUse when setting up clawtel to report token usage from a project that calls the Anthropic API (SDK, Claude Code, or any tapes-wrapped agent) to the claw.tech leaderboard. Covers install, env vars, tapes wiring, verification, and running as a persistent service.
openclaw skills install clawtel-setupGet clawtel reporting token usage from any project that calls Anthropic (SDK, Claude Code, openclaw, staffchief-style agents) to the claw.tech leaderboard.
clawtel is a single Go binary that reads aggregate token counts from a local tapes SQLite database and sends heartbeats to https://ingest.claw.tech/v1/heartbeat. It never reads prompt or response content — only created_at, model, prompt_tokens, completion_tokens from the nodes table.
tapes to capture model callsBefore running clawtel you need three things:
curl -fsSL https://download.tapes.dev/install | bash
tapes init
ik_... key shown once at creation.@anthropic-ai/sdk / anthropic, Claude Code, or an openclaw agent. Anything that hits api.anthropic.com.clawtel only sees usage that tapes has recorded. Point your Anthropic client at the tapes proxy so every call flows through it:
# In the shell where your agent runs
export ANTHROPIC_BASE_URL="http://localhost:8080"
tapes start # starts proxy + API, writes to ~/.tapes/tapes.sqlite
For the Anthropic Node/Python SDK, ANTHROPIC_BASE_URL (or passing baseURL to the client constructor) is enough — no code changes. For Claude Code, set it before launching the CLI.
ANTHROPIC_BASE_URL alone is not enoughIf your workload is an OpenClaw agent (clawchief, staffchief, openclaw-in-a-box), the env var is silently ignored. OpenClaw instantiates its Anthropic client with baseURL: model.baseUrl, which clobbers the SDK's normal readEnv("ANTHROPIC_BASE_URL") fallback. You'll see heartbeats sending with model="", input_tokens=0, output_tokens=0 forever, and the gateway process will hold a direct TLS connection to Anthropic's edge instead of 127.0.0.1:8080.
Fix: set the provider's base URL explicitly in ~/.openclaw/openclaw.json:
{
"models": {
"providers": {
"anthropic": {
"baseUrl": "http://localhost:8080",
"models": [
{ "id": "claude-opus-4-6", "name": "Claude Opus 4.6" },
{ "id": "claude-sonnet-4-6", "name": "Claude Sonnet 4.6" },
{ "id": "claude-haiku-4-5", "name": "Claude Haiku 4.5" }
]
}
}
}
}
Then restart the OpenClaw gateway. Verify the gateway is routing through tapes:
ss -tnp | awk -v pid="$(pgrep -f openclaw-gateway | head -1)" '$0 ~ "pid="pid'
# Expect a line with Peer Address 127.0.0.1:8080
# If instead you see Peer Address 160.79.*.* or a Cloudflare IP, tapes is still being bypassed.
Confirm tapes is capturing rows:
sqlite3 ~/.tapes/tapes.sqlite \
'SELECT count(*), max(created_at) FROM nodes;'
If the count is zero after making a call, tapes isn't in the request path — recheck ANTHROPIC_BASE_URL in the process actually running the agent (and for OpenClaw, the models.providers.anthropic.baseUrl config above).
Pick one of the two options below. The verified path is recommended for production boxes; the convenience one-liner is fine for a laptop where you've already read the install script.
Every release tag publishes a checksums.txt alongside the OS/arch tarballs. Download the release for your platform, verify against the checksum file, then move the binary onto your PATH:
# Adjust VERSION and PLAT for your box. Releases: https://github.com/bdougie/clawtel/releases
VERSION=v0.1.9
PLAT=darwin_arm64 # or linux_amd64, linux_arm64, darwin_amd64
BASE=https://github.com/bdougie/clawtel/releases/download/${VERSION}
curl -fsSLO "${BASE}/clawtel_${PLAT}.tar.gz"
curl -fsSLO "${BASE}/checksums.txt"
# Verify. sha256sum on Linux, shasum -a 256 on macOS.
grep "clawtel_${PLAT}.tar.gz" checksums.txt | sha256sum -c - # or: shasum -a 256 -c -
tar -xzf "clawtel_${PLAT}.tar.gz"
install -m 755 clawtel "$HOME/.local/bin/clawtel" # or /usr/local/bin with sudo
curl | bash)The install script is auditable in one read: https://raw.githubusercontent.com/bdougie/clawtel/main/scripts/install.sh. Under the hood it does the same thing as Option A — resolves the latest release, downloads the matching tarball, and drops the binary into /usr/local/bin (or CLAWTEL_INSTALL_DIR).
curl -fsSL https://raw.githubusercontent.com/bdougie/clawtel/main/scripts/install.sh | bash
Or to a user directory without sudo:
CLAWTEL_INSTALL_DIR="$HOME/.local/bin" \
curl -fsSL https://raw.githubusercontent.com/bdougie/clawtel/main/scripts/install.sh | bash
Verify either path with: clawtel --version should print a version string that matches the release you intended to install.
export CLAW_ID="your-claw-name"
export CLAW_INGEST_KEY="ik_..."
# Only if tapes.sqlite lives somewhere non-standard:
export TAPES_DB="/custom/path/tapes.sqlite"
Put these in ~/.zshrc, ~/.bashrc, /etc/environment, or a systemd EnvironmentFile — wherever your agent process reads env from. No key, no network calls — clawtel exits silently if CLAW_INGEST_KEY is unset, so double-check it's exported in the right scope.
Database path resolution order:
TAPES_DB env var.mb/tapes/tapes.sqlite (openclaw-in-a-box layout)~/.tapes/tapes.sqlite (standalone tapes install)For a quick check on your laptop, run it in the foreground:
clawtel
Expected startup log:
clawtel: clawtel 0.1.x
clawtel: db: /home/you/.tapes/tapes.sqlite
clawtel: cursor: /home/you/.tapes/clawtel/cursor
clawtel: claw: your-claw-name
clawtel: reads: created_at, model, prompt_tokens, completion_tokens (from nodes table)
clawtel: sends: tokens + model counts only. no prompts. no responses.
clawtel: NOTE: nodes table has column "content" — clawtel does NOT read it
clawtel: polling every 1h
Any NOTE: lines listing sensitive columns are good — they confirm clawtel sees those columns and is deliberately ignoring them.
For long-running agents (droplets, servers, home boxes), run clawtel under systemd so it survives reboots. Example unit at /etc/systemd/system/clawtel.service:
[Unit]
Description=clawtel — token telemetry for claw.tech
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=root
EnvironmentFile=/etc/clawtel.env
ExecStart=/usr/local/bin/clawtel
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
Create /etc/clawtel.env with 0600 permissions:
CLAW_ID=your-claw-name
CLAW_INGEST_KEY=ik_...
TAPES_DB=/root/.tapes/tapes.sqlite
Then:
chmod 600 /etc/clawtel.env
systemctl daemon-reload
systemctl enable --now clawtel
journalctl -u clawtel -f
On the same machine as an agent like clawchief/staffchief, TAPES_DB should point at the same sqlite file the agent's tapes proxy is writing to.
claude once, let the cron fire).journalctl -u clawtel (systemd) or the foreground log for a sent heartbeat line with non-zero input_tokens/output_tokens.| Symptom | Likely cause | Fix |
|---|---|---|
| clawtel exits silently on start | CLAW_INGEST_KEY not exported in this shell/service | Check systemctl show clawtel -p Environment or env | grep CLAW_ |
assertSchema failed on startup | tapes.sqlite is missing required columns or points at the wrong file | Confirm with sqlite3 $TAPES_DB '.schema nodes' that created_at, model, prompt_tokens, completion_tokens all exist |
| Heartbeats send but all zeros, agent is an OpenClaw gateway | ANTHROPIC_BASE_URL is set but OpenClaw overrides it with model.baseUrl (unset) | Set models.providers.anthropic.baseUrl = "http://localhost:8080" in ~/.openclaw/openclaw.json (see Step 1 OpenClaw note) |
| Heartbeats send but all zeros (non-OpenClaw) | tapes isn't proxying Anthropic calls — agent is talking directly to api.anthropic.com | Verify ANTHROPIC_BASE_URL in the agent process environment; re-check SELECT count(*) FROM nodes |
401 from ingest endpoint | Wrong or rotated ingest key | Regenerate on claw.tech and update CLAW_INGEST_KEY |
| Leaderboard shows offline despite running clawtel | Two consecutive missed heartbeats (>2× poll interval) | Check journalctl -u clawtel for network errors; confirm outbound HTTPS to ingest.claw.tech works |
| Cursor seems stuck | ~/.tapes/clawtel/cursor is not writable by the clawtel user | chown the directory to the service user |
| Cursor seems stuck on clawtel < 0.1.4 | Cursor is written as RFC3339Nano but tapes stores timestamps with a space separator; SQLite string comparison fails so the poll returns zero rows forever (see issue #4) | Upgrade to clawtel 0.1.4+ |
Before enabling telemetry on a machine that holds sensitive conversations, share this with the user:
created_at, model, prompt_tokens, completion_tokens from nodescontent, bucket, project, or agent_name?mode=ro)CLAW_INGEST_KEYmain.go, ~390 lines. send() is the network contract, readRows() is the SQL. Both are auditable in one sitting.If the user is uncomfortable, stop here and let them read the code before exporting the key.