DirForge
<p align="center"> <img src="img/logo.svg" alt="DirForge logo" width="120"> </p> <p align="center"> <a href="https://github.com/Dissimilis/DirForge/actions/workflows/ci.yml"><img src="https://github.com/Dissimilis/DirForge/actions/workflows/ci.yml/badge.svg" alt="CI"></a> <a href="https://github.com/Dissimilis/DirForge/releases/latest"><img src="https://img.shields.io/github/v/release/Dissimilis/DirForge?label=release" alt="Latest Release"></a> <a href="https://github.com/Dissimilis/DirForge/pkgs/container/dirforge"><img src="https://img.shields.io/badge/GHCR-dirforge-blue?logo=docker" alt="GHCR"></a> <a href="https://buymeacoffee.com/dissimilis"><img src="https://img.shields.io/badge/Buy%20Me%20a%20Coffee-ffdd00?logo=buy-me-a-coffee&logoColor=black" alt="Buy Me a Coffee"></a> </p>A stateless, read-only web file browser for homelab and NAS power users. No database, no background workers, no disk writes - just point it at a mounted path and go.
<p align="center"> <img src="img/screenshot.jpg" alt="DirForge directory listing" width="700"> </p> <p align="center"> <a href="https://dirforge.zenforge.eu/"><strong>Live Demo</strong></a> </p>Browse files in a clean web UI with search, previews, and archive inspection. Download individual files or entire folders as ZIP. Access the same data through a RESTful JSON API, an S3-compatible endpoint for tools like rclone, a read-only WebDAV mount for native OS file managers, or an MCP server for AI assistants. Everything runs in a single stateless container with no external dependencies.
Quick Start
Prerequisite: Docker or Podman (images are published for amd64 and arm64).
Docker run
docker run -d \
--name dirforge \
-p 8091:8080 \
-e RootPath=/data \
-v /srv/share:/data:ro \
ghcr.io/dissimilis/dirforge:latestDocker Compose
cp .env.example .env
# edit HOST_PATH, BasicAuthUser, and BasicAuthPass in .env
docker compose up -dWarning: The default compose file falls back to
admin/dirforgecredentials if you don't setBasicAuthUserandBasicAuthPassin your.env. Change these before exposing the service on your network.
Podman
The same commands work with Podman. Replace docker with podman:
podman run -d \
--name dirforge \
-p 8091:8080 \
-e RootPath=/data \
-v /srv/share:/data:ro \
ghcr.io/dissimilis/dirforge:latestFor Compose, use podman compose (Podman 4.1+):
cp .env.example .env
# edit HOST_PATH in .env
podman compose up -dNotes for Podman users:
podman composeis a built-in subcommand in Podman 4.1+, not the older third-partypodman-composePython package.- DirForge uses ports above 1024, so rootless Podman works without extra configuration.
- On SELinux systems (Fedora, RHEL), add
:zto volume mounts:-v /srv/share:/data:ro,z.
Standalone (Windows)
- Download
dirforge-win-x64.zipfrom the latest release - Extract to a folder (e.g.
C:\DirForge) - Edit
appsettings.json— setRootPathto the directory you want to share - Console mode: Double-click
DirForge.exeto run interactively - Windows Service: Right-click
install-service.bat→ Run as administrator. DirForge will start on boot automatically. Useuninstall-service.batto remove the service.
Standalone (Linux)
- Download
dirforge-linux-x64.tar.gz(orlinux-arm64) from the latest release - Extract:
tar -xzf dirforge-linux-x64.tar.gz -C /opt/dirforge - Edit
appsettings.json— setRootPathto the directory you want to share - Run directly:
./DirForge - Install as systemd service:
sudo useradd -r -s /usr/sbin/nologin dirforge sudo cp dirforge.service /etc/systemd/system/ # Edit /etc/systemd/system/dirforge.service to adjust paths if needed sudo systemctl daemon-reload sudo systemctl enable --now dirforge
The tarball includes an example dirforge.service file with systemd hardening options.
Open http://localhost:8091.
With the default config profile in this repository, sharing, dashboard, and metrics are enabled.
Features
Browsing
- List and grid view layouts
- Sortable columns (name, size, date)
- Light and dark themes (toggle or set default via
DefaultTheme) - File preview modal for text, images, video, audio, and PDF
- Inline archive browser for
.zip,.tar,.tar.gz,.tgz, and.gz - Image lightbox with navigation
- Recursive search with configurable depth and time budget
- Age badges on files and folders
- Custom site title via
SiteTitle
Sharing & Downloads
- Direct file downloads
- Folder download as ZIP archive (with configurable max size)
- Signed share links with expiry
- One-time share links
- QR code generation for share links
- File hash calculation (CRC32, MD5, SHA-1, SHA-256, SHA-512)
- Sidecar checksum verification (
.md5,.sha1,.sha256,.sha512,.sfv)
WebDAV
- Read-only WebDAV endpoint at
/webdav/(DAV Class 1) - Supports
OPTIONS,PROPFIND,GET, andHEADmethods - Compatible with Windows Explorer, macOS Finder, and other WebDAV clients
- Same security policies as the web UI (hidden paths, denied extensions, auth)
S3-Compatible API
- Read-only S3 endpoint at
/s3/for scripted and programmatic access - Compatible with
aws cli,rclone, MinIO client, and other S3-compatible tools - AWS Signature V4 authentication (access key + secret key)
- Supports
ListBuckets,GetBucketLocation,ListObjectsV2,GetObject,HeadObject - HTTP Range requests for partial downloads
- Hidden paths, denied extensions, and auth enforced
Security & Operations
- HTTP Basic Auth (username + password)
- Bearer token auth via configurable header (for MCP clients, API consumers, automation)
- External auth via reverse proxy headers (e.g. Authelia, Authentik)
- Hide files by dotfile flag or glob patterns
- Deny downloads by file extension
- Fixed-window rate limiting (per-IP and global)
- Health endpoints (
/health,/healthz,/readyz) - In-memory dashboard at
/dashboardwith optional dedicated credentials - Prometheus metrics at
/metrics - RESTful JSON API at
/api/(browse, search, share, archive) - MCP server at
/mcp/(JSON-RPC 2.0, Streamable HTTP transport) - Integration stats JSON at
/dashboard/stats - Distroless chiseled container image
Configuration
Defaults are defined in src/DirForge/appsettings.json. Override any value with an environment variable of the same name. Boolean values must be true or false. For the full list of options, see .env.example.
| Variable | Default | Description |
|---|---|---|
RootPath | . | Root directory to browse. |
Port | 8080 | HTTP listen port. |
ListenIp | 0.0.0.0 | IP address the app binds to. |
BasicAuthUser | unset | Basic Auth username. |
BasicAuthPass | unset | Basic Auth password. |
BearerToken | unset | Bearer token for token-based auth (MCP clients, API consumers, automation). |
BearerTokenHeaderName | Authorization | Header to read the bearer token from. |
EnableSharing | true | Enable HMAC-signed share links. |
ShareSecret | empty | Secret for signing share links. Set a long random value in production; if empty an in-memory secret is generated at startup. |
HideDotfiles | false | Hide entries starting with .. |
DenyDownloadExtensions | env,key,pem,... | Extensions blocked in direct and ZIP downloads. |
DefaultTheme | dark | UI theme (dark or light). |
SiteTitle | DirForge | Custom page title/header label. |
EnableWebDav | true | Read-only WebDAV at /webdav/. |
EnableS3Endpoint | false | Read-only S3 API at /s3/. |
EnableJsonApi | true | RESTful JSON API at /api/. |
EnableMcpEndpoint | true | MCP server at /mcp/. |
CalculateDirectorySizes | false | Auto-calculate subdirectory sizes on page load (slow on large trees). |
DirectorySizeCacheTtlSeconds | 300 | Cache TTL for computed directory sizes in seconds (0–2592000; 0 disables). |
ListingCacheTtlSeconds | 2 | Directory listing cache TTL in seconds (1–2592000). |
Security
Hardened example profile - copy into your .env and adjust:
BasicAuthUser=admin
BasicAuthPass=change-this
BearerToken=replace-with-long-random-token
ShareSecret=replace-with-long-random-value
ForwardedHeadersKnownProxies=10.0.0.2
DashboardAuthUser=metrics
DashboardAuthPass=change-this-too- Mount only directories you want to expose; prefer read-only mounts (
:ro). - Baseline defaults are homelab-oriented, not internet-hardened.
- Set
BasicAuthUser/BasicAuthPasswhen exposed outside a trusted network. - Set
BearerTokenfor token-based auth (useful for MCP clients, API consumers, and automation that poorly support Basic Auth). Both auth methods can be enabled simultaneously, either one grants access. - When
BearerTokenHeaderNameisAuthorization(default), the middleware acceptsAuthorization: Bearer <token>andAuthorization: <token>. Set a custom header name (e.g.X-API-Key) to read the raw token from that header instead. - Set
ShareSecretto a long random value in production. If empty, DirForge uses an in-memory secret and share links reset on restart. - For reverse proxy auth, set
ExternalAuthEnabled=trueand pinForwardedHeadersKnownProxies. Bearer token auth is also bypassed when external auth is enabled. - Hidden paths and denied extensions are enforced for both direct downloads and ZIP output.
- Dashboard and metrics data are in-memory only and reset on restart.
- If
DashboardAuthUser/DashboardAuthPassare set,/dashboardand/metricsaccept only those credentials. /dashboard/statsuses the same dashboard auth behavior: if dashboard credentials are configured, they are required.- Static UI assets are served from
/dirforge-assets/*(plus/favicon.ico) and are intentionally public.
WebDAV
DirForge includes a read-only WebDAV endpoint at /webdav/ (DAV Class 1), enabled by default. It supports OPTIONS, PROPFIND, GET, and HEAD. All write methods return 405 Method Not Allowed.
Client Access
| Client | Connection |
|---|---|
| macOS Finder | Finder → Go → Connect to Server → http://host:port/webdav/ |
| Windows Explorer | Map Network Drive → http://host:port/webdav/ (requires HTTPS or registry tweak, see below) |
| Linux (GVFS) | dav://host:port/webdav/ in Nautilus/Thunar |
| cadaver / curl | cadaver http://host:port/webdav/ |
Windows HTTP Limitation
Windows Explorer's WebDAV client (Mini-Redirector) refuses Basic Auth over plain HTTP by default. You must either:
- Use HTTPS (via reverse proxy, recommended)
- Set the registry key
HKLM\SYSTEM\CurrentControlSet\Services\WebClient\Parameters\BasicAuthLevelto2and restart theWebClientservice
Security
WebDAV requests follow the same auth and security pipeline as the web UI:
- Basic Auth or bearer token credentials are required when configured
- External auth headers are honored when
ExternalAuthEnabled=true - Hidden paths (
HideDotfiles,HidePathPatterns) and denied extensions (DenyDownloadExtensions) are enforced - Path traversal protection applies to all WebDAV paths
Set EnableWebDav=false to disable the endpoint entirely.
S3-Compatible API
DirForge includes a read-only S3-compatible endpoint at /s3/, disabled by default. It implements the minimal subset of the S3 API needed for listing and downloading files with standard S3 tools.
Supported Operations
| Operation | Description |
|---|---|
ListBuckets | GET /s3/ - returns a single virtual bucket |
GetBucketLocation | GET /s3/{bucket}?location - retur |
…