A private, light-mode file portal for uploading tickets, PDFs, images and documents — protected by a 6-digit passcode.
httpOnly + SameSite=Strict cookies, random 32-byte tokens, 12h expirynosniff, frame denial, same-origin CSRF checknpm install
npm run set-passcode # choose your 6-digit passcode (hashed to config/auth.json)
npm start # http://localhost:3000
Set the passcode non-interactively:
npm run set-passcode -- 123456
| Env var | Default | Description |
|---|---|---|
PORT |
3000 |
HTTP port |
HOST |
0.0.0.0 |
Bind address |
data/uploads/ # uploaded files (generated, git-ignored)
data/files.json # file metadata (generated, git-ignored)
config/auth.json # scrypt salt + hash only (generated, git-ignored)
Everything user-specific lives under data/ and config/, both git-ignored. Only the salted hash of the passcode is written — never the passcode.
Important: GitHub Pages is static-only hosting and cannot run this app (the passcode gate and uploads need the Node backend). Deploy to a Node host instead.
render.yaml provisions everything, including a persistent disk for uploads.PASSCODE env var on first deploy. Find it under Environment → PASSCODE in the dashboard, or replace it with your own 6-digit code and redeploy.https://<name>.onrender.com.npm install
PASSCODE=123456 npm start # passcode via env var (no config file needed)
# or:
npm run set-passcode && npm start # passcode hashed to config/auth.json
| Var | Default | Purpose |
|---|---|---|
PORT |
3000 |
HTTP port |
HOST |
0.0.0.0 |
Bind address |
PASSCODE |
— | 6-digit passcode (used when config/auth.json is absent) |
BASE_PATH |
— | Serve under a subpath, e.g. /public (for reverse proxies) |
DATA_DIR |
./data |
Where uploads are stored (set to a mounted disk path in production) |
req.secure is true.npm run set-passcode and share it only with people who need access.| Command | What it does |
|---|---|
npm start |
Run the server |
npm run dev |
Run with auto-restart on changes |
npm run set-passcode |
Set / rotate the 6-digit passcode |
MIT