Skip to main content

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 ROFL-deployed Eliza agent running inside a TEE
  • enclave-managed keys for on-chain actions

Prerequisites

You will need:

  • 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

Architecture Overview

  • 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

Init and Create App

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.

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:

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.

# 1) Install bun and ElizaOS CLI
bun --version || curl -fsSL https://bun.sh/install | bash
bun install -g @elizaos/cli

# Configure model credentials locally
elizaos create eliza-agent
# OPENAI key when prompted

# Start the agent locally
elizaos start

Containerize and deploy to ROFL

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
#!/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
docker/Dockerfile
FROM node:24-alpine
WORKDIR /app

# Copy your ElizaOS project (created with `elizaos create ...`).
ADD . /app

# 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"]

Add compose.yaml at project root:

compose.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:

docker compose build
docker compose push

For extra security, pin the image digest in compose.yaml.

Build ROFL bundle

oasis rofl build

This produces the .orc bundle for deployment, sealing in enclave identity.

Then publish the enclave identities and config:

oasis rofl update

Deploy

oasis rofl 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
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<any>((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();
});
}

Use the helper from inside your Eliza agent (e.g., in a tool/action handler):

Example usage in an Eliza handler
import { ensureEnclaveWallet, signSubmitEthTx } from "./rofl";

// 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,
});

Wire into ElizaOS (enable tool and prompting)

Add a small tool wrapper Eliza can call. Register it in your agent config and nudge the model to invoke it via system instructions.

tools/rofl.ts
import { ensureEnclaveWallet, signSubmitEthTx } from "../src/rofl";

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);
}

Register the tool in your Eliza agent configuration (adjust pathing to your build output):

agent.config.json
{
"name": "rofl-eliza",
"model": "openai:gpt-4o-mini",
"tools": ["./dist/tools/rofl.js"]
}

Guide the model to use the tool via system prompt:

system-prompt.txt
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)
services:
eliza-agent:
# ...
volumes:
- /run/rofl-appd.sock:/run/rofl-appd.sock

Install dependencies and build with your app:

npm i ethers @oasisprotocol/rofl-client typescript
npx tsc

Your docker/entry.sh can continue to start just the Eliza agent:

#!/bin/sh
set -e
exec elizaos start

Testing it out

After oasis rofl deploy, use the CLI to check machine status and view logs.

# 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:

Inspect on-chain activity and app details in the Oasis Explorer: