# 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* 💙🔥❄️
