Building Persistent AI Agents on Solana with Marlin TEEs

Are you looking to create autonomous AI agents that can interact with the Solana blockchain securely and consistently? Look no further! In this guide, we’ll walk through building a Solana Agent Server that leverages Marlin’s Trusted Execution Environment (TEE) for secure, persistent wallet management. This approach solves one of the biggest challenges in blockchain AI agents: maintaining a consistent on-chain identity across deployments.

Why Build on Marlin CVM?

Trusted Execution Environments provide several key advantages for blockchain AI agents:

  1. Data Security: Your agent’s private keys are protected within the TEE

  2. Deterministic Computation: Consistent results across runs

  3. Persistent Identity: Same wallet address across deployments for the same code

  4. Tamper Resistance: Protection against both external and internal threats

Let’s build an agent that takes full advantage of these properties!

Prerequisites

Before starting, make sure you have:

  • Node.js (v16+) installed

  • Docker for building the container image

  • The Oyster CVM CLI installed (see getting started guide)

  • A wallet with some funds for deployment (on Arbitrum One)

Getting Started with Solana Agent Integration

Instead of starting from scratch, we’ll use the ready-made Solana Agent Integration repository which already has all the necessary code configured:


git clone https://github.com/marlinprotocol/solana-agent-integration

cd solana-agent-integration

npm install

Let’s explore the key parts of this implementation.

Understanding the Code Architecture

Server Structure

The core of the application is in src/index.ts, which sets up an Express server with three main endpoints:

  1. /init - Initializes the agent and creates the Solana wallet using the TEE-derived key

  2. /chat - Provides an interface to interact with the AI agent

  3. /wallet - Returns the wallet address of the agent


// Core endpoints

app.post('/init', async (req, res) => { /* ... */ });

app.post('/chat', checkInitialization, async (req, res) => { /* ... */ });

app.get('/wallet', checkInitialization, (req, res) => { /* ... */ });

Key Derivation for Persistent Identity

The most important part of the code is how we derive a persistent key from the Marlin TEE. Unlike standard applications that generate a random key each time, we leverage Marlin’s KMS derive server:


// Get Ed25519 private key from localhost derive server

const response = await fetch("http://127.0.0.1:1100/derive/ed25519?path=signing-server");

const arrayBuffer = await response.arrayBuffer();

const privateKey = new Uint8Array(arrayBuffer);

// Use the private key to create a Solana keypair

const keypair = Keypair.fromSeed(privateKey.slice(0, 32));

const walletAddress = keypair.publicKey.toBase58();

This approach utilizes Marlin’s Key Management Service to derive a deterministic key based on the path parameter. The derive server is available at http://127.0.0.1:1100 inside the Oyster CVM, providing various endpoints for key derivation as described in the persistent keys tutorial.

Containerization and Deployment

Understanding the Dockerfile

The Dockerfile uses a two-stage build process to optimize the final image size:


# Build stage

FROM node:18-slim AS builder

WORKDIR /usr/src/app

COPY package*.json ./

RUN npm ci

COPY . .

RUN npm run build

# Production stage

FROM node:18-alpine3.19

RUN apk --no-cache add ca-certificates

WORKDIR /usr/src/app

COPY --from=builder /usr/src/app/dist ./dist

COPY package*.json ./

RUN npm ci --omit=dev --omit=optional

USER node

EXPOSE 8000

CMD ["node", "dist/index.js"]

The first stage compiles the TypeScript code, while the second stage creates a minimal production image with only the necessary dependencies.

Docker Compose Configuration

The docker-compose.yml file defines the service for deployment on Oyster:


services:

solana-agent-kit:

image: <username>/solana-agent-kit:latest

network_mode: "host"

restart: unless-stopped

Important: Make sure to replace <username> with your Docker Hub username before proceeding.

As noted in the Oyster deployment tutorial, the network_mode: "host" setting is required for proper functionality on Oyster CVM.

Deploying on Marlin TEE

Step 1: Build and Push Your Docker Image


docker build -t <username>/solana-agent-kit:latest .

docker push <username>/solana-agent-kit:latest

Replace <username> with your Docker Hub username.

Step 2: Deploy to Oyster CVM

Let’s deploy the application for a short trial period:


oyster-cvm deploy \

