Skip to content

domainfree: find the domains you can actually register - #8

Merged
ralyodio merged 1 commit into
masterfrom
add-domainfree
Aug 19, 2026
Merged

domainfree: find the domains you can actually register#8
ralyodio merged 1 commit into
masterfrom
add-domainfree

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

Adds domainfree — find the domains you can actually register — and a domain plugin so it's available in moshcode.

Ported from the bash version in profullstack/scripts to this repo's conventions.

Why RDAP and not dig

DNS cannot tell registration apart from configuration:

  • a parked domain resolves fine and is taken
  • a domain registered with no nameservers returns NXDOMAIN — exactly what a name nobody owns returns

Over 8,513 generated candidates, dig NAME | grep "ANSWER: 0" reported 20 registered domains as free, and missed none that were genuinely free.

oubliette.com is the one to remember: registered in 1996, paid through 2034, three nameservers, no A record — so dig returns ANSWER: 0 and it reads as available.

Fine as a cheap prefilter. Wrong as a buy signal.

Usage

domainfree sorrycheck.com sinkstate.com
domainfree --file candidates.txt
generate-names | domainfree --jobs 24
domainfree --all example.com        # show TAKEN rows too

Only available names go to stdout; the summary goes to stderr, so domainfree -f in.txt | wc -l counts what you can buy. ~8,500 names in ~45s at the default concurrency.

An indeterminate response is never reported as available. A 429, 5xx or timeout is retried once, then reported as ERR:<code>, and the exit status is 2. A name wrongly reported free is the only failure mode here that costs real time.

Plugin

New domain plugin, registered in the marketplace next to blog:

  • /domain:free — filter a list down to registerable names
  • /domain:lookup — everything about one name, wrapping the existing domainjson
moshcode plugin marketplace add profullstack/cli-tools
moshcode plugin install domain@cli-tools

Layout

Follows the repo: logic in src/domain-free.ts with an injectable fetcher, a thin bin/ entry guarded by isMain, args through the shared parseArgs, .ts import specifiers, vitest tests that touch no network.

One thing worth flagging

My first version of these tests used real setTimeout delays, and that added wall-clock load made blog.test.ts's concurrent-createPost race test fail — it passed alone but failed in the full suite. That test is timing-sensitive and this change happened to expose it.

I didn't touch it. Instead checkMany's retry pause is now injectable (retryDelayMs), the tests pass 0, and no test in this file uses a real timer. Full suite is green across four consecutive runs (82/82).

Worth knowing separately: blog.test.ts asserts that two concurrent creates produce exactly one success and one EEXIST. Under different scheduling both can succeed with different numbers, so it may resurface as other test files are added.

Verified

  • pnpm test — 82/82, four consecutive runs
  • pnpm typecheck — clean
  • CLI exercised for real: args, stdin, --file, --all, non-.com routing, case/whitespace normalisation, junk filtering, --help/bad-flag/invalid-value exit codes
  • isMain guard confirmed: importing bin/domainfree.ts does not run the tool

Note on installation

install-links.mjs --dry-run currently skips all seven commands, including this one, because ~/.local/bin/* still points at ~/scripts/bin. That's the repo's existing ported-but-not-switched-over state, not something this PR changes. --force takes them over when you want to switch.

There is now a bash domainfree in profullstack/scripts (PR #7) and this TypeScript one — same situation as domainjson. Happy to close the scripts PR if you'd rather this be the only home.

🤖 Generated with Claude Code

Ported from the bash version in profullstack/scripts to this repo's
conventions, and exposed to moshcode as a `domain` plugin alongside `blog`.

Availability is read from RDAP, never inferred from DNS, because DNS cannot
tell registration apart from configuration:

- a parked domain resolves fine and is taken
- a domain registered with no nameservers returns NXDOMAIN, exactly like a
  name nobody owns

Over 8,513 generated candidates the DNS shortcut (`dig NAME | grep
"ANSWER: 0"`) called 20 registered domains free and missed none that were
genuinely free. oubliette.com is the one to remember: registered 1996, paid
through 2034, three nameservers, no A record, so dig reports ANSWER: 0 and it
reads as available. Fine as a cheap prefilter, wrong as a buy signal.

An indeterminate response — 429, 5xx, timeout — is retried once and then
reported as ERR:<code>, never as available, and the exit status is 2. A name
wrongly reported free is the only failure here that costs real time.

Layout follows the repo: logic in src/domain-free.ts with an injectable
fetcher, a thin bin/ entry guarded by isMain, args through the shared
parseArgs, and vitest tests that touch no network.

New plugin `domain` exposes /domain:free and /domain:lookup, the latter
wrapping the existing domainjson so the plugin covers both directions —
one verdict across thousands of names, or everything about one.

One thing worth recording: the first version of these tests used real
setTimeout delays, and the added wall-clock load made blog.test.ts's
concurrent-createPost race fail — it passed alone and failed in the full
suite. That test is timing-sensitive and this change happened to expose it.
Rather than touch it, checkMany's retry pause is now injectable
(retryDelayMs), the tests pass 0, and no test in this file uses a real timer.
Full suite is green across four consecutive runs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

ThreatCrush Security Scan

3 finding(s)

MEDIUM: 1 | LOW: 2

Severity Rule Location
MEDIUM redos-nested-quantifier src/domain-free.ts:56
LOW insecure-temp-file test/blog.test.ts:58
LOW insecure-temp-file test/blog.test.ts:59

Snippets are redacted; ThreatCrush never prints matched credential material.

@ralyodio
ralyodio merged commit dcd9bcb into master Aug 19, 2026
4 checks passed
@ralyodio
ralyodio deleted the add-domainfree branch August 19, 2026 11:44
@ralyodio ralyodio mentioned this pull request Aug 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant