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>
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/callbackhttps://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_idcreated (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
architectexists 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:
- Go to Discord server
- Settings → Roles
- 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
- Visit
https://discord-bot.firefrostgaming.com/admin/loginin browser - Should redirect to Discord OAuth
- Authorize with Trinity Discord account
- Should redirect to admin panel
- Verify search, assign functions work
Step 5: End-to-End OAuth Test
Create test member in Ghost CMS:
- Ghost Admin → Members → New Member
- Email:
test@firefrostgaming.com - 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:
- Visit link in browser
- Should redirect to Discord OAuth
- Authorize with test Discord account
- Should show success page
- Check Discord - test account should have "The Awakened" role
- Check Ghost Admin - test member should have
discord_idpopulated
💾 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
- Log in to Paymenter admin panel
- Navigate to: System → Webhooks
- Click "Add Webhook"
- URL:
https://discord-bot.firefrostgaming.com/webhook/billing - Secret: (use value from
.envWEBHOOK_SECRET) - Events: Select:
subscription.createdsubscription.upgradedsubscription.downgradedsubscription.cancelled
- Save webhook
Step 2: Test Paymenter Webhook
From Paymenter admin:
- Find webhook in list
- Click "Test Webhook"
- 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
- Monitor for 24 hours before announcing to users
- Create test subscription with real Paymenter flow
- Verify email delivery reaches inbox (not spam)
- Test all subscription events (upgrade, downgrade, cancel)
- Train Trinity members on admin panel usage
- 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 💙