Skip to main content

Ember Proxy

The Ember proxy (ghost-ember-proxy, port 3010) is a persistent phantom key access service that performs server-side Access Proof generation and proxies AI chat requests to Anthropic's API. It enables the Specter webapp's Ember feature — a privacy-preserving AI assistant that operates within the context of a user's phantom key session.

Purpose

Ember combines two capabilities that require server-side execution:

  1. Phantom key access: Generating ZK Access Proofs requires Poseidon hashing and Groth16 proving, which are too expensive for mobile clients. The Ember proxy performs this computation on behalf of the user, maintaining persistent access sessions tied to phantom keys.

  2. AI chat proxy: The Ember chat feature communicates with Anthropic's Claude API. API keys cannot be embedded in client-side code, so the proxy holds the decrypted API key server-side and forwards chat requests.

Endpoints

POST /api/ember/connect

Establishes a persistent phantom key access session. The client provides their phantom key credentials, and the proxy generates an Access Proof that authenticates the user to the Ghost Protocol without revealing their identity.

Request:

{
"phantomKey": "0x...",
"accessTag": "0x...",
"merkleProof": {
"pathElements": ["0x...", "..."],
"pathIndices": [0, 1, "..."]
}
}

Response:

{
"sessionId": "uuid-v4",
"connected": true,
"expiresAt": "2026-03-17T14:30:00Z"
}

The proxy stores the session state server-side, keyed by the returned sessionId. Subsequent requests include this session ID to authenticate.

POST /api/ember/disconnect

Terminates an active Ember session and clears all server-side state associated with it.

Request:

{
"sessionId": "uuid-v4"
}

Response:

{
"disconnected": true
}

After disconnection, the phantom key material and any cached access proofs are securely wiped from memory.

POST /api/ember/start

Initializes a new AI chat conversation within an active Ember session. This creates a conversation context that persists across multiple chat messages.

Request:

{
"sessionId": "uuid-v4",
"systemPrompt": "You are Ember, an AI assistant for Specter..."
}

Response:

{
"conversationId": "uuid-v4",
"started": true
}

POST /api/ember/chat

Sends a message to the AI assistant and receives a response. The proxy forwards the message to Anthropic's API along with the conversation history.

Request:

{
"sessionId": "uuid-v4",
"conversationId": "uuid-v4",
"message": "How do I reveal a committed token?"
}

Response:

{
"response": "To reveal a committed token, you need to...",
"conversationId": "uuid-v4"
}

The proxy maintains the conversation history server-side, appending each user message and assistant response. This allows the AI to maintain context across a multi-turn conversation without the client needing to retransmit the full history.

POST /api/ember/end

Terminates a specific chat conversation, clearing its history from memory. The Ember session remains active for future conversations.

Request:

{
"sessionId": "uuid-v4",
"conversationId": "uuid-v4"
}

Response:

{
"ended": true
}

Session Lifecycle

Client                          Ember Proxy                    Anthropic API
│ │ │
│── POST /connect ───────────────▶│ │
│ (phantom key credentials) │ │
│◀── sessionId ──────────────────│ │
│ │ │
│── POST /start ─────────────────▶│ │
│ (sessionId, systemPrompt) │ │
│◀── conversationId ─────────────│ │
│ │ │
│── POST /chat ──────────────────▶│── API request ──────────────▶│
│ (sessionId, conversationId, │ (messages + system prompt) │
│ message) │◀── API response ─────────────│
│◀── response ───────────────────│ │
│ │ │
│── POST /chat ──────────────────▶│── API request ──────────────▶│
│◀── response ───────────────────│◀── API response ─────────────│
│ │ │
│── POST /end ───────────────────▶│ │
│ (conversationId) │ │
│◀── ended ──────────────────────│ │
│ │ │
│── POST /disconnect ────────────▶│ │
│◀── disconnected ───────────────│ │

Security Model

Phantom Key Isolation

Each Ember session is bound to a single phantom key. The proxy never stores phantom keys persistently — they exist only in-memory for the duration of the session. When a session is disconnected:

  • The phantom key material is zeroed out in memory.
  • The access proof cache is cleared.
  • The session ID is invalidated.

API Key Protection

The Anthropic API key is stored as an environment variable on the server and is never transmitted to clients. The proxy acts as an authenticated gateway:

  • Clients authenticate to the proxy via their Ember session.
  • The proxy authenticates to Anthropic via the API key.
  • The client never sees the API key.

Session Expiry

Sessions expire automatically after a configurable timeout (default: 1 hour of inactivity). Expired sessions are cleaned up by a background sweep that runs periodically.

Rate Limiting

Chat requests are rate-limited per session to prevent abuse of the Anthropic API:

LimitValue
Chat messages per session per minute10
Maximum conversation length100 messages
Maximum message size4 KB

Configuration

ParameterDefaultDescription
PORT3010HTTP server port
ANTHROPIC_API_KEYAnthropic API key for Claude access
SESSION_TIMEOUT_MS3600000Session inactivity timeout (1 hour)
MAX_CONVERSATIONS_PER_SESSION5Maximum concurrent conversations per session
RPC_URLSpecter chain RPC for access proof validation