Skip to content

Tenant Management ​

Create and manage tenants for a self‑hosted Go Bananas! deployment.

Overview ​

Go Bananas! is multi‑tenant by design. Each tenant:

  • Has isolated data (images, characters, products)
  • Uses up to five provider API keys for Gemini and up to five for OpenAI — encrypted independently in tenant_provider_credentials
  • Picks which models it can route to via an allowed_models allowlist
  • Has configurable quotas and rate limits
  • Can have multiple API keys

Creating Tenants ​

Interactive Setup ​

bash
npm run setup-tenant

Prompts for tenant id, display name, provider keys (Gemini and/or OpenAI), allowed models, quota, and rate limit, then writes the tenant config + initial API keys.

Via Admin API ​

http
POST /admin/tenants
bash
curl -X POST "https://gobananasai.com/admin/tenants" \
  -H "Authorization: Bearer <admin_session_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "tenant_id": "acme-corp",
    "tenant_name": "ACME Corporation",
    "providerCredentials": {
      "gemini": "AIza...",
      "openai": "sk-..."
    },
    "allowedModels": ["gemini-flash-lite-image", "gemini-flash-image", "gemini-pro-image", "openai-gpt-image-2", "openai-gpt-image-2.5-flare", "openai-gpt-image-2.5-sunburst"],
    "default_model_id": "gemini-flash-lite-image",
    "contact_email": "ops@acme.com",
    "monthly_quota_mb": 10240,
    "rate_limit_per_minute": 60
  }'

Response (keys shown only once):

json
{
  "data": {
    "tenant": {
      "tenantId": "acme-corp",
      "tenantName": "ACME Corporation",
      "isActive": true,
      "monthlyQuotaMb": 10240,
      "rateLimitPerMinute": 60
    },
    "apiKeys": [
      { "apiKey": "sk_live_...", "keyName": "Production API Key", "type": "live" },
      { "apiKey": "sk_test_...", "keyName": "Test API Key", "type": "test" }
    ]
  }
}

Legacy admin deployments may use X-Admin-Token instead of a session token.

Provider Key Pools ​

After creating a tenant, open its admin detail view and use Manage Gemini Key Pool or Manage OpenAI Key Pool. Each pool accepts up to five keys and supports two update modes:

  • Append Keys preserves the encrypted pool and adds new, deduplicated keys.
  • Replace Pool removes existing keys that are not included in the submitted set.

Consecutive generation requests rotate their starting key. Authentication, quota, billing-limit, and provider rate-limit responses fail over to the next configured key.

Direct Database / Scripting ​

For bulk setup:

bash
npx tsx scripts/create-tenant.ts \
  --tenant-id new-tenant \
  --tenant-name "New Tenant" \
  --gemini-key "AIza..." \
  --email ops@example.com

The script prints the SQL (and wrangler d1 execute commands) to create the tenant with an encrypted Gemini key; it does not run them. It only takes a Gemini key. Add an OpenAI key and choose allowed models afterwards in the Admin console or with PATCH /admin/tenants/:id. The six model IDs are gemini-flash-lite-image, gemini-flash-image, gemini-pro-image, openai-gpt-image-2, openai-gpt-image-2.5-flare and openai-gpt-image-2.5-sunburst.

Managing API Keys ​

Create Additional Keys ​

http
POST /admin/tenants/:tenantId/api-keys
bash
curl -X POST "https://gobananasai.com/admin/tenants/acme-corp/api-keys" \
  -H "Authorization: Bearer <admin_session_token>" \
  -H "Content-Type: application/json" \
  -d '{ "key_name": "Mobile App", "type": "live" }'

List API Keys ​

http
GET /admin/tenants/:tenantId
json
{
  "data": {
    "apiKeys": [
      {
        "apiKey": "sk_live_a1b2...",
        "keyName": "Production API Key",
        "isActive": true,
        "createdAt": "2024-01-01T00:00:00.000Z",
        "lastUsedAt": "2024-01-15T10:30:00.000Z",
        "usageCount": 120
      }
    ]
  }
}

Revoke / Disable Keys ​

There is no REST endpoint to revoke a key. To disable one:

  1. Set is_active = 0 for the key in the api_keys table.
  2. Remove the key from the API_KEYS KV namespace if present.

Quotas and Rate Limits ​

Update tenant limits via:

http
PATCH /admin/tenants/:tenantId
bash
curl -X PATCH "https://gobananasai.com/admin/tenants/acme-corp" \
  -H "Authorization: Bearer <admin_session_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "monthly_quota_mb": 20480,
    "rate_limit_per_minute": 120
  }'

Quotas reset at the start of each month. To manually reset usage in D1:

sql
DELETE FROM usage_logs
WHERE tenant_id = 'acme-corp'
  AND timestamp >= date('now', 'start of month');

Tenant Lifecycle ​

Activate / Deactivate ​

bash
curl -X PATCH "https://gobananasai.com/admin/tenants/acme-corp" \
  -H "Authorization: Bearer <admin_session_token>" \
  -H "Content-Type: application/json" \
  -d '{ "is_active": false }'

Deleting Tenants ​

Tenant deletion is not exposed via REST. For self‑hosting, delete rows from:

  • tenants
  • api_keys
  • tenant‑scoped tables such as images, sessions, characters, product_references

Prefer scripting migrations for safe bulk cleanup.

Released under the MIT License.