Removes the redundant `/healthz` and `/api/v1/health` endpoints, leaving `/health` as the sole health check endpoint. - Update router to return 404 Not Found for the removed endpoints - Update admin log filtering to only ignore `/health` - Remove health URL from API documentation data - Update tests to verify `/health` returns 200 and others return 404 - Update README documentation to reflect the change
Warpbox.dev
This repository contains the Go backend base for warpbox.dev, a self-hosted transfer-first file sharing application.
Run
./scripts/run/dev.sh
The default server listens on :8080.
Upload size limits are configured in megabytes through WARPBOX_MAX_UPLOAD_SIZE_MB.
Fractions are supported, so 0.5Mb is 512 KiB and 1.5Mb is 1536 KiB.
Upload policy defaults are also configured in megabytes and can later be changed from
/admin/settings:
WARPBOX_ANONYMOUS_UPLOADS_ENABLED=trueWARPBOX_ANONYMOUS_MAX_UPLOAD_MB=512WARPBOX_ANONYMOUS_DAILY_UPLOAD_MB=2048WARPBOX_USER_DAILY_UPLOAD_MB=8192WARPBOX_DEFAULT_USER_STORAGE_MB=51200WARPBOX_USAGE_RETENTION_DAYS=30WARPBOX_LOCAL_STORAGE_MAX_GB=100WARPBOX_ANONYMOUS_MAX_DAYS=30WARPBOX_USER_MAX_DAYS=90WARPBOX_ANONYMOUS_DAILY_BOXES=100WARPBOX_USER_DAILY_BOXES=250WARPBOX_ANONYMOUS_ACTIVE_BOXES=500WARPBOX_USER_ACTIVE_BOXES=1000WARPBOX_SHORT_WINDOW_REQUESTS=60WARPBOX_SHORT_WINDOW_SECONDS=60WARPBOX_ANONYMOUS_STORAGE_BACKEND=localWARPBOX_USER_STORAGE_BACKEND=localWARPBOX_TRUSTED_PROXIES=controls whether forwarded client IP headers are accepted only from specific proxy IPs/CIDRs. See SECURITY_PROXY.md.
Runtime data is configured with WARPBOX_DATA_DIR and defaults to ./data in the dev environment.
The dev script resolves that path from the repository root.
Large uploads are expected to take minutes on normal home/server connections. Keep
WARPBOX_READ_TIMEOUT=0s and WARPBOX_WRITE_TIMEOUT=0s so Go does not close the connection
mid-upload; WARPBOX_READ_HEADER_TIMEOUT=15s still protects header reads from slowloris-style
connections.
Background jobs are enabled with WARPBOX_JOBS_ENABLED=true. Individual jobs can be toggled with
WARPBOX_CLEANUP_ENABLED and WARPBOX_THUMBNAIL_ENABLED, and their schedules are configured with
WARPBOX_CLEANUP_EVERY and WARPBOX_THUMBNAIL_EVERY.
On a fresh data directory, visit /register to create the first account. That first user becomes
the instance admin and normal registration closes after bootstrap. Admins can create copyable invite
links from /admin/users.
The env admin token still exists as emergency fallback access. Set WARPBOX_ADMIN_TOKEN and use it
at /admin/login if you need to recover access without a user session.
Emoji reaction packs
File reactions use emoji packs from the runtime data directory, not from the application code. By
default that means ./data/emoji; if you change WARPBOX_DATA_DIR, use
$WARPBOX_DATA_DIR/emoji instead.
Each folder under ./data/emoji becomes one emoji tab in the reaction picker. Put image files
directly inside the pack folder:
data/
├── db/
├── files/
├── logs/
└── emoji/
├── openmoji/
│ ├── 1F600.svg
│ ├── 1F44D.svg
│ └── 2764.svg
├── pixel-pack/
│ ├── happy.webp
│ ├── fire.webp
│ └── star.webp
└── custom-work/
├── approved.png
└── shipped.png
In this example, the picker shows tabs named Openmoji, Pixel pack, and Custom work.
Supported emoji image extensions are .svg, .webp, .png, .jpg, .jpeg, and .gif.
For one-off Go commands, run them from the backend module:
cd backend
go run ./cmd/warpbox
Docker / Podman
Copy the example environment file and adjust values such as WARPBOX_BASE_URL and
WARPBOX_ADMIN_TOKEN before running the container:
Copy the example docker-compose.example.yml to docker-compose.yml, modify as need-be
cp .env.example .env
docker compose -f docker-compose.yml up --build
The compose example also works with Podman compatible compose tools. Its data volume uses
./data:/data:Z for SELinux relabeling, and the container overrides runtime paths to use
/data, /app/static, and /app/templates.
The image exposes the health endpoint: /health. Docker and compose healthchecks use it.
Reverse Proxy Security
Warpbox uses the resolved client IP for anonymous limits, manual bans, and automatic bans. The
default behavior trusts X-Forwarded-For and X-Real-IP so a normal Caddy reverse proxy works
without extra setup. For hardened deployments where the app port might be reachable from more than
one network, set WARPBOX_TRUSTED_PROXIES to trusted proxy IPs/CIDRs. See
SECURITY_PROXY.md for Caddy examples and Docker/systemd notes.
Systemd
Build the binary on the server, create a dedicated user, and keep runtime data outside the repo:
cd /opt/warpbox-dev/backend
go build -o /usr/local/bin/warpbox ./cmd/warpbox
sudo useradd --system --home /var/lib/warpbox --shell /usr/sbin/nologin warpbox
sudo mkdir -p /var/lib/warpbox /etc/warpbox
sudo chown -R warpbox:warpbox /var/lib/warpbox
sudo cp /opt/warpbox-dev/.env.example /etc/warpbox/warpbox.env
Example /etc/warpbox/warpbox.env values:
WARPBOX_ENV=production
WARPBOX_ADDR=127.0.0.1:6070
WARPBOX_BASE_URL=https://warpbox.dev
WARPBOX_DATA_DIR=/var/lib/warpbox
WARPBOX_STATIC_DIR=/opt/warpbox-dev/backend/static
WARPBOX_TEMPLATE_DIR=/opt/warpbox-dev/backend/templates
WARPBOX_TRUSTED_PROXIES=127.0.0.1,::1
WARPBOX_READ_HEADER_TIMEOUT=15s
WARPBOX_READ_TIMEOUT=0s
WARPBOX_WRITE_TIMEOUT=0s
Example /etc/systemd/system/warpbox.service:
[Unit]
Description=Warpbox file sharing service
After=network-online.target
Wants=network-online.target
[Service]
User=warpbox
Group=warpbox
EnvironmentFile=/etc/warpbox/warpbox.env
ExecStart=/usr/local/bin/warpbox
Restart=always
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ReadWritePaths=/var/lib/warpbox
[Install]
WantedBy=multi-user.target
Then enable it:
sudo systemctl daemon-reload
sudo systemctl enable --now warpbox
sudo systemctl status warpbox
Put Caddy in front of 127.0.0.1:6070 and keep the Warpbox port closed to the public internet.
Layout
backend/cmd/warpbox- main application entry point.backend/libs/config- environment-backed configuration.backend/libs/httpserver- server construction and route composition.backend/libs/handlers- HTTP handlers for pages, API, health, static files.backend/libs/jobs- background job registration and job loop definitions.backend/libs/middleware- request logging, recovery, security headers, gzip, request IDs.backend/libs/services- business logic boundaries, starting with upload limits.backend/libs/helpers- small reusable helpers.backend/libs/web- Go template renderer.backend/templates- server-rendered Go templates.backend/static/css,backend/static/js,backend/static/img,backend/static/fonts- public assets served from/static/.scripts/run/dev.sh- local development runner.scripts/env/dev.env.example- tracked development environment template.scripts/env/dev.env- local development environment, ignored by git.
Stage 2 Operator Tools
/admin/login- token-based admin login./admin- overview metrics: boxes, files, storage, recent uploads, protected/expired boxes./admin/files- recent upload table with view and delete actions.- Expired boxes and boxes that have reached their download limit are cleaned on startup and then every
WARPBOX_CLEANUP_EVERYwhenWARPBOX_CLEANUP_ENABLED=true. - Missing image/video thumbnails are generated in a background worker every
WARPBOX_THUMBNAIL_EVERYwhenWARPBOX_THUMBNAIL_ENABLED=true.
Stage 3 Anonymous Integrations
Anonymous uploads now return a private management link at creation time. Keep that link secret: anyone with it can delete the entire upload box. The raw delete token is not stored and cannot be recovered later.
Browser uploads still show Open box and Copy URL as the primary actions, with a smaller
Manage or delete this upload link in the completion panel.
Curl and custom uploaders can use the same endpoint:
# Terminal-friendly output: one plain box URL.
curl -F file=@./report.pdf http://localhost:8080/api/v1/upload
# JSON output with boxUrl, thumbnailUrl, manageUrl, deleteUrl, zipUrl, and file entries.
curl -F sharex=@./screenshot.png \
-H 'Accept: application/json' \
http://localhost:8080/api/v1/upload
The upload endpoint accepts multipart fields named file and sharex. ShareX users can start
from examples/sharex/warpbox-anonymous.sxcu; update RequestURL to match your instance URL.
Authenticated uploads (your account's limits) add an Authorization: Bearer <token> header — mint
a token under Account → Access tokens. The JSON response uses ShareX placeholders
{json:boxUrl} (URL), {json:thumbnailUrl} (thumbnail), {json:deleteUrl} (deletion), and
{json:error} (error message).
Grouping multiple files into one box (X-Warpbox-Batch)
By default every uploaded file becomes its own box. To put several files in a single box, send
the opt-in X-Warpbox-Batch header: requests that share the same header value (scoped per account,
or per IP for anonymous uploads) within 20s are appended to the same box. This lets a multi-file
ShareX selection — which ShareX sends as separate back-to-back requests — land as one shareable
link. The shipped .sxcu sets X-Warpbox-Batch: sharex; remove that header for one box per file.
Requests without the header behave exactly as before.
Stage 4 Accounts + Personal Boxes
/registerbootstraps the first admin account only when no users exist./loginand/logoutprovide cookie-based web sessions./appis the personal dashboard for logged-in users, showing owned boxes, storage usage, upload history, and flat collections. Uploading still happens from the homepage./admin/userslets admins create invite links, disable/reactivate users, and generate reset links.- Logged-in browser uploads from
/still usePOST /api/v1/upload, but the resulting box is stored with owner and optional collection metadata. - Admin users are exempt from the global max upload size on the homepage upload flow. Future per-user quotas should apply to this same upload path rather than creating a second uploader.
/admin/settingscontrols anonymous uploads, anonymous max upload size, daily upload caps, default user storage quota, and usage retention./admin/usersshows storage/daily usage and lets admins set per-user storage quota overrides./admin/storagemanages the built-in local file backend and S3-compatible bucket backends./admin/bansmanages manual IP/CIDR bans and optional automatic bans for suspicious probes and repeated login failures. Auto-ban is off by default and configured from the admin UI.- Upload limits now include daily bytes, daily box counts, active box counts, short-window request limits, max expiration days, local storage capacity in GB, and per-user policy overrides.
- Uploaded file content, thumbnails, and private box metadata use the selected storage backend.
The bbolt database and JSON logs remain local under
./data/dband./data/logs. - Anonymous uploads, ShareX uploads, unlisted public box links, password protection, expiry, delete tokens, thumbnails, and cleanup continue to work as before.
Email delivery is intentionally deferred. Invite and reset links are copyable today; future SMTP support will power public forgot-password and optional email delivery.
Runtime Data
Warpbox keeps local runtime data under the configured data directory:
data/files/{box_id}/@each@{file_id}.ext- uploaded file contents when the local backend is selected.data/files/{box_id}/@thumb@{file_id}.jpg- generated previews when the local backend is selected.data/db/warpbox.bbolt- bbolt metadata database for boxes and file records.data/db/warpbox.bboltalso stores users, sessions, invites, and collections.data/db/warpbox.bboltstores upload policy settings and daily usage records keyed by plain IP for anonymous uploads and user ID for signed-in uploads.data/db/warpbox.bboltstores manual bans, automatic ban settings, abuse counters, and malicious path rules.data/logs/{YYYY-MM-DD}.log- JSONL logs, one event per line.
Static Asset Policy
The static handler sets long-lived immutable caching for images, video, audio, and fonts, shorter caching for CSS/JS, and gzip compression for compressible responses.