reze dfe3e94aa4 reze: Make the CI deploy step manual-only
Dokploy deploys are triggered from the UI, so the pipeline no longer runs on
push to trunk. The file stays as a template: it is a no-op unless DEPLOY_URL is
available, and the trigger can be switched back to push-on-trunk.
2026-10-21 09:07:15 +00:00

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

node server.js

The server listens on 0.0.0.0:3000 by default. Override the port with PORT:

PORT=8080 node server.js

Open http://localhost:3000.

Or via the POSIX sh helper, which defaults PORT to 3000:

sh scripts/run.sh
PORT=8080 sh scripts/run.sh

Tests

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.

curl -s localhost:3000/healthz
{"status":"ok"}

GET /api/meta

Describes the available word lists.

curl -s localhost:3000/api/meta
{"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').

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}'
{"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.

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}'
{"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.

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

docker build -t reze-test .
docker run --rm -p 3000:3000 reze-test

Or with compose:

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.
  • Deploys are triggered from the Dokploy UI (or a Gitea webhook configured there). CI does not deploy automatically: .woodpecker.yml is a manual-only template. If you want a push-to-deploy hook, set a DEPLOY_URL repository variable (the Dokploy deploy webhook URL) and switch the pipeline trigger back to event: push on trunk.

License

MIT for the code. The bundled word lists are CC BY 3.0, © Electronic Frontier Foundation.

S
Description
No description provided
Readme
107 KiB
Languages
JavaScript 84.1%
CSS 7.5%
HTML 7.3%
Shell 0.6%
Dockerfile 0.5%