Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,10 @@ MAPLE_PORT=8080
# Maple Backend Configuration
# Production: https://enclave.trymaple.ai
# Development: https://enclave.secretgpt.ai
# Local: http://localhost:3000
# Local: use `just run-local`; plain HTTP mock attestation is compile-time
# gated and is never enabled in release, Docker, or embedded Maple builds.
MAPLE_BACKEND_URL=https://enclave.trymaple.ai

# PCR0 trust roots must match the selected backend environment.
# Use development only with the development enclave; production is the default.
MAPLE_PCR0_ENVIRONMENT=production

# Authentication
# Your Maple API key - get this from https://trymaple.ai
MAPLE_API_KEY=your-maple-api-key-here
Expand Down
31 changes: 30 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,40 @@ Maple Proxy is a lightweight OpenAI-compatible proxy server that forwards reques
- Handles streaming responses for chat completions
- Transforms responses to OpenAI format

### TEE attestation boundary

TEE measurement authorization is owned by the OpenSecret SDK. Maple Proxy
should only call `perform_attestation_handshake`; do not add a proxy-specific
PCR allowlist or Sigstore/Rekor verifier.

The intended SDK handshake authenticates the AWS Nitro attestation document,
compares the complete PCR0/PCR1/PCR2 tuple with a release snapshot embedded in
the SDK, and only then accepts the enclave public key and performs key exchange.
There is no runtime Sigstore/Rekor or release-metadata network lookup. At SDK
update time, the updater verifies the release manifest and Cosign bundle,
including the expected signing identity and Rekor evidence, before generating
the embedded snapshot.

Mock attestation is available only through the explicitly named
`insecure-local-mock-attestation` Cargo feature. Keep it disabled in release,
Docker, and embedded Maple builds; only `just run-local` opts in.

Rekor supplies tamper-evident transparency-log evidence for the signed release
statement used to generate that snapshot. It does not itself prove Nix
reproducibility, release freshness, rollback prevention, or revocation.

This integration branch pins an exact reviewed SDK commit with default features
disabled. That staging commit deliberately contains an empty release snapshot,
so remote handshakes fail closed. Before release, replace the pin with a
snapshot-bearing reviewed commit or published crate; do not invent an
unpublished version or vendor the SDK here.

### Request Flow

1. Client sends OpenAI-compatible request to proxy
2. Proxy extracts API key (from header or default config)
3. Creates OpenSecret client and performs TEE attestation
3. Creates an OpenSecret client and delegates TEE authentication, release
authorization, and key exchange to the SDK
4. Forwards request to Maple backend (enclave.trymaple.ai or configured URL)
5. Streams response back to client in OpenAI format

Expand Down
3 changes: 1 addition & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,15 @@ path = "src/lib.rs"
name = "maple-proxy"
path = "src/main.rs"

[features]
default = []
# Explicit development-only opt-in. Release, Docker, and embedded Maple builds
# use default features and cannot accept mock attestation documents.
insecure-local-mock-attestation = ["opensecret/mock-attestation"]

[dependencies]
# OpenSecret SDK
opensecret = "3.6.1"
opensecret = { git = "https://github.com/OpenSecretCloud/OpenSecret-SDK", rev = "8ecd1a31803f45753ed6ac457a4d8553389b2336", default-features = false }

# Web server
axum = { version = "0.8.4", features = ["http2", "macros"] }
Expand Down
1 change: 0 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ USER maple
ENV MAPLE_HOST=0.0.0.0 \
MAPLE_PORT=8080 \
MAPLE_BACKEND_URL=https://enclave.trymaple.ai \
MAPLE_PCR0_ENVIRONMENT=production \
MAPLE_DEBUG=false \
MAPLE_ENABLE_CORS=true \
MAPLE_REQUEST_TIMEOUT_SECS=300 \
Expand Down
62 changes: 51 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ Environment (TEE) processing.
## 🚀 Features

- **OpenAI-Compatible Surface** - Models, chat completions, and embeddings endpoints
- **Secure TEE Processing** - All requests processed in secure enclaves
- **Attested TEE Transport** - The OpenSecret SDK establishes an attested,
encrypted channel before inference requests are forwarded
- **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
Expand Down Expand Up @@ -44,8 +45,7 @@ Set environment variables or use command-line arguments:
# Environment Variables
export 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 development
export MAPLE_BACKEND_URL=https://enclave.trymaple.ai # Maple backend URL
export MAPLE_API_KEY=your-maple-api-key # Default API key (optional)
export MAPLE_DEBUG=true # Enable debug logging
export MAPLE_ENABLE_CORS=true # Enable CORS
Expand All @@ -56,11 +56,12 @@ export MAPLE_STREAM_IDLE_TIMEOUT_SECS=300 # Streaming idle timeout between
Or use CLI arguments:
```bash
cargo run -- --host 0.0.0.0 --port 8080 --backend-url https://enclave.trymaple.ai

# Development enclaves must be selected explicitly
cargo run -- --backend-url https://enclave.secretgpt.ai --pcr0-environment development
```

For an unsigned local backend, use `just run-local`. That recipe alone enables
the explicitly named `insecure-local-mock-attestation` Cargo feature. Generic,
release, Docker, and embedded Maple builds leave the feature disabled.

## 🛠️ Usage

### Using as a Binary
Expand Down Expand Up @@ -123,7 +124,7 @@ curl http://localhost:8080/v1/embeddings \
You can also embed Maple Proxy in your own Rust application:

```rust
use maple_proxy::{Config, Pcr0Environment, create_app};
use maple_proxy::{Config, create_app};
use tokio::net::TcpListener;

#[tokio::main]
Expand All @@ -137,7 +138,6 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
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);
Expand Down Expand Up @@ -458,9 +458,49 @@ cargo run
```

1. **Client** makes standard OpenAI API calls to localhost
2. **Maple Proxy** handles authentication and TEE handshake
3. **Requests** are securely forwarded to Maple's TEE infrastructure
4. **Responses** are streamed back to the client in OpenAI format
2. **Maple Proxy** handles authentication and asks the OpenSecret SDK to
establish the TEE channel
3. **OpenSecret SDK** authenticates and authorizes the enclave before accepting
its key and completing key exchange
4. **Requests** are encrypted and forwarded to Maple's TEE infrastructure
5. **Responses** are streamed back to the client in OpenAI format

### TEE release authorization

The Sigstore/Rekor release-authorization work belongs in the OpenSecret SDK,
not in Maple Proxy. For each non-local backend, the SDK is expected to:

1. verify the AWS Nitro attestation document, certificate chain, nonce, and
signature;
2. extract and validate the complete PCR0/PCR1/PCR2 measurement tuple;
3. compare that tuple with the release snapshot embedded in the SDK; and
4. accept the enclave public key and perform key exchange only after the tuple
is present in that snapshot.

Maple Proxy continues to call `perform_attestation_handshake`; it neither
maintains a second PCR allowlist nor implements a separate Sigstore verifier.
Keeping this policy in the SDK gives every Rust SDK consumer the same
fail-closed authorization boundary before application data is sent.

There is no Sigstore, Rekor, or other release-metadata network lookup during a
runtime handshake. At SDK update time, the release-snapshot updater verifies
the release manifest and Cosign bundle, including the expected signing identity
and Rekor evidence, before generating the embedded snapshot. Consumers then
review and pin the SDK release containing that generated snapshot.

Sigstore makes a release statement and its signing identity tamper-evident in
an append-only transparency log. It does **not** prove that an artifact was
reproducibly built, and it does **not** make an old, previously authorized
release fresh. Reproducibility remains a separate Nix rebuild/compare property;
rollback prevention, revocation, or minimum-version policy must also be handled
separately.

> **Integration status:** this branch pins the exact reviewed SDK integration
> commit with default features disabled. Its embedded release snapshot is
> intentionally empty, so remote handshakes fail closed. Update the pin to a
> reviewed snapshot-bearing commit or published crate after the first signed
> backend release; do not merge or publish this staging state as a working
> production proxy.

## 📝 License

Expand Down
1 change: 0 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ services:

# Backend configuration (defaults to production)
- MAPLE_BACKEND_URL=${MAPLE_BACKEND_URL:-https://enclave.trymaple.ai}
- MAPLE_PCR0_ENVIRONMENT=${MAPLE_PCR0_ENVIRONMENT:-production}

# Authentication - Uncomment ONLY for private/internal deployments!
# For public deployments: Keep this commented out - clients will pass their own API keys
Expand Down
3 changes: 1 addition & 2 deletions examples/library_usage.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use maple_proxy::{create_app, Config, Pcr0Environment};
use maple_proxy::{create_app, Config};
use tokio::net::TcpListener;

#[tokio::main]
Expand All @@ -12,7 +12,6 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
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);
Expand Down
3 changes: 3 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@
clang
libclang

# TypeScript / OpenClaw plugin
nodejs_22

# Useful tools
jq
just
Expand Down
55 changes: 49 additions & 6 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ run-with-backend url:

