DAI — Decentralized Artificial Intelligence. A sovereign network you govern. Influence what AI does at the protocol — not through a corporation.
翻译准备中
其他JavaScript · HTMLGitHub @iamaihub在线运营者未确认
试用过了吗? 点一下就好,无需登录。
DAI Miner node. P2P, decentralized Artificial Intelligence powered by Skills, MCPs, datasets and compute resources of community.
翻译准备中
取自仓库 README
正在加载完整 README。
查看完整 README ↗Decentralized compute layer for Artificial Intelligence, AI-agents, automations and robotics. Decentralized, encrypted, anonymous AI.
Download the latest .deb / .AppImage / Windows installer from miner.iamai.kg.
Inference runs in-process via QVAC (the @qvac/sdk dependency) — there's no
separate engine to install. On first launch the model (default qwen3-1.7b) is
downloaded and loaded automatically.
The desktop app includes Chat (private sync / public async job queue, blockchain history autocomplete), Explorer (blocks, addresses, completed jobs), and P2P exchange tabs. Every node ships builtin MCP tools (public-apis + onion search) plus a set of no-auth HTTP MCPs (docs, ads, catalog, …). Complex chat is planned as a short catalog-first cascade (candidate cards, not full schemas); extra servers use the standard mcpServers JSON format (command, args, env, optional url + headers) in Settings.
Model still downloading? The miner still works — chat and skill queries fall back to peer miners on the network that already have the model.
# Install dependencies once
npm install
# Copy example config and fill in your wallet + bootnodes
cp config.example.json config.json
# Start mining
npm start
# or: node src/cli.js start
docker run -v ~/.dai-miner:/root/.dai-miner ghcr.io/dai/miner:latest
Config file is loaded from the first location that exists (in order):
DAI_CONFIG env var (full path)./config.json (project root — convenient after git clone)./.dai-miner/config.json<script-dir>/.dai-miner/config.json~/.dai-miner/config.jsonMinimal example:
{
"bootnodes": ["https://miner.iamai.kg"],
"inferenceMode": "auto",
"model": "qwen3-1.7b",
"walletApiPort": 3456
}
The miner creates a DAI wallet automatically on first run and stores it in ~/.dai-miner/wallets/. To use an existing wallet, set daiWallet to its DAI address.
| Field | Default | Description |
|---|---|---|
daiWallet | auto-created | DAI address that earns rewards (created on first run if not set) |
bootnodes | production bootnode | Peer discovery + chain sync entry points |
inferenceMode | auto | cpu / gpu / auto |
model | qwen3-1.7b | QVAC model id/alias (qwen3-0.6b…qwen3-8b) or a GGUF URL |
walletApiPort | 3456 | Port for the local API server |
computeEnabled | true | Set false to run as relay-only |
region | auto | Geographic region tag for job routing |
autoStart | true | Start mining immediately on launch |
RPC endpoints can be configured per-chain under the rpc key — see config.example.json.
QVAC SDK 0.21+ can split one model's layers across several machines over llama.cpp's RPC backend, so a model too big for any single device can still load — set the same qvacCluster.topic on each of your own devices (phone, laptop, desktop) under qvacCluster in config.json. Off by default: the channel is unauthenticated, so this is for a private group of devices you control, never the open miner network. See _comment_qvacCluster in config.example.json for the fields.
┌──────────────────────────────────────────────────────────────────┐
│ App Layer (iamai.kg) │
│ Frontend · Profiles · Voting · Feedback │
└───────────────────────────┬──────────────────────────────────────┘
│ submits scan jobs ▼ reads results
┌───────────────────────────▼──────────────────────────────────────┐
│ DAI Miner Network (this repo) │
│ │
│ ┌──────────────┐ P2P gossip (blocks, txs, status) │
│ │ Miner Node │◄────────────────────────────► Miner Node │
│ │ │ │
│ │ • PoW mining │ ← race to compute first valid verdict → │
│ │ • LLM brain │ │
│ │ • Skills │ ← publish/run on-demand agent logic → │
│ │ • P2P market │ ← peer-to-peer DAI/fiat exchange → │
│ │ • Chain sync │ ← bootnode for peer discovery / catch-up → │
│ │ • Wallet API │ │
│ └──────────────┘ │
│ │
│ IPFS durability layer (chain snapshots, brain state, peers) │
└──────────────────────────────────────────────────────────────────┘
Each block contains:
height, previousHash, timestamp, minerWalletscanResults[] — mined job results (verdict scans, skill outputs, compute replies): requestId, verdict, profile, reasoning, minerWallet, signaturestateTransitions[] — on-chain state deltas applied by this block: job-submitted (public job queue metadata + promptPreview), escrow, brain updates, brain-state-roottransactions[] — signed DAI token transfers with noncescoinbaseReward — up to 1 DAI per block, paid to the nodes that did the AI work. Reward model v2 (active above REWARD_V2_HEIGHT in src/rewards/reward.js): if compute work exists, the proposer keeps a small PROPOSER_CUT (~10%) and the rest is split among workers weighted by delivered compute (a deterministic token estimate, so splitting into many identities earns nothing extra); a block with no real work mints only a small keepalive (KEEPALIVE_UDAI), not a full DAI, so emission tracks demand. Blocks at/below the flag-day boundary keep the legacy 60/40 split (history stays valid). Amounts are consensus-recomputed by every node in src/consensus/coinbase-validator.js — gossiped amounts are never trusted.nonce, difficulty, chainWork — PoW fields (chainWork is cumulative hex bigint)stateRoot — SHA-256 of sorted {address, balance, nonce} at this heightbrainStateRoot — SHA-256 of weights.json + pools.json at this heightminerSignature, minerSigningPublicKey — ed25519 signature over block hash| Data | How |
|---|---|
| New blocks | P2P gossip new-block (flood-fill, TTL=4) |
| Pending transactions | P2P gossip new-tx |
| Node status (methodsHash, region, load) | P2P gossip node-status |
| Chain history (cold start) | HTTP pull from bootnode /chain/blocks + IPFS snapshot fallback |
| Verified signals (canonical set + hash) | HTTP from iamai.kg + IPFS fallback |
| Brain feedback events | Peer-to-peer push + bootnode /brain/events |
| Signal weight updates | Same as brain feedback |
| Peer records (host:port) | Bootnode /peers + IPFS peer directory |
| IPFS CIDs (chain, brain, peers) | Bootnode /ipfs/latest + /ipfs/update |
| Property | Implementation |
|---|---|
| Consensus | Longest-chain (heaviest chainWork) |
| PoW | SHA-256, target N leading zeros, auto-adjusts to 30 s/block |
| Fork resolution | Orphan pool + chainWork comparison; reorg with journal rollback |
| Transactions | Account model with nonces (replay protection) + ed25519 signatures |
| Double-spend | Pending balance lock in mempool + nonce validation at application |
| Block signatures | Proposer signs block hash with ed25519 identity key |
| Result signatures | Every scan result signed by computing miner |
Every miner exposes an HTTP API for the mobile wallet and external tools. Amounts are in μDAI (1 DAI = 1 000 000 000 μDAI).
| Endpoint | Description |
|---|---|
GET /status | Node status, chain height, reputation |
GET /api/miner/info | Detailed miner info (version, uptime, peers) |
GET /api/wallet/balance?address=<addr> | Balance in μDAI + assets map of stablecoin holdings ({ aiGEL: { raw, display } }) |
GET /api/assets | On-chain asset registry: DAI + the 5 stablecoins (tickers, decimals, display names, per-currency gas prices) |
GET /api/wallet/nonce?address=<addr> | Current nonce for transaction signing |
GET /api/wallet/transactions?address=<addr> | Transaction history |
GET /api/wallet/history?address=<addr> | Full history with block confirmations |
GET /api/wallet/jobs?address=<addr>&limit=20 | Public job history for a wallet — joins job-submitted transitions with mined scanResults; returns {jobs, latestSkillMemory, chatTurns} |
POST /api/wallet/send | Transfer DAI ({from, to, amount, privateKey}) |
POST /api/wallet/register-key | Register an ed25519 signing key for an address |
POST /api/tx/submit | Submit a pre-signed DAITransaction |
GET /api/tx/pending | Inspect mempool |
| Mode | Context source |
|---|---|
| Private | In-memory chat history + _skillMemory from the last inline skill run on this device; chat runs synchronously |
| Public | Same session memory plus on-chain job history for the paying wallet (job-submitted + mined scanResults), via GET /api/wallet/jobs?address=...; paid jobs are queued asynchronously |
Public follow-ups (e.g. "give me the link") can resolve from chain-stored skill outputs when the session is new or on another device. Prompt text on chain is stored as promptPreview (max 500 chars) on each job-submitted transition. The desktop app's Chat tab shows a queue pill while public jobs are pending.
Browse the local chain from the Electron Explorer tab or via HTTP:
| Endpoint | Description |
|---|---|
GET /api/explorer/blocks?page=0&limit=20 | Recent blocks (newest first); each entry includes jobCount when the block contains mined results |
GET /api/explorer/block/:height | Full block JSON (scanResults, stateTransitions, transactions, hash, miner, reward) |
GET /api/explorer/search?q=<query> | Lookup by block height, tx/block hash, or wallet address |
Address search returns balance, recent DAI transfers, and completed jobs for that wallet. Block detail shows completed jobs in block (from scanResults) and any job submissions (job-submitted transitions not yet mined in the same block).
Meilisearch is mandatory and starts automatically with every miner node (like the model download). On first run the binary is downloaded to ~/.dai-miner/bin/meilisearch; data lives in ~/.dai-miner/meilisearch-data. If port 7700 is already in use (e.g. your own Docker container), the node reuses that instance.
As you type in Chat, the miner searches indexed blockchain job history (promptPreview, replies, skill outputs) and suggests past questions. Repetitive prompts can return a cached on-chain reply without re-running compute.
Remote clients (mobile wallet, other UIs) call the miner wallet API — not Meilisearch directly:
| Endpoint | Description |
|---|---|
GET /api/search/suggest?q=<prefix>&wallet=<addr>&limit=8 | Autocomplete — past prompts + reply snippets |
GET /api/search/history-match?q=<message>&wallet=<addr> | Full cached reply when the prompt closely matches history |
POST /chat/ask also checks history automatically (similarity ≥ 0.86) and may return { fromChainHistory: true }.
Indexing: On startup, each new block, and when jobs complete. NDJSON mirror: ~/.dai-miner/search/chat-history.ndjson.
"meilisearch": {
"mandatory": true,
"autoStart": true,
"port": 7700,
"bindHost": "127.0.0.1",
"indexJobs": "dai-chat-history"
}
Meilisearch listens on localhost only; expose search to the network via walletApiPort (default 3456) with localOnly: false.
The mobile DAI Wallet app uses the same APIs on any connected miner (Ask AI tab).
The chain carries four regional stablecoins alongside DAI — aiGEL, KGST,
aiETB, aiBTN. They use 2 decimals (1 aiGEL = 100 raw units); DAI keeps 9
(1 DAI = 1e9 μDAI). Supply is minted once in the genesis allocation to the
treasury address — there is no runtime mint; future supply changes are network
upgrades.
On-chain currencies: DAI, aiGEL, KGST, aiETB, aiBTN. P2P market also supports: USDT, USDC, GELT (off-chain).
POST /api/wallet/send and /api/tx/submit accept a
currency field (omit for DAI). A transaction's hash includes currency
ONLY when non-DAI, so every historical DAI tx/signature stays valid.currency — the fee floor is priced
per currency (config.gasPrices overridable) and the miner receives
exactly the currency paid (no conversion). The signed payment proof binds
the currency (6th key of the payment hash when non-DAI).p2p-swap-filled transition — no payment-sent step, proceeds go directly to
the taker's own dai address (no receival-wallet input). Off-chain legs (bank
transfer, USDT…) keep the manual escrow/release flow.| Endpoint | Description |
|---|---|
POST /job | Submit a job, returns {jobId, statusUrl, resultUrl}. type: 'verdict' (default) is a scan request; type: 'skill' runs a skill; type: 'compute' runs a user-specified model (and optional dataset) — see below. |
GET /job/:id/status | Poll: queued / computing / done / error |
GET /job/:id/result | Full verdict + profile + evidence when done |
GET /jobs | List active jobs |
POST /gossip | Receive P2P gossip envelopes from peers |
skill and compute jobs always require a fee (maxBudget > 0, in raw units
of the job's currency — μDAI for DAI, the default) and a
signed payment proof — the node rejects the request outright (the job never
runs) unless it includes a valid Ed25519 signature, from a key already
registered via POST /api/wallet/register-key, over a hash binding the
payment to that exact jobId + this node's wallet + maxBudget + the
requester's current nonce (GET /api/wallet/nonce). This debits the
requester's DAI balance and pays the miner network; there is no unverified
fallback for these job types. verdict jobs may optionally carry the same
maxBudget/paymentTx fields, but a fee isn't required for them (the
default identity scan / LLM chat is free).
Pricing (gas). Paid jobs are metered per AI compute token at
gasPrice μDAI per token — default 1 μDAI/token (μDAI is the smallest
unit, so this is the price floor). Since 1 DAI = 1e9 μDAI, 1 DAI buys up to
1,000,000,000 tokens. The fee is tokensUsed × gasPrice and can never be
less than the tokens the job actually used (e.g. 1,000 tokens ⇒ ≥ 1,000 μDAI);
unused budget is refunded. A job whose maxBudget can't cover its token cost is
rejected up front with 402 FEE_TOO_LOW.
DAI price. DAI has no fixed or oracle price — the only reference is the best
open P2P order. GET /api/p2p/price[?quoteCurrency=USDT-TRC20] returns the
market price (best bid/ask/mid) derived solely from live P2P orders.
A compute job looks like:
{
"type": "compute",
"model": "llama3.1:8b",
"dataset": "some-org/some-dataset",
"payload": { "prompt": "Summarize the top 5 rows" },
"maxBudget": 500000000,
"requesterAddress": "dai...",
"paymentTx": { "txHash": "...", "signature": "..." }
}
The SDKs build and sign paymentTx for you — see runCompute() /
submitJob() in sdk-js.
/api/estimate)The eth_estimateGas of DAI jobs. Send the same fields a job or chat request carries and get
back the AI tokens the whole pipeline will use, the minimum fee this node accepts, and a
recommended budget — before you sign or escrow anything. Read-only: no skill, MCP tool or model
runs, and attachments are sized, not stored.
curl -s localhost:3456/api/estimate -d '{
"prompt": "summarise the attached report and compare it with the latest news",
"attachments": [{ "name": "report.md", "text": "…" }],
"currency": "DAI"
}'
# GET works for quick prompts: /api/estimate?prompt=…¤cy=DAI&mcp=shop__search
| Field | Meaning |
|---|---|
prompt / messages | A compute job's prompt, or an OpenAI-style messages[] (type: "chat", floors at the prompt alone) |
history, requesterAddress | Prior turns; with an address the on-chain public turns the executor merges in are counted too |
attachments | Same shapes as chat (text, contentBase64, dataUrl). Text is inlined and measured; images are not metered |
skillId, type: "skill" | A specific skill (else the router picks one from the prompt, as a job would) |
mcp | MCP tool names (server__tool) to run in a cascade |
dataset | An installed Hugging Face dataset — the exact rows the job would inject are measured |
currency, maxOutputTokens, route, model, address | Fee currency (quoted off the live P2P book), output reserve, false to skip routing, model, /job address |
The response has tokens (prompt, output, skillCompute, total, each {min, max}),
calls (every model call the pipeline makes), a breakdown per contributor tagged
measured / bounded / assumed, fees.minimum (what the node rejects below) and
fees.recommended (max of that and the pipeline's worst case), outputCap, and warnings.
Anything whose size only exists after running — what a skill fetched, what an MCP tool
returned — is bounded by the executor's own caps rather than guessed. Routed plans are
predicted from the deterministic router; the live model-planner may choose differently.
SDKs: estimate() in the JS, Python, Rust, Kotlin and Swift SDKs wraps this endpoint (typed results; works against remote nodes without a local node).
Skills are on-demand agent modules that extend what miners can compute. Builtin skills include dai_identity, read_farcaster, read_zora, read_paragraph, code_audit, and web_search.
Economics: deploy a public skill for 1 DAI (escrowed for network code_audit); the community stakes toward a 10 DAI graduation threshold before the skill goes live on all nodes.
| Endpoint | Description |
|---|---|
GET /api/skills | List available skills with context summaries and economics (proposeFeeDai, graduationThresholdDai) |
GET /api/skills/prefs | Skill staking preferences for an address |
POST /api/skills/propose | Propose a new skill {manifest, code, private} — 1 DAI for public proposals |
POST /api/skills/:id/stake | Stake DAI toward graduation (10 DAI total activates the skill) |
| Endpoint | Description |
|---|---|
POST /chat/route | Route a message to the best matching skill {message, budget, wallet} |
POST /chat/ask | Full AI reply — routes to a skill or falls back to free chat {message, wallet, requesterAddress, history}; may return cached chain reply |
| Endpoint | Description |
|---|---|
POST /api/chat | Streaming chat via local QVAC (Ollama-compatible response shape) |
POST /api/generate | QVAC generate (Ollama-compatible response shape) |
GET /api/models | List models available on this node (built-in + loaded + registry) |
GET /api/brain/state | Current weights count, feedback count, model info |
GET /api/brain/weights | Full weights.json |
POST /api/brain/feedback | Submit human correction {address, aiVerdict, correction, comment, signals} |
POST /api/brain/vote | Submit signal weight vote {method, voteType, vote, stakeWeight} |
POST /api/brain/sync/event | Receive brain event pushed by a peer |
Peer-to-peer DAI trading with on-chain escrow. Orders are matched locally; escrow is settled on the DAI chain.
| Endpoint | Description |
|---|---|
GET /api/p2p/currencies | List supported quote currencies |
GET /api/p2p/orders | Open order book (filters: side, quoteCurrency) |
GET /api/p2p/orders/my?address=<addr> | Orders created by this address |
GET /api/p2p/orders/:id | Single order detail |
POST /api/p2p/orders | Create a new order {side, baseCurrency, quoteCurrency, amount, price, address} |
POST /api/p2p/orders/:id/cancel | Cancel an open order |
POST /api/p2p/orders/:id/select | Taker selects an order to trade |
GET /api/p2p/trades/my?address=<addr> | Active and past trades for an address |
GET /api/p2p/trades/:id | Single trade detail |
POST /api/p2p/trades/:id/:action | Advance trade state (payment-sent, release, cancel, dispute) |
POST /api/p2p/local-auth | Sign a P2P auth payload with the node's local wallet |
| Endpoint | Description |
|---|---|
POST /api/push/register | Register an Expo push token {address, token} |
POST /api/push/send | Send a push notification to a registered address |
GET /api/push/tokens | List registered tokens (admin) |
Miners automatically pin data to IPFS and share CIDs via the bootnode.
| What | When | CID shared as |
|---|---|---|
| Chain snapshot (last 500 blocks) | Every 100 blocks | chain |
| Brain state (weights + pools) | After every feedback/vote + every 30 min | brain |
| Own peer record (host:port signed) | After every bootnode registration | selfPeer |
| Peer directory (all known peers) | Bootnode pins every 60 s after changes | peers |
CIDs are cached locally in ~/.dai-miner/ipfs_cid_cache.json. If every bootnode is unreachable at startup, the node uses cached CIDs to discover peers from IPFS and to pull chain snapshots for catch-up — not just for peer discovery.
Configure a pinning service via env vars:
IPFS_API_URL=https://api.pinata.cloud/pinning/pinFileToIPFS
IPFS_API_KEY=yourPinataJWT
Without configuration, the node uses a local Kubo daemon if one is running.
GET /chain/tip
GET /chain/blocks?from=0&to=100
POST /submit-block
POST /register (signed peer registration)
GET /peers (verified peer list with host:port)
POST /brain/events (receive signed brain events)
GET /brain/events?since=<ts>
GET /brain/stats
GET /ipfs/latest (latest chain, brain, peers CIDs)
POST /ipfs/update (miners push new CIDs)
Running your own bootnode:
node src/bootnode.js --port 8080 --data-dir ~/.dai-bootnode
npm test # unit tests (vitest)
npm run test:watch # watch mode
npm run test:integration # end-to-end (requires dev/ checker)
Tests cover: P2P gossip, block/result signatures, chainWork fork resolution, transactions + nonces, double-spend protection, PoW mining + abort, balance journal rollback, job deduplication, on-chain job indexing (chain-job-index), and chat history search (chat-history-search).
All heavy dependencies (the Electron binary and the LLM model) are downloaded over the network. On a slow or unstable connection a download can drop midway. The app now retries and resumes automatically, but if you still get stuck:
Electron failed to install correctly on npm start
The Electron binary (~100 MB) didn't finish downloading during npm install
(npm install doesn't fail when this happens). Re-run one of:
# Re-run the official downloader (no resume — may take a few tries)
node node_modules/electron/install.js
# Or use a faster mirror if the official host is slow/blocked
ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ node node_modules/electron/install.js
# Windows (PowerShell): $env:ELECTRON_MIRROR='https://npmmirror.com/mirrors/electron/'; node node_modules/electron/install.js
A postinstall check prints these same instructions automatically when the
binary is missing.
Model download fails at startup
QVAC downloads the model in-process on first run and retries automatically. If it
can't finish (slow/unstable connection), just restart the miner — the download
resumes. You can also pick a smaller model in ~/.dai-miner/config.json:
{ "model": "qwen3-0.6b" }
Until the model is ready, public-mode chat/skills fall back to peer miners that already have it.
npm run build:bin # standalone binaries (pkg)
npm run build:deb # .deb package (requires fpm)
npm run build:electron # Electron desktop app (current platform)
npm run build:electron:linux # Linux: AppImage + .deb (x64 + arm64)
npm run build:electron:win # Windows: NSIS installer + portable
npm run build:electron:mac # macOS: .dmg (x64 + arm64)
npm run build:electron:all # all platforms
The .deb needs no inference engine — QVAC runs in-process and the model downloads on first run.
src/
core/ block.js, scanRequest.js, transaction.js
chain/ chain-job-index.js (join job-submitted + scanResults for wallet/explorer)
search/ meilisearch-server.js (auto-start), chat-history-search.js
consensus/ pow.js, chain-selection.js
network/ p2p-gossip.js
signals/ methods-manager.js (canonical signal set sync)
compute/ dai-adapter.js → delegates to dev/src checker + brain
brain/ brain-sync.js (network-wide brain state sync)
storage/ chain-store.js, balance-journal.js, ipfs-store.js,
ipfs-sync.js, dataset-sync.js
wallet/ wallet.js (ed25519 keys, nonces, balances)
rewards/ reward.js (1 DAI/block, 60/40 split)
jobs/ job-queue.js, geo.js, gas-estimator.js
validation/ result-validator.js
rpc/ resolver.js, networks.js
p2p/ order-store.js, escrow.js ← P2P exchange
skills/
manager.js skill lifecycle (install, stake, run)
loader.js load builtin + user skills at startup
sandbox/ skill-runner.js (sandboxed execution)
builtin/ dai_identity.md, read_farcaster.md, read_zora.md,
read_paragraph.md, code_audit.md
miner-node.js main orchestrator
bootnode.js peer discovery + chain relay + IPFS registry
cli.js
electron/ GUI desktop app (Electron 31)
renderer/
index.html main UI (Home, Logs, Chat, Explorer, P2P, Settings tabs)
renderer.js frontend logic (chat queue, history autocomplete, model selector, explorer)
i18n.js internationalization strings
main.cjs IPC + QVAC model warm-up + window management
preload.js context bridge
landing/ Promotional landing page + binary downloads
binaries/ dai-miner-linux-x64.deb / .AppImage / windows-x64.exe
test/ vitest unit tests
scripts/ build and dev helper scripts