Code-Verified Runtime Guide
Canonical runtime reference derived from executable code paths — service topology, API surface, security switches, and hardened production env profile.
This document reflects current behavior from executable code paths in:
minotaur_subnet/api/server.pyminotaur_subnet/api/routes/*minotaur_subnet/blockloop/loop.pyminotaur_subnet/validator/main.pyminotaur_subnet/miner/main.pyplatform/local_testnet/docker-compose.yml
It is intended to be the canonical runtime reference when older notes diverge.
Service Topology
Minotaur currently runs as a set of cooperating services:
- API server (FastAPI) at
:8080- Mounts
/v1routes for apps, orders, submissions, wallets, chains, and monitoring. - Can also run its own
OrderBook + BlockLoop + BenchmarkWorkerin-process.
- Mounts
- Validator service (aiohttp) at
:9100- Handles validator-specific endpoints (consensus, leader state, weights, legacy plan submission).
- Runs
BlockLooponly when leader (or whenFORCE_LEADER=1).
- Relayer service at
:8091(optional separate process)- Submits approved plans on-chain.
- Miner CLI
agentmode discovers apps and submits source strategies to/v1/submissions/source.submit/statususe/v1/submissions*(git-based workflow).
Active Contract Path
The current live contract path is the AppIntentBase family of app contracts. In local testnet and the default swap workflow, the canonical swap app is DexAggregatorApp.
DexAggregatorAppis the swap app seeded byplatform/local_testnet/seed.py.- The relayer submits
executeIntent(...)transactions toAppIntentBase-derived contracts.
Current Entrypoints
# API server
python -m minotaur_subnet.api.server --port 8080
# Validator service
python -m minotaur_subnet.validator.main --port 9100
# Miner agent loop (LLM strategy iteration)
python -m minotaur_subnet.miner.main agent --validator-url http://localhost:8080
# Git-based solver submission
python -m minotaur_subnet.miner.main submit \
--repo-url <url> --commit-hash <hash> --hotkey <wallet> \
--epoch <n> --validator-url http://localhost:8080 The CLI subcommands are agent, submit, status (no run, no test, no submit-legacy). In local testnet, /v1/submissions* are served by the API service on :8080. submit auto-detection currently expects GET /v1/status; if unavailable, pass --epoch.
API Server Surface (/v1)
Mounted routers in api/server.py:
apps,chains,wallets,monitoring,submissions,orders,native_bittensor,identityintentsrouter exists in codebase but is not mounted in the current server bootstrap.GET /healthincludes sanitizedprovenance_policyandruntime_security_policyobjects (readiness booleans/counts only; no secrets).
Apps
POST /v1/apps/POST /v1/apps/validatePOST /v1/apps/{app_id}/deployGET /v1/apps/GET /v1/apps/{app_id}/statusPUT /v1/apps/{app_id}/scoringGET /v1/apps/{app_id}/manifestGET /v1/apps/manifestsPOST /v1/apps/{app_id}/activatePOST /v1/apps/{app_id}/score
Orders
POST /v1/apps/{app_id}/ordersGET /v1/orders/{order_id}GET /v1/ordersDELETE /v1/orders/{order_id}POST /v1/orders/{order_id}/dry-runPOST /v1/apps/{app_id}/quoteGET /v1/orders/{order_id}/bridgeGET /v1/blockloop/statusPOST /v1/apps/{app_id}/prepare
Submissions
POST /v1/submissions(git-based; signature required)POST /v1/submissions/source(direct source; queued straight to benchmarking)GET /v1/submissions/{submission_id}/statusGET /v1/submissions
Wallets / Chains / Monitoring
POST /v1/wallets/GET /v1/wallets/GET /v1/wallets/{address}/balancesGET /v1/wallets/{address}POST /v1/apps/{app_id}/fundPOST /v1/testnet/faucetPOST /v1/testnet/faucet_erc20GET /v1/chainsGET /v1/apps/{app_id}/monitor
Validator Service Surface (:9100)
The standalone validator currently exposes:
GET /healthGET /identityGET /intents/availablePOST /intents/{app_id}/submitPOST /reloadGET /weightsGET /weights/historyGET /blockloop/statusPOST /orders/submitGET /ordersGET /intents/{app_id}/detailsGET /intents/{app_id}/scoresPOST /apps/{app_id}/quotePOST /consensus/proposalGET /consensus/infoGET /leader
Execution Flow (Order Lifecycle)
For orders submitted to /v1/apps/{app_id}/orders:
-
Request normalization
- Address parsing (plain
0x, CAIP-10, ERC-7930). - Token symbol/address resolution.
- Optional nonce auto-fill for managed wallets.
- Optional permit/approval helpers.
- Address parsing (plain
-
OrderBook receives order (initial status OPEN)
Persisted with EIP-712 user signature attached.
-
BlockLoop claims orders and executes
- plan generation (
SOLVED) - simulation (
SCORED) - JS threshold gate
- on-chain score gate
- consensus gate (
APPROVED) - relayer submission (
SUBMITTED)
- plan generation (
-
Final outcomes
- success:
FILLED - score/consensus/replay failures:
REJECTED - cross-chain source leg success:
BRIDGING - bridge failure:
BRIDGE_FAILED
- success:
Dual Scoring and Thresholds
Both checks are enforced in BlockLoop:
- JS score:
score(plan, state, context)againstapp.config.score_threshold(or global default). - On-chain score: simulation-derived
on_chain_scoreagainstapp.config.on_chain_threshold.
If either gate fails, the order is rejected.
Solver Submission and Champion Adoption
Git-based (POST /v1/submissions)
Pipeline:
-
Stage 1: static checks
Required files, Dockerfile rules, repo size.
-
Stage 2: Docker build/import checks
Builds image with
--network=none --memory=4g, importsSOLVER_CLASS. -
Stage 3: smoke tests
Three synthetic intents through
generate_plan(). -
Benchmarking
Replay against active scenario set.
-
Ranking + champion adoption
Challenger must beat the current champion by 0.5% (
DETHRONE_MARGIN = 0.005).
Source-based (POST /v1/submissions/source)
- Accepts inline Python source.
- Writes temporary solver file.
- Skips screening/Docker.
- Moves directly to
BENCHMARKING.
Local Testnet Reality
From platform/local_testnet/docker-compose.yml:
- API is exposed on host
:8080 - Validator runs on internal network (
:9100), withFORCE_LEADER=1 - Relayer is exposed on
:8091 - There is no Docker miner service; miner agent is expected on host (
make miner-agent) - API is configured with
USE_EVM_RELAYER=1, and can run full order processing pipeline
Known Drift (Now Corrected Here)
miner.main runexamples are stale; current command isminer.main agent./v1/submissions*live on API server in local testnet.intentsAPI router is present in code but not mounted by default.
Submission Security Switches
Current policy controls in api/routes/submissions.py and worker/server wiring:
| Variable | Default | Purpose |
|---|---|---|
ENABLE_BENCHMARK_WORKER | false | Explicitly enables background screening/benchmark worker. |
ENABLE_SOLVER_ROUND_COORDINATOR | true when benchmark worker enabled | Drives the closed-round solver lifecycle on the API server. |
SOLVER_ROUND_COORDINATOR_INTERVAL_SECONDS | 5 | How often the API server polls durable round state to resume explicitly closed rounds. |
SOLVER_ROUND_OPEN_SECONDS | 300 | How long the elected leader leaves a solver round OPEN before it auto-closes intake and freezes the replay cohort. |
SOLVER_ROUND_EPOCH_SECONDS | 60 | Wall-clock fallback epoch size used for close_epoch / decision_deadline_epoch / effective_epoch only when native chain tempo and explicit block-based fallback are both unavailable. |
SOLVER_ROUND_EPOCH_BLOCKS | unset | Optional block-based fallback epoch size; used only when native chain tempo is unavailable from metagraph/subtensor state. |
SUBMISSIONS_ACCEPTING | true | Global kill switch for new submissions. |
SUBMISSIONS_API_KEY | unset | If set, requires header x-submission-api-key on submission create endpoints. |
SOLVER_ROUND_INTERNAL_API_KEY | unset | Shared secret for validator-to-validator round control and champion proposal traffic via header x-solver-round-internal-key. |
SUBMISSIONS_RATE_LIMIT_PER_MINUTE | 60 | Per-route/per-principal create limit. |
ENABLE_SOURCE_SUBMISSIONS | false | Required to use /v1/submissions/source. |
ALLOW_SUBPROCESS_BENCHMARK | false | Required for solver_path benchmarking. |
ALLOW_CHAMPION_HOT_SWAP | false | Enables runtime champion swaps when explicitly set. |
CHAMPION_SWAP_TIMEOUT_SECONDS | 90 | Timeout when starting champion Docker runtime. |
SUBMISSION_PROVENANCE_SIGNING_PRIVATE_KEY | unset | If set, submission screening signs provenance with EIP-191 secp256k1. |
SUBMISSION_PROVENANCE_SIGNING_ADDRESS | unset | Expected signer address for the configured private key. |
SUBMISSION_PROVENANCE_ALLOWED_SIGNERS | unset | Comma-separated EVM addresses allowed to sign provenance. |
SUBMISSION_PROVENANCE_HMAC_KEY | unset | Legacy HMAC signer/verifier key (kept for compatibility). |
REQUIRE_SIGNED_PROVENANCE | false | When enabled, champion adoption/hot-swap requires valid provenance under configured verifier policy. |
REQUIRE_ASYMMETRIC_PROVENANCE | false | Asymmetric-only policy: disallows HMAC provenance and requires allowed signer verification. |
VALIDATOR_HOTKEY_SS58 | unset | Explicit hotkey override for solver-round metagraph leader election when the API service cannot load a local Bittensor wallet. |
ENFORCE_RUNTIME_SECURITY_PROFILE | false | Strict production guardrail; startup fails if unsafe runtime flags are enabled (source submissions, subprocess benchmarking, weak provenance verifier, missing API key/rate limit when accepting submissions). |
API startup now performs a provenance policy self-check and fails fast on inconsistent signer/verifier config.
Champion adoption policy only allows GENESIS or Docker-screened submissions with an immutable local image_id (sha256:...). Source (solver_path) submissions can be benchmarked when enabled, but are never champion-eligible.
The benchmark worker performs replay scoring only; live champion activation is handled by the solver round coordinator via EpochManager. Round closure may still be forced manually via POST /v1/solver/round/close, but when metagraph leader election is configured the elected leader now auto-closes rounds after SOLVER_ROUND_OPEN_SECONDS, replay-evaluates them, auto-certifies finalists, and activates certified champions once the current solver-round epoch reaches effective_epoch.
Champion certification collects real validator quorum through POST /v1/solver/round/certify. The api service signs proposals when VALIDATOR_PRIVATE_KEY + VALIDATOR_REGISTRY_<chain> + CHAMPION_REGISTRY_<chain> are configured; the validator set comes from on-chain ValidatorRegistry.getValidators() and the quorum threshold from ChampionRegistry.quorumBps() (both refreshed every 60s by ProtocolConfig.refresh_loop). Followers answer POST /v1/solver/round/consensus/proposal. Order consensus reads validator set + quorum from the same on-chain ValidatorRegistry on the order-execution chain (Base in production).
Operators may abort a round explicitly via POST /v1/solver/round/abort to retain the incumbent and reopen intake.
The leader and manual round-control endpoints now push explicit authenticated round state syncs (/v1/solver/round/internal/close, /v1/solver/round/internal/certify, /v1/solver/round/internal/activate, /v1/solver/round/internal/abort) so followers persist closed/certified/activated/aborted state before failover.
The coordinator now enforces decision_deadline_epoch: once the current solver-round epoch passes the deadline without a certificate, the round aborts and the incumbent stays active.
When metagraph sync can read native subnet tempo and BlocksSinceLastStep, solver-round epochs now follow the real subnet epoch/tempo rather than a local approximation.
/health now reports solver_round_role, the current solver_round_epoch, the active epoch-clock mode/config, champion-consensus status, internal round-auth presence, and metagraph leader metadata when available.
When hot-swap is enabled, champion runtime loading uses isolated Docker sessions (docker run harness protocol), not host-side Python imports.
Production Security Checklist
Recommended production posture:
- Disable source submissions and subprocess benchmarking.
- Require asymmetric signed provenance (no HMAC fallback).
- Require submission API key and non-zero rate limiting if submissions are enabled.
- Enable strict runtime profile validation so startup fails on insecure config.
- Verify
/healthreports bothprovenance_policy.valid=trueandruntime_security_policy.valid=true.
Example hardened env profile
# Strict runtime guardrails
ENFORCE_RUNTIME_SECURITY_PROFILE=1
# Submission intake controls
SUBMISSIONS_ACCEPTING=1
SUBMISSIONS_API_KEY=__set_strong_shared_secret__
SOLVER_ROUND_INTERNAL_API_KEY=__set_distinct_internal_shared_secret__
SUBMISSIONS_RATE_LIMIT_PER_MINUTE=60
ENABLE_SOURCE_SUBMISSIONS=0
ALLOW_SUBPROCESS_BENCHMARK=0
# Champion/runtime hardening
ENABLE_SOLVER_ROUND_COORDINATOR=1
SOLVER_ROUND_COORDINATOR_INTERVAL_SECONDS=5
SOLVER_ROUND_OPEN_SECONDS=300
SOLVER_ROUND_EPOCH_SECONDS=60
# Optional block-based epoch clock:
# SOLVER_ROUND_EPOCH_BLOCKS=360
SUBTENSOR_URL=ws://127.0.0.1:9944
NETUID=112
WALLET_NAME=validator
HOTKEY_NAME=default
# Optional if the API cannot read a local Bittensor wallet:
# VALIDATOR_HOTKEY_SS58=5....
VALIDATOR_PRIVATE_KEY=0x__this_validator_evm_key__
# Peers come from on-chain ValidatorRegistry + metagraph axon discovery —
# no VALIDATOR_PEERS env required.
VALIDATOR_REGISTRY_8453=0x__base_validator_registry__
VALIDATOR_REGISTRY_964=0x__btevm_validator_registry__
CHAMPION_REGISTRY_964=0x__btevm_champion_registry__
ALLOW_CHAMPION_HOT_SWAP=1
CHAMPION_SWAP_TIMEOUT_SECONDS=90
# Provenance policy (asymmetric-only)
REQUIRE_SIGNED_PROVENANCE=1
REQUIRE_ASYMMETRIC_PROVENANCE=1
SUBMISSION_PROVENANCE_SIGNING_PRIVATE_KEY=__set_validator_signing_key__
SUBMISSION_PROVENANCE_SIGNING_ADDRESS=__matching_signer_address__
SUBMISSION_PROVENANCE_ALLOWED_SIGNERS=__comma_separated_allowed_addresses__
SUBMISSION_PROVENANCE_HMAC_KEY=