CorpusIQ

Deployment Guide

This guide provides detailed instructions for deploying CorpusIQ Apps SDK to production.

Prerequisites

Before deploying, ensure you have:

  • [ ] A public domain with HTTPS support
  • [ ] An OAuth 2.1 / OIDC provider (Auth0, Okta, AWS Cognito, Google, etc.)
  • [ ] Python 3.10+ on the deployment server
  • [ ] Database (if needed for production features)
  • [ ] Monitoring and logging infrastructure

Deployment Options

Deploy to a managed platform for best reliability and scalability.

Heroku

# Install Heroku CLI
# See: https://devcenter.heroku.com/articles/heroku-cli

# Login
heroku login

# Create app
heroku create your-corpusiq-app

# Set environment variables
heroku config:set CORPUSIQ_CORS_ALLOW_ORIGINS_CSV=https://chat.openai.com
heroku config:set CORPUSIQ_DEBUG_MODE=false
heroku config:set CORPUSIQ_LOG_LEVEL=INFO

# Add Procfile
echo "web: python -m corpusiq" > Procfile

# Deploy
git push heroku main

# Check logs
heroku logs --tail

AWS Elastic Beanstalk

# Install EB CLI
pip install awsebcli

# Initialize
eb init -p python-3.10 corpusiq-app

# Create environment
eb create corpusiq-prod

# Set environment variables
eb setenv CORPUSIQ_CORS_ALLOW_ORIGINS_CSV=https://chat.openai.com
eb setenv CORPUSIQ_DEBUG_MODE=false

# Deploy
eb deploy

# Check status
eb status
eb logs

Google Cloud Run

# Create Dockerfile (see below)
# Build and push
gcloud builds submit --tag gcr.io/PROJECT_ID/corpusiq

# Deploy
gcloud run deploy corpusiq \
  --image gcr.io/PROJECT_ID/corpusiq \
  --platform managed \
  --region us-central1 \
  --allow-unauthenticated \
  --set-env-vars CORPUSIQ_CORS_ALLOW_ORIGINS_CSV=https://chat.openai.com,CORPUSIQ_DEBUG_MODE=false

# Get URL
gcloud run services describe corpusiq --platform managed --region us-central1

DigitalOcean App Platform

# Create app.yaml
# See: https://docs.digitalocean.com/products/app-platform/reference/app-spec/

# Deploy
doctl apps create --spec app.yaml

# Update environment variables via UI or CLI

Option 2: VPS/Dedicated Server

Deploy to your own server with full control.

Ubuntu/Debian Server Setup

# Update system
sudo apt update && sudo apt upgrade -y

# Install Python 3.10+
sudo apt install python3.10 python3.10-venv python3-pip -y

# Create user for the app
sudo useradd -m -s /bin/bash corpusiq
sudo su - corpusiq

# Clone repository
git clone https://github.com/CorpusIQ/corpusiq-openai-sdk.git
cd corpusiq-openai-sdk

# Create virtual environment
python3.10 -m venv .venv
source .venv/bin/activate

# Install dependencies
pip install -e .

# Configure environment
cp .env.example .env
nano .env  # Edit configuration

# Test run
python -m corpusiq

Systemd Service

Create /etc/systemd/system/corpusiq.service:

[Unit]
Description=CorpusIQ Apps SDK Server
After=network.target

[Service]
Type=simple
User=corpusiq
WorkingDirectory=/home/corpusiq/corpusiq-openai-sdk
Environment="PATH=/home/corpusiq/corpusiq-openai-sdk/.venv/bin"
ExecStart=/home/corpusiq/corpusiq-openai-sdk/.venv/bin/python -m corpusiq
Restart=always
RestartSec=10

# Security
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/home/corpusiq/corpusiq-openai-sdk

[Install]
WantedBy=multi-user.target

Enable and start:

sudo systemctl daemon-reload
sudo systemctl enable corpusiq
sudo systemctl start corpusiq
sudo systemctl status corpusiq

View logs:

sudo journalctl -u corpusiq -f

Nginx Reverse Proxy

Install Nginx:

sudo apt install nginx -y

Create /etc/nginx/sites-available/corpusiq:

