diff --git a/Cargo.lock b/Cargo.lock
index 1b95c9b..19483db 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -476,7 +476,7 @@ checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
[[package]]
name = "c0mpute-api"
-version = "0.2.27"
+version = "0.2.28"
dependencies = [
"anyhow",
"c0mpute-proto",
@@ -491,7 +491,7 @@ dependencies = [
[[package]]
name = "c0mpute-bench"
-version = "0.2.27"
+version = "0.2.28"
dependencies = [
"anyhow",
"blake3",
@@ -502,7 +502,7 @@ dependencies = [
[[package]]
name = "c0mpute-cli"
-version = "0.2.27"
+version = "0.2.28"
dependencies = [
"anyhow",
"axum",
@@ -532,7 +532,7 @@ dependencies = [
[[package]]
name = "c0mpute-core"
-version = "0.2.27"
+version = "0.2.28"
dependencies = [
"anyhow",
"async-trait",
@@ -566,7 +566,7 @@ dependencies = [
[[package]]
name = "c0mpute-doctor"
-version = "0.2.27"
+version = "0.2.28"
dependencies = [
"anyhow",
"serde",
@@ -578,7 +578,7 @@ dependencies = [
[[package]]
name = "c0mpute-envelope"
-version = "0.2.27"
+version = "0.2.28"
dependencies = [
"blake3",
"bs58",
@@ -591,7 +591,7 @@ dependencies = [
[[package]]
name = "c0mpute-gateway"
-version = "0.2.27"
+version = "0.2.28"
dependencies = [
"anyhow",
"axum",
@@ -613,7 +613,7 @@ dependencies = [
[[package]]
name = "c0mpute-market"
-version = "0.2.27"
+version = "0.2.28"
dependencies = [
"c0mpute-envelope",
"serde",
@@ -623,7 +623,7 @@ dependencies = [
[[package]]
name = "c0mpute-net"
-version = "0.2.27"
+version = "0.2.28"
dependencies = [
"anyhow",
"async-trait",
@@ -640,7 +640,7 @@ dependencies = [
[[package]]
name = "c0mpute-placement"
-version = "0.2.27"
+version = "0.2.28"
dependencies = [
"anyhow",
"async-trait",
@@ -661,7 +661,7 @@ dependencies = [
[[package]]
name = "c0mpute-proto"
-version = "0.2.27"
+version = "0.2.28"
dependencies = [
"blake3",
"hex",
@@ -673,7 +673,7 @@ dependencies = [
[[package]]
name = "c0mpute-secure-chat"
-version = "0.2.27"
+version = "0.2.28"
dependencies = [
"aes-gcm",
"anyhow",
@@ -696,7 +696,7 @@ dependencies = [
[[package]]
name = "c0mpute-store"
-version = "0.2.27"
+version = "0.2.28"
dependencies = [
"anyhow",
"blake3",
@@ -714,7 +714,7 @@ dependencies = [
[[package]]
name = "c0mpute-transcode"
-version = "0.2.27"
+version = "0.2.28"
dependencies = [
"anyhow",
"c0mpute-proto",
@@ -726,7 +726,7 @@ dependencies = [
[[package]]
name = "c0mpute-update"
-version = "0.2.27"
+version = "0.2.28"
dependencies = [
"anyhow",
"hex",
@@ -740,7 +740,7 @@ dependencies = [
[[package]]
name = "c0mpute-verify"
-version = "0.2.27"
+version = "0.2.28"
dependencies = [
"anyhow",
"blake3",
diff --git a/Cargo.toml b/Cargo.toml
index 0c2ebad..eb33b6b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -23,7 +23,7 @@ members = [
]
[workspace.package]
-version = "0.2.27"
+version = "0.2.28"
edition = "2024"
license = "MIT"
repository = "https://github.com/profullstack/c0mpute"
diff --git a/README.md b/README.md
index 7fef0c4..57f2e33 100644
--- a/README.md
+++ b/README.md
@@ -39,7 +39,12 @@ c0mpute worker start --gpu
# submit jobs
c0mpute transcode submit input.mov --preset hls --max-price 1.25
-c0mpute infernet run prompts.jsonl --model qwen --max-price 5.00
+c0mpute infernet chat --model qwen2.5:7b "hello"
+
+# infernet: see what serves what, reserve a node
+c0mpute pool qwen # live nodes per model
+c0mpute infernet model list --node # one node's advertised models
+c0mpute infernet reservation pricing # managed endpoints, by GPU class
# monitor
c0mpute job status
diff --git a/apps/tui/package.json b/apps/tui/package.json
index eca63a5..88a5ca8 100644
--- a/apps/tui/package.json
+++ b/apps/tui/package.json
@@ -1,6 +1,6 @@
{
"name": "@c0mpute/tui",
- "version": "0.2.27",
+ "version": "0.2.28",
"private": true,
"type": "module",
"bin": {
diff --git a/apps/web/next-env.d.ts b/apps/web/next-env.d.ts
index 9edff1c..ce4e94a 100644
--- a/apps/web/next-env.d.ts
+++ b/apps/web/next-env.d.ts
@@ -1,6 +1,7 @@
///
///
import "./.next/types/routes.d.ts";
+import "./.next/types/root-params.d.ts";
// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
diff --git a/apps/web/package.json b/apps/web/package.json
index b85af47..540ee6e 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -1,6 +1,6 @@
{
"name": "c0mpute",
- "version": "0.2.27",
+ "version": "0.2.28",
"private": true,
"type": "module",
"scripts": {
diff --git a/apps/web/src/app/docs/page.tsx b/apps/web/src/app/docs/page.tsx
index db8d1ad..a781fdb 100644
--- a/apps/web/src/app/docs/page.tsx
+++ b/apps/web/src/app/docs/page.tsx
@@ -160,21 +160,36 @@ c0mpute transcode preset list`}
spot-check duplicate execution.
-{`# Simple batch with default model
-c0mpute infernet run prompts.jsonl \\
- --model qwen \\
- --max-price 5.00
+{`# Run a prompt on the network (stdin works too)
+c0mpute infernet chat --model qwen2.5:7b "summarise this"
+cat prompt.txt | c0mpute infernet chat --model qwen2.5:7b
+
+# How many live nodes serve each model
+c0mpute pool
+c0mpute pool qwen
-# Pin a specific model + runtime image hash for reproducible runs
-c0mpute infernet run prompts.jsonl \\
- --model llama-3.1-8b \\
- --max-price 0.25
+# The models one node advertises (node_id or row id)
+c0mpute infernet model list --node
-# List models the network advertises
-c0mpute infernet models list
+# Models pulled on this machine
+c0mpute infernet model list`}
+
+
-# Benchmark a model against the network's workers
-c0mpute infernet benchmark --model qwen`}
+
+
+ Infernet managed endpoints reserve one identified GPU operator
+ with an exact set of models, by the hour. You get your own API
+ keys, rate limits and usage reports, and pay only for hours that
+ pass the uptime checks. Every infernet reservation{" "}
+ command works through c0mpute infernet.
+
+
+{`c0mpute infernet reservation pricing # price list by GPU class
+c0mpute infernet reservation create --name N --operator O --models m1,m2
+c0mpute infernet reservation key-issue --label customer-a
+c0mpute infernet reservation report
+c0mpute infernet reservation --help`}
diff --git a/apps/web/src/app/getting-started/page.tsx b/apps/web/src/app/getting-started/page.tsx
index cb8c300..55f7934 100644
--- a/apps/web/src/app/getting-started/page.tsx
+++ b/apps/web/src/app/getting-started/page.tsx
@@ -42,7 +42,7 @@ $ c0mpute worker start --gpu`}
{`$ c0mpute transcode submit input.mov --preset hls --max-price 1.25
-$ c0mpute infernet run prompts.jsonl --model qwen
+$ c0mpute infernet chat --model qwen2.5:7b "hello"
$ c0mpute job status `}
diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx
index 53f591c..b9c065f 100644
--- a/apps/web/src/app/page.tsx
+++ b/apps/web/src/app/page.tsx
@@ -66,7 +66,7 @@ $ c0mpute worker start --gpu`}
// submit a job
{`$ c0mpute transcode submit input.mov --preset hls
-$ c0mpute infernet run prompts.jsonl --model qwen --max-price 0.10`}
+$ c0mpute infernet chat --model qwen2.5:7b "hello"`}
diff --git a/apps/web/src/app/releases/latest.json/route.ts b/apps/web/src/app/releases/latest.json/route.ts
index 4530942..52217cd 100644
--- a/apps/web/src/app/releases/latest.json/route.ts
+++ b/apps/web/src/app/releases/latest.json/route.ts
@@ -28,7 +28,7 @@ export const dynamic = "force-dynamic";
* this in sync with the workspace version in `Cargo.toml` on each release so a
* freshly built CLI reports "already latest" rather than a phantom upgrade.
*/
-const FALLBACK_VERSION = "0.2.27";
+const FALLBACK_VERSION = "0.2.28";
const FALLBACK_MIN_REQUIRED = "0.0.1";
type Channel = "stable" | "beta" | "nightly";
diff --git a/node/crates/c0mpute-cli/src/main.rs b/node/crates/c0mpute-cli/src/main.rs
index 836ffb1..6aa8473 100644
--- a/node/crates/c0mpute-cli/src/main.rs
+++ b/node/crates/c0mpute-cli/src/main.rs
@@ -40,7 +40,7 @@ use c0mpute_secure_chat as chat;
name = "c0mpute",
version,
about = "c0mpute.com — decentralized compute network",
- long_about = "c0mpute.com CLI. Submit jobs, run a worker, manage modules.\n\nBuilt-in plugins:\n transcode (FFmpeg, in-process)\n coinpay (DID + payments, peer CLI)\n infernet (AI inference, peer CLI)\n\n c0mpute coinpay reputation did claim\n c0mpute transcode submit input.mov --preset hls\n c0mpute infernet run prompts.jsonl --model qwen"
+ long_about = "c0mpute.com CLI. Submit jobs, run a worker, manage modules.\n\nBuilt-in plugins:\n transcode (FFmpeg, in-process)\n coinpay (DID + payments, peer CLI)\n infernet (AI inference, peer CLI)\n\n c0mpute coinpay reputation did claim\n c0mpute transcode submit input.mov --preset hls\n c0mpute infernet chat --model qwen2.5:7b \"hello\"\n c0mpute infernet model list --node \n c0mpute infernet reservation pricing"
)]
struct Cli {
/// Override the config file location.
@@ -183,10 +183,14 @@ enum Cmd {
/// Show the live infernet distribution pool: how many nodes are serving
/// each model right now (from the control plane's peer list). Pass a model
- /// id to see just that model + which nodes.
+ /// id to see just that model + which nodes, or --node for the models one
+ /// node advertises. To reserve a node: `c0mpute infernet reservation pricing`.
Pool {
/// Model id to filter on (substring match, e.g. `9b`). Omit for all.
model: Option,
+ /// List the models one node advertises (its node_id or row id), live or not.
+ #[arg(long, conflicts_with = "model")]
+ node: Option,
},
}
@@ -469,7 +473,8 @@ async fn run_app(cli: Cli) -> Result<()> {
print_all_versions();
Ok(())
}
- Cmd::Pool { model } => run_pool(model),
+ Cmd::Pool { node: Some(id), .. } => run_pool_node(&id),
+ Cmd::Pool { model, node: None } => run_pool(model),
Cmd::Doctor => run_doctor().await,
Cmd::Bench { cmd, quick, json, threads } => run_bench(cmd, quick, json, threads),
Cmd::StatusAggregator { bind } => c0mpute_core::status_aggregator::run(bind).await,
@@ -527,6 +532,40 @@ fn peer_label(bin: &str) -> &'static str {
if which_on_path(bin).is_some() { "OK" } else { "WARN" }
}
+/// The installed version of a peer CLI, from the first line of ` --version`.
+/// Only that line is read: `infernet --version` goes on to check npm for updates,
+/// so the child is killed rather than waited on.
+fn peer_version(bin: &str) -> Option {
+ use std::io::BufRead;
+ let path = which_on_path(bin)?;
+ let mut child = Command::new(path)
+ .arg("--version")
+ .stdout(std::process::Stdio::piped())
+ .stderr(std::process::Stdio::null())
+ .spawn()
+ .ok()?;
+ let mut line = String::new();
+ let read = child
+ .stdout
+ .take()
+ .map(|out| std::io::BufReader::new(out).read_line(&mut line));
+ let _ = child.kill();
+ let _ = child.wait();
+ read?.ok()?;
+ parse_version(&line)
+}
+
+/// The first dotted version in a line: "infernet v0.1.58" and "0.15.0" both work.
+fn parse_version(line: &str) -> Option {
+ line.split_whitespace()
+ .map(|w| w.trim_start_matches('v'))
+ .find(|w| {
+ let parts: Vec<&str> = w.split('.').collect();
+ parts.len() >= 2 && parts.iter().all(|p| !p.is_empty() && p.chars().all(|c| c.is_ascii_digit()))
+ })
+ .map(str::to_string)
+}
+
fn peer_status_text(bin: &str) -> String {
match which_on_path(bin) {
Some(p) => format!("Ok ({})", p.display()),
@@ -1259,9 +1298,11 @@ fn prompt_new_password() -> Result {
fn run_plugin(cmd: PluginCmd) -> Result<()> {
match cmd {
PluginCmd::List => {
- println!("transcode v0.1.0 in-process built-in");
- println!("coinpay v0.1.0 subprocess {}", peer_status_text("coinpay"));
- println!("infernet v0.1.0 subprocess {}", peer_status_text("infernet"));
+ println!("transcode v{:<8} in-process built-in", env!("CARGO_PKG_VERSION"));
+ for bin in ["coinpay", "infernet"] {
+ let v = peer_version(bin).map(|v| format!("v{v}")).unwrap_or_else(|| "-".into());
+ println!("{bin:<9} {v:<9} subprocess {}", peer_status_text(bin));
+ }
Ok(())
}
PluginCmd::Install { target } => install_plugin(&target),
@@ -1670,7 +1711,8 @@ fn infernet_control_plane_url() -> String {
/// that actually decides how a fits-one-node model distributes.
fn run_pool(model: Option) -> Result<()> {
let base = infernet_control_plane_url();
- let url = format!("{base}/api/peers?limit=1000");
+ // The control plane caps /api/peers at 100.
+ let url = format!("{base}/api/peers?limit=100");
let out = Command::new("curl")
.args(["-fsSL", "--max-time", "12", &url])
.output()
@@ -1687,7 +1729,15 @@ fn run_pool(model: Option) -> Result<()> {
.or_else(|| json.as_array().cloned())
.unwrap_or_default();
- println!("infernet pool @ {base} — {} live nodes", peers.len());
+ println!(
+ "infernet pool @ {base} — {} dialable nodes (public address, seen in the last 15 min)",
+ peers.len()
+ );
+ // /api/peers only lists nodes another node can dial, so NAT'd nodes that
+ // still take jobs are missing from it. The overview has the full count.
+ if let Some((online, registered)) = fetch_online_nodes(&base) {
+ println!("{online} online of {registered} registered (incl. nodes behind NAT)");
+ }
let served = |p: &serde_json::Value| -> Vec {
p.get("served_models")
@@ -1727,9 +1777,90 @@ fn run_pool(model: Option) -> Result<()> {
}
}
}
+ println!("one node's models: c0mpute pool --node reserve one: c0mpute infernet reservation pricing");
+ Ok(())
+}
+
+/// `c0mpute pool --node ` — one node's advertised models, from the control
+/// plane's public GET /api/nodes//models (the same data as
+/// `infernet model list --node`, without needing infernet installed).
+fn run_pool_node(id: &str) -> Result<()> {
+ let base = infernet_control_plane_url();
+ let url = format!("{base}/api/nodes/{}/models", url_path_segment(id));
+ let out = Command::new("curl")
+ .args(["-sSL", "--max-time", "12", "-w", "\n%{http_code}", &url])
+ .output()
+ .map_err(|e| anyhow::anyhow!("run curl: {e}"))?;
+ let text = String::from_utf8_lossy(&out.stdout);
+ let (body, code) = text.trim_end().rsplit_once('\n').unwrap_or(("", text.trim()));
+ match code {
+ "200" => {}
+ "404" => anyhow::bail!("node {id} not found (or not public) at {base}"),
+ _ => anyhow::bail!("failed to fetch {url} (HTTP {code})"),
+ }
+ let json: serde_json::Value = serde_json::from_str(body)
+ .map_err(|e| anyhow::anyhow!("parse node models from {url}: {e}"))?;
+ let node = json.get("data").unwrap_or(&json);
+ let field = |k: &str| node.get(k).and_then(|v| v.as_str()).unwrap_or("-");
+ let models: Vec<&str> = node
+ .get("served_models")
+ .and_then(|m| m.as_array())
+ .map(|a| a.iter().filter_map(|m| m.as_str()).collect())
+ .unwrap_or_default();
+ println!(
+ "node {} ({}) status {} last_seen {}",
+ field("node_id"),
+ field("name"),
+ field("status"),
+ field("last_seen")
+ );
+ println!("advertised models: {}", models.len());
+ for m in models {
+ println!(" {m}");
+ }
Ok(())
}
+/// Percent-encode everything but unreserved characters, so an id can't add
+/// path segments or a query string.
+fn url_path_segment(s: &str) -> String {
+ s.bytes()
+ .map(|b| match b {
+ b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' => (b as char).to_string(),
+ _ => format!("%{b:02X}"),
+ })
+ .collect()
+}
+
+/// (online, registered) from the control plane's /api/overview "Online nodes"
+/// card, whose note reads "of N registered". Best-effort: None on any failure.
+fn fetch_online_nodes(base: &str) -> Option<(u64, u64)> {
+ let out = Command::new("curl")
+ .args(["-fsSL", "--max-time", "8", &format!("{base}/api/overview")])
+ .output()
+ .ok()?;
+ if !out.status.success() {
+ return None;
+ }
+ let json: serde_json::Value = serde_json::from_slice(&out.stdout).ok()?;
+ parse_online_card(&json)
+}
+
+fn parse_online_card(json: &serde_json::Value) -> Option<(u64, u64)> {
+ let card = json
+ .get("cards")?
+ .as_array()?
+ .iter()
+ .find(|c| c.get("label").and_then(|l| l.as_str()) == Some("Online nodes"))?;
+ let online = card.get("value")?.as_u64()?;
+ let registered = card
+ .get("note")?
+ .as_str()?
+ .split_whitespace()
+ .find_map(|w| w.parse::().ok())?;
+ Some((online, registered))
+}
+
fn delegate(bin: &str, args: &[String]) -> Result<()> {
let path = which_on_path(bin).ok_or_else(|| {
anyhow::anyhow!(
@@ -2452,3 +2583,31 @@ fn which_on_path(bin: &str) -> Option {
}
None
}
+
+#[cfg(test)]
+mod infernet_tests {
+ use super::*;
+
+ #[test]
+ fn parse_version_reads_infernet_and_coinpay_lines() {
+ assert_eq!(parse_version("infernet v0.1.58\n").as_deref(), Some("0.1.58"));
+ assert_eq!(parse_version("0.15.0").as_deref(), Some("0.15.0"));
+ assert_eq!(parse_version("update check failed (offline?)"), None);
+ }
+
+ #[test]
+ fn online_card_parses_value_and_registered() {
+ let json = serde_json::json!({ "cards": [
+ { "label": "Models served", "value": 3, "note": "a, b, c" },
+ { "label": "Online nodes", "value": 2, "note": "of 41 registered" }
+ ]});
+ assert_eq!(parse_online_card(&json), Some((2, 41)));
+ assert_eq!(parse_online_card(&serde_json::json!({})), None);
+ }
+
+ #[test]
+ fn node_id_is_one_path_segment() {
+ assert_eq!(url_path_segment("provider-0f44326c"), "provider-0f44326c");
+ assert_eq!(url_path_segment("../x?y"), "..%2Fx%3Fy");
+ }
+}
diff --git a/package.json b/package.json
index 003cc09..72871de 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "c0mpute-monorepo",
- "version": "0.2.27",
+ "version": "0.2.28",
"private": true,
"workspaces": [
"apps/*",
diff --git a/packages/shared/package.json b/packages/shared/package.json
index 56ac95d..a5b4415 100644
--- a/packages/shared/package.json
+++ b/packages/shared/package.json
@@ -1,6 +1,6 @@
{
"name": "@c0mpute/shared",
- "version": "0.2.27",
+ "version": "0.2.28",
"private": true,
"type": "module",
"main": "./dist/index.js",
diff --git a/plugins/ads-manager/module.toml b/plugins/ads-manager/module.toml
index 9956bfe..41a25cc 100644
--- a/plugins/ads-manager/module.toml
+++ b/plugins/ads-manager/module.toml
@@ -18,7 +18,7 @@
[module]
id = "ads-manager"
name = "Ads Manager — shared ad operations layer"
-version = "0.2.27"
+version = "0.2.28"
kind = "service"
category = "applications"
description = "Unified ad operations for the c0mpute network: campaign management, creative registry, publisher inventory, OpenRTB auction primitives, CoinPay escrow, and reporting. Required dependency for ctv-ads and all future ad format plugins."
diff --git a/plugins/coinpay/module.toml b/plugins/coinpay/module.toml
index 46da5b4..fdd8093 100644
--- a/plugins/coinpay/module.toml
+++ b/plugins/coinpay/module.toml
@@ -7,7 +7,7 @@
[module]
id = "coinpay"
name = "CoinPay — DID, payments, escrow, reputation"
-version = "0.2.27"
+version = "0.2.28"
kind = "service"
category = "applications"
description = "Decentralized identifiers, wallet linking, escrow, signed receipts, and worker reputation. The trust spine of the c0mpute network."
diff --git a/plugins/crawl/module.toml b/plugins/crawl/module.toml
index 726a839..d8eae4e 100644
--- a/plugins/crawl/module.toml
+++ b/plugins/crawl/module.toml
@@ -14,7 +14,7 @@
[module]
id = "crawl"
name = "Crawl — distributed web fetching"
-version = "0.2.27"
+version = "0.2.28"
kind = "workload"
category = "data"
description = "Decentralized web crawling and page-rendering pool. Static fetches and JS-rendered pages, robots-aware, WARC-capable."
diff --git a/plugins/ctv-ads/module.toml b/plugins/ctv-ads/module.toml
index 01ba7c1..2a4e391 100644
--- a/plugins/ctv-ads/module.toml
+++ b/plugins/ctv-ads/module.toml
@@ -20,7 +20,7 @@
[module]
id = "ctv-ads"
name = "CTV Ads — Connected TV ad marketplace"
-version = "0.2.27"
+version = "0.2.28"
kind = "service"
category = "applications"
description = "Decentralised Connected TV advertising on the c0mpute network. VAST 4.x endpoint, OpenRTB real-time auction, cryptographically verified impressions, CoinPay settlement. Supports Roku, Fire TV, Apple TV, Smart TVs, and FAST channels. Live-stream integration for ad-monetised broadcasts."
diff --git a/plugins/diffusion/module.toml b/plugins/diffusion/module.toml
index 7cce708..04fd81d 100644
--- a/plugins/diffusion/module.toml
+++ b/plugins/diffusion/module.toml
@@ -14,7 +14,7 @@
[module]
id = "diffusion"
name = "Diffusion — image generation"
-version = "0.2.27"
+version = "0.2.28"
kind = "workload"
category = "ai"
description = "Decentralized image generation (Stable Diffusion, SDXL, Flux) on consumer/prosumer GPUs."
diff --git a/plugins/embed/module.toml b/plugins/embed/module.toml
index f2274ff..3af54a8 100644
--- a/plugins/embed/module.toml
+++ b/plugins/embed/module.toml
@@ -13,7 +13,7 @@
[module]
id = "embed"
name = "Embeddings — vector generation"
-version = "0.2.27"
+version = "0.2.28"
kind = "workload"
category = "ai"
description = "Bulk text-embedding generation (sentence-transformers, BGE, Nomic) with CPU fallback and optional GPU acceleration."
diff --git a/plugins/gameserver/module.toml b/plugins/gameserver/module.toml
index 6b7ef65..4681934 100644
--- a/plugins/gameserver/module.toml
+++ b/plugins/gameserver/module.toml
@@ -14,7 +14,7 @@
[module]
id = "gameserver"
name = "Game server — Minecraft / Factorio / Valheim"
-version = "0.2.27"
+version = "0.2.28"
kind = "workload"
category = "infrastructure"
description = "Continuous-reservation hosting for game servers. Multi-replica placement, persistent world state on c0mpute storage, proof-of-serve settlement."
diff --git a/plugins/hosting/module.toml b/plugins/hosting/module.toml
index 504749c..9780008 100644
--- a/plugins/hosting/module.toml
+++ b/plugins/hosting/module.toml
@@ -12,7 +12,7 @@
[module]
id = "hosting"
name = "Hosting — censorship-resistant static sites"
-version = "0.2.27"
+version = "0.2.28"
kind = "workload"
category = "data"
description = "Static-site hosting with multi-replica placement, mutable naming, epoch settlement, and slashable proof-of-serve. See DIP-0015."
diff --git a/plugins/infernet/module.toml b/plugins/infernet/module.toml
index b839765..88400ed 100644
--- a/plugins/infernet/module.toml
+++ b/plugins/infernet/module.toml
@@ -2,11 +2,15 @@
#
# Source code lives upstream at github.com/infernetprotocol/infernet-protocol, NOT
# this repo. This directory is a marketplace wrapper only.
+#
+# `version` is this wrapper's, kept in lockstep with c0mpute by
+# scripts/bump-version.sh. The installed infernet CLI's own version is what
+# `c0mpute plugin list` reports.
[module]
id = "infernet"
name = "Infernet Protocol — AI inference"
-version = "0.2.27"
+version = "0.2.28"
kind = "workload"
category = "ai"
description = "Decentralized AI inference workloads on c0mpute. Runs models on consumer/prosumer GPUs with model-hash and runtime-image verification."
@@ -23,7 +27,7 @@ arch = ["x86_64", "aarch64"]
capabilities = ["gpu:nvidia?", "model-runtime"]
[module.workloads]
-"infernet.inference" = { command = "run", validation = "schema_and_spotcheck" }
+"infernet.inference" = { command = "chat", validation = "schema_and_spotcheck" }
[module.dispatch]
mode = "subprocess"
diff --git a/plugins/live-stream/module.toml b/plugins/live-stream/module.toml
index dc0240f..328cf17 100644
--- a/plugins/live-stream/module.toml
+++ b/plugins/live-stream/module.toml
@@ -23,7 +23,7 @@
[module]
id = "live-stream"
name = "Live Stream — p2p HLS live streaming"
-version = "0.2.27"
+version = "0.2.28"
kind = "service"
category = "media"
description = "Decentralized live video streaming via BitTorrent-style HLS segment swarming. Broadcaster pushes RTMP/SRT; c0mpute nodes swarm segments to viewers. No central CDN. Censorship-resistant. Per-GB micropayments via CoinPay."
diff --git a/plugins/ocr/module.toml b/plugins/ocr/module.toml
index a49eeb4..8431b87 100644
--- a/plugins/ocr/module.toml
+++ b/plugins/ocr/module.toml
@@ -12,7 +12,7 @@
[module]
id = "ocr"
name = "OCR — document text extraction"
-version = "0.2.27"
+version = "0.2.28"
kind = "workload"
category = "ai"
description = "Decentralized OCR for PDFs and scanned documents (Tesseract, PaddleOCR, docTR) with CPU or GPU runners."
diff --git a/plugins/podcast-ads/module.toml b/plugins/podcast-ads/module.toml
index c5bf9f4..018a751 100644
--- a/plugins/podcast-ads/module.toml
+++ b/plugins/podcast-ads/module.toml
@@ -22,7 +22,7 @@
[module]
id = "podcast-ads"
name = "Podcast Ads — audio ad marketplace"
-version = "0.2.27"
+version = "0.2.28"
kind = "service"
category = "applications"
description = "Decentralised audio advertising for c0mpute-hosted podcasts. DAAST 1.0 endpoint, dynamic ad insertion, Podcasting 2.0 revenue splits, and CoinPay settlement. Publishers sell ad slots; advertisers run campaigns — zero platform cut beyond protocol split."
diff --git a/plugins/podcasting/module.toml b/plugins/podcasting/module.toml
index 198e02b..99115ef 100644
--- a/plugins/podcasting/module.toml
+++ b/plugins/podcasting/module.toml
@@ -20,7 +20,7 @@
[module]
id = "podcasting"
name = "Podcasting — p2p podcast publishing"
-version = "0.2.27"
+version = "0.2.28"
kind = "service"
category = "media"
description = "Censorship-resistant podcast hosting on the c0mpute network. Upload episodes, stream live shows, auto-transcribe, and monetise via CoinPay subscriptions, pay-per-episode, value-for-value, and audio ads — with zero platform cut. RSS 2.0 + Podcasting 2.0 compatible with all major podcast apps."
diff --git a/plugins/podcasts/module.toml b/plugins/podcasts/module.toml
index 286785b..b27b3c0 100644
--- a/plugins/podcasts/module.toml
+++ b/plugins/podcasts/module.toml
@@ -19,7 +19,7 @@
[module]
id = "podcasts"
name = "Podcasts — p2p podcast listener"
-version = "0.2.27"
+version = "0.2.28"
kind = "service"
category = "media"
description = "Discover, subscribe to, and play podcasts hosted on the c0mpute network or any standard RSS feed. Stream live episodes, tip creators directly via CoinPay, and participate in value-for-value streaming payments. Works alongside any existing podcast app via OPML export and local RSS proxy."
diff --git a/plugins/render/module.toml b/plugins/render/module.toml
index 7565d56..f6b7a9d 100644
--- a/plugins/render/module.toml
+++ b/plugins/render/module.toml
@@ -13,7 +13,7 @@
[module]
id = "render"
name = "Render — Blender 3D rendering"
-version = "0.2.27"
+version = "0.2.28"
kind = "workload"
category = "media"
description = "Decentralized 3D rendering using Blender headless (Cycles/Eevee) across consumer GPUs and CPUs."
diff --git a/plugins/secure-chat/module.toml b/plugins/secure-chat/module.toml
index a209e6c..9c02513 100644
--- a/plugins/secure-chat/module.toml
+++ b/plugins/secure-chat/module.toml
@@ -18,7 +18,7 @@
[module]
id = "secure-chat"
name = "Secure Chat — E2E encrypted p2p messaging"
-version = "0.2.27"
+version = "0.2.28"
kind = "service"
category = "applications"
description = "End-to-end encrypted peer-to-peer messaging across the c0mpute network. No central server. Keys stay with the user. Identity and reputation via CoinPay DID. Group chats, sealed-sender mode, and offline store-and-forward included."
diff --git a/plugins/storage/module.toml b/plugins/storage/module.toml
index 33c62fe..c4d50d3 100644
--- a/plugins/storage/module.toml
+++ b/plugins/storage/module.toml
@@ -12,7 +12,7 @@
[module]
id = "storage"
name = "Storage — Reed-Solomon shard service"
-version = "0.2.27"
+version = "0.2.28"
kind = "service"
category = "data"
description = "Reed-Solomon 10/14 shard storage with auto-repair. Compute-locality, E2E encryption, content-addressed, pay-as-you-go. See DIP-0012."
diff --git a/plugins/train/module.toml b/plugins/train/module.toml
index 8bc81ef..052546d 100644
--- a/plugins/train/module.toml
+++ b/plugins/train/module.toml
@@ -13,7 +13,7 @@
[module]
id = "train"
name = "Fine-tune — LoRA/QLoRA training"
-version = "0.2.27"
+version = "0.2.28"
kind = "workload"
category = "ai"
description = "Decentralized model fine-tuning (LoRA, QLoRA) on consumer/prosumer GPUs using axolotl or unsloth."
diff --git a/plugins/transcode/module.toml b/plugins/transcode/module.toml
index df09f3d..4ee86da 100644
--- a/plugins/transcode/module.toml
+++ b/plugins/transcode/module.toml
@@ -7,7 +7,7 @@
[module]
id = "transcode"
name = "FFmpeg Transcoding"
-version = "0.2.27"
+version = "0.2.28"
kind = "workload"
category = "media"
description = "Decentralized audio/video transcoding via FFmpeg with hardware acceleration (NVENC, QSV, AMF, VideoToolbox, AV1)."
diff --git a/plugins/tts/module.toml b/plugins/tts/module.toml
index 898031f..0c8ede5 100644
--- a/plugins/tts/module.toml
+++ b/plugins/tts/module.toml
@@ -12,7 +12,7 @@
[module]
id = "tts"
name = "TTS — text-to-speech"
-version = "0.2.27"
+version = "0.2.28"
kind = "workload"
category = "ai"
description = "Decentralized text-to-speech synthesis (Piper, Coqui XTTS, Kokoro) for narration, audiobooks, and accessibility."
diff --git a/plugins/whisper/module.toml b/plugins/whisper/module.toml
index a9b952e..8c065a2 100644
--- a/plugins/whisper/module.toml
+++ b/plugins/whisper/module.toml
@@ -11,7 +11,7 @@
[module]
id = "whisper"
name = "Whisper — speech-to-text"
-version = "0.2.27"
+version = "0.2.28"
kind = "workload"
category = "ai"
description = "Decentralized speech-to-text transcription via whisper.cpp with optional GPU acceleration (CUDA, Metal, Vulkan)."