docs: Document Gemini consultation on firefrost-services monorepo architecture
WHAT WAS DONE: - Created comprehensive architectural decision record - Documented Gemini's recommendation: Git tags over directory-based versions - Captured approved repository structure with npm workspaces - Documented deployment workflow including detached HEAD handling - Recorded systemd configuration requirements - Established environment variables security strategy - Documented multi-server deployment process WHY: - Preserve critical architectural decisions from Gemini consultation - Provide reference for firefrost-services implementation - Document approved patterns and anti-patterns - Support future Chroniclers and staff onboarding - Validate approach aligns with 'decades not months' philosophy FILES: - docs/reference/architecture-decisions/firefrost-services-monorepo-decision.md (new, 459 lines) Signed-off-by: The Golden Chronicler <claude@firefrostgaming.com>
This commit is contained in:
@@ -0,0 +1,428 @@
|
||||
# Gemini Consultation: firefrost-services Monorepo Architecture
|
||||
## Architectural Decision Record
|
||||
|
||||
**Date:** March 31, 2026
|
||||
**Participants:** Michael (The Wizard), Claude (The Golden Chronicler #50), Gemini (Architectural Consultant)
|
||||
**Topic:** Monorepo structure and version management strategy for Firefrost Gaming backend services
|
||||
**Decision Status:** ✅ APPROVED — Ready for implementation
|
||||
**Related Task:** Task #87 (Arbiter 2.1 deployment will be first service in new structure)
|
||||
|
||||
---
|
||||
|
||||
## Context
|
||||
|
||||
Firefrost Gaming has developed multiple custom Node.js backend services:
|
||||
- **Arbiter** — Discord bot for subscription automation and role management
|
||||
- **Whitelist Manager** — Pterodactyl Panel integration for Minecraft whitelist automation
|
||||
- **Modpack Version Checker** — Monitors modpack updates across game servers
|
||||
- **Future services** — Additional tools and integrations planned
|
||||
|
||||
These services currently exist as separate, unversioned deployments. We need a sustainable monorepo structure that:
|
||||
- Supports remote management from RV (September 2027 goal)
|
||||
- Works with simple `git pull` workflow (Chromebook accessibility requirement)
|
||||
- Handles service-specific versioning
|
||||
- Enables code sharing between services
|
||||
- Aligns with industry best practices
|
||||
|
||||
---
|
||||
|
||||
## The Question
|
||||
|
||||
**Should we use directory-based version archives or Git tags for version management?**
|
||||
|
||||
### Approach A: Directory-Based Versions
|
||||
```
|
||||
firefrost-services/
|
||||
├── arbiter/
|
||||
│ ├── current/
|
||||
│ ├── v2.1/
|
||||
│ ├── v2.0/
|
||||
│ └── archive/
|
||||
```
|
||||
|
||||
**Pros:** Visual history, easy comparison
|
||||
**Cons:** Code duplication, breaks Node.js module resolution, anti-pattern
|
||||
|
||||
### Approach B: Git Tags for Versions
|
||||
```
|
||||
firefrost-services/
|
||||
├── arbiter/
|
||||
│ ├── src/
|
||||
│ └── package.json
|
||||
```
|
||||
|
||||
With service-prefixed tags: `arbiter-v2.1.0`, `whitelist-v1.0.0`
|
||||
|
||||
**Pros:** Native Git features, clean tree, industry standard
|
||||
**Cons:** Less visible (but proper use of Git)
|
||||
|
||||
---
|
||||
|
||||
## Gemini's Recommendation
|
||||
|
||||
**✅ Approach B (Git Tags) is the definitive winner.**
|
||||
|
||||
### Key Reasoning
|
||||
|
||||
**Directory-based versioning is an anti-pattern:**
|
||||
- Bloats repository size
|
||||
- Makes line-by-line change tracking impossible
|
||||
- Breaks standard Node.js module resolution
|
||||
- Not how Git is designed to work
|
||||
|
||||
**Git tags are purpose-built for versioning:**
|
||||
- Clean codebase structure
|
||||
- Full history via `git log`
|
||||
- Easy rollbacks via `git checkout <tag>`
|
||||
- Industry standard approach
|
||||
|
||||
---
|
||||
|
||||
## Approved Architecture
|
||||
|
||||
### Repository Structure
|
||||
|
||||
```
|
||||
firefrost-services/
|
||||
├── package.json # Root workspace configuration
|
||||
├── package-lock.json
|
||||
├── .gitignore
|
||||
├── README.md # Monorepo overview
|
||||
├── services/
|
||||
│ ├── arbiter/
|
||||
│ │ ├── src/
|
||||
│ │ │ └── index.js
|
||||
│ │ ├── deploy/
|
||||
│ │ │ └── arbiter.service
|
||||
│ │ ├── .env.example
|
||||
│ │ ├── package.json
|
||||
│ │ └── README.md
|
||||
│ ├── whitelist-manager/
|
||||
│ └── modpack-version-checker/
|
||||
├── shared/
|
||||
│ ├── src/
|
||||
│ │ ├── utils/
|
||||
│ │ └── logger/
|
||||
│ ├── package.json # Named "@firefrost/shared"
|
||||
│ └── README.md
|
||||
└── future/ # Experimental ideas
|
||||
└── README.md
|
||||
```
|
||||
|
||||
### Version Management Strategy
|
||||
|
||||
**Service-Prefixed Git Tags:**
|
||||
- Arbiter releases: `arbiter-v2.1.0`, `arbiter-v2.2.0`
|
||||
- Whitelist Manager: `whitelist-v1.0.0`, `whitelist-v1.1.0`
|
||||
- Modpack Checker: `modpack-v1.0.0`
|
||||
|
||||
This solves the "Service A is v2.1, Service B is v1.0" problem perfectly.
|
||||
|
||||
### npm Workspaces Configuration
|
||||
|
||||
**Root package.json:**
|
||||
```json
|
||||
{
|
||||
"name": "firefrost-services",
|
||||
"private": true,
|
||||
"workspaces": [
|
||||
"services/*",
|
||||
"shared"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Shared package (shared/package.json):**
|
||||
```json
|
||||
{
|
||||
"name": "@firefrost/shared",
|
||||
"version": "1.0.0",
|
||||
"main": "src/index.js"
|
||||
}
|
||||
```
|
||||
|
||||
**Service dependency (services/arbiter/package.json):**
|
||||
```json
|
||||
{
|
||||
"name": "@firefrost/arbiter",
|
||||
"dependencies": {
|
||||
"discord.js": "^14.0.0",
|
||||
"@firefrost/shared": "*"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Key benefit:** Single `npm install` at root handles all services and shared code automatically.
|
||||
|
||||
---
|
||||
|
||||
## Deployment Workflow
|
||||
|
||||
### Standard Deployment (Update to Newer Version)
|
||||
|
||||
```bash
|
||||
# On the server running Arbiter
|
||||
cd /var/www/firefrost-services
|
||||
|
||||
# Step 1: Fetch latest tags
|
||||
git fetch --all --tags
|
||||
|
||||
# Step 2: Check out specific version
|
||||
git checkout arbiter-v2.1.0
|
||||
|
||||
# Step 3: Install dependencies
|
||||
npm install
|
||||
|
||||
# Step 4: Restart service
|
||||
sudo systemctl restart arbiter
|
||||
```
|
||||
|
||||
### Rollback Workflow
|
||||
|
||||
```bash
|
||||
# Step 1: Return to main branch
|
||||
git checkout main
|
||||
|
||||
# Step 2: Pull latest
|
||||
git pull origin main
|
||||
|
||||
# Step 3: Check out rollback version
|
||||
git checkout arbiter-v2.0.0
|
||||
|
||||
# Step 4: Restart service
|
||||
sudo systemctl restart arbiter
|
||||
```
|
||||
|
||||
### Critical: Detached HEAD State
|
||||
|
||||
When you `git checkout <tag>`, Git enters "detached HEAD" state (static snapshot).
|
||||
|
||||
**Important:** You cannot run `git pull` while in detached HEAD state.
|
||||
|
||||
To update, you must return to main branch first, pull, then checkout the new tag.
|
||||
|
||||
---
|
||||
|
||||
## systemd Configuration
|
||||
|
||||
**Correct configuration for monorepo services:**
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=Arbiter Discord Bot
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=arbiter
|
||||
WorkingDirectory=/var/www/firefrost-services/services/arbiter
|
||||
ExecStart=/usr/bin/node src/index.js
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
**Key change from standalone deployment:**
|
||||
- `WorkingDirectory` set to service folder (not repo root)
|
||||
- `ExecStart` uses relative path from WorkingDirectory
|
||||
- This ensures proper file resolution and environment variable loading
|
||||
|
||||
---
|
||||
|
||||
## Environment Variables Strategy
|
||||
|
||||
**Approved approach:** `.env.example` committed + `.gitignore` for actual `.env`
|
||||
|
||||
### Implementation
|
||||
|
||||
1. **Create `.env.example` in each service directory** (committed to Git):
|
||||
```
|
||||
DISCORD_TOKEN=your_token_here
|
||||
PAYMENTER_WEBHOOK_SECRET=your_secret_here
|
||||
DATABASE_URL=postgresql://user:pass@host:port/db
|
||||
```
|
||||
|
||||
2. **Add `.env` to root `.gitignore`** (prevents accidental commits):
|
||||
```
|
||||
.env
|
||||
node_modules/
|
||||
```
|
||||
|
||||
3. **On server, manually create actual `.env`** in service directory with real credentials
|
||||
|
||||
**Why this works:**
|
||||
- ✅ Secrets never committed to Git
|
||||
- ✅ Configuration template visible to developers
|
||||
- ✅ Industry standard approach
|
||||
- ✅ Easy to document what each service needs
|
||||
|
||||
---
|
||||
|
||||
## Multi-Server Deployment
|
||||
|
||||
**Scenario:** Arbiter runs on Server A, Whitelist Manager runs on Server B
|
||||
|
||||
### Process
|
||||
|
||||
1. **Clone entire repo to both servers:**
|
||||
```bash
|
||||
# Server A (Command Center)
|
||||
git clone https://token@git.firefrostgaming.com/firefrost-gaming/firefrost-services.git
|
||||
|
||||
# Server B (Panel VPS)
|
||||
git clone https://token@git.firefrostgaming.com/firefrost-gaming/firefrost-services.git
|
||||
```
|
||||
|
||||
2. **Server A deployment (Arbiter only):**
|
||||
```bash
|
||||
cd firefrost-services
|
||||
git checkout arbiter-v2.1.0
|
||||
npm install
|
||||
# Create services/arbiter/.env with actual tokens
|
||||
sudo systemctl enable arbiter
|
||||
sudo systemctl start arbiter
|
||||
```
|
||||
|
||||
3. **Server B deployment (Whitelist Manager only):**
|
||||
```bash
|
||||
cd firefrost-services
|
||||
git checkout whitelist-v1.0.0
|
||||
npm install
|
||||
# Create services/whitelist-manager/.env with actual tokens
|
||||
sudo systemctl enable whitelist-manager
|
||||
sudo systemctl start whitelist-manager
|
||||
```
|
||||
|
||||
**Result:** Entire codebase exists on both servers, but only the specific service configured in systemd runs on each.
|
||||
|
||||
---
|
||||
|
||||
## Key Principles Established
|
||||
|
||||
### 1. Simplicity Over Complexity
|
||||
- Native npm workspaces (no Lerna, Nx, or Turborepo)
|
||||
- Standard Git features (no custom versioning tools)
|
||||
- Direct systemd management (no PM2 or other process managers)
|
||||
|
||||
### 2. Security First
|
||||
- Secrets never committed to Git
|
||||
- `.env.example` templates for documentation
|
||||
- Manual `.env` creation on servers
|
||||
|
||||
### 3. Service Independence
|
||||
- Services never import from other services directly
|
||||
- Shared code lives in `@firefrost/shared` package
|
||||
- Each service can version independently
|
||||
|
||||
### 4. Sustainability
|
||||
- Structure supports decades-long maintenance
|
||||
- Simple enough to manage remotely from RV
|
||||
- Clear rollback procedures
|
||||
|
||||
### 5. Accessibility
|
||||
- Works with `git pull` workflow (Chromebook compatible)
|
||||
- Minimal typing required for deployments
|
||||
- Clear, documented procedures
|
||||
|
||||
---
|
||||
|
||||
## Anti-Patterns to Avoid
|
||||
|
||||
**Gemini explicitly warned against:**
|
||||
|
||||
1. **Service interdependency** — Never import code directly from other services. Use `@firefrost/shared` instead.
|
||||
|
||||
2. **Over-complicated tooling** — Don't reach for Lerna/Nx/Turborepo for a small team. Standard npm workspaces are sufficient.
|
||||
|
||||
3. **Stale systemd paths** — Always update `WorkingDirectory` and `ExecStart` to match new monorepo structure.
|
||||
|
||||
4. **Secrets in Git** — Never put actual `.env` files in repository, even temporarily.
|
||||
|
||||
---
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
### Phase 1: Repository Setup
|
||||
1. Create `firefrost-services` repo in Gitea
|
||||
2. Set up directory structure
|
||||
3. Create root `package.json` with workspaces
|
||||
4. Add `.gitignore` and README
|
||||
|
||||
### Phase 2: Arbiter 2.1 Migration (First Service)
|
||||
1. Move Arbiter 2.1 code into `services/arbiter/`
|
||||
2. Create `@firefrost/shared` package
|
||||
3. Update systemd configuration
|
||||
4. Test deployment workflow
|
||||
5. Tag as `arbiter-v2.1.0`
|
||||
|
||||
### Phase 3: Additional Services
|
||||
1. Migrate Whitelist Manager
|
||||
2. Migrate Modpack Version Checker
|
||||
3. Tag each with appropriate versions
|
||||
|
||||
### Phase 4: Future Development
|
||||
1. Add experimental services to `future/`
|
||||
2. Document service creation process
|
||||
3. Establish CI/CD if needed
|
||||
|
||||
---
|
||||
|
||||
## Success Metrics
|
||||
|
||||
**This architecture is successful if:**
|
||||
- ✅ Single `npm install` deploys all services
|
||||
- ✅ Rollbacks take <5 minutes
|
||||
- ✅ New services can be added without restructuring
|
||||
- ✅ Documentation is clear enough for future staff
|
||||
- ✅ Michael can manage from RV in 2027
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- **Task #87:** `docs/tasks/arbiter-2-1-cancellation-flow/README.md` (Arbiter 2.1 architecture)
|
||||
- **Gemini Session Transcript:** This document
|
||||
- **Operations Manual:** `docs/core/infrastructure-manifest.md` (will be updated post-deployment)
|
||||
|
||||
---
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
**Gemini's contributions:**
|
||||
- Validated Git tags as industry standard
|
||||
- Explained npm workspaces mechanics
|
||||
- Caught systemd `WorkingDirectory` requirement
|
||||
- Recommended `.env.example` + `.gitignore` security pattern
|
||||
- Documented detached HEAD workflow clearly
|
||||
|
||||
**Chronicler #49's contributions:**
|
||||
- Documented Arbiter 2.1 architecture
|
||||
- Identified need for monorepo structure
|
||||
- Established "We Don't Kick People Out" philosophy
|
||||
|
||||
---
|
||||
|
||||
## Final Decision
|
||||
|
||||
**APPROVED: Proceed with Approach B (Git Tags + npm Workspaces)**
|
||||
|
||||
Repository location: `git.firefrostgaming.com/firefrost-gaming/firefrost-services`
|
||||
|
||||
First deployment: Arbiter 2.1 (validates workflow, tests monorepo structure)
|
||||
|
||||
**For children not yet born.** 💙🔥❄️
|
||||
|
||||
---
|
||||
|
||||
**Document Status:** ✅ APPROVED
|
||||
**Implementation Status:** Ready to begin
|
||||
**Next Action:** Create `firefrost-services` repository in Gitea
|
||||
**Responsible:** The Golden Chronicler (#50) + Michael (The Wizard)
|
||||
|
||||
**Documented by:** The Golden Chronicler (Chronicler #50)
|
||||
**Date:** March 31, 2026
|
||||
**Session:** Gemini architectural consultation on monorepo strategy
|
||||
Reference in New Issue
Block a user