server {
    listen 80;
    server_name your-domain.com;

    # Redirect HTTP to HTTPS
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name your-domain.com;

    # SSL configuration (use Let's Encrypt)
    ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    # Security headers
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "DENY" always;

    # Proxy to CorpusIQ
    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        
        # Timeouts for long-running requests
        proxy_read_timeout 300;
        proxy_connect_timeout 300;
        proxy_send_timeout 300;
    }
}

Enable and restart:

sudo ln -s /etc/nginx/sites-available/corpusiq /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx

Let’s Encrypt SSL Certificate

# Install certbot
sudo apt install certbot python3-certbot-nginx -y

# Obtain certificate
sudo certbot --nginx -d your-domain.com

# Auto-renewal is configured automatically
sudo certbot renew --dry-run

Option 3: Docker Deployment

Create Dockerfile

Create Dockerfile in the repository root:

FROM python:3.10-slim

# Set working directory
WORKDIR /app

# Install dependencies
COPY pyproject.toml ./
RUN pip install --no-cache-dir -e .

# Copy application code
COPY src/ ./src/
COPY assets/ ./assets/

# Create non-root user
RUN useradd -m -u 1000 corpusiq && \
    chown -R corpusiq:corpusiq /app
USER corpusiq

# Expose port
EXPOSE 8000

# Health check
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
  CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/')"

# Run application
CMD ["python", "-m", "corpusiq"]

Create docker-compose.yml

version: '3.8'

services:
  corpusiq:
    build: .
    ports:
      - "8000:8000"
    environment:
      - CORPUSIQ_CORS_ALLOW_ORIGINS_CSV=https://chat.openai.com
      - CORPUSIQ_DEBUG_MODE=false
      - CORPUSIQ_LOG_LEVEL=INFO
      - CORPUSIQ_RATE_LIMIT_REQUESTS_PER_MINUTE=60
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000/"]
      interval: 30s
      timeout: 5s
      retries: 3
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

Build and Run

# Build
docker build -t corpusiq:latest .

# Run
docker run -d \
  -p 8000:8000 \
  -e CORPUSIQ_CORS_ALLOW_ORIGINS_CSV=https://chat.openai.com \
  -e CORPUSIQ_DEBUG_MODE=false \
  --name corpusiq \
  --restart unless-stopped \
  corpusiq:latest

# Or use docker-compose
docker-compose up -d

# View logs
docker logs -f corpusiq

OAuth Configuration

Step 1: Choose OAuth Provider

Select an OAuth 2.1 / OIDC provider:

  • Auth0: Easy setup, generous free tier
  • Okta: Enterprise-grade, feature-rich
  • AWS Cognito: AWS integration, scalable
  • Google Identity: Google integration
  • Azure AD: Microsoft integration
  • Custom: Build your own (advanced)

Step 2: Configure Provider

Example with Auth0:

  1. Sign up at https://auth0.com/
  2. Create a new application (type: Regular Web Application)
  3. Configure settings:
    • Allowed Callback URLs: https://chat.openai.com/auth/callback
    • Allowed Logout URLs: https://chat.openai.com/
    • Allowed Web Origins: https://chat.openai.com
  4. Enable PKCE (should be default)
  5. Note your domain (e.g., your-tenant.auth0.com)

Step 3: Update CorpusIQ Configuration

Edit src/corpusiq/app.py and update the OAuth endpoints:

# In oauth_protected_resource() function
metadata = {
    "resource": "https://your-domain.com",  # Your actual domain
    "authorization_servers": ["https://your-tenant.auth0.com"],  # Your Auth0 domain
    "scopes_supported": [
        "corpus:read",
        "corpus:search",
        "connectors:read",
        "connectors:write",
    ],
    "resource_documentation": "https://your-domain.com/docs",
}

# In oauth_authorization_server() function
metadata = {
    "issuer": "https://your-tenant.auth0.com",
    "authorization_endpoint": "https://your-tenant.auth0.com/authorize",
    "token_endpoint": "https://your-tenant.auth0.com/oauth/token",
    "jwks_uri": "https://your-tenant.auth0.com/.well-known/jwks.json",
    # ... rest of config
}

Step 4: Implement Token Validation

Add token validation middleware to src/corpusiq/app.py:

import jwt
from jwt import PyJWKClient

