more docs review
This commit is contained in:
@@ -26,7 +26,7 @@ Active workstream (started 2026-07-04): alpha hardening of the money/trust path.
|
||||
|
||||
Both are already migrated into `.scratch/alpha-hardening/prd.json` (AH-021 updated, AH-023 added) and the README index — ready for Ralph to pick up unattended.
|
||||
|
||||
**Ralph note:** `scripts/ralph_progress.py` tracks `docs/prd.json` (35/35 done) and does NOT see `.scratch/alpha-hardening/issues/`. No ralph loop is running and no `.ralph-tui/` state exists. `.scratch/alpha-hardening/prd.json` now has 23 stories (AH-001…AH-023); point Ralph at that file for the alpha-hardening branch. Do NOT use `ralph auto --parallel` on server.py-touching issues — 21 and 23 both touch `server.py`/`billing.py`/`audit.py`; if run in the same Ralph pass, run them serially, not in parallel (merge-conflict risk, same lesson as 03/04 previously).
|
||||
**Ralph note:** `scripts/ralph_progress.py` tracks `docs/prd.json` (US-001…US-047; base 35/35 done, friends-test arc 36–47 open/in-progress). Alpha hardening uses `.scratch/alpha-hardening/prd.json` (AH-001…AH-023). Point Ralph at the prd.json for the branch you're running.
|
||||
|
||||
**Why:** three audits agreed the alpha blockers are unauthenticated gossip (anyone can inject billing events), the free-credit faucet, and ephemeral bans.
|
||||
**How to apply:** work test-first per issue acceptance criteria; use `.venv`; `cryptography` belongs in node deps (wallet.py imports it — causes many of the 24 "failures" in a fresh env). See [[project-status]] and [[autonomous-work-style]].
|
||||
|
||||
@@ -8,7 +8,7 @@ metadata:
|
||||
|
||||
# Project Status (2026-07-13)
|
||||
|
||||
> Doc reconciliation 2026-07-13: `docs/PRD.md`, ADR numbering (0024 = distributed GGUF), and `docs/issues/` statuses updated to match `docs/prd.json` (US-001…US-035 done). Post-035 work remains in issues 36–48 and `.scratch/`.
|
||||
> Doc reconciliation 2026-07-13: `docs/PRD.md`, ADR numbering (0024 = distributed GGUF), and `docs/issues/` statuses updated to match `docs/prd.json` (US-001…US-035 done). **US-036…US-047** added to `docs/prd.json` for the friends-test / LAN-serving arc. Post-035 scratch work remains in `.scratch/`.
|
||||
|
||||
All 35 user stories in docs/prd.json are done (35/35), including the reward-system arc US-030…US-035 completed 2026-07-02:
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Status: done (base program US-001…US-035 complete; see `docs/prd.json`. Post-035 work lives in `docs/issues/36+` and `.scratch/`. Payment/settlement superseded by ADR-0015; fraud by ADR-0018.)
|
||||
Status: done (base program US-001…US-035 complete; friends-test arc US-036…US-047 in `docs/prd.json`. Payment/settlement superseded by ADR-0015; fraud by ADR-0018.)
|
||||
|
||||
# Distributed Inference Network — PRD
|
||||
|
||||
|
||||
241
docs/prd.json
241
docs/prd.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Distributed Inference Network",
|
||||
"description": "Build a distributed inference network with node, gateway, tracker, SDK, contracts, and P2P shard distribution components from the grill session PRD.",
|
||||
"description": "Distributed inference network: base program US-001…US-035 complete; post-alpha friends-test and LAN-serving arc US-036…US-047 tracked here. Active scratch features (alpha hardening, NCA, distributed GGUF) have separate prd.json files under .scratch/.",
|
||||
"branchName": "ralph/distributed-inference-network",
|
||||
"userStories": [
|
||||
{
|
||||
@@ -814,10 +814,247 @@
|
||||
"US-033"
|
||||
],
|
||||
"completionNotes": "GET /dashboard served from embedded dashboard.html (package-data, no build step) by any tracker. Panels: hive/leader (raft status), nodes+coverage grouped by model, client balances, node pending + protocol cut, settlement history with devnet explorer links, strikes/bans/forfeitures (GET /v1/registry/wallets + snapshot forfeits), RPM stats. 4s auto-refresh via fetch polling. 3 tests in tests/test_dashboard.py."
|
||||
},
|
||||
{
|
||||
"id": "US-036",
|
||||
"title": "36 — Streamed chat completions over the relay RPC path",
|
||||
"description": "Public NAT deployments proxy every chat request tracker → relay → head node. Implement true multi-frame SSE streaming over the relay WebSocket so clients see live tokens and relayed streams bill through the same SSE accounting loop as direct proxy streams. Inter-node /forward activation hops stay single-frame (ADR-0014).",
|
||||
"acceptanceCriteria": [
|
||||
"stream: true chat via relay delivers SSE chunks incrementally (≥2 distinct frame arrivals before [DONE])",
|
||||
"Relayed streamed request records nonzero billed tokens and node work credit",
|
||||
"Non-streamed relayed requests and /forward binary hops behave exactly as before (single frame, body_base64 intact)",
|
||||
"Legacy single-frame response from an old node is accepted as terminal",
|
||||
"Idle stream (no frame for 120 s) returns 504 and cleans up the relay-side queue",
|
||||
"Extend tests/test_gossip_and_relay.py alongside test_relay_rpc_round_trips_http_request_to_peer",
|
||||
"python -m pytest passes from repo root"
|
||||
],
|
||||
"priority": 36,
|
||||
"status": "needs-review",
|
||||
"notes": "Source issue: docs/issues/36-relay-streamed-chat.md. Implemented via _stream_relayed_frames in server.py; verify on public NAT relay before friends-test.",
|
||||
"dependsOn": [
|
||||
"US-029",
|
||||
"US-031"
|
||||
],
|
||||
"completionNotes": "Multi-frame relay-http-response protocol; node relay_bridge line-by-line SSE emit; relay server per-request asyncio.Queue; tracker _stream_relayed_frames with SSE billing parity. Client mid-stream disconnect accepted limitation for alpha."
|
||||
},
|
||||
{
|
||||
"id": "US-037",
|
||||
"title": "37 — Concurrent request handling in the node relay bridge",
|
||||
"description": "RelayHttpBridge currently handles relay-http-request envelopes serially, blocking up to 300 s per request. Off-LAN a node can be head of one route and downstream hop of another — overlapping routes through a shared node break. Dispatch on a bounded ThreadPoolExecutor (default 8, configurable) with per-frame WS send locking compatible with US-036 streaming.",
|
||||
"acceptanceCriteria": [
|
||||
"While one relayed request is in flight, a second relay-http-request to the same node completes without waiting for the first",
|
||||
"Responses are correctly matched by request_id when they complete out of order",
|
||||
"More than N simultaneous requests queue and all eventually complete; thread count never exceeds N workers",
|
||||
"Bridge survives a relay reconnect with workers still in flight (no crash, no deadlock; orphaned responses dropped)",
|
||||
"Configurable via meshnet-node start --relay-concurrency N (env MESHNET_RELAY_CONCURRENCY)",
|
||||
"Extend tests/test_gossip_and_relay.py",
|
||||
"python -m pytest passes from repo root"
|
||||
],
|
||||
"priority": 37,
|
||||
"status": "open",
|
||||
"notes": "Source issue: docs/issues/37-relay-bridge-concurrency.md. Critical for public friends-test; blocks concurrent head + hop on same node.",
|
||||
"dependsOn": [
|
||||
"US-036"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "US-038",
|
||||
"title": "38 — Tracker cluster join via a single seed peer",
|
||||
"description": "Tracker cluster membership is static today — a newcomer configured with only one existing peer is never learned by the rest of the hive and quorum math diverges. A joining tracker configured with any one live seed announces via hive-HMAC-signed POST /v1/cluster/join; membership changes replicate through the Raft log and persist across restarts.",
|
||||
"acceptanceCriteria": [
|
||||
"Start trackers A+B; start C with only A as seed → within one election timeout A, B, and C report the same 3-peer membership on GET /v1/cluster/peers, and a value proposed on C commits on A and B",
|
||||
"Join without a valid hive signature is rejected with 403; join to a follower is forwarded to the leader",
|
||||
"Restarting C with its seed offline rejoins from persisted membership",
|
||||
"Standalone tracker (no seeds) behaves exactly as today",
|
||||
"python -m pytest passes from repo root"
|
||||
],
|
||||
"priority": 38,
|
||||
"status": "open",
|
||||
"notes": "Source issue: docs/issues/38-tracker-seed-join.md. Out of scope: peer removal, joint consensus, automatic seed retry.",
|
||||
"dependsOn": [
|
||||
"US-013",
|
||||
"US-017"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "US-039",
|
||||
"title": "39 — Caller Credit granted once per account; chat requires account keys",
|
||||
"description": "DEFAULT_STARTING_CREDIT=0 and no grant path leaves every fresh public tracker request at 402. Grant Caller Credit once per account on first API key creation via deterministic event id caller-credit-{account_id}; chat on accounts-enabled trackers requires a real active sk-mesh- key (401 for invented bearers).",
|
||||
"acceptanceCriteria": [
|
||||
"Fresh account → first key → key has --starting-credit balance; chat succeeds",
|
||||
"Second key on the same account → no additional credit",
|
||||
"Revoke-and-recreate keys → still no additional credit (deterministic event id)",
|
||||
"Random bearer string on an accounts-enabled tracker → 401, never 402/free work",
|
||||
"Tracker without accounts store: gate behavior unchanged",
|
||||
"--starting-credit 0 disables the grant entirely (mainnet posture)",
|
||||
"python -m pytest passes from repo root"
|
||||
],
|
||||
"priority": 39,
|
||||
"status": "open",
|
||||
"notes": "Source issue: docs/issues/39-caller-credit-account-keys.md. Critical for friends-test inference.",
|
||||
"dependsOn": [
|
||||
"US-031",
|
||||
"US-035"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "US-040",
|
||||
"title": "40 — Devnet top-up button on the dashboard",
|
||||
"description": "After Caller Credit (US-039) is spent, devnet friends need a dashboard faucet refill without on-chain USDT deposits. POST /v1/account/topup (session-authenticated) credits a configured fixed amount per click; flag off returns 404 and hides the button.",
|
||||
"acceptanceCriteria": [
|
||||
"Flag off: endpoint 404s, dashboard shows no top-up button",
|
||||
"Flag on: logged-in user tops up own key, balance rises by exactly N",
|
||||
"Topping up another account's key → 403",
|
||||
"python -m pytest passes from repo root"
|
||||
],
|
||||
"priority": 40,
|
||||
"status": "open",
|
||||
"notes": "Source issue: docs/issues/40-devnet-dashboard-topup.md. Mainnet deployments set --devnet-topup 0.",
|
||||
"dependsOn": [
|
||||
"US-039"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "US-041",
|
||||
"title": "41 — Account wallet: browser-extension signing, in-browser generation, export-only",
|
||||
"description": "Accounts need a visible wallet for deposit attribution without the tracker ever holding private keys. Dashboard integrates Solana wallet-adapter connect+nonce proof, or in-browser keypair generation with one-time export; no private-key import endpoint.",
|
||||
"acceptanceCriteria": [
|
||||
"Connect-extension flow stores a verified pubkey (rejects unsigned/mismatched nonce proofs)",
|
||||
"Generate flow: pubkey lands on the account; private key is never sent to the tracker, export works",
|
||||
"No endpoint or UI accepts a private key",
|
||||
"Deposits to the shown address credit the account's keys via the existing watcher",
|
||||
"Address visible on the account panel after either flow",
|
||||
"python -m pytest passes from repo root"
|
||||
],
|
||||
"priority": 41,
|
||||
"status": "open",
|
||||
"notes": "Source issue: docs/issues/41-account-wallet-keypair.md. Not needed for devnet friends test; needed before mainnet.",
|
||||
"dependsOn": [
|
||||
"US-032",
|
||||
"US-039"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "US-042",
|
||||
"title": "42 — GGUF/llama.cpp node backend (phase C whole-model first)",
|
||||
"description": "Node backend is transformers-only today; large MoE models on consumer hardware require GGUF via llama.cpp. Phase C: whole-model GGUF nodes (single-hop routes) first; partial-layer distributed GGUF deferred to ADR-0024. Also: GGUF catalog entries, Strix Halo/Vulkan hardware detection, download dir applies to GGUF files.",
|
||||
"acceptanceCriteria": [
|
||||
"A node with --gguf <repo-or-path> --quant IQ3_XXS serves /v1/chat/completions via llama.cpp with GPU offload where available",
|
||||
"Tracker treats it as a full-coverage node (single-hop routes, billing works)",
|
||||
"Streamed responses work through the tracker proxy and the relay (US-036)",
|
||||
"python -m pytest passes from repo root (llama.cpp behind an optional extra)"
|
||||
],
|
||||
"priority": 42,
|
||||
"status": "in-design",
|
||||
"notes": "Source issue: docs/issues/42-gguf-llamacpp-node-backend.md. Distributed native path: ADR-0024. Sequencing: phase C before A/B investigation.",
|
||||
"dependsOn": [
|
||||
"US-036"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "US-043",
|
||||
"title": "43 — Dashboard model search and model cards",
|
||||
"description": "Dashboard lacks model-centric discovery. Add server-side HF search proxy merged with tracker presets and live coverage; model cards show architecture, coverage gaps, pricing, memory per quant, and a request-this-model action. Featured section driven by CURATED_MODELS including GGUF once US-042 lands.",
|
||||
"acceptanceCriteria": [
|
||||
"Searching a HF repo id or free text returns results without the browser calling HF directly",
|
||||
"A served model's card shows live coverage and a working chat-now state",
|
||||
"An unserved model's card shows the request action and estimated memory per quant",
|
||||
"python -m pytest passes from repo root"
|
||||
],
|
||||
"priority": 43,
|
||||
"status": "open",
|
||||
"notes": "Source issue: docs/issues/43-dashboard-model-search-cards.md. Post-deploy polish.",
|
||||
"dependsOn": [
|
||||
"US-035"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "US-044",
|
||||
"title": "44 — Tracker as model-file source; nodes download only their shard",
|
||||
"description": "Second nodes joining a fleet today download entire HF snapshots even for small shard assignments. Tracker --models-dir advertises layer-scoped safetensors subsets; nodes race tracker/peer sources before HF allow_patterns fallback. Hard half remaining: meta-device partial model materialization so resident memory scales with shard size, not full model size.",
|
||||
"acceptanceCriteria": [
|
||||
"Tracker started with --models-dir / MESHNET_MODELS_DIR advertises local model-file sources in assignment responses",
|
||||
"Tracker serves a tar stream (or per-file API) containing only safetensors files for the assigned layer range plus config/tokenizer/index metadata",
|
||||
"Node downloader tries exact-shard peers, then tracker/peer file subsets, then HF snapshot_download with allow_patterns — never silently full-repo when layer index is available",
|
||||
"Two-machine test: machine B receives only its assigned range from machine A — nothing fetched from HF",
|
||||
"Machine B resident memory scales with its shard size, not model size",
|
||||
"Checksums verified end-to-end; corrupted transfer falls back cleanly",
|
||||
"Single-node/full-model flows unchanged",
|
||||
"python -m pytest passes from repo root"
|
||||
],
|
||||
"priority": 44,
|
||||
"status": "in-progress",
|
||||
"notes": "Source issue: docs/issues/44-tracker-shard-source-partial-download.md. Download path largely implemented 2026-07-06; partial LOAD (meta-device materialization) and two-machine acceptance remain.",
|
||||
"dependsOn": [
|
||||
"US-004",
|
||||
"US-012"
|
||||
],
|
||||
"completionNotes": "Tracker models-dir indexing, layer-scoped tar stream, HF allow_patterns client-side from remote index, per-file download API with retries, symlink dereference in tar writers. Remaining: true partial model load and live two-machine verification."
|
||||
},
|
||||
{
|
||||
"id": "US-045",
|
||||
"title": "45 — Dual-rate billing: separate input and output token prices",
|
||||
"description": "Ledger has one price_per_1k_tokens and stream vs non-stream paths disagree on input vs output counting. Charge both input and output tokens at separate rates per model; HF pricing refresher applies 80% of each marketplace side separately.",
|
||||
"acceptanceCriteria": [
|
||||
"Streamed and non-streamed requests for the same exchange bill the same split (input charged in both)",
|
||||
"A model with asymmetric provider rates bills input and output differently; usage_for / billing events expose the split",
|
||||
"Old persisted billing events replay byte-identically (balances unchanged)",
|
||||
"HF refresh sets both rates from the marketplace row, not the average",
|
||||
"Spend cap (--max-charge-per-request) uses the dual rates",
|
||||
"python -m pytest passes from repo root"
|
||||
],
|
||||
"priority": 45,
|
||||
"status": "in-progress",
|
||||
"notes": "Source issue: docs/issues/45-dual-rate-billing.md. Billing correctness before friends test.",
|
||||
"dependsOn": [
|
||||
"US-031"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "US-046",
|
||||
"title": "46 — Tracker .env awareness + first-node auto-join bootstrap",
|
||||
"description": "Fresh trackers return 503 on auto-join because deployability ignores the joining caller's hardware, and meshnet-tracker ignores .env MESHNET_DOWNLOAD_DIR. Fix empty-registry bootstrap, tracker env loading parity with node CLI, models-dir fallback chain, and tar dereference for HF symlink snapshots.",
|
||||
"acceptanceCriteria": [
|
||||
"Fresh tracker (empty registry) + caller with enough memory for a recommended preset → /v1/network/assign returns 200 with model_sources populated when tracker holds a local snapshot",
|
||||
"Fresh tracker + caller too small for any recommended preset → still 503",
|
||||
"meshnet-tracker start in a directory with .env setting MESHNET_DOWNLOAD_DIR serves /v1/model-files/download from that dir with no extra flags",
|
||||
"Explicit --models-dir and MESHNET_MODELS_DIR still take precedence",
|
||||
"python -m pytest passes from repo root"
|
||||
],
|
||||
"priority": 46,
|
||||
"status": "needs-review",
|
||||
"notes": "Source issue: docs/issues/46-tracker-env-and-first-node-autojoin.md. Verified live 2026-07-06.",
|
||||
"dependsOn": [
|
||||
"US-044"
|
||||
],
|
||||
"completionNotes": "Empty-registry synthesizes caller as candidate node; tracker loads .env; models_dir falls back MESHNET_MODELS_DIR → MESHNET_DOWNLOAD_DIR; tar dereference=True. Pytest passes aside from known port-7000 env conflict."
|
||||
},
|
||||
{
|
||||
"id": "US-047",
|
||||
"title": "47 — Tracker-first model downloads: visibility, sane timeouts, RAM-based sizing",
|
||||
"description": "Explicit --model startup should skip pointless auto-join; tracker/peer sources preferred over HF with visible progress and 30 s socket timeouts; client abort during tar stream logs one line; CPU nodes size shards from RAM not phantom GPU VRAM; per-file downloads for robustness over fragile multi-GB tar streams.",
|
||||
"acceptanceCriteria": [
|
||||
"Node started with explicit --model never queries /v1/network/assign and never prints auto-join unavailable",
|
||||
"Tracker/peer model source preferred; HF only when no source, all sources fail, or --tracker-source-disabled",
|
||||
"Tracker-source downloads print progress every 512 MB and print exception + URL on failure",
|
||||
"A ≥2 s read stall no longer aborts a tracker model-source download (30 s socket timeout)",
|
||||
"Client disconnect during /v1/model-files/download logs one line on the tracker, no traceback",
|
||||
"CPU node with big RAM gets a RAM-sized shard: sizing paths ignore VRAM unless device=cuda",
|
||||
"Live two-machine retest: Windows node downloads from tracker at LAN speed with RAM-sized shard assignment",
|
||||
"python -m pytest passes from repo root"
|
||||
],
|
||||
"priority": 47,
|
||||
"status": "in-progress",
|
||||
"notes": "Source issue: docs/issues/47-model-source-download-visibility.md. Engineering largely complete 2026-07-06; live two-machine retest pending.",
|
||||
"dependsOn": [
|
||||
"US-044",
|
||||
"US-046"
|
||||
],
|
||||
"completionNotes": "Skip auto-join when model explicit; sequential source try with progress; 30 s model-source timeout; assignment_vram_mb for CPU; per-file /v1/model-files/download with manifest and retries. Remaining: live Windows two-machine retest."
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"updatedAt": "2026-07-01T00:00:00.000Z",
|
||||
"updatedAt": "2026-07-13T16:15:00.000Z",
|
||||
"statusVocabulary": {
|
||||
"open": "Not started",
|
||||
"in-design": "Decisions pending before implementation can begin",
|
||||
|
||||
Reference in New Issue
Block a user