diff --git a/SESSION_LOG/2026-02-23-modrinth-install-integration.md b/SESSION_LOG/2026-02-23-modrinth-install-integration.md new file mode 100644 index 0000000..30d3cfb --- /dev/null +++ b/SESSION_LOG/2026-02-23-modrinth-install-integration.md @@ -0,0 +1,200 @@ +# 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.**