Installation & Deployment
System requirements, configuration, and production deployment procedures
Audience
This document is intended for Information System Security Officers (ISSOs), DevOps engineers, and System Administrators responsible for deploying and maintaining RayRay in authorized computing environments. All deployment activities must comply with your organization's Change Control Board (CCB) procedures and Configuration Management Plan.
NIST Control Alignment
| Control | Title | Relevance |
|---|---|---|
CM-2 | Baseline Configuration | Documented infrastructure and software requirements |
CM-6 | Configuration Settings | Secure configuration of environment variables and services |
SA-10 | Developer Configuration Management | Version-controlled deployment artifacts and configurations |
Prerequisites
Software Requirements
| Component | Minimum Version | Notes |
|---|---|---|
| Node.js | 18.x LTS | 20.x recommended for production |
| Python | 3.11+ | 3.12 recommended; 3.10 minimum |
| PostgreSQL | 15.x | 16.x recommended; must support SSL connections |
| Docker | 24.x | Required for containerized deployment |
| Docker Compose | 2.x | For multi-container orchestration |
| Git | 2.x | For source code management |
Hardware Requirements
| Environment | CPU | RAM | Storage |
|---|---|---|---|
| Development | 4 cores | 8 GB | 50 GB SSD |
| Staging | 4 cores | 16 GB | 100 GB SSD |
| Production | 8+ cores | 32 GB | 500 GB SSD (with encryption) |
Network Requirements
- Inbound: TCP 3000 (frontend), TCP 8000 (API) - restrict to authorized networks
- Outbound: TCP 443 (HTTPS) for LLM API calls, database connections
- Internal: TCP 5432 (PostgreSQL) - database traffic must be encrypted
Environment Variables
Backend API (apps/api/.env)
# ============================================
# DATABASE CONFIGURATION (CM-6)
# ============================================
# Production: Use managed PostgreSQL with SSL enforced
DATABASE_URL=postgresql://user:password@host:5432/rayray?sslmode=require
# ============================================
# LLM PROVIDER CONFIGURATION
# ============================================
# Options: openai, anthropic, openai_compatible
LLM_PROVIDER=openai
LLM_MODEL=gpt-4o-mini
# Provider-specific API keys (store in secrets manager)
OPENAI_API_KEY=sk-...
# ANTHROPIC_API_KEY=sk-ant-...
# For self-hosted LLMs (FedRAMP environments)
# LLM_PROVIDER=openai_compatible
# LLM_BASE_URL=https://llm-internal.your-agency.gov/v1
# ============================================
# APPLICATION SETTINGS
# ============================================
ENVIRONMENT=production
DEBUG=false
UPLOAD_DIR=/var/lib/rayray/uploads
# ============================================
# SECURITY SETTINGS (CM-6, IA-5)
# ============================================
# JWT secret - MUST be changed in production
# Generate with: openssl rand -hex 32
JWT_SECRET=your-256-bit-secret-here
JWT_ALGORITHM=HS256
JWT_EXPIRY_HOURS=24
# Rate limiting (NIST SC-5)
RATE_LIMIT_ENABLED=trueFrontend Web (apps/web/.env.local)
# API endpoint (internal or public depending on deployment)
NEXT_PUBLIC_API_URL=https://api.your-agency.gov
# Optional: Analytics (ensure FedRAMP authorization)
# NEXT_PUBLIC_ANALYTICS_ID=...Security Notice: Secrets Management
WARNING: Never commit environment variables containing secrets to version control. In production environments, use an approved secrets management solution:
| Platform | Secrets Manager | FedRAMP Status |
|---|---|---|
| AWS | AWS Secrets Manager / Parameter Store | FedRAMP High |
| Azure | Azure Key Vault | FedRAMP High |
| GCP | Secret Manager | FedRAMP High |
| On-Premises | HashiCorp Vault | Agency ATO required |
Database Setup
PostgreSQL Configuration
The database must be configured in accordance with your organization's security baseline. The following settings are recommended for production deployments:
# postgresql.conf (key security settings)
ssl = on # Force SSL (NIST SC-8)
ssl_cert_file = '/etc/ssl/certs/server.crt'
ssl_key_file = '/etc/ssl/private/server.key'
log_connections = on # Audit logging (NIST AU-2)
log_disconnections = on
log_statement = 'ddl' # Log schema changes
# Connection limits (NIST SC-5)
max_connections = 100
# Password encryption
password_encryption = scram-sha-256# pg_hba.conf (host-based access control)
# TYPE DATABASE USER ADDRESS METHOD
hostssl all all 10.0.0.0/8 scram-sha-256
local all all peerDatabase Initialization
# Create database and user
sudo -u postgres psql
CREATE USER rayray_app WITH PASSWORD 'secure-password-here';
CREATE DATABASE rayray OWNER rayray_app;
GRANT ALL PRIVILEGES ON DATABASE rayray TO rayray_app;
# Enable required extensions
\c rayray
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS "pgcrypto";Schema Migrations
RayRay uses SQLAlchemy's async migration system. Run migrations on each deployment:
cd apps/api
# Apply migrations (production)
alembic upgrade head
# Verify migration status
alembic current
# Rollback if needed (requires CCB approval)
alembic downgrade -1Docker Deployment
Production Dockerfile (API)
# apps/api/Dockerfile
FROM python:3.11-slim
# Security: Run as non-root user
RUN useradd --create-home --shell /bin/bash rayray
USER rayray
WORKDIR /app
# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application
COPY --chown=rayray:rayray . .
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
# Run with production server
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]Production Dockerfile (Web)
# apps/web/Dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
FROM node:20-alpine AS runner
WORKDIR /app
# Security: Run as non-root
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT=3000
CMD ["node", "server.js"]Docker Compose (Production)
# docker-compose.prod.yml
version: "3.9"
services:
api:
image: rayray-api:latest
restart: always
ports:
- "8000:8000"
environment:
- DATABASE_URL=${DATABASE_URL}
- LLM_PROVIDER=${LLM_PROVIDER}
- LLM_MODEL=${LLM_MODEL}
- OPENAI_API_KEY=${OPENAI_API_KEY}
- ENVIRONMENT=production
- DEBUG=false
volumes:
- uploads:/var/lib/rayray/uploads
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
depends_on:
db:
condition: service_healthy
networks:
- rayray-internal
web:
image: rayray-web:latest
restart: always
ports:
- "3000:3000"
environment:
- NEXT_PUBLIC_API_URL=https://api.your-agency.gov
depends_on:
- api
networks:
- rayray-internal
db:
image: postgres:16-alpine
restart: always
environment:
- POSTGRES_DB=rayray
- POSTGRES_USER=${DB_USER}
- POSTGRES_PASSWORD=${DB_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d rayray"]
interval: 10s
timeout: 5s
retries: 5
networks:
- rayray-internal
volumes:
uploads:
postgres_data:
networks:
rayray-internal:
driver: bridgeProduction Deployment Checklist
Complete all items before submitting for ATO (Authority to Operate) or deployment to production:
Pre-Deployment (CM-2, CM-6)
| Item | Status | Notes |
|---|---|---|
| Database SSL enabled | [ ] | Verify sslmode=require in connection string |
| Secrets stored in approved vault | [ ] | No secrets in environment files or code |
| JWT_SECRET changed from default | [ ] | Generate with openssl rand -hex 32 |
| DEBUG=false set in production | [ ] | Prevents verbose error messages |
| Rate limiting enabled | [ ] | RATE_LIMIT_ENABLED=true |
| CORS origins restricted | [ ] | Only authorized domains in allow_origins |
| HSTS header enabled | [ ] | Uncomment in security middleware for HTTPS |
| Containers run as non-root | [ ] | Verify USER directive in Dockerfiles |
| Volume encryption enabled | [ ] | Database and upload volumes encrypted at rest |
Network Security (SC-7, SC-8)
| Item | Status | Notes |
|---|---|---|
| TLS 1.2+ enforced | [ ] | Configure in load balancer/reverse proxy |
| Database not exposed to internet | [ ] | Internal network only |
| API behind WAF | [ ] | AWS WAF, Azure WAF, or equivalent |
| Internal traffic encrypted | [ ] | Service mesh or TLS between services |
Monitoring & Logging (AU-2, AU-6)
| Item | Status | Notes |
|---|---|---|
| Health checks configured | [ ] | /health endpoint monitored |
| Audit log shipping enabled | [ ] | SIEM integration (Splunk, Sentinel, etc.) |
| Alerting configured | [ ] | Health failures, rate limit breaches |
| Log retention policy applied | [ ] | Minimum 7 years for audit logs |
Security Hardening
HTTP Security Headers
RayRay automatically applies the following OWASP-recommended security headers via the SecurityHeadersMiddleware. Verify these are present in production:
| Header | Value | Purpose |
|---|---|---|
| X-Frame-Options | DENY | Prevent clickjacking attacks |
| X-Content-Type-Options | nosniff | Prevent MIME sniffing |
| X-XSS-Protection | 1; mode=block | XSS filter (legacy browsers) |
| Referrer-Policy | strict-origin-when-cross-origin | Control referrer information |
| Content-Security-Policy | default-src 'self'; ... | Control resource loading |
| Permissions-Policy | geolocation=(), microphone=(), camera=() | Disable unnecessary browser features |
| Cache-Control | no-store, no-cache, must-revalidate | Prevent caching of sensitive data |
| Strict-Transport-Security | max-age=31536000; includeSubDomains | Force HTTPS (enable in production) |
CORS Configuration
Restrict CORS to authorized origins only. Production configuration:
# In apps/api/main.py
app.add_middleware(
CORSMiddleware,
allow_origins=[
"https://rayray.your-agency.gov", # Production frontend
],
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
allow_headers=["Authorization", "Content-Type"],
)Rate Limiting (NIST SC-5)
RayRay implements tiered rate limiting to prevent abuse and DoS attacks:
| Endpoint Category | Limit | Window |
|---|---|---|
| Default API | 100 requests | Per minute |
| LLM/Extraction | 20 requests | Per minute |
| Authentication | 10 requests | Per minute |
| File Upload | 30 requests | Per minute |
Note: The default implementation uses in-memory rate limiting. For multi-instance deployments, configure Redis-backed rate limiting.
Health Check Endpoints
RayRay provides multiple health check endpoints for monitoring systems, load balancers, and container orchestrators:
Basic Health Check
GET /health
Response:
{
"status": "healthy",
"version": "0.1.0"
}LLM Provider Health
GET /health/llm
Response:
{
"provider": "openai",
"model": "gpt-4o-mini",
"base_url": null,
"status": "healthy",
"latency_ms": 245,
"response_model": "gpt-4o-mini-2024-07-18"
}Load Balancer Configuration
# AWS ALB Target Group Health Check
Health check path: /health
Health check interval: 30 seconds
Healthy threshold: 2
Unhealthy threshold: 3
# Kubernetes liveness/readiness probes
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 10
periodSeconds: 30
readinessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 5
periodSeconds: 10Troubleshooting
Common Issues
| Issue | Cause | Resolution |
|---|---|---|
503 Service Unavailable | Database connection failure | Verify DATABASE_URL, check SSL mode, confirm network connectivity |
401 Unauthorized with valid token | JWT_SECRET mismatch | Ensure same secret across all API instances |
429 Too Many Requests | Rate limit exceeded | Wait for window reset or adjust limits in middleware |
| LLM extraction timeout | Provider unreachable or slow | Check /health/llm endpoint, verify API key and quota |
| CORS errors in browser | Origin not allowed | Add frontend URL to CORSMiddleware allow_origins |
| Migration conflicts | Out of sync schema | Run alembic history, resolve conflicts, re-run upgrade |
Diagnostic Commands
# Check API health
curl -s http://localhost:8000/health | jq
# Check LLM connectivity
curl -s http://localhost:8000/health/llm | jq
# View recent logs
docker logs rayray-api --tail 100
# Check database connectivity
psql $DATABASE_URL -c "SELECT version();"
# Verify SSL connection
psql $DATABASE_URL -c "SELECT ssl_is_used();"
# Test authenticated endpoint
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:8000/api/users/me
# Check rate limit headers
curl -I http://localhost:8000/api/documents/list \
-H "Authorization: Bearer $TOKEN" | grep -i ratelimitLog Analysis
Key log patterns to monitor for security events:
# Failed authentication attempts (AU-2)
grep "authentication failed" /var/log/rayray/api.log
# Rate limit violations (SC-5)
grep "Rate limit exceeded" /var/log/rayray/api.log
# Database connection issues (CP-9)
grep "connection refused\|connection timeout" /var/log/rayray/api.log
# Audit log integrity failures (AU-9)
grep "hash verification failed" /var/log/rayray/api.logRelated Documentation
- Quick Start - Local development setup
- Architecture - System design and infrastructure
- Authentication - OAuth 2.0 + MFA configuration
- Compliance - NIST control mappings and audit procedures
- API Reference - REST endpoints and error codes