Autonomous DePIN repair gated by physics and proof-of-personhood, with memory it does not own
Verimesh is an autonomous operations agent for multi-operator DePIN infrastructure, a 3D mesh of compute nodes owned by different operators, where no one party is trusted to narrate what happened.
An LLM proposes fixes; a deterministic verifier disposes; real humans break ties. When a node degrades, the agent first queries a subgraph of its own past decisions as trustless memory, then makes one attested inference to diagnose the fault and propose an action (throttle, isolate, rebalance, scale up). That proposal is never executed on the model's say-so. A deterministic verifier projects the action forward 30 steps through a physical model of the mesh — thermal equilibrium, load ceilings, throughput floors — and returns VERIFIED, VIOLATION_TRIGGERED or ESCALATE. An action that would fix one operator's node by breaching another's is caught before it runs.
The core idea is differential authorization: how much human authority an action needs scales with its blast radius. An in-envelope fix confined to one operator commits autonomously (T0). A node the chain remembers as a repeat offender costs a human signature for the same safe action (T1). Anything projecting across an operator boundary demands a quorum of distinct humans (T2). Distinctness is proven by World ID nullifiers and enforced in three independent places — the authorization policy, a unique index in the database, and a DuplicateNullifier revert in the contract — so "two different people approved this" cannot be faked by one person scanning twice.
The three integrations are load-bearing, not decorative. 0G Compute runs the one LLM call inside a TEE, so the inference is verifiable rather than merely remote, and 0G Storage holds the full immutable reasoning blob. A registry contract on Base Sepolia emits one event per decision carrying that blob's 0G root, and a subgraph indexes it — which is what the agent queries before it acts. The memory, the authorization and the audit trail are all things any operator can verify independently, with no central API to trust.
One node in the mesh is not simulated. A Samsung Galaxy S22 reports real battery temperature and real kernel scheduler contention over its own network link. When it is genuinely oversubscribed the agent detects it, reasons about it, and the resulting decision travels back to the handset and actually sheds the load.
Correctness is checked by an acceptance harness that reads the registry's logs straight over RPC and compares them against the deployed subgraph and the operator allowlist — so what the agent remembers, what the chain recorded, and what the humans actually authorized are all forced to agree.
Stack: a pnpm/TypeScript monorepo. The front end is Next.js 15 + React 19 with react-three-fiber/three for the 3D mesh and Zustand for state, fed by Supabase Postgres over realtime subscriptions. The agent loop is a standalone Node service. Contracts are Solidity 0.8.28 on Base Sepolia via ethers v6. The subgraph is AssemblyScript mappings deployed to Subgraph Studio. Correctness is vitest plus fast-check property tests (82 green), on top of a deterministic physics model shared by the simulator, the verifier and the agent.
The pipeline: simulator/device telemetry lands in Supabase; a deterministic detector spots a node past its thermal or contention bound; the agent queries The Graph for that node's prior incidents; it makes one 0G Compute inference (qwen2.5-omni-7b in a dstack TEE) to diagnose and propose; a verifier projects the action 30 steps through the physical model and returns a verdict; an authorization policy maps blast radius and history to T0/T1/T2; humans clear the gate with World ID; then the reasoning blob goes to 0G Storage and the decision plus its 0G root is emitted on Base Sepolia and indexed.
Partner tech, and why each is load-bearing. 0G Compute makes the LLM decision verifiable rather than merely remote — processResponse returns a TEE attestation we store per proposal, so an unattested inference is visibly marked as such. 0G Storage holds the immutable reasoning payload. The Graph is what makes the agent have a memory it does not own: it queries the indexed history of its own past decisions before acting, so "this node has failed twice before" is a fact from an independently queryable index, not from our database. World ID supplies personhood, and specifically distinctness — nullifiers are the only thing that makes "two different humans" unfakeable.
Notable/hacky:
The registry does not live on 0G Chain. The Graph does not support 0G Galileo, so rather than self-host graph-node on the demo laptop we deployed the registry to Base Sepolia and let Studio index it. The seam is preserved in the data: the Committed event carries the 0G Storage root, so every indexed row points back into 0G.
The Graph's own Subgraph MCP server cannot query an arbitrary endpoint — the gateway only serves subgraphs published to the decentralized network, and publishing is mainnet-only. So we wrote our own MCP server over our GraphQL endpoint to expose get_history to the agent.
The physical node was the deepest rabbit hole. A Galaxy S22 runs a Termux reporter, but Android denies /proc/stat, /proc/loadavg, /proc/pressure/cpu and /sys/class/thermal to unprivileged apps via SELinux — so there is no CPU utilisation counter and no SoC sensor. We rebuilt load from run_delay in /proc/self/schedstat, which a process may always read about itself. Window size turned out to be decisive: below ~100ms a freshly-woken thread keeps its scheduling advantage and is handed a core on demand, so it reads ~0% whether the phone is idle or on fire. At 200ms we get 0.1% idle vs 52.3% saturated. So the phone reports genuine scheduler contention — the CPU being oversubscribed, not merely busy — and we say exactly that rather than calling it utilisation.
Actuation had no inbound path: the phone is behind NAT with no server on it. The agent's decision rides back on the HTTP response to the telemetry POST the phone is already making every two seconds, and the reporter scans /proc/*/cmdline to SIGTERM the load generator — not pkill, because procps isn't guaranteed present in Termux. So a committed THROTTLE_NODE genuinely stops the load.
Nullifiers are canonicalised once (lowercase 0x, zero-padded to 32 bytes) so they map straight onto bytes32 with no conversion at the chain boundary — a mismatch there would silently break the quorum in the one place we could not afford it.
The bug we most enjoyed: the agent's staleness guard hashed the grid before and after reasoning and discarded the cycle if anything changed — but the hash included metric timestamps, which change every simulator tick. Any reasoner slower than one tick was therefore always discarded. The instant heuristic fallback was the only thing that ever survived, so the system looked like it worked. Fixing 0G Compute is what exposed it.
Finally, an acceptance harness reads the registry's logs straight over RPC and checks them against the deployed subgraph and the operator allowlist. It is designed to catch the failure mode we feared most: a demo that looks perfect and is quietly lying.

