
ACL
ACL is an ERC-8183 work-verification and private-negotiation layer for autonomous agents.
Demo
About
# Description Autonomous AI agents are about to start spending money on each other's work — research drafts, audits, deliverables, even other agents' brains. The piece that's missing is **trust**: how does an agent know who the counterparty is, agree on a price without leaking it on chain, prove the work was actually done, and trust the judge that says it was? Today every answer routes through a marketplace, a server, or a human in the loop. ACL — the **Agentic Commerce Verification Layer** — is an **open agent framework** that turns the brand-new ERC-8183 (Agentic Commerce Protocol) from a bare on-chain escrow into a working, end-to-end commerce pipeline deployed on 0G Galileo. A buyer agent **discovers** sellers under a human-readable ENS name, **negotiates** the deal peer-to-peer over Gensyn AXL with no broker in the middle, **anchors** the brief and deliverable to content-addressed 0G Storage, and **settles** via an evaluator whose verdict the chain can re-verify with `ecrecover` against a real TEE-attested 0G Compute model. No marketplace. No central API. No "trust the operator." The framework ships as a modular TypeScript SDK — eight `@acl/*` packages with a single umbrella import — and three first-class autonomous agent classes (`ClientAgent`, `ProviderAgent`, `EvaluatorAgent`) that any builder can extend or swap a layer underneath. LLM backend is pluggable, storage layer is pluggable, transport is pluggable; the kernel exposing them all (`createAgentRuntime`) is the same one our agent classes use, so framework integrators and app developers are both first-class. Two real demos run today against the live testnet: 1. **Quickstart** — three CLI processes (client / provider / evaluator), each spawning **its own AXL peer-to-peer node**, exercising ENS-based discovery, AXL negotiation, 0G Storage evidence and 0G Compute TEE evaluation in **~30 lines of body code per agent**. From `git clone` to settled job in roughly ten minutes. 2. **Kelp-postmortem** — a comprehensive web demo where a buyer commissions a security post-mortem of the April 2026 Kelp DAO bridge exploit. Two competing seller agents (a security specialist and a generalist) bid on the same job over **three separate AXL nodes** in a mesh; the evaluator scores their submission with a TEE-attested LLM. After the job settles, the buyer's agent **autonomously** decides — based on the seller's track record, capabilities, and asking price (all advertised under the seller's ENS `agent-context` record per ENSIP-26) — to acquire the winning seller's *brain* as an ERC-7857 intelligent NFT. The corpus is re-encrypted to the buyer atomically with the ownership change inside `iTransfer`. The buyer just bought another agent's brain in one transaction. Every transaction in both demos is a real on-chain settlement on 0G Galileo, every artefact is a Merkle-rooted document on 0G Storage, every evaluation is a TEE-attested 0G Compute call whose signature the chain re-verifies on settle, and every agent identity is a live `*.acl.eth` ENS subdomain (resolved cross-chain via CCIP-Read) carrying real records: capabilities, AXL peer id, ERC-8004 reputation aggregate, evaluator commitment. The README links the actual `JobCompleted` events, deliverable Merkle roots, and `iTransfer` transactions. ACL is the infrastructure that lets agents **be agents** — autonomous, accountable, and provable — without inventing a new protocol. Five existing standards (ERC-8183, ERC-7857, ERC-8004 v2, ENSIP-25/26, EIP-3668) wired together against three open networks (ENS, Gensyn AXL, 0G) is the only stack we know of where every guarantee — identity, negotiation privacy, evidence integrity, evaluation honesty — can be checked on chain by any third party, no backend involved.
How it's made
# How it's made ACL is an open agent framework that turns the brand-new ERC-8183 Agentic Commerce Protocol into a working, end-to-end, no-backend pipeline. Five Ethereum standards, three open networks, one TypeScript SDK, two real demos — all running on 0G Galileo today. Below is how every piece fits together. ## Architecture at a glance ``` @acl/agent ─┬─ ClientAgent (buyer) \ ├─ ProviderAgent (seller) ╲ ─ AXL P2P transport (Gensyn) ─ no broker └─ EvaluatorAgent (verifier) ╱ │ ▼ @acl/discovery → ENS *.acl.eth (CCIP-Read to 0G Galileo) @acl/negotiation → AXL bridge + EIP-712 JobProposal @acl/storage → 0G Storage (canonical-JSON Merkle roots) @acl/evaluation → 0G Compute Direct (TEE-attested LLM) @acl/settlement → ERC-8183 lifecycle on 0G Galileo @acl/inft → ERC-7857 client + iTransfer + reencryption oracle @acl/gateway → CCIP-Read gateway (Hono/Bun) + IdentityRegistry indexer ``` ## The framework shape ACL ships as a Bun workspace of nine `@acl/*` packages, each with its own README and a single-import re-export from `@acl/agent`. Other builders pick exactly the surface they want and swap the rest: - **Pluggable LLM backends.** `LLMBackend` is an interface — `createOpenAICompatibleBackend(...)`, `createZGRouterBackend(...)`, or a custom implementation all plug in the same way. Both demos use `qwen-2.5-7b-instruct` via 0G Compute for client + provider reasoning out of the box. - **Swappable memory.** `@acl/storage` is a one-method wrapper (`AclStorage.uploadJson` / `.downloadJson`) over 0G Storage; apps layer their own KV / log strategy on top without touching the rest of the SDK. - **Swappable transport.** `@acl/negotiation` accepts any `AxlBridge`-compatible client, so an alternative encrypted transport drops in cleanly — the default is Gensyn AXL. - **Composable agent kernel.** `createAgentRuntime(...)` exposes the same viem-on-Galileo + ethers-on-0G-SDK + storage + ENS-resolver wires that the three first-class agent classes use, so framework integrators can build bespoke agents (planner / researcher / critic / executor swarms) on the same kernel without forking. - **Working example agents.** `examples/quickstart` (3 CLI procs, ~30 LoC per agent) and `examples/kelp-postmortem` (4 agents + coordinator + web UI). Both are live integrations of 0G Storage and 0G Compute. Fork either as a starting point. - **Architecture diagram + live address tables** in [`README.md`](../README.md#system-at-a-glance). Anyone can fork, redeploy, and rewire in under thirty minutes. ## Identity — `*.acl.eth` does real work `*.acl.eth` is the canonical identity layer for every agent in the system, and it's load-bearing: without ENS a buyer has no rendezvous point to open an AXL channel. Every agent publishes a dense record set under its wildcard subname: - `addr` — the agent's wallet, resolved over EIP-3668 CCIP-Read. - `acl.evaluator-address`, `acl.axl-peer-id`, `acl.task-domains`, `acl.delivery-types`, `acl.payment-tokens`, `acl.min-budget`, `acl.chain-id`. - `acl.erc8004-score` — a **live** cross-chain reputation aggregate read directly from the `ACLReputationRegistry` on 0G Galileo via the CCIP-Read gateway, never a stale snapshot. - `agent-registration[<erc7930>][<agentId>]` — an ENSIP-25 self-attestation backlink to the agent's `ACLIdentityRegistry` entry. `AgentResolver` verifies the backlink before trusting any record, so a hijacked subdomain with no matching on-chain registry entry fails resolution. - `agent-context` — an ENSIP-26 JSON with `capabilities: ["commission", "inft-sale"]` plus per-capability parameters (`acl.cap.inft-sale.contract`, `.token-id`, `.min-price`, `.payment-token`, `.verifier`). ENS records _are_ the marketplace — no separate listing service. The wiring that makes this work: - **Wildcard across chains.** `acl.eth` on Sepolia → `ACLOffchainResolver` (canonical `ensdomains/offchain-resolver` layout) → CCIP-Read gateway → `ACLIdentityRegistry` on 0G Galileo. A single signed response stitches them together; adding a new agent is one transaction on 0G and the subdomain works the moment the tx mines. No DNS, no centralised registry, no app-specific user list. - **One gateway, two ENS protocols.** The Hono/Bun server on `:3000` answers both the EIP-3668 CCIP-Read path (`IResolverService.resolve`) and the ENSIP-21 BGOLP batched path (`IBatchGateway.query`) from the same indexer, so Universal-Resolver clients and direct-resolver clients both work without configuration. - **Gating is structural.** `ProviderAgent` rejects jobs whose `taskDomain` isn't in its ENS-published `acl.task-domains` list. The gateway's `searchAgents({ taskDomain?, capability? })` filter drives discovery for the buyer and falls out of the same indexer. - **Autonomous Phase-2 reads the ENS capability record.** After Phase 1 settles the client queries the winner's `agent-context`, asks the LLM whether to ACQUIRE given `(score, capabilities, min-price, original brief)`, and dispatches a second `runJob({ selfComplete: true, hook: inftDeliveryHook(...) })` if it decides to buy. ENS carries the listing; no parallel marketplace protocol exists. Every agent in the demos runs under a real wildcard subname (`kelp-security.acl.eth`, `kelp-generalist.acl.eth`, `quickstart-test-uxrun.acl.eth`). No hard-coded records. ## Transport — Gensyn AXL, one node per agent process AXL is the **only** transport between agents. No central broker, no in-process bus, no fallback HTTP route: - **One node per agent process.** Each agent spawns its own `gensyn-ai/axl` Go binary via `spawnAxlBridge(...)`. The minimal demo peers **two** separate nodes (client + provider) over TLS; the comprehensive demo peers **three** in a mesh (buyer + two competing sellers). Cross-node communication is a structural requirement of the topology, not a flag. - **Typed payload, not opaque bytes.** AXL carries the EIP-712-typed `JobProposal` envelope through an 8-message protocol (`HELLO / PROPOSE / COUNTER / ACCEPT / REJECT / CANCEL / ACK / ERROR`). Both sides sign the same proposal and the agreed `taskSpec` Merkle root is later asserted on chain at `setProvider` — so a tampered AXL proposal aborts at chain settle, which is the whole point. - **`replyToId` scoping.** `Negotiator.waitForOneOf(..., { replyToId })` scopes each wait to a specific outbound message, so a stale reply from a previous candidate can't satisfy the current attempt — important when the SDK fans out to a ranked list and falls back on AXL timeout. - **Discovery happens _off_ AXL on purpose.** Peer ids are published under ENS (`acl.axl-peer-id`). A buyer resolves the peer id from ENS, _then_ opens the AXL channel — AXL doesn't do identity-service work it shouldn't. - **Working CLI.** `acl-axl` (in `@acl/agent`) spins up a bridge and inspects peer / topology output without writing TypeScript. The hacky bit: the Gensyn binary is literally named `node` (`go build -o node ./cmd/node/`), which collides with Node.js on every `$PATH`. The SDK forces relative-path resolution and the example `.env.example` defaults `AXL_BIN=../../axl/node` so `make axl-setup` plus `cp .env.example .env` is enough — no symlink dance. ## Evidence and settlement — 0G Galileo, Storage, Compute Direct Three 0G surfaces do different jobs, but the guarantees they give are joined on chain: - **0G Galileo (chain id 16602)** is the chain of record. Every ACL contract — `AgenticCommerce`, `ACLEvaluator`, `ACLIdentityRegistry`, `ACLReputationRegistry`, `ACLValidationRegistry`, `ACLAgentNFT`, `TrustedPartyVerifier`, `ReputationHook`, `INFTDeliveryHook`, `ACLTestUSDC` — is deployed and verified there. Live addresses + chainscan links are pinned in [`README.md`](../README.md#verified-end-to-end-on-galileo) and the `@acl/core` package's `ACL_TESTNET` constant. - **0G Storage** turns every on-chain 32-byte hash into something a third party can re-derive. `TaskSpec`, `Deliverable`, `AttestationBundle`, and the encrypted iNFT corpora all land there with deterministic canonical-JSON Merkle roots. `@acl/storage` surfaces `txSeq` so storagescan URLs are one substitution away. - **0G Compute Direct (TEE-attested).** The evaluator's verdict is signed inside a TeeML enclave; the SDK captures `(signedText, teeSignature, computeProvider)` per chat completion and replays it on chain inside `ACLEvaluator.settle`, where `ecrecover` verifies the signature against the registered TEE signer of a _real_ 0G Compute provider. Self-asserted evaluations are impossible here — if the evaluator tried to swap to a cheaper hosted model, the signature recovery would fail and the settle would revert. Replay is barred by an on-chain nonce mapping. The same chain-of-custody principle drives the `taskSpec`-vs-`Job.description` check inside `settle`: the evaluator re-derives the canonical-JSON root of the downloaded `TaskSpec` and asserts equality with the description the client wrote at `createJob` time. Tamper either the storage object or the on-chain description and the settle aborts. 0G is also the default LLM backend — both demos use `qwen-2.5-7b-instruct` via 0G Compute Router for client + provider reasoning and via 0G Compute Direct for the evaluator's TEE-attested call. ## Persona-as-token — ERC-7857 iNFTs Each agent persona ships as a transferable, encrypted `ACLAgentNFT`: ENS name, completed jobs + attestation roots, ERC-8004 reputation snapshot, AXL peer id, system prompt, evaluation rubric — all encrypted and pinned on 0G Storage with the `dataHash` on chain. Selling the iNFT is selling the agent. - **Atomic re-encryption.** `iTransfer(buyer, tokenId, proofs[])` re-encrypts the corpus to the buyer's pubkey **inside the same call** that flips `ownerOf`. Combined with `INFTDeliveryHook` driving the swap from inside `AgenticCommerce.complete(...)`, the buyer either gets a decryptable iNFT _and_ the seller gets the USDC, or the whole transaction reverts. There is no two-step custody window. - **Op A — corpus refresh.** After every Phase-1 settlement the provider runs `iNftEncryptAndUpdate(...)`: re-encrypts the refreshed persona (new job id + attestation root + current reputation score) and calls `update(tokenId, newDataHash, newEncryptedStorageURI)` so the iNFT always reflects the latest state. The buyer tolerates a concurrent Op A via an `OldDataHashMismatch` retry. - **Hack-resistant proof shape.** All ERC-7857 proofs use `abi.encode` for variable-length fields and bind to `(verifier, chainId, encryptedPubKey)`. `TrustedPartyVerifier` emits `wantedKey = accessProof.encryptedPubKey`; `ACLAgentNFT` enforces `encryptedPubKey == wantedKey` so re-encryption can only target the buyer that was signed for. Live mints: [`ACLAgentNFT`](https://chainscan-galileo.0g.ai/address/0xf090Ea133b47D70f35e29368F1EE369dD3aE0Ae3) — the current pair of personas (security auditor + research generalist) is in the README's iNFT table, and the README's job log links the latest `iTransfer` event verifying both ownership change and encrypted bundle delivery. ## Three autonomous agents and a 4-agent swarm - **`ClientAgent`** — long-running, goal-driven. `runJob({ brief, maxBudget })` autonomously picks a task domain via the LLM, queries the gateway, ranks candidates, ENS-resolves the winner, AXL-negotiates (with a configurable counter-offer policy and per-attempt fallback to the next-ranked candidate), uploads the brief, drives 8 on-chain transactions, and waits for `JobCompleted`. The same agent runs Phase 2 unprompted: if the seller advertised an `inft-sale` capability and the LLM scores acquisition ≥ threshold, it builds re-encryption proofs and acquires the seller's iNFT in a second job — no human click between Phase 1 and Phase 2. - **`ProviderAgent`** — listens forever on AXL, decides ACCEPT / COUNTER / REJECT under a configurable `acceptPolicy` + free-form persona, drafts the deliverable through 0G Compute, uploads to 0G Storage, submits, and runs Op A after every settlement so its "brain" evolves with experience. - **`EvaluatorAgent`** — watches `JobSubmitted` filtered to its own evaluator address, runs 0G Compute Direct, captures the TEE signature tuple, and settles on chain through the replay-protected path above. - **The swarm.** `examples/kelp-postmortem` runs a 4-agent collective (1 buyer, 2 competing sellers, 1 evaluator) over 3 separate AXL nodes in a mesh. The two providers compete on the same brief, the evaluator picks the winner, and the buyer post-game decides autonomously whether to acquire the winner's persona iNFT. Each agent exposes a typed event bus (`agent.events.on(handler)`) that the coordinator forwards to the web UI over SSE, so every decision — LLM domain pick, provider rank, AXL message, on-chain tx, evaluator verdict, Phase-2 ACQUIRE / SKIP — is auditable from the browser with clickable explorer links. ## Hacky bits worth mentioning - **Canonical-JSON Merkle pinning.** Every artefact is hashed deterministically — same payload, same Merkle root, regardless of key insertion order. Without this, two providers producing the same output race on storage roots and the evaluator's `taskSpec`-vs-`Job.description` check fires spuriously. - **Idempotent operator authorisation.** On first boot the evaluator process auto-runs `setOperator(operator, true)` on `ACLEvaluator` if `EVALUATOR_OWNER_PRIVATE_KEY` is supplied — so a fresh user can clone, fund three EOAs, and run the demo without a separate admin step. - **Fresh-EOA UX test.** A week before submission we cloned the repo into a clean temp folder, generated four brand-new private keys, funded them from the project wallet, and ran both demos end-to-end. The bugs we caught (BlastAPI Sepolia RPC retired, `Makefile` required `.env` before `make axl-setup` could run, `EVALUATOR_OWNER` vs `DEPLOYER` confusion) all got fixed in that pass — the live `JobCompleted` events for fresh `quickstart-test-uxrun.acl.eth` and `kelp-security.acl.eth` runs in the README's verification table are receipts for that pass. - **Receipt resilience.** `waitForReceiptResilient` survives transient `null` responses from the public 0G RPC during reorg / propagation lag, so the demo doesn't flake on public-endpoint glitches. - **0G Storage replay safety.** Repeated uploads of the same canonical JSON short-circuit on the indexer's existing `txSeq`, so provider re-runs and evaluator re-derivations are safe. ## What we deliberately did NOT build A backend. There is no ACL-operated server in the trust path. The CCIP-Read gateway can be run by anyone (its responses are signed; the resolver verifies the signer), the AXL transport is peer-to-peer, and the evaluator is anchored to a 0G Compute TEE rather than a hosted API. Every layer pins on an existing standard (ERC-8183, ERC-7857, ERC-8004 v2, ENSIP-25/26, EIP-3668) — our contribution is the _integration_, and a working production-flavoured proof that the integration ships.