This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
ClawChain is "AI Agents as a Service, powered by Solana." Users deposit SOL via an on-chain program and receive a personal AI bot on Telegram — no accounts, no subscriptions. The MVP uses PicoClaw (<10MB RAM, single Go binary) deployed on dedicated GCP micro VMs.
Three main components:
- Solana Program (Anchor/Rust) — On-chain state: user deposits, bot handle registry, active/stopped flags, billing transfers. Wallet-to-bot-handle mapping is intentionally public for cryptographic identity verification.
- Orchestrator (Python + SQLite) — Watches blockchain for new deposits, provisions GCP container VMs, writes bot handles back on-chain, runs periodic billing loop (user -> operator account). Runs on a dedicated GCP VM with IAM permissions. Has a mock mode (YAML file backend) for testing without blockchain or GCP.
- PicoClaw VMs (GCP) — Each user gets a dedicated Container-Optimized OS VM running the
docker.io/sipeed/picoclaw:latestimage with config injected via cloud-init.
anchor build # compile the program
anchor test # run all 25 tests (starts local validator)
anchor deploy --provider.cluster devnet # deploy to devnet
anchor keys list # show program IDnpx ts-node scripts/devnet_wallets.ts # generate 3 test wallets, airdrop SOLWallets are saved to target/test-wallets/wallet_user{1,2,3}.json.
cd orchestrator && pip install -r requirements.txt
cp .env.example .env # edit with your values
# Create telegram_bots.txt with pre-generated bot credentials (bot_name:bot_token per line)
python -m orchestrator.main # run from repo rootMock mode (no Solana/GCP deps needed):
MOCK_STATE_FILE=./orchestrator/mock_state.yaml \
TELEGRAM_BOTS_FILE=./orchestrator/telegram_bots.example.txt \
SQLITE_DB_PATH=/tmp/test.db \
python -m orchestrator.maincd orchestrator && pip install -r requirements.txt
pytest -v # all 51 tests (~0.1s)
pytest tests/test_db.py -v # single module
pytest tests/test_orchestrator.py -v # integration tests onlycd monitor && pip install -r requirements.txt
python read_accounts.py # list all UserBot accounts
python read_accounts.py --wallet <PUBKEY> # show specific user's bot
python read_accounts.py --config # show OperatorConfig
python read_accounts.py --cluster localnet # use localnet instead of devnetThree PDA account types, all owned by the program:
- OperatorConfig (
seeds = [b"operator_config"]) — singleton storing authority, treasury, billing_amount, min_deposit - UserBot (
seeds = [b"user_bot", user_wallet]) — one per user; stores owner, bot_handle, is_active flag, provisioning_status (0=None/1=Locked/2=Ready/3=Failed), timestamps, totals - ServiceStatus (
seeds = [b"service_status"]) — singleton storing active_instances, max_instances, accepting_new (written by orchestrator)
The user's available SOL balance is the PDA's lamport balance minus rent-exempt minimum — no separate balance field.
Ten instructions: initialize, deposit (init_if_needed), set_bot_handle (also sets provisioning_status=Ready), deactivate, bill (requires provisioning_status=Ready, auto-deactivates on insufficient funds), withdraw_remaining, lock_for_provisioning, refund_failed_provision, initialize_service_status, update_service_status.
Commit each major step separately so the history is reviewable:
- Solana program — Anchor scaffold, account structs, instructions, tests
- Devnet deployment — program ID updates, deploy config
- Orchestrator — Python backend modules (config, db, chain, gcp, cloud_init, bot_pool, main)
- Infrastructure changes — VM image approach, cloud-init templates
- Frontend/GUI — (future) web UI for user status
Before committing, always verify no secrets are staged: git diff --cached --name-only — watch for .env, telegram_bots.txt, *-keypair.json, wallet_user*.json.
Every change must pass the full test suite before committing. Run both:
anchor test # 25 Solana program tests (local validator)
cd orchestrator && pytest -v # 51 Python orchestrator testsNew features must include tests:
- Solana program changes → add/update tests in
tests/claw_chain.ts - Orchestrator changes → add/update tests in
orchestrator/tests/- Pure functions → unit test (Level 1: test_db, test_cloud_init, etc.)
- Orchestrator loop logic → integration test (Level 2: test_orchestrator.py using
watcher_tick()) - GCP/Solana interactions → mock external deps, never hit real services in automated tests
Do not merge or push code with failing tests.
This section is append-only. When a decision is superseded, add a new entry referencing the old one. Never edit or delete previous entries — they serve as historical record of why things changed.
NemoClaw (NVIDIA's enterprise OpenClaw stack) was the initial target but proved impractical for rapid iteration. PicoClaw's minimal footprint (<10MB RAM, single binary, native Telegram support) makes it ideal for spinning up isolated agent instances quickly and cheaply on micro VMs.
For the hackathon MVP, GCP micro VMs offer the simplest isolation model with acceptable cost. The production path is Kubernetes on bare metal via tf-xd-venture-talos01.
The user's available balance is the UserBot PDA's lamport balance minus rent-exempt minimum — no separate balance field in the account struct. Deposits transfer SOL into the PDA; billing transfers SOL out to treasury. This keeps the balance always consistent with no sync drift possible.
The bill instruction sets is_active = false when balance is insufficient instead of returning an error. This lets the orchestrator call bill without pre-checking balances client-side. The orchestrator detects the is_active = false state on the next poll and shuts down the VM.
For MVP, Telegram bots are pre-generated manually via @BotFather and stored in a text file (bot_name:bot_token per line). The orchestrator imports the pool into SQLite and tracks allocation. This avoids Telegram API integration complexity for the hackathon.
Supersedes part of ADR-002 (custom base image approach).
Initially planned to use a custom GCP VM image with PicoClaw installed from deb package. Changed to GCP Container-Optimized OS (cos-cloud/cos-stable) running docker.io/sipeed/picoclaw:latest via gce-container-declaration metadata. Reasons:
- No deb package available for x86_64 (only ARM)
- No base image to build or maintain
- No systemd unit to write
- Config injected via cloud-init writing to host path, bind-mounted into container
The orchestrator uses a ChainBackend abstraction (chain.py) with two implementations: SolanaBackend (real RPC) and MockBackend (YAML file). Set MOCK_STATE_FILE env var to activate mock mode. The mock backend re-reads the YAML on every poll (so you can edit state live) and writes back changes from set_bot_handle/bill. All heavy deps (solana, solders, google-cloud-compute) are lazy-imported so mock mode works with just pyyaml + python-dotenv.
Solana Name Service (SNS/.sol domains) only exist on mainnet. Registration of clawchain.sol deferred until mainnet launch. No code changes needed — the on-chain data model already supports the future web GUI without modification.
Added MAX_INSTANCES config to limit concurrent VMs. Orchestrator startup validates max_instances <= bot pool size. A new ServiceStatus PDA (separate from OperatorConfig to avoid reallocation) stores active_instances, max_instances, accepting_new on-chain for external visibility (future GUI). The orchestrator writes this every poll cycle via update_service_status. When at capacity, new deposits are not provisioned — funds sit until a slot opens.
Added provisioning_status field to UserBot (0=None, 1=Locked, 2=Ready, 3=Failed). Orchestrator calls lock_for_provisioning before creating a VM (prevents withdrawal during provisioning). If VM fails to start within ~5 min, refund_failed_provision deactivates the account (user can withdraw). bill instruction now requires provisioning_status == 2 (Ready) — no billing until VM is confirmed healthy and bot handle is set. This is not true escrow (no 3rd party), but guarantees users are never charged for a service they didn't receive.