# BindConfigForge — Planning Document

## 1. Overview

BindConfigForge is a configuration-generation tool for BIND9.

Its purpose is to make large collections of BIND zone declarations easier to maintain by moving the human-maintained configuration into small YAML files and generating BIND configuration.

BindConfigForge is **not** intended to manage DNS zone files or replace ZoneForge. ZoneForge remains responsible for zone management, zone templates, and zone data.

BindConfigForge operates one level above that:

                    BindConfigForge
                           │
                  YAML configuration
                           │
                           ▼
                 BIND zone configuration
                           │
                           ▼
                    named.conf.local
                           │
                           ▼
                         BIND9
                           │
                           ▼
                       DNS zones

                       ZoneForge
                           │
                           ▼
                     zone contents

The initial scope is deliberately limited to BIND `zone { ... };` configuration, plus the key-file imports required by that configuration.

---

# 2. Problem

The current BIND configuration contains a large number of zone declarations in `/etc/bind/named.conf.local`.

The zones are largely standardized, but individual zones occasionally have differences.

For example, most zones may use the same configuration:

* `type primary`
* a standardized zone-file location;
* a standardized key directory;
* inline DNSSEC signing;
* the same DNSSEC policy;
* a TSIG key allowing updates from the administrator's MacBook;
* the same update-policy structure.

Some zones have additional requirements.

For example, a zone used with Traefik may additionally need:

```bind
grant traefik-acme name _acme-challenge.<zone>. TXT;
````

The current configuration therefore contains a large amount of repetition.

## The maintenance problem

When a common configuration needs to change, every zone has to be edited.

For example:

> Add another update-policy grant to every zone.

With 143 zones, this means making the same conceptual change 143 times.

This is error-prone, tedious, and makes it difficult to distinguish configuration that is genuinely specific to a zone from configuration that is merely repeated infrastructure boilerplate.

The problem is not that BIND lacks the ability to express these configurations. The problem is that `named.conf.local` is a poor human-facing source of truth for a large collection of highly similar zones.

---

# 3. Goals

## Primary goals

BindConfigForge should:

1. Provide a human-maintainable YAML representation of BIND zone configuration.
2. Generate valid BIND zone declarations.
3. Generate the key imports required by the generated zone declarations.
4. Eliminate unnecessary repetition between zone configurations.
5. Allow common configuration to be defined once and reused.
6. Allow multiple reusable configuration mixins to be combined for a zone.
7. Allow individual zones to have configuration that differs from common configuration.
8. Validate the complete generated BIND configuration before replacing the generated configuration file.
9. Make generated configuration reproducible from the YAML source.
10. Keep source configuration understandable without requiring knowledge of generated BIND syntax.
11. Work cleanly alongside ZoneForge.
12. Allow users to customize filesystem paths without requiring BindConfigForge-specific changes.

## Secondary goals

BindConfigForge should eventually make it easy to:

* identify which configuration applies to a particular zone;
* identify which zones use a particular mixin;
* identify which zones reference a particular key;
* make a global configuration change and regenerate the entire BIND configuration;
* review configuration changes through version control;
* safely detect configuration errors before they reach the running BIND server.

---

# 4. Non-Goals

The MVP should **not** attempt to become a complete BIND configuration-management system.

In particular, BindConfigForge will initially not manage:

* BIND zone-file contents;
* DNS records;
* DNSSEC keys themselves;
* catalog-zone contents;
* `options { ... };`;
* `logging { ... };`;
* `acl { ... };`;
* `controls { ... };`;
* views;
* statistics channels;
* arbitrary BIND configuration statements.

Those remain outside the scope of BindConfigForge.

ZoneForge remains responsible for zone data and zone templates.

BindConfigForge will reference externally managed TSIG/DNSSEC key files but will not create or manage those keys.

This boundary is intentional. Supporting every possible BIND configuration directive would greatly increase the complexity and the number of ways BindConfigForge itself could introduce configuration errors.

---

# 5. CLI

The CLI name is not yet considered final.

The initial command-line interface is:

```text
bindconfigforge
    -s, --source <path>    Required source file/directory
    -v, --verbose          Verbose diagnostics
    -q, --quiet            Suppress informational diagnostics
        --check            Validate without generating output
    -h, --help
        --version
```

## Source

`--source` is required.

The source may be a file or directory, depending on the configuration layout.

The preferred configuration layout is directory-based, with a base configuration file, mixins, and zone definitions.

## Output

BindConfigForge does **not** require an output filename flag for the MVP.

Generated BIND configuration should be written to stdout by default.

This allows normal Unix redirection:

```text
bindconfigforge --source ./bindconfig > named.conf.local
```

or:

```text
bindconfigforge --source ./bindconfig > /etc/bind/named.conf.local
```

The tool therefore does not need to know whether the generated output is being:

* displayed;
* redirected to a file;
* piped into another command;
* captured by an automation system.

Deployment to a live configuration path is the responsibility of the caller.

Future versions may add an explicit output option if operational experience demonstrates that one is useful.

## Verbosity

`--verbose` enables additional diagnostic information useful for debugging configuration resolution and generation.

`--quiet` suppresses informational diagnostics.

Errors must still be emitted when quiet mode is active.

The exact diagnostic levels and formatting can be determined during implementation.

## Check mode

`--check` validates the source configuration and generated BIND configuration without producing normal generated output.

The purpose is to support CI and pre-deployment validation.

A successful check should result in a successful exit status.

A configuration error should result in a non-zero exit status.

---

# 6. Configuration Layout

The preferred configuration layout is directory-based.

Conceptually:

```text
bindconfig/
├── bindconfig.yaml
├── mixins/
│   ├── standard-zone.yaml
│   ├── update-zone-from-macbook-local.yaml
│   └── update-from-traefik.yaml
└── zones/
    ├── example.com.yaml
    ├── isdavecanalesgoneyet.com.yaml
    └── 1x0.us.yaml
```

The directory is the configuration source.

Adding a zone consists primarily of adding a YAML file.

Removing a zone consists primarily of removing its YAML file.

Changing a shared behavior consists of changing one mixin and regenerating the configuration.

---

# 7. Base Configuration

The base configuration is stored in `bindconfig.yaml`.

Its purpose is to define:

* global defaults;
* known BIND key files.

The initial defaults are intentionally limited to:

```yaml
defaults:
  type:
  zone_directory:
  key_directory:
```

No other global defaults are currently planned.

## Type default

If no type is specified by either a zone or applicable mixin, BindConfigForge internally defaults the zone type to:

```text
primary
```

`primary` is the terminology BindConfigForge will use for the default zone type.

## Zone directory default

If no `zone_directory` is specified, the internal default is:

```text
.
```

The zone directory represents the parent directory containing per-zone directories.

For example:

```text
zone_directory: /var/lib/bind
zone: example.net
```

produces the zone directory:

```text
/var/lib/bind/example.net/
```

The default is deliberately not intended to restrict users to a particular filesystem layout.

A user may choose:

```yaml
defaults:
  zone_directory: /var/lib/bind
```

or:

```yaml
defaults:
  zone_directory: /srv/bind/zones
```

or another appropriate layout.

## Key directory default

If no `key_directory` is specified, the internal default is:

```text
keys
```

For a relative key-directory value, it is resolved within the zone directory.

For example:

```text
zone_directory: /var/lib/bind
zone: example.net
key_directory: keys
```

produces:

```text
/var/lib/bind/example.net/keys/
```

## Example base configuration

```yaml
defaults:
  type: primary
  zone_directory: /var/lib/bind
  key_directory: keys

keys:
  macbook-local:
    file: /etc/bind/keys/macbook-local.key

  traefik-acme:
    file: /etc/bind/keys/traefik-acme.key
```

---

# 8. Keys

BindConfigForge supports a collection of externally managed BIND key files.

A key definition consists of:

* a key name;
* a key file.

The YAML mapping key is the BIND key name.

For example:

```yaml
keys:
  macbook-local:
    file: /etc/bind/keys/macbook-local.key
```

The key name is retained in the BindConfigForge configuration even though BIND obtains the actual key definition from the referenced file.

The name is important because update-policy grants refer to the key by name:

```yaml
key: macbook-local
```

This allows BindConfigForge to detect misspelled or undefined key references before generating the configuration.

## Key imports

Every key declared in the base configuration is imported into the generated BIND configuration.

If three keys are declared, all three are imported, even if only two are referenced by the zones generated by BindConfigForge.

This is intentional.

A key may be required by another portion of the BIND configuration that is outside BindConfigForge's current scope.

BindConfigForge must therefore **not automatically remove unused key imports**.

## Unused key diagnostics

If a declared key is not referenced by any generated BindConfigForge zone configuration, BindConfigForge should produce an informational diagnostic.

For example:

```text
INFO: key "monitoring" is declared but not referenced by any generated zone
```

This should be a warning/informational diagnostic rather than an error.

An unused key may be legitimate because:

* the key was recently renamed;
* another part of BIND configuration uses it;
* another tool uses it;
* the user is preparing for future configuration;
* the key is intentionally imported even though BindConfigForge does not currently consume it.

The diagnostic exists primarily to alert the user to a possible configuration mistake.

---

# 9. Zone Definitions

Each zone has its own YAML configuration file.

For example:

```text
zones/
├── example.com.yaml
├── isdavecanalesgoneyet.com.yaml
└── 1x0.us.yaml
```

A zone definition must identify the zone:

```yaml
zone: isdavecanalesgoneyet.com
```

It may then specify mixins and any zone-specific configuration.

For example:

```yaml
zone: isdavecanalesgoneyet.com

mixins:
  - standard-zone
  - update-zone-from-macbook-local
  - update-from-traefik
```

The zone file should primarily describe what is unique or important about that zone.

Common behavior belongs in mixins.

---

# 10. Mixins

A mixin is a reusable partial zone configuration.

A mixin uses the same configuration vocabulary as a zone wherever the property is meaningful and composable.

A mixin does not identify a zone.

For example:

```yaml
type: primary
inline_signing: true
dnssec_policy: standard-v2
```

A mixin may also define an update policy:

```yaml
update_policy:
  - grant:
      key: macbook-local
      match: name
      name: "{zone}."
      types:
        - ANY

  - grant:
      key: macbook-local
      match: zonesub
      types:
        - ANY
```

The special `{zone}` placeholder is resolved using the zone consuming the mixin.

For example:

```yaml
name: "_acme-challenge.{zone}."
```

becomes:

```text
_acme-challenge.isdavecanalesgoneyet.com.
```

## Mixin restrictions

Not every zone property is meaningful in a mixin.

Properties that identify or locate a specific zone, such as:

* `zone`;
* zone-specific file paths;

should not be used to create nonsensical mixins.

The implementation should distinguish between:

* properties that are composable through mixins;
* properties that are inherently specific to an individual zone.

---

# 11. Mixin Composition

A zone may use multiple mixins.

For example:

```yaml
zone: 1x0.us

mixins:
  - standard-zone
  - update-zone-from-macbook-local
  - update-from-traefik
```

The resulting configuration combines the applicable properties from all three.

The intended merge behavior is:

* list-like configuration is generally accumulated;
* compatible scalar configuration is combined;
* conflicting scalar configuration produces an error;
* explicit zone-level configuration may override or supplement mixin configuration where the property supports that behavior;
* ambiguous configuration must not be silently resolved.

The exact merge rules will be defined per property rather than relying on a generic deep-merge algorithm.

For example:

```text
type
    conflicting values → error

dnssec_policy
    conflicting values → error

inline_signing
    conflicting values → error

update_policy
    accumulate grants

allow_transfer
    accumulate values
