# Filesystem and File Browser Architecture **Updated:** 2026-03-01 **Status:** Active — reflects implemented upload model --- ## Overview ZeroLagHub uses a strict, runtime-aligned filesystem model for game containers. The file browser reflects the real runtime layout and does not use staging directories, shadow folders, or symlink-based indirection. Uploads are written directly into the runtime directory tree with strict policy enforcement and provenance tracking. --- ## Runtime Root Each game container has a single runtime root: ``` /opt/zlh/minecraft/// ``` All file operations are resolved relative to this root. The agent is the only component allowed to perform filesystem mutations. --- ## Hidden Internal Files The following files are internal and never exposed through the file API: - `.zlh-shadow` - `.zlh_metadata.json` These are filtered centrally inside the agent `internal/files` package, not in route handlers. --- ## Upload Model ### No Staging Uploads are written directly into their final runtime location. There is: - ❌ No temporary upload folder - ❌ No symlink-based deployment - ❌ No background relocation job Uploads are atomic: 1. Write to temp file in same directory 2. `os.Rename()` to final path --- ## Upload Allowlist Uploads are restricted to: | Type | Path | Extension | |------|------|-----------| | Mods | `mods/.jar` | `.jar` | | Datapacks | `world/datapacks/.zip` | `.zip` | All other upload paths are rejected. Parent directory must already exist. No directory creation is performed. --- ## Security Constraints Upload is rejected if: - Path traversal (`../`) - Absolute path - Control characters - Target exists as directory - Target is a symlink - Target resolves outside runtime root - Path not allowlisted - Parent directory missing --- ## Provenance Metadata Uploads create or update `.zlh_metadata.json`, stored at runtime root. **Example:** ```json { "mods/sodium.jar": { "source": "user", "uploaded_at": "2026-03-01T22:37:01Z" } } ``` ### stat Response Extension File stat now includes: ``` "source": "user" | null ``` Only `"user"` is currently written by the agent. No curated inference is performed. --- ## File Browser Responsibilities ### Portal (Frontend) - Displays files - Displays `source: "user"` badge - Enforces extension pre-validation - Sends multipart upload ### API - Auth + ownership enforcement - Streams upload to agent - Does **NOT** inspect file contents - Does **NOT** re-implement upload policy ### Agent - Enforces filesystem rules - Writes metadata - Performs atomic writes - Returns authoritative status --- ## Why No Symlinks Symlink-based deployment was rejected because: - It complicates mod loading behavior - Breaks compatibility with server-side mod loaders - Introduces unexpected runtime indirection - Makes provenance ambiguous Direct writes + metadata is simpler and safer. --- ## Data Flow ``` Browser → API → Agent → Filesystem ``` The API uses raw `http.request` piping to stream uploads. It does not buffer large files in memory. --- ## API Transport Considerations The API acts strictly as a streaming proxy. Implementation uses raw `http.request` piping: ```js req.pipe(proxyReq) proxyRes.pipe(res) ``` This avoids: - Fetch streaming incompatibilities - Duplex locking issues - Multipart buffering problems Upload timeout for this route should be substantially larger than normal file routes. --- ## Frontend File Browser Direction ### Layout Split-pane panel — directory tree left, file detail/actions right. Slides in as a panel, does not replace the console view. Server console remains visible. ### Navigation Breadcrumb-based. Flat navigation within each directory rather than deep tree expansion. Click folder → replace view. Not expand-in-place. ### File Listing Columns: name, size, modified date, type icon, source badge (`user` for user-uploaded files). For `.jar` files: enabled / disabled / removed status. ### Actions Context menu or per-row three-dot menu. Actions: download, delete, rename. For `.jar` files: enable / disable toggle. Drag-to-upload supported, file picker fallback. ### In-Browser Editor Plain textarea or Monaco for text files (`server.properties`, `.json`, `.toml`, `.yml`, `.txt`). Binary files get download link only. Not required for launch. ### `mods-removed` Surface "Recently removed" section or toggle to show `/mods-removed` alongside active mods. Makes soft delete visible and gives users a restore path without knowing the underlying filesystem layout. ### What to Avoid - Deep expand/collapse tree for mod directory (use flat list + filter) - In-browser zip/unzip - Making the file browser the primary surface — mod manager stays primary, file browser is secondary/advanced --- ## Per-Container Web Server Decision **Do not run Nginx or Caddy per container.** The agent already runs an HTTP server. Serving static file browser assets from the agent directly keeps per-container footprint minimal. No additional process, no config management, no extra memory overhead. `zlh-proxy` handles SSL at the edge. A local web server per container adds a layer without adding capability. --- ## Dev Container File Access Handled via **WebSSH2 + SFTP**, proxied through the API. Developers get a real SSH2 session with SFTP channel. No direct container access from the browser — API proxy maintains the security boundary (DEC-008). SFTP warrants its own separate process from day one due to SSH server complexity. It does not belong in the main game agent. --- ## Resilience Notes The file agent (when extracted) should follow the same binary resilience pattern as the main agent: - Versioned release layout (`releases//`) - `current` symlink pointing to active binary - Previous version retained on disk - Systemd watchdog flips `current` back to previous on health check failure - No dependency on artifact server for rollback — local fallback only --- ## Design Philosophy The runtime directory is the source of truth. No abstraction layer should diverge from Minecraft's real file structure. --- ## Related Documents - `docs/architecture/mod-deployment-safety.md` — mod lifecycle, upload safety, rollback model - `docs/architecture/dev-to-game-artifact-pipeline.md` — dev container promotion pipeline - `docs/reference/minecraft-file-locations.md` — known files and directories by loader - `OPEN_THREADS.md` — file browser listed as next major feature - WebSSH2: `https://github.com/billchurch/webssh2` — SFTP + SSH2 for dev containers