diff --git a/README.md b/README.md index 504f1db3..aa248cc7 100644 --- a/README.md +++ b/README.md @@ -20,8 +20,9 @@ curl -fsSL https://c0mpute.com/install.sh | sh ``` That is the whole setup. The installer puts `c0mpute`, `infernet` and `coinpay` in -`~/.c0mpute/bin` and starts the worker for you (a systemd service where it can, a -background daemon otherwise). Check it with `c0mpute worker status`, then run +`~/.c0mpute/bin`, checks each release binary's minisign signature, and starts the worker +for you as **your user, not root** (a systemd user service where it can, a background +daemon otherwise; `--system-service` for a root-managed system service). Check it with `c0mpute worker status`, then run `c0mpute bench` once so your node gets a score on the [node board](https://c0mpute.com/nodes). @@ -31,17 +32,23 @@ background daemon otherwise). Check it with `c0mpute worker status`, then run - **What it does:** serves AI inference (through [Infernet](https://infernetprotocol.com)), FFmpeg transcoding and storage jobs from the network. - **What you get:** paid inference is billed through CoinPay (16 coins or PayPal) and you set - your payout address with `infernet payout set
`. Paid demand is early, so - expect small, irregular earnings for now. + your payout address with `infernet payout set
`. Paid demand is early and the + network is small, so expect small, irregular earnings for now. Storage payouts + ([CIP-006](docs/prds/006-challenges-metering-payouts.md)) are not paying yet, and CoinPay + settlement for hosting and ads is built but off by default. - **House models (on by default):** the node pulls and serves the models Infernet publishes at [infernetprotocol.com/house-models.json](https://infernetprotocol.com/house-models.json) that fit - it (RAM, and always leaving 10% of the disk free), today Profullstack's - `saas-business-strategist-agent` and the uncensored `huihui_ai/qwen3.5-abliterated:4b`/`:9b`. - `c0mpute worker status` lists them and the disk they use. Opt out at install with - `--no-house-models` (uncensored only: `--no-uncensored-models`), or later with - `infernet house-models off` / `infernet house-models uncensored off`; cap the disk with + it (RAM, and always leaving 10% of the disk free), today Profullstack's agent models. + The uncensored `huihui_ai/qwen3.5-abliterated:4b`/`:9b` are on the list too, but a node + serves them **only if you opt in** (`--uncensored-models` at install, or + `infernet house-models uncensored on`). `c0mpute worker status` lists them and the disk + they use. Opt out of house models at install with `--no-house-models`, or later with + `infernet house-models off`; cap the disk with `infernet house-models cap `. Models you pulled yourself are never removed. Details: [infernetprotocol.com/docs/house-models](https://infernetprotocol.com/docs/house-models). +- **Trust:** the worker runs code from the network on your machine. What it can touch, how + releases are signed, and how to remove it: [docs/node-safety.md](docs/node-safety.md). If you + would rather not trust a new project with your main machine, run it in a VM or on a spare box. - **Invite people:** share `https://c0mpute.com/r/`. Nodes installed from it show your handle on the board (`C0MPUTE_REF=` does the same by hand). diff --git a/apps/web/src/app/providers/page.tsx b/apps/web/src/app/providers/page.tsx index b40e9718..691f5420 100644 --- a/apps/web/src/app/providers/page.tsx +++ b/apps/web/src/app/providers/page.tsx @@ -108,19 +108,40 @@ $ c0mpute worker status # the installer already started it`} that fit it, so a request for one can reach any node, not only ours. A model is pulled only when the machine has its listed RAM and the download leaves 10% of the disk free; one at a time, never while a job runs, and checked against its pinned digest. Models - you pulled yourself are never removed. + you pulled yourself are never removed. Uncensored models on that list are{" "} + off unless you opt in.

{`$ c0mpute worker status # lists house models + disk used $ infernet house-models off # opt out (removes what it pulled) -$ infernet house-models uncensored off # skip uncensored models only +$ infernet house-models uncensored on # opt in to uncensored models (off by default) $ infernet house-models cap 20 # at most 20 GB`}

- At install: --no-house-models or --no-uncensored-models. + At install: --no-house-models, or --uncensored-models to opt in. The list and how to add a model:{" "} infernetprotocol.com/docs/house-models.

+
+

[ before you install ]

+

+ A node runs code from this network on your machine, so here is exactly what it can do. + The worker runs as your user, not root: + the installer writes to ~/.c0mpute and starts a systemd user service + (--system-service for a root-managed one, which needs sudo). Release + binaries are signed with minisign and the installer checks the signature before it + installs anything (public key: minisign.pub). + Jobs are the workloads your node advertises, run by those signed binaries as child + processes with CPU and GPU limits. That is not a sandbox: they can read what your user + can read. The code is MIT and on GitHub. +

+

+ If you would rather not trust a new project with your main machine, run it in a VM or on a + spare box first. That is a reasonable call, and the node earns the same there. + Full details: docs/node-safety.md. +

+
+

[ CPU voice inference ]

@@ -159,8 +180,11 @@ $ c0mpute worker start --roles tts,stt`} itself by default (it learns the winning price, undercuts it, and asks more when busy) and never goes under the minimum you set with{" "} c0mpute pricing set --min 0.05; or ask one fixed price. - When you win, your node runs the job and publishes a receipt; signed - receipts and automatic settlement are not live yet. A direct protocol deal has{" "} + When you win, your node runs the job and publishes a receipt. Settlement + through CoinPay is built for hosting and ads but{" "} + off by default while it is + tested; storage payouts (CIP-006) + are not paying yet. A direct protocol deal has{" "} no protocol fee. Jobs bought through the market will pay you{" "} 95% once market diff --git a/docs/node-safety.md b/docs/node-safety.md new file mode 100644 index 00000000..8b85a9eb --- /dev/null +++ b/docs/node-safety.md @@ -0,0 +1,83 @@ +# Running a node: what it can touch + +A c0mpute node runs work for strangers on your machine. That deserves a plain +answer to "what am I trusting?", so here it is. If anything below is wrong, +open an issue; this page is meant to be checkable against the code. + +## What the installer does + +`curl -fsSL https://c0mpute.com/install.sh | sh` (source: [`scripts/install.sh`](../scripts/install.sh)): + +- Downloads the `c0mpute`, `infernet` and `coinpay` release binaries and + **verifies each one's minisign signature** against the public key in + [`minisign.pub`](../minisign.pub) before installing it. A bad signature aborts the install. +- Installs everything under your home directory: binaries in `~/.c0mpute/bin`, + data in `~/.local/share/c0mpute` (or a bigger volume it symlinks, unless + `NO_STORAGE_RELOCATE=1`). +- Starts the worker as **your user, not root**: a systemd *user* service + (`~/.config/systemd/user/c0mpute-worker.service`), with systemd lingering so + it starts at boot. Without systemd it runs as a background daemon. +- Uses `sudo` only if it is already configured, and only for conveniences: + a `/usr/local/bin` symlink and a `/etc/profile.d` PATH hook, plus the + system service if you ask for one with `--system-service`. It never runs the + worker as root. Skip all of it by running the installer without sudo. + +## What the worker runs + +- Jobs are the workload types your node advertises (`c0mpute worker status` + lists them): inference through Infernet, FFmpeg transcodes, storage shards, + LoRA training, speech, music and image generation. They are run by the + installed, signed binaries; a buyer sends data and parameters, not code to execute. + Container workloads (video, game servers) start only pinned, well-known images, and only + when Docker or Podman already works on the box. +- Jobs run as child processes of the worker with CPU-thread and GPU limits. + **This is not a sandbox.** A job runs with your user's permissions, so a + bug in a workload runtime could read files your user can read. If that is + not acceptable for your main machine, run the node in a VM, a container or + on a spare box. It earns the same there. +- The node keeps itself up to date (`C0MPUTE_NO_AUTO_UPDATE=1` turns that + off). Updates are verified against the same minisign key before the binary + is swapped. + +## Models it serves + +- **House models** (on by default): the models Infernet lists at + [infernetprotocol.com/house-models.json](https://infernetprotocol.com/house-models.json) + that fit your RAM and leave 10% of the disk free. Turn them off with + `infernet house-models off`, or `--no-house-models` at install. +- **Uncensored house models are off unless you opt in** (`--uncensored-models` + at install, or `infernet house-models uncensored on`). + +## Network + +Outbound connections only; no port forwarding needed. Some workloads make +outbound HTTP requests on a buyer's behalf from your IP (`crawl.fetch`, +`workflow.step` http calls). Those are limited to public addresses, capped in size and +time, and are listed in `c0mpute worker status` like every other workload. The node joins the +libp2p mesh and advertises its capabilities, benchmark score and (if you set +them) a nickname and referral handle, which appear on the public +[node board](https://c0mpute.com/nodes). + +## Money: what pays today + +- **Paid inference** through Infernet pays out via CoinPay to the address you + set with `infernet payout set

