Split-horizon views for octoDNS
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.
typeandttldefault to the record's own when the override leaves them out.- It takes the record's
octodnssettings, withoutviews, and can't set its own. A record that'sincludedorexcludedfor 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
includedinstead. - 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
viewsputs 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.
-
Bump the version and write the changelog from the pending entries:
./script/changelog bump --version 1.0.0-rc.1 --make-changes -
Open a pull request with the result, and merge it once CI passes.
-
From an up-to-date
master, run./script/release. It refuses a dirty tree or aHEADthat isn'torigin/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)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| octodns_views-0.1.0.tar.gz | 11.6 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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 logRelease 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 logSecurity 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.
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 logA 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.