Decentralized inference network CLI for automated mathematical research via Lean code generation
Project description
Research Capacitor
Research Capacitor is a standalone Python CLI for a decentralized inference network focused on mathematical research and Lean 4 code generation. Nodes connect directly to Supabase for coordination and ledger state, while local inference runs through llama.cpp.
The project now also includes a backend-owned research loop:
research_jobstrack high-level requester goals and budgetsresearch_episodestrack individual conjecture-search campaigns under a jobproof_attemptstrack every queued attempt, repair, and verifier result- the FastAPI backend can run an internal worker that keeps the provider queue full and schedules repairs
rc research-runnow seeds a backend-managed research jobrc research-statusshows backend-managed job, episode, and attempt progress
The goal is simple:
- requesters reserve Compute Credits to submit proof-generation work
- providers earn Compute Credits by running local inference
- the ledger records reserves, transfers, and releases explicitly
System Overview
Research Capacitor has two node roles:
requester: submits proof-generation tasksprovider: polls the network, claims tasks, runs inference, and settles resultsboth: does both on a single machine
There is no separate coordinator service in the current architecture. Supabase is used for:
- node registration and heartbeats
- task queueing
- result storage
- credit balance tracking
- ledger entries
- secure RPC-based state transitions
Local inference is done entirely on the node. During rc init, provider-capable nodes detect available hardware, pick the largest supported model that fits, and can download it automatically.
How The Network Works
The end-to-end flow is:
- A node runs
rc initand registers itself in Supabase. - A requester runs
rc submit "..." --budget N. - The network moves the full task budget from available balance into reserved balance.
- A provider running
rc serveclaims the task and runs local inference throughllama.cpp. - When the task completes, the network stores the result plus runtime accounting data.
- On completion, the network transfers the billed amount to the provider.
- Any unused reservation is released back to the requester's available balance.
- If a task fails, it is requeued until retry budget is exhausted. On final failure, the full reservation is released.
Task claiming is exclusive per queued task. A single research job can fan out into many queued attempts, but each tasks.id is claimed atomically by at most one provider at a time.
For autonomous research jobs, the backend repeats that flow multiple times:
- requester submits a high-level research job and budget
- backend creates a conjecture-search episode
- backend enqueues one or more provider attempts across active episodes
- provider generates Lean output
- backend verifies the generated Lean
- if verification fails, backend feeds diagnostics into a repair prompt
- backend enqueues the next repair attempt when needed
- repeat until the theorem is verified or the job exhausts its budget
Credit And Settlement Model
The network uses 1 credit = 1 TFLOP.
Internally, the ledger stores integer microcredits, where 1 microcredit = 1 MFLOP = 0.000001 TFLOP.
Submission
When a requester submits a task:
- the full budget moves from
balancetoreserved_balance - the reservation is recorded in
transactions
Completion
When a provider completes a task:
- the runtime records actual prompt tokens, generated tokens, loaded model parameter count, and eval timings
- the system computes actual model-work MFLOPs from the exact loaded GGUF and actual processed tokens
- the system treats those MFLOPs as the exact integer credit subunits used for settlement
- the provider receives a transfer of
min(actual_credit_subunits, task_budget_subunits) - any unused reservation is released back to the requester
This means:
- requester reserve = full submitted budget
- provider transfer = billed compute
- requester release = unused reserved budget
So for a successful task:
reserve = transfer + release
Failure / Retry
Each task has retry state:
attempt_countmax_attempts- lease timestamps and error metadata
Failure behavior:
- non-final failure: task returns to
pending - final failure: task moves to
failedand the requester receives a full release of the held budget - stale claimed/running tasks are automatically requeued when leases expire
What "Actual Compute" Means
Research Capacitor now uses:
- the exact loaded GGUF model parameter count from
llama.cpp - the actual prompt token count
- the actual generated token count
It does not use the old static catalog estimate anymore.
The raw MFLOP telemetry is model-work accounting:
2 * model_params * (prompt_tokens + generated_tokens) / 1e6
Credits are then settled in exact TFLOP subunits:
billable_credit_subunits = actual_mflops
billable_tflops = billable_credit_subunits / 1_000_000
This is accurate for billing the actual loaded model and actual processed tokens. It is not a hardware performance-counter reading of every fused CPU, Metal, or CUDA kernel, because llama.cpp does not expose a portable per-kernel FLOP counter through the Python API.
Installation
git clone https://github.com/t0mCS/Compute-Capacitor.git
cd Compute-Capacitor
./scripts/install_rc.sh
That installer creates an isolated virtualenv under ~/.local/share/research-capacitor, symlinks rc into ~/.local/bin, and works without requiring npm or a pre-existing project venv.
If you prefer pipx, install directly from the repo:
pipx install git+https://github.com/t0mCS/Compute-Capacitor.git
For local development, keep using an editable install:
python3 -m venv .venv
source .venv/bin/activate
pip install -e .
Supabase Setup
- Create a Supabase project.
- Open the SQL Editor.
- Run
supabase/schema.sql. - Run
supabase/hardening.sql. - Run
supabase/reservation_runtime.sql.
schema.sql creates the base tables and types. hardening.sql enables the secure RPC-based operating mode:
- revokes direct table access from client roles
- enables RLS
- exposes narrow
SECURITY DEFINERRPCs for registration, reads, and public status access
Production note: backend-owned orchestration RPCs and store_verified_lemma should be called with the Supabase service-role key, not a publishable client key.
reservation_runtime.sql installs the canonical reservation-mode runtime RPCs:
- reserves credits when a task is submitted
- transfers billed credits to the provider on completion
- releases unused or failed reservations back to the requester
- requeues or fails expired leased tasks safely
If you already applied an older version of the project schema, rerun all three files in order so the base schema, hardening layer, and reservation runtime are brought back into sync.
If you want to wipe runtime state without dropping the schema, run supabase/clear_runtime.sql before reapplying the SQL files or starting a fresh soak.
After clearing runtime state, re-register the requester and any providers you plan to use, then fund the requester or mark it as unlimited_budget = true before starting a soak.
Quick Start
1. Initialize A Node
rc init \
--name "my-node" \
--role both \
--supabase-url "https://YOUR_PROJECT.supabase.co" \
--supabase-key "YOUR_PUBLISHABLE_KEY"
Or with environment variables:
export SUPABASE_URL="https://YOUR_PROJECT.supabase.co"
export SUPABASE_KEY="YOUR_PUBLISHABLE_KEY"
rc init --name "my-node" --role both
What rc init does:
- generates a local node ID
- writes
~/.rc/config.toml - detects hardware
- selects a model automatically for provider-capable nodes
- optionally downloads the model
- registers the node in Supabase
- creates the node and balance rows, but does not automatically mint test credits
After a fresh reset, fund the requester before submitting work. Two simple SQL-editor options are:
UPDATE nodes
SET unlimited_budget = true
WHERE id = 'YOUR_REQUESTER_NODE_ID';
INSERT INTO credit_balances (node_id, balance, reserved_balance, updated_at)
VALUES ('YOUR_REQUESTER_NODE_ID', 100, 0, now())
ON CONFLICT (node_id)
DO UPDATE SET balance = EXCLUDED.balance,
reserved_balance = EXCLUDED.reserved_balance,
updated_at = now();
2. Run As A Provider
rc serve --poll-interval 2 --max-tokens 256 --temperature 0.2
3. Submit A Task
rc submit "Prove in Lean that for every natural number n, n = n." --budget 1.25
You can also submit richer queued work:
rc submit "Repair this failed proof..." --task-type proof_repair --metadata-json '{"domain":"naturals"}'
4. Check Status
rc status TASK_ID
rc research-status
5. Check Balance And Ledger
rc balance
rc ledger --limit 20
6. Run Backend-Owned Research
Start the backend worker:
export SUPABASE_URL="https://YOUR_PROJECT.supabase.co"
export SUPABASE_KEY="YOUR_PUBLISHABLE_KEY"
export RESEARCH_BACKEND_ENABLED=1
uvicorn backend.main:app --reload
Then submit a high-level research job:
rc research-run \
--domain naturals \
--total-budget 25 \
--budget-per-attempt 1 \
--max-concurrent-episodes 3
The CLI now defaults backend-managed research commands to the deployed Azure backend. To point a node at a different backend, pass --backend-url or set RC_BACKEND_URL.
CLI Commands
All commands are exposed through rc.
rc init
Initialize a node and register it with the network.
Example:
rc init \
--name "provider-1" \
--role provider \
--supabase-url "https://YOUR_PROJECT.supabase.co" \
--supabase-key "YOUR_PUBLISHABLE_KEY"
Useful options:
--name,-n: display name--role,-r:provider,requester, orboth--supabase-url: Supabase project URL--supabase-key: Supabase publishable key--backend-url: backend base URL for research orchestration; defaults to the deployed Azure app orRC_BACKEND_URL--model,-m: override auto-selected model--skip-model-download: register without downloading the model yet
rc serve
Run a provider loop.
What it does:
- sends secure heartbeats
- polls for pending tasks
- claims one task atomically
- marks it running
- renews task leases and node heartbeats while inference is still running
- runs local inference
- settles completion or failure through secure RPCs
Important:
- many providers can work on the same research job at once
- they do that by claiming different queued attempts under that job
- two providers should not claim the same task because
claim_taskusesFOR UPDATE SKIP LOCKED
Example:
rc serve \
--poll-interval 2 \
--max-tokens 256 \
--temperature 0.2 \
--threads 2 \
--lease-seconds 300 \
--keepalive-interval 30
Useful options:
--model,-m: override configured model--poll-interval,-p: seconds between queue polls--max-tokens: completion cap--temperature,-t: sampling temperature--threads: inference threads per provider; defaults to splitting local CPU capacity across concurrent local providers--ctx-size: llama.cpp context window--gpu-layers: override GPU offload layer count--lease-seconds: task lease duration; renewed while inference runs--keepalive-interval: interval between in-task heartbeats and lease renewal attempts
rc submit
Submit a Lean-proof-generation task.
Example:
rc submit "Show in Lean that True implies True." --budget 500
Useful options:
- positional
prompt: the problem statement --model,-m: override model label stored with the task--budget,-b: maximum TFLOP credits to reserve for the task; fractional values such as0.25or1.5are allowed--task-type: classify the queued work, for examplelean_proof_gen,proof_attempt,proof_repair, orcounterexample_search--metadata-json: attach structured JSON metadata that providers can use to shape the prompt
rc research-run
Submit a backend-managed research job.
What it does:
- sends one high-level research goal to the backend
- lets the backend worker create conjecture-search episodes
- lets the backend fill the provider queue with proof or repair prompts
- returns the created research job id so you can inspect progress
Example:
rc research-run \
--total-budget 25 \
--max-concurrent-episodes 3 \
--budget-per-attempt 1 \
--max-attempts-per-episode 4
rc research-status
Inspect backend-managed research jobs, episodes, and proof-attempt history.
Examples:
rc research-status
rc research-status JOB_ID
rc status
Inspect a task.
Example:
rc status c4a239c7-4adc-4d0e-82ac-3263aad880a0
What it shows:
- task status
- model
- budget
- retry count
- generated Lean output
- actual compute in TFLOPs
- billed credits
- prompt and generated token counts
- eval time
- last error for failed/requeued work
rc balance
Show the current credit balance for the local node.
Example:
rc balance
rc ledger
Show recent credit movements for the local node.
Example:
rc ledger --limit 20
Ledger entry types:
reserve: requester moved budget from available to reservedtransfer: reserved credits settled to the providerrelease: reserved credits returned to the requester- historical projects may still contain older
burn,mint, andrefundrows from before the reservation migration
rc rotate-secret
Rotate the local node bearer secret through the secure RPC layer.
Example:
rc rotate-secret
What it does:
- generates a fresh local node secret
- asks Supabase to replace the old secret hash atomically
- writes the new secret into
~/.rc/config.tomlonly after the server confirms the change
Local Configuration
The CLI stores its local config at:
~/.rc/config.toml
It contains:
- node identity
- node secret used for authenticated RPC calls
- selected role
- Supabase URL and key
- chosen model
- downloaded model path
- hardware summary
Database Shape
The Supabase schema uses five main tables:
nodescredit_balancestaskstask_resultstransactions
Important task fields include:
statusresearch_episode_idattempt_indexparent_attempt_idtask_metadatacredit_budgetattempt_countmax_attemptslease_expires_atclaimed_atstarted_atcompleted_atfailed_atlast_error
Important task result fields include:
mflops_usedbilled_mflopsprompt_tokensgenerated_tokensmodel_paramsprefill_eval_msdecode_eval_mstotal_eval_ms
mflops_used remains the raw telemetry field. billed_mflops is the legacy result column name used for exact settled credit subunits, where 1 billed_mflops = 0.000001 TFLOP credit.
Research-specific tables:
research_jobsresearch_episodesproof_attempts
Security Model
The current secure mode is:
- no direct client reads or writes to sensitive tables
- RPC-only state transitions
- atomic settlement in Postgres functions
- node-secret authenticated task and balance RPCs
- RLS enabled across the main tables
What this protects against:
- direct balance editing with the publishable key
- direct task insertion or task mutation from the client
- non-atomic ledger settlement
Current limitation:
- node identity is authenticated with a locally stored bearer secret, not a public-key signature scheme
- a stolen local config would still let an attacker act as that node until the secret is rotated
So the system is much safer than the initial version, but it is not yet a fully trustless identity model. A future version should rotate secrets cleanly or move to signed task-claim messages.
Web Dashboard
The repo now includes a read-only network dashboard in web/.
What it shows:
- healthy, online, and stale node counts
- pending / assigned / running / completed / failed task totals
- 24-hour throughput, success rate, reserves, transfers, releases, and legacy burn/mint/refund totals when present
- recent tasks, recent ledger activity, active nodes, and model mix
The dashboard reads through public, sanitized RPCs defined in supabase/hardening.sql:
get_public_network_summary()get_public_recent_tasks(p_limit integer)get_public_recent_activity(p_limit integer)get_public_nodes(p_limit integer)
Dashboard Setup
- Reapply
supabase/hardening.sqlin Supabase so the public dashboard RPCs exist. - Create
web/.env.localwith:
VITE_SUPABASE_URL="https://YOUR_PROJECT.supabase.co"
VITE_SUPABASE_ANON_KEY="YOUR_PUBLISHABLE_KEY"
- Start the dashboard:
cd web
npm install
npm run dev
- Build for production:
cd web
npm run build
If the environment variables are missing, the dashboard falls back to demo data so the UI can still be previewed locally.
Typical Workflows
Provider
rc init --name "provider-1" --role provider --supabase-url "$SUPABASE_URL" --supabase-key "$SUPABASE_KEY"
rc serve --poll-interval 2 --max-tokens 256 --temperature 0.2
Requester
rc init --name "requester-1" --role requester --supabase-url "$SUPABASE_URL" --supabase-key "$SUPABASE_KEY"
rc submit "Prove in Lean that if P is true then P is true." --budget 500
rc status TASK_ID
rc balance
rc ledger
Single Machine Demo
rc init --name "demo-node" --role both --supabase-url "$SUPABASE_URL" --supabase-key "$SUPABASE_KEY"
rc serve --threads 1 --max-tokens 128
# in another shell
rc submit "Prove in Lean that 1 = 1." --budget 100
Integration Testing
The repo includes an opt-in live integration harness for:
- claim -> run -> renew -> complete
- lease expiry -> requeue -> reclaim
- node secret rotation
- autonomous episode-style soak submission when
scripts/overnight_soak.py --research-modeis used
Run it against a real Supabase project with a funded requester config:
RC_RUN_INTEGRATION_TESTS=1 \
RC_INTEGRATION_REQUESTER_CONFIG="$HOME/.rc/config.toml" \
python -m unittest tests.test_runtime_integration -v
For a short live soak after a reset:
python scripts/overnight_soak.py \
--requester-config "$HOME/.rc/config.toml" \
--provider-count 2 \
--duration-hours 0.05 \
--queue-depth 2 \
--task-budget 1 \
--submit-interval 10 \
--monitor-interval 10 \
--threads 1 \
--max-tokens 32
Notes:
- the integration suite expects an empty queue before it starts
- it uses real RPCs and real credit settlement, so run it against a disposable or controlled project
- if
rotate_node_secretwas added recently, reapplysupabase/hardening.sqlbefore running the rotation test
Development Notes
- Inference uses
llama-cpp-python. - Model selection prefers the largest supported model that fits detected memory.
- The CLI is designed to run on CPU, Apple Silicon, and CUDA-capable hosts supported by
llama.cpp. - Existing projects should reapply
supabase/hardening.sqlandsupabase/reservation_runtime.sqlwhenever RPC signatures or settlement columns change. - Provider liveness now stays fresh during long inference runs via in-task heartbeats and lease renewal.
- On a single machine running many local providers, set
--threadsconservatively to avoid CPU oversubscription and throughput collapse.
Project details
Release history Release notifications | RSS feed
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 research_capacitor-0.1.0.tar.gz.
File metadata
- Download URL: research_capacitor-0.1.0.tar.gz
- Upload date:
- Size: 125.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c731e313e1b4f30914b0fa9f2bb62e1dfb331b185e167476e15d0bd3643e5b38
|
|
| MD5 |
6f6ca1a6244eebd737686b56451825cc
|
|
| BLAKE2b-256 |
d5110834987182806e5de125df1e8dcb5b9d7391bcda3ec6f209e3f044f820eb
|
Provenance
The following attestation bundles were made for research_capacitor-0.1.0.tar.gz:
Publisher:
publish-pypi.yml on t0mCS/Compute-Capacitor
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
research_capacitor-0.1.0.tar.gz -
Subject digest:
c731e313e1b4f30914b0fa9f2bb62e1dfb331b185e167476e15d0bd3643e5b38 - Sigstore transparency entry: 1077046657
- Sigstore integration time:
-
Permalink:
t0mCS/Compute-Capacitor@cbf6c51b16d3d0713a9e5238ff6d9b826f39025a -
Branch / Tag:
refs/heads/main - Owner: https://github.com/t0mCS
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@cbf6c51b16d3d0713a9e5238ff6d9b826f39025a -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file research_capacitor-0.1.0-py3-none-any.whl.
File metadata
- Download URL: research_capacitor-0.1.0-py3-none-any.whl
- Upload date:
- Size: 40.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4f16ebd043cc564ff21e139bc0e7080dfafdcd62e65ab48015295760cbbf5ba7
|
|
| MD5 |
416e34ae8be3157a453124949fa44a6a
|
|
| BLAKE2b-256 |
cb8e832e8a595f8f156f2e7e2b7dffc0ff251b64184250d2ce3b522ac2ea3656
|
Provenance
The following attestation bundles were made for research_capacitor-0.1.0-py3-none-any.whl:
Publisher:
publish-pypi.yml on t0mCS/Compute-Capacitor
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
research_capacitor-0.1.0-py3-none-any.whl -
Subject digest:
4f16ebd043cc564ff21e139bc0e7080dfafdcd62e65ab48015295760cbbf5ba7 - Sigstore transparency entry: 1077046664
- Sigstore integration time:
-
Permalink:
t0mCS/Compute-Capacitor@cbf6c51b16d3d0713a9e5238ff6d9b826f39025a -
Branch / Tag:
refs/heads/main - Owner: https://github.com/t0mCS
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@cbf6c51b16d3d0713a9e5238ff6d9b826f39025a -
Trigger Event:
workflow_dispatch
-
Statement type: