Files
firefrost-services/services/arbiter/DEPLOYMENT.md
Claude (The Golden Chronicler #50) 04e9b407d5 feat: Migrate Arbiter and Modpack Version Checker to monorepo
WHAT WAS DONE:
- Migrated Arbiter (discord-oauth-arbiter) code to services/arbiter/
- Migrated Modpack Version Checker code to services/modpack-version-checker/
- Created .env.example for Arbiter with all required environment variables
- Moved systemd service file to services/arbiter/deploy/
- Organized directory structure per Gemini monorepo recommendations

WHY:
- Consolidate all service code in one repository
- Prepare for Gemini code review (Panel v1.12 compatibility check)
- Enable service-prefixed Git tagging (arbiter-v2.1.0, modpack-v1.0.0)
- Support npm workspaces for shared dependencies

SERVICES MIGRATED:
1. Arbiter (Discord OAuth bot) - Originally written by Gemini + Claude
   - Full source code from ops-manual docs/implementation/
   - Created comprehensive .env.example
   - Ready for Panel v1.12 compatibility verification

2. Modpack Version Checker (Python CLI tool)
   - Full source code from ops-manual docs/tasks/
   - Written for Panel v1.11, needs Gemini review for v1.12
   - Never had code review before

STILL TODO:
- Whitelist Manager - Pull from Billing VPS (38.68.14.188)
  - Currently deployed and running
  - Needs Panel v1.12 API compatibility fix (Task #86)
  - Requires SSH access to pull code

NEXT STEPS:
- Gemini code review for Panel v1.12 API compatibility
- Create package.json for each service
- Test npm workspaces integration
- Deploy after verification

FILES:
- services/arbiter/ (25 new files, full application)
- services/modpack-version-checker/ (21 new files, full application)

Signed-off-by: The Golden Chronicler <claude@firefrostgaming.com>
2026-03-31 21:52:42 +00:00

13 KiB

Firefrost Arbiter - Complete Deployment Guide

Target Server: Command Center (63.143.34.217, Dallas)
Date: March 30, 2026
Prepared by: Claude (Chronicler #49)


📋 Pre-Deployment Checklist

Discord Configuration

  • Discord Application created at discord.com/developers/applications
  • Bot token generated and saved securely
  • Client ID and Client Secret obtained
  • Server Members Intent enabled
  • Redirect URIs added:
    • https://discord-bot.firefrostgaming.com/auth/callback
    • https://discord-bot.firefrostgaming.com/admin/callback
  • Bot invited to server with "Manage Roles" permission
  • Bot role positioned ABOVE all subscription tier roles in hierarchy

Ghost CMS Configuration

  • Custom field discord_id created (Settings → Membership → Custom Fields)
  • Custom Integration created: "Firefrost Arbiter"
  • Admin API Key copied (format: key_id:secret)

Server Configuration

  • Node.js 18.x installed
  • Nginx installed and running
  • UFW firewall configured (ports 80, 443, 3500 if needed)
  • Let's Encrypt SSL certificate obtained for discord-bot.firefrostgaming.com
  • User architect exists with sudo privileges

Credentials Prepared

  • Discord Bot Token
  • Discord Client ID
  • Discord Client Secret
  • Discord Guild ID (server ID)
  • Trinity Discord IDs (Michael, Meg, Holly)
  • Ghost CMS URL
  • Ghost Admin API Key
  • Mailcow SMTP password
  • Paymenter webhook secret
  • SESSION_SECRET generated (32-byte random)

🚀 Phase 1: Initial Setup

Step 1: Connect to Server

ssh architect@63.143.34.217

Step 2: Create Application Directory

cd /home/architect
mkdir -p arbiter
cd arbiter

Step 3: Upload Application Files

From your local machine:

# If using git
git clone <repository-url> /home/architect/arbiter

# Or if uploading manually via scp
scp -r discord-oauth-implementation/* architect@63.143.34.217:/home/architect/arbiter/

Step 4: Install Dependencies

cd /home/architect/arbiter
npm install

Expected output:

added 87 packages in 12s

Step 5: Create Environment File

cp .env.example .env
nano .env

Fill in ALL values:

NODE_ENV=production
PORT=3500
APP_URL=https://discord-bot.firefrostgaming.com
SESSION_SECRET=<generate with: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))">

DISCORD_BOT_TOKEN=<from Discord Developer Portal>
DISCORD_CLIENT_ID=<from Discord Developer Portal>
DISCORD_CLIENT_SECRET=<from Discord Developer Portal>
GUILD_ID=<your Discord server ID>

ADMIN_DISCORD_IDS=<michael_id>,<meg_id>,<holly_id>

CMS_URL=https://firefrostgaming.com
CMS_ADMIN_KEY=<from Ghost Integrations>

SMTP_HOST=38.68.14.188
SMTP_USER=noreply@firefrostgaming.com
SMTP_PASS=<from Mailcow>

WEBHOOK_SECRET=<from Paymenter>

Generate SESSION_SECRET:

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

Save and exit: Ctrl+X, Y, Enter

Step 6: Configure Discord Role Mapping

nano config/roles.json

Get Discord Role IDs:

  1. Go to Discord server
  2. Settings → Roles
  3. Right-click each role → Copy ID

Fill in the file:

{
  "awakened": "1234567890123456789",
  "fire_elemental": "2345678901234567890",
  "frost_elemental": "3456789012345678901",
  "fire_knight": "4567890123456789012",
  "frost_knight": "5678901234567890123",
  "fire_master": "6789012345678901234",
  "frost_master": "7890123456789012345",
  "fire_legend": "8901234567890123456",
  "frost_legend": "9012345678901234567",
  "sovereign": "0123456789012345678"
}

Save and exit

Step 7: Set Permissions

chmod 600 .env
chmod +x backup.sh

🌐 Phase 2: Nginx Configuration

Step 1: Copy Nginx Config

sudo cp nginx.conf /etc/nginx/sites-available/arbiter
sudo ln -s /etc/nginx/sites-available/arbiter /etc/nginx/sites-enabled/

Step 2: Test Nginx Configuration

sudo nginx -t

Expected output:

nginx: configuration file /etc/nginx/nginx.conf test is successful

Step 3: Reload Nginx

sudo systemctl reload nginx

⚙️ Phase 3: Systemd Service Setup

Step 1: Copy Service File

sudo cp arbiter.service /etc/systemd/system/

Step 2: Reload Systemd

sudo systemctl daemon-reload

Step 3: Enable Service (Start on Boot)

sudo systemctl enable arbiter

Step 4: Start Service

sudo systemctl start arbiter

Step 5: Check Status

sudo systemctl status arbiter

Expected output:

● arbiter.service - Firefrost Arbiter - Discord Role Management System
     Loaded: loaded (/etc/systemd/system/arbiter.service; enabled)
     Active: active (running) since Sun 2026-03-30 10:00:00 CDT; 5s ago
   Main PID: 12345 (node)
      Tasks: 11 (limit: 9830)
     Memory: 45.2M
     CGroup: /system.slice/arbiter.service
             └─12345 /usr/bin/node src/index.js

Mar 30 10:00:00 command-center systemd[1]: Started Firefrost Arbiter.
Mar 30 10:00:00 command-center arbiter[12345]: [Server] Listening on port 3500
Mar 30 10:00:01 command-center arbiter[12345]: [Discord] Bot logged in as ArbiterBot#1234
Mar 30 10:00:01 command-center arbiter[12345]: [Database] Cleaned up 0 expired tokens.

If status shows "failed":

sudo journalctl -u arbiter -n 50

Phase 4: Validation & Testing

Step 1: Check Application Logs

sudo journalctl -u arbiter -f

Look for:

  • [Server] Listening on port 3500
  • [Discord] Bot logged in as <BotName>
  • [Database] Cleaned up X expired tokens

Press Ctrl+C to exit

Step 2: Test Health Endpoint

curl https://discord-bot.firefrostgaming.com/health

Expected response:

{
  "uptime": 123.456,
  "discord": "ok",
  "database": "ok",
  "timestamp": "2026-03-30T15:00:00.000Z"
}

If you get 502 Bad Gateway:

  • Check application is running: sudo systemctl status arbiter
  • Check application logs: sudo journalctl -u arbiter -n 50
  • Check Nginx is running: sudo systemctl status nginx

Step 3: Test Webhook Reception (Local)

curl -X POST http://localhost:3500/webhook/billing \
  -H "Content-Type: application/json" \
  -d '{
    "event": "subscription.created",
    "customer_email": "test@firefrostgaming.com",
    "customer_name": "Test User",
    "tier": "awakened",
    "subscription_id": "test_sub_123"
  }'

Check logs:

sudo journalctl -u arbiter -n 20

Look for:

  • [Webhook] Received subscription.created for test@firefrostgaming.com
  • [Webhook] Sent linking email to test@firefrostgaming.com

Check database:

sqlite3 linking.db "SELECT * FROM link_tokens;"

Should show newly created token.

Step 4: Test Admin OAuth Login

  1. Visit https://discord-bot.firefrostgaming.com/admin/login in browser
  2. Should redirect to Discord OAuth
  3. Authorize with Trinity Discord account
  4. Should redirect to admin panel
  5. Verify search, assign functions work

Step 5: End-to-End OAuth Test

Create test member in Ghost CMS:

  1. Ghost Admin → Members → New Member
  2. Email: test@firefrostgaming.com
  3. Name: "Test User"

Trigger webhook:

curl -X POST http://localhost:3500/webhook/billing \
  -H "Content-Type: application/json" \
  -d '{
    "event": "subscription.created",
    "customer_email": "test@firefrostgaming.com",
    "customer_name": "Test User",
    "tier": "awakened",
    "subscription_id": "test_001"
  }'

Check Mailcow logs for sent email:

ssh root@38.68.14.188
docker logs -f --tail 50 mailcowdockerized_postfix-mailcow_1

Copy linking URL from email (or get from database):

sqlite3 linking.db "SELECT token FROM link_tokens WHERE email='test@firefrostgaming.com';"

Build link:

https://discord-bot.firefrostgaming.com/link?token=<token>

Test flow:

  1. Visit link in browser
  2. Should redirect to Discord OAuth
  3. Authorize with test Discord account
  4. Should show success page
  5. Check Discord - test account should have "The Awakened" role
  6. Check Ghost Admin - test member should have discord_id populated

💾 Phase 5: Backup Configuration

Step 1: Create Backup Directory

mkdir -p /home/architect/backups/arbiter
chmod 700 /home/architect/backups/arbiter

Step 2: Test Backup Script

cd /home/architect/arbiter
./backup.sh

Check output:

cat /home/architect/backups/arbiter/backup_log.txt

Should show:

--- Backup Started: 20260330_100000 ---
Backup completed successfully.

Verify backup files exist:

ls -lh /home/architect/backups/arbiter/

Should show:

-rw-r--r-- 1 architect architect  12K Mar 30 10:00 linking_20260330_100000.db
-rw-r--r-- 1 architect architect 4.0K Mar 30 10:00 sessions_20260330_100000.db
-rw------- 1 architect architect  892 Mar 30 10:00 env_20260330_100000.bak
-rw-r--r-- 1 architect architect  421 Mar 30 10:00 roles_20260330_100000.json

Step 3: Schedule Daily Backups

crontab -e

Add this line:

0 4 * * * /home/architect/arbiter/backup.sh >> /home/architect/backups/arbiter/cron_error.log 2>&1

Save and exit

Verify cron job:

crontab -l

Should show the backup line.


🔗 Phase 6: Paymenter Integration

Step 1: Configure Paymenter Webhook

  1. Log in to Paymenter admin panel
  2. Navigate to: System → Webhooks
  3. Click "Add Webhook"
  4. URL: https://discord-bot.firefrostgaming.com/webhook/billing
  5. Secret: (use value from .env WEBHOOK_SECRET)
  6. Events: Select:
    • subscription.created
    • subscription.upgraded
    • subscription.downgraded
    • subscription.cancelled
  7. Save webhook

Step 2: Test Paymenter Webhook

From Paymenter admin:

  1. Find webhook in list
  2. Click "Test Webhook"
  3. Should show successful delivery

Or manually trigger:

curl -X POST https://discord-bot.firefrostgaming.com/webhook/billing \
  -H "Content-Type: application/json" \
  -H "x-signature: <generate_valid_hmac>" \
  -d '{
    "event": "subscription.created",
    "customer_email": "real_customer@example.com",
    "customer_name": "Real Customer",
    "tier": "awakened",
    "subscription_id": "sub_real_123"
  }'

📊 Phase 7: Monitoring Setup

Step 1: Set Up Log Rotation

sudo nano /etc/logrotate.d/arbiter

Add:

/var/log/nginx/arbiter-*.log {
    daily
    missingok
    rotate 14
    compress
    delaycompress
    notifempty
    create 0640 www-data adm
    sharedscripts
    postrotate
        systemctl reload nginx > /dev/null
    endscript
}

Step 2: Create Monitoring Script (Optional)

nano /home/architect/arbiter/monitor.sh

Add:

#!/bin/bash
STATUS=$(curl -s https://discord-bot.firefrostgaming.com/health | jq -r '.discord')
if [ "$STATUS" != "ok" ]; then
    echo "Arbiter health check failed at $(date)" >> /home/architect/arbiter/monitor.log
    sudo systemctl restart arbiter
fi

Make executable:

chmod +x /home/architect/arbiter/monitor.sh

Schedule (every 5 minutes):

crontab -e

Add:

*/5 * * * * /home/architect/arbiter/monitor.sh

🎉 Deployment Complete!

Final Checklist

  • Application running (sudo systemctl status arbiter)
  • Health check returns "ok" for all services
  • Test webhook received and logged
  • Test OAuth flow completes successfully
  • Admin panel accessible and functional
  • Backups scheduled and tested
  • Paymenter webhook configured
  • Logs rotating properly

Next Steps

  1. Monitor for 24 hours before announcing to users
  2. Create test subscription with real Paymenter flow
  3. Verify email delivery reaches inbox (not spam)
  4. Test all subscription events (upgrade, downgrade, cancel)
  5. Train Trinity members on admin panel usage
  6. Update documentation with any deployment-specific notes

Rollback Plan (If Issues Occur)

# Stop service
sudo systemctl stop arbiter

# Disable service
sudo systemctl disable arbiter

# Remove Nginx config
sudo rm /etc/nginx/sites-enabled/arbiter
sudo systemctl reload nginx

# Application files remain in /home/architect/arbiter for debugging

📞 Support Contacts

System Administrator: Michael (The Wizard)
Implementation Partner: Claude (Chronicler #49)
Architecture Consultant: Gemini AI

Documentation: /home/architect/arbiter/README.md
Troubleshooting: /home/architect/arbiter/TROUBLESHOOTING.md


🔥❄️ Deployment completed by Chronicler #49 on March 30, 2026 💙