docs(consultations): Gemini hybrid detection architecture
Key decisions from consultation: - Use DaemonFileRepository for file detection (cron only, never page load) - CurseForge fingerprinting REJECTED (too resource intensive) - Single table schema (no separate config table) - Hybrid approach: auto-discovery + self-service fallback Implementation patterns included for: - DaemonFileRepository injection - manifest.json reading - Database schema additions (detection_method, is_user_overridden) Signed-off-by: Claude (Chronicler #63) <claude@firefrostgaming.com>
This commit is contained in:
139
docs/consultations/gemini-hybrid-detection-2026-04-06.md
Normal file
139
docs/consultations/gemini-hybrid-detection-2026-04-06.md
Normal file
@@ -0,0 +1,139 @@
|
||||
# Gemini Consultation: Hybrid Detection Architecture
|
||||
|
||||
**Date:** April 6, 2026
|
||||
**Topic:** ModpackChecker UX Pivot — Auto-Discovery Strategy
|
||||
**Chronicler:** #63
|
||||
|
||||
---
|
||||
|
||||
## The Question
|
||||
|
||||
ModpackChecker requires egg variables (`MODPACK_PLATFORM`, `MODPACK_ID`) that most standard eggs don't have. This creates unacceptable friction for:
|
||||
- Admins with many servers (Michael has 21)
|
||||
- BuiltByBit customers expecting plug-and-play
|
||||
|
||||
We asked Gemini to evaluate detection strategies.
|
||||
|
||||
---
|
||||
|
||||
## Gemini's Response
|
||||
|
||||
### File Detection Feasibility
|
||||
|
||||
**Yes, it's feasible** via `DaemonFileRepository`. This is the industry standard for premium Pterodactyl extensions.
|
||||
|
||||
**Security:** Completely secure. The repository uses the server's daemon token — sandboxed to `/home/container/` only.
|
||||
|
||||
**Critical Warning:** It's a network call to Wings. **Never do this on page load** — only in background cron jobs. Doing it synchronously for 20 servers will hang and timeout.
|
||||
|
||||
### CurseForge Fingerprinting
|
||||
|
||||
**Hard pass. Rejected.**
|
||||
|
||||
CurseForge fingerprinting requires hashing every `.jar` file in `mods/` folder with Murmur2. Pulling gigabytes of mod data over Wings API will:
|
||||
- Crash the daemon
|
||||
- Destroy VPS CPU
|
||||
- Be extremely slow
|
||||
|
||||
Keep it lightweight — just read manifest files.
|
||||
|
||||
### Database Schema
|
||||
|
||||
**Do NOT create a separate config table.** It complicates joins and slows the dashboard API. Keep everything in `modpackchecker_servers`.
|
||||
|
||||
---
|
||||
|
||||
## Recommended Architecture: "Magic & Manual" Hybrid
|
||||
|
||||
### Step 1: Auto-Discovery Cron (The Magic)
|
||||
|
||||
When `CheckModpackUpdates` cron runs, execute this hierarchy for unconfigured servers:
|
||||
|
||||
1. Check Egg Variables (fastest)
|
||||
2. If missing → `DaemonFileRepository` to read `manifest.json` (CurseForge)
|
||||
3. If missing → Read `modrinth.index.json` (Modrinth)
|
||||
4. If found → Save to DB with `detection_method = 'file'`
|
||||
|
||||
### Step 2: Server Owner Self-Service (The Fallback)
|
||||
|
||||
If cron finds nothing, mark `status = 'unconfigured'`.
|
||||
|
||||
Console widget shows setup UI instead of failing:
|
||||
- **UI:** "Modpack not automatically detected." → [Configure Manually] button
|
||||
- **Action:** Modal lets server owner select platform + paste modpack ID
|
||||
- **Result:** Saves with `detection_method = 'manual'`
|
||||
|
||||
The `is_user_overridden` flag prevents cron from overwriting manual configs.
|
||||
|
||||
---
|
||||
|
||||
## Implementation Code
|
||||
|
||||
### DaemonFileRepository Injection
|
||||
|
||||
```php
|
||||
use Pterodactyl\Repositories\Wings\DaemonFileRepository;
|
||||
|
||||
public function __construct(
|
||||
private DaemonFileRepository $fileRepository
|
||||
) {}
|
||||
```
|
||||
|
||||
### CurseForge Detection
|
||||
|
||||
```php
|
||||
private function detectCurseForge(Server $server): ?string
|
||||
{
|
||||
try {
|
||||
$this->fileRepository->setServer($server);
|
||||
|
||||
// Attempt to read manifest.json
|
||||
$content = $this->fileRepository->getContent('manifest.json');
|
||||
$manifest = json_decode($content, true);
|
||||
|
||||
return $manifest['projectID'] ?? null;
|
||||
|
||||
} catch (\Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException $e) {
|
||||
// Node is offline, skip for now
|
||||
return null;
|
||||
} catch (\Exception $e) {
|
||||
// File doesn't exist or is unreadable
|
||||
return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Database Schema Additions
|
||||
|
||||
```php
|
||||
// Tracks how we found it: 'egg', 'file', or 'manual'
|
||||
$table->string('detection_method')->default('unknown');
|
||||
|
||||
// Prevents the auto-discovery cron from overwriting a user's manual fix
|
||||
$table->boolean('is_user_overridden')->default(false);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Rejected Approaches
|
||||
|
||||
| Approach | Reason |
|
||||
|----------|--------|
|
||||
| CurseForge Fingerprinting | Too resource-intensive, would crash Wings |
|
||||
| Admin-only UI mapping | Poor scalability for large hosts |
|
||||
| Separate config table | Complicates joins, slows dashboard |
|
||||
|
||||
---
|
||||
|
||||
## Decision
|
||||
|
||||
**Proceed with Hybrid Approach:**
|
||||
1. Backend auto-discovery via `DaemonFileRepository` in cron
|
||||
2. Frontend self-service modal for server owners
|
||||
3. All data in single `modpackchecker_servers` table
|
||||
|
||||
This provides "magic" for standard packs while offloading edge cases to server owners (not system admins).
|
||||
|
||||
---
|
||||
|
||||
*Fire + Frost + Foundation = Where Love Builds Legacy* 💙🔥❄️
|
||||
Reference in New Issue
Block a user