# Run pointing to local backend
run-local:
@just run-with-backend "http://localhost:3000"
@echo "🚧 Starting with development-only mock attestation enabled"
@bash -c 'set -a; source .env 2>/dev/null; set +a; MAPLE_BACKEND_URL=http://localhost:3000 cargo run --features insecure-local-mock-attestation'

# Run pointing to production backend
run-prod:
Expand Down Expand Up @@ -163,7 +164,6 @@ env:
@echo "MAPLE_HOST: ${MAPLE_HOST:-127.0.0.1}"
@echo "MAPLE_PORT: ${MAPLE_PORT:-8080}"
@echo "MAPLE_BACKEND_URL: ${MAPLE_BACKEND_URL:-https://enclave.trymaple.ai}"
@echo "MAPLE_PCR0_ENVIRONMENT: ${MAPLE_PCR0_ENVIRONMENT:-production}"
@echo "MAPLE_API_KEY: ${MAPLE_API_KEY:-[not set]}"
@echo "MAPLE_DEBUG: ${MAPLE_DEBUG:-false}"
@echo "MAPLE_ENABLE_CORS: ${MAPLE_ENABLE_CORS:-false}"
Expand All @@ -183,7 +183,6 @@ docker-run:
-p ${MAPLE_PORT:-8080}:8080 \
-e MAPLE_API_KEY=${MAPLE_API_KEY} \
-e MAPLE_BACKEND_URL=${MAPLE_BACKEND_URL:-https://enclave.trymaple.ai} \
-e MAPLE_PCR0_ENVIRONMENT=${MAPLE_PCR0_ENVIRONMENT:-production} \
-e MAPLE_DEBUG=${MAPLE_DEBUG:-false} \
-e MAPLE_ENABLE_CORS=${MAPLE_ENABLE_CORS:-true} \
-e MAPLE_REQUEST_TIMEOUT_SECS=${MAPLE_REQUEST_TIMEOUT_SECS:-300} \
Expand All @@ -198,7 +197,6 @@ docker-run-detached:
-p ${MAPLE_PORT:-8080}:8080 \
-e MAPLE_API_KEY=${MAPLE_API_KEY} \
-e MAPLE_BACKEND_URL=${MAPLE_BACKEND_URL:-https://enclave.trymaple.ai} \
-e MAPLE_PCR0_ENVIRONMENT=${MAPLE_PCR0_ENVIRONMENT:-production} \
-e MAPLE_DEBUG=${MAPLE_DEBUG:-false} \
-e MAPLE_ENABLE_CORS=${MAPLE_ENABLE_CORS:-true} \
-e MAPLE_REQUEST_TIMEOUT_SECS=${MAPLE_REQUEST_TIMEOUT_SECS:-300} \
Expand Down Expand Up @@ -266,5 +264,50 @@ ghcr-pull tag="latest":
@{{container}} pull ghcr.io/opensecretcloud/maple-proxy:{{tag}}
@echo "✅ Pulled ghcr.io/opensecretcloud/maple-proxy:{{tag}}"

# Compatibility alias for the complete repository check.
check-all: check
# === OpenClaw Plugin ===

# Install plugin dependencies
plugin-install:
@echo "📦 Installing plugin dependencies..."
@cd openclaw-plugin && npm install
@echo "✅ Plugin dependencies installed"

# Build plugin (TypeScript -> JS)
plugin-build:
@echo "🔨 Building OpenClaw plugin..."
@cd openclaw-plugin && npm run build
@echo "✅ Plugin built"

# Lint plugin
plugin-lint:
@echo "🔍 Linting plugin..."
@cd openclaw-plugin && npm run lint
@echo "✅ Plugin linted"

# Test plugin
plugin-test:
@echo "🧪 Testing plugin..."
@cd openclaw-plugin && npm test
@echo "✅ Plugin tests passed"

# Check all (Rust + plugin)
check-all: check plugin-lint plugin-test
@echo "✅ All checks passed (Rust + Plugin)"

# Link plugin locally for OpenClaw development
plugin-link:
@echo "🔗 Linking plugin to OpenClaw extensions..."
@openclaw plugins install -l ./openclaw-plugin
@echo "✅ Plugin linked"

# Pack plugin for npm publishing
plugin-pack:
@echo "📦 Packing plugin for npm..."
@cd openclaw-plugin && npm pack
@echo "✅ Plugin packed"

# Publish plugin to npm
plugin-publish:
@echo "🚀 Publishing plugin to npm..."
@cd openclaw-plugin && npm publish --access public
@echo "✅ Plugin published"
Loading
Loading