Skip to content

feat(ctx): universal subnet CLI + A→Z miner docs on the public gateway - #207

Open
echobt wants to merge 4 commits into
mainfrom
cursor/miner-docs-and-ctx-cli-152c
Open

feat(ctx): universal subnet CLI + A→Z miner docs on the public gateway#207
echobt wants to merge 4 commits into
mainfrom
cursor/miner-docs-and-ctx-cli-152c

Conversation

@echobt

@echobt echobt commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Miners had no single entry point: the guides carried <gateway> placeholders, Bounty told them to export an operator secret (BOUNTY_CHAT_COMMAND), and the only CLI (cortex-bounty) covered one third of one challenge. This PR ships one binary, one host, and rewritten guides that a miner can follow start to finish.

New: bins/ctx — the subnet CLI for the four live challenges.

Command Does
ctx challenges The four live ids, their emission, what each pays for, and which command starts it
ctx status / ctx weights can_score per challenge, and whether the epoch is sealed or on the fail-closed burn vector
ctx relearn|image|agent submit | show | status Submit an artifact digest plus a training manifest, then poll (--wait) to a terminal state
ctx image prompts The frozen public split, its seeds, and the holdout commitment
ctx bounty pair | report | show | status Sign the pairing challenge locally (sr25519), bind through the gateway, cache the session claim at ~/.config/cortex/bounty-session.json (0600), then file reports

Default gateway is https://network.cortex.foundation; --gateway overrides it for a local stack. LIUM_API_KEY is forwarded as X-Lium-Api-Key and never printed.

The CLI refuses locally what the services refuse anyway, so a miner does not learn a gate by paying for a run: an empty manifest is rejected before the request (contamination_evidence_missing), pairing prints the terms and will not proceed without --accept-terms, and a thin report is rejected before it burns a rate-limit window. Every HTTP failure is explained — a 503 says nothing was stored, nothing was rented, and the challenge cannot score right now.

Release + install. .github/workflows/release-ctx.yml builds ctx on tags v*.*.* for linux-amd64/arm64 (static musl), darwin-amd64/arm64, and windows-amd64, then attaches the archives plus SHA256SUMS.txt to the release. scripts/install-ctx.sh is the curl one-liner; it verifies the checksum and installs to ~/.local/bin/ctx, and it aborts rather than installing something it could not verify.

Docs. docs/external-miner/ is rewritten A→Z: install ctx, what you need before submitting, pick a challenge, read can_score before spending anything, submit (CLI and the equivalent curl), read the verdict. Every host is written out; the curl routes are real URLs. troubleshoot.md is reorganised around what a miner actually sees (installer, connectivity, reject reasons, Bounty states) and no longer hands miners operator env names. Top-level README.md gained a Mine section pointing at the installer and the four challenges. cortex-bounty still works for operator scripts but prints a deprecation notice pointing at ctx bounty pair.

Gate. xtask external-docs-check now reads DEFAULT_GATEWAY out of bins/ctx/src/api.rs and requires every miner page to name that exact host, so the docs and the binary cannot drift. It fails on a <gateway> / <host> placeholder anywhere under docs/external-miner/ (recursively, which caught the relearn-seed copy), and fails if a miner page mentions BOUNTY_CHAT_COMMAND, BOUNTY_BACKEND_PUBLIC_URL, or BASE_GATEWAY_*. validators.md is exempt from the env rule — a validator does set its own gateway endpoint. The index no longer pins those env names, which is how they got into miner docs in the first place.

Greptile

Every PR is reviewed by Greptile before merge. Config: .greptile/.

  • Greptile has reviewed this PR; findings are fixed or answered
  • If the bot was silent, I commented @greptileai review

Test plan

  • cargo test --workspace — green (includes 33 new ctx tests and a new external-docs-check test that runs the whole gate against this repo)
  • cargo fmt --all -- --check, cargo clippy --workspace --all-targets -- -D warnings
  • xtask loc-cap (bins/ctx 1190 / 1500), consensus-lint, spec-check, design-check, external-docs-check
  • Gate negative-tested: reintroducing <gateway> in relearn.md fails with both the placeholder and the missing-host findings
  • cargo build -p ctx --release --locked --target x86_64-unknown-linux-musl → static-pie binary, 5.8 MB, ctx --version runs
  • Installer end-to-end against a local file server: happy path installs and runs; a mismatched checksum and a missing SHA256SUMS.txt both abort
  • Submission simulated end-to-end against a local mock of the gateway routes: status / weights / image prompts; Relearn and Image submit (happy path, plus the empty-manifest refusal) and show; Bounty pair (terms refusal, then a real sr25519 sign → session cached 0600), report, show, status, the thin-report refusal, and the fail-closed 503 on a host with no adjudication feed
  • cargo deny check — cargo-deny is not installed on this runner; the lockfile delta is the ctx package entry only, with no new third-party crates
  • deploy/scripts/assert-compose-matrix.sh — needs docker, unavailable here; no compose file changed

Risk

Docs and a new miner-facing binary; no challenge scoring, emission, model pin, or deploy path is touched. Two things need an operator before miners can use this: network.cortex.foundation does not resolve yet (the legacy chain.joinbase.ai name is still what deploy/compose/env-prod.yml pins, and this PR deliberately leaves that pin alone), and the first v*.*.* tag after merge is what publishes the release assets the install script downloads. Until both land, the install one-liner and the documented host will 404 / fail to resolve.

ctx install and self-update are deliberately out of scope for this first cut.

Follow-up outside this repo

The public miner repo CortexLM/relearn still shows the old submit snippet. Its in-repo seed copy (docs/external-miner/relearn-seed/) is updated here with the ctx install line and the concrete gateway host; the same edit needs to land on the public repo, which this PR cannot reach.