```

The final rules should be encoded in the application's configuration model and tested explicitly.

---

# 12. Zone Path Configuration

BindConfigForge should not require users to specify complete zone-file paths for every zone.

The normal layout is:

```text
{zone_directory}/{zone}/domain.zone
```

with the key directory:

```text
{zone_directory}/{zone}/{key_directory}
```

For example:

```yaml
defaults:
  zone_directory: /var/lib/bind
  key_directory: keys
```

and:

```yaml
zone: isdavecanalesgoneyet.com
```

produce:

```text
/var/lib/bind/isdavecanalesgoneyet.com/domain.zone
/var/lib/bind/isdavecanalesgoneyet.com/keys/
```

## `zone_file`

A zone may explicitly specify `zone_file`.

If `zone_file` is relative:

```yaml
zone_file: zone.db
```

it is resolved relative to:

```text
{zone_directory}/{zone}/
```

producing:

```text
{zone_directory}/{zone}/zone.db
```

If `zone_file` begins with `/`, it is treated as an absolute path.

For example:

```yaml
zone_file: /srv/bind/zones/example.net.zone
```

produces exactly:

```text
/srv/bind/zones/example.net.zone
```

In this case, `zone_directory` is ignored for resolving the zone file.

This allows both conventional and unconventional layouts without making the defaults restrictive.

For example:

```yaml
zone: example.net
zone_directory: /srv/bind/zones
zone_file: zone.db
```

or:

```yaml
zone: example.net
zone_file: /srv/bind/zones/example.net.zone
```

are both valid configurations.

## `key_directory`

`key_directory` follows the same general relative/absolute path principle.

A relative value is resolved within the zone directory.

An absolute value is used as-is.

The key directory is relevant when the generated zone configuration requires DNSSEC key storage.

---

# 13. Initial Zone Behaviors

The MVP intentionally supports only the behaviors currently required by the environment.

The initial supported behaviors are:

1. `type`
2. DNSSEC policy
3. `inline-signing`
4. `update-policy`
5. `allow-transfer`

Additional BIND zone behaviors may be added later.

The tool should not attempt to support additional BIND directives merely because they exist.

---

# 14. `type`

The zone type defaults internally to:

```text
primary
```

A zone or mixin may explicitly specify another BIND zone type supported by the target BIND installation.

BindConfigForge does not need to impose BIND's complete set of valid zone types at the YAML schema level.

The generated configuration is ultimately validated by BIND.

---

# 15. DNSSEC

BindConfigForge supports specifying a BIND DNSSEC policy:

```yaml
dnssec_policy: standard-v2
```

The value is passed through to BIND.

BindConfigForge should not attempt to implement or reproduce BIND's DNSSEC policy validation.

Whether a particular DNSSEC policy exists or is valid is ultimately BIND's responsibility.

---

# 16. Inline Signing

BindConfigForge supports:

```yaml
inline_signing: true
```

which generates:

```bind
inline-signing yes;
```

The application should not invent BIND-version-specific DNSSEC behavior.

If a user wants a particular DNSSEC configuration, it should be explicitly represented in the YAML or a mixin.

For the current environment, `standard-zone` may contain both:

```yaml
inline_signing: true
dnssec_policy: standard-v2
```

---

# 17. Update Policy

Update-policy grants are represented structurally in YAML rather than as raw BIND configuration strings.

For example:

```yaml
update_policy:
  - grant:
      key: traefik-acme
      match: name
      name: "_acme-challenge.{zone}."
      types:
        - TXT
```

generates:

```bind
grant traefik-acme name _acme-challenge.example.net. TXT;
```

A grant may refer to a key declared in the base configuration.

The key reference is validated by BindConfigForge.

However, BindConfigForge should **not attempt to lint arbitrary BIND update-policy values**.

For example:

```yaml
match: zonesup
```

should not necessarily be rejected by BindConfigForge merely because it does not recognize the value.

The generated configuration is passed to:

```text
named-checkconf
```

and BIND is responsible for determining whether the resulting configuration is valid.

This keeps BindConfigForge from attempting to duplicate BIND's grammar and validation rules.

---

# 18. Allow Transfer

BindConfigForge supports zone-level `allow-transfer`.

For example:

```yaml
allow_transfer:
  - 192.0.2.10
  - 192.0.2.11
```

generates:

```bind
allow-transfer {
    192.0.2.10;
    192.0.2.11;
};
```

`allow-transfer` may be defined directly on a zone or supplied through a mixin.

Where multiple mixins contribute compatible transfer addresses, the values should be accumulated.

Conflicting semantics should produce an error rather than being silently resolved.

---

# 19. Example Configuration

A complete configuration for the current environment might look like:

## `bindconfig.yaml`

```yaml
# yaml-language-server: $schema=https://www.1x0.us/schemas/base-config.json

defaults:
  type: primary
  zone_directory: /var/lib/bind
  key_directory: keys

keys:
  macbook-local:
    file: /etc/bind/keys/macbook-local.key

  traefik-acme:
    file: /etc/bind/keys/traefik-acme.key
```

## `mixins/standard-zone.yaml`

```yaml
# yaml-language-server: $schema=https://www.1x0.us/schemas/mixin.json

type: primary
inline_signing: true
dnssec_policy: standard-v2
```

## `mixins/update-zone-from-macbook-local.yaml`

```yaml
update_policy:
  - grant:
      key: macbook-local
      match: name
      name: "{zone}."
      types:
        - ANY

  - grant:
      key: macbook-local
      match: zonesub
      types:
        - ANY
```

## `mixins/update-from-traefik.yaml`

```yaml
update_policy:
  - grant:
      key: traefik-acme
      match: name
      name: "_acme-challenge.{zone}."
      types:
        - TXT
```

## `zones/isdavecanalesgoneyet.com.yaml`

```yaml
# yaml-language-server: $schema=https://www.1x0.us/schemas/zone.json

zone: isdavecanalesgoneyet.com

mixins:
  - standard-zone
  - update-zone-from-macbook-local
  - update-from-traefik
```

This generates approximately:

```bind
zone "isdavecanalesgoneyet.com" {
    type primary;

    file "/var/lib/bind/isdavecanalesgoneyet.com/domain.zone";
    key-directory "/var/lib/bind/isdavecanalesgoneyet.com/keys";

    inline-signing yes;
    dnssec-policy "standard-v2";

    update-policy {
        grant traefik-acme name _acme-challenge.isdavecanalesgoneyet.com. TXT;
        grant macbook-local name isdavecanalesgoneyet.com. ANY;
        grant macbook-local zonesub ANY;
    };
};
```

---

# 20. Generated Configuration

BindConfigForge generates BIND configuration containing the required key imports and zone declarations.

Conceptually:

```bind
// GENERATED FILE
// DO NOT EDIT

include "/etc/bind/keys/macbook-local.key";
include "/etc/bind/keys/traefik-acme.key";

zone "example.com" {
    ...
};

zone "isdavecanalesgoneyet.com" {
    ...
};
```

The generated configuration is an artifact.

The YAML source is the source of truth.

The generated configuration should have deterministic output.

Given the same source YAML, the same input configuration, and the same BindConfigForge version, the generated configuration should be reproducible.

Zones will be generated in alphabetical order.

---

# 21. Validation

Validation occurs at several levels.

## YAML validation

YAML files should be validated against the appropriate JSON Schema.

The schemas are maintained separately from this planning document.

The schemas are intended to provide IDE validation in applications such as:

* PhpStorm;
* WebStorm;
* GoLand;
* Visual Studio Code;
* other editors supporting YAML and JSON Schema.

The YAML files may identify their corresponding schemas using the standard YAML language-server schema mechanism.

## BindConfigForge validation

BindConfigForge validates things that require knowledge of the complete BindConfigForge configuration.

Examples include:

* duplicate zone names;
* missing required zone properties;
* invalid YAML structure;
* invalid mixin references;
* missing key references;
* conflicting mixin configuration;
* invalid configuration relationships;
* invalid or ambiguous path configuration.

## BIND validation

After BindConfigForge generates the complete BIND configuration, it should run:

```text
named-checkconf
```

against that generated configuration.

BindConfigForge should not attempt to reproduce BIND's complete configuration grammar.

For example, if a user writes:

```yaml
match: zonesup
```

BindConfigForge should not necessarily reject it simply because its own implementation does not recognize that BIND keyword.

The generated BIND configuration should be submitted to BIND's own validator.

The validation pipeline is:

```text
Read YAML
    ↓
