- Python 3.10+ and
pip proto/types.protoandproto/network.proto(these are included inproto/; ensure they remain in place)
git clone https://github.com/<your-org>/succinct.git
cd succinct
python -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install -e .The CLI installs as succinct. Generated gRPC stubs live in src/succinct_analytics/_generated and will be created on first use if missing.
- First run (creates cache in
data/):
succinct update --days 9 - Incremental refresh:
succinct update - Daily metric: orders >3B and sum gas (in billions):
succinct daily --threshold 3000000000(now also shows total earnings) - Prover stats: order count and gas used for a prover from the cache:
succinct prover-stats --prover 0xABC... [--days 30] [--threshold 3000000000] [--no-by-day] [--no-total] - Notify latest order to Discord:
succinct notify-latest --prover 0xABC... --webhook-url https://discord.com/api/webhooks/... [--dry-run]
Run the API:
uvicorn succinct_analytics.api:app --host 0.0.0.0 --port 8000Endpoints (all GET):
/health→{"status":"ok"}/update→ refresh cache (query params:rpc_url,days,full_refresh,start_ts,end_ts)/daily→ daily gas/orders over threshold (params:threshold,tail)/assigned→ rows for a prover (params:prover,rpc_url,limit,max_pages,include_executed,head)/is-assigned→ boolean status{assigned: true|false}(same params as/assignedexcepthead)/latest→ latest assigned/fulfilled row (params:prover,rpc_url,limit,max_pages,include_executed)/notify-latest→ renders latest order message; setsend=true&webhook_url=...to post to Discord
Examples:
curl "http://localhost:8000/assigned?prover=0xABC..."
curl "http://localhost:8000/is-assigned?prover=0xABC..."
curl "http://localhost:8000/latest?prover=0xABC..."
curl "http://localhost:8000/notify-latest?prover=0xABC...&send=true&webhook_url=https://discord.com/api/webhooks/..."Sample units are in systemd/:
succinct-update.service+succinct-update.timerrunssuccinct updateonce per day (via Docker).succinct-notify.service+succinct-notify.timerrunssuccinct notify-latestevery 30 minutes (via Docker).succinct-cleaner.service+succinct-cleaner.timerchecks for assigned orders every 5 minutes; if none, it restarts the cluster inSUCCINCT_CLUSTER_DIR.succinct-switcher.service+succinct-switcher.timerchecks every minute for an assigned order withgas_limit > 1,000,000,000; if found, it runsSUCCINCT_SWITCH_CMD.succinct.env.exampleis an environment file template for Docker image/tag, data path, prover/webhook, and optional RPC URL/extra Docker flags.
Steps:
- Copy the unit files into place (requires sudo if using system units):
sudo cp systemd/succinct-update.{service,timer} /etc/systemd/system/ sudo cp systemd/succinct-notify.{service,timer} /etc/systemd/system/ sudo cp systemd/succinct-cleaner.{service,timer} /etc/systemd/system/ sudo cp systemd/succinct-switcher.{service,timer} /etc/systemd/system/ sudo cp systemd/succinct.env.example /etc/succinct.env - Edit
/etc/succinct.envto set:SUCCINCT_IMAGE(tag you built/pulled),SUCCINCT_DATA_DIR(host cache dir), optionalSUCCINCT_DOCKER_OPTS.SUCCINCT_PROVER,SUCCINCT_DISCORD_WEBHOOK, optionalSUCCINCT_RPC_URL.- Cleaner:
SUCCINCT_CLUSTER_DIR(path to~/sp1-cluster/infraor similar). - Switcher:
SUCCINCT_SWITCH_CMD(the command that repoints other clusters).
- Reload and enable the timers:
sudo systemctl daemon-reload sudo systemctl enable --now succinct-update.timer sudo systemctl enable --now succinct-notify.timer sudo systemctl enable --now succinct-cleaner.timer sudo systemctl enable --now succinct-switcher.timer
Timers are marked Persistent=true, so missed runs will catch up after reboots.
- The services now shell out to Docker. The default image name is
succinct(override withSUCCINCT_IMAGEin/etc/succinct.env). - CSV/cache data is bind-mounted from
SUCCINCT_DATA_DIR(default/opt/succinct/data) to/app/datain the container so it persists outside the container. - Optional
SUCCINCT_DOCKER_OPTSlets you inject extra flags (e.g.,--pull=always,--log-driver=..., auth). - You still need to build/push/pull the image yourself (see Docker section above).
Build the image:
docker build -t succinct .Run the CLI, mounting a host directory to persist CSVs outside the container (the app writes to data/ relative to the workdir):
mkdir -p ./data
docker run --rm -v "$(pwd)/data:/app/data" succinct update --days 9For notifications (requires prover + Discord webhook):
docker run --rm -v "$(pwd)/data:/app/data" succinct notify-latest \
--prover 0xABC... \
--webhook-url https://discord.com/api/webhooks/...The VOLUME /app/data declaration in the image highlights where to mount storage; you can change the mount point/path as needed (just keep data/ as the working directory relative path).