# Firefrost Arbiter **Discord Role Management and Subscription OAuth Gateway** A centralized Node.js/Express service for managing Discord community roles, authenticating users via OAuth2, and processing subscription webhooks from Paymenter billing platform. --- ## ๐ŸŽฏ Purpose Firefrost Arbiter automates the entire subscription-to-Discord-role workflow: 1. **User subscribes** via Paymenter (billing system) 2. **Webhook fires** to Arbiter 3. **Email sent** with secure 24-hour linking URL 4. **User clicks link** โ†’ Discord OAuth โ†’ Role assigned automatically 5. **Ghost CMS updated** with Discord ID for future reference **Admin Interface** allows Trinity members to manually assign/remove roles, search subscribers, and view audit logs. --- ## ๐Ÿ—๏ธ Architecture Overview ### Components - **Webhook Gateway**: Receives subscription events from Paymenter, generates secure single-use tokens, dispatches notification emails via SMTP - **OAuth2 Linking**: Authenticates users via Discord, updates Ghost CMS member metadata, automatically assigns Discord server roles based on subscription tiers - **Admin Dashboard**: Protected by Discord OAuth (restricted to specific User IDs), allows staff to manually assign roles, view audit logs, and search CMS records - **State Management**: Utilizes local SQLite databases (`linking.db` and `sessions.db`) for lightweight, persistent data storage ### Tech Stack - **Runtime**: Node.js 18.x+ - **Framework**: Express 4.x - **Database**: SQLite (better-sqlite3) - **Discord**: discord.js 14.x - **CMS**: Ghost 5.x (Admin API) - **Email**: Nodemailer (SMTP) - **Session**: express-session + connect-sqlite3 - **Security**: express-rate-limit, Zod validation, HMAC webhook verification --- ## ๐Ÿ“‹ Prerequisites Before installation, ensure you have: - **Node.js 18.x or higher** installed - **A Discord Application** with Bot User created ([Discord Developer Portal](https://discord.com/developers/applications)) - Server Members Intent enabled - Bot invited to your Discord server with "Manage Roles" permission - Bot role positioned ABOVE all subscription tier roles in role hierarchy - **Ghost CMS 5.x** with Admin API access - Custom field `discord_id` created in Ghost Admin - Integration created for Admin API key - **SMTP Server** for outgoing mail (Mailcow, Gmail, SendGrid, etc.) - **Nginx** for reverse proxy and SSL termination - **SSL Certificate** (Let's Encrypt recommended) --- ## ๐Ÿš€ Installation ### 1. Clone and Install Dependencies ```bash cd /home/architect git clone arbiter cd arbiter npm install ``` ### 2. Configure Environment Variables ```bash cp .env.example .env nano .env ``` Fill in all required values: - Discord credentials (bot token, client ID/secret, guild ID) - Ghost CMS URL and Admin API key - SMTP server details - Admin Discord IDs (comma-separated, no spaces) - Generate SESSION_SECRET: `node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"` - Webhook secret from Paymenter ### 3. Configure Discord Role Mapping Edit `config/roles.json` with your Discord role IDs: ```bash nano config/roles.json ``` Get role IDs: Right-click role in Discord โ†’ Copy ID ### 4. Set Up Nginx Reverse Proxy ```bash sudo cp nginx.conf /etc/nginx/sites-available/arbiter sudo ln -s /etc/nginx/sites-available/arbiter /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl reload nginx ``` ### 5. Set Up Systemd Service ```bash sudo cp arbiter.service /etc/systemd/system/ sudo systemctl daemon-reload sudo systemctl enable arbiter sudo systemctl start arbiter ``` ### 6. Verify Installation Check service status: ```bash sudo systemctl status arbiter ``` Check logs: ```bash sudo journalctl -u arbiter -f ``` Visit health check: ```bash curl https://discord-bot.firefrostgaming.com/health ``` Should return: ```json { "uptime": 123.456, "discord": "ok", "database": "ok", "timestamp": "2026-03-30T15:00:00.000Z" } ``` ### 7. Set Up Automated Backups ```bash chmod +x backup.sh crontab -e ``` Add this line (runs daily at 4:00 AM): ``` 0 4 * * * /home/architect/arbiter/backup.sh >> /home/architect/backups/arbiter/cron_error.log 2>&1 ``` Create backup directory: ```bash mkdir -p /home/architect/backups/arbiter chmod 700 /home/architect/backups/arbiter ``` --- ## ๐Ÿ”ง Configuration ### Discord Developer Portal Setup 1. Go to [Discord Developer Portal](https://discord.com/developers/applications) 2. Create New Application (or select existing) 3. **Bot Tab**: - Generate bot token (save to `.env` as `DISCORD_BOT_TOKEN`) - Enable "Server Members Intent" 4. **OAuth2 โ†’ General**: - Add Redirect URIs: - `http://localhost:3500/auth/callback` (testing) - `https://discord-bot.firefrostgaming.com/auth/callback` (production) - `https://discord-bot.firefrostgaming.com/admin/callback` (admin login) 5. **OAuth2 โ†’ URL Generator**: - Scopes: `bot` - Permissions: `Manage Roles` - Copy generated URL and invite bot to server **CRITICAL**: In Discord Server Settings โ†’ Roles, drag the bot's role ABOVE all subscription tier roles! ### Ghost CMS Setup 1. **Create Custom Field**: - Navigate to: Settings โ†’ Membership โ†’ Custom Fields - Add field: `discord_id` (type: Text) 2. **Generate Admin API Key**: - Navigate to: Settings โ†’ Integrations โ†’ Add Custom Integration - Name: "Firefrost Arbiter" - Copy Admin API Key (format: `key_id:secret`) - Save to `.env` as `CMS_ADMIN_KEY` ### Paymenter Webhook Configuration 1. In Paymenter admin panel, navigate to Webhooks 2. Add new webhook: - URL: `https://discord-bot.firefrostgaming.com/webhook/billing` - Secret: (generate secure random string, save to `.env` as `WEBHOOK_SECRET`) - Events: `subscription.created`, `subscription.upgraded`, `subscription.downgraded`, `subscription.cancelled` --- ## ๐Ÿ“– Usage ### For Subscribers (Automated Flow) 1. User subscribes via Paymenter 2. User receives email with secure linking URL 3. User clicks link โ†’ redirected to Discord OAuth 4. User authorizes โ†’ role automatically assigned 5. User sees new channels in Discord immediately ### For Admins (Manual Assignment) 1. Visit `https://discord-bot.firefrostgaming.com/admin/login` 2. Authenticate via Discord 3. **Search user** by email (from Ghost CMS) 4. **Assign role** or remove all roles 5. **Provide reason** (logged to audit trail) 6. View **audit log** of all manual actions --- ## ๐Ÿงช Testing ### Local Testing Setup 1. Set `APP_URL=http://localhost:3500` in `.env` 2. Add `http://localhost:3500/auth/callback` to Discord redirect URIs 3. Run in development mode: ```bash npm run dev ``` ### Test Webhook Reception ```bash curl -X POST http://localhost:3500/webhook/billing \ -H "Content-Type: application/json" \ -H "x-signature: test_signature" \ -d '{ "event": "subscription.created", "customer_email": "test@example.com", "customer_name": "Test User", "tier": "awakened", "subscription_id": "test_sub_123" }' ``` ### Test OAuth Flow 1. Trigger webhook (above) to generate token 2. Check database: `sqlite3 linking.db "SELECT * FROM link_tokens;"` 3. Check email sent (Mailcow logs) 4. Click link in email 5. Complete Discord OAuth 6. Verify role assigned in Discord 7. Verify Ghost CMS updated: Ghost Admin โ†’ Members โ†’ search email ### Test Admin Panel 1. Visit `/admin/login` 2. Authenticate with Discord 3. Search for test user by email 4. Assign/remove role 5. Check audit log displays action --- ## ๐Ÿ“ Project Structure ``` arbiter/ โ”œโ”€โ”€ src/ โ”‚ โ”œโ”€โ”€ routes/ โ”‚ โ”‚ โ”œโ”€โ”€ webhook.js # Paymenter webhook handler โ”‚ โ”‚ โ”œโ”€โ”€ oauth.js # User Discord linking flow โ”‚ โ”‚ โ”œโ”€โ”€ admin.js # Admin panel routes โ”‚ โ”‚ โ””โ”€โ”€ adminAuth.js # Admin OAuth login โ”‚ โ”œโ”€โ”€ middleware/ โ”‚ โ”‚ โ”œโ”€โ”€ auth.js # Admin access control โ”‚ โ”‚ โ”œโ”€โ”€ verifyWebhook.js # HMAC signature verification โ”‚ โ”‚ โ””โ”€โ”€ validateWebhook.js # Zod schema validation โ”‚ โ”œโ”€โ”€ utils/ โ”‚ โ”‚ โ””โ”€โ”€ templates.js # HTML success/error pages โ”‚ โ”œโ”€โ”€ views/ โ”‚ โ”‚ โ””โ”€โ”€ admin.html # Admin panel UI โ”‚ โ”œโ”€โ”€ database.js # SQLite initialization โ”‚ โ”œโ”€โ”€ email.js # Nodemailer SMTP โ”‚ โ”œโ”€โ”€ discordService.js # Bot client + role management โ”‚ โ”œโ”€โ”€ cmsService.js # Ghost CMS integration โ”‚ โ””โ”€โ”€ index.js # Main application entry โ”œโ”€โ”€ config/ โ”‚ โ””โ”€โ”€ roles.json # Tier โ†’ Discord Role ID mapping โ”œโ”€โ”€ .env # Environment variables (not in git) โ”œโ”€โ”€ .env.example # Template for .env โ”œโ”€โ”€ package.json # Dependencies โ”œโ”€โ”€ backup.sh # Automated backup script โ”œโ”€โ”€ arbiter.service # Systemd service file โ””โ”€โ”€ nginx.conf # Nginx reverse proxy config ``` --- ## ๐Ÿ” Security - **Webhook Verification**: HMAC SHA256 signature validation - **Input Validation**: Zod schemas for all webhook payloads - **Rate Limiting**: 100 requests per 15 minutes per IP - **Session Security**: httpOnly, SameSite cookies - **Admin Access Control**: Discord ID whitelist via environment variable - **HTTPS Enforcement**: Nginx SSL termination with HSTS headers - **Secure Tokens**: 32-byte cryptographically random tokens (64 hex chars) - **Token Expiration**: 24-hour automatic expiry - **Database Permissions**: `chmod 700` on backup directory --- ## ๐Ÿ› ๏ธ Maintenance ### View Logs ```bash # Application logs sudo journalctl -u arbiter -f # Nginx access logs sudo tail -f /var/log/nginx/arbiter-access.log # Nginx error logs sudo tail -f /var/log/nginx/arbiter-error.log # Backup logs tail -f /home/architect/backups/arbiter/backup_log.txt ``` ### Restart Service ```bash sudo systemctl restart arbiter ``` ### Update Application ```bash cd /home/architect/arbiter git pull npm install sudo systemctl restart arbiter ``` ### Database Maintenance ```bash # View link tokens sqlite3 linking.db "SELECT * FROM link_tokens WHERE used = 0;" # View audit logs sqlite3 linking.db "SELECT * FROM audit_logs ORDER BY timestamp DESC LIMIT 10;" # Manual cleanup of expired tokens sqlite3 linking.db "DELETE FROM link_tokens WHERE created_at < datetime('now', '-1 day');" ``` --- ## ๐Ÿšจ Troubleshooting See [TROUBLESHOOTING.md](TROUBLESHOOTING.md) for detailed solutions to common issues. Quick reference: - Invalid redirect URI โ†’ Check Discord Developer Portal OAuth settings - Bot missing permissions โ†’ Check role hierarchy in Discord - Session not persisting โ†’ Check `trust proxy` setting in code - Ghost API 401 โ†’ Verify Admin API key format - Database locked โ†’ Increase timeout in database.js - Email not sending โ†’ Check SMTP credentials and port 587 firewall rule --- ## ๐Ÿ“ฆ Backup & Restore ### Backup Procedure Automated daily at 4:00 AM via cron. Manual backup: ```bash ./backup.sh ``` Backs up: - `linking.db` (tokens and audit logs) - `sessions.db` (admin sessions) - `.env` (configuration with secrets) - `config/roles.json` (tier mappings) Retention: 7 days ### Restore Procedure ```bash # Stop service sudo systemctl stop arbiter # Move corrupted database mv linking.db linking.db.corrupt # Restore from backup cp /home/architect/backups/arbiter/linking_YYYYMMDD_HHMMSS.db linking.db # Start service sudo systemctl start arbiter ``` Verify restored backup: ```bash sqlite3 /home/architect/backups/arbiter/linking_20260330_040000.db "SELECT count(*) FROM link_tokens;" ``` --- ## ๐Ÿ“š Documentation - [DEPLOYMENT.md](DEPLOYMENT.md) - Complete deployment guide - [TROUBLESHOOTING.md](TROUBLESHOOTING.md) - Common issues and solutions - [API.md](API.md) - API endpoint documentation (if created) --- ## ๐Ÿค Contributing This is a private system for Firefrost Gaming. For internal team members: 1. Create feature branch 2. Test locally 3. Commit with detailed messages 4. Deploy to staging first 5. Monitor logs before production rollout --- ## ๐Ÿ“ License Private - Firefrost Gaming Internal Use Only --- ## ๐Ÿ‘ฅ Team **Built by:** - Michael "Frostystyle" Krause (The Wizard) - Technical Lead - Claude (Chronicler #49) - Implementation Partner - Gemini AI - Architecture Consultant **For:** Firefrost Gaming Community **Date:** March 30, 2026 --- **๐Ÿ”ฅโ„๏ธ Fire + Frost + Foundation = Where Love Builds Legacy ๐Ÿ’™**