donazopy
Pre-release / pre-alpha. The command surface changed in this revision (issue 203) — old commands have been removed and replaced with a unified target notation.
donazopy is a focused Python CLI for local DNS zone-file work and real provider operations.
Supported workflows:
- Local zone files — validate, normalize, compare two zones as a change summary.
- Provider DNS — load credentials from
.envor environment, list/export/import records, copy zones between providers, read/assign nameservers, diff a live zone against a file.
Operational providers today: Cloudflare, GoDaddy, IONOS, and Joker.com (DMAPI). Further providers are tracked in TODO.md.
Installation
uv sync
Python 3.12+, Hatch + hatch-vcs for git-tag-derived versions.
Credentials
Credentials are loaded with python-dotenv:
donazopyauto-discovers.envfrom the current working directory.- Pass an explicit file with
--dotenv-path=path/to/.env. - Real environment variables override
.envvalues. - Status output shows presence and source only — secret values are never printed.
Create an ignored .env with the credentials for the provider(s) you use:
CLOUDFLARE_DNS_TOKEN=your-token # Cloudflare (DNS Read for list/export, DNS Edit for import/copy, Zone Edit for create-zone)
CLOUDFLARE_DNS_ACCOUNT=your-account-id # Cloudflare, optional — only needed for create-zone when the token spans multiple accounts
GODADDY_API_KEY=your-key # GoDaddy
GODADDY_API_SECRET=your-secret
IONOS_API_PUBLIC=your-public-prefix # IONOS (combined as "{public}.{secret}" in the X-API-Key header)
IONOS_API_SECRET=your-secret
JOKER_API_KEY=your-dmapi-api-key # Joker.com DMAPI
Run donazopy status to see, per provider, which variables are present and where they came from (values are never printed).
Target notation
Most commands accept a TARGET argument using the notation:
[provider/][domain][:record_type][:host_name][:value]
| Example | Meaning |
|---|---|
example.com |
that domain on whichever sole operational provider manages it |
cloudflare/example.com |
example.com on Cloudflare specifically |
cloudflare/* |
all domains on Cloudflare |
cloudflare/example.com:A |
A records for example.com |
cloudflare/example.com:A:www:1.2.3.4 |
filtered to a specific host and value |
example.com.zone |
a local zone file (resolved by extension / path heuristics) |
* in any filter segment means "no filter". A trailing :TYPE:host:value tuple is optional and defaults to no filtering.
Quick start
# Tool version and operational providers
uv run donazopy version
uv run donazopy providers
# Domains a provider manages
uv run donazopy domains ionos --dotenv-path=.env
# Provider metadata and credential status
uv run donazopy status
uv run donazopy status cloudflare
uv run donazopy status cloudflare/example.com --dotenv-path=.env
# DNS records (with optional record-level filters)
uv run donazopy records cloudflare/example.com --dotenv-path=.env
uv run donazopy records cloudflare/example.com:A --dotenv-path=.env
# Export a live zone as BIND text
uv run donazopy export cloudflare/example.com --dotenv-path=.env
uv run donazopy export cloudflare/example.com --output=example.com.zone --overwrite
uv run donazopy export cloudflare/* --output=zones --dotenv-path=.env # writes zones/<domain>.zone for every zone --skip-ns
# Import BIND zone text into a provider
uv run donazopy import-zone cloudflare/example.com example.com.zone --dotenv-path=.env
uv run donazopy import-zone cloudflare/example.com example.com.zone --clean --dotenv-path=.env # wipe target first
# Copy a zone from one provider target to another
uv run donazopy copy cloudflare/example.com cloudflare/example-staging.com --skip-ns
uv run donazopy copy cloudflare/example.com cloudflare/example.com --replace # wipe dest first
uv run donazopy copy cloudflare/* ionos/ --clean --skip-ns --dotenv-path=.env # iterate every Cloudflare zone, copy each to IONOS (same name)
uv run donazopy copy ionos/example.com cloudflare/example.com --skip-ns --replace # migrate IONOS -> Cloudflare (creates the CF zone if missing)
# Create a hosted zone (Cloudflare)
uv run donazopy create-zone cloudflare/example.com --dotenv-path=.env
# Read or assign nameservers
uv run donazopy nameservers cloudflare/example.com --dotenv-path=.env
uv run donazopy nameservers godaddy/* --dotenv-path=.env # {domain: [nameserver, ...]} for every domain
uv run donazopy nameservers godaddy/example.com ns1.example.net ns2.example.net # assign (GoDaddy/Joker only)
# Validate and normalize a local zone file
uv run donazopy validate example.com.zone --origin=example.com.
uv run donazopy normalize example.com.zone --origin=example.com.
uv run donazopy normalize example.com.zone --output=normalized.zone --overwrite
# Diff two zones — each side can be a file path or a provider target
uv run donazopy diff before.zone after.zone --origin=example.com.
uv run donazopy diff cloudflare/example.com example.com.zone
# Diagnose a zone (provider target or local file); add --fix to apply safe repairs
uv run donazopy doctor cloudflare/example.com --dotenv-path=.env
uv run donazopy doctor example.com.zone --origin=example.com.
uv run donazopy doctor cloudflare/example.com --fix --dotenv-path=.env
uv run donazopy doctor cloudflare/example.com --json --dotenv-path=.env
uv run donazopy doctor cloudflare/example.com --fix --dmarc-email=ops@dmarc-service.com --dotenv-path=.env
uv run donazopy doctor cloudflare/* --fix --dotenv-path=.env # diagnose & fix every Cloudflare zone
Command reference
| Command | Description |
|---|---|
donazopy version |
Print installed version |
donazopy providers |
List operational provider keys |
donazopy domains PROVIDER [--dotenv-path=PATH] |
List the domains/zones a provider manages (PROVIDER may be ionos or ionos/*) |
donazopy status [TARGET] [--dotenv-path=PATH] |
Provider metadata + credential status |
donazopy records TARGET [--dotenv-path=PATH] |
List DNS records (target may include record filters) |
donazopy export TARGET [--output=PATH] [--overwrite] [--skip-ns] [--skip-types=A,AAAA,...] [--dotenv-path=PATH] |
Export zone as BIND text. Wildcard target (provider/*) requires --output=DIR and writes each zone to DIR/<domain>.zone; for single-domain targets --output may be a file or a directory (DIR/<domain>.zone is autogenerated). |
donazopy import-zone TARGET PATH [--proxied] [--clean] [--dotenv-path=PATH] |
Import BIND zone file into provider; --clean wipes existing records first |
donazopy create-zone TARGET [--dotenv-path=PATH] |
Create a hosted zone for the domain (Cloudflare; idempotent). Other providers raise "not supported" — the zone exists with the domain. |
donazopy copy SOURCE DEST [--skip-ns] [--skip-types=...] [--replace] [--clean] [--create=BOOL] [--dotenv-path=PATH] |
Copy zone between provider targets; by default the destination zone is created if missing (--create=False to skip). --clean is a synonym for --replace (wipe target before copying). Wildcard source (provider/*) iterates every zone the source provider manages and copies each to the same-named zone on the destination (use dest_provider/). |
donazopy nameservers TARGET [NS1 NS2 ...] [--dotenv-path=PATH] |
Read or assign nameservers |
donazopy diff A B [--origin=...] [--dotenv-path=PATH] |
Diff two zones (file paths or provider targets) |
donazopy validate PATH [--origin=...] |
Validate a local BIND zone file |
donazopy normalize PATH [--origin=...] [--output=PATH] [--overwrite] |
Normalize a local BIND zone file |
donazopy doctor TARGET [--fix] [--dmarc-email=ADDR] [--json] [--output=PATH] [--overwrite] [--origin=...] [--dotenv-path=PATH] |
Diagnose a zone (provider target or local file) for migration artifacts, TXT duplicates, missing email-security records, and CNAME conflicts; with --fix, applies safe repairs and writes a backup. --dmarc-email=ADDR plugs ADDR into the suggested/applied DMARC rua=mailto: and prints the external-destination authorization record when the receiving domain differs from the zone. |
Provider matrix
| Provider | Status |
|---|---|
| Cloudflare | Operational: record listing, BIND export/import, zone copy, nameserver read, credential status. assign_nameservers is "not supported" (Cloudflare DNS cannot set registrar delegation). |
| GoDaddy | Operational: domain list, record listing, BIND export, record import (PATCH/append), delete_all_records, registrar nameserver read and assignment. |
| IONOS | Operational: zone list, record listing, BIND export/import, delete_all_records, apex nameserver read. assign_nameservers is "not supported" (the IONOS DNS API cannot change registrar delegation). |
| Joker.com (DMAPI) | Operational: domain list, virtual DNS zone export/import (Joker↔BIND), delete_all_records, registrar nameserver read and assignment. |
| AWS Route 53, Google Cloud DNS, Azure DNS, Namecheap, DNSimple, Gandi, Porkbun, Dynadot, Vercel, DigitalOcean, Hetzner, Linode, Vultr, Hosting.com, Hostinger, Bluehost | Planned only — not exposed until real adapters and tests exist. |
Safety model
- Zone-file operations are local and deterministic.
- Output writes refuse to overwrite existing files unless
--overwriteis passed. copy --replacedeletes all destination records before importing; use with care.- Credentials are loaded through
python-dotenvand environment variables, then redacted in status output. - Unsupported providers are not exposed by
donazopy providers; only providers with a real adapter and tests appear. assign_nameserversraises a clear "not supported" error on DNS-only delegation surfaces (Cloudflare, IONOS); use a registrar-capable provider (GoDaddy, Joker) to change delegation.
Documentation
Full docs are built from src_docs/md/ with MkDocs + ProtoDocs + MaterialX:
./docs.sh serve # live preview at http://127.0.0.1:8000
./docs.sh build # write static output to docs/
Config: mkdocs/mkdocs.yml. Pre-built output is committed to docs/.
Development commands
uv sync
uvx hatch test
uvx ruff check .
uvx --with pytest --with dnspython --with fire --with httpx --with python-dotenv pyright src tests
python -m compileall src tests
./build.sh
./build.sh runs uvx hatch clean and uvx hatch build. ./publish.sh runs uvx hatch clean, uvx gitnextver, uvx hatch build, then uv publish.
Do not commit provider credentials, .env, .pypirc, or private research material.
Release files for donazopy 1.0.22
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| donazopy-1.0.22.tar.gz | 935.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| donazopy-1.0.22-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.0 MB
Release files / donazopy-1.0.22.tar.gz
| Download URL | donazopy-1.0.22.tar.gz |
|---|---|
| Size | 935.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
535e99997408cebcc40b50d8ee081f51b657f978fa0c57693e966ea515aa8a48
|
|
BLAKE2b-256 checksum How to use checksums |
86d34a3b88c388b0f768279455ff5222d631b1daa1e012c19f81161661283c52
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / donazopy-1.0.22-py3-none-any.whl
| Download URL | donazopy-1.0.22-py3-none-any.whl |
|---|---|
| Size | 64.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1a5ae37373d6a4667c7aa69639cf3067dc5c25dc8add80c2d0799c77211a9c6a
|
|
BLAKE2b-256 checksum How to use checksums |
cb6a8a4a2cf816bfb5181ae5022e719b7623a3a8a4d608a5cf10551baa387a57
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|