--wallet-private-key <wallet-secret> \

--duration-in-minutes 15 \

--docker-compose ./docker-compose.yml

The deployment process follows the standard steps outlined in the deployment tutorial:

  1. Parses your Docker Compose file

  2. Calculates required resources and associated costs

  3. Deploys your application to the Oyster CVM

  4. Returns a transaction hash, job ID, and IP address

Make note of the IP address that’s returned - we’ll use it to interact with our agent.

Testing the Persistent Identity

Step 1: Initialize the Agent

Let’s initialize the agent with our OpenAI API key and Solana RPC URL:


curl --location '<cvm-ip-address>:8000/init' \

--header 'Content-Type: application/json' \

--data '{

"RPC_URL": "https://api.testnet.solana.com",

"OPENAI_API_KEY": "your-openai-api-key-here"

}

'

Step 2: Get the Wallet Address

Now retrieve the wallet address:


curl --location '<cvm-ip-address>:8000/wallet' \

--header 'Content-Type: application/json'

The response will show the Solana public key address of your agent.

Step 3: Deploy a Second Instance and Verify Persistence

To prove that the wallet persists across deployments, let’s terminate the current instance and deploy a new one:

  1. Stop the current deployment (or wait for it to expire after 15 minutes)

  2. Deploy a new instance:


oyster-cvm deploy \

--wallet-private-key <wallet-secret> \

--duration-in-minutes 15 \

--docker-compose ./docker-compose.yml

  1. Initialize the agent again on the new instance:

curl --location '<new-cvm-ip-address>:8000/init' \

--header 'Content-Type: application/json' \

--data '{

"RPC_URL": "https://api.testnet.solana.com",

"OPENAI_API_KEY": "your-openai-api-key-here"

}

'

  1. Check the wallet address:

curl --location '<new-cvm-ip-address>:8000/wallet' \

--header 'Content-Type: application/json'

You’ll notice that the wallet address is exactly the same as before! This confirms that the identity persists across deployments.

How Persistent Key Derivation Works

The Oyster KMS provide the foundation for this persistent identity. According to the Oyster Key Management Service documentation:

“Applications running inside Oyster CVM can retrieve secrets from the KMS service… Secrets remain accessible even after an Oyster CVM restart… The same secret can be shared across multiple CVM instances running the same application.”

The key features of this system are:

  1. Security: “Secrets are accessible only to authorized applications running in Oyster CVMs”

  2. Isolation: “Secrets are protected from third parties, eavesdroppers, CVM hosts, and other CVM applications”

  3. Persistence: For the same image, the same key is returned when the same path parameter is used, even across different deployments

Key Benefits of This Architecture

1. Persistent Wallet Identity

Unlike traditional setups where each deployment generates a new wallet, your Oyster TEE-based agent maintains the same wallet address across restarts and redeployments. This means:

  • Your agent maintains a consistent on-chain identity

  • You only need to fund the wallet once

  • On-chain reputation and history remain intact

  • Smart contract approvals persist between deployments

2. Secure Key Management

The private key derivation process leverages the TEE’s secure enclave:

  • Private keys never leave the secure environment

  • The derive server runs within the same TEE as your application

  • Only your code, running in the same TEE, can access the private key

  • Even if someone gained access to your deployment, they couldn’t extract the keys

3. Deterministic Results

The key derivation is deterministic based on the code and path parameter:

  • Same code + same path = same key

  • Reproducible across any number of deployments

  • No need for external key management services

Integration with External Services

Your agent can interact with any Solana dApp or service using its persistent identity:

  • Connect to NFT marketplaces

  • Interact with DeFi protocols

  • Execute trades based on AI analysis

  • Manage DAOs or other on-chain governance

Conclusion

Building AI agents on Solana with Marlin TEE provides a powerful combination of security, persistence, and autonomy. The approach outlined in this guide ensures your agent maintains a consistent identity while keeping private keys secure.

This architecture is particularly valuable for:

  • Long-running agents that need to maintain state across restarts

  • Multi-agent systems where identity is crucial

  • Financial applications handling real value

  • Services requiring strict security guarantees

By combining the intelligence of LLMs with the security of TEEs and the power of Solana, you’ve created a foundation for truly autonomous on-chain agents!

Further Resources

Happy building!