diff --git a/docs/build/use-cases/trustless-agent.mdx b/docs/build/use-cases/trustless-agent.mdx index 9ce688fcb4..db9d9cfb16 100644 --- a/docs/build/use-cases/trustless-agent.mdx +++ b/docs/build/use-cases/trustless-agent.mdx @@ -1,155 +1,179 @@ --- description: Deploy a trustless Eliza AI agent on Oasis using ROFL enclaves, with enclave-managed keys and on-chain verification on Sapphire. -tags: [ROFL, AI, appd, secrets] +tags: [ROFL, Sapphire, AI, agents, secrets, governance] --- import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -# Trustless AI Agent +# Launch Trustless AI Agents on Oasis Learn how to deploy a trustless Eliza agent on Oasis using ROFL enclaves. -## What You’ll Build -By the end you will have a working Eliza agent running inside a ROFL Trusted -Execution Environment (TEE), registered and validated as a trustless agent in -the [ERC-8004] registry. The agent's code can be fully audited and proved that -the deployed instance really originates from it and cannot be silently altered. +## What You’ll Build -[ERC-8004]: https://eips.ethereum.org/EIPS/eip-8004 +By the end you will have: +- a ROFL-deployed Eliza agent running inside a TEE +- enclave-managed keys for on-chain actions ## Prerequisites You will need: -- **Docker** (or Podman) with credentials on docker.io, ghcr.io or other - public OCI registry -- **Oasis CLI** and at least **120 TEST** tokens in your wallet - (use [Oasis Testnet faucet]). -- **Node.js 22+** (for Eliza and helper scripts) -- **OpenAI** API key -- **RPC URL** for accessing the ERC-8004 registry (e.g. Infura) -- **Pinata JWT** for storing agent information to IPFS +- Docker (or Podman) +- [Oasis CLI] +- Node.js 18+ (for Eliza and helper scripts) +- a Sapphire Testnet wallet funded with TEST + +Optional depending on your LLM choice: +- OpenAI API key or a reachable Ollama instance + +[Oasis CLI]: https://github.com/oasisprotocol/cli/blob/master/docs/README.md + + +## Architecture Overview -Check [Quickstart Prerequisites] for setup details. +- ROFL (Runtime Off-chain Logic) + - Enclave execution, sealed secrets, app identity, attestation + - In-enclave key manager via `appd` UNIX socket +- Sapphire (Verification Layer) + - Confidential EVM chain verifying ROFL identities and policies + - Runs confidential contracts that can verify ROFL origin -[Quickstart Prerequisites]: ../rofl/quickstart#prerequisites -[Oasis Testnet faucet]: https://faucet.testnet.oasis.io +## Init and Create App -## Create an Eliza Agent +We package the agent as a container and deploy it to a TEE via ROFL. ROFL +handles attestation, sealed secrets, and app identity bound to your on-chain +registration. + + + +```shell +oasis rofl init rofl-eliza +cd rofl-eliza +oasis rofl create --network testnet +``` + +After creation, note your ROFL app ID. You can monitor deployments with: +`oasis rofl machine show` and `oasis rofl machine logs`, or use the Oasis +Explorer on Sapphire Testnet. + +## Eliza example (local run) + +First we initialize our Eliza agent: + +```shell +mkdir eliza-app && cd eliza-app +npm init -y +# install your Eliza framework deps here +# npm install @your-eliza/pkg ... +``` Initialize a project using the ElizaOS CLI and prepare it for ROFL. + ```shell -# Install bun and ElizaOS CLI +# 1) Install bun and ElizaOS CLI bun --version || curl -fsSL https://bun.sh/install | bash bun install -g @elizaos/cli -# Create and configure the agent -elizaos create -t project rofl-eliza -# 1) Select Pqlite database -# 2) Select the OpenAI model and enter your OpenAI key +# Configure model credentials locally +elizaos create eliza-agent +# OPENAI key when prompted -# Test the agent locally -cd rofl-eliza +# Start the agent locally elizaos start -# Visiting http://localhost:3000 with your browser should open Eliza UI -``` - -## Containerize the App and the ERC-8004 wrapper - -The Eliza agent startup wizard already generated `Dockerfile` that packs your -agent into a container and `docker-compose.yaml` that orchestrates the -`postgres` and `elizaos` containers. Edit `docker-compose.yaml` with the -following changes: - -1. In the PostgreSQL section replace relative `image: ankane/pgvector:latest` - with `image: docker.io/ankane/pgvector:latest`. -2. Name our `elizaos` image with a corresponding absolute path, e.g. - `image: docker.io/YOUR_USERNAME/elizaos:latest` -3. Make our Eliza agent registered as a trustless agent in the ERC-8004 - registry. Paste the following [`rofl-8004`] snippet that will register it - for us (keep the environment variables mapping!): - - ```yaml title="docker-compose.yaml" - rofl-8004: - image: ghcr.io/oasisprotocol/rofl-8004@sha256:f57373103814a0ca4c0a03608284451221b026e695b0b8ce9ca3d4153819a349 - platform: linux/amd64 - environment: - - RPC_URL=${RPC_URL} - - PINATA_JWT=${PINATA_JWT} - volumes: - - /run/rofl-appd.sock:/run/rofl-appd.sock - ``` +``` +#### Containerize and deploy to ROFL -Build and push: +Proceed to “Eliza agent container (inside ROFL)” below to package the agent +for enclave execution and deploy it using the Oasis CLI. + +Create a `docker` folder with a minimal runner that starts an ElizaOS agent via the CLI and reads model configuration from environment variables. + +
+ docker/entry.sh ```shell -docker compose build -docker compose push +#!/bin/sh +set -e + +# Expected env in ROFL: +# OPENAI_API_KEY for the model provider +# Provide any additional vars your agent needs here. +exec elizaos start ``` +
-For full verifiability pin the digest by appending `image: ...@sha256:...` in -`docker-compose.yaml` to all images. -[`rofl-8004`]: https://github.com/oasisprotocol/erc-8004 +
+ docker/Dockerfile -## Init ROFL and Create App +```dockerfile +FROM node:24-alpine +WORKDIR /app -The agent will run in a container inside a TEE. ROFL will handle the startup -attestation of the container and the secrets in form of environment variables. -This way TEE will be completely transparent to the Eliza agent app. +# Copy your ElizaOS project (created with `elizaos create ...`). +ADD . /app -```shell -oasis rofl init -oasis rofl create --network testnet +# Install ElizaOS CLI and project dependencies (npm-based). +RUN npm i -g @elizaos/cli && npm ci + +# Minimal entrypoint that launches the agent. +ENTRYPOINT ["/bin/sh", "/app/docker/entry.sh"] ``` +
-Inspect on-chain activity and app details in the [Oasis Explorer]. -## Build ROFL bundle +Add `compose.yaml` at project root: + +
+ compose.yaml -Eliza requires at least 2 GiB of memory and 10 GB of storage. First, update the -`resources` section in `rofl.yaml` accordingly: +```yaml +services: + eliza-agent: + build: ./docker + image: docker.io/YOUR_USERNAME/rofl-eliza:latest + platform: linux/amd64 + environment: + - OPENAI_API_KEY=${OPENAI_API_KEY} +``` +
+ +Build and push: -```yaml title="rofl.yaml" -resources: - memory: 2048 - cpus: 1 - storage: - kind: disk-persistent - size: 10000 +```shell +docker compose build +docker compose push ``` -Then, build the ROFL bundle by invoking: +For extra security, **pin the image digest** in `compose.yaml`. + +## Build ROFL bundle - - ```shell - oasis rofl build - ``` - - - ```shell - docker run --platform linux/amd64 --volume .:/src \ - -it ghcr.io/oasisprotocol/rofl-dev:main oasis rofl build - ``` - - + -## Secrets +```shell +oasis rofl build +``` -Let's end-to-end encrypt `OPENAI_API_KEY` and store it on-chain. Also, provide -the `RPC_URL` and `PINATA_JWT` values for ERC-8004 registration. + + ```shell -echo -n "" | oasis rofl secret set OPENAI_API_KEY - -echo -n "https://sepolia.infura.io/v3/" | oasis rofl secret set RPC_URL - -echo -n "" | oasis rofl secret set PINATA_JWT - +docker run --platform linux/amd64 --volume .:/src -it \ + ghcr.io/oasisprotocol/rofl-dev:main oasis rofl build ``` -Then store the secrets and previously built enclave identities on-chain: + + + +This produces the `.orc` bundle for deployment, sealing in enclave identity. + +Then publish the enclave identities and config: ```shell oasis rofl update @@ -157,70 +181,211 @@ oasis rofl update ## Deploy -Deploy your Eliza agent to a ROFL provider by invoking: - ```shell oasis rofl deploy ``` -By default, the Oasis-maintained provider is selected on Testnet that lends -you a node for 1 hour. You can extend the rental, for example by 4 hours by -invoking `oasis rofl machine top-up --term hour --term-count 4` -[command][deploy]. +By default, the Oasis-maintained provider is selected on Testnet. + +## How the agent interacts on Sapphire + +- Encode calldata for your contract method and call [`signSubmit()`] via + `appd`. The tx originates from the enclave-bound key of your ROFL app. +- Contracts can require verified ROFL origin to bind calls to your app. + +### Eliza + ROFL TypeScript client (inline) + +Use the official ROFL TypeScript client directly inside your Eliza agent. Add a +small helper you can import from any Eliza action/tool to derive the enclave key, +expose the wallet address, and sign+submit transactions via appd. + +
+ src/rofl.ts + +```ts +import { RoflClient, KeyKind } from "@oasisprotocol/rofl-client"; +import { Interface, Wallet } from "ethers"; +import http from "node:http"; + +export async function ensureEnclaveWallet(keyId = "agent-identity-key") { + const client = new RoflClient(); // connects to /run/rofl-appd.sock inside ROFL + const key = await client.generateKey(keyId, KeyKind.SECP256K1); + const wallet = new Wallet("0x" + key); + await client.setMetadata({ key_fingerprint: key.slice(0, 16) }); + return { wallet, key, client }; +} + +export async function signSubmitEthTx(params: { + to: string; + abi: any[]; + method: string; + args?: any[]; + gasLimit?: number; +}) { + const { to, abi, method, args, gasLimit } = params; + const iface = new Interface(abi); + const data = iface.encodeFunctionData(method, args ?? []); + const body = JSON.stringify({ + tx: { kind: "eth", data: { gas_limit: gasLimit ?? 200000, to, value: 0, data } }, + }); + return await new Promise((resolve, reject) => { + const req = http.request( + { + socketPath: "/run/rofl-appd.sock", + path: "/rofl/v1/tx/sign-submit", + method: "POST", + headers: { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(body) }, + }, + (res) => { + let buf = ""; + res.on("data", (c) => (buf += c)); + res.on("end", () => { + try { + resolve(JSON.parse(buf || "{}")); + } catch { + resolve({ raw: buf }); + } + }); + } + ); + req.on("error", reject); + req.write(body); + req.end(); + }); +} +``` +
-[deploy]: https://github.com/oasisprotocol/cli/blob/master/docs/rofl.md#deploy +Use the helper from inside your Eliza agent (e.g., in a tool/action handler): -## Trying it out - -After deploying the agent, use the CLI to check, if the agent is running: +
+ Example usage in an Eliza handler -```shell -# Show machine details (state, proxy URLs, expiration). -oasis rofl machine show -``` +```ts +import { ensureEnclaveWallet, signSubmitEthTx } from "./rofl"; -If the agent successfully booted up, the `Proxy:` section contains the -URL where your agent is accessible on, for example: +// During agent startup (once): +const { wallet } = await ensureEnclaveWallet("agent-identity-key"); +console.log("ROFL enclave EVM address:", wallet.address); +// Later, on user intent (e.g., tool call): +await signSubmitEthTx({ + to: "0xYourContract", + abi: [ /* contract ABI */ ], + method: "yourMethod", + args: [ /* arguments */ ], + gasLimit: 200000, +}); ``` -Proxy: - Domain: m1058.opf-testnet-rofl-25.rofl.app - Ports from compose file: - 3000 (elizaos): https://p3000.m1058.opf-testnet-rofl-25.rofl.app -``` +
-In the example above, our app is accessible at -https://p3000.m1058.opf-testnet-rofl-25.rofl.app. +#### Wire into ElizaOS (enable tool and prompting) -## ERC-8004 Registration and Validation +Add a small tool wrapper Eliza can call. Register it in your agent config and nudge the model to invoke it via system instructions. -When spinning up the agent for the first time, the `rofl-8004` service will -derive the ethereum address for registering the agent. You will need to -fund that account with a small amount of ether to pay for the fees. +
+ tools/rofl.ts -Fetch your app logs: +```ts +import { ensureEnclaveWallet, signSubmitEthTx } from "../src/rofl"; -```shell -oasis rofl machine logs +export async function generate_rofl_wallet(params?: { keyId?: string }) { + const { wallet } = await ensureEnclaveWallet(params?.keyId ?? "agent-identity-key"); + return { address: wallet.address }; +} + +export async function submit_rofl_tx(input: { + to: string; + abi: any[]; + method: string; + args?: any[]; + gasLimit?: number; +}) { + return await signSubmitEthTx(input); +} ``` +
-Then look for `Please top it up` line which contains the derived address. -After funding it, your agent will automatically be registered and validated. +Register the tool in your Eliza agent configuration (adjust pathing to your build output): -:::warning +
+ agent.config.json -Logs are accessible to the app admin and are stored **unencrypted on the ROFL -node**. Avoid printing secrets! +```json +{ + "name": "rofl-eliza", + "model": "openai:gpt-4o-mini", + "tools": ["./dist/tools/rofl.js"] +} +``` +
-::: +Guide the model to use the tool via system prompt: -:::example Trustless Agent Demo +
+ system-prompt.txt -You can fetch a complete example shown in this chapter from -https://github.com/oasisprotocol/demo-trustless-agent. +``` +You are a ROFL-enabled Eliza agent. When a user asks to create a wallet or perform an on-chain action: +- First call generate_rofl_wallet to ensure an enclave-bound EVM address exists. +- Then call submit_rofl_tx with the target contract (to), ABI, method and args. +Never expose private keys; only share the EVM address. +``` +
-::: +Ensure the appd UNIX socket is available to your container: +
+ compose.yaml (socket mount) + +```yaml +services: + eliza-agent: + # ... + volumes: + - /run/rofl-appd.sock:/run/rofl-appd.sock +``` +
+ +Install dependencies and build with your app: + +```shell +npm i ethers @oasisprotocol/rofl-client typescript +npx tsc +``` + +Your `docker/entry.sh` can continue to start just the Eliza agent: + +```shell +#!/bin/sh +set -e +exec elizaos start +``` + +## Testing it out + +After `oasis rofl deploy`, use the CLI to check machine status and view logs. + +```shell +# Show machine details (IDs, state, proxy URLs, expiration). +oasis rofl machine show + +# Fetch logs from your running ROFL app. +oasis rofl machine logs +``` + +- Expect standard output from your app container (anything your entrypoint prints). +- If your app initializes services on startup, those startup logs will appear here. +- Use this to check enclave startup issues and app readiness. + +Note: Logs are accessible to the app admin and are stored unencrypted on the ROFL node. Avoid printing secrets. See the official docs: +- [oasis rofl machine logs][machine-logs] +- [ROFL workflow — logs and deploy notes][sdk-deploy-logs] + +Inspect on-chain activity and app details in the Oasis Explorer: +- [Oasis Explorer] — inspect ROFL apps and Sapphire activity + [machine-logs]: https://github.com/oasisprotocol/cli/blob/master/docs/rofl.md#machine-logs [sdk-deploy-logs]: https://github.com/oasisprotocol/oasis-sdk/blob/main/docs/rofl/workflow/deploy.md#check-that-the-app-is-running [Oasis Explorer]: https://explorer.oasis.io/testnet/sapphire +[`signSubmit()`]: https://github.com/oasisprotocol/oasis-sdk/blob/main/docs/rofl/features/appd.md