Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 25 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,30 @@ Missing evidence fails closed rather than passing.
Some env vars and host paths still spell `BASE_*`. That is leftover naming,
not a second product.

- **[How to mine — Bounty](docs/external-miner/bounty.md)**
- **[How to mine — Proof](docs/external-miner/proof.md)**
- **[How to validate](docs/external-miner/validators.md)**
## Mine

Miners and validators talk to one public gateway:
**`https://network.cortex.foundation`**. Install the subnet CLI:

```bash
curl -fsSL https://raw.githubusercontent.com/CortexLM/cortex/main/scripts/install-ctx.sh | sh

ctx challenges # the two live challenges and what they pay for
ctx status # can each challenge score right now, and is the epoch sealed
```

`ctx` ([`bins/ctx`](bins/ctx)) submits to the two live challenges and handles
Bounty pairing. `ctx relearn|image|agent` still exist for a local stack;
those challenges are **off** and earn nothing. `curl` works against the same
routes.

| Challenge | Start with | Guide |
|-----------|-----------|-------|
| Bounty | `ctx bounty pair` | **[How to mine — Bounty](docs/external-miner/bounty.md)** |
| Proof | `ctx proof submit` | **[How to mine — Proof](docs/external-miner/proof.md)** |

Start at **[docs/external-miner/](docs/external-miner/README.md)** for the
A→Z, and **[How to validate](docs/external-miner/validators.md)** if you run a
validator.

Apache License 2.0 — see [LICENSE](./LICENSE).
64 changes: 63 additions & 1 deletion bins/ctx/src/catalog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,54 @@ pub const LIVE: [Challenge; 2] = [
},
];

/// Resolve a challenge by id or by its `ctx` subcommand name.
/// Off challenges. Commands exist for a local stack; they earn nothing.
pub const OFF: [Challenge; 3] = [
Challenge {
id: "relearn",
label: "Relearn",
command: "relearn",
entry: "ctx relearn submit (off: no emission)",
emission_bps: 0,
work: "post-train Qwen/Qwen3.8-27B",
guide: "docs/external-miner/relearn.md",
},
Challenge {
id: "relearn-image",
label: "Relearn Image",
command: "image",
entry: "ctx image submit (off: no emission)",
emission_bps: 0,
work: "fine-tune nvidia/Cosmos3-Super-Text2Image, judged by Q-Judger",
guide: "docs/external-miner/relearn-image.md",
},
Challenge {
id: "relearn-agent",
label: "Relearn Agent",
command: "agent",
entry: "ctx agent submit (off: no emission)",
emission_bps: 0,
work: "post-train the same checkpoint into a tool-using agent",
guide: "docs/external-miner/relearn-agent.md",
},
];

/// Resolve a live challenge by id or by its `ctx` subcommand name.
#[must_use]
pub fn find(name: &str) -> Option<&'static Challenge> {
LIVE.iter().find(|c| c.id == name || c.command == name)
}

/// Resolve an off challenge. Never used for emission or `ctx challenges`.
#[must_use]
pub fn find_off(name: &str) -> Option<&'static Challenge> {
OFF.iter()
.find(|c| c.id == name || c.command == name)
.or_else(|| match name {
"relearn-t2i" | "t2i" => find_off("relearn-image"),
_ => None,
})
}

