tractatus/docs/PHASE-2-INFRASTRUCTURE-PLAN.md
TheFlow 41526f5afd docs: comprehensive Phase 2 planning - roadmap, costs, governance, infrastructure
Phase 2 Planning Documents Created:

1. PHASE-2-ROADMAP.md (Comprehensive 3-month plan)
   - Timeline & milestones (Month 1: Infrastructure, Month 2: AI features, Month 3: Soft launch)
   - 5 workstreams: Infrastructure, AI features, Governance, Content, Analytics
   - Success criteria (technical, governance, user, business)
   - Risk assessment with mitigation strategies
   - Decision points requiring approval

2. PHASE-2-COST-ESTIMATES.md (Budget planning)
   - Total Phase 2 cost: $550 USD (~$900 NZD) for 3 months
   - Recommended: VPS Essential ($30/mo) + Claude API ($50/mo)
   - Usage scenarios: Minimal, Standard (recommended), High
   - Cost optimization strategies (30-50% savings potential)
   - Monthly budget template for post-launch

3. PHASE-2-INFRASTRUCTURE-PLAN.md (Technical specifications)
   - Architecture: Cloudflare → Nginx → Node.js → MongoDB
   - Server specs: OVHCloud VPS Essential (2 vCore, 4GB RAM, 80GB SSD)
   - Deployment procedures (step-by-step server setup)
   - Security hardening (UFW, Fail2ban, SSH, MongoDB)
   - SSL/TLS with Let's Encrypt
   - Monitoring, logging, backup & disaster recovery
   - Complete deployment checklist (60+ verification steps)

4. Governance Documents (TRA-OPS-0001 through TRA-OPS-0005)

   TRA-OPS-0001: AI Content Generation Policy (Master policy)
   - Mandatory human approval for all AI content
   - Values boundary enforcement (Tractatus §12.1-12.7)
   - Transparency & attribution requirements
   - Quality & accuracy standards
   - Privacy & data protection (GDPR-lite)
   - Cost & resource management ($200/month cap)

   TRA-OPS-0002: Blog Editorial Guidelines
   - Editorial mission & content principles
   - 4 content categories (Framework updates, Case studies, Technical, Commentary)
   - AI-assisted workflow (topic → outline → human draft → approval)
   - Citation standards (APA-lite, 100% verification)
   - Writing standards (tone, voice, format, structure)
   - Publishing schedule (2-4 posts/month)

   TRA-OPS-0003: Media Inquiry Response Protocol
   - Inquiry classification (Press, Academic, Commercial, Community, Spam)
   - AI-assisted triage with priority scoring
   - Human approval for all responses (no auto-send)
   - PII anonymization before AI processing
   - Response templates & SLAs (4h for HIGH priority)
   - Escalation procedures to John Stroh

   TRA-OPS-0004: Case Study Moderation Standards
   - Submission requirements (title, summary, source, failure mode)
   - AI-assisted relevance assessment & Tractatus mapping
   - Quality checklist (completeness, clarity, sources)
   - Moderation workflow (approve/edit/request changes/reject)
   - Attribution & licensing (CC BY-SA 4.0)
   - Seed content: 3-5 curated case studies for launch

   TRA-OPS-0005: Human Oversight Requirements
   - 3 oversight models: MHA (mandatory approval), HITL (human-in-loop), HOTL (human-on-loop)
   - Admin reviewer role & responsibilities
   - Service level agreements (4h for media HIGH, 7 days for case studies)
   - Approval authority matrix (admin vs. John Stroh)
   - Quality assurance checklists
   - Incident response (boundary violations, poor quality)
   - Training & onboarding procedures

Key Principles Across All Documents:
- Tractatus dogfooding: Framework governs its own AI operations
- "What cannot be systematized must not be automated"
- Zero tolerance for AI values decisions without human approval
- Transparency in all AI assistance (clear attribution)
- Human-in-the-loop for STRATEGIC/OPERATIONAL quadrants
- Audit trail for all AI decisions (2-year retention)

Next Steps (Awaiting Approval):
- [ ] John Stroh reviews all 8 documents
- [ ] Budget approval ($550 for Phase 2, $100-150/month ongoing)
- [ ] Phase 2 start date confirmed
- [ ] OVHCloud VPS provisioned
- [ ] Anthropic Claude API account created

Phase 2 Status: PLANNING COMPLETE → Awaiting approval to begin deployment

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-07 12:52:14 +13:00

24 KiB

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
  2. Server Specifications
  3. Network Architecture
  4. Deployment Procedures
  5. Security Hardening
  6. Monitoring & Alerting
  7. Backup & Disaster Recovery
  8. DNS & Domain Configuration
  9. SSL/TLS Configuration
  10. Environment Configuration
  11. Deployment Checklist

Architecture Overview

High-Level Architecture

┌─────────────────────────────────────────────────────────────┐
│                      Internet                                │
└────────────────────┬────────────────────────────────────────┘
                     │
            ┌────────▼────────┐
            │   Cloudflare    │ (Optional CDN + DDoS protection)
            │   DNS + Proxy   │
            └────────┬────────┘
                     │
            ┌────────▼────────┐
            │  OVHCloud VPS   │ (Ubuntu 22.04 LTS)
            │  mysy.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

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:

# 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):

# Default deny incoming
ufw default deny incoming

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

Activate:

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:

ssh root@<server_ip>

2. Create Non-Root User

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

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

ssh tractatus@<server_ip>

3. Harden SSH

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

sudo systemctl restart sshd

4. System Updates

# 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

# 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

# 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

# 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

# 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

# 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

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

# Edit configuration
nano .env.production

Production Environment Variables:

# 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@mysy.digital
SMTP_PASSWORD=<protonbridge_password>
SMTP_FROM=contact@mysy.digital

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

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

Generate JWT Secret:

node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"

3. Database Initialization

# 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

# 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

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

Service Configuration:

[Unit]
Description=Tractatus AI Safety Framework
Documentation=https://mysy.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:

# 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

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

Nginx Configuration:

# 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 mysy.digital www.mysy.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 mysy.digital www.mysy.digital;

    # SSL certificates (Let's Encrypt)
    ssl_certificate /etc/letsencrypt/live/mysy.digital/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/mysy.digital/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/mysy.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:

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

# Obtain SSL certificate
sudo certbot --nginx -d mysy.digital -d www.mysy.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):

# Certbot creates a systemd timer for auto-renewal
# Certificates renew 30 days before expiry
# No manual intervention needed

Security Hardening

Fail2ban Configuration

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

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

sudo systemctl restart fail2ban
sudo systemctl enable fail2ban

# Verify
sudo fail2ban-client status

MongoDB Security

# Enable authentication
sudo nano /etc/mongod.conf

Add:

security:
  authorization: enabled

net:
  bindIp: 127.0.0.1
  port: 27017

Restart MongoDB:

sudo systemctl restart mongod

Automatic Security Updates

# 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

# 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

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

Script:

#!/bin/bash

# Healthcheck endpoint
URL="https://mysy.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):

# 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

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

#!/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):

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:

# 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/
  1. Restore Database:
# 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
  1. Reconfigure DNS (if IP changed)

  2. Verify:

curl https://mysy.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:

# 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 (mysy.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


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

# Restart application
sudo systemctl restart tractatus.service

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

# Check status
sudo systemctl status tractatus.service

Nginx

# Test configuration
sudo nginx -t

# Reload configuration
sudo systemctl reload nginx

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

MongoDB

# 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

# 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