# Affine (Bittensor SN120) > King-of-the-hill subnet. Miners submit HF checkpoints; the validator crowns the reigning king by a teacher-anchored distillation score S*, not an LLM judge. This file is the miner index: submit path, public contract, and links to the exact scoring code the network runs. Machine-readable knobs (subset of the contract below) also ship as `data/contract.json` on this site. When in doubt, trust the linked sources under `code/` (republished from the validator's own tree on every site push) over any prose summary. Full source repository: [github.com/AffineFoundation/affine](https://github.com/AffineFoundation/affine) — validator, eval server, and this site. The `code/` mirror below is the load-bearing subset, republished on every site push. --- ## Table of contents Everything the project publishes, in one index. All URLs are on this site's root (https://s3.hippius.com/affine-sn120/). **In this file (read on)** - How the game works — rules, duel flow, emissions - Submit checklist — HF layout, repo naming, commit-reveal payload - Serving stack — how your checkpoint is loaded; pre-flight before you burn the slot - S* v2 — the gates and ranking term you optimize - Public data — full field-level description of every published object - Source of truth — links to the exact validator code under `code/` **Code** (verbatim copies of the validator's own tree, republished on every site push — always current) - [code/affine.toml](https://s3.hippius.com/affine-sn120/code/affine.toml) — chain contract SSOT — every frozen knob the validator runs - [code/affine/score.py](https://s3.hippius.com/affine-sn120/code/affine/score.py) — S* v2: gates, ranking term, duel decision — the scoring code - [code/scripts/submit.py](https://s3.hippius.com/affine-sn120/code/scripts/submit.py) — standalone commit-reveal submission client — this single file is the whole submit path (trust it over any prose) - [code/affine/priors.py](https://s3.hippius.com/affine-sn120/code/affine/priors.py) — published prior bank behind the bank gate - [code/affine/chain.py](https://s3.hippius.com/affine-sn120/code/affine/chain.py) — reveal payload contract + commit builders - [code/evalsrv/dueling.py](https://s3.hippius.com/affine-sn120/code/evalsrv/dueling.py) — live duel: slice seeding, injectability probe, scoring loop - [code/evalsrv/chat.py](https://s3.hippius.com/affine-sn120/code/evalsrv/chat.py) — the chat contract: prompt assembly, thought-injection template, z/y rollout parsing — byte-exact - [code/evalsrv/terms.py](https://s3.hippius.com/affine-sn120/code/evalsrv/terms.py) — per-turn instrumentation: teacher references + the ten forced-logprob calls behind every lp* component - [code/evalsrv/vllm_client.py](https://s3.hippius.com/affine-sn120/code/evalsrv/vllm_client.py) — vLLM sampling + echo/logprob forcing + per-byte normalization (lp_per_byte) - [code/evalsrv/engine.py](https://s3.hippius.com/affine-sn120/code/evalsrv/engine.py) — slot lifecycle + the exact `vllm serve` invocation your checkpoint is loaded with (_vllm_cmd) - [code/pyproject.toml](https://s3.hippius.com/affine-sn120/code/pyproject.toml) — eval-pod dependency floors ([eval] extra) — vllm / transformers are installed fresh at pod provision - [code/affine/model_store.py](https://s3.hippius.com/affine-sn120/code/affine/model_store.py) — checkpoint hygiene rules + weight-copy detection - [code/affine/state.py](https://s3.hippius.com/affine-sn120/code/affine/state.py) — 1-hotkey-1-eval policy, king lineage, queue invariants **Validator logs** (separate file, not inlined here) - [data/validator_log.txt](https://s3.hippius.com/affine-sn120/data/validator_log.txt) — redacted tail of the live control-plane log, refreshed ~every minute **Eval results** (full duel records — the training data) - [evals/index.jsonl](https://s3.hippius.com/affine-sn120/evals/index.jsonl) — append-only manifest, one line per duel - `evals/{challenge_id}.json.gz` — everything computed during a duel: rollouts, teacher refs, every forced logprob **Live dashboard API** (hot path on the validator box — prefer this for UI) Root: https://affine.io/ - [api/v1/snapshot](https://affine.io/api/v1/snapshot) — king, reign, intake, duel queue, live eval - [api/v1/history](https://affine.io/api/v1/history) — filterable verdicts (`?q=&event=`) - [api/v1/benchmarks](https://affine.io/api/v1/benchmarks) — advisory benches - [api/v1/contract](https://affine.io/api/v1/contract) — machine-readable knobs - `api/v1/duels/{challenge_id}` — duel detail (gates, S*, rejection) - `api/v1/duels/{challenge_id}/series` — chart-safe per-turn Λ2/L1lift - [api/v1/stream](https://affine.io/api/v1/stream) — SSE snapshot deltas - [index.html](https://affine.io/) — interactive dashboard UI **Hippius archive** (cold public mirror — miners / replay; same objects) - [data/dashboard.json](https://s3.hippius.com/affine-sn120/data/dashboard.json) — king, reign chain, intake, duel queue, live eval progress - [data/history.json](https://s3.hippius.com/affine-sn120/data/history.json) — last 100 verdicts/failures - [data/benchmarks.json](https://s3.hippius.com/affine-sn120/data/benchmarks.json) — advisory tau2 scores (never part of S*) - [data/contract.json](https://s3.hippius.com/affine-sn120/data/contract.json) — machine-readable contract knobs **Complete audit logs** (gzipped JSONL, on Hippius) - [data/history_full.jsonl.gz](https://s3.hippius.com/affine-sn120/data/history_full.jsonl.gz) — every verdict and failure since genesis - [data/bench_history_full.jsonl.gz](https://s3.hippius.com/affine-sn120/data/bench_history_full.jsonl.gz) — every completed bench run **Turn corpus D** (the prompts) - [turns/manifest.json](https://s3.hippius.com/affine-sn120/turns/manifest.json) — current manifest: shards, hashes, corpus epoch - `turns/shards/*.jsonl.gz` — immutable shard objects - `turns/manifests/{sha256}.json` — every manifest revision ever, immutable **Website / index** - [llms.txt](https://s3.hippius.com/affine-sn120/llms.txt) — this file (also served from https://affine.io/llms.txt) --- ## How the game works 1. A frozen teacher C (`zai-org/GLM-4.5-Air-FP8`) and a public turn corpus D (sharded + manifest-pinned on this site under `turns/`) define the capability axis (SWE-style coding). 2. You commit-reveal an HF checkpoint pinned to a 40-hex git revision. 3. The validator burns your hotkey's **one eval slot at enqueue** (not at verdict). Failed hygiene, failed probe, or lost duel still burns the slot. 4. Eval machine runs a duel on an `n_turns` slice of D seeded by `blake2b(reveal_block_hash ‖ your_hotkey)` — you cannot know the slice before reveal; anyone can re-derive it after. 5. Both sides are scored with S* v2 (gates + ranking). Challenger dethrones the king iff both are gate-valid AND `mean(S_c − S_k) > 3·SE` AND `mean > min_margin` (SE floored by `min_se`). 6. Emissions go to the rolling last-`king_chain_size` distinct kings, equal share — **registered hotkeys only** (see step 0 of the submit checklist). Advisory tau2 benches never affect S* or crowning. There is no validator-private data. Replayability is the trust model: two checkpoints + public D + `affine/score.py` → recompute the verdict. --- ## Submit checklist (do this) **Step 0 — wallet + registration.** Everything below assumes a Bittensor wallet: `btcli wallet create` makes the coldkey + hotkey pair, and `btcli subnet register --netuid 120 --wallet YOUR_WALLET --hotkey YOUR_HOTKEY` registers the hotkey on this subnet (dynamic burn cost — check `btcli subnet burn-cost 120`, or `reg_cost_tao` in `api/v1/snapshot`). Registration is what maps your hotkey to a UID, and weights can only be set on UIDs: **an unregistered hotkey earns nothing, even if it wins the crown**. The validator re-reads the metagraph every weight cycle and silently skips unregistered reign members (`set_rolling_weights` in `code/affine/chain.py`), so registering late only costs you the emission cycles you already missed — but register before you submit anyway. If your hotkey is ever pruned from the metagraph, re-register to resume earning: your place in the reign chain is tracked by hotkey and survives deregistration. 1. Train / distill a coding model that emits closed bash-fenced actions and usable thoughts under the Affine chat contract (see probe below). 2. Push weights to Hugging Face as safetensors in canonical layout (`model.safetensors` **or** sharded `model-XXXXX-of-YYYYY.safetensors` + `model.safetensors.index.json`). No `*.py`. No `auto_map` in `config.json`. Safetensors ≤ 90 GB; whole repo ≤ 100 GB; ≤ 5000 files; `config.json` ≤ 1 MiB. 3. Repo id must match `^[^/]+/[Aa]ffine-.+$` **and** embed your identity: the first 5 AND last 5 chars (lowercase) of your coldkey **or** hotkey ss58 must both appear in the repo id — the compact token or the full ss58 both work. Example: `you/Affine-{token}-mymodel`. 4. Pin a 40-hex revision (never a moving branch tip). 5. Submit with the standalone client — one file, no package install beyond `pip install "bittensor>=11,<12" huggingface_hub` (the script uses the bittensor 11 SDK: `bt.timelock` + raw `Commitments.set_commitment`). The client **pre-flights every intake check the validator runs** (naming + identity, anonymous readability of the pinned revision, safetensors layout, no `*.py` / no `auto_map`, size caps) and refuses to send a submission that would burn your slot at intake. Add `--check` to validate and print the payload without submitting anything: ```bash curl -O https://s3.hippius.com/affine-sn120/code/scripts/submit.py python submit.py --repo you/Affine-{token}-mymodel \ --wallet YOUR_WALLET --hotkey YOUR_HOTKEY [--revision <40hex>] --check # happy with the pre-flight output? drop --check to submit for real python submit.py --repo you/Affine-{token}-mymodel \ --wallet YOUR_WALLET --hotkey YOUR_HOTKEY [--revision <40hex>] ``` (or clone [github.com/AffineFoundation/affine](https://github.com/AffineFoundation/affine) and run `affine/scripts/submit.py`) 6. Payload committed on-chain: ``` affine1||| ``` Live path uses bittensor 11 timelock encrypt (`reveal_in="60s"`) → `Commitments.set_commitment` — **trust `scripts/submit.py`** over any prose. **Commit ≠ duel-queue row.** `LastCommitment` alone is encrypted and not a dashboard row. After ~60s the payload must land in `RevealedCommitments`; only then does the validator run **intake**. Intake may enqueue a duel slot, skip, or reject — see dashboard **intake** (reason) → **duel queue** (eval slots only) → **fails** / history. Lifetime `stats.queued` / `enqueued_total` is not "your commit is waiting." 7. Wait ~1 minute for reveal, then check the dashboard in order: **intake → duel queue → fails**. Do not expect a queue row from commit alone. Common intake outcomes: - `enqueued` — you have a duel-queue challenge id - `skipped_min_block` — reveal block ≤ `min_submission_block` (ignored) - `skipped_slot_burned` — this hotkey already burned its one eval slot - `skipped_king` / `skipped_repo_queued` — already crowned or same repo waiting - `rejected_*` — bad payload / revision already submitted / etc. (see fails) **Hard policies** - One submission per hotkey, ever. Slot burned at enqueue (prior enqueue ⇒ no new duel-queue row). - A content revision that was ever submitted can never be resubmitted, by anyone. - Weight-identical copy of the current king → reject, unless your HF commit timestamp is earlier than the king's → `crown_earlier` without a duel. - Current king's hotkey is skipped (already crowned). - Infra faults (dead eval pod, busy server, chain hiccup on block hash) requeue without burning a failure record; miner-attributable failures burn. --- ## Serving stack (will your checkpoint load?) Your checkpoint is served with stock `vllm serve` — **never** `--trust-remote-code`. Combined with the hygiene rules (no `*.py`, no `auto_map`), only architectures natively supported by the pod's vLLM build can play. If vLLM cannot load or serve your model, the injectability probe rejects it — and your slot is already burned. Pre-flight before submitting. - **Exact invocation**: `_vllm_cmd` in [code/evalsrv/engine.py](https://s3.hippius.com/affine-sn120/code/evalsrv/engine.py). Current knobs (`affine.toml [miner_serving]`, substituted here at site build time): `--tensor-parallel-size 2`, `--max-model-len 32768`, `--gpu-memory-utilization 0.8`, `--max-num-batched-tokens 8192`, FLASH_ATTN attention, triton MoE backend. - **dtype** is vLLM `auto`: it follows `torch_dtype` in your `config.json`. - **Versions float**: pods install the [eval] extra fresh at provision time (floors from `pyproject.toml`: `vllm>=0.8`, `transformers>=4.51`). The versions actually running right now are reported live at `api/v1/snapshot` under `eval_machine.versions` (vllm / transformers / torch) and in `data/contract.json` `serving` for the frozen knobs. - **Hardware**: an 8-GPU B300/B200/H200 pod; the miner slot is 2 GPUs, tensor-parallel 2. Your model (≤ 90 GB safetensors) must load and serve under exactly that. - **Pre-flight recipe**: same vLLM version as the snapshot reports, then `vllm serve you/Affine-... --revision --max-model-len 32768 --tensor-parallel-size 2` and check it answers `/v1/completions` with finite logprobs on an echo request (see `score_action` in `code/evalsrv/vllm_client.py`). --- ## S* v2 (what you optimize) Per pair / miner gates (INVALID ⇒ cannot win, S = −∞ for ranking): 1. **Causality + leakage** — pair passes if no fuzzy z⊃y leakage and `lpA(y_A|z_A) − lpA(y_A|∅) ≥ τ` (τ=0.02). Miner INVALID if pass_rate < γ=0.30. 2. **Prior-bank positivity** — `frac_bank` = share of pairs with Λ2_bank > 0 over the published priors in `affine/priors.py`. INVALID if frac_bank < γ_bank=0.08. 3. **Calibration ratio** — `r = mean|lpA(y_C|z_A)| / mean|lpA(y_C|∅)|`. INVALID if r ∉ [0.3, 4.0]. (r_lo was 1.0 until 2026-08-06; it gated faithful teacher distills, whose own thoughts legitimately raise their own p(y_C).) 3b. **Empty-baseline band** (challenger only, paired) — your `mean|lpA(y_C|∅)|` must be ≤ 1.25× the king's on the same slice. Closes free-L1lift via a sabotaged empty baseline; honest models sit ≤ 1.14×. Ranking term: ``` S = mean( Λ2 + w · clip(L1lift, ±0.1) ) with w = 1.0 Λ2 = lpC(y_C|z_A) − lpC(y_C|∅) L1lift = lpA(y_C|z_A) − lpA(y_C|∅) ``` Duel crowning: challenger wins iff **all** of: - both sides gate-valid - paired `mean(S_c − S_k) > 3 · SE` - `mean > min_margin` (δ = 0.02 — a noise floor, not an effect floor: any challenger statistically above the king crowns) - SE floored by `min_se = 0.005` Before the full duel, an injectability probe rejects checkpoints that cannot emit a parsable bash action or return finite forced logprobs. **Simulate before you submit.** Your eval slot is burned at enqueue, one per hotkey, ever — so replay the duel locally first. The complete measurement layer is published under `code/` and is import-closed (every module `dueling.py` touches is in the list above): `evalsrv/chat.py` is the chat contract — models are rendered through their own chat template to a string and driven via `/v1/completions`, injection plants thoughts as the canonical assistant body `\nTHOUGHT: {z}\n\n{y}`, and `split_rollout` defines exactly what counts as z (all reasoning text) and y (the last closed bash-fenced block). `evalsrv/terms.py` makes the ten forced-logprob calls behind every `lp*` component; `evalsrv/vllm_client.py` shows the echo+logprobs forcing and the per-byte normalization (`lp_per_byte`). Serve the teacher, the current king (`api/v1/snapshot`), and your checkpoint with vLLM, draw an `n_turns` slice from public D, and run the same code that will judge you — every knob is in `affine.toml` `[duel]`. Frozen numeric knobs live in `affine.toml` `[duel]` (linked under `code/`). Changing score.py, priors, duel knobs, or the reveal format is a chain fork (`weight_version_key` bump). Corpus refreshes are data events, not forks: the manifest's `corpus_epoch` increments and every verdict records which manifest it was scored against. --- ## Public data (train on it) Everything the validator scores is published — there is no validator-private data. All paths are relative to this site's root (Hippius S3 bucket `affine-sn120`); fetch them directly with curl or any HTTP client. **Live dashboard API** (hot path — `https://affine.io`): - `GET /api/v1/snapshot` — king, reign chain, intake, duel queue, live eval. - `GET /api/v1/history?limit=&cursor=&q=&event=` — filterable verdicts. - `GET /api/v1/benchmarks` — advisory suite scores (never part of S*). - `GET /api/v1/contract` — machine-readable contract knobs. - `GET /api/v1/duels/{id}` — duel detail (gates, z, margin, S*). - `GET /api/v1/duels/{id}/series` — per-turn Λ2 / L1lift (no raw logprobs). - `GET /api/v1/stream` — SSE snapshot deltas for live UIs. **Hippius archive mirror** (cold path — this site's Hippius root, same disclosure): - `data/dashboard.json` / `data/history.json` / `data/benchmarks.json` / `data/contract.json` — slim JSON also pushed for miners without the API. - `data/validator_log.txt` — recent validator log tail (plain text, refreshed ~every minute). Pod network coordinates are redacted; nothing else is. **Complete audit logs** (gzipped JSONL, updated on every verdict): - `data/history_full.jsonl.gz` — every verdict and failure since genesis, with full per-side S* summaries, gate stats, slice seeds, block hashes, rejection reasons. - `data/bench_history_full.jsonl.gz` — every completed bench run. **Full duel records — the training data** (one immutable object per challenge, published right after the verdict): - `evals/index.jsonl` — append-only manifest. One line per duel: `{key, bytes, at, challenge_id, repo, revision, hotkey, challenger_wins, z, margin, rejection_reason}`. Poll this to discover new records. - `evals/{challenge_id}.json.gz` — gzipped JSON with everything computed during the duel: - `request` — king/challenger repos + revisions, hotkey, block hash. - `verdict` — same audit summary as history. - `slice` — seed, digest, n, block_hash, corpus_epoch, manifest_sha256. The manifest hash resolves at `turns/manifests/{hash}.json` forever, so you can re-derive the exact slice from public D even after shards are retired. - `turn_ids` — `{traj_id}:{turn_idx}` keys into the public corpus. - `teacher_refs` — the teacher's reference rollouts per turn: `{turn_id: [{z, y, lp_own, lp_empty}]}`. This is frontier-teacher distillation data for the exact turns that were scored. - `king_rows` / `challenger_rows` — per-turn instrumented records: `{turn_id, miner, valid, n_pairs, bank_frac, L2_bank, pairs: [...]}`. Each pair carries the miner rollout text (`z_a` thoughts, `y_a` action) plus every forced-logprob component S* is computed from (`lpA_yc_za`, `lpC_yc_za`, `lpA_yc_zc`, `lpA_yc_e`, `lpA_ya_za`, `lpC_ya_za`, `lpA_ya_zc`, `lpA_ya_e`, `lpC_ya_e`, `lpC_ya_zc`, `lpC_yc_zc`, `lpC_yc_e`, `L2_bank`). You can recompute any verdict offline from this file + `affine/score.py`. **Turn corpus D** (the prompts themselves) — sharded, on this site: - `turns/manifest.json` — current manifest: `{corpus_epoch, created_at, shards: [{key, sha256, n_turns, active}], prev_manifest}`. Poll it like `evals/index.jsonl`; a hash change means the corpus moved. - `turns/shards/*.jsonl.gz` — immutable shard objects; `sha256` in the manifest is over the uncompressed jsonl. Download every `active: true` shard and concatenate in manifest order to reproduce exactly what the eval machine scores against. - `turns/manifests/{sha256}.json` — every manifest revision ever published, immutable. The `manifest_sha256` stamped in any verdict resolves here, so retired data stays replayable. Slices are seeded by the reveal-block hash, so future slices are unpredictable; past records tell you the distribution, not the next slice. The corpus is refreshed continuously — new shards appear and old ones retire via manifest revisions (`corpus_epoch` increments each time), so keep your local copy synced to the manifest. Suggested agent loop: poll `evals/index.jsonl` → fetch new `evals/*.json.gz` → train on `teacher_refs` (distillation) and on your own gate/logprob diagnostics from `pairs`. --- ## Source of truth (linked) The files below are byte-identical copies of the validator's own tree, republished under `code/` every time the site is pushed — they can never be newer or older than the code that scores you. Fetch them with curl or any HTTP client. The set is import-closed over the scoring path: everything `dueling.py` calls (chat contract, forced-logprob instrumentation, vLLM client) is in the list, so a local pre-submit simulator needs nothing that is not linked here. - [code/affine.toml](https://s3.hippius.com/affine-sn120/code/affine.toml) — chain contract SSOT — every frozen knob the validator runs - [code/affine/score.py](https://s3.hippius.com/affine-sn120/code/affine/score.py) — S* v2: gates, ranking term, duel decision — the scoring code - [code/scripts/submit.py](https://s3.hippius.com/affine-sn120/code/scripts/submit.py) — standalone commit-reveal submission client — this single file is the whole submit path (trust it over any prose) - [code/affine/priors.py](https://s3.hippius.com/affine-sn120/code/affine/priors.py) — published prior bank behind the bank gate - [code/affine/chain.py](https://s3.hippius.com/affine-sn120/code/affine/chain.py) — reveal payload contract + commit builders - [code/evalsrv/dueling.py](https://s3.hippius.com/affine-sn120/code/evalsrv/dueling.py) — live duel: slice seeding, injectability probe, scoring loop - [code/evalsrv/chat.py](https://s3.hippius.com/affine-sn120/code/evalsrv/chat.py) — the chat contract: prompt assembly, thought-injection template, z/y rollout parsing — byte-exact - [code/evalsrv/terms.py](https://s3.hippius.com/affine-sn120/code/evalsrv/terms.py) — per-turn instrumentation: teacher references + the ten forced-logprob calls behind every lp* component - [code/evalsrv/vllm_client.py](https://s3.hippius.com/affine-sn120/code/evalsrv/vllm_client.py) — vLLM sampling + echo/logprob forcing + per-byte normalization (lp_per_byte) - [code/evalsrv/engine.py](https://s3.hippius.com/affine-sn120/code/evalsrv/engine.py) — slot lifecycle + the exact `vllm serve` invocation your checkpoint is loaded with (_vllm_cmd) - [code/pyproject.toml](https://s3.hippius.com/affine-sn120/code/pyproject.toml) — eval-pod dependency floors ([eval] extra) — vllm / transformers are installed fresh at pod provision - [code/affine/model_store.py](https://s3.hippius.com/affine-sn120/code/affine/model_store.py) — checkpoint hygiene rules + weight-copy detection - [code/affine/state.py](https://s3.hippius.com/affine-sn120/code/affine/state.py) — 1-hotkey-1-eval policy, king lineage, queue invariants This index and the `code/` copies are regenerated together on every validator website push.