tractatus/docs/PHASE-2-INFRASTRUCTURE-PLAN.md
TheFlow 2298d36bed fix(submissions): restructure Economist package and fix article display
- Create Economist SubmissionTracking package correctly:
  * mainArticle = full blog post content
  * coverLetter = 216-word SIR— letter
  * Links to blog post via blogPostId
- Archive 'Letter to The Economist' from blog posts (it's the cover letter)
- Fix date display on article cards (use published_at)
- Target publication already displaying via blue badge

Database changes:
- Make blogPostId optional in SubmissionTracking model
- Economist package ID: 68fa85ae49d4900e7f2ecd83
- Le Monde package ID: 68fa2abd2e6acd5691932150

Next: Enhanced modal with tabs, validation, export

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-24 08:47:42 +13:00

1175 lines
25 KiB
Markdown

# 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