WHAT WAS DONE:
Added comprehensive versioning and changelog for legacy documentation
VERSION FILES ADDED:
- VERSION (single line: 2.0.0)
- CHANGELOG.md (complete version history and semantic versioning guide)
CODE UPDATES:
- src/index.js: Added version constant and header comment
- package.json: Updated version from 1.0.0 to 2.0.0
- Health check endpoint now returns version in JSON response
CHANGELOG CONTENTS:
- Full v2.0.0 release notes with all features
- v1.0.0 legacy documentation (retired)
- Semantic versioning guide for future releases
- Version history summary table
- Examples of future MAJOR/MINOR/PATCH releases
VERSION CONSTANT:
```javascript
const VERSION = '2.0.0';
```
HEALTH CHECK NOW RETURNS:
```json
{
"version": "2.0.0",
"uptime": 123.456,
"discord": "ok",
"database": "ok",
"timestamp": "2026-03-30T15:00:00.000Z"
}
```
ARBITER VERSION HISTORY:
- Arbiter 1.0.0 (Unknown date - March 30, 2026)
- Basic webhook receiver
- Manual role assignment
- Holly's admin config panel
- Status: RETIRED
- Arbiter 2.0.0 (March 30, 2026 - Present)
- Complete OAuth soft gate system
- Automated subscriber flow
- Manual admin interface
- Ghost CMS integration
- Full audit logging
- Enhanced security
- Status: CURRENT
WHY THIS MATTERS:
"Documentation is king for legacy" - proper versioning ensures future
Chroniclers and team members can understand system evolution, track
changes, and maintain backward compatibility. This is infrastructure
built to last.
SEMANTIC VERSIONING:
- MAJOR (X.0.0): Breaking changes
- MINOR (2.X.0): New features, backward compatible
- PATCH (2.0.X): Bug fixes, backward compatible
FILES MODIFIED:
- docs/implementation/discord-oauth-arbiter/VERSION (new)
- docs/implementation/discord-oauth-arbiter/CHANGELOG.md (new)
- docs/implementation/discord-oauth-arbiter/src/index.js (version header)
- docs/implementation/discord-oauth-arbiter/package.json (version bump)
DOCUMENTATION FOR FUTURE:
CHANGELOG.md includes examples of what would constitute future
2.0.1 (patch), 2.1.0 (minor), and 3.0.0 (major) releases, guiding
future development and maintenance.
Built with love for children not yet born.
Signed-off-by: Claude (Chronicler #49) <claude@firefrostgaming.com>
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:
- User subscribes via Paymenter (billing system)
- Webhook fires to Arbiter
- Email sent with secure 24-hour linking URL
- User clicks link → Discord OAuth → Role assigned automatically
- 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.dbandsessions.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)
- 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_idcreated in Ghost Admin - Integration created for Admin API key
- Custom field
- 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
cd /home/architect
git clone <repository-url> arbiter
cd arbiter
npm install
2. Configure Environment Variables
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:
nano config/roles.json
Get role IDs: Right-click role in Discord → Copy ID
4. Set Up Nginx Reverse Proxy
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
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:
sudo systemctl status arbiter
Check logs:
sudo journalctl -u arbiter -f
Visit health check:
curl https://discord-bot.firefrostgaming.com/health
Should return:
{
"uptime": 123.456,
"discord": "ok",
"database": "ok",
"timestamp": "2026-03-30T15:00:00.000Z"
}
7. Set Up Automated Backups
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:
mkdir -p /home/architect/backups/arbiter
chmod 700 /home/architect/backups/arbiter
🔧 Configuration
Discord Developer Portal Setup
- Go to Discord Developer Portal
- Create New Application (or select existing)
- Bot Tab:
- Generate bot token (save to
.envasDISCORD_BOT_TOKEN) - Enable "Server Members Intent"
- Generate bot token (save to
- 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)
- Add Redirect URIs:
- OAuth2 → URL Generator:
- Scopes:
bot - Permissions:
Manage Roles - Copy generated URL and invite bot to server
- Scopes:
CRITICAL: In Discord Server Settings → Roles, drag the bot's role ABOVE all subscription tier roles!
Ghost CMS Setup
-
Create Custom Field:
- Navigate to: Settings → Membership → Custom Fields
- Add field:
discord_id(type: Text)
-
Generate Admin API Key:
- Navigate to: Settings → Integrations → Add Custom Integration
- Name: "Firefrost Arbiter"
- Copy Admin API Key (format:
key_id:secret) - Save to
.envasCMS_ADMIN_KEY
Paymenter Webhook Configuration
- In Paymenter admin panel, navigate to Webhooks
- Add new webhook:
- URL:
https://discord-bot.firefrostgaming.com/webhook/billing - Secret: (generate secure random string, save to
.envasWEBHOOK_SECRET) - Events:
subscription.created,subscription.upgraded,subscription.downgraded,subscription.cancelled
- URL:
📖 Usage
For Subscribers (Automated Flow)
- User subscribes via Paymenter
- User receives email with secure linking URL
- User clicks link → redirected to Discord OAuth
- User authorizes → role automatically assigned
- User sees new channels in Discord immediately
For Admins (Manual Assignment)
- Visit
https://discord-bot.firefrostgaming.com/admin/login - Authenticate via Discord
- Search user by email (from Ghost CMS)
- Assign role or remove all roles
- Provide reason (logged to audit trail)
- View audit log of all manual actions
🧪 Testing
Local Testing Setup
- Set
APP_URL=http://localhost:3500in.env - Add
http://localhost:3500/auth/callbackto Discord redirect URIs - Run in development mode:
npm run dev
Test Webhook Reception
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
- Trigger webhook (above) to generate token
- Check database:
sqlite3 linking.db "SELECT * FROM link_tokens;" - Check email sent (Mailcow logs)
- Click link in email
- Complete Discord OAuth
- Verify role assigned in Discord
- Verify Ghost CMS updated: Ghost Admin → Members → search email
Test Admin Panel
- Visit
/admin/login - Authenticate with Discord
- Search for test user by email
- Assign/remove role
- 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 700on backup directory
🛠️ Maintenance
View Logs
# 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
sudo systemctl restart arbiter
Update Application
cd /home/architect/arbiter
git pull
npm install
sudo systemctl restart arbiter
Database Maintenance
# 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 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 proxysetting 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:
./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
# 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:
sqlite3 /home/architect/backups/arbiter/linking_20260330_040000.db "SELECT count(*) FROM link_tokens;"
📚 Documentation
- DEPLOYMENT.md - Complete deployment guide
- TROUBLESHOOTING.md - Common issues and solutions
- API.md - API endpoint documentation (if created)
🤝 Contributing
This is a private system for Firefrost Gaming. For internal team members:
- Create feature branch
- Test locally
- Commit with detailed messages
- Deploy to staging first
- 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 💙