class OAuthTokenMiddleware:
    def __init__(self, app: ASGIApp) -> None:
        self.app = app
        self.jwks_client = PyJWKClient("https://your-tenant.auth0.com/.well-known/jwks.json")
    
    async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
        if scope["type"] == "http" and scope["path"].startswith("/mcp"):
            headers = dict(scope.get("headers", []))
            auth_header = headers.get(b"authorization", b"").decode()
            
            if not auth_header.startswith("Bearer "):
                response = JSONResponse(
                    {"error": "Unauthorized"},
                    status_code=401,
                    headers={
                        "WWW-Authenticate": 'Bearer resource_metadata="/.well-known/oauth-protected-resource"'
                    }
                )
                await response(scope, receive, send)
                return
            
            token = auth_header[7:]  # Remove "Bearer " prefix
            
            try:
                # Get signing key
                signing_key = self.jwks_client.get_signing_key_from_jwt(token)
                
                # Verify token
                payload = jwt.decode(
                    token,
                    signing_key.key,
                    algorithms=["RS256"],
                    audience="https://your-domain.com",
                    issuer="https://your-tenant.auth0.com"
                )
                
                # Add user info to scope for later use
                scope["user"] = payload
                
            except Exception as e:
                logger.error(f"Token validation failed: {str(e)}")
                response = JSONResponse(
                    {"error": "Invalid token"},
                    status_code=401,
                    headers={
                        "WWW-Authenticate": 'Bearer error="invalid_token"'
                    }
                )
                await response(scope, receive, send)
                return
        
        await self.app(scope, receive, send)

# Add to middleware stack in build_app()
app.add_middleware(OAuthTokenMiddleware)

Install required package:

pip install PyJWT[crypto]

Environment Configuration

Production Environment Variables

Set these in your production environment:

# Required
CORPUSIQ_CORS_ALLOW_ORIGINS_CSV=https://chat.openai.com
CORPUSIQ_DEBUG_MODE=false

# Recommended
CORPUSIQ_LOG_LEVEL=INFO
CORPUSIQ_RATE_LIMIT_REQUESTS_PER_MINUTE=100

# Optional (based on your needs)
CORPUSIQ_MAX_REQUEST_SIZE=2097152  # 2MB

Secrets Management

Use your platform’s secrets management:

  • Heroku: heroku config:set SECRET_NAME=value
  • AWS: AWS Secrets Manager or Parameter Store
  • GCP: Secret Manager
  • Azure: Key Vault
  • Kubernetes: Secrets or Vault integration

Monitoring and Logging

Application Monitoring

Set up monitoring for:

  • Uptime: Use UptimeRobot, Pingdom, or CloudWatch
  • Performance: New Relic, Datadog, or Application Insights
  • Errors: Sentry, Rollbar, or CloudWatch Logs

Example Sentry integration:

pip install sentry-sdk

Add to src/corpusiq/app.py:

import sentry_sdk
from sentry_sdk.integrations.starlette import StarletteIntegration
from sentry_sdk.integrations.fastapi import FastApiIntegration

sentry_sdk.init(
    dsn="your-sentry-dsn",
    integrations=[
        StarletteIntegration(),
        FastApiIntegration(),
    ],
    traces_sample_rate=0.1,  # Adjust based on traffic
    environment="production",
)

Logging

Configure structured logging:

import logging.config

LOGGING_CONFIG = {
    "version": 1,
    "disable_existing_loggers": False,
    "formatters": {
        "json": {
            "class": "pythonjsonlogger.jsonlogger.JsonFormatter",
            "format": "%(asctime)s %(name)s %(levelname)s %(message)s"
        }
    },
    "handlers": {
        "console": {
            "class": "logging.StreamHandler",
            "formatter": "json",
            "stream": "ext://sys.stdout"
        }
    },
    "root": {
        "level": "INFO",
        "handlers": ["console"]
    }
}

logging.config.dictConfig(LOGGING_CONFIG)

Install:

pip install python-json-logger

Metrics

Track key metrics:

  • Request rate (requests/second)
  • Response time (p50, p95, p99)
  • Error rate (5xx responses)
  • Rate limit hits
  • OAuth failures

Scaling

Horizontal Scaling

For high traffic, run multiple instances:

# docker-compose.yml with scaling
version: '3.8'
services:
  corpusiq:
    build: .
    ports:
      - "8000-8003:8000"
    deploy:
      replicas: 4

Use a load balancer:

