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:
-
Data Security: Your agent’s private keys are protected within the TEE
-
Deterministic Computation: Consistent results across runs
-
Persistent Identity: Same wallet address across deployments for the same code
-
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:
-
/init
- Initializes the agent and creates the Solana wallet using the TEE-derived key -
/chat
- Provides an interface to interact with the AI agent -
/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:
-
Parses your Docker Compose file
-
Calculates required resources and associated costs
-
Deploys your application to the Oyster CVM
-
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:
-
Stop the current deployment (or wait for it to expire after 15 minutes)
-
Deploy a new instance:
oyster-cvm deploy \
--wallet-private-key <wallet-secret> \
--duration-in-minutes 15 \
--docker-compose ./docker-compose.yml
- 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"
}
'
- 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:
-
Security: “Secrets are accessible only to authorized applications running in Oyster CVMs”
-
Isolation: “Secrets are protected from third parties, eavesdroppers, CVM hosts, and other CVM applications”
-
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!