# reze-test A small, dependency-free password generator. It has two modes: - **Memorable** — diceware-style passphrases shaped like `[num][word]-[num][word]-…`, e.g. `42abacus-91abdomen`. - **Random** — fully random characters from a configurable character set. Everything is generated server-side with Node's CSPRNG (`crypto.randomInt`). There is no account, no login, no database, and nothing you generate is stored or logged. ## Features - Two randomization levels: memorable passphrases and fully random passwords. - EFF word lists (7776-word and 1296-word) selectable per request. - Configurable digits per word, digit separator, group separator and capitalization. - Configurable length, character classes, custom symbol set, ambiguous-character exclusion and "at least one of each selected set". - Generate up to 10 passwords at once, each with an entropy estimate and a strength bar. - One-click copy with a clipboard fallback. - Light/dark theme with a system-theme fallback, persisted in `localStorage`. - Options persisted in `localStorage`; generated passwords are never persisted. - No npm dependencies at all — Node.js built-ins only. ## Requirements - Node.js 22 or newer (ESM, `"type": "module"`). ## Run locally ```sh node server.js ``` The server listens on `0.0.0.0:3000` by default. Override the port with `PORT`: ```sh PORT=8080 node server.js ``` Open . Or via the POSIX sh helper, which defaults `PORT` to 3000: ```sh sh scripts/run.sh PORT=8080 sh scripts/run.sh ``` ## Tests ```sh node --test ``` ## API All API responses are JSON. Every response carries the security headers `X-Content-Type-Options: nosniff`, `Referrer-Policy: no-referrer`, `X-Frame-Options: DENY` and a strict `Content-Security-Policy`. ### `GET /healthz` Liveness probe used by the Docker `HEALTHCHECK`. ```sh curl -s localhost:3000/healthz ``` ```json {"status":"ok"} ``` ### `GET /api/meta` Describes the available word lists. ```sh curl -s localhost:3000/api/meta ``` ```json {"lists":[{"id":"eff-large","name":"EFF Large Word List","size":7776},{"id":"eff-short","name":"EFF Short Word List (2.0)","size":1296}]} ``` ### `POST /api/generate` Body is a JSON object. `mode` selects the algorithm; `count` (1–10, default 1) is the number of passwords to return. Unknown modes, malformed JSON and out-of-range options are rejected with `400` and a JSON `{"error": "…"}` body. Request bodies are limited to 64 KB. Memorable options: `words` (2–10), `digits` (0–4), `digitSeparator` (`''` or `'-'`), `separator` (`'-'`, `'_'`, `'.'`, `' '` or `''`), `capitalization` (`'none'`, `'title'`, `'upper'`), `list` (`'eff-large'`, `'eff-short'`). ```sh curl -s -X POST localhost:3000/api/generate \ -H 'Content-Type: application/json' \ -d '{"mode":"memorable","words":2,"digits":2,"separator":"-","digitSeparator":"","capitalization":"title","list":"eff-large","count":2}' ``` ```json {"mode":"memorable","count":2,"passwords":[{"value":"07Sample-29Breach","mode":"memorable","entropy":39.1},{"value":"69Reassign-85Probation","mode":"memorable","entropy":39.1}],"entropy":39.1} ``` Random options: `length` (8–64), `useLower`, `useUpper`, `useDigits`, `useSymbols`, `customSymbols` (replaces the default symbol set when non-empty), `excludeAmbiguous` (removes `0 O 1 l I |`) and `requireEach`. ```sh curl -s -X POST localhost:3000/api/generate \ -H 'Content-Type: application/json' \ -d '{"mode":"random","length":24,"useLower":true,"useUpper":true,"useDigits":true,"useSymbols":true,"requireEach":true,"count":1}' ``` ```json {"mode":"random","count":1,"passwords":[{"value":"3V#P@bQ2C4c3;=igw}E(EpnG","mode":"random","entropy":154.6}],"entropy":154.6} ``` ### Entropy - Random mode: `length * log2(poolSize)`, where `poolSize` is the size of the union of all enabled character classes. - Memorable mode: `words * log2(listSize) + words * digits * log2(10)`. Separators and the capitalization scheme add no entropy. ### Other paths Unknown `/api/*` paths return a JSON `404`. Unknown page paths return `public/404.html` with HTTP `404`. Only `GET` and `HEAD` are accepted for static files. ## Word lists | id | list | size | | --- | --- | --- | | `eff-large` | EFF Large Word List | 7776 | | `eff-short` | EFF Short Word List (2.0) | 1296 | Both files live in `public/words/` and are loaded once at startup. - Source: [Electronic Frontier Foundation](https://www.eff.org/dice) — the [EFF Diceware wordlists](https://www.eff.org/dice-roll-the-dice). - Licence: Creative Commons Attribution 3.0 Unported (CC BY 3.0), . The lists contain a few hyphenated entries (`drop-down`, `felt-tip`, `t-shirt`, `yo-yo`), which are used verbatim. ## Environment variables | variable | default | description | | --- | --- | --- | | `PORT` | `3000` | TCP port the HTTP server binds to on `0.0.0.0`. | | `NODE_ENV` | — | Set to `production` in the image. | ## Docker ```sh docker build -t reze-test . docker run --rm -p 3000:3000 reze-test ``` Or with compose: ```sh docker compose up --build -d ``` The image is a single stage based on `node:22-alpine`, runs as the non-root `node` user and ships a `HEALTHCHECK` that polls `http://127.0.0.1:3000/healthz` with `wget`. ## Deployment (Dokploy) - Repository: `druid520/reze-test` on Gitea (git.kg.boson.party). - Branch: `trunk`. - Build type: Dockerfile (or "Nixpacks" — the app needs nothing but Node). - Environment variable: `PORT=3000`. - Exposed port: `3000`. - CI hook: add the repository secret `DEPLOY_URL` (the Dokploy deploy webhook URL). `.woodpecker.yml` posts to it on every push to `trunk` so Dokploy rebuilds and redeploys the service. ## License MIT for the code. The bundled word lists are CC BY 3.0, © Electronic Frontier Foundation.