# Nginx load balancing
upstream corpusiq_backend {
    least_conn;
    server 127.0.0.1:8000;
    server 127.0.0.1:8001;
    server 127.0.0.1:8002;
    server 127.0.0.1:8003;
}

server {
    listen 443 ssl http2;
    server_name your-domain.com;
    
    location / {
        proxy_pass http://corpusiq_backend;
        # ... proxy settings ...
    }
}

Rate Limiting with Redis

For multi-instance deployments, use Redis for rate limiting:

import redis
from datetime import datetime, timedelta

class RedisRateLimitMiddleware:
    def __init__(self, app: ASGIApp, redis_url: str, requests_per_minute: int = 60):
        self.app = app
        self.redis = redis.from_url(redis_url)
        self.requests_per_minute = requests_per_minute
    
    async def __call__(self, scope: Scope, receive: Receive, send: Send):
        if scope["type"] == "http":
            client_ip = self._get_client_ip(scope)
            key = f"rate_limit:{client_ip}"
            
            # Increment counter
            count = self.redis.incr(key)
            
            # Set expiry on first request
            if count == 1:
                self.redis.expire(key, 60)
            
            # Check limit
            if count > self.requests_per_minute:
                response = JSONResponse(
                    {"error": "Rate limit exceeded"},
                    status_code=429,
                    headers={"Retry-After": "60"}
                )
                await response(scope, receive, send)
                return
        
        await self.app(scope, receive, send)

Backup and Recovery

Database Backups

If using a database:

# PostgreSQL
pg_dump -U user -d corpusiq > backup_$(date +%Y%m%d_%H%M%S).sql

# Automated backups (cron)
0 2 * * * /usr/bin/pg_dump -U user -d corpusiq | gzip > /backups/corpusiq_$(date +\%Y\%m\%d).sql.gz

Application State

If maintaining any state:

  • Use object storage (S3, GCS, Azure Blob)
  • Regular snapshots
  • Version control for configuration

Testing Production Deployment

Before going live:

# 1. Test health endpoint
curl -v https://your-domain.com/

# 2. Test OAuth metadata
curl https://your-domain.com/.well-known/oauth-protected-resource

# 3. Test MCP endpoint (with valid token)
curl -X POST https://your-domain.com/mcp \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}'

# 4. Load testing
# Install artillery
npm install -g artillery

# Create test.yml
artillery quick --count 100 --num 10 https://your-domain.com/

# 5. Security scan
# Use tools like OWASP ZAP or nmap

Rollback Plan

Always have a rollback plan:

  1. Keep previous version available:

    # Tag releases
    git tag v1.0.0
    git push origin v1.0.0
    
  2. Blue-Green Deployment:

    • Keep old version running
    • Deploy new version to separate environment
    • Switch traffic after validation
    • Can quickly revert if issues
  3. Database migrations:

    • Make migrations backward compatible
    • Test rollback procedure

Post-Deployment Checklist

  • [ ] All endpoints accessible via HTTPS
  • [ ] OAuth endpoints returning correct metadata
  • [ ] CORS configured for ChatGPT
  • [ ] SSL certificate valid
  • [ ] Monitoring and alerting active
  • [ ] Logs aggregated and searchable
  • [ ] Backup strategy in place
  • [ ] Rate limiting working
  • [ ] Security headers present
  • [ ] Documentation updated with production URLs
  • [ ] Team notified of deployment
  • [ ] Tested from ChatGPT Developer Mode

Troubleshooting

Issue: 502 Bad Gateway

Cause: Server not responding

Solution:

# Check if server is running
ps aux | grep corpusiq

# Check logs
sudo journalctl -u corpusiq -n 50

# Restart service
sudo systemctl restart corpusiq

Issue: SSL Certificate Errors

Cause: Certificate expired or misconfigured

Solution:

# Check certificate
sudo certbot certificates

# Renew
sudo certbot renew

# Test SSL
curl -vI https://your-domain.com/

Issue: High Memory Usage

Cause: Memory leak or insufficient resources

Solution:

# Monitor memory
watch -n 1 free -m

# Check process
ps aux --sort=-%mem | head

# Restart if needed
sudo systemctl restart corpusiq

# Consider upgrading server resources

Support

For deployment assistance:

  • Review OpenAI Apps SDK documentation
  • Check server logs for errors
  • Contact your hosting provider support
  • Create an issue in the repository

Resources