Affix Domains
ASecurityAttach popular brand prefixes/suffixes to a seed word and check availability against the authoritative registry. Starts at `.com`; if the seed word's own `.com` is taken, escalates the whole sweep to .com/.net/.org/.ai/.io/.co/.app/.cc. Use when the user names a word and asks for prefix/suffix domain variants, or wants domains generated by adding endings like -labs/-ify/-ly or beginnings like deep-/open-/get-. Every surviving name is also checked as a GitHub org name and an X (Twitter) handle...
- 3 stars
- 0 votes
- 0 copies
- 5 views
- Added September 5, 2026
Works with
Security analysis
100/100Pro scans all 3 files and shows the line behind each finding
npx -y skills add soulmachine/skills --skill affix-domains --agent claude-codeAre you the author of Affix Domains?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/soulmachine-affix-domains)---
name: affix-domains
description: Attach popular brand prefixes/suffixes to a seed word and check availability against the authoritative registry. Starts at `.com`; if the seed word's own `.com` is taken, escalates the whole sweep to .com/.net/.org/.ai/.io/.co/.app/.cc. Use when the user names a word and asks for prefix/suffix domain variants, or wants domains generated by adding endings like -labs/-ify/-ly or beginnings like deep-/open-/get-. Every surviving name is also checked as a GitHub org name and an X (Twitter) handle, so the domain, the org and the handle can be taken together. Catalogs are ranked by 2025–26 hot-AI-startup evidence; scan and surface in rank order.
---
# affix-domains
**Affix** a seed word with common brand prefixes and suffixes, check each candidate live against the authoritative registry, **against GitHub's org namespace, and against X's handle namespace**, and prompt the user to register the free ones.
## Steps
1. **Gate on the bare seed's `.com`.** Before generating anything, check `<seed>.com` alone. It decides the scope of everything below:
- **Free** → the seed itself is winnable. Say so up front, then run the affix sweep on **`.com` only** — the original contract. Nothing else is needed.
- **Taken** → escalate. Every check from here on runs across the **full TLD set**: `.com .net .org .ai .io .co .app .cc`. Also sweep the **bare seed** across the other seven — `mate.ai` is worth more than `matebuddy.com`, and it costs eight lookups to find out.
2. **Generate candidates.** For seed `W`, produce `W` + each suffix and each prefix + `W` from the catalog below. Lowercase. Dedupe. Always drop triple-letter runs (`mateee`) and a suffix that repeats the seed (`matemate`).
The English-word filter is **mode-dependent**: in `.com`-only mode, drop any combination that spells an existing English word (e.g. `getup`, `openbox`) — its `.com` is almost certainly taken and burns a lookup. In **escalated mode, keep them** — that rationale is `.com`-specific, and a real word gone in `.com` says nothing about `.ai` or `.cc`. (`mately` is a dictionary adverb *and* the flagship `-ly` SaaS pattern; it is free in six TLDs.) If the user hints a style (playful, techy, marketplace…), weight the matching categories; default is every category.
3. **Check availability** with the bundled script, which already encodes the per-TLD endpoint, parallelism, and retry rules. It lives in this skill's `scripts/` dir — invoke it by absolute path:
```bash
~/.claude/skills/affix-domains/scripts/check_domains.sh \
--tlds com,net,org,ai,io,co,app,cc cand1 cand2 cand3 ...
```
With many candidates, pass them on stdin instead: `printf '%s\n' "${cands[@]}" | … --stdin`.
Pass **bare names, not domains** — the script forms `name.tld` itself. Output is one `<domain>\tFREE|TAKEN|UNKNOWN` line per lookup. A full 68-affix escalated sweep is ~550 lookups and takes roughly 30–60s, paced by the serial `.app` path.
**`UNKNOWN` is not `FREE`.** It means the registry never gave a clean answer. Never show it as available; re-run those few names, and if they stay UNKNOWN, say so explicitly.
4. **Check the GitHub org name and the X handle** for every name that survived step 3 — a brand needs its handles as much as the domain: GitHub is where the code will live, X is where it will be announced. GitHub first:
```bash
~/.claude/skills/affix-domains/scripts/check_github.sh cand1 cand2 cand3 ...
```
Takes `--stdin` like its sibling. One `<name>\tFREE|TAKEN|RESERVED|INVALID|UNKNOWN` line per name; ~30 names/second at parallelism 8, so run it over every surviving name rather than a shortlist. Note it checks the **name**, not the domain — pass `matebuddy`, never `matebuddy.com`.
Only `FREE` is claimable. The other verdicts each mean something different:
- **`TAKEN`** — users and orgs share **one** namespace, so a name some *person* holds is exactly as gone as one an org holds.
- **`RESERVED`** — GitHub's own route namespace (`settings`, `new`, `about`, `explore`, `login`…). Never claimable, and it is the trap: these **404 on the API** and only reveal themselves as a 30x redirect on the web.
- **`INVALID`** — breaks GitHub's account-name rules: anything outside alphanumerics and single interior hyphens, or over 39 characters. Also 404s, so it is screened before the lookup.
Then the X handle, with the sibling script:
```bash
~/.claude/skills/affix-domains/scripts/check_x.sh cand1 cand2 cand3 ...
```
Same contract: `--stdin`, bare names, one `<name>\tFREE|TAKEN|RESERVED|INVALID|UNKNOWN` line per name. It runs in two stages. First `twitter user <name>` — twitter-cli on your own logged-in Chrome session, two lanes, about 2 s a name: a profile means TAKEN, and that is all stage one decides. Every other name — no profile, or any twitter-cli error — goes to X's sign-up availability check, which is what splits them into FREE, RESERVED and INVALID — **a profile miss alone is never FREE.** After the sweep the script re-queries every FREE once, serially, and prints the re-check's verdict; you do not repeat that. The script installs twitter-cli with `uv tool install twitter-cli` when it is missing; when it is installed but not logged in on this host, it says so on stderr and runs the sign-up stage over every name instead, with the same verdicts. The verdicts mean:
- **`TAKEN`** — a profile exists; one namespace, case-insensitive: `GetMate` and `getmate` are the same handle. A suspended or deactivated account counts — X holds the handle, a deactivated one for at least 30 days.
- **`RESERVED`** — X's sign-up refuses it. That covers route words (`home`, `settings`, `explore`, `i`, `search`, `login`…), anything containing `twitter` or `admin`, **and a held set of ordinary-looking handles** (`matearoo`, `evermate`) that show no profile page yet can never be claimed. Never claimable, and it is the trap: `x.com/<name>` 404s on those, exactly as GitHub's API does on its reserved names.
- **`INVALID`** — outside `[A-Za-z0-9_]`, shorter than 5, or longer than 15; screened locally, so it costs no request. The two namespaces disagree here: GitHub allows hyphens and 39 characters, X allows underscores and 15 — a long candidate can be valid on GitHub and INVALID on X, and that is a real answer, not an error.
5. **Surface the survivors.** In `.com`-only mode, list one line per free `.com`, in catalog rank order (note each name's category).
In escalated mode the raw cross-product is far too long to dump. Instead, order surviving names by their affix's catalog rank, breaking ties by best available TLD in this preference order:
`.com` > `.ai` > `.io` > `.co` > `.net` > `.org` > `.app` > `.cc`
Show the best one or two TLDs per name, and offer the complete matrix only if the user asks. Note when a name is free across *many* TLDs — that usually means it is genuinely unclaimed rather than merely available in the leftovers.
**Carry the GitHub and X verdicts on the same line as the name**, in both modes — two columns, not two more lists; the user is picking a brand, and the domain, the org and the handle are one decision. A name where the domain, the org *and* the handle are all free is the strongest result the sweep can return: lead with those, and say so. Where a name's domain is free but the org or the handle is taken, keep it on the list and mark which — it is still registrable, just with a handle compromise ahead.
6. **Prompt the user to register.** Ask which they want to take. Point them to Cloudflare Registrar (or their preferred one) — note that Cloudflare Registrar does not sell every TLD in this set, so a second registrar may be needed. Do not enter payment on their behalf — registration is the user's action.
For the matching GitHub org, point them at <https://github.com/organizations/new> — free, no payment, seconds to claim. Worth saying plainly: an org name costs nothing and is **first-come**, so it is the cheapest part of the brand to lose by waiting. Creating it is still the user's action, not yours.
The X handle is claimed by signing up at <https://x.com/i/flow/signup> with that username, or by renaming an existing account at <https://x.com/settings/screen_name>. Free and first-come like the org, and the same rule: the user claims it, not you.
## TLD endpoints
Verified 2026-08-27, each against a known-taken and a known-free control. The script holds these; the table is here so a human can audit it.
| TLD | Method | Endpoint | Notes |
|---|---|---|---|
| `.com` | RDAP | `rdap.verisign.com/com/v1/domain/` | Verisign, authoritative |
| `.net` | RDAP | `rdap.verisign.com/net/v1/domain/` | Verisign |
| `.cc` | RDAP | `tld-rdap.verisign.com/cc/v1/domain/` | Verisign, but a **different host** than `.com`/`.net` |
| `.org` | RDAP | `rdap.publicinterestregistry.org/rdap/domain/` | PIR |
| `.ai` | RDAP | `rdap.identitydigital.services/rdap/domain/` | Identity Digital |
| `.io` | RDAP | `rdap.identitydigital.services/rdap/domain/` | same host as `.ai`; **not in the IANA bootstrap** |
| `.app` | RDAP | `pubapi.registry.google/rdap/domain/` | Google Registry; **rate-limited**, see below |
| `.co` | **WHOIS** | `whois.registry.co` | **No RDAP service exists.** Free ⇔ `DOMAIN NOT FOUND` |
**`.app` rate limiting is a rolling quota, not a concurrency cap.** Dropping to `-P 2` does not help — once the budget is spent, every request 429s regardless of parallelism. The only fix is serial requests plus backoff-and-retry, which is what the script does. Treat `429` as *retry*, never as an answer.
**`.ai` and `.io` share one host, and so share one quota.** `rdap.identitydigital.services` serves both at `-P 6`, so an escalated sweep hits it with twice the requests of any other host and can exhaust it late in the run — observed 2026-09-16 on a 67-name sweep, where the last 13 `.io` lookups all came back UNKNOWN while `.com` and `.cc` were clean. Once it is tripped the block is **long** — measured at over 150s of cumulative backoff on a single name without clearing, so neither the script's 4-attempt loop nor a hand-rolled serial retry with 10s-and-up sleeps will outlast it inside a session. Do not grind: a full serial retry of 13 names costs ~30 minutes and can still return all UNKNOWN. Report those lookups as unresolved, name the TLD and the cause, and say they can be re-checked in a later session. Spending the quota more slowly does not buy more of it. **A block of UNKNOWNs confined to one TLD is a quota symptom, not a registry answer** — never let it reach the user as availability, in either direction.
### GitHub org namespace
Verified 2026-09-16 against four controls: `github` (org) 200, `soulmachine` (user) 200, `agentsyncaroo` (free) 404, `settings` and `new` (reserved) 302.
| Target | Method | Endpoint | Notes |
|---|---|---|---|
| GitHub org/user name | HTML status | `https://github.com/<name>` | 404 free · 200 taken · 301/302 reserved. No token, no API budget; P8 is comfortable |
**Do not use `api.github.com/users/<name>` for this.** It answers `404` for GitHub's *reserved* route names — `settings`, `new`, `about`, `explore`, `login` — which would be reported as available when they can never be registered. Only the web endpoint separates reserved from free, by redirecting instead of 404ing. The API is also capped at 60 requests/hour unauthenticated, which a single 67-name sweep blows straight through.
### X handle namespace
Verified 2026-09-18. Stage 1 controls: `x` taken (the preflight's own call, past the local screen), `soulmachine` hollow (held), `agentsyncaroo` and `matearoo` not found. Stage 2 controls: `agentsyncaroo` available, `explore` is_banned_word, `twitteradmin` contains_banned_word; `get-mate`, `abc` and `abcdefghijklmnop` are INVALID from the local screen and never reach either stage (`home` is four characters, so it is INVALID too, however famous the account).
| Stage | Question | Method | Endpoint | Notes |
|---|---|---|---|---|
| 1 | profile exists? | twitter-cli `user <name> --json` | GraphQL `UserByScreenName` on the user's Chrome session | `ok:true` taken — an empty `id` is X's `UserUnavailable`, a held handle; `code:not_found` and any other code fall through to stage 2. About 2 s a name; the session limit is community-reported at ~95 per 15 min, and twitter-cli retries a 429 itself (5/10/20 s) before surfacing it, so a second sweep inside the window runs slower, not emptier |
| 2 | registrable? | JSON `reason` field | `api.x.com/i/users/username_available.json?username=<name>` | X's own sign-up check, unauthenticated, no API credits, case-insensitive: `available` free · `taken` · `is_banned_word` / `contains_banned_word` reserved · `invalid_username` / `improper_format` invalid |
**Stage 2 has a budget of about 250 requests per IP, then a 429 that lasts minutes; a burst on the first 200 re-trips it, and fifteen quiet minutes clears it.** A sweep sends it only the no-profile names — 18 of 66 in the `mate` sweep — so it fits many times over; if the column still goes UNKNOWN part-way through, that is the quota, not a registry answer: report those names unresolved and re-run only them after the quiet period.
**For a few ordinary-looking names the sign-up answer is not stable.** Measured 2026-09-18 on a calm endpoint, serially, nothing else in flight: `mateora` answered `available`, then `is_banned_word` sixty seconds later; `quickmate` did the same across runs; `matehub` answered `available` early in the day and `is_banned_word` from twenty minutes on. The twelve other banned `mate` handles held across every query. No name that answered `taken` ever answered `available`, but `mateor` (taken) did answer `is_banned_word` once, in a run that was also drawing 429s — a flicker can land on a taken name too, and both answers are not-claimable. Which of the two answers is true for a flickering free name cannot be settled without registering it, so the rule is: **a FREE that does not repeat is not FREE.** The script re-queries every FREE once after the sweep, serially, and prints that second verdict verbatim, whatever it is; the flicker itself is worth a sentence to the user. RESERVED from a clean run is otherwise reliable. Querying a name does not put it on hold — 250 probe names checked once still read `available` 35 minutes later — but FREE is still a snapshot: re-check the shortlist right before the user claims it.
### Endpoints that look right and are wrong
Both of these return **HTTP 404 for domains that are definitely registered** — a silent false-available that would tell the user to go buy a taken domain. Do not substitute them:
- `rdap.godaddy.com/v1/domain/` — 404s on `t.co`. It is the *registrar's* RDAP, not the registry's.
- `rdap.org/domain/` — a bootstrap proxy; 404s on both `.co` and `.io` because neither is in the IANA bootstrap file.
And for the X handle, both of these would report a *reserved* handle as free:
- `https://x.com/<name>` HTTP status — 404 for free names, but also 404 for held handles like `matearoo` and `evermate`, which sign-up refuses as `is_banned_word`. It encodes none of the sign-up rules.
- X API v2 `GET /2/users/by/username/<name>` (via `xurl`, or the `xapi` MCP's user lookup) — answers "does an account exist", not "can you register it", so reserved words and held handles come back not-found. It also spends API credits, which neither stage does; the DomainHunter app answered `402 credits depleted` on 2026-09-18.
- twitter-cli's `not_found` on its own — it is stage 1, not a verdict: `matearoo` is `not_found` there and refused by sign-up. Only the sign-up check promotes a no-profile name to FREE.
## Catalog ranking
Both catalogs are **ranked by evidence**: how often the hottest AI startups actually use each affix
in their brand names — a 433-company corpus (Forbes AI 50 2026+2025, CB Insights AI 100 2026, YC,
a16z Top-100 consumer apps, LinkedIn Top Startups US, 2025–26 mega-rounds, AI dev-tools peer group;
snapshot 2026-08-28) — blended with fusability and meaning (Score = 2·E + F + M). Generate
candidates from **every** row, but check and surface them **in rank order**. The full evidence
tables (scores, old-vs-new ranks, corpus examples) are in
[references/ranking-2026-08.md](references/ranking-2026-08.md). Known catalog gap, deliberately not
auto-added: the era's dominant suffix is a trailing "AI" word (~60 hot companies), plus -lab, -0,
-gpt, applied- — see the reference before extending the catalog.
## Prefix catalog — ranked
| # | Affix | Category | Hot uses 2025–26 | Feel · exemplars |
|---|---|---|---|---|
| 1 | deep- | Scale & depth | 4 | serious tech (DeepMind, DeepSeek) |
| 2 | open- | Newness & openness | 4 | transparent/community (OpenAI, OpenTable) |
| 3 | super- | Scale & depth | 2 | boosted (Superhuman) |
| 4 | live- | Newness & openness | 2 | real-time (LiveChat) |
| 5 | agent- | AI & agents | 2 | autonomous doer (Agentforce) |
| 6 | ai- | AI & agents | 3 | AI-native signal (AI21) |
| 7 | up- | Scale & depth | 1 | rising (Upwork) |
| 8 | hey- | Call-to-action | 1 | friendly hail (Hey.com) |
| 9 | chat- | AI & agents | 1 | conversational (ChatGPT) |
| 10 | the- | Possessive | 1 | definitive (TheVerge) |
| 11 | go- | Call-to-action | 0 | momentum (GoDaddy, GoPro) |
| 12 | fast- | Speed & ease | 0 | velocity (Fastly) |
| 13 | get- | Call-to-action | 0 | grab it (GetResponse) |
| 14 | next- | Newness & openness | 0 | successor (NextDoor) |
| 15 | quick- | Speed & ease | 0 | instant (QuickBooks) |
| 16 | smart- | Speed & ease | 0 | intelligent (SmartThings) |
| 17 | my- | Possessive | 0 | personal (MySpace, MyFitnessPal) |
| 18 | easy- | Speed & ease | 0 | frictionless (EasyJet) |
| 19 | ever- | Scale & depth | 0 | permanence (Evernote) |
| 20 | real- | Newness & openness | 0 | authentic (RealPage) |
| 21 | neo- | Newness & openness | 0 | new wave (Neobank) |
| 22 | hyper- | Scale & depth | 0 | past the limit (Hyperloop, Hyperscience) |
| 23 | use- | Call-to-action | 0 | utility (Usebutton) |
| 24 | try- | Call-to-action | 0 | low-commitment invite |
| 25 | ultra- | Scale & depth | 0 | extreme |
| 26 | meta- | Newness & openness | 0 | beyond (Meta) |
| 27 | meet- | Call-to-action | 0 | introduction (MeetUp-style) |
| 28 | agentic- | AI & agents | 0 | agent-native workflows (agentic AI wave) |
| 29 | join- | Call-to-action | 0 | community invite |
| 30 | every- | Scale & depth | 0 | universality (Everlane-like) |
## Suffix catalog — ranked
| # | Affix | Category | Hot uses 2025–26 | Feel · exemplars |
|---|---|---|---|---|
| 1 | -labs | Tool & hub | 12 | R&D shop (Bell Labs) |
| 2 | -o | Vowel endings | 11 | punchy (Venmo) |
| 3 | -a | Vowel endings | 10 | smooth (Tesla, Zara) |
| 4 | -er | Doer | 4 | actor/agent (Uber, Tinder) |
| 5 | -ia | Vowel endings | 3 | name-like (Nvidia, Expedia) |
| 6 | -base | Tool & hub | 2 | foundation/platform (Coinbase, Firebase, Supabase) |
| 7 | -ly | Short & modern | 3 | SaaS all-rounder (Bitly, Calendly, Grammarly) |
| 8 | -r | Short & modern | 3 | drop-vowel web-2.0 (Tumblr, Flickr) |
| 9 | -us | Vowel endings | 4 | Latin gravitas (Prius) |
| 10 | -ify | Verbifier | 1 | turns the word into a service (Spotify, Shopify) |
| 11 | -kit | Tool & hub | 1 | toolkit (ConvertKit) |
| 12 | -bot | Companion | 1 | automated agent |
| 13 | -ora | Vowel endings | 1 | elegant Latin (Pandora) |
| 14 | -cast | Vertical positioning | 1 | media/broadcast (Podcast) |
| 15 | -verse | Stylized & meme | 1 | a whole universe (metaverse) |
| 16 | -app | Vertical positioning | 1 | application (Cash App) |
| 17 | -hub | Tool & hub | 0 | gathering point (GitHub) |
| 18 | -stack | Tool & hub | 0 | tech stack (Substack) |
| 19 | -or | Doer | 1 | mechanical (Vector) |
| 20 | -ee | Vowel endings | 1 | soft (Renee-like) |
| 21 | -box | Tool & hub | 0 | packaged tool (Dropbox) |
| 22 | -mate | Companion | 0 | sidekick (Mailmate) |
| 23 | -works | Tool & hub | 0 | workshop (SolidWorks) |
| 24 | -pal | Companion | 0 | trusted friend (PayPal) |
| 25 | -pad | Tool & hub | 0 | workspace (Launchpad) |
| 26 | -ize | Verbifier | 0 | systematize (Rentalize) |
| 27 | -ist | Doer | 0 | specialist (Stylist) |
| 28 | -space | Tool & hub | 0 | room to work (Squarespace) |
| 29 | -buddy | Companion | 0 | friendly helper (GasBuddy) |
| 30 | -ix | Vowel endings | 0 | techy (Netflix) |
| 31 | -matic | Stylized & meme | 0 | machine-magic (Instamatic) |
| 32 | -bay | Vertical positioning | 0 | marketplace (eBay) |
| 33 | -ex | Vowel endings | 0 | sharp, techy (FedEx) |
| 34 | -y | Short & modern | 0 | cute, casual (Etsy) |
| 35 | -ate | Verbifier | 0 | automate/activate (Automate) |
| 36 | -zilla | Stylized & meme | 0 | monster-big (Mozilla) |
| 37 | -oo | Vowel endings | 0 | round, cute (Yahoo) |
| 38 | -aroo | Stylized & meme | 0 | bouncy-cute (Kangaroo) |
## Rules
- **Live lookups only** — never claim availability from cached knowledge or prior turns of this conversation. Domains, org names and handles get registered between checks.
- **Only an explicit negative means available.** RDAP `404`, WHOIS `DOMAIN NOT FOUND`, `github.com/<name>` returning `404`, or X's sign-up check answering `"reason":"available"`. Anything else — `429`, `5xx`, a timeout, an empty body, a `30x` — is `UNKNOWN` or `RESERVED`, reported as such and never as free.
- **Use the bundled scripts rather than hand-rolled `curl` or a bare `twitter user`.** `check_domains.sh` for TLDs, `check_github.sh` for the org name, `check_x.sh` for the X handle. The per-TLD hosts, the `.co` WHOIS special case, the `.app` retry loop, GitHub's reserved-name redirect, and X's two-stage split are all easy to get wrong in a way that fails silently toward false-available.
- **The escalated TLD set is fixed**: `.com .net .org .ai .io .co .app .cc`. If the user wants a TLD outside it, check it explicitly and say the endpoint was not pre-verified.
- **A free domain with a taken org or handle is still a result** — report it, marked, rather than dropping it. The three namespaces are independent and the user may not care equally about all of them; suppressing part of the picture makes that call for them.
- **Never report a GitHub name as available on the strength of an API 404, nor an X handle on the strength of an `x.com/<name>` 404, a v2 API not-found, or a twitter-cli `not_found` alone** — see the reserved-name traps above. GitHub's web endpoint and X's sign-up check, or nothing.
- **No payment on the user's behalf.** Prompt them to register; they check out. Same for creating the GitHub org and claiming the X handle, which cost nothing but are still theirs to do.
Files in this skill
- SKILL.md
- references/ranking-2026-08.md
- scripts/check_domains.sh
Attribution
Comments
Loading comments…
Newsletter
Get the weekly list of safe new skills, and the ones we pulled.