# Phase 2 Infrastructure Plan

**Project**: Tractatus AI Safety Framework Website
**Phase**: 2 of 3
**Created**: 2025-10-07
**Owner**: John Stroh
**Status**: Planning
**Target Deployment**: TBD (awaiting Phase 2 approval)

---

## Table of Contents

1. [Architecture Overview](#architecture-overview)
2. [Server Specifications](#server-specifications)
3. [Network Architecture](#network-architecture)
4. [Deployment Procedures](#deployment-procedures)
5. [Security Hardening](#security-hardening)
6. [Monitoring & Alerting](#monitoring--alerting)
7. [Backup & Disaster Recovery](#backup--disaster-recovery)
8. [DNS & Domain Configuration](#dns--domain-configuration)
9. [SSL/TLS Configuration](#ssltls-configuration)
10. [Environment Configuration](#environment-configuration)
11. [Deployment Checklist](#deployment-checklist)

---

## Architecture Overview

### High-Level Architecture

```
┌─────────────────────────────────────────────────────────────┐
│                      Internet                                │
└────────────────────┬────────────────────────────────────────┘
                     │
            ┌────────▼────────┐
            │   Cloudflare    │ (Optional CDN + DDoS protection)
            │   DNS + Proxy   │
            └────────┬────────┘
                     │
            ┌────────▼────────┐
            │  OVHCloud VPS   │ (Ubuntu 22.04 LTS)
            │  agenticgovernance.digital   │
            └────────┬────────┘
                     │
        ┌────────────┴────────────┐
        │                         │
   ┌────▼─────┐           ┌──────▼──────┐
   │  Nginx   │           │  Fail2ban   │
   │  :80/443 │           │  Firewall   │
   └────┬─────┘           └─────────────┘
        │
   ┌────▼─────┐
   │ Node.js  │
   │ Express  │
   │  :9000   │
   └────┬─────┘
        │
   ┌────▼─────┐
   │ MongoDB  │
   │  :27017  │
   └──────────┘
```

### Component Stack

| Layer | Component | Version | Purpose |
|-------|-----------|---------|---------|
| **CDN** | Cloudflare | Free tier | DDoS protection, CDN (optional) |
| **DNS** | Cloudflare/OVH | - | Domain nameservers |
| **Firewall** | UFW + Fail2ban | Latest | Perimeter security |
| **Web Server** | Nginx | 1.24+ | Reverse proxy, SSL termination |
| **Application** | Node.js + Express | 18 LTS + 4.x | Tractatus platform |
| **Database** | MongoDB | 7.x | Document storage |
| **Process Manager** | systemd | System default | Service management |
| **SSL/TLS** | Let's Encrypt | Latest | HTTPS certificates |
| **Email** | ProtonBridge | Latest | SMTP gateway |
| **Analytics** | Plausible (self-hosted) | Latest | Privacy-respecting analytics |
| **Monitoring** | Self-hosted scripts | Custom | Uptime, performance |

---

## Server Specifications

### Recommended Configuration (OVHCloud VPS Essential)

**Compute**:
- vCores: 2 (Intel Xeon or AMD EPYC)
- RAM: 4GB DDR4
- Storage: 80GB SSD NVMe
- Architecture: x86_64

**Network**:
- Bandwidth: 500 Mbps
- Traffic: Unlimited
- IPv4: 1 dedicated
- IPv6: 1 dedicated (/64 subnet)
- Anti-DDoS: Included

**Operating System**:
- Distribution: Ubuntu 22.04 LTS (Jammy Jellyfish)
- Kernel: 5.15+ (HWE)
- Init system: systemd

**Geographic Location**:
- Preferred: Singapore or Australia (closest to NZ)
- Alternative: Europe (if latency acceptable)
- Avoid: US East/West (regulatory, latency)

**Cost**: ~$20-30/month USD

---

### Server Sizing Rationale

**Memory Allocation**:
- MongoDB: ~1.5GB (production database + WiredTiger cache)
- Node.js: ~1GB (application + dependencies)
- Nginx: ~100MB (minimal footprint)
- System: ~500MB (OS, utilities)
- Plausible Analytics: ~500MB (if self-hosted)
- Buffer: ~400MB (peak load)
- **Total**: ~4GB (fits VPS Essential)

**Storage Allocation**:
- OS + System: ~10GB
- MongoDB data: ~20GB (estimated Year 1)
- Application code: ~2GB (node_modules)
- Logs: ~5GB (1-year retention with rotation)
- Backups (on-server): ~20GB (7-day retention)
- Free space: ~23GB (buffer)
- **Total**: ~80GB (VPS Essential)

**CPU Usage**:
- Typical: <20% (2 vCores)
- Peak: <60% (during deployments, backups)
- Acceptable: 2 vCores sufficient for Phase 2 traffic

---

## Network Architecture

### Firewall Rules (UFW)

**Allow**:
```bash
# SSH (restricted to specific IPs in production)
ufw allow from <admin_ip> to any port 22 proto tcp comment 'SSH Admin'

# HTTP (redirect to HTTPS)
ufw allow 80/tcp comment 'HTTP'

# HTTPS
ufw allow 443/tcp comment 'HTTPS'

# MongoDB (localhost only, no external access)
ufw allow from 127.0.0.1 to any port 27017 proto tcp comment 'MongoDB local'
```

**Deny** (default):
```bash
# Default deny incoming
ufw default deny incoming

# Default allow outgoing (for apt, npm, git)
ufw default allow outgoing
```

**Activate**:
```bash
ufw enable
ufw status verbose
```

---

### Port Configuration

| Service | Port | Bind Address | Firewall | Notes |
|---------|------|--------------|----------|-------|
| **SSH** | 22 | 0.0.0.0 | Restricted IP | Key-only auth |
| **HTTP** | 80 | 0.0.0.0 | Allow | Redirect to 443 |
| **HTTPS** | 443 | 0.0.0.0 | Allow | Nginx reverse proxy |
| **Node.js** | 9000 | 127.0.0.1 | Deny | Internal only |
| **MongoDB** | 27017 | 127.0.0.1 | Deny | Internal only |

---

## Deployment Procedures

### Initial Server Setup

#### 1. Provision VPS

**OVHCloud Control Panel**:
1. Select VPS Essential tier
2. Choose Ubuntu 22.04 LTS
3. Select geographic region (Singapore/Australia)
4. Generate root password (save securely)
5. Provision (5-10 minutes)

**Verify Access**:
```bash
ssh root@<server_ip>
```

---

#### 2. Create Non-Root User

```bash
# Create deploy user
adduser tractatus
usermod -aG sudo tractatus

# Set up SSH key auth
mkdir -p /home/tractatus/.ssh
chmod 700 /home/tractatus/.ssh
```

**On local machine**:
```bash
# Generate SSH key (if not exists)
ssh-keygen -t ed25519 -C "tractatus-deploy"

# Copy public key to server
ssh-copy-id tractatus@<server_ip>
```

**Test**:
```bash
ssh tractatus@<server_ip>
```

---

#### 3. Harden SSH

```bash
# Edit SSH config
sudo nano /etc/ssh/sshd_config
```

**Changes**:
```
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
Port 22  # Consider changing to non-standard port (2222)
AllowUsers tractatus
```

**Restart SSH**:
```bash
sudo systemctl restart sshd
```

---

#### 4. System Updates

```bash
# Update package lists
sudo apt update

# Upgrade all packages
sudo apt upgrade -y

# Install essential tools
sudo apt install -y \
  curl \
  wget \
  git \
  ufw \
  fail2ban \
  htop \
  vim \
  certbot \
  python3-certbot-nginx
```

---

#### 5. Configure Firewall

```bash
# Allow SSH (before enabling UFW!)
sudo ufw allow from <your_ip> to any port 22 proto tcp

# Allow HTTP/HTTPS
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# Enable firewall
sudo ufw enable
sudo ufw status verbose
```

---

### Install Application Stack

#### 1. Install Node.js

```bash
# Add NodeSource repository (Node 18 LTS)
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -

# Install Node.js
sudo apt install -y nodejs

# Verify
node --version  # v18.x.x
npm --version   # 9.x.x
```

---

#### 2. Install MongoDB

```bash
# Import MongoDB public GPG key
curl -fsSL https://www.mongodb.org/static/pgp/server-7.0.asc | \
   sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/mongodb-server-7.0.gpg

# Add MongoDB repository
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/7.0 multiverse" | \
   sudo tee /etc/apt/sources.list.d/mongodb-org-7.0.list

# Update and install
sudo apt update
sudo apt install -y mongodb-org

# Start MongoDB
sudo systemctl start mongod
sudo systemctl enable mongod

# Verify
sudo systemctl status mongod
mongosh --eval 'db.version()'  # 7.0.x
```

---

#### 3. Install Nginx

```bash
# Install Nginx
sudo apt install -y nginx

# Start Nginx
sudo systemctl start nginx
sudo systemctl enable nginx

# Verify
sudo systemctl status nginx
nginx -v  # nginx/1.24.x
```

---

### Deploy Application

#### 1. Clone Repository

```bash
# Create application directory
sudo mkdir -p /var/www/tractatus
sudo chown tractatus:tractatus /var/www/tractatus

# Clone repository
cd /var/www/tractatus
git clone https://github.com/your-org/tractatus.git .

# Install dependencies
npm install --production
```

---

#### 2. Configure Environment

```bash
# Create production environment file
cp .env.example .env.production

# Edit configuration
nano .env.production
```

**Production Environment Variables**:
```bash
# Application
NODE_ENV=production
PORT=9000
APP_NAME=Tractatus

# MongoDB
MONGODB_URI=mongodb://localhost:27017/tractatus_prod
MONGODB_PORT=27017

# JWT
JWT_SECRET=<generate_secure_random_string_64_chars>
JWT_EXPIRY=7d
JWT_AUDIENCE=tractatus-admin
JWT_ISSUER=tractatus

# Claude API
CLAUDE_API_KEY=<anthropic_api_key>
CLAUDE_MODEL=claude-sonnet-4-5-20250929
CLAUDE_MAX_TOKENS=200000

# Email (ProtonBridge)
SMTP_HOST=127.0.0.1
SMTP_PORT=1025
SMTP_USER=contact@agenticgovernance.digital
SMTP_PASSWORD=<protonbridge_password>
SMTP_FROM=contact@agenticgovernance.digital

# Admin
ADMIN_EMAIL=john.stroh.nz@pm.me

# Logging
LOG_LEVEL=info
LOG_FILE=/var/log/tractatus/app.log
```

**Generate JWT Secret**:
```bash
node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"
```

---

#### 3. Database Initialization

```bash
# Create production database and admin user
mongosh tractatus_prod --eval "
  db.createUser({
    user: 'tractatus',
    pwd: '<secure_password>',
    roles: [{ role: 'readWrite', db: 'tractatus_prod' }]
  })
"

# Run migration scripts
npm run init:db

# Seed admin user
npm run seed:admin
```

---

#### 4. Build Assets

```bash
# Build Tailwind CSS for production
npm run build:css

# Verify build
ls -lh public/css/tailwind.css  # Should be ~24KB minified
```

---

#### 5. Create Systemd Service

```bash
# Create service file
sudo nano /etc/systemd/system/tractatus.service
```

**Service Configuration**:
```ini
[Unit]
Description=Tractatus AI Safety Framework
Documentation=https://agenticgovernance.digital/docs
After=network.target mongod.service

[Service]
Type=simple
User=tractatus
WorkingDirectory=/var/www/tractatus
Environment=NODE_ENV=production
EnvironmentFile=/var/www/tractatus/.env.production
ExecStart=/usr/bin/node src/server.js
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal
SyslogIdentifier=tractatus

# Security
NoNewPrivileges=true
PrivateTmp=true

[Install]
WantedBy=multi-user.target
```

**Enable and Start**:
```bash
# Reload systemd
sudo systemctl daemon-reload

# Enable service (start on boot)
sudo systemctl enable tractatus.service

# Start service
sudo systemctl start tractatus.service

# Verify
sudo systemctl status tractatus.service
journalctl -u tractatus.service -f
```

---

### Configure Nginx

#### 1. Create Nginx Configuration

```bash
# Create site configuration
sudo nano /etc/nginx/sites-available/tractatus
```

**Nginx Configuration**:
```nginx
# Upstream Node.js application
upstream tractatus_app {
    server 127.0.0.1:9000;
    keepalive 64;
}

# HTTP (redirect to HTTPS)
server {
    listen 80;
    listen [::]:80;
    server_name agenticgovernance.digital www.agenticgovernance.digital;

    # Let's Encrypt verification
    location /.well-known/acme-challenge/ {
        root /var/www/html;
    }

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

# HTTPS
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name agenticgovernance.digital www.agenticgovernance.digital;

    # SSL certificates (Let's Encrypt)
    ssl_certificate /etc/letsencrypt/live/agenticgovernance.digital/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/agenticgovernance.digital/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/agenticgovernance.digital/chain.pem;

    # SSL configuration (Mozilla Intermediate)
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
    ssl_prefer_server_ciphers off;

    # SSL session cache
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;

    # OCSP stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 1.1.1.1 1.0.0.1 valid=300s;
    resolver_timeout 5s;

    # Security headers
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
    add_header X-Frame-Options "DENY" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "no-referrer-when-downgrade" always;
    add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'" always;

    # Logging
    access_log /var/log/nginx/tractatus-access.log;
    error_log /var/log/nginx/tractatus-error.log;

    # Root and index
    root /var/www/tractatus/public;
    index index.html;

    # Static files (served directly by Nginx)
    location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|webp|woff|woff2|ttf|eot)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }

    # API and dynamic routes (proxy to Node.js)
    location /api/ {
        proxy_pass http://tractatus_app;
        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;
        proxy_cache_bypass $http_upgrade;
        proxy_read_timeout 60s;
    }

    # Try static files first, then proxy to Node.js
    location / {
        try_files $uri $uri/ @nodejs;
    }

    location @nodejs {
        proxy_pass http://tractatus_app;
        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;
        proxy_cache_bypass $http_upgrade;
    }

    # Security: Deny access to sensitive files
    location ~ /\. {
        deny all;
    }

    location ~ /\.git {
        deny all;
    }

    location ~ /node_modules {
        deny all;
    }

    location ~ /\.env {
        deny all;
    }
}
```

**Enable Site**:
```bash
# Create symlink
sudo ln -s /etc/nginx/sites-available/tractatus /etc/nginx/sites-enabled/

# Remove default site
sudo rm /etc/nginx/sites-enabled/default

# Test configuration
sudo nginx -t

# Reload Nginx
sudo systemctl reload nginx
```

---

### SSL/TLS Setup (Let's Encrypt)

```bash
# Obtain SSL certificate
sudo certbot --nginx -d agenticgovernance.digital -d www.agenticgovernance.digital

# Follow prompts:
# - Enter email: john.stroh.nz@pm.me
# - Agree to terms
# - Redirect HTTP to HTTPS: Yes

# Verify auto-renewal
sudo certbot renew --dry-run

# Auto-renewal is configured via systemd timer
sudo systemctl list-timers | grep certbot
```

**Certificate Renewal** (automatic):
```bash
# Certbot creates a systemd timer for auto-renewal
# Certificates renew 30 days before expiry
# No manual intervention needed
```

---

## Security Hardening

### Fail2ban Configuration

```bash
# Install Fail2ban (if not already)
sudo apt install -y fail2ban

# Create local configuration
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.local
```

**Jail Configuration**:
```ini
[DEFAULT]
bantime = 3600        # 1 hour ban
findtime = 600        # 10 minutes
maxretry = 5          # 5 attempts before ban

[sshd]
enabled = true
port = 22
filter = sshd
logpath = /var/log/auth.log

[nginx-http-auth]
enabled = true
port = 80,443
filter = nginx-http-auth
logpath = /var/log/nginx/error.log

[nginx-limit-req]
enabled = true
port = 80,443
filter = nginx-limit-req
logpath = /var/log/nginx/error.log
maxretry = 10
```

**Restart**:
```bash
sudo systemctl restart fail2ban
sudo systemctl enable fail2ban

# Verify
sudo fail2ban-client status
```

---

### MongoDB Security

```bash
# Enable authentication
sudo nano /etc/mongod.conf
```

**Add**:
```yaml
security:
  authorization: enabled

net:
  bindIp: 127.0.0.1
  port: 27017
```

**Restart MongoDB**:
```bash
sudo systemctl restart mongod
```

---

### Automatic Security Updates

```bash
# Install unattended-upgrades
sudo apt install -y unattended-upgrades

# Configure
sudo dpkg-reconfigure -plow unattended-upgrades
# Select "Yes" to enable

# Verify
sudo cat /etc/apt/apt.conf.d/20auto-upgrades
```

---

## Monitoring & Alerting

### Log Management

```bash
# Create log directories
sudo mkdir -p /var/log/tractatus
sudo chown tractatus:tractatus /var/log/tractatus

# Configure logrotate
sudo nano /etc/logrotate.d/tractatus
```

**Logrotate Configuration**:
```
/var/log/tractatus/*.log {
    daily
    rotate 7
    compress
    delaycompress
    missingok
    notifempty
    create 0640 tractatus tractatus
    sharedscripts
    postrotate
        systemctl reload tractatus >/dev/null 2>&1 || true
    endscript
}
```

---

### Uptime Monitoring Script

```bash
# Create monitoring script
sudo nano /usr/local/bin/tractatus-healthcheck.sh
```

**Script**:
```bash
#!/bin/bash

# Healthcheck endpoint
URL="https://agenticgovernance.digital/health"

# Check if site is up
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" $URL)

if [ "$HTTP_CODE" != "200" ]; then
    # Alert (email to admin)
    echo "Tractatus is DOWN! HTTP code: $HTTP_CODE" | \
        mail -s "ALERT: Tractatus Down" john.stroh.nz@pm.me
    exit 1
fi

exit 0
```

**Cron Job** (every 5 minutes):
```bash
# Make executable
sudo chmod +x /usr/local/bin/tractatus-healthcheck.sh

# Add to crontab
sudo crontab -e
```

**Add**:
```
*/5 * * * * /usr/local/bin/tractatus-healthcheck.sh
```

---

## Backup & Disaster Recovery

### MongoDB Backup Script

```bash
# Create backup directory
sudo mkdir -p /var/backups/tractatus/mongodb
sudo chown tractatus:tractatus /var/backups/tractatus/mongodb

# Create backup script
nano /home/tractatus/backup-mongodb.sh
```

**Script**:
```bash
#!/bin/bash

# Configuration
BACKUP_DIR="/var/backups/tractatus/mongodb"
DB_NAME="tractatus_prod"
DATE=$(date +%Y%m%d_%H%M%S)
RETENTION_DAYS=7

# Create backup
mongodump --db $DB_NAME --out $BACKUP_DIR/$DATE

# Compress backup
tar -czf $BACKUP_DIR/tractatus_backup_$DATE.tar.gz -C $BACKUP_DIR $DATE
rm -rf $BACKUP_DIR/$DATE

# Delete old backups
find $BACKUP_DIR -name "tractatus_backup_*.tar.gz" -mtime +$RETENTION_DAYS -delete

echo "Backup completed: $BACKUP_DIR/tractatus_backup_$DATE.tar.gz"
```

**Cron Job** (daily at 2am):
```bash
chmod +x /home/tractatus/backup-mongodb.sh

crontab -e
```

**Add**:
```
0 2 * * * /home/tractatus/backup-mongodb.sh >> /var/log/tractatus/backup.log 2>&1
```

---

### Disaster Recovery Procedure

**Scenario**: Server failure, data loss

**Recovery Steps**:

1. **Provision New Server** (same specs)

2. **Restore Application**:
```bash
# Clone repository
git clone https://github.com/your-org/tractatus.git /var/www/tractatus

# Restore environment file
scp .env.production tractatus@<new_server_ip>:/var/www/tractatus/
```

3. **Restore Database**:
```bash
# Copy backup to new server
scp tractatus_backup_YYYYMMDD.tar.gz tractatus@<new_server_ip>:/tmp/

# Extract and restore
tar -xzf /tmp/tractatus_backup_YYYYMMDD.tar.gz -C /tmp/
mongorestore --db tractatus_prod /tmp/YYYYMMDD/tractatus_prod
```

4. **Reconfigure DNS** (if IP changed)

5. **Verify**:
```bash
curl https://agenticgovernance.digital/health
```

**RTO** (Recovery Time Objective): <4 hours
**RPO** (Recovery Point Objective): 24 hours (daily backups)

---

## DNS & Domain Configuration

### Cloudflare DNS Setup

**A Records**:
```
Type: A
Name: @
Content: <server_ip>
Proxy status: Proxied (or DNS only)
TTL: Auto
```

```
Type: A
Name: www
Content: <server_ip>
Proxy status: Proxied (or DNS only)
TTL: Auto
```

**AAAA Records** (IPv6):
```
Type: AAAA
Name: @
Content: <server_ipv6>
Proxy status: Proxied
TTL: Auto
```

**MX Records** (if using custom email):
```
Type: MX
Name: @
Content: mail.protonmail.ch
Priority: 10
```

**TXT Records** (SPF, DKIM):
```
Type: TXT
Name: @
Content: v=spf1 include:_spf.protonmail.ch ~all
```

---

## Environment Configuration

### Production .env File

**Template** (already shown above, see Deploy Application > Configure Environment)

**Security**:
```bash
# Restrict permissions
chmod 600 /var/www/tractatus/.env.production

# Verify
ls -l /var/www/tractatus/.env.production
# Should show: -rw------- (owner only)
```

---

## Deployment Checklist

### Pre-Deployment

- [ ] OVHCloud VPS provisioned (Essential tier)
- [ ] Domain registered (agenticgovernance.digital)
- [ ] Cloudflare account created (optional)
- [ ] DNS configured (A/AAAA records pointing to server)
- [ ] SSH key generated and added to server
- [ ] John Stroh has admin access

---

### Server Setup

- [ ] Ubuntu 22.04 LTS installed
- [ ] Non-root user created (tractatus)
- [ ] SSH hardened (key-only, no root)
- [ ] Firewall configured (UFW)
- [ ] Fail2ban installed and configured
- [ ] Automatic security updates enabled

---

### Application Stack

- [ ] Node.js 18 LTS installed
- [ ] MongoDB 7.x installed and running
- [ ] Nginx installed and running
- [ ] Application repository cloned
- [ ] npm dependencies installed (`npm install --production`)
- [ ] Environment file configured (.env.production)
- [ ] Database initialized (`npm run init:db`)
- [ ] Admin user created (`npm run seed:admin`)
- [ ] Tailwind CSS built (`npm run build:css`)

---

### Service Configuration

- [ ] systemd service created (tractatus.service)
- [ ] Service enabled and started
- [ ] Service logs verified (`journalctl -u tractatus`)
- [ ] Nginx configured (sites-available/tractatus)
- [ ] Nginx configuration tested (`nginx -t`)
- [ ] SSL certificates obtained (Let's Encrypt)
- [ ] HTTPS redirect working

---

### Security

- [ ] Firewall rules verified (`ufw status`)
- [ ] SSH access tested (key-only)
- [ ] MongoDB authentication enabled
- [ ] MongoDB bound to localhost only
- [ ] Application environment secrets secure (chmod 600)
- [ ] Security headers verified (browser dev tools)
- [ ] SSL Labs test: A+ rating (https://www.ssllabs.com/ssltest/)

---

### Monitoring

- [ ] Log rotation configured (logrotate)
- [ ] Uptime monitoring script installed
- [ ] Backup script configured and tested
- [ ] Email alerts configured (john.stroh.nz@pm.me)
- [ ] Plausible Analytics installed (optional, self-hosted)

---

### Testing

- [ ] Homepage loads: https://agenticgovernance.digital/
- [ ] API health check: https://agenticgovernance.digital/health
- [ ] Document viewer: https://agenticgovernance.digital/docs-viewer.html
- [ ] Admin login: https://agenticgovernance.digital/admin/login.html
- [ ] Static assets loading (CSS, JS)
- [ ] CSP compliance (no console errors)
- [ ] Mobile responsiveness (test on phone)

---

### Post-Deployment

- [ ] DNS propagation complete (24-48 hours)
- [ ] SSL certificate auto-renewal tested (`certbot renew --dry-run`)
- [ ] Backup restore tested (disaster recovery drill)
- [ ] Performance baseline recorded (Lighthouse, WebPageTest)
- [ ] Monitoring alerts tested (trigger fake downtime)

---

## Appendix: Quick Commands

### Service Management

```bash
# Restart application
sudo systemctl restart tractatus.service

# View logs
sudo journalctl -u tractatus.service -f

# Check status
sudo systemctl status tractatus.service
```

### Nginx

```bash
# Test configuration
sudo nginx -t

# Reload configuration
sudo systemctl reload nginx

# View error log
sudo tail -f /var/log/nginx/tractatus-error.log
```

### MongoDB

```bash
# Connect to database
mongosh tractatus_prod

# Backup manually
mongodump --db tractatus_prod --out /tmp/backup

# Restore from backup
mongorestore --db tractatus_prod /tmp/backup/tractatus_prod
```

### SSL

```bash
# Renew certificates manually
sudo certbot renew

# Check certificate expiry
sudo certbot certificates
```

---

## Revision History

| Date | Version | Changes |
|------|---------|---------|
| 2025-10-07 | 1.0 | Initial infrastructure plan for Phase 2 |

---

**Document Owner**: John Stroh
**Last Updated**: 2025-10-07
**Next Review**: Upon deployment completion
