config.ts
| 1 | // Runtime configuration, read once from the environment at startup. |
| 2 | // Defaults are chosen to be secure-but-non-breaking; tighten them in production |
| 3 | // (see README for the full list and the "run behind TLS" note). |
| 4 | |
| 5 | const env = process.env; |
| 6 | |
| 7 | /** parseInt with a default that distinguishes "unset" from "explicit 0". All |
| 8 | * settings here are non-negative quantities, so a negative value falls back to |
| 9 | * the default rather than being passed through (e.g. busboy fileSize: -5). */ |
| 10 | function intEnv(value: string | undefined, defaultValue: number): number { |
| 11 | if (value === undefined || value === "") return defaultValue; |
| 12 | const parsed = parseInt(value, 10); |
| 13 | return Number.isFinite(parsed) && parsed >= 0 ? parsed : defaultValue; |
| 14 | } |
| 15 | |
| 16 | /** Truthy only for "true"/"1"; anything else (incl. "false", "0", unset) is off. */ |
| 17 | function boolEnv(value: string | undefined): boolean { |
| 18 | return value === "true" || value === "1"; |
| 19 | } |
| 20 | |
| 21 | export const config = { |
| 22 | // Hard cap on a single upload, in bytes (default 100 MiB). |
| 23 | maxUploadBytes: intEnv(Bun.env.MAX_UPLOAD_BYTES, 100 * 1024 * 1024), |
| 24 | // Maximum retention in minutes. null = unlimited; when set, every upload's |
| 25 | // deletion time is clamped to at most now + maxAgeMinutes. |
| 26 | maxAgeMinutes: Bun.env.MAX_AGE_MINUTES |
| 27 | ? intEnv(Bun.env.MAX_AGE_MINUTES, 0) || null |
| 28 | : null, |
| 29 | // Minimum seconds between uploads from the same client IP. 0 = disabled. |
| 30 | uploadCooldownSeconds: intEnv(Bun.env.UPLOAD_COOLDOWN_SECONDS, 0), |
| 31 | // Minimum seconds between server-side decryption attempts from the same client |
| 32 | // IP. 0 = disabled. Bounds the PBKDF2 CPU cost an attacker who knows a UUID can |
| 33 | // force by hammering /show or /raw with password cookies. |
| 34 | decryptCooldownSeconds: intEnv(Bun.env.DECRYPT_COOLDOWN_SECONDS, 0), |
| 35 | // Cap on total stored content bytes across all files. null = unlimited; when |
| 36 | // set, an upload that would push the total over the cap is rejected (507). |
| 37 | maxTotalBytes: Bun.env.MAX_TOTAL_BYTES |
| 38 | ? intEnv(Bun.env.MAX_TOTAL_BYTES, 0) || null |
| 39 | : null, |
| 40 | // Set when running behind a trusted TLS-terminating reverse proxy (the usual |
| 41 | // production setup). Enables reading X-Forwarded-For / X-Real-IP for the |
| 42 | // client IP and adds the Secure flag to the password cookie. Leave off for |
| 43 | // direct/local HTTP, otherwise clients can spoof the cooldown key and the |
| 44 | // Secure cookie won't be sent over plain HTTP. |
| 45 | behindProxy: boolEnv(env.BEHIND_PROXY), |
| 46 | }; |
| 47 |