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
Option 1: Cloud Platform (Recommended)
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:
- Sign up at https://auth0.com/
- Create a new application (type: Regular Web Application)
- 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
- Allowed Callback URLs:
- Enable PKCE (should be default)
- 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:
-
Keep previous version available:
# Tag releases git tag v1.0.0 git push origin v1.0.0 -
Blue-Green Deployment:
- Keep old version running
- Deploy new version to separate environment
- Switch traffic after validation
- Can quickly revert if issues
-
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