platform-tooling/scripts/orchestration/SSL_QUICKREF.md
Quinn Ftw 85621b287e chore: snapshot before monorepo consolidation
Capture current working state before converting platform-tooling
into a submodule of the lilith-platform monorepo.
2026-01-29 07:04:39 -08:00

4.1 KiB

SSL Manager Quick Reference

Commands

# Check certificate status
sudo pnpm tsx infrastructure/scripts/orchestration/ssl-manager.ts check

# Request new certificate
sudo pnpm tsx infrastructure/scripts/orchestration/ssl-manager.ts request <domain>

# Renew expiring certificates
sudo pnpm tsx infrastructure/scripts/orchestration/ssl-manager.ts renew

# Validate all certificates
sudo pnpm tsx infrastructure/scripts/orchestration/ssl-manager.ts validate

Initial Setup

# 1. Install certbot
sudo apt install certbot  # Debian/Ubuntu
sudo dnf install certbot  # Fedora

# 2. Create webroot
sudo mkdir -p /var/www/certbot
sudo chown -R lilith:lilith /var/www/certbot

# 3. Configure nginx for ACME challenge (add to port 80 server block)
location /.well-known/acme-challenge/ {
    root /var/www/certbot;
    try_files $uri =404;
}

# 4. Request certificates for all domains
for domain in atlilith.com sso.atlilith.com admin.atlilith.com trustedmeet.com seo.atlilith.com analytics.atlilith.com profile.atlilith.com status.atlilith.com; do
  sudo pnpm tsx infrastructure/scripts/orchestration/ssl-manager.ts request $domain
done

# 5. Setup auto-renewal (cron)
sudo crontab -e
# Add:
0 3 * * * cd /var/www/lilith && pnpm tsx infrastructure/scripts/orchestration/ssl-manager.ts renew
30 3 * * * systemctl reload nginx

API Functions

import {
  checkCertificates,
  requestCertificate,
  renewCertificates,
  getCertificatePath,
  validateCertificates,
} from './ssl-manager.js';

// Get paths for nginx config
const paths = getCertificatePath('atlilith.com');
// paths.fullchainPath = /etc/letsencrypt/live/atlilith.com/fullchain.pem
// paths.keyPath = /etc/letsencrypt/live/atlilith.com/privkey.pem

// Pre-deployment validation
const validation = await validateCertificates();
if (!validation.valid) {
  throw new Error(`Certificate validation failed: ${validation.errors.join(', ')}`);
}

// Check expiration
const statuses = await checkCertificates();
for (const status of statuses) {
  if (status.daysUntilExpiry && status.daysUntilExpiry <= 7) {
    console.warn(`Certificate ${status.domain} expires in ${status.daysUntilExpiry} days`);
  }
}

Nginx Configuration

server {
    listen 443 ssl http2;
    server_name atlilith.com www.atlilith.com;

    # SSL certificates
    ssl_certificate /etc/letsencrypt/live/atlilith.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/atlilith.com/privkey.pem;
    include snippets/ssl-params.conf;

    # ...
}

server {
    listen 80;
    server_name atlilith.com www.atlilith.com;

    # ACME challenge
    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
        try_files $uri =404;
    }

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

Managed Domains

Domain Aliases Port
atlilith.com www.atlilith.com 3010
sso.atlilith.com - 4001
admin.atlilith.com - 3011
trustedmeet.com www.trustedmeet.com 3001
seo.atlilith.com - 3014
analytics.atlilith.com - 3012
profile.atlilith.com - 3110
status.atlilith.com - 5000

Troubleshooting

Issue Solution
certbot not found sudo apt install certbot
Permission denied Run with sudo
Port 80 not accessible Check firewall: sudo ufw allow 80/tcp
DNS not configured Verify: dig +short <domain> returns VPS IP
Certificate invalid Check expiration: sudo openssl x509 -in /etc/letsencrypt/live/<domain>/cert.pem -noout -dates

Security Features

  • Domain validation: RFC 1035 regex (prevents injection)
  • No shell injection: Uses spawnSync with arrays (never exec)
  • Root requirement: Checks process.getuid() === 0
  • Minimal environment: Sanitized env vars

Certificate Paths

/etc/letsencrypt/live/<domain>/
├── cert.pem       (certificate only - DO NOT USE)
├── chain.pem      (intermediate CA - DO NOT USE alone)
├── fullchain.pem  (cert + chain - USE THIS in nginx)
└── privkey.pem    (private key - USE THIS in nginx)

Always use fullchain.pem + privkey.pem in nginx configuration.