Parse
    ↓
Validate YAML structure
    ↓
Load keys
    ↓
Load mixins
    ↓
Load zones
    ↓
Resolve mixins
    ↓
Apply defaults
    ↓
Validate BindConfigForge model
    ↓
Generate temporary BIND configuration
    ↓
Run named-checkconf
    ↓
Valid?
   / \
 yes  no
  │    │
  │    └── abort; preserve existing configuration
  │
  └── output generated configuration
```

---

# 22. Operational Safety

BindConfigForge should be designed around the assumption that DNS configuration errors are potentially serious.

The tool should therefore favor:

* explicit errors;
* deterministic output;
* validation before deployment;
* temporary output during validation;
* preservation of the last known-good configuration;
* clear error messages identifying the source YAML and mixin responsible for an error.

A failed generation or validation must never destroy a known-good generated configuration when BindConfigForge itself is performing a replacement operation in a future deployment workflow.

The MVP does not need to directly replace `/etc/bind/named.conf.local`; stdout is the default output mechanism.

---

# 23. Handler Architecture

Each supported BIND zone behavior should have its own handler or renderer.

The design-time decision is that configuration handlers should be isolated rather than implementing all BIND rendering in one large class. This will enable future renderers to be modularly implemented.

The initial handlers correspond approximately to:

```text
Zone/
├── Type
├── DnssecPolicy
├── InlineSigning
├── UpdatePolicy
└── AllowTransfer
```

Path handling is separate because paths are resolved into the internal zone model before rendering.

Key import handling is likewise separate from zone behavior.

The handler architecture should make adding a new supported BIND zone behavior a localized change.

For example, if BindConfigForge later supports:

```text
notify
also-notify
masters
check-names
```

each can be added without redesigning the existing configuration system.

Handlers should operate on the resolved BindConfigForge model rather than reading YAML directly.

The intended pipeline is:

```text
YAML
  ↓
Parser
  ↓
BindConfigForge configuration model
  ↓
Defaults
  ↓
Mixin resolution
  ↓
Validation
  ↓
Handlers
  ↓
BIND configuration
```

This keeps YAML parsing, configuration resolution, validation, and BIND rendering separate.

---

# 24. Relationship to ZoneForge

BindConfigForge and ZoneForge are separate tools with separate responsibilities.

## ZoneForge

ZoneForge manages:

* DNS zones;
* zone contents;
* zone templates;
* records;
* zone-specific DNS data;
* dynamic zone workflows.

## BindConfigForge

BindConfigForge manages:

* BIND zone declarations;
* common zone configuration;
* mixins;
* update policies;
* DNSSEC-related zone configuration;
* zone paths;
* key imports;
* generation of BIND configuration.

The tools may operate on the same zones, but neither needs to understand the internal implementation of the other.

For example:

```text
ZoneForge
    isdavecanalesgoneyet.com
        ↓
    domain.zone


BindConfigForge
    isdavecanalesgoneyet.com
        ↓
    zone "isdavecanalesgoneyet.com" { ... };