Naming

I did not rename BASE_* environment variables, deployed host paths
(/opt/base, /run/base, …), GHCR baseintelligence/base package names, or
base-*-v1 cryptographic domain tags, unless this PR’s purpose is a coordinated
cutover documented in docs/NAMING.md.

Open in Web Open in Cursor 

cursoragent and others added 4 commits September 3, 2026 18:49
Co-authored-by: Mathis <echobt@users.noreply.github.com>
Co-authored-by: Mathis <echobt@users.noreply.github.com>
Co-authored-by: Mathis <echobt@users.noreply.github.com>
Co-authored-by: Mathis <echobt@users.noreply.github.com>
@echobt
echobt marked this pull request as ready for review September 3, 2026 19:10
@greptile-apps

greptile-apps Bot commented Sep 3, 2026

Copy link
Copy Markdown

Greptile Summary

This change adds the cross-platform ctx miner CLI, release packaging, installation support, and updated miner documentation. Three failures were reproduced: JSON-mode commands report failed gateway requests as successful process exits, release build jobs run a mutable third-party action with repository write authority, and the Bounty session cache can follow a planted symlink before the bearer credential is secured. These issues should be resolved before publishing the CLI.

Confidence Score: 0/5

Not safe to merge until the release workflow is least-privilege and immutable, and Bounty session storage is made resistant to local path attacks.

Focused executable checks reproduced the CLI exit-status failure, confirmed the write-authorized release artifact path, and exercised symlinked session storage through an authenticated report request.

Files Needing Attention: bins/ctx/src/bounty.rs, bins/ctx/src/catalog.rs, bins/ctx/src/main.rs, and .github/workflows/release-ctx.yml.

Security Review

The release workflow allows a mutable third-party action to execute in build jobs that inherit contents: write, and those jobs produce binaries later attached to releases. Separately, the Bounty session cache writes bearer claims through a deterministic path that follows symlinks and applies restrictive permissions only afterward; the redirected value is subsequently used to authenticate report submissions.

T-Rex T-Rex Logs

What T-Rex did

  • T-Rex produced a proof for a posted P1 finding and linked it to review comment 0.
  • T-Rex produced a proof for a posted P1 finding and linked it to review comment 1.
  • T-Rex produced a proof for a posted P1 finding and linked it to review comment 2.
  • T-Rex produced a proof for a posted P1 finding and linked it to review comment 3.
  • T-Rex ran the requested contract validation verification, but local artifact references were not uploaded.

T-Rex Ran code and verified through T-Rex

Comments Outside Diff (1)

  1. General comment

    P1 Build matrix receives write-capable repository token while running mutable third-party action

    • Bug
      • The workflow-level permissions: contents: write at lines 18–19 applies to the build job because it has no job-level permissions override. Each of its five matrix executions runs dtolnay/rust-toolchain@master at line 57, a mutable branch reference. Those builds package and upload archives that the release job downloads and attaches to a GitHub Release.
    • Cause
      • Write authority is granted at workflow scope rather than restricted to the release job, while an unpinned external action runs in the build job.
    • Fix
      • Set the workflow default to contents: read (or omit workflow-level write permission), explicitly grant contents: write only to release, and pin third-party actions—especially dtolnay/rust-toolchain—to immutable full commit SHAs.

    T-Rex Ran code and verified through T-Rex

Reviews (1): Last reviewed commit: "docs(bounty): use the real report id pre..." | Re-trigger Greptile

Comment thread bins/ctx/src/bounty.rs
Comment on lines +194 to +199
if json_out {
println!("{}", reply.body);
return Ok(());
}
if !reply.ok() {
return Err(explain(reply.status, &reply.message()));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 JSON mode hides HTTP failures

ctx --json bounty show prints a non-2xx response body and returns Ok(()) before reply.ok() is checked, so automation receives exit code 0 for gateway failures. A local gateway returned 404 for bounty show, 503 for bounty status and weights, and 401 for image prompts; every JSON-mode command printed the error payload with an exit status of 0, while the equivalent non-JSON Bounty command exited 1. Check the HTTP status before rendering either output format so failed requests consistently return a nonzero status.

T-Rex Ran code and verified through T-Rex

Comment on lines +18 to +19
permissions:
contents: write

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 security Build jobs inherit repository write authority

The workflow-level contents: write permission is inherited by all five build-matrix jobs because build has no narrower permissions block. Those jobs execute dtolnay/rust-toolchain@master, then produce archives that the write-capable release job downloads and publishes. Restrict write permission to the release job and pin third-party actions to immutable commit SHAs, so a mutable action revision cannot use a build token or influence miner-installed release assets.

T-Rex Ran code and verified through T-Rex

Comment thread bins/ctx/src/bounty.rs
Comment on lines +327 to +340
let path = session_path();
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent).map_err(|e| format!("create {}: {e}", parent.display()))?;
}
let record = json!({
"gateway": gateway,
"account_id": account_id,
"miner_hotkey": ss58,
"session": reply.get("session"),
"session_id": reply.get("session_id"),
});
std::fs::write(&path, format!("{record}\n")).map_err(|e| format!("write session: {e}"))?;
restrict(&path)?;
Ok(path)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 security Bounty session cache follows attacker-planted symlinks

store_session writes the bearer session claim through the deterministic cache path before applying 0600. A runtime check planted cortex/bounty-session.json as a symlink: the CLI wrote the claim to its target, the target was initially created with mode 0644, and the subsequently loaded redirected claim was sent to the report endpoint. Create the cache atomically with owner-only permissions and no-follow semantics, and reject non-regular cache entries before loading the credential.

T-Rex Ran code and verified through T-Rex

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants