Skip to main content
Mission Control implements multiple layers of security to protect sensitive agent orchestration data, API keys, and user credentials. This guide covers authentication methods, role-based access control (RBAC), network security, and deployment best practices.

Authentication

Mission Control supports multiple authentication methods:

Session-Based Authentication

The primary authentication method uses secure HTTP-only cookies with cryptographically random session tokens. Login flow:
  1. User submits credentials to /api/auth/login
  2. Server validates credentials using constant-time comparison (timing attack prevention)
  3. On success, creates session token (32-byte random hex) with 7-day expiration
  4. Returns mc-session cookie with httpOnly, secure (in production), and sameSite=strict flags
  5. Subsequent requests include session cookie for authentication
Session security features (src/lib/auth.ts:104-121):
const SESSION_DURATION = 7 * 24 * 60 * 60 // 7 days
const token = randomBytes(32).toString('hex')
  • Cryptographic randomness: Uses Node.js crypto.randomBytes(32) for unpredictable tokens
  • Automatic expiration: Sessions expire after 7 days
  • Cleanup: Expired sessions are purged on new session creation
  • IP & User-Agent tracking: Sessions log IP address and user agent for audit trails

API Key Authentication

For headless/programmatic access, use API key authentication:
curl -H "x-api-key: your-api-key" https://mc.example.com/api/...
Security implementation (src/proxy.ts:100-106):
const apiKey = request.headers.get('x-api-key')
if (apiKey && safeCompare(apiKey, process.env.API_KEY || '')) {
  return applySecurityHeaders(NextResponse.next())
}
  • Constant-time comparison: Uses crypto.timingSafeEqual() to prevent timing attacks
  • Admin privileges: API key grants full admin access (synthetic user with role: 'admin')
  • Single key: One API key per instance (rotate regularly)
Generate a strong API key with high entropy:
openssl rand -hex 32
Never commit API_KEY to version control.

Google OAuth (Optional)

Mission Control supports Google Sign-In with approval workflow:
  1. User clicks “Sign in with Google”
  2. Google OAuth flow validates user identity
  3. Admin approval required: New Google users are created with is_approved=0
  4. Admin approves user via UI or API
  5. User can then access the dashboard
Configuration:
GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
NEXT_PUBLIC_GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
Create OAuth credentials in Google Cloud Console and configure authorized origins/redirect URIs.

Role-Based Access Control (RBAC)

Mission Control implements a three-tier role hierarchy:
RoleLevelPermissions
viewer0Read-only access to dashboards, logs, and metrics
operator1Viewer permissions + trigger pipelines, manage agents, modify configurations
admin2Operator permissions + user management, system settings, API key access
Role enforcement (src/lib/auth.ts:300-319):
const ROLE_LEVELS: Record<string, number> = { viewer: 0, operator: 1, admin: 2 }

export function requireRole(
  request: Request,
  minRole: User['role']
): { user: User } | { error: string; status: 401 | 403 } {
  const user = getUserFromRequest(request)
  if (!user) {
    return { error: 'Authentication required', status: 401 }
  }
  if ((ROLE_LEVELS[user.role] ?? -1) < ROLE_LEVELS[minRole]) {
    return { error: `Requires ${minRole} role or higher`, status: 403 }
  }
  return { user }
}
API routes use requireRole() to enforce minimum role requirements:
// Example: /api/users (admin only)
const auth = requireRole(request, 'admin')
if ('error' in auth) {
  return NextResponse.json({ error: auth.error }, { status: auth.status })
}
const user = auth.user

Managing Users

Admins can manage users via the UI or API:
curl -X POST https://mc.example.com/api/users \
  -H "x-api-key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "username": "operator1",
    "password": "strong-password-min-12-chars",
    "display_name": "Operator One",
    "role": "operator"
  }'
Password minimum length: 12 characters (enforced in src/lib/auth.ts:208)

Network Access Control

Mission Control implements default-deny host-based access control in production environments.

How It Works (src/proxy.ts:56-70)

const hostName = getRequestHostname(request) // From x-forwarded-host or host header
const allowAnyHost = envFlag('MC_ALLOW_ANY_HOST') || process.env.NODE_ENV !== 'production'
const allowedPatterns = String(process.env.MC_ALLOWED_HOSTS || '')
  .split(',')
  .map((s) => s.trim())
  .filter(Boolean)

const isAllowedHost = allowAnyHost || allowedPatterns.some((p) => hostMatches(p, hostName))

if (!isAllowedHost) {
  return new NextResponse('Forbidden', { status: 403 })
}
Behavior:
  • Development (NODE_ENV !== 'production'): All hosts allowed by default
  • Production (NODE_ENV === 'production'): Only hosts matching MC_ALLOWED_HOSTS patterns are allowed
  • Override: Set MC_ALLOW_ANY_HOST=true to disable (not recommended in production)

Allowed Host Patterns

Supports flexible pattern matching (src/proxy.ts:28-46):
PatternExampleMatchesDoes NOT Match
Exact hostapp.example.comapp.example.comapi.example.com, app.example.com:8080
Subdomain wildcard*.example.comapi.example.com, app.example.comexample.com (bare domain)
Prefix wildcard100.*100.64.0.1, 100.100.100.10010.0.0.1
Example configuration:
MC_ALLOWED_HOSTS=localhost,127.0.0.1,mc.example.com,*.internal.example.com,100.*
This allows:
  • localhost and 127.0.0.1 (local development/testing)
  • mc.example.com (production domain)
  • api.internal.example.com, dashboard.internal.example.com (internal subdomains)
  • 100.64.0.1, 100.100.100.100 (Tailscale IP range)
Always configure MC_ALLOWED_HOSTS in production to prevent unauthorized access from spoofed Host headers.Never set MC_ALLOW_ANY_HOST=true in production unless absolutely necessary.

CSRF Protection

Mission Control validates the Origin header for state-changing requests (src/proxy.ts:74-88):
const method = request.method.toUpperCase()
if (['POST', 'PUT', 'DELETE', 'PATCH'].includes(method)) {
  const origin = request.headers.get('origin')
  if (origin) {
    const originHost = new URL(origin).host
    const requestHost = request.headers.get('host')?.split(',')[0]?.trim()
    if (originHost && requestHost && originHost !== requestHost) {
      return NextResponse.json({ error: 'CSRF origin mismatch' }, { status: 403 })
    }
  }
}
Protection:
  • Validates Origin header matches Host header for POST/PUT/DELETE/PATCH requests
  • Prevents cross-site request forgery attacks
  • Works automatically with modern browsers

Security Headers

All responses include security headers (src/proxy.ts:48-53):
function applySecurityHeaders(response: NextResponse): NextResponse {
  response.headers.set('X-Content-Type-Options', 'nosniff')
  response.headers.set('X-Frame-Options', 'DENY')
  response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin')
  return response
}
HeaderValuePurpose
X-Content-Type-OptionsnosniffPrevent MIME type sniffing attacks
X-Frame-OptionsDENYPrevent clickjacking (no iframe embedding)
Referrer-Policystrict-origin-when-cross-originLimit referrer information leakage
Recommended reverse proxy headers (add via Nginx/Caddy):
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';" always;

TLS / HTTPS

Always deploy Mission Control behind a reverse proxy with TLS in production.Never expose the Node.js server directly to the internet.

Enable Secure Cookies

When serving over HTTPS, enable secure cookies:
MC_COOKIE_SECURE=true
This sets the Secure flag on session cookies, preventing transmission over unencrypted HTTP. Default behavior:
  • Production (NODE_ENV=production): secure=true unless explicitly set to false
  • Development: secure=false (allows testing over HTTP)

Reverse Proxy TLS Termination

See Production Deployment - Reverse Proxy Configuration for Nginx/Caddy/Traefik examples with:
  • TLS 1.2/1.3 only
  • Strong cipher suites
  • HSTS (Strict-Transport-Security)
  • HTTP to HTTPS redirect

Password Security

Mission Control uses industry-standard password hashing:
  • Algorithm: Argon2id (via @node-rs/argon2 or fallback to bcrypt)
  • Verification: Constant-time comparison to prevent timing attacks
  • Minimum length: 12 characters (enforced at user creation)
Password with special characters: If AUTH_PASS contains #, use one of these methods:
# Method 1: Quote the value
AUTH_PASS="my#password"

# Method 2: Base64 encoding
AUTH_PASS_B64=$(echo -n 'my#password' | base64)

Secrets Management

Never commit .env files to version control.The repository .gitignore already excludes these files:
  • .env
  • .env.local
  • .env.production

Best Practices

  1. Use environment-specific files:
    • Development: .env.local
    • Production: .env or secret management service
  2. Rotate credentials regularly:
    # Generate new API key
    openssl rand -hex 32
    
    # Update AUTH_PASS for admin
    openssl rand -base64 32
    
  3. Use secret management services:
    • Docker Swarm: Docker Secrets
    • Kubernetes: Sealed Secrets or External Secrets Operator
    • Cloud: AWS Secrets Manager, Azure Key Vault, Google Secret Manager
  4. Restrict file permissions:
    chmod 600 /opt/mission-control/.env
    chown mission-control:mission-control /opt/mission-control/.env
    

1Password Integration

Mission Control supports pulling secrets from 1Password CLI:
OP_VAULT_NAME=production
The Integrations panel can fetch secrets from the specified vault.

Gateway Security

Device Identity & WebCrypto

Mission Control uses WebCrypto for device identity signing when connecting to gateways:
Device identity requires a secure context (HTTPS or localhost).If you see “Gateway error: device identity required”, ensure Mission Control is served over HTTPS.

Gateway Origin Allowlist

Configure your OpenClaw gateway to allow Mission Control origins:
openclaw.json
{
  "gateway": {
    "controlUi": {
      "allowedOrigins": ["https://mc.example.com"]
    }
  }
}
Restart the gateway after updating the configuration.

Audit Logging

Mission Control logs security-relevant events:
  • User authentication: Login attempts, session creation/destruction
  • User management: User creation, role changes, deletions
  • API access: API key usage with IP address and user agent
  • Configuration changes: System settings modifications
Retention:
MC_RETAIN_AUDIT_DAYS=365  # Keep audit logs for 1 year
See Environment Variables - Data Retention.

Security Checklist

1

Authentication

  • Set strong AUTH_PASS (min 12 characters, high entropy)
  • Generate secure API_KEY with openssl rand -hex 32
  • Rotate credentials on initial deployment
  • Enable Google OAuth approval workflow if using SSO
2

Network Security

  • Configure MC_ALLOWED_HOSTS to restrict access
  • Deploy behind reverse proxy (Nginx/Caddy)
  • Enable TLS with valid certificates (Let’s Encrypt)
  • Set MC_COOKIE_SECURE=true for HTTPS
  • Configure firewall to allow only necessary ports
3

Access Control

  • Assign users appropriate roles (viewer/operator/admin)
  • Regularly review user list and remove inactive accounts
  • Use API key only for automation (not humans)
  • Enable 2FA for admin accounts (if using Google OAuth)
4

Data Protection

  • Restrict .env file permissions (chmod 600)
  • Keep .env out of version control
  • Use secret management service in production
  • Configure audit log retention (MC_RETAIN_AUDIT_DAYS=365)
5

Monitoring

  • Monitor authentication failures
  • Alert on unauthorized access attempts (403 errors)
  • Review audit logs regularly
  • Track API key usage patterns

Reporting Vulnerabilities

If you discover a security vulnerability in Mission Control:
Do not open a public issue.Email [email protected] with:
  • Description of the vulnerability
  • Steps to reproduce
  • Potential impact
  • Suggested fix (if any)
We will acknowledge receipt within 48 hours and aim to provide a fix within 7 days for critical issues.

Additional Resources