Bytespace Proxy Architecture
The Bytespace Proxy is a secure intermediary that handles all external API calls on behalf of the bot0 daemon. This architecture ensures that sensitive credentials never exist on the daemon, which runs AI agents that could be vulnerable to prompt injection attacks.
Overview
┌─────────────────────────────────────────────────────────────────────────────────┐
│ UNIFIED PROXY ARCHITECTURE │
│ │
│ Daemon Proxy (Bytespace or Self-Hosted) Backend │
│ ────── ──────────────────────────────── ─────── │
│ │
│ NO SECRETS Retrieves keys from ctx0 DB: │
│ on daemon • User's API keys (encrypted) │
│ • Or Bytespace business keys │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ │ Request │ │ Request │ │ │
│ │ Daemon │──────────▶│ Proxy │──────────▶│ Anthropic/ │ │
│ │ (bot0) │ (session │ (open src) │ (with │ OpenAI/ │ │
│ │ │ token) │ │ API key) │ Google │ │
│ │ │◀──────────│ │◀──────────│ │ │
│ └──────────────┘ Stream └──────────────┘ Response └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────┘
Core Components
1. Proxy Package (@bot0/proxy)
The proxy package provides the core functionality for secure credential management and request proxying. It's designed to be used by both Bytespace (hosted) and self-hosted deployments.
Location: packages/proxy/src/
packages/proxy/
├── src/
│ ├── lib/
│ │ ├── encryption.ts # AES-256-GCM encryption utilities
│ │ ├── validation.ts # API key and credential validation
│ │ └── types.ts # Type definitions
│ ├── routes/
│ │ ├── keys.ts # API key CRUD operations
│ │ ├── db-credentials.ts # Self-hosted DB credential management
│ │ ├── llm.ts # LLM request proxy
│ │ └── db.ts # Database request proxy
│ └── index.ts # Main exports
└── package.json
2. Bytespace API Routes
Bytespace implements the proxy endpoints with billing integration.
Location: apps/bytespace/src/app/api/
| Endpoint | Purpose |
|---|---|
/api/keys | Manage user API keys |
/api/keys/[id] | Delete specific API key |
/api/db-credentials | Manage self-hosted database credentials |
/api/proxy/llm/[provider]/[...path] | Proxy LLM requests to providers |
/api/proxy/db | Proxy database operations |
Deployment Tiers
The proxy architecture supports three deployment tiers, allowing users to choose their level of self-hosting.
Tier 1: Fully Hosted (Default)
Everything runs on Bytespace infrastructure.
┌────────────────────────────────────────────────────┐
│ TIER 1: Fully Hosted │
│ ───────────────────── │
│ • LLM Proxy: Bytespace │
│ • ctx0 DB: Bytespace │
│ • API Keys: Bytespace (encrypted) │
│ • Credits: Bytespace │
│ │
│ User provides: Nothing (pay-as-you-go credits) │
└────────────────────────────────────────────────────┘
Benefits:
- Zero configuration
- Pay-as-you-go pricing
- No infrastructure to manage
Tier 2: Self-Hosted Database
User hosts their own ctx0 database on Supabase.
┌────────────────────────────────────────────────────┐
│ TIER 2: Self-Hosted DB │
│ ─────────────────────── │
│ • LLM Proxy: Bytespace │
│ • ctx0 DB: User's Supabase │
│ • API Keys: User's ctx0 DB (encrypted) │
│ • Credits: Bytespace (if using Bytespace credits) │
│ │
│ User provides: Supabase URL + credentials │
└────────────────────────────────────────────────────┘
Benefits:
- Full control over conversation history and memories
- Data stays in user's infrastructure
- Can still use Bytespace credits for LLM calls
Tier 3: Fully Self-Hosted
User runs their own proxy server and database.
┌────────────────────────────────────────────────────┐
│ TIER 3: Fully Self-Hosted │
│ ──────────────────────────────────────── │
│ • LLM Proxy: User's proxy (open source) │
│ • ctx0 DB: User's Supabase │
│ • API Keys: User's ctx0 DB (user's encryption key)│
│ • Credits: N/A (user's own API keys) │
│ │
│ User provides: Everything (no Bytespace needed) │
└────────────────────────────────────────────────────┘
Benefits:
- Complete independence from Bytespace
- User controls encryption key
- No usage tracking or billing
LLM Proxy
The LLM proxy forwards requests to AI providers while handling authentication and billing.
Supported Providers
| Provider | Base URL | Auth Header |
|---|---|---|
| Anthropic | https://api.anthropic.com | x-api-key |
| OpenAI | https://api.openai.com | Authorization: Bearer |
https://generativelanguage.googleapis.com | x-goog-api-key | |
| XAI | https://api.x.ai | Authorization: Bearer |
Request Flow
// Daemon sends request to proxy POST /api/proxy/llm/anthropic/v1/messages Authorization: Bearer <session_token> Content-Type: application/json { "model": "claude-sonnet-4-6", "messages": [{ "role": "user", "content": "Hello" }], "max_tokens": 1024 }
Proxy Processing:
- Validate session token - Verify the request comes from an authenticated user
- Retrieve API key - Get user's key from
ctx0_api_keystable, or use Bytespace fallback - Credit check (if using Bytespace credits) - Verify user has sufficient credits
- Forward request - Send to LLM provider with decrypted API key
- Stream response - Return streaming response to daemon
- Log usage (if using Bytespace credits) - Record tokens for billing
API Key Priority
1. User's own API key (from ctx0_api_keys, encrypted)
↓ if not found
2. Bytespace fallback key (requires credit check)
↓ if no credits
3. Error: "Add credits or configure your own API key"
Code Example
import { createLlmProxy } from '@bot0/proxy'; const llmProxy = createLlmProxy({ // Optional billing hooks (Bytespace only) billingHook: { checkCredits: async (userId) => { const result = await creditLimitCheck(userId, 'bot0_daemon'); return { canMakeRequest: result.canMakeRequest, reason: result.limitExceededReason, }; }, logUsage: async (userId, model, inputTokens, outputTokens) => { await logAIUsage(model, inputTokens, outputTokens, 0, 'bot0_daemon', userId); }, }, // Optional fallback keys (Bytespace business keys) fallbackKeys: { anthropic: process.env.BYTESPACE_ANTHROPIC_KEY, openai: process.env.BYTESPACE_OPENAI_KEY, google: process.env.BYTESPACE_GOOGLE_KEY, }, });
Database Proxy
The database proxy allows the daemon to interact with ctx0 tables without having database credentials.
Supported Operations
| Operation | Description |
|---|---|
select | Query rows with filters |
insert | Insert new rows |
update | Update existing rows |
delete | Delete rows |
upsert | Insert or update based on conflict |
rpc | Call database functions |
Security: User ID Enforcement
For all ctx0 tables (prefixed with ctx0_), the proxy automatically:
- Filters queries by
user_idon SELECT, UPDATE, DELETE - Sets
user_idon INSERT and UPSERT
This prevents users from accessing other users' data, even if they craft malicious requests.
Request Format
POST /api/proxy/db Authorization: Bearer <session_token> Content-Type: application/json { "operation": "select", "table": "ctx0_sessions", "select": "id, created_at, summary", "filters": { "status": "active" } }
Response Format
{ "data": [...], // Query results "error": null // Or error message string }
Code Example
// From daemon const db = new ProxiedSupabaseClient(config.sessionToken, config.proxyUrl); // Query sessions const { data: sessions } = await db .from('ctx0_sessions') .select('id, summary') .eq('status', 'active'); // Insert message await db .from('ctx0_session_messages') .insert({ session_id: sessionId, role: 'user', content: 'Hello' });
Encryption
All credentials are encrypted at rest using AES-256-GCM.
Encryption Process
import { encryptSecret, decryptSecret } from '@bot0/proxy'; // Encrypt an API key const { encrypted, nonce } = encryptSecret('sk-ant-api03-...'); // encrypted: Base64 ciphertext + auth tag // nonce: Base64 12-byte nonce // Decrypt when needed const apiKey = decryptSecret(encrypted, nonce);
Key Storage
| What | Where Stored | How Protected |
|---|---|---|
| Encryption key | Environment variable (API_KEY_ENCRYPTION_KEY) | Server-side only |
| Encrypted credentials | Database (ctx0_api_keys, ctx0_db_credentials) | AES-256-GCM |
| Session tokens | Daemon config (~/.bot0/config.json) | Revocable server-side |
Generating Encryption Key
For self-hosted deployments:
# Generate a 32-byte (256-bit) key openssl rand -hex 32 > ~/.bot0/encryption_key # Set in environment export API_KEY_ENCRYPTION_KEY=$(cat ~/.bot0/encryption_key)
Database Schema
ctx0_api_keys
Stores encrypted API keys for LLM providers.
CREATE TABLE ctx0_api_keys ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL, -- Provider info provider TEXT NOT NULL, -- 'anthropic', 'openai', 'google' name TEXT, -- User's label -- Encrypted key storage encrypted_key TEXT NOT NULL, -- AES-256-GCM encrypted key_nonce TEXT NOT NULL, -- Unique nonce key_suffix TEXT NOT NULL, -- Last 4 chars (sk-...xxxx) -- Status is_active BOOLEAN DEFAULT true, is_valid BOOLEAN DEFAULT true, -- Usage stats request_count INT DEFAULT 0, total_input_tokens BIGINT DEFAULT 0, total_output_tokens BIGINT DEFAULT 0, -- Timestamps created_at TIMESTAMPTZ DEFAULT NOW(), last_used_at TIMESTAMPTZ, last_validated_at TIMESTAMPTZ, UNIQUE(user_id, provider) );
ctx0_db_credentials
Stores encrypted Supabase credentials for self-hosted users.
CREATE TABLE ctx0_db_credentials ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL, -- Connection info supabase_url TEXT NOT NULL, project_name TEXT, -- Encrypted credentials encrypted_anon_key TEXT NOT NULL, anon_key_nonce TEXT NOT NULL, encrypted_service_role_key TEXT NOT NULL, service_role_key_nonce TEXT NOT NULL, -- Display info anon_key_suffix TEXT NOT NULL, -- Status is_active BOOLEAN DEFAULT true, is_valid BOOLEAN DEFAULT true, -- Usage stats request_count INT DEFAULT 0, last_used_at TIMESTAMPTZ, UNIQUE(user_id) );
Daemon Integration
The daemon uses proxied clients that route all requests through the proxy.
ProxiedAnthropicClient
import { ProxiedAnthropicClient } from '@bot0/daemon'; const client = new ProxiedAnthropicClient({ sessionToken: config.sessionToken, proxyUrl: config.proxyUrl, }); // Uses Anthropic SDK interface, but routes through proxy const response = await client.createMessage({ model: 'claude-sonnet-4-6', messages: [{ role: 'user', content: 'Hello' }], max_tokens: 1024, });
ProxiedSupabaseClient
import { ProxiedSupabaseClient } from '@bot0/daemon'; const db = new ProxiedSupabaseClient( config.sessionToken, config.proxyUrl ); // Supabase-like interface, but routes through proxy const { data } = await db .from('ctx0_sessions') .select('*') .eq('status', 'active');
Daemon Configuration
The daemon stores only non-secret configuration:
// ~/.bot0/config.json { "proxyUrl": "https://api.bytespace.ai", "sessionToken": "byt_xxx...", "defaultProvider": "anthropic", "defaultModel": "claude-sonnet-4-6" }
What's NOT in the config:
- API keys (stored encrypted on proxy)
- Database credentials (stored encrypted on proxy)
- Encryption keys (only on proxy server)
API Reference
POST /api/keys
Add or update an API key.
Request:
{ "provider": "anthropic", "apiKey": "sk-ant-api03-...", "name": "My Anthropic Key" }
Response:
{ "id": "uuid", "provider": "anthropic", "name": "My Anthropic Key", "suffix": "xxxx", "createdAt": "2024-01-01T00:00:00Z" }
GET /api/keys
List all API keys (masked).
Response:
[ { "id": "uuid", "provider": "anthropic", "name": "My Anthropic Key", "suffix": "xxxx", "isActive": true, "requestCount": 42, "createdAt": "2024-01-01T00:00:00Z", "lastUsedAt": "2024-01-15T12:00:00Z" } ]
DELETE /api/keys/[id]
Delete an API key.
Response:
{ "success": true }
POST /api/db-credentials
Save self-hosted database credentials.
Request:
{ "supabaseUrl": "https://xxx.supabase.co", "anonKey": "eyJ...", "serviceRoleKey": "eyJ...", "projectName": "My ctx0" }
POST /api/proxy/llm/[provider]/[...path]
Forward LLM request to provider.
Request: Same as native provider API Response: Streaming response from provider
POST /api/proxy/db
Execute database operation.
Request:
{ "operation": "select" | "insert" | "update" | "delete" | "upsert" | "rpc", "table": "ctx0_sessions", "data": {}, "filters": {}, "select": "*" }
Self-Hosting Guide
Prerequisites
- Node.js 20+
- Supabase project (for ctx0 database)
- 32-byte encryption key
Setup Steps
-
Clone and build the proxy package:
bashgit clone https://github.com/bot0/bot0.git cd bot0 pnpm install pnpm --filter @bot0/proxy build -
Generate encryption key:
bashopenssl rand -hex 32 > encryption_key.txt -
Set up Supabase:
- Create a new Supabase project
- Run the ctx0 migrations from
packages/ctx0/migrations/
-
Configure environment:
bashexport API_KEY_ENCRYPTION_KEY=$(cat encryption_key.txt) export CTX0_SUPABASE_URL=https://your-project.supabase.co export CTX0_SUPABASE_SERVICE_KEY=your-service-role-key -
Deploy the proxy:
- Use the routes from
packages/proxy/src/routes/ - Deploy as Express, Fastify, or Next.js API routes
- Use the routes from
-
Configure daemon:
json// ~/.bot0/config.json { "proxyUrl": "https://your-proxy.example.com" }
What's Bytespace-Proprietary
The following components are not open-sourced and remain Bytespace-only:
| Component | Purpose |
|---|---|
| Credit tracking | Pay-as-you-go billing |
logAIUsage | Usage logging for billing |
creditLimitCheck | Credit limit enforcement |
| Subscription management | Pro/Enterprise tiers |
| Bytespace business keys | Fallback API keys |
Self-hosted users bypass all billing by using their own API keys.