/// Print the live challenge table.
pub fn print_challenges() {
println!("Live Cortex challenges (emission in basis points of the subnet):");
Expand All @@ -64,6 +106,8 @@ pub fn print_challenges() {
}
println!("relearn, relearn-image, relearn-agent, relearn-mm, design, and prism");
println!("are off: no trust-root row, no emission. Submitting to them earns nothing.");
println!("`ctx relearn|image|agent` still talk to a local stack (`--gateway`);");
println!("they are not live work.");
println!();
println!("Bounty pays precision times severity. Proof pays the mean of per-topic");
println!("lattices over currently open ids; an empty eval digest cannot score.");
Expand All @@ -84,6 +128,20 @@ pub async fn fetch_status(client: &Client, challenge_id: &str) -> Result<Value,

/// Print the status of every live challenge plus the sealed weights vector.
pub async fn print_status(client: &Client, only: Option<&str>, json: bool) -> Result<(), String> {
if let Some(want) = only {
if find(want).is_none() {
if let Some(off) = find_off(want) {
return Err(format!(
"{} is off: no trust-root row, no emission. Live ids: bounty, proof. \
Use `ctx {} status` against a local stack.",
off.id, off.command
));
}
return Err(format!(
"unknown challenge {want:?}. Live ids: bounty, proof."
));
}
}
let mut collected = serde_json::Map::new();
if only.is_none() && !json {
println!("gateway: {}", client.gateway());
Expand Down Expand Up @@ -229,6 +287,10 @@ mod tests {
] {
assert!(find(off).is_none(), "{off} must not be live");
}
assert_eq!(find_off("relearn").map(|c| c.emission_bps), Some(0));
assert_eq!(find_off("image").map(|c| c.id), Some("relearn-image"));
assert_eq!(find_off("t2i").map(|c| c.id), Some("relearn-image"));
assert!(find_off("relearn-mm").is_none());
}

#[test]
Expand Down
63 changes: 58 additions & 5 deletions bins/ctx/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
mod api;
mod bounty;
mod catalog;
mod off;
mod proof;

use std::path::PathBuf;
Expand All @@ -30,7 +31,8 @@ use proof::SubmitInput;
long_about = "ctx talks to the public Cortex gateway at https://network.cortex.foundation.

Live challenges: bounty (7000 bps) and proof (3000 bps). relearn*, design, and
prism are off and earn nothing.
prism are off and earn nothing. `ctx relearn|image|agent` still exist for a
local stack; they are not live work.

Start with 'ctx challenges' for what each one pays for, then 'ctx status' to
see whether a host can score right now. A challenge that cannot score answers
Expand Down Expand Up @@ -78,6 +80,21 @@ enum Cmd {
#[command(subcommand)]
cmd: BountyCmd,
},
/// Relearn (off): no emission. Local-stack submit only.
Relearn {
#[command(subcommand)]
cmd: off::OffCmd,
},
/// Relearn Image (off): no emission. Local-stack submit only.
Image {
#[command(subcommand)]
cmd: off::ImageCmd,
},
/// Relearn Agent (off): no emission. Local-stack submit only.
Agent {
#[command(subcommand)]
cmd: off::OffCmd,
},
}

#[derive(Debug, Subcommand)]
Expand Down Expand Up @@ -214,6 +231,9 @@ async fn run(cli: Cli) -> Result<(), String> {
Cmd::Weights => catalog::print_weights(&client, cli.json).await,
Cmd::Proof { cmd } => run_proof(&client, cmd, cli.json).await,
Cmd::Bounty { cmd } => run_bounty(&client, cmd, cli.json).await,
Cmd::Relearn { cmd } => off::run(&client, "relearn", cmd, cli.json).await,
Cmd::Image { cmd } => off::run_image(&client, cmd, cli.json).await,
Cmd::Agent { cmd } => off::run(&client, "relearn-agent", cmd, cli.json).await,
}
}

Expand Down Expand Up @@ -323,10 +343,43 @@ mod tests {
}

#[test]
fn relearn_commands_are_not_on_the_cli() {
assert!(Cli::try_parse_from(["ctx", "relearn", "status"]).is_err());
assert!(Cli::try_parse_from(["ctx", "image", "status"]).is_err());
assert!(Cli::try_parse_from(["ctx", "agent", "status"]).is_err());
fn off_commands_parse_but_are_not_live() {
assert!(Cli::try_parse_from(["ctx", "relearn", "status"]).is_ok());
assert!(Cli::try_parse_from(["ctx", "image", "prompts"]).is_ok());
assert!(Cli::try_parse_from(["ctx", "agent", "status"]).is_ok());
assert!(Cli::try_parse_from(["ctx", "relearn", "prompts"]).is_err());
assert!(catalog::find("relearn").is_none());
assert_eq!(
catalog::find_off("relearn").map(|c| c.emission_bps),
Some(0)
);
}

#[test]
fn off_submit_parses_repeatable_manifest_flags() {
let cli = Cli::try_parse_from([
"ctx",
"relearn",
"submit",
"--hotkey",
&"ab".repeat(32),
"--artifact-digest",
&"cd".repeat(32),
"--train-id",
"1",
"--train-dataset",
"my-mix",
])
.expect("parse");
match cli.cmd {
Cmd::Relearn {
cmd: off::OffCmd::Submit(args),
} => {
assert_eq!(args.train_ids, vec![1]);
assert_eq!(args.train_datasets, vec!["my-mix".to_owned()]);
}
other => panic!("wrong command: {other:?}"),
}
}

#[test]
Expand Down
Loading
Loading