Metadata-Version: 2.4
Name: bindantic
Version: 1.0.0
Summary: Pydantic-based BIND9 configuration management library
Author-email: Daniil Gruzdev <gruzdev.daniil@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/DVSAWR/bindantic
Project-URL: Issue Tracker, https://github.com/DVSAWR/bindantic/issues
Keywords: bind,bind9,dns,pydantic,configuration,named,named.conf
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: Topic :: Internet :: Name Service (DNS)
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pydantic<3.0.0,>=2.12.5
Provides-Extra: dev
Requires-Dist: pytest>=8.0.0; extra == "dev"
Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
Requires-Dist: mypy>=1.8.0; extra == "dev"
Requires-Dist: ruff>=0.3.0; extra == "dev"
Requires-Dist: pre-commit>=3.6.0; extra == "dev"
Provides-Extra: docs
Requires-Dist: mkdocs>=1.6.0; extra == "docs"
Requires-Dist: mkdocs-material>=9.5.0; extra == "docs"
Requires-Dist: mkdocstrings[python]>=0.25.0; extra == "docs"
Requires-Dist: griffe-pydantic>=1.0.0; extra == "docs"
Dynamic: license-file

# bindantic

[![PyPI version](https://img.shields.io/pypi/v/bindantic)](https://pypi.org/project/bindantic/)
[![Python versions](https://img.shields.io/pypi/pyversions/bindantic)](https://pypi.org/project/bindantic/)
[![License](https://img.shields.io/github/license/DVSAWR/bindantic)](LICENSE)
[![CI](https://github.com/DVSAWR/bindantic/actions/workflows/ci.yml/badge.svg)](https://github.com/DVSAWR/bindantic/actions/workflows/ci.yml)
[![Coverage](https://codecov.io/gh/DVSAWR/bindantic/branch/main/graph/badge.svg)](https://codecov.io/gh/DVSAWR/bindantic)
[![Docs](https://img.shields.io/badge/docs-mkdocs-blue)](https://DVSAWR.github.io/bindantic/)
[![PyPI Downloads](https://static.pepy.tech/personalized-badge/bindantic?period=total&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=downloads)](https://pepy.tech/projects/bindantic)

**bindantic** - a library for managing **[BIND9](https://bind9.readthedocs.io/en/latest/reference.html)** DNS server configuration via **[Pydantic](https://github.com/pydantic/pydantic)** models.

Instead of manually editing `named.conf` , you describe the configuration in Python, and the library generates correct BIND9 syntax and (optionally) places files into the required directories.


## Features

- **Full support for all `named.conf` blocks:**
    - `acl`, `controls`, `dnssec-policy`, `http`, `key`, `key-store`, `logging`, `options`,
`remote-servers`, `server`, `statistics-channels`, `tls`, `trust-anchors`, `view`, `zone`.
- **All common resource record types:**
    - `A`, `AAAA`, `CAA`, `CERT`, `CNAME`, `DNAME`, `DNSKEY`, `DS`, `HINFO`, `LOC`, `MX`, `NAPTR`, `NSEC`, `NS`, `PTR`, `RP`, `RRSIG`, `SOA`, `SPF`, `SRV`, `SSHFP`, `TLSA`, `TXT`.
- **Built-in validation** - pass strings, numbers, IP addresses, durations – the library will format them correctly for BIND.
- **Syntax generation in one line**
    - `model.model_bind_syntax()` - for any block or the whole `named.conf`,
    - `zone.model_bind_syntax_zone_file()` - for a ready-to-use zone file.
- **Generate files without writing / write to disk**
    - `config.generate_files()` - returns a list of generated files
    - `config.write_files("./my_config")` - creates `named.conf`, zones, keys, DNSSEC policies and organises them into subdirectories.
- **Python** - 3.10+, static typing, 96% test coverage.
- **No extra dependencies** - only Pydantic


## Installation

```bash
pip install bindantic
```

## ⚠️ NOTE:

The  `named-checkconf` utility from the `bind-utils` package may be older than your BIND server and may not recognise new directives.

**bindantic** generates syntax according to the latest stable BIND 9.20.x version. If you check the configuration with an older utility you may get errors. Always use the same version of `named-checkconf` as your server, if possible.

## Quick Start

Example of a minimal configuration

```python
from bindantic import (
    ARecord,
    NamedConfig,
    NSRecord,
    OptionsBlock,
    SOARecord,
    ZoneBlock,
    ZoneTypeEnum,
)

config = NamedConfig(
    options_block=OptionsBlock(
        directory="/etc/bind",
        recursion=True,
        allow_recursion=["localhost", "localnets"],
        listen_on=["any"],
        listen_on_v6=["any"],
    ),
    zone_blocks=[
        ZoneBlock(
            comment="optional comment",
            name="example.com",
            zone_type=ZoneTypeEnum.PRIMARY,
            file="zones/example.com.zone",
            resource_records=[
                SOARecord(
                    mname="ns1.example.com",
                    rname="admin.example.com",
                    serial=2026010101,
                    refresh=10800,
                    retry=3600,
                    expire=604800,
                    minimum=3600,
                    origin="example.com",
                    ttl=3600,
                ),
                NSRecord(nsdname="ns1.example.com", comment="optional comment"),
                ARecord(name="@", address="192.168.1.1"),
            ],
        )
    ],
)
```

<details> <summary> Output of `config.model_bind_syntax()` </summary>

```txt
options {
    allow-recursion {
        localhost;
        localnets;
    };
    directory "/etc/bind";
    listen-on {
        any;
    };
    listen-on-v6 {
        any;
    };
    recursion yes;
};

# optional comment
zone example.com. {
    type primary;
    file "zones/example.com.zone";
};
```
</details>

<details> <summary> Output of `config.zone_blocks[0].model_bind_syntax_zone_file()` </summary>

```txt
$TTL 3600
$ORIGIN example.com.
@                    IN   SOA ns1.example.com. admin.example.com. (
                                     2026010101 ; Serial number (YYYYMMDDNN)
                                     10800      ; Refresh time
                                     3600       ; Retry time
                                     604800     ; Expire time
                                     3600       ; Minimum TTL
                     )
@                    IN   NS         ns1.example.com. ; optional comment
@                    IN   A          192.168.1.1
```
</details>

<details> <summary> Output of `config.generate_files()` </summary>

```txt
[
    GeneratedFile(
        path=PosixPath("/etc/bind/zones/example.com.zone"),
        content="<CONTENT>",
        type="zone",
    ),
    GeneratedFile(
        path=PosixPath("/etc/bind/named.conf"),
        content="<CONTENT>",
        type="config",
    ),
]
```
</details>

<details> <summary> Output of `config.write_files(base_dir="./examples/example")` </summary>

```txt
example_bind/
├── named.conf
└── zones/
    └── example.com.zone
```

```
# Automatically generated by bindantic - please adjust!

options {
    allow-recursion {
        localhost;
        localnets;
    };
    directory "examples/example";
    listen-on {
        any;
    };
    listen-on-v6 {
        any;
    };
    recursion yes;
};

# optional comment
zone example.com. {
    type primary;
    file "zones/example.com.zone";
};
```

```txt
$TTL 3600
$ORIGIN example.com.
@                    IN   SOA ns1.example.com. admin.example.com. (
                                     2026010101 ; Serial number (YYYYMMDDNN)
                                     10800      ; Refresh time
                                     3600       ; Retry time
                                     604800     ; Expire time
                                     3600       ; Minimum TTL
                     )
@                    IN   NS         ns1.example.com. ; optional comment
@                    IN   A          192.168.1.1
```
</details>

\
The file [./examples/manual_example.py](./examples/manual_example.py) contains usage examples for all supported models.

## More examples

Focused, runnable scripts for common real-world setups:

- [`examples/multi_view_split_horizon.py`](./examples/multi_view_split_horizon.py) - split-horizon
  DNS: internal clients see private records, everyone else sees public ones.
- [`examples/secondary_zone_tsig.py`](./examples/secondary_zone_tsig.py) - a primary/secondary zone
  pair with TSIG-authenticated zone transfers.
- [`examples/dnssec_signed_zone.py`](./examples/dnssec_signed_zone.py) - a DNSSEC-signed zone, end
  to end: key-store, `dnssec-policy` (KSK+ZSK), and a zone using that policy.

## Versioning

bindantic follows [Semantic Versioning](https://semver.org/).

- **Public API** - everything importable from the top-level `bindantic` package (models, enums,
  field type aliases) is covered by semver guarantees.
- **Internal** - any module prefixed with `_` (e.g. `bindantic._base_model`,
  `bindantic._base_types_validation`) is an implementation detail and may change without notice.
- **Major** - removing/renaming a public model or field, or a change that makes previously valid
  input invalid, or a change to the generated BIND syntax output.
- **Minor** - new models, new optional fields, support for new BIND directives.
- **Patch** - bug fixes that don't change the public API surface.

bindantic targets the latest stable BIND 9.20.x release; tracking a new BIND directive is treated
as a minor bump unless it conflicts with existing behavior.
