Captures what isn't derivable from the code: build_app times out at the MCP layer while continuing to build (poll for the image, don't retry), the app doesn't exist to build_app until platform CI goes green, and the three generator guards (prefix-only hint matching, -er-only syncopation, truncation rejection) each exist because of specific bad output. Also records why lookups must not route through rdap.org: its redirector returns a bare 404 both for "unregistered" and "I don't serve this TLD", which would report every .io as available. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JChWdFJPCRxMBxErb8kUVK
names.poweredbyotm.com
Free business-name finder: type a few keywords, get brandable name ideas, and see which domains are actually free — checked against the registries themselves.
- Stack: Next.js 15 (App Router,
output: "standalone"), React 18, no DB, no auth, public. Name generation runs client-side; availability is a server route because browsers can't do DNS and RDAP servers send no CORS headers. - Repo:
tonym/namesongit.poweredbyotm.com. - Deploy: OTM platform — first-party app (
/apps), built + deployed via the platform MCP, not docker-compose-from-this-repo.
How availability works — and what it costs (nothing)
There is no paid domain API here and no key to manage.
- RDAP (RFC 7482), the registries' own replacement for WHOIS. IANA
publishes a bootstrap file at
data.iana.org/rdap/dns.jsonmapping every TLD to its authoritative RDAP server. We cache it for 24h, resolve the TLD ourselves, and query that server directly —404means unregistered,200means registered. Covers ~1,200 TLDs including.com/.net/.org/.ai/.dev/.app. - DNS
NSlookup, for the TLDs that publish no RDAP server at all —.io,.co,.me,.sh,.gg,.us,.de. NXDOMAIN strongly suggests unregistered, but a registered domain with no delegated nameservers looks identical. These reportunverified-availableand the UI shows them in amber as probably free, never as a confirmed green.
Do not "simplify" this by pointing everything at rdap.org. Its redirector
returns a bare 404 both for "this domain is free" and for "I have no RDAP
server for this TLD" — the two cases are indistinguishable from the status code,
which is exactly the bug that would report every .io as available.
Manners, since we're an anonymous client against other people's public endpoints: 60 domains max per request, 8-wide concurrency, a per-IP token bucket, and a 10-minute result cache.
Name generation
src/lib/generate.ts is pure and deterministic — the same
(keywords, style, seed) always yields the same list, so re-renders don't
reshuffle results and a shared URL reproduces what the sender saw.
Eight strategies (compound, suffix-word, action, blend, coined, clipped, root,
domain hack) over the vocabulary in src/lib/wordbank.ts. The strategies are
cheap and dumb on purpose — quality comes from scoreName, so tune the
scorer before adding more words.
Three guards exist because of specific bad output, and removing them brings it straight back:
expandKeywordsmatches category hints by prefix, never substring — plain containment letrepairmatch theaicategory (rep-ai-r) and drag neural-network vocabulary into an auto-shop brief.syncopateonly drops the schwa from a final-er(flicker→flickr). The original "drop the last vowel" producedcrema→crem,kettle→kettl,security→securit.isTruncationrejects any candidate that is a strict prefix of a word the generator knows. This is what keeps stumps out of the results.
diversify() caps each strategy at ~28% of the returned list. Without it the
top 20 came back as eighteen coinages — scoring alone clumps badly.
Bring-your-own AI key
Optional. /settings stores an Anthropic or Gemini key in localStorage
(names.aiKey) and the browser calls that provider directly — the key never
touches our server. Same pattern as rpo's Google Vision key, for the same
reason: the platform has no per-app env tool, and this is a free public tool that
shouldn't hold a metered credential.
The anthropic-dangerous-direct-browser-access header is correct here and would
be wrong in a product that owns the key. The distinction is whose key it is.
Registrar links
src/lib/registrars.ts. Links are plain today — every affiliateCode is "".
Fill one in and HAS_AFFILIATE_LINKS flips, which turns on the footer
disclosure automatically. Keeping both in one file is deliberate: a disclosure
that can drift out of sync with whether links actually pay is worse than none.
Local development
npm install
npm run dev # http://localhost:3000
npm run typecheck
npm run build
No env vars are needed to run it. The suggestions lightbulb degrades to a quiet
"not configured" state unless GITEA_URL / GITEA_TOKEN / GITEA_REPO are set
(the platform injects those at deploy time).