domainhack 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- domainhack-0.2.0/LICENSE +21 -0
- domainhack-0.2.0/PKG-INFO +501 -0
- domainhack-0.2.0/README.md +456 -0
- domainhack-0.2.0/pyproject.toml +117 -0
- domainhack-0.2.0/setup.cfg +4 -0
- domainhack-0.2.0/src/domainhack/__init__.py +5 -0
- domainhack-0.2.0/src/domainhack/__main__.py +5 -0
- domainhack-0.2.0/src/domainhack/adapters/__init__.py +0 -0
- domainhack-0.2.0/src/domainhack/adapters/_circuit.py +105 -0
- domainhack-0.2.0/src/domainhack/adapters/_http.py +22 -0
- domainhack-0.2.0/src/domainhack/adapters/_registration.py +128 -0
- domainhack-0.2.0/src/domainhack/adapters/_stderr.py +16 -0
- domainhack-0.2.0/src/domainhack/adapters/_text_sink.py +33 -0
- domainhack-0.2.0/src/domainhack/adapters/_throttle.py +150 -0
- domainhack-0.2.0/src/domainhack/adapters/brand_data.py +49 -0
- domainhack-0.2.0/src/domainhack/adapters/brand_report_render.py +222 -0
- domainhack-0.2.0/src/domainhack/adapters/cached_registrar.py +340 -0
- domainhack-0.2.0/src/domainhack/adapters/composite_writer.py +27 -0
- domainhack-0.2.0/src/domainhack/adapters/console_writer.py +77 -0
- domainhack-0.2.0/src/domainhack/adapters/csv_writer.py +79 -0
- domainhack-0.2.0/src/domainhack/adapters/dns_confirming_writer.py +72 -0
- domainhack-0.2.0/src/domainhack/adapters/dns_resolver.py +120 -0
- domainhack-0.2.0/src/domainhack/adapters/file_word_source.py +51 -0
- domainhack-0.2.0/src/domainhack/adapters/iana_tlds.py +110 -0
- domainhack-0.2.0/src/domainhack/adapters/json_writer.py +51 -0
- domainhack-0.2.0/src/domainhack/adapters/pacing.py +46 -0
- domainhack-0.2.0/src/domainhack/adapters/rdap_bootstrap.py +223 -0
- domainhack-0.2.0/src/domainhack/adapters/rdap_registrar.py +328 -0
- domainhack-0.2.0/src/domainhack/adapters/registrar_catalog.py +120 -0
- domainhack-0.2.0/src/domainhack/adapters/registrar_router.py +72 -0
- domainhack-0.2.0/src/domainhack/adapters/registry_sources.py +192 -0
- domainhack-0.2.0/src/domainhack/adapters/tqdm_progress.py +101 -0
- domainhack-0.2.0/src/domainhack/adapters/whois_registrar.py +386 -0
- domainhack-0.2.0/src/domainhack/cli/__init__.py +0 -0
- domainhack-0.2.0/src/domainhack/cli/app.py +800 -0
- domainhack-0.2.0/src/domainhack/cli/name_cmd.py +222 -0
- domainhack-0.2.0/src/domainhack/cli/tlds_cmd.py +96 -0
- domainhack-0.2.0/src/domainhack/data/hack_words.json +20 -0
- domainhack-0.2.0/src/domainhack/data/iana_tlds.txt +1438 -0
- domainhack-0.2.0/src/domainhack/data/rdap_dns.json +1 -0
- domainhack-0.2.0/src/domainhack/data/registry_sources.json +2367 -0
- domainhack-0.2.0/src/domainhack/data/second_level.json +28 -0
- domainhack-0.2.0/src/domainhack/data/tld_presets.json +7 -0
- domainhack-0.2.0/src/domainhack/domain/__init__.py +0 -0
- domainhack-0.2.0/src/domainhack/domain/entities.py +287 -0
- domainhack-0.2.0/src/domainhack/domain/label_rules.py +96 -0
- domainhack-0.2.0/src/domainhack/domain/parking.py +81 -0
- domainhack-0.2.0/src/domainhack/ports/__init__.py +0 -0
- domainhack-0.2.0/src/domainhack/ports/dns_lookup.py +11 -0
- domainhack-0.2.0/src/domainhack/ports/known_tlds.py +21 -0
- domainhack-0.2.0/src/domainhack/ports/progress.py +32 -0
- domainhack-0.2.0/src/domainhack/ports/registrar.py +27 -0
- domainhack-0.2.0/src/domainhack/ports/result_cache.py +19 -0
- domainhack-0.2.0/src/domainhack/ports/result_writer.py +13 -0
- domainhack-0.2.0/src/domainhack/ports/word_source.py +9 -0
- domainhack-0.2.0/src/domainhack/py.typed +0 -0
- domainhack-0.2.0/src/domainhack/usecases/__init__.py +0 -0
- domainhack-0.2.0/src/domainhack/usecases/brand_candidates.py +195 -0
- domainhack-0.2.0/src/domainhack/usecases/brand_report.py +271 -0
- domainhack-0.2.0/src/domainhack/usecases/check_domains.py +287 -0
- domainhack-0.2.0/src/domainhack/usecases/confirm_dns.py +64 -0
- domainhack-0.2.0/src/domainhack/usecases/estimate_run.py +80 -0
- domainhack-0.2.0/src/domainhack/usecases/filter_words.py +37 -0
- domainhack-0.2.0/src/domainhack/usecases/generate_range.py +107 -0
- domainhack-0.2.0/src/domainhack/usecases/lanes.py +193 -0
- domainhack-0.2.0/src/domainhack/usecases/rank_candidates.py +106 -0
- domainhack-0.2.0/src/domainhack.egg-info/PKG-INFO +501 -0
- domainhack-0.2.0/src/domainhack.egg-info/SOURCES.txt +118 -0
- domainhack-0.2.0/src/domainhack.egg-info/dependency_links.txt +1 -0
- domainhack-0.2.0/src/domainhack.egg-info/entry_points.txt +2 -0
- domainhack-0.2.0/src/domainhack.egg-info/requires.txt +16 -0
- domainhack-0.2.0/src/domainhack.egg-info/top_level.txt +1 -0
- domainhack-0.2.0/tests/test_brand_candidates.py +192 -0
- domainhack-0.2.0/tests/test_brand_report.py +404 -0
- domainhack-0.2.0/tests/test_cache_ttl.py +384 -0
- domainhack-0.2.0/tests/test_cached_registrar.py +282 -0
- domainhack-0.2.0/tests/test_check_domains.py +118 -0
- domainhack-0.2.0/tests/test_circuit.py +140 -0
- domainhack-0.2.0/tests/test_cli.py +51 -0
- domainhack-0.2.0/tests/test_cli_ordering.py +248 -0
- domainhack-0.2.0/tests/test_cli_progress.py +54 -0
- domainhack-0.2.0/tests/test_cli_unit.py +257 -0
- domainhack-0.2.0/tests/test_composite_writer.py +48 -0
- domainhack-0.2.0/tests/test_confirm_dns.py +263 -0
- domainhack-0.2.0/tests/test_console_writer.py +73 -0
- domainhack-0.2.0/tests/test_csv_writer.py +116 -0
- domainhack-0.2.0/tests/test_dns_resolver.py +129 -0
- domainhack-0.2.0/tests/test_dropping_output.py +147 -0
- domainhack-0.2.0/tests/test_entities.py +80 -0
- domainhack-0.2.0/tests/test_exit_codes.py +326 -0
- domainhack-0.2.0/tests/test_file_word_source.py +46 -0
- domainhack-0.2.0/tests/test_filter_words.py +38 -0
- domainhack-0.2.0/tests/test_generate_range.py +82 -0
- domainhack-0.2.0/tests/test_http_identity.py +109 -0
- domainhack-0.2.0/tests/test_iana_tlds.py +98 -0
- domainhack-0.2.0/tests/test_integration.py +89 -0
- domainhack-0.2.0/tests/test_json_writer.py +102 -0
- domainhack-0.2.0/tests/test_label_validation.py +373 -0
- domainhack-0.2.0/tests/test_multi_label_tld.py +196 -0
- domainhack-0.2.0/tests/test_multi_tld.py +249 -0
- domainhack-0.2.0/tests/test_name_cli.py +241 -0
- domainhack-0.2.0/tests/test_netguard.py +117 -0
- domainhack-0.2.0/tests/test_offline_resolution.py +97 -0
- domainhack-0.2.0/tests/test_parallel_check.py +754 -0
- domainhack-0.2.0/tests/test_progress.py +152 -0
- domainhack-0.2.0/tests/test_rank_candidates.py +135 -0
- domainhack-0.2.0/tests/test_rdap_bootstrap.py +222 -0
- domainhack-0.2.0/tests/test_rdap_registrar.py +481 -0
- domainhack-0.2.0/tests/test_registrar_catalog.py +66 -0
- domainhack-0.2.0/tests/test_registrar_integration.py +97 -0
- domainhack-0.2.0/tests/test_registrar_port.py +12 -0
- domainhack-0.2.0/tests/test_registrar_router.py +96 -0
- domainhack-0.2.0/tests/test_registration_details.py +330 -0
- domainhack-0.2.0/tests/test_registry_sources.py +121 -0
- domainhack-0.2.0/tests/test_taken_details.py +585 -0
- domainhack-0.2.0/tests/test_throttle.py +192 -0
- domainhack-0.2.0/tests/test_tlds_cmd.py +75 -0
- domainhack-0.2.0/tests/test_whois_backoff.py +138 -0
- domainhack-0.2.0/tests/test_whois_fixtures.py +58 -0
- domainhack-0.2.0/tests/test_whois_registrar.py +357 -0
domainhack-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Pablo Alaniz
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: domainhack
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Find domain hacks hiding in real words
|
|
5
|
+
Author: Pablo Alaniz
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/PabloAlaniz/find-amazing-domains
|
|
8
|
+
Project-URL: Repository, https://github.com/PabloAlaniz/find-amazing-domains
|
|
9
|
+
Project-URL: Issues, https://github.com/PabloAlaniz/find-amazing-domains/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/PabloAlaniz/find-amazing-domains/blob/main/CHANGELOG.md
|
|
11
|
+
Keywords: domain,domain-hack,tld,rdap,whois,availability,cli
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
24
|
+
Classifier: Topic :: Internet :: Name Service (DNS)
|
|
25
|
+
Classifier: Topic :: Utilities
|
|
26
|
+
Classifier: Typing :: Typed
|
|
27
|
+
Requires-Python: >=3.10
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
License-File: LICENSE
|
|
30
|
+
Requires-Dist: dnspython<3,>=2.6
|
|
31
|
+
Requires-Dist: httpx>=0.27
|
|
32
|
+
Requires-Dist: idna>=3.7
|
|
33
|
+
Requires-Dist: tqdm>=4.66
|
|
34
|
+
Provides-Extra: dev
|
|
35
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
36
|
+
Requires-Dist: pytest-cov>=6.0; extra == "dev"
|
|
37
|
+
Requires-Dist: coverage>=7.10; extra == "dev"
|
|
38
|
+
Requires-Dist: ruff~=0.16.10; extra == "dev"
|
|
39
|
+
Requires-Dist: mypy~=2.4.0; extra == "dev"
|
|
40
|
+
Requires-Dist: pre-commit>=4.0; extra == "dev"
|
|
41
|
+
Requires-Dist: types-tqdm>=4.66; extra == "dev"
|
|
42
|
+
Provides-Extra: docs
|
|
43
|
+
Requires-Dist: mkdocs-material>=9.5; extra == "docs"
|
|
44
|
+
Dynamic: license-file
|
|
45
|
+
|
|
46
|
+
# domainhack
|
|
47
|
+
|
|
48
|
+
[](https://pypi.org/project/domainhack/)
|
|
49
|
+
[](https://pypi.org/project/domainhack/)
|
|
50
|
+
[](https://github.com/PabloAlaniz/find-amazing-domains/actions/workflows/lint-typecheck-test.yml)
|
|
51
|
+
[](https://codecov.io/gh/PabloAlaniz/find-amazing-domains)
|
|
52
|
+
[](LICENSE)
|
|
53
|
+
|
|
54
|
+
**Find domain hacks hiding in real words, and check them live against the registries.**
|
|
55
|
+
|
|
56
|
+
Some words have a secret: they end in a top-level domain. The Spanish word *plato* (plate) becomes `pla.to`. *Abasto* (supply) becomes `abas.to`. *Grato* (pleasant) becomes `gra.to`. domainhack finds those words, asks the registry whether each domain is really free, and tells you what to register.
|
|
57
|
+
|
|
58
|
+

|
|
59
|
+
|
|
60
|
+
## Install
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pipx install domainhack # or: pip install domainhack
|
|
64
|
+
uvx domainhack name myname # run without installing
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Python 3.10 or newer. No API keys, no accounts, no captchas: domainhack talks to the registries directly.
|
|
68
|
+
|
|
69
|
+
## Why domainhack
|
|
70
|
+
|
|
71
|
+
- **It asks the registry, not a reseller.** Every generic TLD is checked over RDAP through the IANA bootstrap, and 170 of the 248 country-code TLDs over RDAP or port-43 WHOIS, each verified live with a registered and a random name. Requests are paced per host, back off on 429/503 as RFC 7480 asks, and a circuit breaker skips a host that stops answering.
|
|
72
|
+
- **It knows when "available" lies.** A public-DNS cross-check flags names the registry calls free but that already have nameservers. Taken names come with their registration date, expiry date, registrar, the parking or aftermarket service behind them, and whether they are about to drop (redemption or pending delete).
|
|
73
|
+
- **One command for a name.** `domainhack name acme` checks the exact name under a set of TLDs, finds hacks that spell it (`acme` + studio -> `acmestud.io`), optionally tries brand variants, and ends with a ranked recommendation.
|
|
74
|
+
- **It handles the hard cases.** Second-level suffixes (`com.ar`, `co.uk`), IDNs (`ñandú.de`), per-TLD label rules, a SQLite cache with TTLs that match how fast each kind of result goes stale, and CSV, JSON Lines, markdown and JSON output.
|
|
75
|
+
|
|
76
|
+
## How it compares
|
|
77
|
+
|
|
78
|
+
| | domainhack | [domaingrep](https://github.com/ysm-dev/domaingrep) | [tldx](https://github.com/brandonyoungdev/tldx) | [DomainHacks.info](https://domainhacks.info/) |
|
|
79
|
+
|---|---|---|---|---|
|
|
80
|
+
| Finds domain hacks (word split at a TLD) | yes | yes | no: prefix/suffix permutations | yes |
|
|
81
|
+
| Availability source | registry RDAP + WHOIS | DNS | RDAP | registry lookups (about 900 of 1466 TLDs) |
|
|
82
|
+
| Cross-checks "available" against DNS | yes | DNS is the source | not documented | not documented |
|
|
83
|
+
| Taken-name details (expiry, registrar, parking, dropping) | yes | not documented | `_for-sale` TXT record | not documented |
|
|
84
|
+
| Second-level suffixes (`com.ar`, `co.uk`) and IDN | yes | not documented | not documented | not documented |
|
|
85
|
+
| Country-code TLDs you can check | 170 of 248, each verified live | "every TLD", list not published | not published | about 900 TLDs |
|
|
86
|
+
| Runs as | CLI, Python 3.10+ | CLI, Rust | CLI, Go | website |
|
|
87
|
+
| License | MIT | MIT | Apache-2.0 | proprietary |
|
|
88
|
+
|
|
89
|
+
"Not documented" means the project's README or site did not say so in October 2026; corrections are welcome in an issue.
|
|
90
|
+
|
|
91
|
+
## Find a domain for a name
|
|
92
|
+
|
|
93
|
+
You have a name and want a domain for it. One command checks the exact name under a set of TLDs, looks for domain hacks that spell it, optionally tries brand variants, and recommends what to register:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
domainhack name sumanda
|
|
97
|
+
domainhack name sumanda --tlds startup,latam --variants --format markdown --output sumanda.csv
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
Domains for 'sumanda'
|
|
102
|
+
TLDs: com, app, io, ai, co, dev, so, xyz
|
|
103
|
+
|
|
104
|
+
Exact name
|
|
105
|
+
sumanda.com taken since 2015-11-12, expires 2026-11-12, registrar Nikko Reg LLP, parked: domainrecover
|
|
106
|
+
sumanda.app available DNS: not delegated
|
|
107
|
+
sumanda.io available DNS: not delegated
|
|
108
|
+
...
|
|
109
|
+
|
|
110
|
+
Domain hacks
|
|
111
|
+
no TLD is a suffix of 'sumanda'
|
|
112
|
+
sumanda.studio available sumanda studio: a creative or production studio -- DNS: not delegated
|
|
113
|
+
sumandastud.io available sumanda studio: a creative or production studio -- DNS: not delegated
|
|
114
|
+
sumandarad.io available sumanda radio: a radio show or podcast -- DNS: not delegated
|
|
115
|
+
sumandagam.es unsupported sumanda games: games -- no registrar supports .es (restricted: ...)
|
|
116
|
+
...
|
|
117
|
+
|
|
118
|
+
Recommendation
|
|
119
|
+
1. sumanda.ai exact name, strong TLD .ai
|
|
120
|
+
...
|
|
121
|
+
- sumanda.com is taken (expires 2026-11-12); parked at domainrecover: may be purchasable from the owner
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
What it checks:
|
|
125
|
+
|
|
126
|
+
- **Exact name** under every TLD of `--tlds`. It takes presets and TLDs, mixed: `--tlds startup,com.ar,la`. The presets are `startup` (the default: com, app, io, ai, co, dev, so, xyz), `latam` (com.ar, ar, com.mx, mx, com.co, co, com.br, cl, pe), `classic` (com, net, org) and `all-supported` (every TLD the tool can check). A TLD the tool cannot check (e.g. `.es`, whose registry only serves accredited users) is reported as unsupported, with the reason.
|
|
127
|
+
- **Domain hacks**: the name split at a TLD it ends with (`plato` -> `pla.to`), and the name plus a curated word ending in a TLD (`sumanda` + studio -> `sumandastud.io`, or `sumanda.studio` where the word is itself a TLD). The words and their meanings live in `src/domainhack/data/hack_words.json`. When the name ends in no TLD, the report says so.
|
|
128
|
+
- **Brand variants** (`--variants`): `get`/`use`/`try`/`hola`/`my` + name and name + `hq`/`app`/`labs`/`studio`. They are checked under `.com` and the first 3 other requested TLDs only, so there are at most 36 of them.
|
|
129
|
+
|
|
130
|
+
The report lists the exact names by TLD, the hacks, the available variants and the taken names. Taken names come with their details (registration and expiry dates, parking service, dropping status). It also names everything it could not verify (registry error or unreachable, no registrar for the TLD) and every DNS conflict (the registry says available, but DNS has nameservers). `--format` picks `text` (default), `markdown` (to share) or `json`. `--output FILE` also saves the raw results as CSV or JSON Lines, like `check`.
|
|
131
|
+
|
|
132
|
+
The recommendation ranks available names only:
|
|
133
|
+
|
|
134
|
+
- an exact name under `.com`;
|
|
135
|
+
- then an exact name under `.app`/`.io`/`.ai`/`.co`;
|
|
136
|
+
- then an exact name under another TLD;
|
|
137
|
+
- then a `.com` variant;
|
|
138
|
+
- then a hack that spells the name;
|
|
139
|
+
- then other variants;
|
|
140
|
+
- then hacks with an extra word.
|
|
141
|
+
|
|
142
|
+
Shorter names win within a tier. Taken, dropping, DNS-conflict and unverifiable names are never recommended.
|
|
143
|
+
|
|
144
|
+
`--confirm-dns` / `--no-confirm-dns` turns the DNS cross-check on or off (on by default when a resolver is available). The registry check uses the same options as `check`: `--delay`, `--parallel`, `--no-cache`, `--cache-*`, `--contact` and `--no-progress`. The exit status follows `check`: it is 1 when some check ended in an error, and the report is still printed.
|
|
145
|
+
|
|
146
|
+
## Examples
|
|
147
|
+
|
|
148
|
+
| Word | Language | Domain | Meaning |
|
|
149
|
+
|------|----------|--------|---------|
|
|
150
|
+
| plato | Spanish | `pla.to` | plate / dish |
|
|
151
|
+
| grato | Spanish | `gra.to` | pleasant |
|
|
152
|
+
| abasto | Spanish | `abas.to` | supply |
|
|
153
|
+
| veto | English | `ve.to` | veto |
|
|
154
|
+
| gusto | English | `gus.to` | gusto |
|
|
155
|
+
| pinto | English | `pin.to` | pinto |
|
|
156
|
+
|
|
157
|
+
## How It Works
|
|
158
|
+
|
|
159
|
+
The tool has **two modes** to generate candidate domains, and then checks their availability against the registrar.
|
|
160
|
+
|
|
161
|
+
### Mode 1: Word list -- find domains that are real words
|
|
162
|
+
|
|
163
|
+
Feed a dictionary file. The tool finds words ending in a TLD suffix, splits them, and checks the resulting domain.
|
|
164
|
+
|
|
165
|
+
```
|
|
166
|
+
plato --> ends in "to" --> pla.to --> AVAILABLE / TAKEN
|
|
167
|
+
grato --> ends in "to" --> gra.to --> AVAILABLE / TAKEN
|
|
168
|
+
hello --> no TLD match --> (skipped)
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
This mode produces **memorable, meaningful domains** because every candidate is a real word.
|
|
172
|
+
|
|
173
|
+
### Mode 2: Brute-force -- try all letter combinations
|
|
174
|
+
|
|
175
|
+
Generate all combinations from `a` to `zzzzzz` (max length 6) and check each one as a domain. No dictionary needed.
|
|
176
|
+
|
|
177
|
+
```
|
|
178
|
+
a.to, b.to, ..., z.to, aa.to, ab.to, ..., zz.to, aaa.to, ...
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
This mode is useful for finding **short, available domains** regardless of whether they form real words.
|
|
182
|
+
|
|
183
|
+
## Usage
|
|
184
|
+
|
|
185
|
+
### Filter only (no availability check)
|
|
186
|
+
|
|
187
|
+
List which words from a file would form domain hacks, without querying the registrar. Useful to preview candidates before a long check run.
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
# Spanish words ending in "to", at least 5 letters
|
|
191
|
+
domainhack --tld to filter data/words_es.txt --min-length 5
|
|
192
|
+
|
|
193
|
+
# English words ending in "in"
|
|
194
|
+
domainhack --tld in filter data/words_en.txt --min-length 4
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Check availability -- word list mode
|
|
198
|
+
|
|
199
|
+
Filter a word list **and** check each domain against the registrar in one step.
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
# Check Spanish .to domains with 1s delay between requests
|
|
203
|
+
domainhack --tld to check --file data/words_es.txt --delay 1.0
|
|
204
|
+
|
|
205
|
+
# Preview which domains would be checked (no HTTP requests)
|
|
206
|
+
domainhack --tld to check --file data/words_es.txt --dry-run
|
|
207
|
+
|
|
208
|
+
# Also show taken domains in the output
|
|
209
|
+
domainhack --tld to check --file data/words_es.txt --show-taken
|
|
210
|
+
|
|
211
|
+
# Show taken domains that are about to drop (redemption or pending delete)
|
|
212
|
+
domainhack --tld to check --file data/words_es.txt --show-dropping
|
|
213
|
+
|
|
214
|
+
# Only the 50 best candidates per TLD (shortest names first)
|
|
215
|
+
domainhack --tld to,it check --file data/words_es.txt --limit 50
|
|
216
|
+
|
|
217
|
+
# Check in word-list order instead
|
|
218
|
+
domainhack --tld to check --file data/words_es.txt --order input
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Taken names in their registry's redemption period or pending delete are "dropping": they may become free soon. They print as `TAKEN (dropping: pending delete, expires 2026-11-02): x.to` with `--show-taken` or `--show-dropping`, and the summary counts them (`12 taken (1 dropping)`). Statuses and expiry dates come from RDAP; for WHOIS TLDs they are read only when the reply uses EPP status codes and ISO dates (e.g. `.it`).
|
|
222
|
+
|
|
223
|
+
Other taken names print with what the registry says about them, so you can tell an active site from a name parked for sale:
|
|
224
|
+
|
|
225
|
+
```
|
|
226
|
+
TAKEN: sumanda.com (since 2015-11-12, expires 2026-11-12, parked: domainrecover)
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Only the known parts are shown. `parked:` names the parking or aftermarket service the nameservers point to (`domainrecover`, `sedo`, `parkingcrew`, `bodis`, `afternic`, `dan.com`, `hugedomains`, `godaddy-parked`, `namebright`, ...; the curated list is in `domain/parking.py`). RDAP gives the registration date, registrar and nameservers; WHOIS gives the creation date and nameservers when the reply has `Creation Date:`/`Created:` in ISO form and `Name Server:`/`nserver:` lines.
|
|
230
|
+
|
|
231
|
+
Registries rightly limit how many automated lookups a client may make, so the best candidates are checked first. `--order` picks the order:
|
|
232
|
+
|
|
233
|
+
- `score` (default): shortest SLD first, then shortest full word, then alphabetical. The order is deterministic.
|
|
234
|
+
- `alpha`: alphabetical by domain name.
|
|
235
|
+
- `input`: the order of the word list.
|
|
236
|
+
|
|
237
|
+
`--limit N` checks at most N domains per TLD, taken in that order. `--dry-run` lists exactly what a run with the same options would check, and the progress bar total accounts for the limit.
|
|
238
|
+
|
|
239
|
+
### Check availability -- brute-force mode
|
|
240
|
+
|
|
241
|
+
Generate all letter combinations up to a given length (max 6) and check each one.
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
# Try all 1-2 letter .to domains (26 + 676 = 702 combinations)
|
|
245
|
+
domainhack --tld to check --range-max 2
|
|
246
|
+
|
|
247
|
+
# Try up to 3 letters, but stop at "ba"
|
|
248
|
+
domainhack --tld to check --range-max 3 --range-end "ba"
|
|
249
|
+
|
|
250
|
+
# Preview combinations without checking
|
|
251
|
+
domainhack --tld to check --range-max 2 --dry-run
|
|
252
|
+
|
|
253
|
+
# Only the first 500 combinations per TLD
|
|
254
|
+
domainhack --tld to,io check --range-max 3 --limit 500
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Combinations are generated shortest first, lazily (`--range-max 6` is over 300 million names), so range mode is never sorted and `--order` only accepts `input` there.
|
|
258
|
+
|
|
259
|
+
Before a brute-force run, domainhack prints the estimated number of queries and the minimum run time. The estimate accounts for TLDs that share a host and for each host's pacing:
|
|
260
|
+
|
|
261
|
+
```
|
|
262
|
+
Estimated 36,556 queries to 2 hosts (rdap.tonicregistry.to, rdap.identitydigital.services): at least 5 h 4 min at the current pacing.
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Runs over 10,000 queries are refused with exit status 2 unless you pass `--yes`. Narrow the run with `--range-end` or `--limit` instead where you can: registry terms of use forbid bulk querying. `--dry-run` never queries anything, so it is exempt.
|
|
266
|
+
|
|
267
|
+
### Multiple TLDs
|
|
268
|
+
|
|
269
|
+
Pass a comma-separated list to `--tld`. Each TLD is routed to a registrar that supports it; unsupported TLDs are skipped with a warning.
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
# Words that form .to, .io or .it hacks (prints "word -> sld.tld")
|
|
273
|
+
domainhack --tld to,io,it filter data/words_en.txt
|
|
274
|
+
|
|
275
|
+
# Check all of them
|
|
276
|
+
domainhack --tld to,io,it check --file data/words_en.txt
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
Second-level suffixes work too: `--tld com.ar,com.mx,co.uk`. A word matches by plain concatenation, ignoring the suffix's dots (`fotocomar` -> `foto.com.ar`). Each one is checked against the registry of its top-level domain (`com.ar` -> `rdap.nic.ar`, `com.mx` -> `whois.mx`), with the whole name in the query.
|
|
280
|
+
|
|
281
|
+
#### DNS confirmation
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
domainhack --tld io,com.ar check --file words.txt --confirm-dns
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
`--confirm-dns` looks up every available or taken name in public DNS (NS, then A/AAAA, 3 s timeout, system resolver) as results come in. DNS is a second opinion, never the verdict. If the registry says a name is available but it has NS records, it prints as
|
|
288
|
+
|
|
289
|
+
```
|
|
290
|
+
AVAILABLE? x.io -- registry says free but DNS has NS records
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
and the summary counts these names. Treat them as taken until a registrar confirms otherwise. NXDOMAIN means "not in DNS". It is not an error, because many registered names are not delegated. Timeouts and SERVFAIL are recorded as DNS errors and never stop the run.
|
|
294
|
+
|
|
295
|
+
#### Unreachable registries
|
|
296
|
+
|
|
297
|
+
When a registry does not answer (timeouts, rate limits, an open circuit breaker), the summary names it, e.g. `warning: registry for .com.ar did not respond (3 errors); re-run later`. A TLD with only some failed checks gets `registry for .io failed 2 of 10 checks`. Errors are never cached, so the re-run checks only those names again.
|
|
298
|
+
|
|
299
|
+
#### Parallel checks
|
|
300
|
+
|
|
301
|
+
Different registry hosts are checked in parallel, each with one request in flight at a time. TLDs served by the same host share it: `.io`, `.sh`, `.ac` and `.me` (Identity Digital) are checked one after another, while `.to` and `.it` go ahead independently. One slow or unresponsive host (e.g. `whois.nic.it` at one query per 4 s) no longer holds up the others.
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
# Up to 8 hosts at once (default: 4)
|
|
305
|
+
domainhack --tld to,io,it,co,de check --file data/words_en.txt --parallel 8
|
|
306
|
+
|
|
307
|
+
# Strictly one domain at a time, in check order (the old behaviour)
|
|
308
|
+
domainhack --tld to,io,it check --file data/words_en.txt --parallel 1
|
|
309
|
+
|
|
310
|
+
# Parallel, but print and save results in check order
|
|
311
|
+
domainhack --tld to,io,it check --file data/words_en.txt --keep-order
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
- **Output order is completion order.** With `--parallel` above 1, results print (and are saved) as they come back, so domains from a fast host come out ahead of a slow one. Within one host they keep the check order. `--keep-order` holds results back to restore the check order (a slow host then delays the output, not the checks). `--parallel 1` checks in order, so output is in order too.
|
|
315
|
+
- **What gets checked does not change.** `--order` and `--limit` pick the domains before they are dispatched, and each host gets its share in that order.
|
|
316
|
+
- **Cache hits skip the queue.** Cached results are answered before dispatch and never wait for a host.
|
|
317
|
+
- **Memory stays bounded in brute-force mode.** Candidates are generated lazily. Each host has a queue of at most 16 waiting domains; when it is full, generation pauses until that host catches up. At most `hosts x 16` candidates (plus their results) are in memory, however large `--range-max` is.
|
|
318
|
+
- **Ctrl-C** stops dispatching at once, gives checks in flight up to a second to finish (their results are kept), and exits with status 130 as before.
|
|
319
|
+
|
|
320
|
+
### Saving results
|
|
321
|
+
|
|
322
|
+
```bash
|
|
323
|
+
# CSV (format inferred from the extension)
|
|
324
|
+
domainhack --tld to check --file data/words_es.txt --output results.csv
|
|
325
|
+
|
|
326
|
+
# JSON Lines
|
|
327
|
+
domainhack --tld to check --file data/words_es.txt --output results.jsonl
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
Rows are written as they are checked, so an interrupted run (Ctrl-C) keeps everything checked so far. Columns: `fqdn` (ASCII, what was queried), `display` (as written, e.g. `ñandú.de`), `word`, `sld`, `tld`, `availability`, `error_message`, `statuses`, `expires_at`, `registered_at`, `registrar`, `nameservers`, `parked_hint`, `dns_nameservers`, `dns_conflict`. New columns are always appended, so existing positions stay stable.
|
|
331
|
+
|
|
332
|
+
- `statuses`: registry statuses of a taken name (RFC 9083 form, e.g. `client transfer prohibited`).
|
|
333
|
+
- `expires_at` / `registered_at`: expiration and registration dates in ISO 8601 UTC (`2026-11-30T07:38:29Z`).
|
|
334
|
+
- `registrar`: the sponsoring registrar's name (RDAP only).
|
|
335
|
+
- `nameservers`: NS hosts the registry lists, lowercase without the trailing dot.
|
|
336
|
+
- `parked_hint`: the parking/aftermarket service those nameservers belong to (e.g. `domainrecover`), empty if none is recognised.
|
|
337
|
+
- `dns_nameservers`: NS hosts public DNS returned, when DNS was consulted.
|
|
338
|
+
- `dns_conflict`: `true` when the registry said available but DNS shows a delegation (do not trust it as free), else `false`.
|
|
339
|
+
|
|
340
|
+
Lists are joined with `;` in CSV and are arrays in JSON; unknown values are empty in CSV and `null` (dates) or empty in JSON.
|
|
341
|
+
|
|
342
|
+
Only results go to stdout; warnings, errors and the final summary go to stderr, so stdout can be piped.
|
|
343
|
+
|
|
344
|
+
### Name validation and international names
|
|
345
|
+
|
|
346
|
+
Candidates are validated before any query: letters, digits and hyphens only, 1-63 characters, plus per-TLD rules (e.g. `.it` requires at least 3 characters; `.com.ar` allows at most 50 and accepts `ñ` and accents, per NIC Argentina's rules). Names with accents or `ñ` are converted to their IDN form (`ñandú.de` -> `xn--and-6ma2c.de`) only for TLDs that accept IDNs; elsewhere they are skipped. Skipped candidates are counted on stderr.
|
|
347
|
+
|
|
348
|
+
Word lists are read as UTF-8; use `--encoding latin-1` for older lists.
|
|
349
|
+
|
|
350
|
+
### Progress and caching
|
|
351
|
+
|
|
352
|
+
- A progress bar is shown on stderr when running in a terminal. Disable it with `--no-progress`.
|
|
353
|
+
- Results are cached in SQLite (`$XDG_CACHE_HOME/domainhack/results.sqlite3`), so re-runs skip domains already checked. How long a result is reused depends on what it says:
|
|
354
|
+
|
|
355
|
+
| Result | Reused for |
|
|
356
|
+
|--------|------------|
|
|
357
|
+
| Available | 24 hours (`--cache-ttl-available HOURS`): someone else can register it at any moment |
|
|
358
|
+
| Taken, dropping (redemption / pending delete) | 24 hours |
|
|
359
|
+
| Taken, expiration date known | until that date, at least 24 hours and at most 90 days |
|
|
360
|
+
| Taken, no expiration date | 30 days |
|
|
361
|
+
| Error | never cached |
|
|
362
|
+
|
|
363
|
+
`--cache-ttl HOURS` caps all of these (e.g. `--cache-ttl 1` rechecks anything older than an hour). Use `--no-cache` to skip the cache and `--cache-path PATH` to move it. Cache files from older versions are upgraded in place. The cache keeps registration details (statuses, dates, registrar, nameservers, parking hint) but never DNS evidence, which is looked up fresh.
|
|
364
|
+
|
|
365
|
+
## Supported TLDs
|
|
366
|
+
|
|
367
|
+
Availability is checked against the registry directly, with no captchas or API keys. Ask the tool what it covers; it answers offline:
|
|
368
|
+
|
|
369
|
+
```bash
|
|
370
|
+
domainhack tlds # summary + every country-code TLD
|
|
371
|
+
domainhack tlds cl es com.ar it # how these are checked
|
|
372
|
+
domainhack tlds --unsupported # what cannot be checked, and why
|
|
373
|
+
domainhack tlds --all --json # every TLD, machine-readable
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
```
|
|
377
|
+
170 of 248 TLDs can be checked (102 rdap, 68 whois, 78 unsupported)
|
|
378
|
+
.cl whois whois.nic.cl 2026-10-07
|
|
379
|
+
.es unsupported restricted: the registry provides automated lookups to accredited users only; ...
|
|
380
|
+
.com.ar rdap rdap.nic.ar 2026-10-06
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
Every generic TLD (com, app, dev...) is covered through the IANA RDAP bootstrap. Registries missing from it live in `data/registry_sources.json`: RDAP endpoints, WHOIS servers with their "not found" patterns and pacing, and the TLDs that cannot be checked with the reason why (no published lookup service, lookups for accredited users only, terms of use that do not cover automated lookups...), plus a link to check by hand where there is one. Each supported entry was verified live with a registered and a random name, and `tests/fixtures/whois/` keeps the real replies so a wrong pattern (which would mean false "available" results) fails the tests. A weekly CI job re-checks a rotating sample against the live registries.
|
|
384
|
+
|
|
385
|
+
A snapshot of the IANA RDAP bootstrap ships with the package, so the backend chosen for each TLD is the same online and offline.
|
|
386
|
+
|
|
387
|
+
The package also bundles IANA's list of every TLD (`data/iana_tlds.txt`, snapshot with its `# Version` line) and a curated list of common registrable second-level suffixes, Latin America first (`data/second_level.json`, checked against the [Public Suffix List](https://publicsuffix.org/)). `adapters/iana_tlds.IanaTldList` answers "is this a public suffix?" and "which suffixes does this name end with?" (`plato` -> `to`, `fotocomar` -> `com.ar`, `ar`). Both lists are snapshot-only, so results are deterministic offline; refresh them by replacing the files.
|
|
388
|
+
|
|
389
|
+
Note: "available" means *not registered*. Premium or reserved names may still show as available; confirm with a registrar before buying.
|
|
390
|
+
|
|
391
|
+
### Being a good citizen
|
|
392
|
+
|
|
393
|
+
- Requests identify the tool: `User-Agent: domainhack/<version> (+repo URL)`. Add `--contact you@example.com` (or `DOMAINHACK_CONTACT`) to include a `From` header.
|
|
394
|
+
- Each registry host gets at most one request at a time from domainhack, however high `--parallel` is. Parallelism only goes across hosts (RFC 9112 §9.4 asks clients to limit simultaneous connections per server).
|
|
395
|
+
- Requests to each host are spaced (`--delay`, with stricter per-server minimums, e.g. 4 s for `whois.nic.it`). Each wait gets ±20% random jitter.
|
|
396
|
+
- The spacing adapts to each host (RFC 7480 §5.5). A 429, a 503, a timeout or WHOIS rate-limit text doubles that host's interval, up to 60 s. Each real answer shrinks it by 10%, back down to `--delay`.
|
|
397
|
+
- RDAP retries a 429, a 5xx, a timeout or a dropped connection at most twice. It waits for `Retry-After` when the server sends one (a 429 asking for more than 30 s is not retried, but the host is still held back, for up to 60 s). Otherwise it waits a "full jitter" exponential backoff, `random(0, min(30, 2 × 2^n))` seconds. WHOIS retries a timeout or a rate-limited reply only once.
|
|
398
|
+
- A check counts once, however many retries it takes: it slows its host down at most once and counts as at most one failure for the circuit breaker below.
|
|
399
|
+
- Checks go best-first, `--limit` caps them per TLD, and large brute-force runs need `--yes` (see above).
|
|
400
|
+
- If a host stops responding (3 consecutive failures), its domains are skipped for 60 s instead of waiting on timeouts; skipped checks are reported as errors and re-checked on the next run.
|
|
401
|
+
|
|
402
|
+
### Exit codes
|
|
403
|
+
|
|
404
|
+
| Code | Meaning |
|
|
405
|
+
|------|---------|
|
|
406
|
+
| 0 | Completed, no errors |
|
|
407
|
+
| 1 | Completed with some failed checks, or a runtime error |
|
|
408
|
+
| 2 | Usage error |
|
|
409
|
+
| 130 | Interrupted (Ctrl-C) |
|
|
410
|
+
|
|
411
|
+
## Word Lists
|
|
412
|
+
|
|
413
|
+
The sample files in `data/samples/` contain ~20 words each for quick testing. For serious domain hunting, you'll need full dictionaries:
|
|
414
|
+
|
|
415
|
+
- **Spanish**: Any comprehensive Spanish word list (600K+ words recommended)
|
|
416
|
+
- **English**: `/usr/share/dict/words` on macOS/Linux, or download from open word list repositories
|
|
417
|
+
|
|
418
|
+
Place them as `data/words_es.txt` and `data/words_en.txt` (these paths are gitignored).
|
|
419
|
+
|
|
420
|
+
## Architecture
|
|
421
|
+
|
|
422
|
+
Built with **Clean Architecture** and **SOLID principles**:
|
|
423
|
+
|
|
424
|
+
```
|
|
425
|
+
domain/ Pure entities: TLD, DomainHack, Availability
|
|
426
|
+
ports/ Abstract interfaces: WordSource, RegistrarClient, ResultWriter,
|
|
427
|
+
ResultCache, DnsLookup, KnownTlds
|
|
428
|
+
usecases/ Business logic: FilterWords, RangeCandidates, RankCandidates,
|
|
429
|
+
EstimateRun, CheckDomains (sequential, or parallel lanes
|
|
430
|
+
per registry host), ConfirmWithDns,
|
|
431
|
+
BrandCandidates + BrandReport (`name`)
|
|
432
|
+
adapters/ Implementations: FileWordSource, RDAP/WHOIS registrars,
|
|
433
|
+
RegistrarRouter, CachedRegistrar, Console/CSV/JSON writers, tqdm progress,
|
|
434
|
+
DnsPythonLookup (dnspython), IanaTldList
|
|
435
|
+
cli/ Composition root: argparse + dependency injection
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
Adding support for a new TLD registrar or output format requires implementing a single interface -- zero changes to existing code.
|
|
439
|
+
|
|
440
|
+
## Why .to?
|
|
441
|
+
|
|
442
|
+
`.to` is the country code top-level domain (ccTLD) for Tonga. It's popular for domain hacks because many Spanish words end in "-to" (a common suffix in verb conjugations and nouns). English has plenty too: veto, photo, gusto, motto.
|
|
443
|
+
|
|
444
|
+
The tool is TLD-agnostic: see [Supported TLDs](#supported-tlds), or add a new source by implementing the `RegistrarClient` interface and registering it in `adapters/registrar_catalog.py`.
|
|
445
|
+
|
|
446
|
+
## Development
|
|
447
|
+
|
|
448
|
+
```bash
|
|
449
|
+
pip install -e ".[dev]"
|
|
450
|
+
pre-commit install # enable git hooks
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
```bash
|
|
454
|
+
pytest # unit tests (hermetic, no network)
|
|
455
|
+
pytest -m integration # live registry tests (network)
|
|
456
|
+
ruff check src tests # linting
|
|
457
|
+
ruff format --check src tests # formatting
|
|
458
|
+
mypy # strict type checking
|
|
459
|
+
pre-commit run --all-files # exactly what CI runs for lint + types
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
The ruff and mypy revs in `.pre-commit-config.yaml` are the source of truth. The `dev` extra pins matching ranges, so update both together.
|
|
463
|
+
|
|
464
|
+
CI (GitHub Actions) does the following:
|
|
465
|
+
|
|
466
|
+
- runs pre-commit;
|
|
467
|
+
- runs the unit tests on Python 3.10 to 3.14, with a 95% coverage floor;
|
|
468
|
+
- builds the wheel and smoke-tests it in a fresh venv.
|
|
469
|
+
|
|
470
|
+
A separate weekly workflow runs the live `integration` tests against real registries. It is non-blocking and skips the `.it` case, because whois.nic.it throttles hard. See [CHANGELOG.md](CHANGELOG.md) for release notes.
|
|
471
|
+
|
|
472
|
+
## Contributing
|
|
473
|
+
|
|
474
|
+
Bug reports, new TLDs and WHOIS pattern fixes are welcome. [CONTRIBUTING.md](CONTRIBUTING.md) has the setup and a step-by-step guide to adding a TLD; [SECURITY.md](SECURITY.md) says how to report a vulnerability privately.
|
|
475
|
+
|
|
476
|
+
## Roadmap
|
|
477
|
+
|
|
478
|
+
| Feature | Status |
|
|
479
|
+
|---------|--------|
|
|
480
|
+
| CSV/JSON ResultWriter | Done |
|
|
481
|
+
| Multi-TLD support (`--tld to,in,io`) | Done |
|
|
482
|
+
| Progress bar (tqdm) | Done |
|
|
483
|
+
| RDAP + WHOIS registrar adapters | Done |
|
|
484
|
+
| Result caching (decorator pattern) | Done |
|
|
485
|
+
| Best-first ordering (`--order`, `--limit`) | Done |
|
|
486
|
+
| Adaptive per-host backoff with jitter | Done |
|
|
487
|
+
| Parallel checks, one request per registry host (`--parallel`) | Done |
|
|
488
|
+
| DNS confirmation (`--confirm-dns`) | Done |
|
|
489
|
+
| Second-level suffixes (`com.ar`, `co.uk`) | Done |
|
|
490
|
+
| Registration details and parking detection for taken names | Done |
|
|
491
|
+
| `name` command: exact names, domain hacks, brand variants, recommendation | Done |
|
|
492
|
+
| `tlds` command and live-verified registry data (170 of 248 ccTLDs) | Done |
|
|
493
|
+
| Reference prices per TLD: a bundled, dated snapshot of public registrar price lists (registration and renewal), shown as a reference only; per-name premium pricing is out of scope | Planned |
|
|
494
|
+
| Re-verify registries that did not answer during verification (e.g. .pt, .uy, .dk, .do) and the .my RDAP service | Planned |
|
|
495
|
+
| Word-frequency scoring (`Scorer` hook) | Planned |
|
|
496
|
+
| Open zone files (.nu, .ch, .li) as an offline "registered" source | Planned |
|
|
497
|
+
| Async HTTP (`httpx.AsyncClient`) | Planned |
|
|
498
|
+
|
|
499
|
+
## License
|
|
500
|
+
|
|
501
|
+
MIT
|