Design Decisions¶
Architecture and integration decisions for octodns-metaname.
Decision Record Format¶
Each decision is documented with:
- Context - What problem are we solving?
- Options - What alternatives did we consider?
- Decision - What did we choose?
- Rationale - Why did we choose it?
- Consequences - What are the trade-offs?
Current Decisions¶
| ID | Title | Status |
|---|---|---|
| DD-001 | pyproject.toml vs setup.py | Accepted |
| DD-002 | ruff vs black+isort | Accepted |
| DD-003 | 100% test coverage requirement | Accepted |
| DD-004 | Changelog management approach | Under Review |
| DD-005 | Domain registration safety guardrail | Accepted |
| DD-006 | Domain registration in MetanameClient vs MetanameProvider | Accepted |
DD-001 - pyproject.toml vs setup.py¶
Context: OctoDNS template uses setup.py alongside pyproject.toml. Modern Python packaging supports pyproject.toml only.
Options:
- Use setup.py + pyproject.toml (OctoDNS pattern)
- Use pyproject.toml only (modern approach)
Decision: Use pyproject.toml only.
Rationale:
- Modern Python packaging standard (PEP 517/518)
- Simpler maintenance (one file vs two)
- Fully compatible with pip, build, and other tools
- No functional difference for end users
Consequences:
- Diverges from OctoDNS template pattern
- May need to update if OctoDNS adds setup.py-specific features
DD-002 - ruff vs black+isort¶
Context: OctoDNS template uses black for formatting and isort for import sorting. Ruff provides both in a single, faster tool.
Options:
- Use black + isort (OctoDNS pattern)
- Use ruff (modern approach)
Decision: Use ruff.
Rationale:
- 10-100x faster than black+isort
- Single tool for linting, formatting, and import sorting
- Compatible with black formatting style
- Active development and community
Consequences:
- Diverges from OctoDNS ecosystem convention
- May require reformatting if contributing upstream
DD-003 - 100% test coverage requirement¶
Context: Module bridges two upstream systems (OctoDNS and Metaname API). Higher reliability needed.
Options:
- Standard coverage (80-90%)
- 100% coverage (strict)
Decision: Require 100% test coverage.
Rationale:
- Two upstream dependencies increase risk
- DNS is critical infrastructure
- Catches edge cases and regressions
- Aligns with OctoDNS template pattern
Consequences:
- More test writing effort
- May need pragma: no cover for legitimate cases
- Slower development initially, faster maintenance long-term
DD-004 - Changelog management approach¶
Context: Current manual CHANGELOG.md vs changelet pattern from OctoDNS template.
Options:
- Manual CHANGELOG.md (current approach)
- Changelet with .changelog/ directory (OctoDNS pattern)
Decision: Under review - leaning toward changelet.
Rationale (for changelet):
- Individual entries per PR (easier to review)
- Automatic compilation at release time
- Consistent with OctoDNS ecosystem
- Better traceability (each change has its own file)
Consequences (if adopted):
- Need to add changelet dependency
- Need to update CONTRIBUTING.md
- Migration of existing CHANGELOG.md entries
- Training for contributors
DD-005 - Domain registration safety guardrail¶
Context: Domain registration costs real money and is irreversible. Adding register_domain() to MetanameClient brings this mutating operation into a library used by CI pipelines and AI agent workflows.
Options:
- Trust the caller — no guardrails, just document the danger
- Require explicit
confirm=True— raiseValueErrorotherwise - Use a separate class or client for registration (API surface segregation)
Decision: Require confirm=True.
Rationale:
- Domain registration is the most expensive and irreversible operation the module performs
- A
ValueErroronconfirm=Falseis a loud, discoverable failure at development time, not a silent mistake at runtime - The guardrail survives refactoring (e.g. if an agent wraps
register_domain()in a loop) because every call site must opt in - Consistent with the principle that mutating operations with financial cost should require explicit intent
Consequences:
- Every caller must pass
confirm=True— a minor ergonomic cost - The guardrail is enforced in code, not just documentation, reducing the risk of accidental CI/automation registrations
- The
register_domain()method also internally callscheck_domain()and refuses to proceed if the domain is not available
DD-006 - Domain registration in MetanameClient vs MetanameProvider¶
Context: Domain registration (register_domain_name RPC) and domain listing (domain_names RPC) are Metaname API operations that sit outside the OctoDNS provider contract. We needed to decide where these methods belong in the module's class hierarchy.
Options:
- Add to
MetanameProvider— the OctoDNS provider class - Add to
MetanameClient— the lower-level JSON-RPC client wrapper - Create a separate
MetanameRegistrarclass
Decision: Add domain lifecycle methods to MetanameClient.
Rationale:
MetanameProviderimplements the OctoDNSBaseProviderinterface (populate,apply,SUPPORTS). Domain registration is not a DNS zone operation — it's a Metaname-specific API call with no OctoDNS counterpart. Mixing it into the provider would blur the separation of concerns.MetanameClientis the thin JSON-RPC wrapper that already owns authentication, secret resolution, and raw API calls. Registration methods (check_domain_name,register_domain_name,domain_names) are just more RPC methods — they fit naturally alongsidedns_zone,create_dns_record, etc.- The standalone
scripts/metaname_register.pyscript had its own duplicated secret resolution and HTTP logic. TheMetanameClientalready has all that plumbing — putting registration there eliminates the duplication. - A separate
MetanameRegistrarclass would add a new concept for only 3 methods, creating unnecessary API surface fragmentation.
Consequences:
MetanameClientnow has two concerns (DNS zone management + domain lifecycle), but both are thin wrappers over Metaname JSON-RPC methods that share the same auth and networking layer- Consumers who only need DNS zone management via the provider are unaffected — provider-only usage doesn't instantiate
MetanameClientdirectly MetanameProviderstays focused on the OctoDNS contract, preserving the clean OctoDNS provider surface