A lightweight proxy for Maple/OpenSecret's OpenAI-compatible inference endpoints, with the security and privacy benefits of Trusted Execution Environment (TEE) processing.
- OpenAI-Compatible Surface - Models, chat completions, and embeddings endpoints
- Secure TEE Processing - All requests processed in secure enclaves
- Lossless Chat Parameters - Provider-specific request fields pass through unchanged
- Streaming and Non-Streaming - Supports both chat completion response modes
- Flexible Authentication - Environment variables or per-request API keys
- Familiar Clients - Point compatible OpenAI clients at the proxy base URL
- Lightweight - Minimal overhead, maximum performance
- CORS Support - Ready for web applications
git clone <repository>cd maple-proxy
cargo build --locked --releaseAdd to your Cargo.toml:
[dependencies]
maple-proxy = { git = "https://github.com/opensecretcloud/maple-proxy" }
# Or if published to crates.io:# maple-proxy = "0.3.2"Set environment variables or use command-line arguments:
# Environment Variablesexport MAPLE_HOST=127.0.0.1 # Server host (default: 127.0.0.1)export MAPLE_PORT=8080 # Server port (default: 8080)export MAPLE_BACKEND_URL=http://localhost:3000 # Maple backend URL (prod: https://enclave.trymaple.ai)export MAPLE_PCR0_ENVIRONMENT=production # PCR0 trust roots: production (default) or developmentexport MAPLE_API_KEY=your-maple-api-key # Default API key (optional)export MAPLE_DEBUG=true # Enable debug loggingexport MAPLE_ENABLE_CORS=true # Enable CORSexport MAPLE_REQUEST_TIMEOUT_SECS=300 # Backend request timeoutexport MAPLE_STREAM_IDLE_TIMEOUT_SECS=300 # Streaming idle timeout between chunksOr use CLI arguments:
cargo run --locked -- --host 0.0.0.0 --port 8080 --backend-url https://enclave.trymaple.ai
# Development enclaves must be selected explicitly
cargo run --locked -- --backend-url https://enclave.secretgpt.ai --pcr0-environment developmentcargo run --lockedYou should see:
🚀 Maple Proxy Server started successfully!
📋 Available endpoints:
GET /health - Health check
GET /v1/models - List available models
POST /v1/chat/completions - Create chat completions (streaming & non-streaming)
POST /v1/embeddings - Create embeddings
curl http://localhost:8080/v1/models \
-H "Authorization: Bearer YOUR_MAPLE_API_KEY"curl -N http://localhost:8080/v1/chat/completions \
-H "Authorization: Bearer YOUR_MAPLE_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "model": "llama3-3-70b", "messages": [ {"role": "user", "content": "Write a haiku about technology"} ], "stream": true }'Set stream to true for Server-Sent Events or false for one JSON response.
Additional provider-specific JSON fields are forwarded without being parsed or
rewritten by the proxy or Rust SDK.
curl http://localhost:8080/v1/embeddings \
-H "Authorization: Bearer YOUR_MAPLE_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "model": "nomic-embed-text", "input": "Generate an embedding for this text" }'You can also embed Maple Proxy in your own Rust application:
use maple_proxy::{Config,Pcr0Environment, create_app};use tokio::net::TcpListener;#[tokio::main]asyncfnmain() -> Result<(),Box<dyn std::error::Error>>{// Initialize tracing
tracing_subscriber::fmt::init();// Create config programmaticallylet config = Config::new("127.0.0.1".to_string(),8081,// Custom port"https://enclave.trymaple.ai".to_string(),).with_pcr0_environment(Pcr0Environment::Production).with_api_key("your-api-key-here".to_string()).with_debug(true).with_cors(true);// Create the applet app = create_app(config.clone());// Start the serverlet addr = config.socket_addr()?;let listener = TcpListener::bind(addr).await?;println!("Maple proxy server running on http://{}", addr);
axum::serve(listener, app).await?;Ok(())}Run the example:
cargo run --locked --example library_usageimportopenaiclient=openai.OpenAI(
api_key="YOUR_MAPLE_API_KEY",
base_url="http://localhost:8080/v1"
)
# Streaming chat completionstream=client.chat.completions.create(
model="llama3-3-70b",
messages=[{"role": "user", "content": "Hello, world!"}],
stream=True
)
forchunkinstream:
ifchunk.choices[0].delta.contentisnotNone:
print(chunk.choices[0].delta.content, end="")importOpenAIfrom'openai';constopenai=newOpenAI({apiKey: 'YOUR_MAPLE_API_KEY',baseURL: 'http://localhost:8080/v1',});conststream=awaitopenai.chat.completions.create({model: 'llama3-3-70b',messages: [{role: 'user',content: 'Hello!'}],stream: true,});forawait(constchunkofstream){process.stdout.write(chunk.choices[0]?.delta?.content||'');}# Health check
curl http://localhost:8080/health
# List models
curl http://localhost:8080/v1/models \
-H "Authorization: Bearer YOUR_MAPLE_API_KEY"# Streaming chat completion
curl -N http://localhost:8080/v1/chat/completions \
-H "Authorization: Bearer YOUR_MAPLE_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "model": "llama3-3-70b", "messages": [{"role": "user", "content": "Tell me a joke"}], "stream": true }'# Embeddings
curl http://localhost:8080/v1/embeddings \
-H "Authorization: Bearer YOUR_MAPLE_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "model": "nomic-embed-text", "input": "Generate an embedding for this text" }'Maple Proxy supports two authentication methods:
Set MAPLE_API_KEY - all requests will use this key by default:
export MAPLE_API_KEY=your-maple-api-key
cargo run --lockedOverride the default key or provide one if not set:
curl -H "Authorization: Bearer different-api-key" ...Enable CORS for web applications:
export MAPLE_ENABLE_CORS=true
cargo run --lockedPull and run the official image from GitHub Container Registry:
# Pull the latest image
docker pull ghcr.io/opensecretcloud/maple-proxy:latest
# Run with your API key
docker run -p 8080:8080 \
-e MAPLE_BACKEND_URL=https://enclave.trymaple.ai \
-e MAPLE_REQUEST_TIMEOUT_SECS=300 \
-e MAPLE_STREAM_IDLE_TIMEOUT_SECS=300 \
ghcr.io/opensecretcloud/maple-proxy:latest# Build the image locally
just docker-build
# Run the container
just docker-run- Option A: Use pre-built image from GHCR
# In your docker-compose.yml, use:
image: ghcr.io/opensecretcloud/maple-proxy:latest- Option B: Build your own image
docker build -t maple-proxy:latest .- Run with docker-compose:
# Copy the example environment file
cp .env.example .env
# Edit .env with your configuration
vim .env
# Start the service
docker-compose up -dWhen deploying Maple Proxy on a public network:
- DO NOT set
MAPLE_API_KEYin the container environment - Instead, require clients to pass their API key with each request:
# Client-side authentication for public proxyclient=OpenAI(
base_url="https://your-proxy.example.com/v1",
api_key="user-specific-maple-api-key"# Each user provides their own key
)This ensures:
- Users' API keys remain private
- Multiple users can share the same proxy instance
- No API keys are exposed in container configurations
# Build image
just docker-build
# Run interactively
just docker-run
# Run in background
just docker-run-detached
# View logs
just docker-logs
# Stop container
just docker-stop
# Use docker-compose
just compose-up
just compose-logs
just compose-downThe Docker image:
- Uses multi-stage builds for minimal size (~130MB)
- Runs as non-root user for security
- Includes health checks
- Optimizes dependency caching with cargo-chef
- Supports both x86_64 and ARM architectures
# docker-compose.yml environment sectionenvironment:
- MAPLE_BACKEND_URL=https://enclave.trymaple.ai # Production backend
- MAPLE_ENABLE_CORS=true # Enable for web apps
- MAPLE_REQUEST_TIMEOUT_SECS=300 # Backend request timeout
- MAPLE_STREAM_IDLE_TIMEOUT_SECS=300 # Streaming idle timeout
- RUST_LOG=info # Logging level# - MAPLE_API_KEY=xxx # Only for private deployments!Automated Builds (GitHub Actions)
- Every push to
masterautomatically builds and publishes toghcr.io/opensecretcloud/maple-proxy:latest - Git tags (e.g.,
v1.0.0) trigger versioned releases - Multi-platform images (linux/amd64, linux/arm64) built automatically
- No manual intervention needed - just push your code!
Local Development (Justfile)
# For local testing and debugging
just docker-build # Build locally
just docker-run # Test locally
just ghcr-push v1.2.3 # Manual push (requires login)Use GitHub Actions for production releases, Justfile for local development.
cargo build --lockedexport MAPLE_DEBUG=true
cargo run --lockedcargo test --lockedMaple Proxy supports all models available in the Maple/OpenSecret platform, including:
llama3-3-70b- Llama 3.3 70B parameter modelnomic-embed-text- Embedding model for/v1/embeddings- And many others - check
/v1/modelsendpoint for current list
"No API key provided"
- Set
MAPLE_API_KEYenvironment variable or provideAuthorization: Bearer <key>header
"Failed to establish secure connection"
- Check your
MAPLE_BACKEND_URLis correct - Ensure your API key is valid
- Check network connectivity
Connection refused
- Make sure the server is running on the specified host/port
- Check firewall settings
Enable debug logging for detailed information:
export MAPLE_DEBUG=true
cargo run --locked┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ OpenAI Client │───▶│ Maple Proxy │───▶│ Maple Backend │
│ (Python/JS) │ │ (localhost) │ │ (TEE) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
- Client makes standard OpenAI API calls to localhost
- Maple Proxy handles authentication and TEE handshake
- Requests are securely forwarded to Maple's TEE infrastructure
- Responses are streamed back to the client in OpenAI format
MIT License - see LICENSE file for details.
Contributions welcome! Please feel free to submit a Pull Request.