Kommandozeilen-Client für die Domain- und DNS-Verwaltung bei INWX über die DomRobot-API. Unterstützt Verfügbarkeits- und Preisabfragen, Domain-Registrierung, Kontakt-Handles sowie das Auflisten, Erstellen und Löschen einzelner DNS-Records und das deklarative, idempotente Ausrollen einer kompletten Zone aus einer JSON-Beschreibung, mit vorgeschaltetem Änderungsplan und Dry-Run.
In TypeScript geschrieben (strikt typisiert), im Stil von vercel dns. Inoffizielles
Community-Projekt, nicht mit INWX affiliiert. MIT-lizenziert.
📖 Dokumentation: https://noel-lang.github.io/inwx-cli/ (automatisch aus dieser CLI generiert)
- Node.js ≥ 18 (nutzt das globale
fetchundHeaders.getSetCookie; empfohlen ≥ 20, getestet auf 22) - Zum Bauen aus dem Quellcode: TypeScript 5 (als devDependency enthalten, kein globales
tscnötig) - Ein INWX-Account mit aktiviertem API-Zugang. Für Trockenläufe von Domain-Registrierungen ein separater OT&E-Testaccount (eigene Registrierung, eigene Zugangsdaten).
Direkte Ausführung ohne Installation (npx klont, installiert transient und baut über den
prepare-Hook automatisch dist):
npx github:noel-lang/inwx-cli login
npx github:noel-lang/inwx-cli dns ls example.comGlobale Installation:
npm install -g github:noel-lang/inwx-cliLokale Entwicklung:
git clone https://github.com/noel-lang/inwx-cli.git
cd inwx-cli
npm install # installiert Deps und baut via prepare-Hook nach dist/
npm link # registriert das `inwx`-Binary globalDer Quellcode liegt in TypeScript unter src/ und bin/, das ausführbare Ergebnis kompiliert
tsc nach dist/ (die einzige vom Binary genutzte Ausgabe, per .gitignore nicht eingecheckt).
npm run build # tsc: src + bin + test -> dist/
npm run dev -- domain check example.de # baut und führt direkt aus (Args nach --)
npm test # baut und läuft node --testDer prepare-Hook baut dist automatisch bei npm install und bei Installation via
npx/npm install -g github:…. Das Binary ist in package.json auf dist/bin/inwx.js
verdrahtet; veröffentlicht wird nur dist plus die Beispiel-Zonendatei.
-
Unit-Tests (
test/*.test.tsaußere2e) laufen ohne Netzwerk: FQDN-Ableitung, Perioden-/Ländercode-/Telefon-Normalisierung, TLD-Hinweise, Plan-Diff. -
E2E-Tests (
test/e2e.test.ts) sprechen das INWX-OT&E-Testsystem an, bewusst nur READ-ONLY (Login,domain.check,domain.getPrices,contact.list,domain.list), damit nichts registriert wird und die API nicht belastet wird. Sie laufen nur, wenn OT&E-Credentials in der Umgebung stehen, sonst überspringen sie sich selbst:INWX_USER=… INWX_PASSWORD=… npm test # inkl. E2E gegen OT&E npm test # nur Unit, E2E werden übersprungen
Der GitHub-Actions-Workflow (.github/workflows/ci.yml) baut, führt die Unit-Tests aus und
lässt die E2E-Tests gegen OT&E laufen, sobald die Repo-Secrets INWX_OTE_USER /
INWX_OTE_PASSWORD gesetzt sind (in Fork-PRs ohne Secrets werden sie übersprungen).
inwx login fragt Benutzername, Passwort und optional die 2FA-Konfiguration ab, verifiziert
die Zugangsdaten mit einem echten account.login-Aufruf und persistiert sie erst danach.
inwx login # produktiv
inwx login --ote # gegen das OT&E-Testsystem (separater Account, siehe unten)
inwx whoami # aktives Profil anzeigen
inwx logout # Profil entfernenIst auf dem Account 2FA aktiv, gibt es zwei Betriebsmodi:
- Secret hinterlegen: das Base32-TOTP-Secret (aus der 2FA-Einrichtung) wird gespeichert;
die CLI erzeugt gültige Codes selbst (RFC 6238, SHA-1, 6 Stellen, 30 s, via
otpauth). - Interaktiv: kein Secret gespeichert; jede authentifizierte Aktion fragt den aktuellen 6-stelligen Code ab.
Intern wird account.login gefolgt von account.unlock mit dem generierten bzw. eingegebenen
tan ausgeführt.
Profile werden in ~/.inwx/config.json mit Dateirechten 0600 abgelegt:
{
"profiles": {
"prod": {
"user": "kunde",
"pass": "<base64>",
"totpSecret": "<base64>",
"savedAt": "2026-07-04T13:37:00.000Z"
},
"ote": { }
}
}Hinweis:
passundtotpSecretsind lediglich Base64-kodiert. Das ist keine Verschlüsselung, sondern nur Schutz vor versehentlicher Klartext-Anzeige. Wer keine Credentials auf die Platte schreiben will, nutzt Umgebungsvariablen.
Überschreiben das gespeicherte Profil und ermöglichen Betrieb ohne persistierte Datei (z. B. in CI):
| Variable | Zweck |
|---|---|
INWX_USER |
Benutzername |
INWX_PASSWORD |
Passwort |
INWX_TOTP_SECRET |
Base32-TOTP-Secret für die Code-Erzeugung |
| Befehl | Beschreibung |
|---|---|
inwx login / logout / whoami |
Anmelden, Profil entfernen, Profil anzeigen |
inwx dns ls <domain> |
Alle Records einer Zone auflisten |
inwx dns add <domain> <name> <type> <content> |
Einzelnen Record anlegen |
inwx dns rm <domain> <id> |
Record anhand seiner ID löschen |
inwx dns apply <datei> |
Zone deklarativ abgleichen |
inwx domain check <name...> |
Verfügbarkeit prüfen (READ-ONLY) |
inwx domain price <name> |
Preisinfo zu Domain/TLD |
inwx domain ls |
Eigene Domains auflisten |
inwx domain info <name> |
Details zu einer eigenen Domain |
inwx domain buy <name> |
Domain registrieren (Standard: OT&E) |
inwx contact ls |
Domain-Kontakte (Handles) auflisten |
inwx contact add |
Kontakt anlegen (interaktiv oder per Flags) |
Globale Option --ote schaltet jeden Befehl auf das OT&E-Testsystem.
Prüft die Verfügbarkeit einer oder mehrerer Domains über domain.check. Vor dem API-Call
wird jeder Name syntaktisch validiert (Label-Länge, erlaubte Zeichen, IDN/Punycode-Hinweis)
und für einige TLDs ein Registrierungshinweis ausgegeben.
inwx domain check example.de
inwx domain check meine-idee.de meine-idee.com meine-idee.ioDer avail-Code der API wird übersetzt:
| avail | Anzeige | Bedeutung |
|---|---|---|
1 |
frei |
registrierbar |
0 |
vergeben |
bereits registriert |
2 |
premium |
frei, aber Premium-Preis |
-1 |
ungültig |
ungültiger Name / nicht prüfbar |
Sofern die API einen Preis mitliefert, wird er angezeigt.
inwx domain price example.deKombiniert den konkreten Domainpreis samt Verfügbarkeit aus domain.check mit der
TLD-weiten Preisliste (Registrierung/Verlängerung/Transfer) aus domain.getPrices.
inwx domain ls # eigene Domains (domain.list)
inwx domain info example.de # Status, Ablaufdatum, Handles, Nameserver (domain.info)Registriert eine Domain über domain.create. Der Befehl ist bewusst mehrfach abgesichert:
Wenn das Default-Set ns.inwx.de,ns2.inwx.de verwendet wird, stellt die CLI vor
domain.create automatisch eine INWX-MASTER-Zone mit SOA- und NS-Basisrecords bereit. Damit
können Registries wie DENIC die Nameserver bei der Registrierung autoritativ prüfen.
# 1) Testkauf gegen OT&E (Standard, keine Kosten, keine echte Registrierung)
inwx domain buy meine-idee.de --registrant 12345
# 2) Non-interaktiv (Rückfrage überspringen), weiterhin OT&E
inwx domain buy meine-idee.de --registrant 12345 --yes
# 3) Nur validieren, ohne zu registrieren (testing=true)
inwx domain buy meine-idee.de --registrant 12345 --dry-run
# 4) Echter, kostenpflichtiger Kauf auf PROD (erfordert --yes-live + Tippbestätigung)
inwx domain buy meine-idee.de --registrant 12345 --yes-live| Option | Default | Beschreibung |
|---|---|---|
--registrant <id> |
– | Inhaber-Kontakt-ID (Pflicht) |
--period <dauer> |
1Y |
Registrierungsdauer, Format \d+Y |
--admin/--tech/--billing |
= --registrant |
weitere Kontakt-Handles (Default: Registrant) |
--ns <liste> |
ns.inwx.de,ns2.inwx.de |
Nameserver (kommasepariert) |
--renewal-mode <modus> |
– | AUTORENEW, AUTOEXPIRE, AUTODELETE |
--dry-run |
– | nur validieren (testing=true) |
-y, --yes |
– | Rückfrage überspringen (nur OT&E) |
--yes-live |
– | echte PROD-Registrierung erlauben |
Sicherheitsmodell:
- Ohne
--yes-liveläuftbuyimmer gegen OT&E. Eindomain.creategegen PROD ist ohne dieses Flag technisch ausgeschlossen. - Vor dem Kauf zeigt der Befehl Verfügbarkeit und Preis aus
domain.check. Ist die Domain nicht frei, bricht er ab. - Es folgt eine Bestätigung mit Preisanzeige.
-y/--yesüberspringt diese Rückfrage nur auf OT&E; bei--yes-livemuss der Domainname trotzdem exakt eingetippt werden. - INWX verlangt alle vier Kontakt-Handles. Werden
--admin/--tech/--billingnicht gesetzt, übernimmt die CLI den Registranten.
Eine Zone lässt sich auch explizit anlegen und eine bestehende Domain erneut auf das Default-Set setzen:
inwx dns zone add example.de
inwx domain ns example.de --yesDomain-Registrierungen brauchen mindestens einen Inhaber-Kontakt (Handle).
inwx contact ls # contact.list: ID, Typ, Name, Firma, Ort, Land
# interaktiv (fragt alle Felder ab)
inwx contact add
# oder vollständig per Flags (scriptbar, non-interaktiv)
inwx contact add \
--name "Max Inhaber" --street "Musterstr. 1" --pc 10115 --city Berlin --cc DE \
--email max@example.com --voice "+49.30123456"contact add legt einen Kontakt über contact.create an und gibt die neue Kontakt-ID aus
(direkt nutzbar als domain buy --registrant <id>). Werden alle Pflichtfelder als Flags
übergeben (--name, --street, --pc, --city, --email, --voice), läuft der Befehl
komplett ohne Prompts; sonst fragt er die fehlenden Felder interaktiv ab.
| Flag | Default | Hinweis |
|---|---|---|
--type |
person |
person | org | role |
--name |
– | Voller Name (Pflicht) |
--org |
– | Firma / Organisation (optional) |
--street |
– | Straße und Hausnummer (Pflicht) |
--pc / --city |
– | PLZ / Ort (Pflicht) |
--cc |
DE |
Ländercode ISO 3166-1 alpha-2 |
--email |
– | E-Mail (Pflicht, lokal validiert) |
--voice |
– | Telefon im Format +Ländercode.Nummer |
Telefonformat: INWX erwartet +Ländercode.Nummer mit genau einem Punkt (z. B.
+49.30123456). Die CLI normalisiert übliche Schreibweisen automatisch: aus +49.30.999-8877
wird +49.309998877. Eine Nummer ganz ohne Punkt ist mehrdeutig und wird abgelehnt.
Das OT&E-Testsystem (eigener Account auf ote.inwx.com) eignet sich, um den kompletten Registrierungs-Weg gefahrlos zu proben:
inwx login --ote
# 1) Verfügbarkeit über mehrere TLDs prüfen (read-only)
inwx domain check codegeschichten.com codegeschichten.io codegeschichten.org \
codegeschichten.ai codegeschichten.ch codegeschichten.at --ote
# 2) Preise ansehen
inwx domain price codegeschichten.io --ote
# 3) Inhaber-Kontakt anlegen (gibt eine Kontakt-ID zurück)
inwx contact add --ote \
--name "Max Inhaber" --street "Musterstr. 1" --pc 10115 --city Berlin --cc DE \
--email max@example.com --voice "+49.30123456"
# 4) Registrierung validieren bzw. auf OT&E durchspielen (buy ist ohne --yes-live immer OT&E)
inwx domain buy codegeschichten.com --registrant <id> --dry-run # nur validieren
inwx domain buy codegeschichten.com --registrant <id> --yes # OT&E-RegistrierungZum OT&E-Guthaben: domain.create durchläuft auch im Test eine Abrechnungsprüfung. Hat der
OT&E-Account kein Test-Guthaben, endet der Aufruf mit Billing failure (Code 2104). Das ist kein
CLI-Fehler, sondern ein Kontostand-Zustand; Test-Guthaben wird im OT&E-Panel aufgeladen. Die
vorgelagerten Schritte (check, price, contact add) funktionieren unabhängig davon.
inwx dns add example.com www CNAME cname.vercel-dns.com
inwx dns add example.com mail MX feedback-smtp.eu-central-1.amazonses.com --prio 10
inwx dns add example.com @ A 203.0.113.10 --ttl 300| Option | Default | Beschreibung |
|---|---|---|
--ttl <sek> |
3600 |
Time-to-live |
--prio <n> |
– | Priorität (für MX, SRV) |
name ist der Host relativ zur Zone; @ bezeichnet den Apex. Intern wird daraus der
FQDN gebildet (www → www.example.com, @ → example.com).
inwx dns ls example.com # IDs ermitteln
inwx dns rm example.com 12345678 # löschen (mit Rückfrage)
inwx dns rm example.com 12345678 --yesapply gleicht den Ist-Zustand einer Zone gegen eine Soll-Beschreibung ab. Es zeigt zuerst
einen Plan, fragt (außer bei --yes) nach und schreibt dann nur die Differenz.
{
"domain": "example.com",
"records": [
{ "name": "@", "type": "A", "content": "76.76.21.21" },
{ "name": "www", "type": "CNAME", "content": "cname.vercel-dns.com" },
{ "name": "mail", "type": "MX", "content": "feedback-smtp.eu-central-1.amazonses.com", "prio": 10 }
]
}| Feld | Pflicht | Default | Beschreibung |
|---|---|---|---|
domain |
ja | – | Zone (Top-Level der Datei) |
name |
ja | – | Host relativ zur Zone (@ = Apex) |
type |
ja | – | Record-Typ (A, AAAA, CNAME, MX, TXT, …) |
content |
ja | – | Wert des Records |
ttl |
nein | 3600 |
Time-to-live in Sekunden |
prio |
nein | – | Priorität (nur MX/SRV) |
Eine vollständige Vorlage liegt unter records/example.records.json.
Für jeden Soll-Record wird ein bestehender Record über (FQDN, Typ) gesucht (bei MX
zusätzlich über die Priorität). Daraus ergibt sich die Aktion:
| Zustand | Aktion |
|---|---|
| kein passender Record vorhanden | anlegen (createRecord) |
Record vorhanden, content oder ttl weicht ab |
ändern (updateRecord) |
| Record vorhanden und identisch | unverändert (übersprungen) |
Der Vorgang ist damit idempotent: mehrfaches apply derselben Datei erzeugt keine Duplikate.
Aktuelle Einschränkung: mehrere gleichnamige
TXT-Records werden über(Name, Typ)gematcht und können nicht eindeutig unterschieden werden. Siehe Roadmap.
inwx dns apply zone.json --dry-run # nur lesen, Plan ausgeben, nichts schreiben
inwx dns apply zone.json # mit Bestätigung schreiben
inwx dns apply zone.json --yes # ohne Rückfrage (CI/Automation)Der --dry-run liest die reale Zone (read-only) und ist damit ein gefahrloser Vorab-Check,
auch ohne separaten OT&E-Account.
OT&E (Operational Test & Evaluation) ist die Sandbox von INWX und ein eigenständiger Account: Die Produktiv-Zugangsdaten funktionieren dort nicht. Für Trockenläufe von Registrierungen daher zuerst unter ote.inwx.com registrieren, dann:
inwx login --ote # OT&E-Zugang speichern
inwx domain check example.de --ote # Verfügbarkeit im Testsystem
inwx domain buy example.de --ote --registrant <id> # Testkauf ohne KostenOhne --yes-live läuft domain buy ohnehin gegen OT&E, --ote erzwingt es zusätzlich für alle
übrigen Befehle.
Die CLI spricht die DomRobot-API im JSON-RPC-Format an:
| Umgebung | Endpoint |
|---|---|
prod |
https://api.domrobot.com/jsonrpc/ |
ote |
https://api.ote.domrobot.com/jsonrpc/ |
Genutzte Methoden: account.login, account.unlock, account.logout,
nameserver.info, nameserver.create, nameserver.createRecord, nameserver.updateRecord, nameserver.deleteRecord,
domain.check, domain.getPrices, domain.list, domain.info, domain.create, domain.update,
contact.list, contact.create.
Die Session wird über das von account.login gesetzte Cookie gehalten und bei Folge-Requests
mitgesendet. Jede Antwort trägt einen numerischen code; 1000 bedeutet Erfolg, alles andere
wird als Fehler mit Meldung und Code weitergereicht (z. B. 2200 = Authentifizierungsfehler).
bin/inwx.ts Einstiegspunkt (Shebang)
src/cli.ts Commander-Verdrahtung + Command-Handler
src/api.ts DomRobot-Client (JSON-RPC, Session, TOTP, Record-/Domain-/Kontakt-Methoden)
src/config.ts Profil-/Credential-Verwaltung (~/.inwx)
src/ui.ts Ausgabe (Farben, Tabellen, Plan-Diff)
src/validate.ts Validierung (Domain-Syntax, TLD-Ableitung, Periode, Ländercode)
src/types.ts Interfaces für DomRobot-Requests/-Responses, Config, Optionen
test/*.test.ts Unit-Tests (node --test, ohne Netzwerk)
records/ Beispiel-Zonendatei
dist/ Build-Ausgabe (nicht eingecheckt)
- Credentials liegen ausschließlich lokal in
~/.inwx/config.json(0600), Base64-kodiert, nicht verschlüsselt. Für höhere Anforderungen Umgebungsvariablen nutzen. - Es werden keine Zugangsdaten geloggt oder an Dritte gesendet; einziger Kontakt ist die DomRobot-API.
domain buyregistriert standardmäßig nur auf OT&E; ein echter, kostenpflichtiger Kauf auf PROD verlangt das explizite Flag--yes-liveund eine Tippbestätigung des Domainnamens.- Empfehlung: einen INWX-Sub-Account mit auf DNS/Domains beschränkten Rechten verwenden.
Netzwerkfreie Unit-Tests über das eingebaute node --test:
npm testAbgedeckt: FQDN-Ableitung (toFqdn), TOTP-Generierung (RFC-6238-Vektoren),
Domain-Validierung, TLD-Ableitung inkl. mehrteiliger Suffixe, Perioden- und Ländercode-Parsing.
-
dns export <domain>– bestehende Zone in eine Records-Datei serialisieren (Gegenstück zuapply) - Session-Cookie wiederverwenden statt bei jedem Befehl neu anzumelden
- Eindeutiges Matching mehrfacher
TXT/MX-Records inapply -
dns rmanhand von Name/Typ statt nur per ID - Löschen nicht deklarierter Records in
apply(--prune, opt-in) - Testabdeckung (FQDN-Ableitung, TOTP, Domain-Validierung)
- Domain-Verwaltung (
domain check/price/ls/info/buy,contact ls/add) - TypeScript-Umbau mit strikter Typisierung
- Veröffentlichung auf npm (Namensverfügbarkeit prüfen) für
npx inwx-cli - Optionale Credential-Ablage im OS-Keychain
- Windows-Terminal verifizieren
MIT © Noel Lang