System Overview
Complete system architecture diagram and component descriptions.
Full System Diagram

Clients → Cloudflare Edge → Storage → Provider Registry (Gemini Flash, Gemini Pro, OpenAI gpt-image-2)
Component Details
Client Layer
| Component | Protocol | Purpose |
|---|---|---|
| Claude.ai / Claude Desktop | MCP over Streamable HTTP or STDIO proxy | AI assistant integration |
| Claude Code / Codex | MCP over Streamable HTTP or STDIO proxy | Coding-agent integration |
| Cursor / VS Code | MCP over Streamable HTTP or STDIO proxy | Code editor integration |
| Web Console | REST over HTTPS | Browser-based management |
| REST Client | REST over HTTPS | Custom integrations |
Edge Layer
Worker (src/index.ts)
The entry point for all requests:
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
// 1. Parse request
// 2. Authenticate
// 3. Route to handler
}
}Responsibilities:
- Parse incoming HTTP requests
- Extract and validate API keys or OAuth bearer tokens, depending on route
- Route to appropriate handlers
- Handle CORS and errors
Auth Middleware (src/auth/)
Authentication and authorization:

Check cache, query D1, decrypt key, cache result
Durable Objects (src/mcp/agent.ts)
Stateful MCP handling:
export class GoBananasMcpAgent extends DurableObject {
private server: Server;
private tenant: ResolvedTenant;
private sessionId: string;
async fetch(request: Request) {
// Handle MCP protocol
}
}Responsibilities:
- Maintain session state
- Execute MCP tools
- Track
last_image_id - Return formatted responses
Storage Layer
D1 Database
SQLite database with these tables:
| Table | Purpose | Key Fields |
|---|---|---|
tenants | Tenant configuration | tenant_id, encrypted_gemini_key, allowed_models |
tenant_provider_credentials | Per-provider keys (Gemini, OpenAI) | tenant_id, provider, encrypted_key |
api_keys | API key mappings | api_key, tenant_id |
users | User accounts | email, role |
images | Image metadata | r2_key, prompt, parent_image_id |
sessions | Session state | last_image_id, total_images |
characters | Character library | base_prompt, reference_image_ids |
character_videos | Character video refs | character_id, r2_key |
product_references | Product refs | product_url, r2_key |
reference_groups | Reusable image groups | group_name, reference_image_ids |
scene_presets | Scene presets | scene_prompt, reference_image_ids |
style_presets | Style templates | prompt, negative_prompt |
search_presets | Saved search filters | name, filters |
usage_logs | Analytics data | operation, images_generated |
tool_execution_logs | Execution tracking | tool_name, status |
tenant_webhooks | Webhook endpoints | url, events |
quota_notifications | Quota alert dedup | threshold, billing_period |
R2 Bucket
Object storage with structure:
go-bananas-images/
├── tenant_abc123/
│ ├── 2024-01-15/
│ │ ├── generate-xyz789.png
│ │ ├── generate-xyz789-thumb.jpg
│ │ └── edit-abc456.png
│ └── 2024-01-14/
│ └── ...
└── tenant_def456/
└── ...KV Store
Fast key-value lookups:
| Namespace | Key Pattern | Value |
|---|---|---|
| API_KEYS | sk_live_xxx | tenant_id |
| TENANT_CONFIG | tenant:{id} | Tenant JSON |
External Services
Image Provider Registry
The provider abstraction (src/services/image-provider/) routes each generation request to the appropriate API based on the tenant's selected model_id. Two providers ship today, serving six models:
| Provider | Models | Endpoint pattern | Default timeout |
|---|---|---|---|
| Gemini | gemini-flash-lite-image (default), gemini-flash-image, gemini-pro-image | Google AI Studio (generativelanguage.googleapis.com) | 30s / 60s / 120s |
| OpenAI | openai-gpt-image-2, openai-gpt-image-2.5-flare, openai-gpt-image-2.5-sunburst | api.openai.com/v1/images/{generations,edits} | 240s |
Each provider implements the same ImageProviderClient contract — generate, edit, testConnection, getModelInfo — and is wrapped with a per-key circuit breaker, exponential-backoff retry, and aspect-ratio enforcement. Adding a new provider is a matter of dropping a new file into src/services/image-provider/ and registering the model in src/models/registry.ts.

Eight steps from HTTP request to response — step 6 is where the registry picks Gemini or OpenAI based on the tenant's `model_id`.
Data Relationships

Tenant owns all resources with complete isolation
Deployment Topology

Edge workers worldwide with centralized D1 and R2 storage
Configuration Files
| File | Purpose |
|---|---|
wrangler.jsonc | Cloudflare Worker config |
schema.sql | Database schema |
package.json | Dependencies |
tsconfig.json | TypeScript config |