Skip to main content

Split-horizon views for octoDNS

CI Licence: MIT

An octoDNS processor that gives a record a different answer per target, so one config can drive both the public and the internal side of a split-horizon zone.

octoDNS sends every target the same records. Without views, the inside answer for a name has to live in a second config with its own sources. This processor lets a record carry an override for a named view, and maps each view to the targets that serve it. An override can change the record's type, which covers the common case of a CNAME outside and an A record inside.

Installation

Command line

pip install octodns-views

requirements.txt/setup.py

Pinning specific versions or SHAs is recommended to avoid unplanned upgrades. The release workflow updates these pins after each stable release.

Versions
octodns==1.22.0
octodns-views==0.1.0
SHAs
-e git+https://git@github.com/octodns/octodns.git@94bc80eb03f662565940dd2d4af5c20ccd61915e#egg=octodns
-e git+https://git@github.com/DonQuinleone/octodns-views.git@0000000000000000000000000000000000000000#egg=octodns_views

Configuration

Map each view to the ids of the targets that serve it, and add the processor to the zones that use views:

processors:
  views:
    class: octodns_views.Views
    views:
      inside: [internal-dns]

zones:
  example.com.:
    sources: [config]
    processors: [views]
    targets: [public-dns, internal-dns]

A target can be in only one view. Targets outside every view get the records as written.

Give a record an override for a view under octodns.views:

app:
  type: CNAME
  value: edge.example.net.
  octodns:
    views:
      inside:
        type: A
        value: 192.0.2.5
www:
  type: A
  value: 198.51.100.1
  octodns:
    views:
      inside:
        value: 192.0.2.1

public-dns gets app as a CNAME and www as 198.51.100.1. internal-dns gets app as an A record for 192.0.2.5, and www as 192.0.2.1.

An override is a record of its own, written as any other record:

  • It replaces the record's values outright, rather than merging with them.
  • type and ttl default to the record's own when the override leaves them out.
  • It takes the record's octodns settings, without views, and can't set its own. A record that's included or excluded for a target stays that way whatever its override says.

Every target gets its records without the views data. That keeps a YAML target or octodns-dump free of the overrides for other views.

For a name that exists in only one view, use octoDNS's built-in included or excluded instead:

dashboard:
  type: A
  value: 192.0.2.7
  octodns:
    included: [internal-dns]

Validation

When octodns-sync or octodns-dump processes the source zone, the processor applies each view to a copy of the zone. It then validates the result the same way octoDNS validates any zone. The plan fails before any target is touched if:

  • an override names a view the processor config doesn't define;
  • an override isn't a valid record of its type, or sets octodns;
  • octodns.views, or an override in it, isn't a mapping;
  • an override leaves the zone invalid. Examples are a CNAME beside other records, or a type that another record at the name already has.

For each target in a view, the target's own support checks run again over the overrides. With strict_supports, an override of a type or feature the target doesn't support fails the plan. Without it, the override is dropped with a warning.

octodns-validate doesn't run processors, so it checks none of this.

Limits

  • Every target must support the record's own type, as well as any override type. octoDNS drops or rejects unsupported records before processors run, so a record of a type the view's target lacks never reaches its override. Give such a name one record per view with included instead.
  • For a target in a view, the provider's desired-zone hook runs a second time over the overrides. That's safe for octoDNS's own checks, and a provider that overrides the hook needs to tolerate it.
  • The processor can't see which targets exist. A misspelt target id in views puts that target in no view, and it gets the records as written.

Contributing

Contributions are welcome as pull requests against master. Report security vulnerabilities privately, as SECURITY.md describes, rather than in an issue.

Development

./script/bootstrap

This creates a virtual environment in env/, installs the pinned runtime and development requirements, and installs a pre-commit hook that runs the same checks as CI. The scripts follow the Scripts to Rule Them All pattern:

Script What it does
./script/test Runs the tests
./script/coverage Runs the tests with branch coverage; anything under 100% fails
./script/lint Checks the code with pyflakes
./script/format Formats the code with isort and black
./script/cibuild Runs everything CI runs

The tests run the processor against in-memory zones, and one test runs a full octodns-sync with YAML targets. Nothing reaches a real DNS provider.

Every pull request needs a changelog entry, which CI checks. Use none for changes that don't belong in the release notes:

./script/changelog create -t <none|patch|minor|major> "What changed, as a release note"

Releasing

Versions follow SemVer, with pre-releases written 1.0.0-beta.1 and 1.0.0-rc.1. The version lives in octodns_views/__init__.py and the tag is v plus that version. PyPI shows pre-releases in Python's spelling (1.0.0rc1), and pip installs them only when asked with --pre.

  1. Bump the version and write the changelog from the pending entries:

    ./script/changelog bump --version 1.0.0-rc.1 --make-changes
    
  2. Open a pull request with the result, and merge it once CI passes.

  3. From an up-to-date master, run ./script/release. It refuses a dirty tree or a HEAD that isn't origin/master, then signs and pushes the tag.

The tag starts the release workflow, which waits for approval on the pypi environment before publishing:

Step Pre-release Stable
Checks the tag is on master and matches the version, then lints, tests and builds ✅ ✅
Publishes to PyPI through trusted publishing ✅ ✅
Creates a GitHub release with the changelog section and built files ✅ ✅
Opens a pull request updating the pins under Installation ❌ ✅

Workflow-opened pull requests don't start CI on their own; close and reopen them to run the checks.

Metadata

Release files for octodns-views 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for octodns-views 0.1.0
File Size Uploaded Provenance
octodns_views-0.1.0.tar.gz 11.6 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for octodns-views 0.1.0
File Interpreter ABI Platform
octodns_views-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 19.0 kB

Release files / octodns_views-0.1.0.tar.gz

Download URL octodns_views-0.1.0.tar.gz
Size 11.6 kB
Tags Source
SHA-256 checksum
How to use checksums
117d8da5b686da0b434aa8c51dafd7a2a9ae320f2d17bb57f3c75df75009b830
BLAKE2b-256 checksum
How to use checksums
1cff9695d1bced6b6a2b1023a01821e5c1ba3af05ed2f6c11d566d73c3283c1a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.

Transparency log

Release files / octodns_views-0.1.0-py3-none-any.whl

Download URL octodns_views-0.1.0-py3-none-any.whl
Size 7.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
15526e7593d093150643c38fa81fbff0bb78759b09a6b59f07f81693a9c7e3e2
BLAKE2b-256 checksum
How to use checksums
31266f2570a07df0453ab7887945e889a206c12939c972e317db7f5c177c9f3d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.

Transparency log

Security for octodns-views 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for octodns_views-0.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page