BlackFiles is a self-hosted file manager for a single storage root. It combines a Rust/Rocket API, PostgreSQL-backed accounts and roles, and a React web client for browsing, downloading, uploading, deleting, and sharing files through one-time upload links.
The production image builds the Vite client and serves it from the same Rocket process. Persistent data lives outside the image: files in storage/ and PostgreSQL data in its Docker volume.
- Browse, search, sort, paginate, preview, and download files from the configured storage root
- Resumable authenticated and public-link uploads using the tus protocol
- File and directory deletion, controlled by role permissions
- One-time public upload links scoped to a destination directory; interrupted transfers can resume for 24 hours
- Cookie-based JWT authentication with refresh sessions
- Role-based access control for file, user, role, and upload-link operations
- User and role administration from the web UI
- Path-component validation to prevent traversal outside
storage/
- Server: Rust, Rocket, Tokio
- Database: PostgreSQL with
deadpool-postgres - Client: React, Vite, TanStack Router/Query, Mantine
- Deployment: Docker Compose
cp template.env .envSet strong values before exposing the service:
POSTGRES_PASSWORDJWT_SECRET— a random value of at least 32 charactersDEFAULT_ADMIN_PASSWORD
template.env is configured for the Compose service names and exposes the application on port 4000.
docker compose up --build -dOpen http://localhost:4000 and sign in as admin with the value of DEFAULT_ADMIN_PASSWORD. The application creates this account only when no users exist in the database.
Useful commands:
# Follow application and database logs
docker compose logs -f
# Stop containers while keeping PostgreSQL and storage data
docker compose downUploaded files are bind-mounted at ./storage. PostgreSQL data is stored in the named blackfiles-pgdata volume. Removing either is destructive.
Use template.docker-compose.yml as a starting point. It keeps the application and PostgreSQL private to Docker networks and includes a database health check. Attach a reverse proxy to the external caddy_net network, or change the network and port configuration for your environment.
Before deployment:
- Copy
template.docker-compose.ymltodocker-compose.ymlandtemplate.envto.env. - Replace the example database password, JWT secret, and bootstrap-admin password.
- Ensure the host directory mounted at
./storageis writable by the container. - Terminate TLS at a reverse proxy; the application cookies are
HttpOnlyandSameSite=Lax, but HTTPS is still required for an Internet-facing deployment.
Every idempotent feature script in dbinit/ is embedded in the server and applied at startup. The same directory is mounted into PostgreSQL for fresh-volume initialization, so new and existing installations follow the identical schema path; do not apply scripts manually.
Requirements: Rust, Bun, and PostgreSQL 18 (or the PostgreSQL Compose service).
Start only PostgreSQL with Compose:
docker compose up postgres -dFor a locally run server, set POSTGRES_HOST=localhost in .env; the Compose default (blackfiles-db) resolves only inside the Docker network. Then run the server and client in separate terminals:
cargo runbun install
bun run devThe Vite development server runs on http://localhost:3000 and proxies /api requests to the Rocket server on port 4000.
Useful frontend commands:
bun run build
bun run typecheck
bun run lintAll API routes are prefixed with /api.
| Area | Routes |
|---|---|
| Authentication | POST /auth/login, POST /auth/logout, POST /auth/refresh, GET /auth/me |
| File browsing | GET /list, GET /list/<path..>, GET /files/<path..> |
| File management | DELETE /files/<path..>, authenticated tus uploads at /uploads |
| Upload links | POST /upload-links, GET /upload-links, DELETE /upload-links/<id>, and public tus uploads under /public/upload-links/<token>/uploads |
| Administration | User, role, and permission endpoints under /users, /roles, and /permissions |
Authenticated operations require the relevant role permission. Public upload-link endpoints are the exception: a valid token authorizes one resumable file transfer to its preconfigured destination. The link is consumed only after that transfer completes successfully.
BlackFiles restricts filesystem operations to the storage/ root. Request paths are normalized as relative path components; absolute paths, parent traversal, and invalid components are rejected. Authorization is enforced separately for listing, downloads, uploads, deletion, user administration, role management, and upload-link management.
This is not a substitute for operational controls. Keep the storage mount and database private, use strong secrets, put the application behind HTTPS, and back up both the storage directory and PostgreSQL volume.