```

This separation should be preserved.

The operation and design of BindConfigForge does not require any dependencies or knowledge of ZoneForge

---

# 25. Directory Layout for Zone Data

The preferred default filesystem layout is a directory per zone.

For example:

```text
/var/lib/bind/
├── isdavecanalesgoneyet.com/
│   ├── domain.zone
│   └── keys/
│       ├── ...
│
├── 1x0.us/
│   ├── domain.zone
│   └── keys/
│       ├── ...
│
└── ...
```

This is a BindConfigForge default, not a restriction.

The tool should not require users to use per-zone directories.

A user may instead configure:

```yaml
zone_directory: /srv/bind/zones
zone_file: zone.db
```

or use an absolute path:

```yaml
zone_file: /srv/bind/zones/example.net.zone
```

The purpose of the defaults is to provide a sensible standard layout, not to prevent users from using an existing or automated filesystem arrangement.

A user or external automation that deliberately chooses a flat layout is responsible for ensuring that the necessary directories and files exist.

---

# 26. Comments and Documentation

Comments should live in the YAML source files and mixins.

Generated BIND configuration should not be considered the canonical location for human documentation.

For example:

```yaml
# This zone is served through GitLab Pages.
# It does not require Traefik ACME updates.
zone: isdavecanalesgoneyet.com

mixins:
  - standard-zone
  - update-zone-from-macbook-local
```

The comment remains part of the source configuration even if the generated BIND file is subsequently rewritten.

---

# 27. Version Control

The YAML source files should be suitable for storing in Git.

The generated BIND configuration may also be stored in Git if desired, but it is not the source of truth.

The preferred workflow is:

```text
Edit YAML
    ↓
Git diff
    ↓
Generate
    ↓
Validate
    ↓
Review generated diff
    ↓
Deploy
```

This provides both a human-readable source configuration and a concrete representation of what BIND will receive.

---

# 28. Future Possibilities

These are intentionally outside the MVP.

Potential future features include:

* additional BIND zone behaviors;
* additional BIND configuration sections;
* server-wide `options` configuration;
* ACL definitions;
* logging configuration;
* views;
* configuration inheritance beyond mixins;
* automatic `rndc reload`;
* deployment to remote BIND servers;
* integration with ZoneForge;
* configuration linting beyond BIND's own validation;
* generated documentation;
* visualization of which zones use which mixins;
* diff/preview commands;
* watch mode;
* CI validation.

None of these should be required for the initial implementation.

---

# 29. MVP Definition

BindConfigForge MVP is complete when it can:

1. Read a base YAML configuration.
2. Read a directory of YAML zone definitions.
3. Read a directory of named mixins.
4. Resolve multiple mixins for each zone.
5. Apply the configured defaults.
6. Resolve relative and absolute zone paths.
7. Generate the required BIND key imports.
8. Generate valid BIND `zone {}` declarations.
9. Detect duplicate zones.
10. Detect missing mixins.
11. Detect missing key references.
12. Detect invalid or conflicting BindConfigForge configuration.
13. Run `named-checkconf` against the complete generated configuration.
14. Refuse to produce successful output when BIND validation fails.
15. Produce deterministic output.
16. Report unused declared keys as informational diagnostics.
17. Support the existing BIND configuration currently maintained manually.
18. Provide the documented CLI flags:

    * `--source`
    * `--verbose`
    * `--quiet`
    * `--check`
    * `--help`
    * `--version`

The MVP does **not** need to support arbitrary BIND configuration outside the defined zone behaviors and key imports.

---

# 30. Guiding Principles

The primary design principle is:

> **Configure what is different; inherit what is common.**

Additional principles are:

> **Let BIND validate BIND.**

BindConfigForge should validate its own configuration model, but should not attempt to reproduce BIND's grammar.

> **Defaults should be useful, not restrictive.**

The default filesystem layout should make the common case simple while allowing users to override it without fighting the tool.

> **The YAML is the source of truth.**

The generated BIND configuration is an implementation artifact.

> **Mixins are partial zone configurations.**

A mixin should use the same configuration vocabulary as a zone wherever that configuration is composable.

> **Prefer explicit failure over silent configuration changes.**

Conflicts and ambiguous configuration should result in errors rather than BindConfigForge silently choosing a value.

> **Keep BIND-specific functionality modular.**

Each supported zone behavior should have its own handler so that new behaviors can be added without destabilizing the existing configuration system.


