201 lines
5.0 KiB
Markdown
201 lines
5.0 KiB
Markdown
# Session Summary – Modrinth Mod Install Integration
|
||
|
||
**Date:** 2026-02-23
|
||
**Type:** Foundational Architecture + Implementation
|
||
**Status:** Core flow complete, polish pending
|
||
|
||
---
|
||
|
||
## Objective
|
||
|
||
Implement automated mod installation from Modrinth into Minecraft game containers via:
|
||
|
||
```
|
||
Portal → API → Agent → Filesystem
|
||
```
|
||
|
||
With security controls, validation, and production-grade guardrails.
|
||
|
||
---
|
||
|
||
## What Was Completed
|
||
|
||
### 1. Modrinth Resolver (API Side)
|
||
|
||
Implemented `resolveCompatibleVersion()`:
|
||
|
||
- Fetches project versions from Modrinth API
|
||
- Filters by loader (`neoforge`, `fabric`, `quilt`, etc.) and game version (e.g. `1.21.4`)
|
||
- Excludes alpha builds
|
||
- Sorts by publish date (newest first)
|
||
- Selects primary file and extracts `filename`, `downloadUrl`, `sha512` (preferred), `sha1` (fallback), `size`
|
||
|
||
Hardened:
|
||
- `.jar` only enforcement
|
||
- Host allowlist (`cdn.modrinth.com`)
|
||
- Required hash presence validation
|
||
- Proper `400` mapping for "not found" and "no compatible version"
|
||
|
||
### 2. Engine Metadata Bug Fix
|
||
|
||
**Before (broken):**
|
||
```
|
||
engineType = "minecraft"
|
||
engineVersion = "neoforge-1.21.4"
|
||
```
|
||
|
||
**After (correct):**
|
||
```
|
||
engineType = "neoforge"
|
||
engineVersion = "1.21.4"
|
||
```
|
||
|
||
Loader filtering now works correctly. The version string no longer includes the loader prefix.
|
||
|
||
### 3. API → Agent Payload Contract Fix
|
||
|
||
**API was sending:**
|
||
```json
|
||
{
|
||
"fileName": "...",
|
||
"downloadUrl": "...",
|
||
"size": 12345,
|
||
"sha512": "..."
|
||
}
|
||
```
|
||
|
||
**Agent expected:**
|
||
```json
|
||
{
|
||
"source": "modrinth",
|
||
"download_url": "...",
|
||
"filename": "...",
|
||
"sha512": "..."
|
||
}
|
||
```
|
||
|
||
Mismatch caused `invalid mod_id` errors. API payload corrected to match agent contract.
|
||
|
||
### 4. Agent Host Allowlist Expanded
|
||
|
||
Added `cdn.modrinth.com` alongside existing `artifacts.zerolaghub.com`.
|
||
|
||
Redirect policy still enforced:
|
||
- HTTPS only
|
||
- Must resolve to allowed host
|
||
- Max 3 redirects
|
||
|
||
### 5. Agent Filename Validation Updated
|
||
|
||
Modrinth filenames contain `+` (e.g. `sodium-neoforge-0.6.13+mc1.21.4.jar`).
|
||
|
||
Updated allowed character set from `[a-zA-Z0-9._-]` to `[a-zA-Z0-9._+-]`.
|
||
|
||
Still blocks: `/`, `\`, whitespace, path traversal, control characters.
|
||
|
||
### 6. End-to-End Install Verified
|
||
|
||
Mod successfully:
|
||
- Downloaded from Modrinth CDN
|
||
- SHA verified
|
||
- Written to `/tmp/zlh-agent/mods`
|
||
- Moved to `/opt/zlh/minecraft/neoforge/world/mods/`
|
||
- Ownership set to `minecraft`, permissions `0644`
|
||
|
||
Second install correctly returned `mod already exists` — duplicate protection confirmed working.
|
||
|
||
---
|
||
|
||
## Security Controls Now In Place
|
||
|
||
**Agent level:**
|
||
- HTTPS-only downloads
|
||
- Host allowlist enforcement
|
||
- Redirect limit (max 3)
|
||
- 200MB hard cap
|
||
- SHA256 verification
|
||
- Filename sanitization
|
||
- Duplicate prevention
|
||
- Ownership enforcement (`minecraft:minecraft`)
|
||
- Temp file cleanup on failure
|
||
- Controlled mod directory write path
|
||
- Cache invalidation
|
||
|
||
**API level:**
|
||
- Auth + ownership enforcement
|
||
- VMID validation
|
||
- Timeout protection with `AbortController`
|
||
- Resolver filtering (loader + version + stability)
|
||
- Correct payload contract to agent
|
||
|
||
---
|
||
|
||
## Current System State
|
||
|
||
| Component | Status |
|
||
|-----------|--------|
|
||
| Modrinth resolver | ✅ Stable |
|
||
| API route | ✅ Working |
|
||
| Agent integration | ✅ Working |
|
||
| Install flow | ✅ Confirmed |
|
||
| Duplicate protection | ✅ Working |
|
||
| Security model | ✅ Intact |
|
||
|
||
---
|
||
|
||
## Remaining Issues
|
||
|
||
### Response Corruption (Needs Verification)
|
||
|
||
One early `curl` output appeared corrupted — part of the curl command echoed in the response. Needs clean reproduction to determine root cause:
|
||
|
||
```bash
|
||
curl -sS -D headers.txt -o body.txt ...
|
||
```
|
||
|
||
Could be: terminal wrap artifact, API returning raw non-JSON, agent returning malformed error, or logging leaking into response body.
|
||
|
||
**Reproduce before wiring portal.**
|
||
|
||
### API Error Mapping Refinement
|
||
|
||
Currently all agent non-2xx responses return `502`. Should improve:
|
||
|
||
- `"mod already exists"` → `409 Conflict`
|
||
|
||
Small API enhancement, low effort.
|
||
|
||
---
|
||
|
||
## Next Steps
|
||
|
||
1. **Reproduce and resolve** response corruption issue with clean curl test
|
||
2. **Tighten API error mapping** (`409` for duplicate, etc.)
|
||
3. **Wire install endpoint to portal UI** — display `Installing`, `Already installed`, `Failed (reason)`
|
||
4. **Begin dev-server linking system** (see `docs/architecture/dev-to-game-artifact-pipeline.md`)
|
||
|
||
---
|
||
|
||
## Future Work (Not Blocked On)
|
||
|
||
- Automatic server restart after install
|
||
- Update detection and mod version upgrade logic
|
||
- Rollback integration (snapshot + shadow — see `docs/architecture/mod-deployment-safety.md`)
|
||
- Mod state tracking in DB
|
||
- Curated artifact coordinator
|
||
|
||
---
|
||
|
||
## Strategic Note
|
||
|
||
This session was not routine bug fixing. The work done today:
|
||
|
||
- Aligned contract layers across API and agent
|
||
- Corrected the loader/version metadata architecture
|
||
- Established a secure mod ingestion path
|
||
- Created the foundation for the full mod management system
|
||
|
||
The architecture decisions locked here (host allowlist, filename validation, channel separation, payload contract) will apply to every future mod install path including dev promotion, curated updates, and self-healing rollback.
|
||
|
||
**This was a foundational milestone.**
|