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
34 changes: 17 additions & 17 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ members = [
]

[workspace.package]
version = "0.3.60"
version = "0.3.61"
edition = "2024"
license = "MIT"
repository = "https://github.com/profullstack/c0mpute"
Expand Down
2 changes: 1 addition & 1 deletion apps/web/public/llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
- [Chat](https://c0mpute.com/chat): chat with open models served by c0mpute/Infernet provider nodes. Sign in with CoinPay (a CoinPay DID is required). Same chat from the CLI (`c0mpute chat login`, `c0mpute chat "prompt"`), an OpenAI-compatible API (base URL `https://c0mpute.com/v1`, `Authorization: Bearer c0k_...`), and MCP (`https://c0mpute.com/api/mcp`, tools chat, list_models, whoami; stdio via `c0mpute chat mcp`).
- [Run a node](https://c0mpute.com/providers): `curl -fsSL https://c0mpute.com/install.sh | sh` installs c0mpute and starts the worker (Linux, macOS, Windows via WSL2; CPU or GPU). The goal is 100,000 nodes, enough pooled capacity to train and serve open-weight models in the open.
- [CPU and GPU earnings estimates](https://c0mpute.com/estimates): compare illustrative monthly scenarios using public cloud rental rates and assumed paid utilization. CPU reference rates are DigitalOcean Basic shared-CPU VM prices, including RAM, storage, and transfer. These are not actual c0mpute payouts or guaranteed earnings; the public status does not attribute jobs or payouts by hardware.
- [Market](https://c0mpute.com/market): GPU nodes online with each operator's listing. Fully competitive: every job is a sealed-bid auction under the buyer's cap and the lowest bid wins automatically; no rate card. `c0mpute pricing set --min <usd>` = dynamic pricing (starts at 60% of the cap, undercuts the winning price by 2% after a loss, probes up after a win, rises with queue load) never under the floor; 5% market fee on market jobs (the operator keeps 95%; Infernet charges the same 5% on paid inference and the two never stack); `--fixed <usd>` = one price. The listing (`pricing: {mode, min_usd, fixed_usd}`) is in https://c0mpute.com/api/nodes.
- [Market](https://c0mpute.com/market): GPU nodes online with each operator's listing. Fully competitive: every job is a sealed-bid auction under the buyer's cap and the lowest bid wins automatically; no rate card. New nodes default to a $0.001 minimum per job; existing node settings are preserved. `c0mpute pricing set --min <usd>` = dynamic pricing (starts at 60% of the cap, undercuts the winning price by 2% after a loss, probes up after a win, rises with queue load) never under the floor; 5% market fee on market jobs (the operator keeps 95%; Infernet charges the same 5% on paid inference and the two never stack); `--fixed <usd>` = one price. The listing (`pricing: {mode, min_usd, fixed_usd}`) is in https://c0mpute.com/api/nodes.
- [Node board](https://c0mpute.com/nodes): every node online now, ranked by `c0mpute bench` score, with the operator's nickname. JSON at https://c0mpute.com/api/nodes (`name` per node, `operators` = nodes online per nickname).
- Node nicknames: `c0mpute name <handle>` then restart the worker (`c0mpute worker stop && c0mpute worker start -d`), or install with `curl -fsSL https://c0mpute.com/install.sh | sh -s -- --name <handle>`. Stored in `~/.c0mpute/name`; `C0MPUTE_NAME` overrides it. Lowercase a-z, 0-9, - and _, up to 32 chars. Public and unverified; earnings follow the payout address, not the name. Needs 0.2.64+.
- [Benchmarks](https://c0mpute.com/benchmark): live CPU, cores, RAM, composite score and per-workload throughput for online nodes. These synthetic CPU workloads do not measure AI model inference speed.
Expand Down
4 changes: 3 additions & 1 deletion apps/web/src/app/market/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,9 @@ $ infernet payout set USDC 0xYourAddress --network polygon`}</CodeBlock>
winning price by {Math.round(UNDERCUT * 100)}% after it loses to a
cheaper bid, edges back up after it wins, and asks more as its job
queue fills. All you set is a minimum: it never bids under it, and
jobs capped below it are skipped. Or ask one fixed price.
jobs capped below it are skipped. New nodes default to a $0.001
minimum per job; existing node settings are preserved. Or ask one
fixed price.
</p>
<CodeBlock>{`$ c0mpute pricing set --min 0.05 # dynamic, never under $0.05 a job
$ c0mpute pricing set --fixed 0.40 # always $0.40 a job
Expand Down
4 changes: 3 additions & 1 deletion apps/web/src/app/providers/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,9 @@ $ c0mpute worker start --roles tts,stt`}</CodeBlock>
cap, nodes bid under it, and the lowest bid wins. Your node prices
itself by default (it learns the winning price, undercuts it, and asks
more when busy) and never goes under the minimum you set with{" "}
<code>c0mpute pricing set --min 0.05</code>; or ask one fixed price.
<code>c0mpute pricing set --min 0.05</code>. New nodes start with a
$0.001 minimum per job; existing settings are preserved. Or ask one
fixed price.
When you win, your node runs the job and publishes a receipt. Settlement
through CoinPay is built for hosting and ads but{" "}
<strong className="text-[var(--color-fg)]">off by default</strong> while it is
Expand Down
1 change: 1 addition & 0 deletions node/crates/c0mpute-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ enum Cmd {
///
/// Dynamic (default) undercuts the buyer's cap when idle and rises as the
/// queue fills; fixed always asks one price. Neither goes under `--min`.
/// New nodes default to a $0.001 minimum per job; explicit settings persist.
/// A running worker picks a change up on restart.
Pricing {
#[command(subcommand)]
Expand Down
6 changes: 6 additions & 0 deletions node/crates/c0mpute-core/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1031,6 +1031,12 @@ impl Config {
mod tests {
use super::*;

#[test]
fn a_new_partial_config_gets_the_default_pricing_floor() {
let cfg: Config = toml::from_str("roles = []\n").unwrap();
assert_eq!(cfg.pricing.min_usd, 0.001);
}

#[test]
fn gateways_opt_into_adult_and_an_old_serve_key_is_ignored() {
assert!(!Config::default().adult.buyer);
Expand Down
36 changes: 33 additions & 3 deletions node/crates/c0mpute-core/src/pricing.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! How a worker prices its bids: the operator's listing.
//!
//! The operator sets a floor (`min_usd`, per job) and a mode:
//! The operator sets a floor (`min_usd`, per job) and a mode. A new node's
//! floor defaults to [`DEFAULT_MINIMUM_USD`]:
//!
//! - `dynamic` (the default): the node prices itself against the market.
//! It starts at [`DYNAMIC_IDLE_SHARE`] of the buyer's cap and learns from
Expand All @@ -27,6 +28,9 @@ pub const UNDERCUT: f64 = 0.02;
/// upward, so the market does not only ratchet down).
pub const WIN_PROBE: f64 = 0.05;

/// New nodes start at a one-tenth-cent minimum bid per job.
pub const DEFAULT_MINIMUM_USD: f64 = 0.001;

/// What a dynamic node has learned about the clearing price for one
/// workload, as a share of the buyer's cap. Buyers award the lowest bid
/// (`buyer.rs`) and broadcast the `JobAccept` with the agreed price, so
Expand Down Expand Up @@ -76,18 +80,32 @@ impl PricingMode {
}
}

#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct PricingConfig {
#[serde(default)]
pub mode: PricingMode,
/// The lowest price per job this node accepts, in USD. 0 = no floor.
#[serde(default)]
#[serde(default = "default_minimum_usd")]
pub min_usd: f64,
/// The price per job in `fixed` mode, in USD.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub fixed_usd: Option<f64>,
}

impl Default for PricingConfig {
fn default() -> Self {
Self {
mode: PricingMode::default(),
min_usd: DEFAULT_MINIMUM_USD,
fixed_usd: None,
}
}
}

fn default_minimum_usd() -> f64 {
DEFAULT_MINIMUM_USD
}

impl PricingConfig {
/// Reject negative or non-finite numbers and a fixed mode with no price.
pub fn validate(&self) -> Result<(), String> {
Expand Down Expand Up @@ -175,6 +193,15 @@ mod tests {
assert_eq!(dynamic(1.5).quote(1.0, 0.0), None);
}

#[test]
fn new_nodes_default_to_a_one_tenth_cent_floor() {
let p = PricingConfig::default();
assert_eq!(p.mode, PricingMode::Dynamic);
assert_eq!(p.min_usd, 0.001);
assert_eq!(p.quote(0.001, 0.0), Some(0.001));
assert_eq!(p.quote(0.0005, 0.0), None);
}

#[test]
fn fixed_quotes_its_price_and_respects_the_cap() {
let p = PricingConfig {
Expand Down Expand Up @@ -250,9 +277,12 @@ mod tests {
let p: PricingConfig = toml::from_str("").unwrap();
assert_eq!(p, PricingConfig::default());
assert_eq!(p.mode, PricingMode::Dynamic);
assert_eq!(p.min_usd, 0.001);
let p: PricingConfig =
toml::from_str("mode = \"fixed\"\nmin_usd = 0.1\nfixed_usd = 0.2").unwrap();
assert_eq!(p.quote(1.0, 0.0), Some(0.2));
let explicit_zero: PricingConfig = toml::from_str("min_usd = 0.0").unwrap();
assert_eq!(explicit_zero.min_usd, 0.0);
}

#[test]
Expand Down
Loading