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

ControlTitleRelevance
CM-2Baseline ConfigurationDocumented infrastructure and software requirements
CM-6Configuration SettingsSecure configuration of environment variables and services
SA-10Developer Configuration ManagementVersion-controlled deployment artifacts and configurations

Prerequisites

Software Requirements

ComponentMinimum VersionNotes
Node.js18.x LTS20.x recommended for production
Python3.11+3.12 recommended; 3.10 minimum
PostgreSQL15.x16.x recommended; must support SSL connections
Docker24.xRequired for containerized deployment
Docker Compose2.xFor multi-container orchestration
Git2.xFor source code management

Hardware Requirements

EnvironmentCPURAMStorage
Development4 cores8 GB50 GB SSD
Staging4 cores16 GB100 GB SSD
Production8+ cores32 GB500 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=true

Frontend 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:

PlatformSecrets ManagerFedRAMP Status
AWSAWS Secrets Manager / Parameter StoreFedRAMP High
AzureAzure Key VaultFedRAMP High
GCPSecret ManagerFedRAMP High
On-PremisesHashiCorp VaultAgency 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                peer

Database 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 -1

Docker 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: bridge

Production Deployment Checklist

Complete all items before submitting for ATO (Authority to Operate) or deployment to production:

Pre-Deployment (CM-2, CM-6)

ItemStatusNotes
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)

ItemStatusNotes
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)

ItemStatusNotes
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:

HeaderValuePurpose
X-Frame-OptionsDENYPrevent clickjacking attacks
X-Content-Type-OptionsnosniffPrevent MIME sniffing
X-XSS-Protection1; mode=blockXSS filter (legacy browsers)
Referrer-Policystrict-origin-when-cross-originControl referrer information
Content-Security-Policydefault-src 'self'; ...Control resource loading
Permissions-Policygeolocation=(), microphone=(), camera=()Disable unnecessary browser features
Cache-Controlno-store, no-cache, must-revalidatePrevent caching of sensitive data
Strict-Transport-Securitymax-age=31536000; includeSubDomainsForce 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 CategoryLimitWindow
Default API100 requestsPer minute
LLM/Extraction20 requestsPer minute
Authentication10 requestsPer minute
File Upload30 requestsPer 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: 10

Troubleshooting

Common Issues

IssueCauseResolution
503 Service UnavailableDatabase connection failureVerify DATABASE_URL, check SSL mode, confirm network connectivity
401 Unauthorized with valid tokenJWT_SECRET mismatchEnsure same secret across all API instances
429 Too Many RequestsRate limit exceededWait for window reset or adjust limits in middleware
LLM extraction timeoutProvider unreachable or slowCheck /health/llm endpoint, verify API key and quota
CORS errors in browserOrigin not allowedAdd frontend URL to CORSMiddleware allow_origins
Migration conflictsOut of sync schemaRun 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 ratelimit

Log 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.log

Related Documentation