diff --git a/.coderabbit.yaml b/.coderabbit.yaml
new file mode 100644
index 000000000..42a64f916
--- /dev/null
+++ b/.coderabbit.yaml
@@ -0,0 +1,56 @@
+# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
+language: en-US
+reviews:
+ profile: assertive
+ auto_review:
+ enabled: true
+ drafts: true
+ path_filters:
+ - "!docs/BUNDLE_SPEC.md"
+ - "!docs/external-miner/proof-tbench.md"
+ - "!uv.lock"
+ path_instructions:
+ - path: "**"
+ instructions: |
+ Cortex review rules.
+ Cortex is a Python Bittensor research subnet. The owner-signed trust root lists
+ the live challenges: legacy bounty/proof 2,000/8,000 (algorithm 1), 3,000/7,000
+ (algorithm 2, version >=2) or 1..64 unique ids (algorithm 3, version >=3). The
+ sum is always 10,000. Proof is built in; every other challenge is a Docker
+ container per docs/CHALLENGES.md. Design, Prism and Relearn are retired and removed.
+
+ - Preserve frozen SCALE encodings, Merkle construction, aggregation and every
+ `base-*-v1` signature preimage. Cross-language vectors must remain green.
+ Algorithm 2 counts each valid Bounty report once, distributes proportionally,
+ scales its 30% share by min(total_valid/10, 1), and burns unused/quarantined
+ mass. The total includes only the signed expected participant population.
+ Algorithm 3 pays each challenge share * min(sum(leaves), 10^12) / 10^12 and
+ burns the rest; unpaid mass never moves to another challenge.
+ Never allow an algorithm 1 body under the new profile or activate before
+ the owner-signed epoch; historical seals and journals stay immutable.
+ - Preserve existing `BASE_*` names and deployed compatibility paths. New master
+ settings may add the matching `CORTEX_*` alias but conflicting values fail.
+ - Missing or unknown image digests, offers, baselines, topics, keys, feeds and
+ VM evidence fail closed. Never add a guessed digest or a simulation fallback.
+ - Refused Proof intake does not reserve a nonce, create a row or start paid work
+ unless the documented durable acceptance point has been reached.
+ - Proof environment values are checked before signatures, stored only in the
+ private vault, injected only into paid jobs and removed at terminal state.
+ - A measured result scores only after topic/job/image/artifact binding and
+ confirmed dedicated VM teardown. Experiment guests have no network.
+ - Recursive children share the parent's hard call, token, tool, depth and wall
+ budgets. External side effects require durable intent and idempotent recovery.
+ - Shared observations are untrusted and private until owner-signed approval.
+ - A trusted challenge container that is unregistered, failing or returns invalid
+ weights yields explicit `ChallengeInternal` leaves; never a stale or guessed
+ score. Containers never receive a leaf seed; only `challenge-supervisor`
+ mounts the Docker socket; the proxy never forwards `internal/` paths.
+ - Validators independently fetch historical chain state, recompute exact u16
+ weights and refuse the unsealed UID0 fallback. A sealed UID0 burn is valid.
+ - Production images are digest-pinned. Secrets are private files and must never
+ enter Git, image layers, request/status output, fixtures or logs.
+ - CI remains offline: no OpenRouter call, Lium rent, Firecracker boot or chain
+ submission. Fake-boundary tests cannot be described as live evidence.
+ - API changes update the matching `docs/external-miner/` guide.
+ - Do not edit the byte-pinned frozen specification (docs/BUNDLE_SPEC.md). Do not weaken
+ `scripts/check_repo.py` to accept protocol drift.
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index efa8fa207..2211ac15a 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -17,10 +17,10 @@
- [ ] No challenge content, credential, floating production image, or invented digest was added
- [ ] Frozen protocol specifications and `BASE_*`/domain compatibility remain intact
-## Greptile
+## CodeRabbit
-- [ ] Greptile reviewed this PR and findings are resolved or answered
-- [ ] If the bot was silent, I commented `@greptileai review`
+- [ ] CodeRabbit reviewed this PR and findings are resolved or answered
+- [ ] If the bot was silent, I commented `@coderabbitai review`
## Risk
diff --git a/.greptile/config.json b/.greptile/config.json
deleted file mode 100644
index b14f60c01..000000000
--- a/.greptile/config.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "triggerOnDrafts": true,
- "autoReview": ["open", "push"],
- "statusCheck": true,
- "statusCommentsEnabled": true,
- "ignorePatterns": "docs/BUNDLE_SPEC.md\ndocs/DESIGN_CHALLENGE.md\ndocs/PRISM.md\ndocs/external-miner/relearn*.md\ndocs/external-miner/proof-tbench.md"
-}
diff --git a/.greptile/rules.md b/.greptile/rules.md
deleted file mode 100644
index 9f0ab318e..000000000
--- a/.greptile/rules.md
+++ /dev/null
@@ -1,43 +0,0 @@
-# Cortex review rules
-
-Cortex is a Python Bittensor research subnet. The owner-signed trust root lists
-the live challenges: legacy bounty/proof 2,000/8,000 (algorithm 1), 3,000/7,000
-(algorithm 2, version >=2) or 1..64 unique ids (algorithm 3, version >=3). The
-sum is always 10,000. Proof is built in; every other challenge is a Docker
-container per docs/CHALLENGES.md. Design, Prism and Relearn are historical only.
-
-- Preserve frozen SCALE encodings, Merkle construction, aggregation and every
- `base-*-v1` signature preimage. Cross-language vectors must remain green.
- Algorithm 2 counts each valid Bounty report once, distributes proportionally,
- scales its 30% share by min(total_valid/10, 1), and burns unused/quarantined
- mass. The total includes only the signed expected participant population.
- Algorithm 3 pays each challenge share * min(sum(leaves), 10^12) / 10^12 and
- burns the rest; unpaid mass never moves to another challenge.
- Never allow an algorithm 1 body under the new profile or activate before
- the owner-signed epoch; historical seals and journals stay immutable.
-- Preserve existing `BASE_*` names and deployed compatibility paths. New master
- settings may add the matching `CORTEX_*` alias but conflicting values fail.
-- Missing or unknown image digests, offers, baselines, topics, keys, feeds and
- VM evidence fail closed. Never add a guessed digest or a simulation fallback.
-- Refused Proof intake does not reserve a nonce, create a row or start paid work
- unless the documented durable acceptance point has been reached.
-- Proof environment values are checked before signatures, stored only in the
- private vault, injected only into paid jobs and removed at terminal state.
-- A measured result scores only after topic/job/image/artifact binding and
- confirmed dedicated VM teardown. Experiment guests have no network.
-- Recursive children share the parent's hard call, token, tool, depth and wall
- budgets. External side effects require durable intent and idempotent recovery.
-- Shared observations are untrusted and private until owner-signed approval.
-- A trusted challenge container that is unregistered, failing or returns invalid
- weights yields explicit `ChallengeInternal` leaves; never a stale or guessed
- score. Containers never receive a leaf seed; only `challenge-supervisor`
- mounts the Docker socket; the proxy never forwards `internal/` paths.
-- Validators independently fetch historical chain state, recompute exact u16
- weights and refuse the unsealed UID0 fallback. A sealed UID0 burn is valid.
-- Production images are digest-pinned. Secrets are private files and must never
- enter Git, image layers, request/status output, fixtures or logs.
-- CI remains offline: no OpenRouter call, Lium rent, Firecracker boot or chain
- submission. Fake-boundary tests cannot be described as live evidence.
-- API changes update the matching `docs/external-miner/` guide.
-- Do not edit the three byte-pinned frozen specifications. Do not weaken
- `scripts/check_repo.py` to accept protocol drift.
diff --git a/AGENTS.md b/AGENTS.md
index e36ea2e78..b9870e514 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -21,9 +21,8 @@ bounty/proof 2,000/8,000 profile; algorithm 2 is bounty/proof 3,000/7,000
(document version >=2); algorithm 3 (document version >=3) admits any 1..64
unique ids and pays each challenge `share * min(sum(leaves), 10^12) / 10^12`.
Activation is an offline owner ceremony. Design, Prism and Relearn are retired
-products. Their frozen specifications and historical miner pointers remain for
-compatibility; no active code, service, trust-root row or leaf may register
-them.
+products and their documentation has been removed. No active code, service,
+trust-root row or leaf may register them.
Start with [the architecture](docs/ARCHITECTURE.md),
[Proof](docs/PROOF.md), [challenge containers](docs/CHALLENGES.md) and
@@ -51,8 +50,7 @@ The working branch is `main`. Production releases are annotated `v*.*.*` tags.
- Preserve `BASE_*` names, deployed compatibility paths and every `base-*-v1`
signature domain. `CORTEX_*` is an accepted master alias only.
-- Do not edit `docs/BUNDLE_SPEC.md`, `docs/DESIGN_CHALLENGE.md` or
- `docs/PRISM.md`. Their exact SHA-256 values are checked by
+- Do not edit `docs/BUNDLE_SPEC.md`. Its exact SHA-256 value is checked by
`scripts/check_repo.py`.
- Production image references are digest-only. Never invent a digest; an empty
or unknown pin remains fail-closed.
@@ -186,7 +184,7 @@ signed leaves, gateway seal, a `sealed: true` fetch and validator recomputation.
Health endpoints alone do not verify a challenge. CI must not contact OpenRouter,
rent provider compute, boot Firecracker or submit an on-chain transaction.
-Pull requests require a Greptile review. If silent, comment `@greptileai review`.
+Pull requests require a CodeRabbit review. If silent, comment `@coderabbitai review`.
Commit subjects use `type(scope): lowercase summary`, at most 72 characters.
## Never commit
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 8469cb1f9..46347db5c 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -41,7 +41,7 @@ SQLite, signatures, RLM state, scoring, sealing, and validator verification.
## Pull requests
-Target `main`, fill in the pull request template, and request a Greptile review.
+Target `main`, fill in the pull request template, and request a CodeRabbit review.
Use Conventional Commit subjects no longer than 72 characters:
```text
diff --git a/README.md b/README.md
index a95491317..665a4022c 100644
--- a/README.md
+++ b/README.md
@@ -1,30 +1,130 @@
-# Cortex
+
Cortex
-Cortex research subnet on Bittensor: agentic Proof plus Docker challenge containers.
+
+
+
-The master runs the gateway, Proof and every challenge container, then signs one
-leaf per miner and seals each epoch. Validators only verify the sealed bundle
-from the gateway API and submit the weights.
+
+ An autonomous research network on Bittensor subnet 100. Miners compete in challenges, a master seals each epoch into one signed bundle, and validators verify that bundle and submit the weights on-chain.
+
+
+
+
+
+
+
+
+
+
+
+ Documentation ·
+ Website ·
+ Validator guide ·
+ Miner guides ·
+ Architecture ·
+ Challenges ·
+ Threat model ·
+ License
+
+
+> [!WARNING]
+> **Research software.** Cortex is experimental. Interfaces, wire formats and emission shares can change. Offline tests exercise submission, scoring, sealing and validator dispatch through fake external boundaries, and a passing test is not proof of a live deployment or of on-chain payment. Don't use it to manage funds you can't afford to lose.
+
+---
+
+## What is Cortex
+
+Cortex is the subnet that runs research competitions. Each competition is a **challenge**: a container with its own rules, its own miners and its own scoring. Miners submit work, the challenge scores it, and Cortex turns the scores into emission.
+
+Two roles keep that honest:
+
+- The **master** runs the gateway, Proof and every challenge container. At the end of each epoch it signs one leaf per miner and seals the epoch into a bundle.
+- A **validator** runs none of that. It downloads the sealed bundle, checks it against locally held trust files and the chain, and submits the weights.
+
+An owner-signed trust root sets each challenge's emission share. If a challenge earns less than its share, the shortfall burns to UID 0. It never moves to another challenge.
+
+Challenges today:
+
+| Challenge | What miners do | Repository |
+| --- | --- | --- |
+| `bounty` | Report vulnerabilities in products | [CortexLM/bounty](https://github.com/CortexLM/bounty) |
+| `hypertrain` | Decentralized, verifiable LLM pretraining (research stage) | [CortexLM/hypertrain](https://github.com/CortexLM/hypertrain) |
+
+A further challenge, Sentinel, is planned and is not live. See [docs/ecosystem.md](docs/ecosystem.md).
+
+## The Cortex ecosystem
+
+
+
+
+
+Cortex is the engine behind a family of products. Miners improve models and detectors through challenges, and the apps are where those improvements are meant to ship.
+
+| App | What the challenges give it | Status |
+| --- | --- | --- |
+| Cortex Chat | Models from Hypertrain | Hypertrain is research stage; monetization is PLANNED |
+| Cortex Decisions | Models from Hypertrain | Hypertrain is research stage; monetization is PLANNED |
+| Cortex Security Cloud | Vulnerability findings from Bounty today; Sentinel is PLANNED to improve it | Sentinel is PLANNED, not live |
+| Cortex Code | Models from Hypertrain | Hypertrain is research stage; monetization is PLANNED |
+| Cortex Bot | Models from Hypertrain | Hypertrain is research stage; monetization is PLANNED |
+
+Sentinel is planned as a miner-improved competitor to CodeRabbit and Greptile, positioned inside Cortex Security Cloud. None of it exists yet. Monetizing Hypertrain models in the apps is also a plan, not a shipped feature. Details are in [docs/ecosystem.md](docs/ecosystem.md).
+
+## How it works
+
+### 1. One epoch
+
+```mermaid
+flowchart LR
+ M["Miner"] --> C["Challenge container"] --> L["Signed leaf"] --> G["Gateway seals epoch"] --> V["Validators verify"] --> N["Weights on chain"]
+```
+
+A miner submits work to a challenge. The challenge scores it and the master signs a leaf for that miner. When the epoch ends, the gateway seals all leaves into one bundle. Validators fetch the bundle from `GET https://chain.joinbase.ai/v1/weights/latest`, recompute the weights from the signed leaves and submit them to Bittensor.
+
+### 2. Who runs what
+
+```mermaid
+flowchart LR
+ subgraph Master["Master operator machine"]
+ GW["Gateway"]
+ P["Proof"]
+ CH["Challenge containers"]
+ SUP["Challenge supervisor"]
+ end
+ subgraph Val["Any validator"]
+ VAL["cortex validator"]
+ end
+ GW -->|"sealed bundle"| VAL
+ VAL -->|"weights"| CHAIN["Bittensor"]
+```
| Role | Runs | Command |
| --- | --- | --- |
-| validator master | gateway, Proof, [challenge containers](docs/CHALLENGES.md), auto-updater | `cortex master` + `cortex challenge-supervisor` |
+| master | gateway, Proof, [challenge containers](docs/CHALLENGES.md), auto-updater | `cortex master` and `cortex challenge-supervisor` |
| validator | verification and weight submission, nothing else | `cortex validator` |
-Challenges live in their own repositories and are loaded automatically:
-[CortexLM/bounty](https://github.com/CortexLM/bounty) (vulnerability reports) and
-[OpentypeAI/challenge](https://github.com/OpentypeAI/challenge) (exact-gold
-DiffusionGemma duels). The owner-signed trust root sets each challenge's emission
-share. The shortfall of a challenge burns to UID 0 and never moves to another one.
+Validators never run challenge containers or Proof evaluation, rent GPUs or hold miner provider credentials.
+
+## Run a validator in one command
+
+The master runs only on the master operator's machine. Any operator with a registered hotkey can run a validator, and a script does the setup. Miners use the miner CLI instead.
+
+```bash
+sudo apt-get install libsodium23
+uv sync --locked --extra chain
+
+export WALLET_NAME=validator WALLET_HOTKEY=default
+export GATEWAY_PUBLIC=
+
+scripts/run-validator.sh --verify-only # one verification, never submits
+scripts/run-validator.sh # verify and submit every epoch
+```
-Cortex is experimental research software. Offline tests exercise submission, scoring,
-sealing and validator dispatch through fake external boundaries. A live model
-smoke is distinct from live KVM execution or confirmed on-chain payment.
+The script checks the gateway answer, reads the trust files from `config/` and starts `cortex validator`. It refuses master-only commands and master environments. Flags are `--verify-only`, `--once`, `--dry-run` and `--help`. Full walkthrough: [docs/validator-quickstart.md](docs/validator-quickstart.md).
-## Installation
+## Quickstart for developers
-Linux, Python 3.12 or 3.13, `uv`, and libsodium 1.0.18 or newer are required.
-The VM host additionally requires working `/dev/kvm`, Firecracker and jailer.
+Linux, Python 3.12 or 3.13, `uv`, and libsodium 1.0.18 or newer are required. The VM host also needs working `/dev/kvm`, Firecracker and jailer.
```bash
sudo apt-get install libsodium23
@@ -32,7 +132,7 @@ uv sync --locked --extra chain --group build
uv run cortex --help
```
-## Usage
+Other entry points:
```bash
uv run cortex master --help
@@ -43,22 +143,7 @@ uv run cortex miner --help
uv run cortex topic-create --help
```
-Configure the master using private credential files and signed trust roots.
-An operator gives Proof a research objective; its topic agent prepares an
-experiment, measures a baseline and proposes rules and miner documentation.
-A topic opens only after the control plane verifies the host's execution
-receipts and signs the resulting document. No research task catalog is built in.
-
-- [Operator configuration](docs/reference/configuration.md)
-- [Architecture and trust boundaries](docs/ARCHITECTURE.md)
-- [Challenge container contract](docs/CHALLENGES.md)
-- [Deployment](deploy/README.md)
-- [Proof miner guide](docs/external-miner/proof.md)
-- [Bounty miner guide](docs/external-miner/bounty.md)
-- [Validator guide](docs/external-miner/validators.md)
-- [Documentation index](docs/index.md)
-
-## Development
+Checks:
```bash
uv run ruff format --check src tests scripts
@@ -69,16 +154,52 @@ uv run python scripts/check_deploy.py --check-examples
uv build --no-build-isolation
```
-Tests cover signatures and Rust wire vectors, replay protection, challenge
-container outages, auto-update rollback, artifact validation, topic setup, rejected submissions, VM lifecycle failures,
-RLM recursion/compaction, reward allocation and sealed-weight submission.
-CI runs offline and never rents a GPU or boots Firecracker.
+Tests cover signatures and Rust wire vectors, replay protection, challenge container outages, auto-update rollback, artifact validation, topic setup, rejected submissions, VM lifecycle failures, RLM recursion and compaction, reward allocation and sealed-weight submission. CI runs offline and never rents a GPU or boots Firecracker.
+
+## Repository layout
+
+```
+src/ Python package: gateway, master, validator, miner CLI, Proof, VM host
+config/ trust roots and measurements (owner-signed)
+deploy/ master, validator and KVM host deployment
+docs/ architecture, challenges, guides, specifications
+scripts/ validator script, repository and deployment checks, smoke tests
+tests/ offline test suite
+```
+
+## Components
+
+| Component | Who runs it | What it does |
+| --- | --- | --- |
+| Gateway | Master | Serves sealed weights and bundles at `/v1/weights/latest`, `/v1/weights/{epoch}` and `/v1/bundle/{epoch}` |
+| Proof | Master | Opens operator-defined research topics, runs the topic agent and evaluates submissions in VMs |
+| Challenge supervisor | Master | Loads challenge containers from the registry and applies verified updates |
+| VM host | Dedicated KVM machine | Runs Firecracker guests for Proof evaluation and signs execution receipts |
+| Validator | Anyone with a registered hotkey | Verifies the sealed bundle and submits weights |
+| Miner CLI | Miners | Discovers topics and submits signed artifacts |
+
+## Security model and limitations
+
+What the design relies on:
+
+- The master signs one leaf per miner and seals each epoch. Validators recompute weights from the signed leaves and never trust display fields in the gateway response.
+- Validators pin the gateway key and the owner key themselves. Trust files come from a local copy, not from the gateway.
+- Challenge shares come from the owner-signed trust root. Unused share burns to UID 0.
+- A Proof topic opens only after the control plane verifies the host's execution receipts and signs the document.
+
+What isn't done, honestly:
+
+- **The gateway is a central authority.** Validators verify what the master signed. They don't reproduce Proof or challenge scoring themselves. Peer cross-checking between validators is opt-in and off by default.
+- **Live status is unproven.** Offline tests don't show live KVM execution, a live model run or confirmed on-chain payment.
+- **Hypertrain is research.** Quality parity with centralized training is not claimed.
+- **Sentinel is not built.** The same goes for monetization in the apps.
+- **The validator CLI needs a consensus seed.** See the [validator guide](docs/external-miner/validators.md) for the current limit.
+
+Read the [threat model](docs/THREAT_MODEL.md) and [operator security](docs/OPERATOR_SECURITY.md) before running a master.
## Contributing
-Keep research content out of source control. Add tests for observable behavior
-at service boundaries, and update the matching miner documentation when an API
-changes. Follow [the agent contract](AGENTS.md) and the PR review requirements.
+Keep research content out of source control. Add tests for observable behavior at service boundaries, and update the matching miner documentation when an API changes. Follow [the agent contract](AGENTS.md) and the PR review requirements in [CONTRIBUTING.md](CONTRIBUTING.md).
## License
diff --git a/deploy/README.md b/deploy/README.md
index 53c94afa0..dc138fafe 100644
--- a/deploy/README.md
+++ b/deploy/README.md
@@ -192,6 +192,12 @@ reports `validator outcome=verified` for the same root/vector from historical
chain state. The unsealed UID0 fallback is a readiness failure, not a weight to
submit.
+To run a validator without Compose, use `scripts/run-validator.sh` from a
+repository clone. It needs `WALLET_NAME`, `WALLET_HOTKEY` and an independently
+pinned `GATEWAY_PUBLIC`, takes trust files from `config/`, and refuses to run
+master-only commands; see the
+[quick start](../docs/external-miner/validators.md#quick-start-one-script).
+
## Proof VM host
The host requires Linux, `/dev/kvm`, Firecracker, jailer, nftables/network
diff --git a/docs/AGENTS.md b/docs/AGENTS.md
index 0b15ec915..92a52120a 100644
--- a/docs/AGENTS.md
+++ b/docs/AGENTS.md
@@ -23,10 +23,10 @@ of copying the same contract into a second runbook.
## Frozen and historical material
-`BUNDLE_SPEC.md`, `DESIGN_CHALLENGE.md` and `PRISM.md` are byte-pinned by
-`scripts/check_repo.py`. Do not edit them. Design, Prism and Relearn are not live
-products. The Relearn files under `external-miner/` and `proof-tbench.md` remain
-short historical pointers so old URLs do not disappear.
+`BUNDLE_SPEC.md` is byte-pinned by `scripts/check_repo.py`. Do not edit it.
+Design, Prism and Relearn are retired products and their documentation has been
+removed. `external-miner/proof-tbench.md` remains a short historical pointer so
+an old URL does not disappear.
## API changes
diff --git a/docs/DESIGN_CHALLENGE.md b/docs/DESIGN_CHALLENGE.md
deleted file mode 100644
index b945b37c1..000000000
--- a/docs/DESIGN_CHALLENGE.md
+++ /dev/null
@@ -1,633 +0,0 @@
-# Design Challenge (Base)
-
-**Status: FROZEN** for `challenge_id = "design"`, `challenge_scoring_version` **u16 = 2**.
-
-Normative contract for the design challenge on Base. Byte-level epoch bundle
-rules live in [`BUNDLE_SPEC.md`](./BUNDLE_SPEC.md) (`protocol_version = 1`).
-Pin map for CI: [`DESIGN_CHALLENGE_CHECKLIST.md`](./DESIGN_CHALLENGE_CHECKLIST.md).
-Gate: `cargo run -p xtask -- design-check`.
-
-This challenge **replaces** agent-v1 (Phala CVM) and hypertraining. Miners submit
-Python harness source over HTTP — no miner-provided Docker image, no Phala/CVM path.
-
----
-
-## 1. What runs where (topology)
-
-```text
-Miner --POST /v1/harness--> design-challenge (:8093)
- |
- round clock (10/day UTC) + quota
- |
- +-------------v--------------+
- | design-sandbox (two phase) |
- | install → run |
- | image: design-runtime |
- | net: design-sandbox-egress|
- +------+---------------------+
- | HTTP only
- +------v---------------------+
- | design-egress-proxy |
- | open egress, internal |
- | blocklist; OpenRouter key |
- +----------------------------+
- |
- /out/pages/*.html
- |
- design-sanitize → store (postgres)
- |
- +----------------+----------------+
- | |
- viewer (index.png agentic review → admin winners (1|2)
- screenshots only; |
- HTML never served) exact-E leaves → gateway /v1/weights/raw
-```
-
-| Process | Host | Holds `design_sk`? | Holds OpenRouter key? |
-|---------|------|--------------------|------------------------|
-| `design-challenge` | **master only** | **yes** (file mount) | **yes** (agentic review; optional Sim) |
-| `design-egress-proxy` | **master only** | no | **yes** (sandbox LLM path) |
-| sandbox container | master (ephemeral) | **never** | **never** |
-| gateway | master | no | no |
-| validator | validators | no | no — **no challenge exec**; fetch sealed weights only |
-
-Evaluation (sandbox, sanitize, `AgenticReview`, admin winners, leaf emit) is
-**master-only**. Validators never run design-challenge / egress / socket-proxy.
-
-Sandbox containers attach only to the internal Docker network
-`design-sandbox-egress`. The sole reachable peer is `design-egress-proxy`.
-
----
-
-## 2. Identifiers and versions
-
-| Field | Value |
-|-------|-------|
-| `challenge_id` | `design` |
-| `challenge_scoring_version` | **u16 = 3** |
-| `SCORE_MAX` | `1_000_000` |
-| Listen port | `8093` (local overlay `28093`) |
-| Gateway proxy prefix | `/challenge/design/*` |
-| Bundle `protocol_version` | `1` ([`BUNDLE_SPEC.md`](./BUNDLE_SPEC.md)) |
-| `emission_share_bps` | **0** (prism 100%; sum `10000`) |
-| Policy | `all_metagraph_hotkeys` |
-| Round length | `ROUND_SECS = 8_640` (10 rounds / UTC day) |
-| Agent run timeout | `AGENT_RUN_TIMEOUT_SECS = 1_800` (30 min; distinct from round length) |
-| Scoring window | `SCORING_WINDOW_ROUNDS = 10` (rolling round-win points share) |
-| `round_id` | `floor(unix_secs / 8640)` |
-| Domain `round_id` | `base-design-round-id-v1` |
-| Domain submission | `base-design-submission-v1` |
-| Domain pair | `base-design-pair-id-v1` |
-| Domain run | `base-design-run-id-v1` |
-| Raw-weight domain | `base-rawweight-v1` (via bundle) |
-
-Staging knobs (defaults above are the prod pins and never change implicitly):
-`DESIGN_ROUND_SECS`, `DESIGN_AGENT_RUN_TIMEOUT_SECS`, `DESIGN_PROMPTS_PER_ROUND`
-override round length / run timeout / prompts per round (staging uses ~15-minute
-rounds; `env-staging.yml`).
-
-Emission posture: `emission_share_bps = 0` for design and `10000` for prism
-(100% prism; sum `10000`). Rebalance via the owner ceremony in
-[`runbooks/design-enable-and-emission.md`](./runbooks/design-enable-and-emission.md)
-and [`config/challenges.toml`](../config/challenges.toml).
-
----
-
-## 3. Miner harness contract
-
-Miners submit **source**, not images. Preferred transport is **ZIP**
-(`Content-Type: application/zip` + `X-Miner-Hotkey`, or JSON `zip_base64`).
-JSON with inline `agent_py` / `pyproject_toml` remains accepted for local/CI.
-
-Optional `env_vars` (JSON map or `X-Env-Json` on ZIP) are injected into the
-sandbox **run** phase only. Keys must match `[A-Z][A-Z0-9_]*`; operator /
-proxy prefixes (`DESIGN_`, `HTTP_`, `PYTHON…`, …) are rejected. Values are
-**never logged**.
-
-### Bundle
-
-| File | Rule |
-|------|------|
-| `agent.py` | Must define `def run(task, llm, out) -> None` |
-| `pyproject.toml` | Required; deps installed in sandbox install phase |
-| Extra files | ≤ 16 files, ≤ 256 KiB each, total bundle ≤ 1 MiB |
-
-`harness_id = sha256(base-design-submission-v1 || hotkey || agent || pyproject || extras || env)`.
-`POST /v1/harness` is idempotent on that digest.
-
-### Submission gating (1-max) + auto round enqueue
-
-- The miner hotkey must be **in the metagraph** (bulk cached snapshot, **15m**
- TTL fail-closed; no per-UID RPC on the request path). Unknown hotkey →
- `403 hotkey_not_in_metagraph`; snapshot missing/stale →
- `503 metagraph_unavailable`.
-- **One accepted submission per `(challenge, hotkey)`** (`submission_gating`
- table). While the row is `registered` / `blocked` / `rejected`, a *different*
- harness from the same hotkey → `409 submission_gated`. The identical re-POST
- stays idempotent (`200 already-queued`).
-- **Auto round enqueue**: `POST /v1/harness` schedules the harness into
- `round_id(now) + 1` ([`RunOrigin::Manual`]). On every subsequent open round
- the orchestrator auto-enqueues the **latest active harness per hotkey** into
- that round with the round's shared prompt ([`RunOrigin::Scheduled`]) —
- idempotent if already queued. Eliminated harnesses
- (`eliminated_until_round > rid`) are skipped. Infra auto-retry on the *same*
- run id (up to 3) is not a new schedule. Ops may still
- `POST /v1/admin/rounds/current/requeue` (same enqueue path).
-- **Auto-retry**: infra-class failures (`install`, `ast_infra`, `llm_infra`)
- requeue automatically up to **3** times; `cheat` / `rejected` / admin reject /
- unscored timeout are terminal. Budget exhaustion → `failed` + gating
- `blocked`. Manual `POST /v1/runs/{id}/retry` is unchanged.
-- A metagraph **watcher** reopens eligibility (`open`) when the hotkey leaves
- the metagraph (uid deregistered or hotkey replaced), so the **same hotkey**
- may resubmit under a **new uid**. The hotkey is never permanently burned.
-- Miner env (`env_vars`) is **locked at submission** into the stored bundle;
- changing it requires a new digest (and a free gating slot).
-
-### Required output (`/out/pages/`)
-
-- `index.html`
-- `pricing.html`
-- `components.html`
-- `manifest.json` (present in operator harness layout; pages gated by sanitize)
-
-### Injected SDK (`base_design`, not miner-modifiable)
-
-- `task.prompt`, `task.round_id`, `task.pages_required`, `task.budget`
-- `llm.chat(messages, model)` → HTTP to egress proxy (no key in harness)
-- `out.write_page(name, html)`, `out.write_asset(name, bytes)` (size-capped)
-
-Operator entrypoint `design_harness.py` loads miner `agent.py` after install.
-
----
-
-## 4. Sandbox hardening
-
-Two phases on pinned `design-runtime`:
-
-1. **install** — `pip install --no-cache-dir -e /work` into a work-root venv,
- resolving `pyproject.toml` deps from PyPI via the egress proxy (open
- Internet; internal-target blocklist). Miner env is **not** injected in this
- phase. Timeout `DESIGN_INSTALL_TIMEOUT_SECS` (default 300s); non-zero exit
- or timeout → error class `install` (auto-retry ≤ 3, then `failed` with
- `NoScore(ChallengeInternal)`).
-2. **run** — same work root; open egress via the proxy for external APIs /
- MCP servers (same blocklist); `llm.chat` rides the budgeted `/v1/chat`
- path; miner env injected; loads `design_harness.py`.
-
-The egress proxy blocklist refuses cloud metadata (`169.254.169.254`),
-loopback, RFC1918/VPC (`10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`),
-CGNAT (`100.64.0.0/10`), and control-plane service names, enforced **after
-DNS resolution** (DNS-rebinding safe).
-
-`docker-engine` `RunSpec` hardening for design (prefix `base-design-`):
-
-- `ReadonlyRootfs: true` + bounded tmpfs `/tmp`
-- `CapDrop: ["ALL"]`
-- `SecurityOpt: ["no-new-privileges:true"]`
-- `PidsLimit`, `Memory`, `MemorySwap`, `NanoCpus` set
-- `NetworkMode: design-sandbox-egress` (pre-created; socket-proxy denies Networks API)
-- `User: 65532:65532`
-- Wall-clock timeout → stop/rm
-
-Host `SimSandbox` is fail-closed outside explicit non-prod/CI opt-in
-(`BASE_ALLOW_HOST_SIM=1` + non-prod, typically via `env-local.yml` or e2e).
-Staging/prod paths are Docker-only via `socket-proxy` — no silent fallback.
-
-Floating tags (`:latest`) are **forbidden** for `design-runtime` / challenge images in prod pins — digest-only.
-
----
-
-## 5. Sanitize rules
-
-Ingestion via `design-sanitize` (ammonia + CSS filter). **Produced HTML is
-never served** — sanitized pages are orchestrator input only (screenshot
-capture, anti-cheat review); the public viewer serves PNG screenshots
-only (§6).
-
-### Stripped / rejected
-
-- Tags: `script`, `iframe`, `object`, `embed`, `applet`, `base`, `form`, `meta[http-equiv=refresh]`, `link[rel=import]`, scriptable SVG
-- All `on*` event attributes
-- URL schemes: reject `javascript:`, `vbscript:`, `data:text/html`; allow `http`, `https`, `mailto`, `data:image/*`
-- CSS: soft-strip `@import …;` rules (remainder of the `