Bittensor and Ethereum chain monitoring for AI agents.
Project description
Blockmachine Chainwake
Chain monitoring for AI agents, starting with Bittensor and Ethereum.
Chainwake includes a watcher CLI and MCP server. A watcher invocation suspends until a chain-state condition fires, then exits with a structured JSON payload. It is observation-only: no keys, no signing, no extrinsic submission.
Agents spawn a chainwake watcher as a subprocess and parse stdout to decide what to do next. The exit code is for shell-level control flow; the JSON is where the detail lives.
Install
Install the stable CLI in an isolated environment with uv:
uv tool install chainwake
Or install it in the active Python environment:
pip install chainwake
To try unreleased code or contribute, install the current GitHub source:
uv tool install git+https://github.com/taostat/chainwake.git
Quickstart
All three examples run against a live Bittensor node. Set CHAINWAKE_BT_RPC_URL
or pass --rpc-url; the default is wss://rpc.blockmachine.io.
Enable shell completion: chainwake --install-completion.
Ethereum works without configuration through the anonymous public endpoint. Watch the EIP-1559 base fee in gwei, or wait for a transaction receipt:
chainwake eth network base-fee --below 10
chainwake eth tx 0x0123...abcd
chainwake eth tx 0x0123...abcd --confirmations 3
chainwake eth tx 0x0123...abcd --finality finalized
The watcher reads one baseline, subscribes to newHeads, and then reads each
notified block by its exact hash. Use --above, --drop-pct, --rise-pct, or
--move-pct for other base-fee conditions; delta windows may use time or
blocks. Transaction watches default to one canonical confirmation and return
success or reverted, gas used, and effective gas price. A missing receipt
remains pending; Chainwake does not guess that it was dropped or replaced.
1. Threshold — wait until subnet price drops below 0.05 TAO
chainwake bt subnet 19 price --below 0.05
Add --durable when the watcher must outlive the shell or agent turn that
created it:
chainwake --json --durable \
--context "Tell me the observed price and block." \
bt subnet 19 price --below 0.05
Chainwake persists the validated watcher, starts its local supervisor, prints the job id, and exits immediately. A host can wait without polling the chain:
chainwake --json jobs wait <job-id>
The completion contains the supplied context and the normal watcher result.
Use jobs list, jobs show, and jobs cancel to manage
persisted jobs. Because job arguments are stored locally, durable mode rejects
literal --api-key and --rpc-url values; configure
CHAINWAKE_BT_API_KEY or CHAINWAKE_BT_RPC_URL in the supervisor environment.
Subscribes to subnet 19's TAO and alpha pool reserves and recomputes the price when either changes. Exits 0 when the price crosses below 0.05 TAO, with stdout:
{
"status": "matched",
"watcher": { "chain": "bt", "resource": "subnet", "resource_id": "19",
"sub_resource": "pool.price", "name": null,
"primitive": "threshold",
"invocation": ["chainwake", "bt", "subnet", "19", "price",
"--below", "0.05"] },
"condition": { "operator": "below", "target": 0.05 },
"observed": { "path": "subnet.19.pool.price", "value": 0.0487,
"block": 4291820, "block_hash": "0xabc...",
"timestamp": "2026-05-06T10:00:00Z" },
"budget": { "runtime_ms": 3210, "rpc_calls": 3, "estimated_ru_consumed": 3 },
"process": { "pid": 12345, "started_at": "2026-05-06T09:59:57Z" }
}
budget.estimated_ru_consumed and --max-ru form a registry-estimated
observation budget, not a provider billing cap. They intentionally exclude
connection bootstrap, transient retries, and RPCs hidden inside the SDK.
2. Delta — wait until price drops 5% within a 1-hour window
chainwake bt subnet 19 price --drop-pct 5 --window-time 1h
Fires when the oldest price in the rolling 1-hour window is more than 5%
above the current price. The window controls which samples are compared; it
does not impose a runtime limit. --max-runtime alone controls when an
unmatched watcher exits with timeout.
Omit the window to keep the first successful observation as an unexpiring baseline for this watcher:
chainwake bt subnet 28 burnrate --move-pct 1
3. Event — wait for a new subnet to be registered
chainwake bt event --type subnet-registered --max-runtime 24h
Subscribes to subnet registration events. Exits 0 on the first match with
the event's decoded args in observed. Exits 1 if no event fires within
24 hours.
Exit codes
Agents parse JSON for detail; exit codes are for shell-level control flow.
| Code | status field |
Meaning |
|---|---|---|
0 |
matched |
Condition fired |
1 |
stopped / timeout / budget_exhausted |
Watcher finished without a match |
2 |
user_error |
Invalid args, unknown resource |
3 |
provider_error / auth_error |
RPC unavailable, or credentials/access required |
4 |
internal_error |
Bug in chainwake |
For watcher automation, pass --json; every watcher exit then emits a JSON
payload on stdout, including watcher errors. Without that flag, an interactive
TTY uses human-readable output automatically. Stderr carries diagnostics and
is not part of the stable contract.
Output schema
Every watcher invocation run with --json emits exactly one JSON object on
exit. Piped stdout also selects JSON automatically, but agents should use the
explicit flag instead of depending on TTY detection. MCP configuration helpers,
help/version output, and parsing before a watcher is selected are outside this
watcher-envelope guarantee and use their documented formats. Current watcher
output uses
schemas/output.json.
schemas/output.json is the sole current output contract.
Consumers must validate payloads against it and reject unknown fields.
Output shape changes update the Pydantic models and this schema atomically.
Adapters (--out)
By default Chainwake uses human output on an interactive TTY and indented JSON
when stdout is piped. Automation should pass --json explicitly. Use --out
to redirect output:
# NDJSON to stdout, keep running after each match
chainwake bt subnet 19 price --below 0.05 --out stream
# Append NDJSON to a file
chainwake bt subnet 19 price --below 0.05 --out file:///tmp/watchers.ndjson
# Notify via Telegram (any apprise URI)
chainwake bt subnet 19 price --below 0.05 --out "tgram://bottoken/chatid"
--out is repeatable. Apprise supports ~100 notification destinations
(Telegram, Discord, Slack, email, webhooks, and more). See the
apprise URI reference for the full
list.
MCP server
chainwake includes a built-in MCP server that exposes chain watchers as MCP
tools for Hermes, OpenClaw, Claude Desktop, Cursor, and other MCP clients. Its
exit-oriented catalogue maps each wired command to a tool; one filtered event
tool covers both curated friendly names and raw Module.Event types. See
the MCP guide.
chainwake mcp serve --stdio
chainwake mcp config hermes
chainwake mcp config openclaw
Configuration
| Method | Precedence | Example |
|---|---|---|
--rpc-url flag |
Highest | --rpc-url wss://my-node:9944 |
CHAINWAKE_BT_RPC_URL env var |
Middle | export CHAINWAKE_BT_RPC_URL=wss://... |
| Default (Blockmachine RPC) | Lowest | wss://rpc.blockmachine.io |
For Ethereum, the equivalent environment variable is
CHAINWAKE_ETH_RPC_URL; its default is
wss://ethereum-rpc.publicnode.com. Both defaults are anonymous.
Blockmachine is the default RPC provider for Bittensor at launch. The free tier works anonymously, so no configuration is needed for a first watcher. No API key is required. The architecture also supports any Substrate-compatible WebSocket endpoint.
--api-key is accepted by watcher commands for higher-limit Blockmachine
access or other authenticated RPC endpoints. Per-chain environment variables
are CHAINWAKE_BT_API_KEY and CHAINWAKE_ETH_API_KEY.
--max-runtime accepts duration strings: 30s, 10m, 2h, 1d. Default is unbounded.
Agent and automation calls should always set an explicit bounded runtime.
Agent integration
An agent starts a Chainwake process and ends its turn. While Chainwake watches the chain, no model or agent loop runs. When the condition matches (or the watch reaches a limit), Chainwake exits; a capable host wakes the originating agent with the JSON result.
Hermes users install taostat/chainwake from the dashboard's Plugins page.
OpenClaw users install @blockmachine/chainwake from its Skills UI or with:
openclaw skills install @blockmachine/chainwake --global
Both integrations use native background-process completion notifications; they do not poll and do not keep an MCP request pending. See the agent integration guide.
Pass --json for this flow. Stdout is one valid JSON object, the exit code is
always 0–4, and stderr is not parsed. Without --json, an interactive TTY is
rendered as human-readable text.
import json, subprocess
proc = subprocess.run(
[
"chainwake", "bt", "subnet", "19", "price",
"--below", "0.05", "--max-runtime", "5m",
"--json",
],
capture_output=True, text=True,
)
payload = json.loads(proc.stdout)
if proc.returncode == 0:
print(f"Matched at block {payload['observed']['block']}")
elif proc.returncode == 1:
print(f"No match: {payload['reason']}") # timeout or budget_exhausted
elif payload["status"] == "auth_error":
raise RuntimeError(
"RPC access needs operator action; do not retry unchanged credentials"
)
For MCP tools, parallel wakes, detached processes, and restart durability, see Agent integration and MCP integration.
Status
chainwake is in active development. See
PROGRESS.md for
in-flight work.
License — MIT
Links
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file chainwake-0.4.0.tar.gz.
File metadata
- Download URL: chainwake-0.4.0.tar.gz
- Upload date:
- Size: 485.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
866a3bfe25f15bc47a92a5ab587eb3acb0116ec399909c16993416acfc80beca
|
|
| MD5 |
7cf5813b6a8bed1ee524b8a4abbe327b
|
|
| BLAKE2b-256 |
e7f316fcf8167b93dfac48f55fd0e7a65e57c7ede132b7be49fe2691f87b92aa
|
Provenance
The following attestation bundles were made for chainwake-0.4.0.tar.gz:
Publisher:
publish.yml on taostat/chainwake
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
chainwake-0.4.0.tar.gz -
Subject digest:
866a3bfe25f15bc47a92a5ab587eb3acb0116ec399909c16993416acfc80beca - Sigstore transparency entry: 2280826696
- Sigstore integration time:
-
Permalink:
taostat/chainwake@bc0d7cca129ecda7366ae1db7d305b84117b18dc -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/taostat
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@bc0d7cca129ecda7366ae1db7d305b84117b18dc -
Trigger Event:
push
-
Statement type:
File details
Details for the file chainwake-0.4.0-py3-none-any.whl.
File metadata
- Download URL: chainwake-0.4.0-py3-none-any.whl
- Upload date:
- Size: 167.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7be99cf111d48c41eab0fd027423a358df3aec2f7bcbb8442c598b57a658c599
|
|
| MD5 |
434512c9bf8ffef38ce9e3e4828e3f01
|
|
| BLAKE2b-256 |
2aeff097de7abdb238af39c15ae4f9e492bdc643744fdb65cdd6c7004730ab50
|
Provenance
The following attestation bundles were made for chainwake-0.4.0-py3-none-any.whl:
Publisher:
publish.yml on taostat/chainwake
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
chainwake-0.4.0-py3-none-any.whl -
Subject digest:
7be99cf111d48c41eab0fd027423a358df3aec2f7bcbb8442c598b57a658c599 - Sigstore transparency entry: 2280826710
- Sigstore integration time:
-
Permalink:
taostat/chainwake@bc0d7cca129ecda7366ae1db7d305b84117b18dc -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/taostat
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@bc0d7cca129ecda7366ae1db7d305b84117b18dc -
Trigger Event:
push
-
Statement type: