diff --git a/docs/readme/noosphere.md b/docs/readme/noosphere.md index e3a0ea6..e3ed490 100644 --- a/docs/readme/noosphere.md +++ b/docs/readme/noosphere.md @@ -4,6 +4,10 @@ description: A Gateway to Verifiable Off-Chain Intelligence # NoΓΆsphere +> **πŸ“˜ Builder manual:** the full Noosphere documentation β€” running an agent, selling compute +> per-call with x402, and requesting compute from smart contracts β€” lives at +> [**docs.hpp.io/noosphere**](/noosphere). + Smart contracts are powerful but limited β€” they can’t think, adapt, or process complex real-world data. As Web3 intersects with AI, RWA, and scientific computation, this becomes a major bottleneck. Noosphere introduces a verifiable off-chain intelligence layer, enabling smart contracts to securely delegate inference and computation ### Limitations of Smart Contracts diff --git a/docusaurus.config.ts b/docusaurus.config.ts index bdbe20e..1373dd6 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -105,6 +105,18 @@ const config: Config = { editUrl: 'https://github.com/hpp-io/docs/tree/main/', }, ], + // Noosphere docs β€” the verifiable off-chain compute network on HPP, served + // at /noosphere as a standalone product section (same pattern as /x402). + [ + '@docusaurus/plugin-content-docs', + { + id: 'noosphere', + path: 'noosphere', + routeBasePath: 'noosphere', + sidebarPath: './sidebarsNoosphere.ts', + editUrl: 'https://github.com/hpp-io/docs/tree/main/', + }, + ], ], themes: [ '@docusaurus/theme-mermaid', @@ -113,7 +125,7 @@ const config: Config = { { hashed: true, indexDocs: true, - docsRouteBasePath: ['/', 'hub', 'hpp-router', 'x402', 'hpp-coder'], + docsRouteBasePath: ['/', 'hub', 'hpp-router', 'x402', 'hpp-coder', 'noosphere'], highlightSearchTermsOnTargetPage: true, language: ['en'], }, @@ -165,6 +177,13 @@ const config: Config = { label: 'HPP Coder', position: 'right', }, + { + type: 'docSidebar', + sidebarId: 'noosphereSidebar', + docsPluginId: 'noosphere', + label: 'Noosphere', + position: 'right', + }, { href: 'https://github.com/hpp-io/docs', label: 'GitHub', diff --git a/noosphere/configuration.md b/noosphere/configuration.md new file mode 100644 index 0000000..177d088 --- /dev/null +++ b/noosphere/configuration.md @@ -0,0 +1,121 @@ +--- +title: Configuration +sidebar_label: Configuration +description: The Noosphere agent's config.json, block by block β€” chain, containers, verifiers, scheduler, payload storage, and VRF. +--- + +# Configuration + +The agent is configured by a single `config.json` (generate one interactively with +`npm run generate:config`; template in +[`config.example.json`](https://github.com/hpp-io/noosphere-agent-js/blob/main/config.example.json)). +Secrets are never written into the file β€” use `${ENV_VAR}` substitution and put values in `.env`. + +## Blocks + +| Block | Purpose | +| --- | --- | +| `chain` | RPC/WS endpoints, Router & Coordinator addresses, wallet (keystore path + receiving address). Addresses per network: [Registry & deployments](./registry-and-deployments.md) | +| `containers[]` | The Docker images this agent can run β€” see [Container contract](./container-contract.md#registering-the-container) | +| `verifiers[]` | Verifier contracts this agent serves, each optionally paired with a proof-service container | +| `scheduler` / `retry` | Scheduled-subscription interval commitment and retry policy | +| `payload` | Large input/output storage (below) | +| `vrf` | Opt-in NoosphereVRF epoch serving (pair with the registry's `noosphere-vrng` container) | +| `x402Seller` | The separate per-call selling rail β€” configured here but documented with its own product: [Sell from an agent](/x402/sell-from-an-agent) | + +## `chain` + +```jsonc +"chain": { + "enabled": true, + "rpcUrl": "https://sepolia.hpp.io", + "wsRpcUrl": "wss://sepolia.hpp.io", + "routerAddress": "0x480a4f7506548773040d47dd7b6372dbf71358d4", + "coordinatorAddress": "0xeda4a7957e8f5de6cd6bd747c3ccd5e1c295302c", + "deploymentBlock": 295062, // start scanning from here β€” use a recent block + "processingInterval": 5000, // ms between chain-processing passes + "wallet": { + "keystorePath": "./.noosphere/keystore.json", + "paymentAddress": "0xYourReceivingWallet" // written by setup:wallet, or set by hand + } +} +``` + +`npm run generate:config` fills the addresses for the network you pick (from its built-in +per-network presets; the [community registry](./registry-and-deployments.md) supplies the +container/verifier catalog). It leaves `paymentAddress` as the zero address β€” set it, or let +`npm run setup:wallet` fill it when you create the agent's payment wallet. + +:::note +`config.json` is parsed as plain JSON against the agent's TypeScript types β€” there is **no +schema validation**, so a misspelled key is silently ignored. `${ENV_VAR}` substitution works +in any string value; an unset variable is left as the literal `${…}` with only a console +warning. +::: + +## `containers[]` + +```jsonc +"containers": [ + { + "id": "hf-sentiment", // referenced by subscriptions / registry ID + "name": "hf-sentiment", // docker name (agent prefixes it) + "image": "hf-sentiment:latest", + "port": "8090", // where /computation listens inside + "env": { "HF_TOKEN": "${HF_TOKEN}" } + } +] +``` + +## `verifiers[]` + +Serve proof-checked subscriptions: each entry names the on-chain verifier contract and, +when the proof is produced off-chain, the companion proof-service container. +`PROOF_SERVICE_PRIVATE_KEY` in `.env` signs proof submissions. + +## `scheduler` / `retry` / `containerExecution` + +```jsonc +"scheduler": { "enabled": true, "cronIntervalMs": 60000, "syncPeriodMs": 3000 }, +"retry": { "maxRetries": 3, "retryIntervalMs": 30000 } +``` + +An optional `containerExecution` block (`timeout`, `connectionRetries`, +`connectionRetryDelayMs`) tunes how the agent calls your containers. + +## `payload` + +Large inputs/outputs stay off-chain; the agent resolves URI-based payloads: + +| Scheme | Use case | Env | +| --- | --- | --- | +| `data:` | Inline base64 below `payload.uploadThreshold` | β€” | +| `ipfs://` | IPFS / Pinata | `PINATA_API_KEY`, `PINATA_API_SECRET`, `IPFS_GATEWAY` | +| `https://` | S3-compatible storage (R2/S3/MinIO) | `R2_ENDPOINT`, `R2_BUCKET`, `R2_ACCESS_KEY_ID`, `R2_SECRET_ACCESS_KEY`, `R2_PUBLIC_URL_BASE` | + +```jsonc +"payload": { "uploadThreshold": 1024, "defaultStorage": "s3" } +``` + +Defaults when the block is omitted: `uploadThreshold: 1024`, `defaultStorage: "ipfs"`. +IPFS/S3 backends can also be configured inline via `payload.ipfs { … }` / `payload.s3 { … }` +instead of env vars. + +## Environment variables + +| Variable | Purpose | +| --- | --- | +| `KEYSTORE_PASSWORD` | Decrypts the agent keystore *(required)* | +| *(any)* | `${ENV_VAR}` substitution is generic β€” reference any variable from a `config.json` string value | +| `EXPRESS_PORT` | Agent API port (default `4000`) | +| `PROOF_SERVICE_PRIVATE_KEY` | Only for verifiers with a proof service | +| `R2_*` / `PINATA_*` / `IPFS_*` | Payload storage backends (above) | + +## Networks + +| Network | Chain ID | RPC | +| --- | --- | --- | +| HPP Mainnet | `190415` | `https://mainnet.hpp.io` | +| HPP Sepolia | `181228` | `https://sepolia.hpp.io` | + +Full contract addresses: [Registry & deployments](./registry-and-deployments.md). diff --git a/noosphere/container-contract.md b/noosphere/container-contract.md new file mode 100644 index 0000000..d3f7a1e --- /dev/null +++ b/noosphere/container-contract.md @@ -0,0 +1,99 @@ +--- +title: Container contract +sidebar_label: Container contract +description: The one-endpoint interface every Noosphere container implements β€” POST /computation in, { output } out. +--- + +# Container contract + +Everything an agent runs for the compute network is an ordinary Docker image that implements +**one endpoint**: + +``` +POST /computation +Content-Type: application/json + +{ "input": "", ...extra fields } +``` + +β†’ responds + +```json +{ "output": "" } +``` + +That's the whole interface. The agent starts your container, forwards the request inputs to +`localhost:/computation`, and delivers `output` on-chain as the subscription result. + +## Rules of thumb + +- **`output` is a string.** Return structured results as a JSON-encoded string; consumers decode it. +- **Stateless requests.** Each call should be self-contained; keep model state (weights, caches) + in the image or a mounted volume. +- **Fail loudly.** A non-200 response or a crash means no delivery is submitted β€” the consumer + is never charged for failed work. +- **Size the port.** The `containers[]` entry declares the internal port the agent posts to. + +## A complete example (~30 lines) + +[`examples/hf-sentiment`](https://github.com/hpp-io/noosphere-agent-js/tree/main/examples/hf-sentiment) +wraps a free HuggingFace model with FastAPI: + +```python +from fastapi import FastAPI +from pydantic import BaseModel +from transformers import pipeline + +app = FastAPI() +clf = pipeline("sentiment-analysis", + model="distilbert-base-uncased-finetuned-sst-2-english") + +class Req(BaseModel): + input: str = "" + text: str | None = None + +@app.post("/computation") +def compute(req: Req): + result = clf(req.text or req.input)[0] + return {"output": f"{result['label']} ({result['score']:.4f})"} +``` + +```dockerfile +FROM python:3.11-slim +RUN pip install --no-cache-dir fastapi uvicorn "transformers<5" torch \ + --extra-index-url https://download.pytorch.org/whl/cpu +COPY app.py . +CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8090"] +``` + +Swap the `pipeline(...)` task and the input/output mapping for any other model β€” text +generation, summarization, translation, embeddings, your own fine-tune. + +> Pin `"transformers<5"` if you use seq2seq pipelines (summarization/translation) β€” v5 removed +> those tasks. + +## Registering the container + +```jsonc +"containers": [ + { + "id": "hf-sentiment", // referenced by services / subscriptions + "name": "hf-sentiment", // docker container name (agent prefixes it) + "image": "hf-sentiment:latest", + "port": "8090", // where /computation listens inside + "env": { "HF_TOKEN": "${HF_TOKEN}" } // optional, ${VAR} comes from .env + } +] +``` + +The agent pulls the image if needed and manages the container lifecycle (start on demand via +the Docker socket). To make the container requestable by other consumers, publish it to the +[community registry](./registry-and-deployments.md#contributing-to-the-registry) β€” subscriptions +reference containers by their registry ID +([Request compute on-chain](./request-onchain-compute.mdx)). + +## Large inputs and outputs + +For payloads too big for a request body or on-chain storage, the agent resolves URI-based +payloads (`data:`, `ipfs://`, `https://` S3-compatible) via the `payload` config block β€” see +[Configuration](./configuration.md#payload). diff --git a/noosphere/dashboard.mdx b/noosphere/dashboard.mdx new file mode 100644 index 0000000..b77d7aa --- /dev/null +++ b/noosphere/dashboard.mdx @@ -0,0 +1,59 @@ +--- +title: The dashboard +sidebar_label: Dashboard +description: The Noosphere agent's web dashboard β€” node health, scheduler and event counters, wallets, x402 seller earnings, and on-chain compute history, panel by panel. +--- + +# The dashboard + +Every agent ships a web dashboard: `npm run dev` β†’ **http://localhost:3100**. It's the fastest +answer to "is my node healthy, and what has it earned?". Screenshots below are from a live +production agent. + +## Agent (home) + +![Agent dashboard β€” scheduler, events, wallet](/img/noosphere/dashboard-agent.png) + +| Panel | What it tells you | +| --- | --- | +| **Header badges** | `Healthy` = agent API is up Β· `WebSocket` = live chain connection (falls back to polling if your RPC has no WS) | +| **Scheduler** | The scheduled-subscription engine: how many subscriptions it's *Tracking*, *Active* interval commitments, *Pending Txs* in flight, its commitment/sync intervals, and its `Running` status | +| **Events (All Requests)** | Lifetime request counters β€” *Total / Completed / Failed / Skipped / Expired*. **Completed going up = you are earning.** Failed β†’ check container logs; Skipped = requests for containers you don't serve or that another agent won | +| **Agent Wallet** | The delivery-signing wallet: address, **gas balance** (keep it funded!), the Router/Coordinator addresses the node is wired to, your payment wallets, and the RPC in use | +| **Available Containers** Β· **Verifiers** | Which images are up (with ports) and which verifier contracts + proof services this node serves | + +## x402 Seller + +*(Shown when `x402Seller.enabled` β€” see [Sell from an agent](/x402/sell-from-an-agent).)* + +![x402 seller tab β€” earnings, services, paid jobs](/img/noosphere/dashboard-seller.png) + +| Panel | What it tells you | +| --- | --- | +| **KPI cards** | *Earnings 30d* Β· *Paid calls 24h* (+ all-time) Β· *Settle success 24h* Β· *Services* (direct / on-chain) | +| **Receiving (payTo) vs Agent gas (EOA)** | The two wallets, side by side: where buyer payments land (direct settlement, no custody) vs what funds delivery gas for the on-chain rail | +| **Services** | Each service's settlement mode, price (USDC.e/call), schemes, calls (24h), earnings (30d) | +| **Recent paid jobs** | Live feed: time, service, payer, scheme, amount, **settle tx** (on-chain proof), status | + +Raw data behind this tab: `GET /api/seller/{summary,wallets,services,jobs,earnings}` on the +agent API (`:4000`). + +## Computing History + +![Computing history β€” deliveries with fees and profit](/img/noosphere/dashboard-history.png) + +Every on-chain delivery your node made: subscription, interval, container, the delivery +transaction, the **fee earned vs gas spent β€” your profit per job**. Use it to sanity-check +pricing: if gas regularly eats the fee, the subscription's `feeAmount` is too low for your +network conditions. + +**Prepare History** (header button) shows the scheduler's interval commitments β€” useful when +you're debugging why a scheduled subscription was or wasn't picked up. + +## Reading the dashboard like an operator + +- **Am I healthy?** Header badges + Scheduler `Running`. +- **Am I earning?** Events *Completed* rising (on-chain rail) Β· Seller *Recent paid jobs* + filling (x402 rail). +- **Am I about to stop earning?** Agent Wallet balance low β†’ deliveries will start reverting. +- **Was that job worth it?** Computing History β†’ fee vs gas per delivery. diff --git a/noosphere/first-request.mdx b/noosphere/first-request.mdx new file mode 100644 index 0000000..3cb7c28 --- /dev/null +++ b/noosphere/first-request.mdx @@ -0,0 +1,384 @@ +--- +title: "Tutorial: hello-world, end to end" +sidebar_label: "Tutorial: hello-world" +description: The complete beginner walkthrough β€” install the agent with Docker, sell the hello-world container per-call with x402, and serve an on-chain compute request. Every command, output, and screenshot from a real run. +--- + +# Tutorial: hello-world, end to end + +One container, both markets. In this tutorial you will, from scratch: + +1. **Run an agent in Docker** serving the `hello-world` container +2. **Sell it per-call with x402** β€” and pay for it yourself as the buyer +3. **Serve an on-chain compute request** β€” a contract asks, *your* agent delivers + +Everything below β€” commands, outputs, screenshots β€” comes from one real run on **HPP Sepolia**. + +:::tip Want a feel before the terminal? +The **[Noosphere Playground](https://dapptest.hpp.io/)** runs this same protocol from a +browser dApp β€” on-chain LLM chat and VRF games on HPP Sepolia, no installation. +::: + +**You'll need:** Node.js β‰₯ 18, Docker β‰₯ 20.10, [Foundry](https://book.getfoundry.sh/) +(`cast`/`forge`), `jq`, and one wallet (the "buyer") holding: + +- a little **HPP Sepolia ETH** β€” free from the [HPP Sepolia Faucet](https://faucet.hpp.io/) + (0.01 ETH goes a very long way at HPP gas prices) +- some testnet **USDC.e** for Part 2 β€” see + [Networks & Token β†’ Funding](/x402/networks-and-token#funding); on Sepolia it's + distributed on request via [Official Links](/community/official-links) + +The agent itself starts with **zero funds**. + +--- + +## Part 1 β€” Run the agent (Docker) + +### 1.1 Install + +```bash +git clone https://github.com/hpp-io/noosphere-agent-js.git +cd noosphere-agent-js +npm install +``` + +### 1.2 Create the agent's key + +The agent signs with a keystore. Generate a fresh key and import it: + +```bash +cast wallet new # note the Address + Private key +cp .env.example .env # set KEYSTORE_PASSWORD= + +PRIVATE_KEY=0x KEYSTORE_PASSWORD= npm run init +``` + +```text +πŸ” Initializing Noosphere Agent Keystore + +Creating keystore at ./.noosphere/keystore.json... +Encrypting EOA keystore... +βœ“ Keystore initialized: ./.noosphere/keystore.json + EOA Address: 0x6D4c904369C1ED5B42371D36714532E00Da30F26 + +βœ… Keystore initialized successfully! + +IMPORTANT: + 1. Backup the keystore file: ./.noosphere/keystore.json + 2. Store the password securely + 3. Never commit the keystore file to git + 4. Fund the wallet address with ETH for gas fees +``` + +That printed address is **your agent**. It needs ETH only for Part 3 (on-chain deliveries) β€” +Part 2 works with an empty wallet. + +### 1.3 Configure + +Docker mounts `docker/config.docker.json` as the agent's config. Start from the shipped +`config.example.json`, save it as `docker/config.docker.json`, and make it look like this +(a minimal Sepolia config β€” `hello-world` container + x402 selling on): + +```jsonc +{ + "chain": { + "enabled": true, + "rpcUrl": "https://sepolia.hpp.io", + "wsRpcUrl": "wss://sepolia.hpp.io", + "routerAddress": "0x480a4f7506548773040d47dd7b6372dbf71358d4", + "coordinatorAddress": "0xeda4a7957e8f5de6cd6bd747c3ccd5e1c295302c", + "deploymentBlock": 295062, // start scanning here β€” use a RECENT block (explorer β†’ latest) + "processingInterval": 5000, + "wallet": { + "keystorePath": "./.noosphere/keystore.json", + "paymentAddress": "0x6D4c904369C1ED5B42371D36714532E00Da30F26" // your agent address + } + }, + "containers": [ + { + "id": "0x2fe108c896fbbc20874ff97c7f230c6d06da1e60e731cbedae60125468f8333a", + "name": "noosphere-hello-world", + "image": "ghcr.io/hpp-io/example-hello-world-noosphere:latest", + "port": "8081" // the port /computation listens on INSIDE the image + } + ], + "x402Seller": { + "enabled": true, + "payTo": "0x6D4c904369C1ED5B42371D36714532E00Da30F26", // your agent address + "facilitators": { "eip155:181228": "https://facilitator-sepolia.hpp.io" }, + "defaultAsset": { + "eip155:181228": { + "address": "0x401eCb1D350407f13ba348573E5630B83638E30D", + "extra": { "name": "Bridged USDC", "version": "2" } + } + }, + "services": [ + { + "name": "hello-world", + "containerId": "0x2fe108c896fbbc20874ff97c7f230c6d06da1e60e731cbedae60125468f8333a", + "settlement": "direct", + "network": "eip155:181228", + "schemes": ["exact"], + "x402Price": "1000", // $0.001 per call + "inputSchema": { "type": "object" }, + "receipt": true, + "description": "Hello-world compute, my first paid service" + } + ] + } +} +``` + +The container `id` comes from the [community registry](./registry-and-deployments.md) β€” it's +the on-chain identity subscriptions reference. Router/Coordinator addresses: +[Registry & deployments](./registry-and-deployments.md). + +### 1.4 Build and start + +```bash +npm run docker:build +npm run docker:up # agent :4000, dashboard :3100 +npm run docker:logs +``` + +Healthy startup (excerpt from the real run): + +```text +βœ“ Loaded keystore: ./.noosphere/keystore.json + EOA: 0x6D4c904369C1ED5B42371D36714532E00Da30F26 +[x402-seller] initialized β€” 1 service(s) (direct=1, onchain=0), payTo=0x6D4c904369C1ED5B42371D36714532E00Da30F26 +[x402-seller] mounted direct routes β€” POST /paid/compute/hello-world (receipt) +[x402-seller] mcp mounted β€” /mcp (+/mcp/sse), tools: compute_hello-world +Express server running on http://localhost:4000 +WebSocket ready +πŸ“Š Total subscriptions in registry: 189 +βœ“ Sync completed - processed all 189 subscriptions +``` + +The agent pulled the `hello-world` image, started it as a sibling container (via the Docker +socket), mounted your paid route + MCP tool, and synced the chain. Open the dashboard at +**http://localhost:3100**: + +![Fresh dashboard β€” healthy, connected, nothing served yet](/img/noosphere/tut-01-dashboard-fresh.png) + +The **x402 Seller** tab already lists your `hello-world` service β€” price, scheme, zero calls β€” +waiting for its first sale. + +--- + +## Part 2 β€” Sell it (and buy it) with x402 + +Your service is already live at `POST /paid/compute/hello-world`. Now be your own first +customer. The buyer side is standard x402 β€” wrap `fetch` with a payment-signing client. +Set up a minimal buyer in a fresh directory: + +```bash +mkdir hello-buyer && cd hello-buyer +npm init -y +npm install @x402/core @x402/evm @x402/fetch viem tsx +``` + +Save the **HPP Sepolia** client from the +[buyer quickstart](/x402/quickstart-buyers#2-wrap-fetch-with-payment) as `buy.ts`, change +`SCHEME` to `"exact"` (what our service advertises), and append the paid call: + +```ts +const res = await fetchWithPay("http://localhost:4000/paid/compute/hello-world", { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ input: "hello from the tutorial" }), +}); +console.log(JSON.stringify(await res.json(), null, 2)); +``` + +```bash +PRIVATE_KEY=0x npx tsx buy.ts +``` + +The buyer's first request gets `402 + price`, signs a USDC.e authorization (no gas), retries β€” +and gets the result **plus a receipt with the on-chain settlement**: + +```jsonc +{ + "jobId": "…", + "service": "hello-world", + "output": "hello world, your input was: hello from 01", + "receipt": { + "sellerServiceId": "hello-world", + "payer": "0x26907E8d732F4abe3E120ef1743352d12738c116", + "settlement": { + "network": "eip155:181228", + "scheme": "exact", + "transaction": "0x30402029661813c03b94f2a9c3a5dda53d56be0746be8ad8ec3bb34f5ea6e73d", + "amount": "1000", + "asset": "0x401eCb1D350407f13ba348573E5630B83638E30D" + } + } +} +``` + +That `transaction` is a real on-chain transfer: **$0.001 of USDC.e moved from the buyer to +your agent's wallet**, gas sponsored by the facilitator. The Seller tab now shows it: + +![Seller tab after the first paid call β€” earnings, the job, the settle tx](/img/noosphere/tut-03-seller-paid.png) + +Buyers can also reach the same service as an **MCP tool** (`compute_hello-world` at `/mcp`) +and β€” once you're on a public URL β€” discover it on the +[x402 Explorer](https://x402-explorer.hpp.io). Full selling guide: +[Sell from an agent](/x402/sell-from-an-agent). + +--- + +## Part 3 β€” Serve an on-chain request + +Now the other market: a **smart contract** asks for the same compute, and your agent delivers +the result on-chain. + +### 3.1 Prepare your agent for deliveries + +Two one-time steps the x402 rail didn't need: + +```bash +# a) delivery gas β€” send a little Sepolia ETH (from the faucet) to your AGENT address (1.2's output) + +# b) the agent's on-chain payment wallet (receives subscription fees β€” +# not to be confused with the buyer-side compute wallet of 3.3): +PRIVATE_KEY=0x KEYSTORE_PASSWORD= \ +WALLET_FACTORY_ADDRESS=0xe1ccab0b5deeca0b240f9bbaeccdbcb252934fa7 \ +npm run setup:wallet +``` + +```text +βœ… Agent wallet setup completed successfully! + βœ“ Agent EOA: 0x6D4c904369C1ED5B42371D36714532E00Da30F26 + βœ“ Payment Wallet (CA): 0xd8B16479944FE9015453836C04b8010822bC7906 +``` + +`setup:wallet` wrote the new wallet address into `config.json` +(`chain.wallet.paymentAddress`). Docker mounts `docker/config.docker.json`, so mirror that +field there and restart (`npm run docker:restart`) so the container picks it up. + +### 3.2 Deploy a consumer contract (the buyer side) + +The consumer is a contract extending `TransientComputeClient` β€” the +[`noosphere-evm`](https://github.com/hpp-io/noosphere-evm) repo ships a sample, +`MyTransientClient`. Clone it **at the deployed protocol version** and deploy the sample +pointed at the Sepolia Router: + +```bash +git clone https://github.com/hpp-io/noosphere-evm.git && cd noosphere-evm +git checkout 80bce17f1c679142c161197a5ddd612e70864f6e # the protocol version live on-chain +git submodule update --init --recursive + +BUYER_KEY=0x +BUYER_ADDRESS= + +forge create src/v1_0_0/sample/MyTransientClient.sol:MyTransientClient \ + --rpc-url https://sepolia.hpp.io --private-key $BUYER_KEY --broadcast \ + --constructor-args 0x480a4f7506548773040d47dd7b6372dbf71358d4 $BUYER_ADDRESS +``` + +```text +Deployed to: 0x2F2800CD931D2d4A6e9CFB4Fbdc17cdaB3aD26ff +``` + +:::caution Match the deployed protocol version +The repo's newest `main` targets the **next** protocol version β€” its `createSubscription` +takes a `redundancy` parameter the live contracts don't have, so a `main`-compiled client +reverts with no data. The `80bce17` checkout above matches what is actually deployed. +::: + +### 3.3 Create a compute wallet, approve, subscribe, request + +Four transactions β€” the whole consumer-side ceremony (real hashes from this run): + +```bash +RPC=https://sepolia.hpp.io +CLIENT=0x2F2800CD931D2d4A6e9CFB4Fbdc17cdaB3aD26ff # your deploy from 3.2 +FACTORY=0xe1ccab0b5deeca0b240f9bbaeccdbcb252934fa7 +ROUTE=$(cast format-bytes32-string "Coordinator_v1.0.0") + +# 1) a compute wallet to escrow fees (skip if you have one) + fund it a little +TX=$(cast send $FACTORY "createWallet(address)" $BUYER_ADDRESS \ + --private-key $BUYER_KEY --rpc-url $RPC --json | jq -r .transactionHash) +WALLET=0x$(cast receipt $TX --rpc-url $RPC --json \ + | jq -r --arg t $(cast sig-event "WalletCreated(address,address,address)") \ + '.logs[] | select(.topics[0]==$t) | .data' | cut -c27-66) +echo $WALLET # 0xd8b16479… in this run +cast send $WALLET --value 20000000000000 --private-key $BUYER_KEY --rpc-url $RPC + +# 2) let the consumer contract spend from it (native token, unlimited) +cast send $WALLET "approve(address,address,uint256)" $CLIENT \ + 0x0000000000000000000000000000000000000000 $(cast max-uint) \ + --private-key $BUYER_KEY --rpc-url $RPC + +# 3) subscribe: which container, what fee per delivery, which wallet pays +TX=$(cast send $CLIENT "createSubscription(string,bool,address,uint256,address,address,bytes32)" \ + "noosphere-hello-world" false 0x0000000000000000000000000000000000000000 100 $WALLET \ + 0x0000000000000000000000000000000000000000 $ROUTE \ + --private-key $BUYER_KEY --rpc-url $RPC --json | jq -r .transactionHash) +SUB=$(cast to-dec $(cast receipt $TX --rpc-url $RPC --json \ + | jq -r --arg t $(cast sig-event "SubscriptionCreated(uint64)") \ + '.logs[] | select(.topics[0]==$t) | .topics[1]')) +echo $SUB # 194 in this run + +# 4) request compute with your input +cast send $CLIENT "requestCompute(uint64,bytes)" $SUB \ + $(cast from-utf8 '{"input":"hello from the tutorial"}') \ + --private-key $BUYER_KEY --rpc-url $RPC +``` + +### 3.4 Watch your agent deliver + +Within seconds, the agent logs (real excerpt): + +```text +RequestStarted: 0x… SubscriptionId: 194 +πŸ“¦ Container: noosphere-hello-world +πŸ“₯ Inputs received: {"input":"hello from the tutorial"} +βœ“ Execution completed in 12ms +βœ“ Result delivered successfully (block 295109) +``` + +The dashboard's **Events** card ticks *Completed: 1* β€” your node just earned its first +on-chain delivery fee. (The Failed/Skipped counts are leftovers from this capture session's +earlier attempts β€” a clean run shows 1/1. One of them was a lost delivery race, explained in +3.5.) + +![Events after the delivery β€” Completed 1](/img/noosphere/tut-04-events-completed.png) + +**Computing History** shows the job with its fee and gas: + +![Computing history β€” the delivered job](/img/noosphere/tut-05-history.png) + +### 3.5 Verify on-chain + +The consumer contract received the callback β€” read the stored result hash straight from the +chain: + +```bash +cast call $CLIENT "lastReceivedOutputHash()(bytes32)" --rpc-url $RPC +# 0x663dedda91f9788a34661eec74b68cfbe1dd20b20826c0bca4008c3de53d0444 +``` + +> On a live network, **any agent serving that container may win the delivery** β€” during this +> run, an unrelated production agent beat ours to an earlier request. That's the marketplace +> working. To guarantee *your* agent serves a request (like this tutorial's final run), use a +> container ID only your node serves. + +--- + +## What you just proved + +| | x402 rail (Part 2) | On-chain rail (Part 3) | +| --- | --- | --- | +| Buyer | An HTTP client with a wallet | A smart contract | +| Request path | `POST /paid/compute/hello-world` | subscription β†’ `requestCompute` | +| Your agent's job | verify payment β†’ run container β†’ respond | watch chain β†’ run container β†’ deliver tx | +| You got paid | USDC.e per call, instantly, no gas | Subscription fee per delivery | +| Proof | settle tx + signed receipt | delivery tx + on-chain callback | + +**Next steps:** [serve your own model](./container-contract.md) Β· +[build a real consumer](./request-onchain-compute.mdx) Β· +[get listed on the explorer](/x402/sell-from-an-agent) Β· +[read the dashboard like an operator](./dashboard.mdx) diff --git a/noosphere/how-it-works.md b/noosphere/how-it-works.md new file mode 100644 index 0000000..014ead5 --- /dev/null +++ b/noosphere/how-it-works.md @@ -0,0 +1,144 @@ +--- +title: How it works +sidebar_label: How it works +description: The Noosphere protocol β€” subscriptions, the commitment lifecycle through Router and Coordinator, compute wallets and billing, verification, and NoosphereVRF. +--- + +# How it works + +Noosphere connects three parties through the chain: **consumers** (contracts that want +computation), **agents** (nodes that run it), and the **protocol contracts** that coordinate +requests, escrow, and delivery between them. + +## The roles + +- **Consumer.** A smart contract that extends one of the client base contracts and creates a + **compute subscription**. It funds a **compute wallet** and receives results in a callback. +- **Protocol contracts.** + - **Router** β€” the single address consumers talk to. It registers protocol components and + routes each subscription to the right Coordinator version (`routeId`). + - **Coordinator** β€” runs the request lifecycle: opens requests as **commitments**, validates + agent deliveries, and reports fulfillment back through the Router, which invokes the + consumer callback. + - **Billing** β€” meters fees per delivery and settles them from the consumer's compute wallet. + - **WalletFactory / Wallet** β€” creates and manages the escrow wallets that fund subscriptions. +- **Agent.** A node running [`noosphere-agent-js`](https://github.com/hpp-io/noosphere-agent-js) + (built on the [`@noosphere/sdk`](https://github.com/hpp-io/noosphere-sdk) packages). It watches + chain events, runs the requested **container**, and submits the delivery transaction. +- **Containers.** Ordinary Docker images exposing one endpoint β€” + [`POST /computation`](./container-contract.md). Subscriptions reference containers by ID from + the [community registry](./registry-and-deployments.md), so *any* agent running that container + can serve the request. + +## The request lifecycle + +```mermaid +sequenceDiagram + autonumber + participant C as πŸ“œ Consumer contract + participant R as 🧭 Router + participant K as πŸŽ› Coordinator + participant A as πŸ€– Agent + participant D as 🐳 Container + C->>R: createComputeSubscription(containerId, fee, wallet, …) + Note over C: requestCompute(subscriptionId, inputs)
β€” inputs stay stored ON THE CLIENT + C->>R: sendRequest(subscriptionId, interval) + R->>K: open request (commitment) + K-->>A: RequestStarted event + A->>C: getComputeInputs(subscriptionId, interval, …) + A->>D: POST /computation { input } + D-->>A: { output } + A->>K: deliver(output [, proof]) + K->>R: validated fulfillment + R->>C: callback: _receiveCompute(output, node, …) + Note over K: Billing pays the agent's fee
from the consumer's compute wallet +``` + +1. **Subscribe.** The consumer creates a subscription: which container to run, the fee per + delivery (`feeToken` + `feeAmount`), which compute wallet funds it, and optionally a + **verifier**. +2. **Request.** The client stores the inputs and calls `sendRequest` (or the interval schedule + opens one). The Coordinator records a **commitment** β€” the on-chain fingerprint of what was + asked, for which fee, in which interval β€” and emits `RequestStarted`. Inputs never travel + through the Router: agents fetch them from the client (`getComputeInputs`), which is what + keeps large inputs cheap. +3. **Execute.** Agents see the request, fetch the inputs, run the container, and submit the + output on-chain (the delivery transaction is the agent's gas cost). +4. **Deliver & settle.** The Coordinator validates the delivery against the commitment, the + Router invokes the consumer's callback, and Billing pays the agent from the compute wallet. With + `useDeliveryInbox`, results are stored in a **DeliveryInbox** for the consumer to pull instead. + +## For comparison: the x402 per-call flow (separate rail) + +The same agent can also sell per-call over plain HTTP with [x402](/x402) β€” worth seeing side +by side, because **none of the protocol contracts above appear in it**. No subscription, no +Router/Coordinator, no compute wallet: the buyer pays per request and the +[HPP facilitator](/x402/facilitator) settles straight to the operator's wallet. + +```mermaid +sequenceDiagram + autonumber + participant B as πŸ§‘β€πŸ’» Buyer (app / AI agent) + participant A as πŸ€– Agent (paid route) + participant D as 🐳 Container + participant F as βš™οΈ HPP Facilitator + B->>A: POST /paid/compute/svc (no payment) + A-->>B: 402 + payment terms (price Β· payTo Β· asset) + Note over B: Sign a USDC.e authorization β€” no gas needed + B->>A: retry + payment-signature + A->>F: verify(payment) + F-->>A: valid βœ“ + A->>D: POST /computation { input } + D-->>A: { output } + A->>F: settle(payment) + Note over F: On-chain tx: USDC.e β†’ operator's wallet + A-->>B: 200 { output, receipt } +``` + +| | On-chain rail (above) | x402 rail | +| --- | --- | --- | +| Who asks | Smart contract, via subscription | Anyone, via HTTP/MCP | +| Protocol contracts involved | Router Β· Coordinator Β· Billing Β· compute wallet | **None** | +| Payment | Escrowed fee per delivery | Signed stablecoin authorization per call | +| Result returns | On-chain callback / DeliveryInbox | The HTTP response itself | + +Configuration and selling guide: [Sell from an agent](/x402/sell-from-an-agent). + +## Transient vs scheduled subscriptions + +| | Transient | Scheduled | +| --- | --- | --- | +| Shape | One-shot: create β†’ request β†’ one delivery | Recurring: every `intervalSeconds`, up to `maxExecutions` | +| Inputs | Stored on-chain per request | Produced by the consumer per interval | +| Typical use | "Ask the model, act on the answer" | Price feeds, periodic scoring, batch jobs | +| Base contract | `TransientComputeClient` | `ScheduledComputeClient` | + +Subscriptions activate lazily and can be cancelled by their owner at any time. + +## Payment: compute wallets and billing + +Consumers pre-fund a **compute wallet** (created via the WalletFactory) and point their +subscriptions at it. On every valid delivery, **Billing** settles the subscription's +`feeAmount` in `feeToken` from that wallet β€” the delivering agent receives `feeAmount` minus +a small protocol cut (the fee comes *out of* `feeAmount`, not on top). One delivery per +request; the first valid delivery wins. (The delivering agent must also have its own +factory-created payment wallet to receive the fee.) + +Budget rule of thumb: exactly `feeAmount` is escrow-locked per request, so plan +`feeAmount Γ— executions`. + +## Verification: trust is a dial + +- **Baseline** β€” accept the delivering agent's answer as-is (no verifier): cheapest and fastest. +- **Verifier contracts** β€” a subscription can name an on-chain verifier (the `IVerifier` + interface). Deliveries then carry a proof and only count once the verifier accepts it. + Available verifiers are listed per network in the + [community registry](./registry-and-deployments.md). + +## NoosphereVRF + +The same agent network serves **NoosphereVRF** β€” epoch-based verifiable randomness that +contracts can consume on both networks (addresses in +[Registry & deployments](./registry-and-deployments.md)). Agent operators opt in by running the +registry's `noosphere-vrng` container and enabling the `vrf` config block. Try it live: the +[Playground](https://dapptest.hpp.io/)'s **Raffle** and **Dice** draw with NoosphereVRF. diff --git a/noosphere/intro.md b/noosphere/intro.md new file mode 100644 index 0000000..a8a96f7 --- /dev/null +++ b/noosphere/intro.md @@ -0,0 +1,82 @@ +--- +title: Noosphere +sidebar_label: Overview +slug: / +description: Noosphere is HPP's on-chain compute framework β€” smart contracts request off-chain computation as subscriptions, decentralized agents execute it in containers, and results, payment, and verification all flow through the chain. +--- + +# Noosphere + +Smart contracts can't run an AI model, crunch a dataset, or react to complex off-chain +conditions. **Noosphere** fixes that: it's HPP's on-chain framework for **requesting off-chain +compute from smart contracts** β€” and getting the result back on-chain, with payment and +verification handled by the protocol. + +The defining property: **both the request and the settlement live on-chain.** A consumer +contract creates a *compute subscription*; decentralized **agents** pick the work up, run it in +ordinary Docker containers, and deliver the output back through the protocol, which pays them +from the consumer's escrow wallet β€” all in the same on-chain lifecycle. + +```mermaid +flowchart LR + C["πŸ“œ Consumer contract
(your dApp)"] -- "subscription +
request" --> P["🧭 Noosphere protocol
Router Β· Coordinator Β· Billing"] + P -- "request event" --> A["πŸ€– Agents
(decentralized nodes)"] + A --> D["🐳 Containers
(any Docker image)"] + A -- "deliver output" --> P + P -- "callback + payment" --> C +``` + +## What you can build + +- **AI-powered dApps** β€” ask an LLM or a model a question from a contract and act on the answer + on-chain: risk scoring, valuations, predictions, content generation. +- **Recurring pipelines** β€” scheduled subscriptions run the same computation every interval: + price feeds, portfolio rebalancing signals, periodic risk checks. +- **Verifiable randomness** β€” consume **NoosphereVRF**, served by the same agent network. + +## One agent, two markets + +The same agent node β€” and the same containers β€” can earn from **two independent markets**. +This documentation covers the on-chain protocol; per-call selling is a separate product with +its own docs ([x402 on HPP](/x402)). + +| | β›“ Compute network (this docs) | πŸ’° x402 per-call selling | +| --- | --- | --- | +| Who buys | **Smart contracts** (subscriptions) | Apps, scripts, AI agents β€” plain HTTP/MCP | +| Request path | **On-chain** β€” Router/Coordinator route it, results return by callback | Off-chain β€” a normal paid HTTP call; no protocol contracts involved | +| Settlement | **On-chain billing** from the consumer's escrow wallet, per delivery | Stablecoin payment per call, settled by the [HPP facilitator](/x402/facilitator) straight to your wallet | +| Funds to start | Agent wallet needs ETH (delivery gas) | **None** β€” an empty wallet works, gas is sponsored | +| Verification | On-chain verifier contracts | Optional signed execution receipt | +| Docs | You are here | **[Sell from an agent](/x402/sell-from-an-agent)** | + +## The pieces + +| Piece | What it is | Repository | +| --- | --- | --- | +| **Protocol contracts** | Router (entry point), Coordinator (request lifecycle), Billing + compute wallets (escrow & fees), client base contracts | [`noosphere-evm`](https://github.com/hpp-io/noosphere-evm) | +| **Agent node** | Runs containers, watches the chain, delivers results | [`noosphere-agent-js`](https://github.com/hpp-io/noosphere-agent-js) | +| **SDK** | `@noosphere/*` npm packages the agent is built from (contracts, crypto, payload, registry) | [`noosphere-sdk`](https://github.com/hpp-io/noosphere-sdk) | +| **Registry** | Community catalog of containers, verifiers, and the deployed contract addresses per network | [`noosphere-registry`](https://github.com/hpp-io/noosphere-registry) | + +Noosphere is live on **HPP Mainnet** and **HPP Sepolia** β€” contract addresses in +[Registry & deployments](./registry-and-deployments.md). + +## Try it in your browser first + +No setup at all: the **[Noosphere Playground](https://dapptest.hpp.io/)** (HPP Sepolia) lets +you drive the protocol from a wallet-connected dApp β€” create a compute subscription and chat +with an **on-chain LLM**, or draw provably-fair winners with **NoosphereVRF** (Raffle & Dice). + +[![Noosphere Playground](/img/noosphere/playground-home.png)](https://dapptest.hpp.io/) + +## Choose your path + +| I want to… | Start here | +| --- | --- | +| **feel it, zero setup** (browser dApp) | **[Noosphere Playground β†—](https://dapptest.hpp.io/)** | +| **see it work end to end** (15 min, Sepolia) | **[Tutorial: hello-world](./first-request.mdx)** | +| understand the protocol | **[How it works](./how-it-works.md)** | +| call compute **from my contract** | **[Request compute on-chain](./request-onchain-compute.mdx)** | +| **run an agent** and earn fees | **[Set up the node](./node-setup.mdx)** β†’ [serve the network](./serve-compute-network.mdx) | +| sell my model **per-call** (x402, no funds needed) | **[Sell from an agent](/x402/sell-from-an-agent)** | +| package my model as a container | **[Container contract](./container-contract.md)** | diff --git a/noosphere/node-setup.mdx b/noosphere/node-setup.mdx new file mode 100644 index 0000000..915da35 --- /dev/null +++ b/noosphere/node-setup.mdx @@ -0,0 +1,184 @@ +--- +title: Set up the node +sidebar_label: Set up the node +description: Install and start a Noosphere agent node β€” the setup shared by both revenue rails, with the exact prompts and outputs you'll see, and a health checklist. +--- + +# Set up the node + +This is the setup **common to everything an agent can do** β€” serve the on-chain compute +network, sell per-call with x402, or both. At the end you pick your rail. + +**What you'll need** + +- Node.js β‰₯ 18 and Docker β‰₯ 20.10 (`docker ps` should work) +- ~10 minutes +- No funds yet β€” funding depends on the rail you pick [at the end](#choose-your-rail) + +## 1. Install + +```bash +git clone https://github.com/hpp-io/noosphere-agent-js.git +cd noosphere-agent-js +npm install +``` + +## 2. Generate your config (interactive) + +```bash +npm run generate:config # HPP Sepolia (testnet) by default +# npm run generate:config -- --network mainnet +``` + +The generator connects to the [community registry](./registry-and-deployments.md) and walks you +through it. A typical session: + +```text +🌐 Network: testnet (chainId: 181228) +πŸ“‘ Fetching registry from https://raw.githubusercontent.com/hpp-io/noosphere-registry/main/networks/181228.json + +πŸ“¦ Available Containers: + + [1] noosphere-hello-world + Simple Hello World example container for testing Noosphere compute requests + Port: 8081, Image: ghcr.io/hpp-io/example-hello-world-noosphere:latest + + [2] noosphere-llm + LLM (Large Language Model) inference container supporting multiple models via LLM Router and Gemini integration + Port: 8082, Image: ghcr.io/hpp-io/example-llm-noosphere:latest + ... + +Enter container numbers to add (comma-separated, e.g., "1,2") or "all": 1 + +πŸ’° Sell these containers per-call via x402? (y/N): n + +βœ“ Config generated: ./config.json + Containers: noosphere-hello-world + +πŸ“ Next steps: + 1. Update wallet.paymentAddress in config.json (receives x402 payments too) + 2. Set environment variables for container env (if any) + 3. Run: npm run init (to create keystore) + 4. Run: npm run agent (to start the agent) +``` + +Start with `noosphere-hello-world` β€” the "is everything wired" container. Add more (or +[your own](./container-contract.md)) any time by editing `config.json`; every block is +explained in the [configuration reference](./configuration.md). Answering **y** to the selling +question pre-fills the x402 seller block (it will also ask a per-call price) β€” harmless either +way, you can change it later. + +About "Next steps 1": the generator writes `wallet.paymentAddress` as the **zero address**. +Set it to the wallet that should receive your earnings β€” `npm run setup:wallet` fills it in +automatically when you [create the agent's payment wallet](./serve-compute-network.mdx), or +edit it by hand. + +```bash +cp .env.example .env # then edit: KEYSTORE_PASSWORD= +``` + +## 3. Create your wallet + +`init` imports a private key into an encrypted keystore β€” generate a fresh one first +(e.g. with Foundry's `cast`): + +```bash +cast wallet new # prints Address + Private key + +PRIVATE_KEY=0x KEYSTORE_PASSWORD= npm run init +``` + +```text +πŸ” Initializing Noosphere Agent Keystore + +Creating keystore at ./.noosphere/keystore.json... +Encrypting EOA keystore... +βœ“ Keystore initialized: ./.noosphere/keystore.json + EOA Address: 0xYourAgentAddress… + +βœ… Keystore initialized successfully! + +IMPORTANT: + 1. Backup the keystore file: ./.noosphere/keystore.json + 2. Store the password securely + 3. Never commit the keystore file to git + 4. Fund the wallet address with ETH for gas fees +``` + +Two addresses matter: + +- **Agent address** (printed above) β€” signs on-chain delivery transactions. Needs ETH **only + for the compute-network rail**. +- **Receiving address** (`chain.wallet.paymentAddress` in `config.json`) β€” where earnings + accumulate on either rail. Not set automatically: the generator leaves the zero address + until `setup:wallet` (or you) fills it. + +## 4. Run it + +```bash +npm run agent # agent API on :4000 +``` + +Healthy startup logs look like (excerpt from a real run): + +```text +πŸ“¦ Payload storage config: default=ipfs, S3=βœ—, IPFS=βœ— +βœ“ Loaded keystore: ./.noosphere/keystore.json + EOA: 0xYourAgentAddress… +πŸ”Œ WebSocket connection attempt 1/3... +βœ“ Connected via WebSocket (push-based events) +Starting from block 295062 +πŸ• Starting Scheduler Service... + Commitment generation interval: 60000ms +βœ“ Noosphere Agent is running +[INFO] Express server running on http://localhost:4000 +``` + +In a second terminal, start the [dashboard](./dashboard.mdx): + +```bash +npm run dev # dashboard on http://localhost:3100 +``` + +![Noosphere agent dashboard](/img/noosphere/dashboard-agent.png) + +## 5. Is it healthy? (checklist) + +| Check | How | Expect | +| --- | --- | --- | +| Node healthy | [Dashboard](./dashboard.mdx) at `localhost:3100` | **Healthy** + **WebSocket** badges in the header | +| Container up | `docker ps` | `noosphere-noosphere-hello-world` running | +| Chain connection | Dashboard β†’ Agent Wallet card | Router/Coordinator addresses + RPC shown | + +## Docker deployment + +Prefer everything in containers? The repo ships a compose setup β€” the agent manages your model +containers through the Docker socket (siblings, started on demand): + +```bash +npm run docker:build +npm run docker:up # agent :4000, dashboard :3100 +npm run docker:logs +``` + +It mounts `docker/config.docker.json` as the config and your `.noosphere/` keystore; secrets +come from `.env`. + +## Troubleshooting (setup) + +| Symptom | Fix | +| --- | --- | +| Agent won't start | `.env` has `KEYSTORE_PASSWORD`? Docker running? `config.json` exists? (`npm run init` itself requires an existing `config.json`) | +| Port already in use | Something else on 4000/3100 β€” set `EXPRESS_PORT` or remap compose ports | +| Dashboard shows no connection | RPC/WS unreachable β€” check `chain.rpcUrl`/`wsRpcUrl` in `config.json` | +| Container errors | `docker logs noosphere-` | + +## Choose your rail + +Your node is up. Now decide what it earns from β€” either, or both; they share the containers +and don't interfere: + +| Rail | Buyers | Funds to start | Next | +| --- | --- | --- | --- | +| β›“ **Compute network** | Smart contracts (subscriptions, on-chain settlement) | Agent wallet needs ETH (delivery gas) | **[Serve the compute network](./serve-compute-network.mdx)** | +| πŸ’° **x402 per-call** | Apps & AI agents over HTTP/MCP (stablecoin per call) | **None** β€” empty wallet works | **[Sell from an agent](/x402/sell-from-an-agent)** *(x402 docs)* | diff --git a/noosphere/registry-and-deployments.md b/noosphere/registry-and-deployments.md new file mode 100644 index 0000000..a0349b9 --- /dev/null +++ b/noosphere/registry-and-deployments.md @@ -0,0 +1,94 @@ +--- +title: Registry & deployments +sidebar_label: Registry & deployments +description: Deployed Noosphere contract addresses on HPP Mainnet and Sepolia, and the community registry of containers and verifiers. +--- + +# Registry & deployments + +The [**community registry**](https://github.com/hpp-io/noosphere-registry) is the network's +source of truth: one JSON file per chain listing the **deployed protocol contracts**, the +**containers** agents can serve, and the **verifiers** subscriptions can require. The agent SDK +auto-syncs from it. + +## Deployed contracts + +### HPP Mainnet β€” chain ID `190415` + +| Contract | Address | +| --- | --- | +| Router | `0x043F992d67dE8c86141EA5e0897b5244cD97dac4` | +| Coordinator | `0x8b4951d0C2B15Ef4DE1f355e132A40Ac6c84E728` | +| WalletFactory | `0xD57d57F93266555302abD8EB3A1A349249453C16` | +| NoosphereVRF | `0x6d179D718C7B772CA0d6f694308fb22A516a6eFf` | + +RPC `https://mainnet.hpp.io` Β· Explorer [explorer.hpp.io](https://explorer.hpp.io) + +### HPP Sepolia (testnet) β€” chain ID `181228` + +| Contract | Address | +| --- | --- | +| Router | `0x480a4f7506548773040d47dd7b6372dbf71358d4` | +| Coordinator | `0xeda4a7957e8f5de6cd6bd747c3ccd5e1c295302c` | +| WalletFactory | `0xe1ccab0b5deeca0b240f9bbaeccdbcb252934fa7` | +| NoosphereVRF | `0xb49Cf5e93A225638cD7fa8e4479149f453AE2e39` | + +RPC `https://sepolia.hpp.io` (`wss://sepolia.hpp.io`) Β· Explorer +[sepolia-explorer.hpp.io](https://sepolia-explorer.hpp.io) + +Both networks route subscriptions with +`routeId = "Coordinator_v1.0.0"` (bytes32: +`0x436f6f7264696e61746f725f76312e302e30…`). Consumers **must pass this routeId** when +creating subscriptions β€” there is no default route: with `routeId = bytes32(0)` the +subscription is created, but every `sendRequest` reverts (`CoordinatorNotFound`). + +> Always confirm against the registry files β€” +> [`networks/190415.json`](https://github.com/hpp-io/noosphere-registry/blob/main/networks/190415.json) Β· +> [`networks/181228.json`](https://github.com/hpp-io/noosphere-registry/blob/main/networks/181228.json) β€” +> they are updated with every deployment. + +## Containers + +Community-verified images any agent can serve and any subscription can reference by ID: + +| Container | What it does | +| --- | --- | +| `noosphere-hello-world` | Echo test β€” the "is everything wired" container | +| `noosphere-llm` | LLM inference | +| `noosphere-freqtrade` | Crypto price prediction (15-minute candles) | +| `noosphere-vrng` | Serves NoosphereVRF randomness epochs | + +Each registry entry carries the image, port, base price, verification status and β€” where the +container declares one β€” an input schema: everything an agent needs to serve it and a +consumer needs to request it. + +## Verifiers + +Verifier contracts that subscriptions can require (per network). Both networks currently list +an **Immediate Finalize Verifier** β€” deliveries finalize as soon as the proof-carrying +delivery is accepted. + +## Using the registry from code + +```ts +import { RegistryManager } from '@noosphere/registry'; + +const registry = new RegistryManager({ + remotePath: 'https://raw.githubusercontent.com/hpp-io/noosphere-registry/main/networks/190415.json', + autoSync: true, +}); +await registry.load(); + +const containers = registry.searchContainers('llm'); +const hello = registry.getContainer('noosphere-hello-world'); +``` + +The SDK syncs the **container and verifier catalog**. Contract addresses are not exposed +through it β€” read them from the network JSON files linked above (or the tables on this page). + +## Contributing to the registry + +Publish your own container (or verifier) by PR to +[`hpp-io/noosphere-registry`](https://github.com/hpp-io/noosphere-registry): add an entry to the +network file with your image, port, input schema, and pricing. CI validates the schema; once +merged, every agent's registry sync can discover and serve it. diff --git a/noosphere/request-onchain-compute.mdx b/noosphere/request-onchain-compute.mdx new file mode 100644 index 0000000..d48b5aa --- /dev/null +++ b/noosphere/request-onchain-compute.mdx @@ -0,0 +1,115 @@ +--- +title: Request compute on-chain +sidebar_label: Request compute on-chain +description: Call off-chain compute from a smart contract β€” extend TransientComputeClient or ScheduledComputeClient, create a subscription, and receive results in a callback. +--- + +# Request compute on-chain + +Smart contracts request Noosphere compute through **subscriptions**. You extend a client base +contract from [`noosphere-evm`](https://github.com/hpp-io/noosphere-evm), point it at the +Router, and implement one callback. + +## Minimal one-shot client + +`TransientComputeClient` is the one-shot flavor: create a subscription, send a request with +inputs, receive the output in a callback. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +import {TransientComputeClient} from "noosphere-evm/v1_0_0/client/TransientComputeClient.sol"; +import {Commitment} from "noosphere-evm/v1_0_0/types/Commitment.sol"; +import {PayloadData} from "noosphere-evm/v1_0_0/types/PayloadData.sol"; + +contract MyClient is TransientComputeClient { + bytes32 public lastOutputHash; + + constructor(address router) TransientComputeClient(router) {} + + function createSubscription( + string memory containerId, // which container agents must run, e.g. "noosphere-hello-world" + address feeToken, // fee currency (address(0) = native) + uint256 feeAmount, // fee paid per delivery + address wallet, // your compute wallet (funds the fees; must approve this contract) + address verifier, // optional proof verifier (address(0) = none) + bytes32 routeId // coordinator route, e.g. bytes32("Coordinator_v1.0.0") + ) external returns (uint64) { + return _createComputeSubscription( + containerId, false, feeToken, feeAmount, wallet, verifier, routeId + ); + } + + function request(uint64 subscriptionId, bytes memory inputs) + external + returns (uint64, Commitment memory) + { + return _requestCompute(subscriptionId, inputs); // inputs stored HERE for agents to fetch + } + + // Called by the protocol when an agent delivers. Payloads arrive as + // PayloadData { contentHash, uri } β€” inline data or an off-chain URI. + function _receiveCompute( + uint64 subscriptionId, + uint32 interval, + bool useDeliveryInbox, + address node, + PayloadData calldata input, + PayloadData calldata output, + PayloadData calldata proof, + bytes32 containerId + ) internal override { + lastOutputHash = output.contentHash; + // resolve output.uri off-chain, or decode inline data β€” then act on it + } + + function typeAndVersion() external pure override returns (string memory) { + return "MyClient 1.0.0"; + } +} +``` + +> Signatures above match the **deployed v1.0.0 protocol** (verified against the live Sepolia +> Router). The repo's newest `main` targets the next protocol version β€” compile against the +> deployed interfaces, or `createSubscription` will revert with no data. + +The repo ships a complete example: +[`src/v1_0_0/sample/MyTransientClient.sol`](https://github.com/hpp-io/noosphere-evm/blob/80bce17f1c679142c161197a5ddd612e70864f6e/src/v1_0_0/sample/MyTransientClient.sol). + +## The subscription parameters + +| Parameter | Meaning | +| --- | --- | +| `containerId` | The compute to run β€” any agent running this container can serve you (first valid delivery wins) | +| `useDeliveryInbox` | `true` β†’ results land in the DeliveryInbox for you to pull, instead of a push callback | +| `feeToken` / `feeAmount` | What each delivery pays, from your compute wallet | +| `wallet` | Your **compute wallet** β€” created via the WalletFactory, funded by you, and **approved** for this consumer contract (`Wallet.approve(consumer, token, amount)`) | +| `verifier` | Optional `IVerifier` contract; deliveries only count once their proof verifies | +| `routeId` | Which Coordinator serves the subscription β€” `bytes32("Coordinator_v1.0.0")` on today's networks ([Registry & deployments](./registry-and-deployments.md)) | + +## Recurring work + +`ScheduledComputeClient` adds a schedule: `maxExecutions` runs, one every `intervalSeconds`. +Agents watch upcoming intervals and deliver each one β€” price feeds, periodic scoring, batch +pipelines. The callback is the same. + +## Paying for it + +Fund a compute wallet, **approve your consumer contract to spend from it**, and pass it as +`wallet`. Every delivery, the protocol's Billing moves `feeAmount` in `feeToken` from that +wallet to the delivering agent (minus a small protocol cut taken out of `feeAmount`, not +added on top). Budget `feeAmount Γ— executions`. + +## Trust levels + +- Start with `verifier = address(0)` for cheap, fast results β€” the first valid delivery wins. +- Set a `verifier` for proof-checked compute β€” agents attach proofs (from a companion proof + service) and only verified deliveries reach your callback. + +Router addresses and available container IDs per network: +[Registry & deployments](./registry-and-deployments.md). + +> **See it live:** the [Noosphere Playground](https://dapptest.hpp.io/)'s on-chain LLM chat is +> exactly this pattern behind a UI β€” "Setup Compute Subscription", then every question is a +> `requestCompute` and every answer arrives through the callback. diff --git a/noosphere/serve-compute-network.mdx b/noosphere/serve-compute-network.mdx new file mode 100644 index 0000000..7c1d4b3 --- /dev/null +++ b/noosphere/serve-compute-network.mdx @@ -0,0 +1,85 @@ +--- +title: Serve the compute network +sidebar_label: Serve the compute network +description: The on-chain rail β€” fund delivery gas, earn subscription fees per delivery, level up with verifiers and VRF, and troubleshoot the earning loop. +--- + +# Serve the compute network + +The **on-chain rail**: consumers create subscriptions on-chain, the protocol routes requests to +agents, and your node earns the subscription's fee for every valid delivery. +*(Prerequisite: a [running node](./node-setup.mdx).)* + +## 1. Fund delivery gas + +Every delivery is a transaction your **agent address** signs, so it needs ETH on the target +network (HPP Sepolia for testnet). The address was printed by `npm run init` and is shown on +the [dashboard's](./dashboard.mdx) Agent Wallet card, along with its live balance. + +## 2. Create the agent's payment wallet (one-time) + +Deliveries are only accepted from agents whose **payment wallet** was created by the +protocol's WalletFactory β€” without it, delivery transactions revert (`InvalidWallet`): + +```bash +PRIVATE_KEY=0x KEYSTORE_PASSWORD= \ +WALLET_FACTORY_ADDRESS= \ +npm run setup:wallet +``` + +```text +βœ… Agent wallet setup completed successfully! + βœ“ Agent EOA: 0xYourAgent… + βœ“ Payment Wallet (CA): 0xYourPaymentWallet… +``` + +Factory addresses per network: [Registry & deployments](./registry-and-deployments.md). +(Running in Docker? Copy the updated `config.json` over `docker/config.docker.json` and +restart.) + +## 3. How you earn + +``` +subscription (consumer) β†’ request β†’ your agent runs the container + β†’ delivery tx (your gas) β†’ Billing pays feeAmount to you +``` + +- The [dashboard](./dashboard.mdx) **Events** card counts it: *Completed* rising = earning. +- **Computing History** shows each delivery's fee earned vs gas spent β€” profit per job. +- One delivery per request β€” the first valid delivery wins, so being fast and reliable wins requests. + +**See it happen end to end**: the [first-request tutorial](./first-request.mdx) sends a real +subscription + request to your own node on Sepolia and reads the result back on-chain. + +## 4. Levels of participation + +| Level | What you run | What it earns | +| --- | --- | --- | +| **Basic worker** | Registry containers (`hello-world`, `llm`, …) | Subscription fees for plain deliveries | +| **Verified compute** | A container **plus its proof service**, configured under `verifiers[]` | Fees from subscriptions that require proof-checked results | +| **VRF operator** | The `noosphere-vrng` container + the `vrf` config block | Fees from NoosphereVRF epochs | + +## Serving your own container + +Anything that speaks the [container contract](./container-contract.md) +(`POST /computation` β†’ `{ output }`) can be served. Add it to `containers[]`; to make it +requestable by *other* consumers, publish it to the +[community registry](./registry-and-deployments.md#contributing-to-the-registry) so +subscriptions can reference its ID. + +## Troubleshooting (earning loop) + +| Symptom | Fix | +| --- | --- | +| No requests arriving | Agent wallet funded? Active subscriptions exist for a container you serve? [Send one yourself](./first-request.mdx) | +| Requests *Skipped* in Events | The request was for a container you don't serve, or another agent won it | +| Delivery reverts | Agent wallet gas balance; container ID must match the subscription's | +| Fee barely covers gas | Computing History shows fee vs gas per job β€” that subscription's `feeAmount` is too low to be worth serving | + +--- + +:::tip The other rail +The same node and containers can also earn **per-call over HTTP/MCP with zero funds** β€” see +[Sell from an agent](/x402/sell-from-an-agent) in the x402 docs. Both flows side by side: +[How it works](./how-it-works.md#for-comparison-the-x402-per-call-flow-separate-rail). +::: diff --git a/sidebarsNoosphere.ts b/sidebarsNoosphere.ts new file mode 100644 index 0000000..ed84de9 --- /dev/null +++ b/sidebarsNoosphere.ts @@ -0,0 +1,37 @@ +import type {SidebarsConfig} from '@docusaurus/plugin-content-docs'; + +// Sidebar for the standalone "Noosphere" product docs (served at /noosphere). +// Concepts -> hands-on tutorial -> operator (common setup, then a rail each β€” +// the x402 rail's detail lives in /x402, linked in place) -> contract +// developers -> reference. +const sidebars: SidebarsConfig = { + noosphereSidebar: [ + {type: 'doc', id: 'intro', label: 'Overview'}, + 'how-it-works', + 'first-request', + { + type: 'category', + label: 'Run an agent', + collapsed: false, + items: [ + 'node-setup', + 'serve-compute-network', + { + type: 'link', + label: 'Sell per-call (x402) β†—', + href: '/x402/sell-from-an-agent', + }, + 'dashboard', + ], + }, + 'request-onchain-compute', + { + type: 'category', + label: 'Reference', + collapsed: false, + items: ['container-contract', 'registry-and-deployments', 'configuration'], + }, + ], +}; + +export default sidebars; diff --git a/sidebarsX402.ts b/sidebarsX402.ts index 8cd864d..799b165 100644 --- a/sidebarsX402.ts +++ b/sidebarsX402.ts @@ -11,7 +11,7 @@ const sidebars: SidebarsConfig = { type: 'category', label: 'Build with the SDK', collapsed: false, - items: ['quickstart-sellers', 'quickstart-buyers'], + items: ['quickstart-sellers', 'sell-from-an-agent', 'quickstart-buyers'], }, 'how-it-works', 'service-directory', diff --git a/static/img/noosphere/dashboard-agent.png b/static/img/noosphere/dashboard-agent.png new file mode 100644 index 0000000..af2fe4b Binary files /dev/null and b/static/img/noosphere/dashboard-agent.png differ diff --git a/static/img/noosphere/dashboard-history.png b/static/img/noosphere/dashboard-history.png new file mode 100644 index 0000000..91955b5 Binary files /dev/null and b/static/img/noosphere/dashboard-history.png differ diff --git a/static/img/noosphere/dashboard-seller.png b/static/img/noosphere/dashboard-seller.png new file mode 100644 index 0000000..dbb4dd2 Binary files /dev/null and b/static/img/noosphere/dashboard-seller.png differ diff --git a/static/img/noosphere/playground-home.png b/static/img/noosphere/playground-home.png new file mode 100644 index 0000000..600da57 Binary files /dev/null and b/static/img/noosphere/playground-home.png differ diff --git a/static/img/noosphere/playground-llm.png b/static/img/noosphere/playground-llm.png new file mode 100644 index 0000000..2dd680b Binary files /dev/null and b/static/img/noosphere/playground-llm.png differ diff --git a/static/img/noosphere/playground-raffle.png b/static/img/noosphere/playground-raffle.png new file mode 100644 index 0000000..cc7a118 Binary files /dev/null and b/static/img/noosphere/playground-raffle.png differ diff --git a/static/img/noosphere/tut-01-dashboard-fresh.png b/static/img/noosphere/tut-01-dashboard-fresh.png new file mode 100644 index 0000000..6408b97 Binary files /dev/null and b/static/img/noosphere/tut-01-dashboard-fresh.png differ diff --git a/static/img/noosphere/tut-03-seller-paid.png b/static/img/noosphere/tut-03-seller-paid.png new file mode 100644 index 0000000..aa0c656 Binary files /dev/null and b/static/img/noosphere/tut-03-seller-paid.png differ diff --git a/static/img/noosphere/tut-04-events-completed.png b/static/img/noosphere/tut-04-events-completed.png new file mode 100644 index 0000000..0d03b6b Binary files /dev/null and b/static/img/noosphere/tut-04-events-completed.png differ diff --git a/static/img/noosphere/tut-05-history.png b/static/img/noosphere/tut-05-history.png new file mode 100644 index 0000000..9e50e6d Binary files /dev/null and b/static/img/noosphere/tut-05-history.png differ diff --git a/x402/intro.md b/x402/intro.md index 113360f..00a5e16 100644 --- a/x402/intro.md +++ b/x402/intro.md @@ -57,6 +57,7 @@ Pick your side of the payment. | How | Start here | | --- | --- | | Put a price on an HTTP endpoint (SDK) | **[Quickstart: Sellers](./quickstart-sellers.mdx)** | +| Sell any Docker container, no server code (Noosphere agent) | **[Sell from an agent](./sell-from-an-agent.mdx)** | | One command, no server code | `hpp-x402 serve` β€” see the [agent guide](pay-from-an-ai-agent.mdx#sell-from-the-cli) | ### πŸ“– Learn the concepts diff --git a/x402/sell-from-an-agent.mdx b/x402/sell-from-an-agent.mdx new file mode 100644 index 0000000..2d4a041 --- /dev/null +++ b/x402/sell-from-an-agent.mdx @@ -0,0 +1,164 @@ +--- +title: Sell from a Noosphere agent +sidebar_label: Sell from an agent +description: Turn any Docker container into a paid x402 API with a Noosphere agent β€” no server code. Buyers pay USDC.e per call over HTTP or MCP; settlement goes directly to your wallet. +--- + +# Sell from a Noosphere agent + +The [Noosphere agent](/noosphere/node-setup) has a built-in **x402 seller**: point it at any +Docker container and it serves paid HTTP routes and MCP tools for it β€” 402 challenges, payment +verification, settlement, and receipts all handled for you. No server code, unlike the +[SDK quickstart](./quickstart-sellers.mdx) where you wire the middleware yourself. + +This is **independent of the Noosphere compute network**: no subscriptions, no on-chain +requests β€” just per-call x402 payments settled by the [HPP facilitator](./facilitator.mdx) +directly to your wallet, with gas sponsored. + +> **Selling requires no funds.** An empty wallet works β€” you only ever *receive*. + +## From a free model to your first payment + +About 10 minutes end-to-end, starting from a free HuggingFace model. + +### 1. Wrap the model β€” one endpoint is the whole contract + +``` +POST /computation { "input": "", ...buyer JSON } β†’ { "output": "" } +``` + +The agent repo's +[`examples/hf-sentiment`](https://github.com/hpp-io/noosphere-agent-js/tree/main/examples/hf-sentiment) +does this for a sentiment model in ~30 lines of FastAPI +(the interface is Noosphere's standard [container contract](/noosphere/container-contract)): + +```bash +git clone https://github.com/hpp-io/noosphere-agent-js.git +cd noosphere-agent-js && npm install +cd examples/hf-sentiment +docker build -t hf-sentiment:latest . +``` + +### 2. Configure the seller + +Add the container and a service to the agent's `config.json`: + +```jsonc +{ + "containers": [ + { "id": "hf-sentiment", "name": "hf-sentiment", + "image": "hf-sentiment:latest", "port": "8090" } + ], + "x402Seller": { + "enabled": true, + "payTo": "0xYourReceivingWallet", // where the USDC.e lands + "facilitators": { "eip155:181228": "https://facilitator-sepolia.hpp.io" }, + "defaultAsset": { + "eip155:181228": { + "address": "0x401eCb1D350407f13ba348573E5630B83638E30D", + "extra": { "name": "Bridged USDC", "version": "2" } + } + }, + "services": [ + { + "name": "sentiment", // β†’ POST /paid/compute/sentiment + "containerId": "hf-sentiment", + "settlement": "direct", + "network": "eip155:181228", + "schemes": ["exact"], + "x402Price": "5000", // atomic USDC.e β†’ $0.005 per call + "inputSchema": { // validated BEFORE payment + "type": "object", "required": ["text"], + "properties": { "text": { "type": "string" } } + }, + "receipt": true, + "description": "Sentiment analysis, per call" + } + ] + } +} +``` + +Mainnet: use `eip155:190415` with `https://facilitator.hpp.io` β€” see +[Networks & Token](./networks-and-token.mdx). + +### 3. Run and earn + +```bash +npm run init # creates the agent's keystore (its signing key) +npm run agent # paid routes live on :4000 +``` + +Payments land at `x402Seller.payTo` (falling back to `chain.wallet.paymentAddress` if unset) β€” +the seller refuses to start with neither configured. + +When a buyer calls `POST /paid/compute/sentiment` with a valid payment: + +```jsonc +{ + "jobId": "6b2c1e5e-…", + "service": "sentiment", + "output": "POSITIVE (0.9998)", + "receipt": { "settlement": { "transaction": "0xb83a…" }, "…": "…" } +} +``` + +…and the USDC.e is already in your wallet. Track earnings in the agent dashboard +(`npm run dev` β†’ the **x402 Seller** tab) or `GET /api/seller/summary`. + +## What buyers can rely on + +- **Invalid input β†’ HTTP 400 *before* payment.** Your `inputSchema` gates every request; + nobody pays for a call your container can't serve. +- **Compute failure β†’ no charge.** Settlement happens only after a successful response. +- **You never custody funds.** Payment moves buyer β†’ your wallet directly on-chain; the agent + holds no spending keys. + +### Execution receipts + +With `"receipt": true` the response embeds a deterministic receipt binding the advertised +price, the on-chain settlement transaction, and sha256 hashes of the exact request and result β€” +verifiable proof of *what the payment bought*. + +## How buyers reach you + +- **HTTP** β€” any x402 client; see the [buyer quickstart](./quickstart-buyers.mdx). +- **MCP** β€” every service doubles as a `compute_` tool at `/mcp` (StreamableHTTP) and + `/mcp/sse`; AI agents pay through the [x402 MCP bridge](./pay-from-an-ai-agent.mdx). A failed + tool call cancels the payment. +- **Explorer** β€” after your first settled sale, the + [x402 Explorer](https://x402-explorer.hpp.io) indexes your service automatically. To be listed + *before* the first sale, register from the config: + +```jsonc +"x402Seller": { + "discovery": { + "enabled": true, + "apiUrl": "https://x402-explorer.hpp.io", + "publicBaseUrl": "https://your-public-host.example.com", + "register": true // signed by the keystore wallet β€” payTo must equal it + } +} +``` + +`publicBaseUrl` (your domain or tunnel) is your responsibility in production. New listings start +**pending** and become publicly visible once approved β€” see +[selling on the explorer](./explorer/sell.mdx). + +> For **local testing only**, `"demoTunnel": true` auto-starts an ephemeral Cloudflare Quick +> Tunnel. Never use it in production. + +## Pricing + +`x402Price` is in **atomic USDC.e** (6 decimals): `"1000"` = $0.001, `"5000"` = $0.005, +`"10000"` = $0.01 β€” charged per call, on success only. + +## Operating tips + +| Symptom | Fix | +| --- | --- | +| Buyer gets `400` before paying | Working as intended β€” their body failed your `inputSchema` | +| Buyer gets `402` repeatedly | Their wallet lacks USDC.e on the right network, or their client doesn't speak the advertised scheme | +| Buyer gets `502`, not charged | Your container failed β€” `docker logs noosphere-` | +| Explorer registration skipped (log warning) | `payTo` must be the agent's keystore wallet to sign β€” or rely on auto-listing after the first sale | +| `demoTunnel` fails | `cloudflared` not installed β€” and remember it's test-only |