Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: CI

on:
push:
branches: [main]
pull_request:

jobs:
check:
name: check + unit tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: denoland/setup-deno@v2
with:
deno-version: "2.7.14"
- name: Install dependencies
run: deno install
- name: Format, lint, type-check
run: deno task check
- name: Unit tests
run: deno task test

e2e:
name: e2e (stdio + remote)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: denoland/setup-deno@v2
with:
deno-version: "2.7.14"
- name: Install dependencies
run: deno install
# Builds the production bundle, then runs the stdio, prod-build, and
# vite-dev /mcp e2e tests (each spins up a real server + MCP client).
- name: End-to-end tests
run: deno task test:e2e
204 changes: 140 additions & 64 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,76 +11,81 @@ Built on top of the JSR library [Synthima](https://jsr.io/@jakeave/synthima).
## Features

- Cryptographically random output via `crypto.getRandomValues`
- 47 built-in character set presets (Latin, Cyrillic, Greek, Arabic, CJK, and more)
- Simple mode: toggle the four common sets (uppercase, lowercase, numbers, symbols)
- Advanced mode: add any preset or hand-type custom character sets with per-requirement min/max
- 47 built-in character set presets (Latin, Cyrillic, Greek, Arabic, CJK, and
more)
- Simple mode: toggle the four common sets (uppercase, lowercase, numbers,
symbols)
- Advanced mode: add any preset or hand-type custom character sets with
per-requirement min/max
- Shareable URLs — the full configuration is encoded in the URL and updates live

---

## Shareable URLs

Every configuration is encoded in the URL so you can share or bookmark specific setups.
Every configuration is encoded in the URL so you can share or bookmark specific
setups.

**Format:** `?length=N&r=<preset>&r=<preset>:<min>&r=<preset>:<min>:<max>`

- `length` — password length (default: 12)
- `r` — one requirement per param; value is a preset key, optionally followed by `:min` and `:max`
- `r` — one requirement per param; value is a preset key, optionally followed by
`:min` and `:max`
- Omit `:min` when it's 1 (the default); omit `:max` when there's no upper limit
- Unknown preset keys are treated as raw character strings

### Example links

| Description | Link |
|---|---|
| Description | Link |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| Strong password (16 chars) | [uppercase + lowercase + numbers + symbols](https://synthima-web.jakeave.deno.net/?length=16&r=uppercase&r=lowercase&r=numbers&r=special) |
| 6-digit PIN | [numbers only](https://synthima-web.jakeave.deno.net/?length=6&r=numbers) |
| Letters only | [uppercase + lowercase, no numbers or symbols](https://synthima-web.jakeave.deno.net/?length=12&r=uppercase&r=lowercase) |
| Numbers-heavy | [at least 4 digits](https://synthima-web.jakeave.deno.net/?length=16&r=uppercase&r=lowercase&r=numbers:4&r=special) |
| Constrained symbols | [max 2 special chars](https://synthima-web.jakeave.deno.net/?length=16&r=uppercase&r=lowercase&r=numbers&r=special:1:2) |
| Greek + numbers | [exotic mix](https://synthima-web.jakeave.deno.net/?length=20&r=greek&r=numbers) |
| Russian + symbols | [Cyrillic with special chars](https://synthima-web.jakeave.deno.net/?length=24&r=russian&r=numbers&r=special) |
| 6-digit PIN | [numbers only](https://synthima-web.jakeave.deno.net/?length=6&r=numbers) |
| Letters only | [uppercase + lowercase, no numbers or symbols](https://synthima-web.jakeave.deno.net/?length=12&r=uppercase&r=lowercase) |
| Numbers-heavy | [at least 4 digits](https://synthima-web.jakeave.deno.net/?length=16&r=uppercase&r=lowercase&r=numbers:4&r=special) |
| Constrained symbols | [max 2 special chars](https://synthima-web.jakeave.deno.net/?length=16&r=uppercase&r=lowercase&r=numbers&r=special:1:2) |
| Greek + numbers | [exotic mix](https://synthima-web.jakeave.deno.net/?length=20&r=greek&r=numbers) |
| Russian + symbols | [Cyrillic with special chars](https://synthima-web.jakeave.deno.net/?length=24&r=russian&r=numbers&r=special) |

### Preset keys

| Key | Characters |
|---|---|
| `uppercase` | A–Z |
| `lowercase` | a–z |
| `numbers` | 0–9 |
| `special` | !@#$%^&* |
| `german-upper` / `german-lower` | Base Latin + ÄÖÜ / äöüß |
| `french-upper` / `french-lower` | Base Latin + French diacritics |
| `spanish` | Base Latin + ñÑ¿¡ |
| `nordic-upper` / `nordic-lower` | Base Latin + ÅÆØÐÞ / åæøðþ |
| `central-eu-upper` / `central-eu-lower` | Base Latin + Central European |
| `romanian-upper` / `romanian-lower` | Base Latin + ĂÂÎȘȚ / ăâîșț |
| `turkish-upper` / `turkish-lower` | Base Latin + ÇĞİŞÖÜ / çğışöü |
| Key | Characters |
| --------------------------------------- | ---------------------------------- |
| `uppercase` | A–Z |
| `lowercase` | a–z |
| `numbers` | 0–9 |
| `special` | !@#$%^&* |
| `german-upper` / `german-lower` | Base Latin + ÄÖÜ / äöüß |
| `french-upper` / `french-lower` | Base Latin + French diacritics |
| `spanish` | Base Latin + ñÑ¿¡ |
| `nordic-upper` / `nordic-lower` | Base Latin + ÅÆØÐÞ / åæøðþ |
| `central-eu-upper` / `central-eu-lower` | Base Latin + Central European |
| `romanian-upper` / `romanian-lower` | Base Latin + ĂÂÎȘȚ / ăâîșț |
| `turkish-upper` / `turkish-lower` | Base Latin + ÇĞİŞÖÜ / çğışöü |
| `portuguese-upper` / `portuguese-lower` | Base Latin + Portuguese diacritics |
| `latin-extended` | Full Latin Extended block |
| `extended-special` | £€¥₹ ± × ÷ ≠ ≈ ∞ … and more |
| `currency` | £ € ¥ ₹ ₽ ₩ ₪ ₿ ¢ |
| `math` | ± × ÷ ≠ ≈ ∞ ∑ ∏ √ ∫ ∂ |
| `arrows` | ← → ↑ ↓ ↔ ↗ ↘ ↙ ↖ ⇒ ⇐ ⇑ ⇓ |
| `typographic` | « » „ " " ' ' … † ‡ § ¶ • |
| `superscripts` / `subscripts` | ⁰¹²³… / ₀₁₂₃… |
| `russian` | Full Russian Cyrillic |
| `ukrainian` | Full Ukrainian Cyrillic |
| `bulgarian` | Full Bulgarian Cyrillic |
| `serbian-cyrillic` | Full Serbian Cyrillic |
| `greek` | Full Greek alphabet |
| `arabic-letters` | Arabic consonants |
| `arabic-indic` | Arabic-Indic digits ٠١٢… |
| `arabic-punct` | ، ؛ ؟ |
| `hebrew-letters` | Hebrew alphabet |
| `hindi-consonants` / `hindi-vowels` | Devanagari consonants / vowels |
| `cjk` | 3500 common CJK characters |
| `hiragana` | Japanese Hiragana |
| `katakana` | Japanese Katakana |
| `korean-consonants` / `korean-vowels` | Korean Jamo |
| `georgian` | Georgian Mkhedruli |
| `armenian` | Armenian alphabet |
| `thai-consonants` / `thai-vowels` | Thai consonants / vowels |
| `latin-extended` | Full Latin Extended block |
| `extended-special` | £€¥₹ ± × ÷ ≠ ≈ ∞ … and more |
| `currency` | £ € ¥ ₹ ₽ ₩ ₪ ₿ ¢ |
| `math` | ± × ÷ ≠ ≈ ∞ ∑ ∏ √ ∫ ∂ |
| `arrows` | ← → ↑ ↓ ↔ ↗ ↘ ↙ ↖ ⇒ ⇐ ⇑ ⇓ |
| `typographic` | « » „ " " ' ' … † ‡ § ¶ • |
| `superscripts` / `subscripts` | ⁰¹²³… / ₀₁₂₃… |
| `russian` | Full Russian Cyrillic |
| `ukrainian` | Full Ukrainian Cyrillic |
| `bulgarian` | Full Bulgarian Cyrillic |
| `serbian-cyrillic` | Full Serbian Cyrillic |
| `greek` | Full Greek alphabet |
| `arabic-letters` | Arabic consonants |
| `arabic-indic` | Arabic-Indic digits ٠١٢… |
| `arabic-punct` | ، ؛ ؟ |
| `hebrew-letters` | Hebrew alphabet |
| `hindi-consonants` / `hindi-vowels` | Devanagari consonants / vowels |
| `cjk` | 3500 common CJK characters |
| `hiragana` | Japanese Hiragana |
| `katakana` | Japanese Katakana |
| `korean-consonants` / `korean-vowels` | Korean Jamo |
| `georgian` | Georgian Mkhedruli |
| `armenian` | Armenian alphabet |
| `thai-consonants` / `thai-vowels` | Thai consonants / vowels |

---

Expand All @@ -92,12 +97,12 @@ Generate passwords programmatically via `GET /api/generate`.

### Parameters

| Param | Default | Description |
|---|---|---|
| `length` | `12` | Password length (integer ≥ 1) |
| `r` | — | Charset requirement — repeatable; same format as URL params above |
| `count` | `1` | Number of passwords to return (integer 1–100) |
| `format` | `json` | `json` → JSON array, `csv` → one password per line |
| Param | Default | Description |
| -------- | ------- | ----------------------------------------------------------------- |
| `length` | `12` | Password length (integer ≥ 1) |
| `r` | — | Charset requirement — repeatable; same format as URL params above |
| `count` | `1` | Number of passwords to return (integer 1–100) |
| `format` | `json` | `json` → JSON array, `csv` → one password per line |

### Examples

Expand Down Expand Up @@ -125,6 +130,74 @@ Invalid parameters return `400` with a JSON error body:

---

## MCP server

The generator is also available as a
[Model Context Protocol](https://modelcontextprotocol.io) server, so MCP clients
(Claude Desktop, Claude Code, etc.) can generate passwords directly. It exposes
two tools:

| Tool | Arguments | Returns |
| ---------------------- | ----------------------------------------------------------------------------------------------------- | --------------------------------------------- |
| `generate_password` | `length` (1–256, default 12), `count` (1–100, default 1), `requirements` (optional array — see below) | The generated password(s), one per line |
| `list_charset_presets` | none | Every preset as `{ key, name, size, sample }` |

Each `requirements` item is either a preset `key` or a literal `charSet`, with
optional `min`/`max` occurrence counts. With no `requirements`, passwords use a
strong default mix of uppercase, lowercase, numbers, and special characters.

```jsonc
// example generate_password arguments
{
"length": 20,
"count": 3,
"requirements": [
{ "preset": "uppercase", "min": 2 },
{ "preset": "numbers", "min": 2 },
{ "charSet": "!@#$", "max": 4 }
]
}
```

### Remote endpoint (hosted)

Streamable HTTP at `POST /mcp` — no install required, just add the URL:

```bash
claude mcp add --transport http password-gen https://synthima-web.jakeave.deno.net/mcp
```

In Claude Desktop / Claude.ai: **Settings → Connectors → Add custom connector**
and paste `https://synthima-web.jakeave.deno.net/mcp`.

### Local (stdio)

Runs on your machine over stdio — nothing leaves the process. Requires
[Deno](https://deno.com/) v2+ and a checkout of this repo:

```bash
claude mcp add password-gen -- deno run -A /absolute/path/to/synthima-web/mcp/stdio.ts
```

Or configure it directly in your client:

```jsonc
{
"mcpServers": {
"password-gen": {
"command": "deno",
"args": ["run", "-A", "mcp/stdio.ts"],
"cwd": "/absolute/path/to/synthima-web"
}
}
}
```

Both transports are thin wrappers over the same generator core as the web app
and REST API.

---

## Setup

**Prerequisites:** [Deno](https://deno.com/) v2+
Expand All @@ -145,18 +218,21 @@ The app will be available at `http://localhost:5173/`.

### Available tasks

| Command | What it does |
|---|---|
| `deno task dev` | Start Vite dev server with HMR |
| `deno task build` | Build for production into `_fresh/` |
| `deno task start` | Serve the production build |
| `deno task check` | Run formatter check, lint, and type-check |
| `deno test` | Run the test suite |
| Command | What it does |
| -------------------- | ------------------------------------------------- |
| `deno task dev` | Start Vite dev server with HMR |
| `deno task build` | Build for production into `_fresh/` |
| `deno task start` | Serve the production build |
| `deno task check` | Run formatter check, lint, and type-check |
| `deno task test` | Run the unit tests (`lib/`, `routes/`) |
| `deno task test:e2e` | Build, then run the stdio + HTTP end-to-end tests |

### Tech stack

- [Fresh 2](https://fresh.deno.dev/) — Deno-native web framework
- [Preact](https://preactjs.com/) + [@preact/signals](https://preactjs.com/guide/v10/signals/) — UI and reactive state
- [Preact](https://preactjs.com/) +
[@preact/signals](https://preactjs.com/guide/v10/signals/) — UI and reactive
state
- [Tailwind CSS v4](https://tailwindcss.com/) — Styling
- [Synthima](https://jsr.io/@jakeave/synthima) — Password generation library
- [Vite](https://vite.dev/) — Build tooling
7 changes: 6 additions & 1 deletion deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
"build": "vite build",
"start": "deno serve -A _fresh/server.js",
"check": "deno fmt --check . && deno lint . && deno check",
"test": "deno test lib/ routes/",
"test:e2e": "deno task build && deno test -A e2e/",
"pre-commit": "deno fmt --check . && deno lint . && deno check",
"pre-push": "deno fmt --check . && deno lint . && deno check && deno test",
"pre-push": "deno fmt --check . && deno lint . && deno check && deno task test",
"install:githooks": "git config core.hooksPath .githooks",
"update": "deno run -A -r jsr:@fresh/update ."
},
Expand All @@ -18,6 +20,9 @@
"exclude": ["**/_fresh/*", ".playwright-mcp/"],
"imports": {
"@jakeave/synthima": "jsr:@jakeave/synthima@^0.0.3",
"@modelcontextprotocol/sdk": "npm:@modelcontextprotocol/sdk@^1.29.0",
"@modelcontextprotocol/sdk/": "npm:/@modelcontextprotocol/sdk@^1.29.0/",
"zod": "npm:zod@^3.25.76",
"fresh": "jsr:@fresh/core@^2.3.3",
"preact": "npm:preact@^10.29.1",
"@preact/signals": "npm:@preact/signals@^2.9.0",
Expand Down
Loading
Loading