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.
187 lines
5.8 KiB
Markdown
187 lines
5.8 KiB
Markdown
# 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 <http://localhost:3000>.
|
||
|
||
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),
|
||
<https://www.eff.org/copyright>.
|
||
|
||
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`.
|
||
- 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.
|