zlh-grind/SESSION_LOG/2026-02-23-modrinth-install-integration.md

201 lines
5.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.**