HowlURL is a small Cloudflare Worker that suggests memorable temporary
preview hostnames: two curated wolf-themed words and a random three-digit
number, joined to a base domain. It exists because
test4-final-FINAL.mrdemonwolf.dev is impossible to say out loud, and
because a preview site deserves a name you can read to somebody over a
call.
It suggests names. It provisions nothing.
Name the pack before you build the den.
- Speakable names -
lunar-pack-482.mrdemonwolf.devinstead of a timestamp or a hash. - Runtime base domain - the suffix comes from the
BASE_DOMAINWorker binding, so it changes without touching code. - Cryptographically random - Web Crypto with rejection sampling, no modulo bias and no timestamp seeding.
- One screen, no clutter - a large result, three obvious actions, and nothing to configure.
- Copy hostname or full URL - with a selection-based fallback when the Clipboard API is unavailable.
- On-device history - the last five names live in
localStorageand nowhere else. - Honest about what it is not - the UI never claims a name is reserved, unique, or live.
- No stored data - no database, no accounts, no analytics, no cookies.
- Clone the repository and install dependencies with
bun install. - Start the Worker with
bun run dev:apiin one terminal. - Start the UI with
bun run devin another, then open the address Vite prints. - A name is generated as soon as the page loads. Press Generate another for a new one.
- Create the matching hostname in cPanel before you use it. HowlURL does not do that for you.
The web interface is the main way to use HowlURL. The Worker also exposes a single endpoint:
curl https://howlurl.mrdemonwolf.com/api/generate{
"first": "lunar",
"second": "pack",
"number": 482,
"hostname": "lunar-pack-482.mrdemonwolf.dev",
"url": "https://lunar-pack-482.mrdemonwolf.dev"
}Responses always carry Cache-Control: no-store. A missing or invalid
BASE_DOMAIN returns 500 rather than falling back to a hard-coded
domain.
- The wildcard DNS record already points every unclaimed subdomain at the hosting origin, so nothing needs adding in Cloudflare.
- In cPanel, add the hostname as a domain with its own document root.
- Install or copy the site into that document root, then issue the certificate.
- Names are guessable, so if a preview must stay private put Cloudflare Access or cPanel basic auth on that hostname. The name itself is not a secret.
| Layer | Technology |
|---|---|
| Runtime | Cloudflare Workers |
| Language | TypeScript |
| Frontend | React 19, Vite 7 |
| Styling | Tailwind CSS v4, shadcn/ui |
| Icons | Lucide |
| Static | Cloudflare Workers Static Assets |
| Tests | Vitest |
| Tooling | Bun, Wrangler |
| CI | GitHub Actions |
- Bun 1.3 or newer
- Node.js 20 or newer (Wrangler and Vite run on it)
- A Cloudflare account, for deployment only
-
Clone the repository.
git clone https://github.com/MrDemonWolf/howlurl.git cd howlurl -
Install dependencies.
bun install
-
Generate the Worker binding types.
bun run cf-typegen
-
Run the UI and the Worker side by side.
bun run dev:api
bun run dev
bun run dev- Vite dev server with HMR, proxying/apito port 8787.bun run dev:api-wrangler devon port 8787.bun run build- build the client intodist/.bun run preview- build, then serve the real thing through Wrangler.bun run deploy- build, thenwrangler deploy.bun run typecheck- type-check the Worker and the web app.bun run test- run the Vitest suite.bun run cf-typegen- regenerateworker-configuration.d.tsfromwrangler.jsonc.
- TypeScript in strict mode, with
noUncheckedIndexedAccess, across two project configs so Worker and DOM globals never mix. - Vitest covers domain normalization, random distribution, name format, and every Worker response path.
- CI runs type-check, tests, build, and
wrangler deploy --dry-runon every push and pull request. - Accessibility is part of the definition of done: visible focus rings, a live region for announcements, a 320-pixel layout, and reduced-motion support.
howlurl/
├── src/ Cloudflare Worker
│ ├── generator.ts word lists, domain normalization, random
│ ├── index.ts the single /api/generate route
│ └── generator.test.ts Vitest suite
├── web/ React single-page interface
│ ├── index.html
│ └── src/
│ ├── App.tsx the whole screen
│ ├── components/ui/ shadcn/ui components
│ └── lib/ fetch, clipboard, on-device history
├── dist/ built assets served by the Worker
├── wrangler.jsonc Worker config, bindings, custom domain
├── vite.config.ts
└── AGENTS.md project rules
Released under the MIT License. See LICENSE for the full text.
Questions, ideas, or a better wolf word? Open an issue on the repository or come say hello.
- Discord: Join my server
- Website: mrdemonwolf.com
Made with love by MrDemonWolf, Inc.