尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证制作者的介绍仅有上面的一句话。 更多详情请阅读仓库 README。 仓库 README ↗
正在加载完整 README。
查看完整 README ↗Sovereign inference market observability + oracle for Routstr and centralized catalogs (OpenRouter, OrcaRouter).
Rust CLI + lightweight Axum dashboard. Listens on Nostr for provider discovery (RIP-02), polls model catalogs, normalizes prices to USD and sats, persists to SQLite, and serves a live price board.
License: MPL-2.0
# Requires Rust 1.86+
cargo run --release -- serve
# Dashboard
open http://127.0.0.1:8080/
First boot pulls the OpenRouter and OrcaRouter models catalogs (~650 quotes), Coinbase BTC/USD, and (unless disabled) connects to Nostr relays for Routstr kind 38421 node announcements.
tokenstats serve [OPTIONS]
Options:
--bind <ADDR> HTTP bind (default 127.0.0.1:8080) [env: TOKENSTATS_BIND]
--relay <URL> Nostr relays (repeatable / comma) [env: TOKENSTATS_RELAYS]
--node <URL> Seed Routstr-compatible endpoints [env: TOKENSTATS_NODES]
--allow-private-nodes Poll Nostr nodes on private IPs (trusted LAN only)
--db <PATH> SQLite path (default data/tokenstats.db) [env: TOKENSTATS_DB]
--no-nostr Skip Nostr listener
--no-poll Skip HTTP provider polling
--no-persist In-memory only (no SQLite)
-v, --verbose Debug (-v) / trace (-vv)
# Catalog + dashboard only (no Nostr)
cargo run -- serve --no-nostr
# Seed a known Routstr node
cargo run -- serve --node https://your-node.example.com
# Custom DB + bind
cargo run -- serve --bind 0.0.0.0:8080 --db ./tokenstats.db
| Feature | Description |
|---|---|
| Best Now | Cheapest blended price for popular families (Claude, Grok, DeepSeek, GPT, Gemini, …). Shows node under the price. |
| Blend ratio | Toggle 1:2 / 1:3 / 1:4 in:out workload (?ratio=3). |
| Presets | Cheapest Frontier · Fastest · Most Private · Local-first |
| Feed strip | Feed status + reasons, last cycle, per-source health, Routstr nodes OK + dormant, stale count, BTC rate age |
| Signals | Per-quote 24h reliability, p50 latency, age (stale highlighted), outlier tag; Quality pills: healthy only, hide stale / outliers |
| Capabilities | tools / reasoning / vision tags per model (dashed = inferred from OpenRouter / OrcaRouter for Routstr nodes); pills filter on them |
| Δ out | Price change vs previous catalog snapshot |
| Hover → copy | curl or OpenAI-compatible config JSON for that quote; compare opens the model across sources |
| Compare | /compare?model=…: one model's offers from every source side by side, cheapest first, with spread and cheapest healthy offer |
| Export CSV | Current filtered view → /export.csv?... |
| Nodes | Discovered RIP-02 endpoints + reliability (last hour; 24h/7d on hover) + p50/p95 latency |
Query params: ?source=openrouter&preset=frontier&ratio=4&model=deepseek
| Path | Description |
|---|---|
GET / | HTML dashboard (auto-refresh ~15s) |
GET /health | Liveness + feed status (ok/degraded/down/…) + counts + last_updated |
GET /api/quotes | JSON quotes (source, model, kind, preset, ratio, limit, max_age_secs, min_reliability, min_reliability_24h, max_latency_ms, exclude_stale, exclude_outliers, healthy, capabilities); each quote carries canonical_model and tools / reasoning / vision / input_modalities / max_output_tokens |
GET /api/nodes | Discovered nodes + status (healthy/failing/dormant/skipped) + failure_class + reliability (1h/24h/7d) + latency p50/p95 + last success/error (+ skip_reason when not polled) |
GET /api/summary | Aggregates + Best Now strip |
GET /api/compare | Same model across sources, grouped by canonical_model (model, ratio, limit groups, plus the quote filters): offers, min/median/max blend, spread_pct, cheapest, cheapest_healthy |
GET /compare | HTML compare view |
GET /export.csv | CSV of current filtered view |
Nostr (kind 38421) ──► store.nodes ──► poll /v1/models ──┐
├──► store.quotes ──► dashboard / API
OpenRouter + OrcaRouter /v1/models ─────────────────────┘
Coinbase BTC-USD ──► oracle (USD ↔ sats)
SQLite snapshot every 30s
| Module | Role |
|---|---|
src/nostr/ | RIP-02 discovery (also listens for legacy kind 40500) |
src/providers/ | Reference catalogs (OpenRouter, OrcaRouter) + Routstr-compatible /v1/models |
src/market/ | Popular families, blend, presets |
src/oracle/ | BTC/USD + dual-unit normalization |
src/store/ | In-memory quotes/nodes + poll reliability |
src/persist/ | SQLite restore + periodic save |
src/web/ | Axum routes + HTML |
d, u, mint, version tags). Models/pricing come from the node’s /v1/models, not the event body.Specs: Routstr/protocol
data/tokenstats.db (gitignored)--node seeds are trusted| Concern | Flag / env | Default |
|---|---|---|
| Bind | --bind / TOKENSTATS_BIND | 127.0.0.1:8080 |
| SQLite | --db / TOKENSTATS_DB | data/tokenstats.db |
| Poll interval | --poll-interval-secs | 60 |
| BTC interval | --btc-interval-secs | 60 |
| Persist interval | --persist-interval-secs | 30 |
| Node poll concurrency / timeout | --node-concurrency / --node-timeout-secs | 16 / 10 |
| Quote expiry | --quote-ttl-secs | 86400 |
| Node cap / expiry | --max-nodes / --node-ttl-secs | 512 / 604800 |
| Stale flag threshold | --stale-after-secs | 600 |
| Price ceiling (USD/1M) | --max-price-usd-per-1m | 1000 |
| Poll private Nostr-announced nodes | --allow-private-nodes | off (SSRF guard) |
| Disable Nostr | --no-nostr | off |
| Disable OpenRouter | --no-openrouter | off |
| Disable OrcaRouter | --no-orcarouter | off |
| Plausible analytics | --plausible-script / TOKENSTATS_PLAUSIBLE_SCRIPT | off (no third-party JS) |
| JSON logs | --log-json / TOKENSTATS_LOG_JSON | off |
See docs/USAGE.md for the full list. SIGINT / SIGTERM trigger graceful shutdown (HTTP drain + final DB snapshot).
CI publishes ghcr.io/johnzilla/tokenstats (:latest, :sha-<short>) for every green main; production pulls it.
# Production with Caddy HTTPS (set DOMAIN in .env — see deploy/env.example)
docker compose --profile proxy pull && docker compose --profile proxy up -d
# Local build instead (dev / forks); app on 127.0.0.1:8080
docker compose up -d --build
Full doctl + DNS + Caddy/Nginx guide: docs/DEPLOY.md.
cargo fmt --check
cargo clippy --all-targets -- -D warnings
cargo test
cargo run -- serve --no-nostr -v
cargo build --release
CI runs fmt, clippy (warnings denied) and tests on Rust 1.99.1, plus cargo check on the 1.86 MSRV, for every PR and push to main.
Mozilla Public License 2.0 — see LICENSE.