Skip to content

noel-lang/inwx-cli

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

14 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

inwx-cli

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)

Anforderungen

  • Node.js ≥ 18 (nutzt das globale fetch und Headers.getSetCookie; empfohlen ≥ 20, getestet auf 22)
  • Zum Bauen aus dem Quellcode: TypeScript 5 (als devDependency enthalten, kein globales tsc nötig)
  • Ein INWX-Account mit aktiviertem API-Zugang. Für Trockenläufe von Domain-Registrierungen ein separater OT&E-Testaccount (eigene Registrierung, eigene Zugangsdaten).

Installation

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.com

Globale Installation:

npm install -g github:noel-lang/inwx-cli

Lokale 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 global

Build & Entwicklung

Der 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 --test

Der 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.

Tests

  • Unit-Tests (test/*.test.ts außer e2e) 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).

Authentifizierung

Ablauf

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 entfernen

Zwei-Faktor-Authentifizierung (TOTP)

Ist auf dem Account 2FA aktiv, gibt es zwei Betriebsmodi:

  1. 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).
  2. 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.

Zugangsdaten-Speicherung

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: pass und totpSecret sind 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.

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

Befehle

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.

Domains

domain check (READ-ONLY)

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.io

Der 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.

domain price

inwx domain price example.de

Kombiniert den konkreten Domainpreis samt Verfügbarkeit aus domain.check mit der TLD-weiten Preisliste (Registrierung/Verlängerung/Transfer) aus domain.getPrices.

domain ls / domain info

inwx domain ls                 # eigene Domains (domain.list)
inwx domain info example.de    # Status, Ablaufdatum, Handles, Nameserver (domain.info)

domain buy (mit Sicherheitsnetz)

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-live läuft buy immer gegen OT&E. Ein domain.create gegen 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-live muss der Domainname trotzdem exakt eingetippt werden.
  • INWX verlangt alle vier Kontakt-Handles. Werden --admin/--tech/--billing nicht 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 --yes

Kontakte

Domain-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.

OT&E: Registrierung durchspielen

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-Registrierung

Zum 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.

DNS-Records

dns add

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 (wwwwww.example.com, @example.com).

dns rm

inwx dns ls example.com          # IDs ermitteln
inwx dns rm example.com 12345678 # löschen (mit Rückfrage)
inwx dns rm example.com 12345678 --yes

Deklaratives Zonen-Management (apply)

apply 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.

Dateischema

{
  "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.

Abgleichslogik

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.

Dry-Run

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-Testsystem

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 Kosten

Ohne --yes-live läuft domain buy ohnehin gegen OT&E, --ote erzwingt es zusätzlich für alle übrigen Befehle.

Verwendete DomRobot-API

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).

Projektstruktur

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)

Sicherheit

  • 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 buy registriert standardmäßig nur auf OT&E; ein echter, kostenpflichtiger Kauf auf PROD verlangt das explizite Flag --yes-live und eine Tippbestätigung des Domainnamens.
  • Empfehlung: einen INWX-Sub-Account mit auf DNS/Domains beschränkten Rechten verwenden.

Tests

Netzwerkfreie Unit-Tests über das eingebaute node --test:

npm test

Abgedeckt: FQDN-Ableitung (toFqdn), TOTP-Generierung (RFC-6238-Vektoren), Domain-Validierung, TLD-Ableitung inkl. mehrteiliger Suffixe, Perioden- und Ländercode-Parsing.

Roadmap

  • dns export <domain> – bestehende Zone in eine Records-Datei serialisieren (Gegenstück zu apply)
  • Session-Cookie wiederverwenden statt bei jedem Befehl neu anzumelden
  • Eindeutiges Matching mehrfacher TXT/MX-Records in apply
  • dns rm anhand 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

Lizenz

MIT © Noel Lang

About

CLI für INWX-DNS über die DomRobot-API

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages