- Go 98.7%
- Dockerfile 0.7%
- templ 0.6%
| .forgejo/workflows | ||
| cmd | ||
| internal | ||
| tmp | ||
| web | ||
| .air.toml | ||
| .gitignore | ||
| auth.go | ||
| config.go | ||
| config.toml | ||
| docker-compose.yml | ||
| Dockerfile | ||
| ftp.go | ||
| go.mod | ||
| go.sum | ||
| http.go | ||
| LICENSE | ||
| main.go | ||
| README.md | ||
| sftp.go | ||
| smb.go | ||
| vfs.go | ||
| webdav.go | ||
fileserver
A multi-protocol file server written in Go. Serves files over HTTP/HTTPS, WebDAV (same port), FTP, SFTP, and SMB — all sharing a single root directory, authentication, and permission system.
Quickstart
go build -o fileserver . && ./fileserver
Open http://localhost:8080 in your browser to see the landing page.
Web interface
| Path | Description |
|---|---|
/ |
Landing page with server info |
/browse/ |
File browser (directory listings) |
/webdav/ |
WebDAV endpoint |
Protocols
| Protocol | Default Port | Access URL |
|---|---|---|
| HTTP | 8080 | http://localhost:8080/browse/ |
| HTTPS | 8080 | https://localhost:8080/browse/ |
| WebDAV | 8080 | http://localhost:8080/webdav |
| FTP | 2121 | ftp://localhost:2121 |
| SFTP | 2222 | sftp://localhost:2222 |
| SMB | 445 | smb://localhost/files |
HTTP and WebDAV share the same port — landing page at /, file browser at /browse/, WebDAV at /webdav/.
Permission system
The server uses a virtual filesystem (VFS) layer that enforces ACL rules on every file operation. Every protocol handler (HTTP, WebDAV, FTP, SFTP, SMB) routes through the same VFS, so permissions apply uniformly.
How it works
-
Users are defined in config with password and group memberships.
-
Groups collect users for shared access.
-
ACL rules assign an owner, a group, and a permission mode to directory paths.
-
When a user accesses a file, the VFS checks the most specific ACL that matches the path's prefix.
-
Permission is determined by the ACL's mode bits and the user's relationship to the ACL:
- Owner — user matches the ACL's
ownerfield → uses owner permission bits - Group member — user belongs to the ACL's
group→ uses group permission bits - Other — everyone else → uses other permission bits
- Owner — user matches the ACL's
Permission bits
ACL modes use Unix-style 9-character strings: rwxrwxrwx
| Bits | Meaning |
|---|---|
r |
Read (list directory, download files) |
w |
Write (create, upload, delete, modify) |
x |
Execute (traverse directory) |
ACL matching
The most specific matching ACL wins. For a path like /projects/foo/bar.txt:
- Check for an ACL on
/projects/foo - If none, check
/projects - If none, check
/ - If no ACL matches at all, full access is granted (backward compatible)
Home directories
Every user gets a private home directory. By default it's /home/<name> under the root, and it is created automatically on startup.
Home directories get an ACL with owner set to the user and mode = rwx------ (full access for the owner, nothing for anyone else). This ACL takes priority over broader rules because it is more specific.
To customize the path, set home_dir in the user config:
[[users]]
name = "alice"
password = "secret"
home_dir = "/users/alice"
Example
[[users]]
name = "alice"
password = "secret"
groups = ["editors"]
[[users]]
name = "bob"
password = "secret"
groups = ["viewers"]
[[groups]]
name = "editors"
[[groups]]
name = "viewers"
[[acl]]
path = "/"
owner = "alice"
group = "editors"
mode = "rwxr-x---" # alice: rwx, editors: r-x, others: ---
[[acl]]
path = "/public"
owner = "alice"
group = "viewers"
mode = "rwxr-xr-x" # alice: rwx, viewers: r-x, others: r-x
alicecan read and write everywhere (/and/public).- Editors group members can read everywhere (
/givesr-x), write only if/has write for group (it doesn't here). bob(viewers) can read/public(r-xgroup bits), but---for others on/means no access to other paths.- Anonymous users have
---on both, so they're denied everywhere.
Configuration
./fileserver -config config.toml
All options
root_dir = "./files"
[http]
enabled = true
address = ":8080"
[http.tls]
enabled = false
[ftp]
enabled = true
address = ":2121"
passive_min = 50000
passive_max = 50100
[sftp]
enabled = true
address = ":2222"
host_key = "" # auto-generated on first run
[webdav]
enabled = true
prefix = "/webdav"
[smb]
enabled = false
address = ":445"
netbios = "FILESERVER"
share_name = "files"
[[users]]
name = "admin"
password = "admin"
groups = ["admin"]
home_dir = "/home/admin" # defaults to /home/<name>
[[groups]]
name = "admin"
[[acl]]
path = "/"
owner = "admin"
group = "admin"
mode = "rwxr-x---"
When no ACLs are defined, all authenticated users have full access.
Password hashing
[[users]]
name = "admin"
password = "$2a$10$..."
home_dir = "/home/admin"
Generate a hash:
htpasswd -nbB admin your-password # requires apache2-utils
When no users are defined, authentication is disabled entirely (anonymous access).
Build
go build -o fileserver .
HTTP API
| Method | Path | Action |
|---|---|---|
| GET | /browse/dir/ |
List directory |
| GET | /browse/file |
Download file |
| POST | /browse/dir/ |
Upload files (multipart) |
| PUT | /browse/dir/file |
Upload single file |
| DELETE | /browse/path |
Delete file or directory |
| MKDIR | /browse/dir/name/ |
Create directory |
Legacy paths without the /browse/ prefix (e.g., /file) also work for backward compatibility.
Project
fileserver/
├── main.go - entry point, wiring
├── config.go - TOML config structs
├── auth.go - authentication manager
├── vfs.go - virtual filesystem with permission checks
├── http.go - HTTP/HTTPS + WebDAV server
├── webdav.go - WebDAV handler (VFS-backed)
├── ftp.go - FTP server (VFS-backed)
├── sftp.go - SFTP server
├── smb.go - SMB server (skeleton)
├── config.toml - default configuration
└── go.mod