`. +- **Hosting and ads** settlement through CoinPay is built (PRs #162, #163, + #166) but **off by default** while it is tested. +- **Storage payouts** ([CIP-006](prds/006-challenges-metering-payouts.md)) are + not paying yet. + +The network is small and paid demand is early. Expect small, irregular +earnings for now, and budget for the electricity and wear your hardware takes. + +## Removing it + +```sh +systemctl --user disable --now c0mpute-worker # or: c0mpute worker stop +rm -rf ~/.c0mpute ~/.local/share/c0mpute ~/.config/systemd/user/c0mpute-worker.service +loginctl disable-linger "$USER" # if nothing else of yours needs it +``` + +With a system service, also run +`sudo systemctl disable --now c0mpute-worker && sudo rm /etc/systemd/system/c0mpute-worker.service`. +Remove Infernet with `infernet uninstall`. diff --git a/docs/prds/006-challenges-metering-payouts.md b/docs/prds/006-challenges-metering-payouts.md index 936a0faa..b16c27fa 100644 --- a/docs/prds/006-challenges-metering-payouts.md +++ b/docs/prds/006-challenges-metering-payouts.md @@ -1,11 +1,11 @@ --- cip: 006 title: "Storage challenges, metering, and provider payouts" -status: Draft +status: In progress authors: - anthony@profullstack.com created: 2026-08-29 -updated: 2026-08-29 +updated: 2026-11-09 implements: DIP-0012 (0012-storage-plugin.md) Phase 5, DIP-0007 (CoinPay DID) depends-on: 003, 004 blocks: @@ -13,6 +13,25 @@ implementation: estimate: "4–6 weeks" --- +## Status (2026-11-09) + +Storage payouts are **not paying yet**. What exists today: + +| Piece | State | +|---|---| +| Shared payment path (`node/crates/c0mpute-core/src/payments.rs`: `Rail`, idempotent `Ledger`, per-round USD cap) on CoinPay payouts | Merged (#163), **off by default** | +| Hosting marketplace: proof of storage + settlement for hosted sites | Merged (#163), **off by default** | +| Ads billing + settlement through the same path | Merged (#162, #166), **off by default** | +| Storage challenges issued continuously by the network, storage metering, monthly provider statements (this CIP) | Not started beyond `c0mpute-verify`'s `StorageChallenge` primitive | +| Paid inference payouts (Infernet, via CoinPay) | Live today; not part of this CIP | + +The planned storage rate is $0.0015/GB-month, so 1 TB earns about $1.50 a +month. The network is still small (live count on +[c0mpute.com/nodes](https://c0mpute.com/nodes)), so nobody should run a node +for storage income yet. The README, `/providers` and +[`docs/node-safety.md`](../node-safety.md) say this in plain words; keep them in step +with this table. + ## Summary Pay people for the disk they contribute, and make sure they are actually diff --git a/docs/prds/README.md b/docs/prds/README.md index ea67804b..2549c9e9 100644 --- a/docs/prds/README.md +++ b/docs/prds/README.md @@ -69,7 +69,7 @@ Delivering read/write network storage for c0mpute, implementing | [003](003-shard-placement-transport.md) | Cross-node shard placement and streaming transport | 002 | In progress | | [004](004-metadata-durability.md) | Metadata durability: manifests, volumes, and the root pointer | 002 | Draft | | [005](005-repair-daemon.md) | Auto-repair daemon | 003, 004 | In progress | -| [006](006-challenges-metering-payouts.md) | Storage challenges, metering, and provider payouts | 003, 004 | Draft | +| [006](006-challenges-metering-payouts.md) | Storage challenges, metering, and provider payouts | 003, 004 | In progress (payment rail merged, off by default; storage payouts not live) | | [007](007-c0mputefs-filesystem.md) | c0mputefs: mutable filesystem over immutable content | 004 | Draft | | [008](008-write-path-consistency.md) | Write path: chunking, journal, and crash consistency | 007 | Draft | | [009](009-mount-cli.md) | `c0mpute storage` CLI and the FUSE mount | 008 | Draft | diff --git a/scripts/install.sh b/scripts/install.sh index 1b96e6ff..95a6945d 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -26,8 +26,13 @@ # INFERNET_NO_HOUSE_MODELS=1). They are ON by default: # the node pulls and serves the models listed at # https://infernetprotocol.com/house-models.json that fit it. -# --no-uncensored-models Skip uncensored house models only +# --uncensored-models Also serve uncensored house models. They are OFF +# unless you opt in (same as INFERNET_UNCENSORED_MODELS=1) +# --no-uncensored-models Keep uncensored house models off # (same as INFERNET_NO_UNCENSORED_MODELS=1) +# --system-service Run the worker as a root-managed systemd system +# service (needs sudo). Default: a systemd *user* +# service under your account, no root involved. # --no-exec Don't auto-exec a fresh shell at the end # (CI-friendly; default is to drop you into a new # shell with c0mpute already on $PATH) @@ -59,6 +64,7 @@ WORKER_MODE=0 DEVELOPER_MODE=0 FORCE=0 NO_START=0 +SYSTEM_SERVICE="${C0MPUTE_SYSTEM_SERVICE:-0}" say() { printf '\033[1;36m→\033[0m %s\n' "$*"; } warn() { printf '\033[1;33m!\033[0m %s\n' "$*" >&2; } @@ -91,9 +97,11 @@ while [ $# -gt 0 ]; do --name) shift; C0MPUTE_NAME="${1:-}" ;; --name=*) C0MPUTE_NAME="${1#--name=}" ;; --no-house-models) INFERNET_NO_HOUSE_MODELS=1 ;; + --uncensored-models) INFERNET_UNCENSORED_MODELS=1 ;; --no-uncensored-models) INFERNET_NO_UNCENSORED_MODELS=1 ;; + --system-service) SYSTEM_SERVICE=1 ;; --help|-h) - sed -n '2,33p' "$0" + sed -n '2,39p' "$0" exit 0 ;; *) @@ -331,13 +339,17 @@ house_models_notice() { fi if [ "${INFERNET_NO_UNCENSORED_MODELS:-}" = "1" ]; then infernet house-models uncensored off >/dev/null 2>&1 || true + elif [ "${INFERNET_UNCENSORED_MODELS:-}" = "1" ]; then + infernet house-models uncensored on >/dev/null 2>&1 || true fi say "house models: ON — this node pulls and serves the models listed at" say " https://infernetprotocol.com/house-models.json that fit it (RAM; keeps 10% of disk free)." - if [ "${INFERNET_NO_UNCENSORED_MODELS:-}" = "1" ]; then - say " uncensored house models: off (--no-uncensored-models)" + if [ "${INFERNET_UNCENSORED_MODELS:-}" = "1" ] && [ "${INFERNET_NO_UNCENSORED_MODELS:-}" != "1" ]; then + say " uncensored house models: ON (you opted in with --uncensored-models)" + else + say " uncensored house models: off (opt in: infernet house-models uncensored on)" fi - say " opt out any time: infernet house-models off (uncensored only: infernet house-models uncensored off)" + say " opt out any time: infernet house-models off" } # transcode is in-process (no separate binary), but it depends on @@ -701,20 +713,14 @@ EOF } # ──────────────────────────────────────────────────────────────────────── -# Auto-start the worker so a node self-onboards on install/upgrade. Prefers a -# systemd system service (survives reboots, restarts on crash); falls back to a -# background daemon. Idempotent: restarts onto the freshly installed binary. +# Auto-start the worker so a node self-onboards on install/upgrade. Default: +# a systemd *user* service under your own account (no root, nothing written +# outside $HOME), with lingering so it starts at boot. A root-managed system +# service only with --system-service, or when a previous install made one. +# Falls back to a background daemon. Idempotent: restarts onto the new binary. # ──────────────────────────────────────────────────────────────────────── -start_worker() { - [ "$NO_START" -eq 1 ] && return 0 - bin="$C0MPUTE_HOME/bin/c0mpute" - [ -x "$bin" ] || return 0 - say "starting the c0mpute worker (auto-registers + serves its models)" - - if command -v systemctl >/dev/null 2>&1 && [ -d /run/systemd/system ] \ - && { [ -n "$SUDO" ] || [ "$(id -u)" = "0" ]; }; then - tmp=$(mktemp) - cat > "$tmp" < [User= line] + cat </dev/null; then - rm -f "$tmp" - $SUDO systemctl daemon-reload 2>/dev/null - $SUDO systemctl enable c0mpute-worker >/dev/null 2>&1 - if $SUDO systemctl restart c0mpute-worker 2>/dev/null; then - ok "worker running as a systemd service (auto-starts on boot, restarts on crash)" - return 0 - fi - fi +} + +start_system_service() { + { [ -n "$SUDO" ] || [ "$(id -u)" = "0" ]; } || return 1 + tmp=$(mktemp) + worker_unit multi-user.target "User=$(id -un)" > "$tmp" + if $SUDO cp "$tmp" /etc/systemd/system/c0mpute-worker.service 2>/dev/null; then rm -f "$tmp" - warn "systemd setup failed; falling back to a background daemon" + $SUDO systemctl daemon-reload 2>/dev/null + $SUDO systemctl enable c0mpute-worker >/dev/null 2>&1 + if $SUDO systemctl restart c0mpute-worker 2>/dev/null; then + ok "worker running as a systemd system service as $(id -un) (auto-starts on boot, restarts on crash)" + return 0 + fi + fi + rm -f "$tmp" + return 1 +} + +start_user_service() { + [ "$(id -u)" = "0" ] && return 1 + systemctl --user show-environment >/dev/null 2>&1 || return 1 + unit_dir="${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user" + mkdir -p "$unit_dir" || return 1 + worker_unit default.target > "$unit_dir/c0mpute-worker.service" || return 1 + "$C0MPUTE_HOME/bin/c0mpute" worker stop >/dev/null 2>&1 # a daemon from an earlier install + systemctl --user daemon-reload 2>/dev/null + systemctl --user enable c0mpute-worker >/dev/null 2>&1 + systemctl --user restart c0mpute-worker 2>/dev/null || return 1 + ok "worker running as a systemd user service as $(id -un) (no root; restarts on crash)" + # Lingering starts user services at boot, before anyone logs in. logind + # lets a user enable it for themselves without sudo on most distros. + if [ "$(loginctl show-user "$(id -un)" -p Linger --value 2>/dev/null)" = "yes" ] \ + || loginctl enable-linger "$(id -un)" >/dev/null 2>&1; then + ok " starts at boot (systemd lingering on for $(id -un); undo: loginctl disable-linger)" + else + warn " starts when you log in. To start at boot too: sudo loginctl enable-linger $(id -un)" + fi + return 0 +} + +start_worker() { + [ "$NO_START" -eq 1 ] && return 0 + bin="$C0MPUTE_HOME/bin/c0mpute" + [ -x "$bin" ] || return 0 + say "starting the c0mpute worker (auto-registers + serves its models)" + + if command -v systemctl >/dev/null 2>&1 && [ -d /run/systemd/system ]; then + # Keep a system service an earlier install (or --system-service) set up, + # so an upgrade never leaves two workers running. + if [ -f /etc/systemd/system/c0mpute-worker.service ]; then + start_system_service && return 0 + # Never start a second worker next to the system service. + warn "a c0mpute-worker system service from an earlier install exists and needs sudo to restart:" + warn " sudo systemctl restart c0mpute-worker" + warn " (or switch to a user service: sudo systemctl disable --now c0mpute-worker &&" + warn " sudo rm /etc/systemd/system/c0mpute-worker.service, then re-run this installer)" + return 0 + fi + if [ "$SYSTEM_SERVICE" = "1" ]; then + start_system_service && return 0 + warn "systemd system service setup failed (needs sudo); using a user service instead" + fi + start_user_service && return 0 + if [ "$(id -u)" = "0" ]; then + start_system_service && return 0 + fi fi # Fallback: detached daemon. Restart onto the new binary on upgrade. @@ -804,6 +866,7 @@ main() { # House-model opt-outs reach infernet's installer through the environment. export INFERNET_NO_HOUSE_MODELS="${INFERNET_NO_HOUSE_MODELS:-}" export INFERNET_NO_UNCENSORED_MODELS="${INFERNET_NO_UNCENSORED_MODELS:-}" + export INFERNET_UNCENSORED_MODELS="${INFERNET_UNCENSORED_MODELS:-}" chain_install infernet "$INFERNET_INSTALL_URL" || true house_models_notice fi