Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
91 commits
Select commit Hold shift + click to select a range
ee2a012
Move persistence related fields into the existing persistence struct.
ximon18 Jun 8, 2026
6e91977
Merge branch 'main' into prune-diffs-take-three
ximon18 Jun 10, 2026
ae8293c
Move IXFR in-memory diff management into new IxfrZoneDiffs.
ximon18 Jun 11, 2026
959639e
Update RustDoc comment to match recent code changes.
ximon18 Jun 11, 2026
0c92a60
Replace tuple with struct with named fields.
ximon18 Jun 11, 2026
5a678fd
Improve comments and check if fetched diff matches expectations.
ximon18 Jun 11, 2026
26909f6
Clippy.
ximon18 Jun 11, 2026
1725063
Merge branch 'main' into prune-diffs-take-three
ximon18 Jun 11, 2026
f757530
Remove commented out code.
ximon18 Jun 11, 2026
a2fbe68
More cleanup and consistency between loaded and signed restoration.
ximon18 Jun 16, 2026
a59d661
Clippy.
ximon18 Jun 16, 2026
a50a3c1
Cargo fmt.
ximon18 Jun 16, 2026
e8a67b7
Use dedicated types.
ximon18 Jun 16, 2026
1b1dc02
Add purging of diffs.
ximon18 Jun 18, 2026
3d6e187
FIX: Revert temporary local change that breaks the test.
ximon18 Jun 19, 2026
b830659
Consistency.
ximon18 Jun 19, 2026
4292fef
Removed unused field.
ximon18 Jun 19, 2026
cf59337
More remove unused field.
ximon18 Jun 19, 2026
d4bfab7
Ensure that compaction blocks changes elsewhere concurrently to the p…
ximon18 Jun 19, 2026
b719a04
Fix a comment.
ximon18 Jun 19, 2026
11181ca
FIX: Revert persistence of loaded zone when loaded zone is discarded …
ximon18 Jun 19, 2026
38d5848
Don't leave nulls in the state file.
ximon18 Jun 19, 2026
e95037d
Remove outdated comment.
ximon18 Jun 19, 2026
0b26f4c
cargo fmt.
ximon18 Jun 19, 2026
3afd8a0
Add missing state transition to zone storage diagram,
ximon18 Jun 19, 2026
2ae2010
Cargo fmt.
ximon18 Jun 19, 2026
aba4ab1
Oops, the loaded zone wasn't persisted yet.
ximon18 Jun 19, 2026
3235690
Add a test of restore following signed reject.
ximon18 Jun 19, 2026
70b714a
FIX: Set last published SOA fields in storage on restore.
ximon18 Jun 19, 2026
9363857
On signed rejection, discard the loaded in-memory diff that was added.
ximon18 Jun 19, 2026
69c0a2c
Clippy.
ximon18 Jun 19, 2026
8a770b8
Cargo fmt.
ximon18 Jun 19, 2026
80ee201
Add max-diffs-size policy setting.
ximon18 Jun 25, 2026
0ec472a
FIX: Set new policy in zone after reload.
ximon18 Jun 25, 2026
61c4074
Clippy.
ximon18 Jun 25, 2026
acc2102
Cargo fmt.
ximon18 Jun 25, 2026
bc2ebf8
Merge branch 'main' into persist-zone-fixes-and-improvements
ximon18 Jun 25, 2026
cd96247
Merge branch 'persist-zone-fixes-and-improvements' into prune-diffs-t…
ximon18 Jun 25, 2026
adcefbc
Consistency: refactor into `on_zone_policy_changed()` style.
ximon18 Jun 25, 2026
cb134f0
Update purging policy setting descriptions.
ximon18 Jun 25, 2026
00e293d
Update persistence developer notes to include purging.
ximon18 Jun 25, 2026
ee09f62
Tweaks to IxfrZoneDiffs RustDocs.
ximon18 Jun 25, 2026
a657b3b
Remove excessive trace logging.
ximon18 Jun 26, 2026
355ddab
Refactor free fn as associated fn.
ximon18 Jun 26, 2026
396ae49
FIX: Restore last published SOAs from existing persisted state.
ximon18 Jun 26, 2026
730552b
Does this change make subcmd mentions into links?
ximon18 Jun 26, 2026
28dc62a
Revert "Does this change make subcmd mentions into links?"
ximon18 Jun 26, 2026
3e2f98d
RST syntax fix.
ximon18 Jun 26, 2026
bfbe480
Mention provide-xfr-to policy setting in zone transfer core topic.
ximon18 Jun 26, 2026
c8a5bc4
Say something in the docs about removal of excess diffs.
ximon18 Jun 26, 2026
1281755
Say something about special statuses that zone status can report.
ximon18 Jun 26, 2026
e5e56ec
Remove errant text.
ximon18 Jun 26, 2026
a55ba83
More zone transfer page updates.
ximon18 Jun 26, 2026
0f43c94
Consistency.
ximon18 Jun 26, 2026
28bc2f4
Shorter better heading.
ximon18 Jun 26, 2026
95c6fc7
Unit test a tiny piece of logic.
ximon18 Jun 26, 2026
2735dbe
Better log message.
ximon18 Jun 26, 2026
c0112e5
Better log message.
ximon18 Jun 26, 2026
a3918a1
Manual application of changes made in the base branch persist-zone-fi…
ximon18 Jul 13, 2026
a6863d4
Merge branch 'main' into prune-diffs-take-two
ximon18 Jul 13, 2026
7c2fa16
Make cargo doc happy.
ximon18 Jul 13, 2026
24f0e56
Review feedback: Remove accidentally committed file.
ximon18 Jul 13, 2026
0b81ed9
Review feedback: Typo correction.
ximon18 Jul 13, 2026
30e3b70
Be more specific about diff size.
ximon18 Jul 13, 2026
83c93ee
Clarify how diff limits are combined.
ximon18 Jul 13, 2026
d1db390
Review feedback: Use correct config file setting name syntax.
ximon18 Jul 13, 2026
e931338
Review feedback: Typo correction.
ximon18 Jul 13, 2026
ec73680
Review feedback: No need for abs_diff(), and return early.
ximon18 Jul 13, 2026
d9e27b4
Review feedback: Use let else style.
ximon18 Jul 13, 2026
e345918
Rewrap comments after use of early return caused changes to indenting.
ximon18 Jul 13, 2026
1822cf0
Review feedback: use checked_add().
ximon18 Jul 13, 2026
b6e834e
Review feedback: Remove into_iter().
ximon18 Jul 13, 2026
ce5cf14
Review feedback: Less cryptic `cascade policy show` output.
ximon18 Jul 14, 2026
02c1078
Review feedback: Hopefully improved description of max-diffs vs max-d…
ximon18 Jul 14, 2026
33b9e49
cargo fmt.
ximon18 Jul 14, 2026
893ce51
Review feedback: include policy TOML example, and reword max-diffs de…
ximon18 Jul 14, 2026
7827aa3
Merge branch 'main' into prune-diffs-take-two
ximon18 Jul 14, 2026
b9fc37f
Review feedback: Move code used by both persist and restore to the mo…
ximon18 Jul 15, 2026
b2e723d
FIX: Actually update next_idx.
ximon18 Jul 15, 2026
f23a31c
FIX: Ensure after storing diffs that we are not exceeding limits.
ximon18 Jul 15, 2026
7c6c823
Additional trace logging.
ximon18 Jul 15, 2026
1ed1077
Added some TODOs.
ximon18 Jul 15, 2026
bb6b7fa
Review feedback: Clearer separation of next_idx and restore_base_idx.
ximon18 Jul 15, 2026
d1bc2ad
Note some of the known limitations of the current implementation to b…
ximon18 Jul 15, 2026
e30a010
More TODO notes.
ximon18 Jul 15, 2026
552f698
Review feedback: Rename from_parts() to for_existing_diffs().
ximon18 Jul 15, 2026
b56510f
Review feedback: Clarify that not all diffs are applied if compaction…
ximon18 Jul 15, 2026
f9005f6
Review feedback: Complete the renaming of restore_base_idx and add mo…
ximon18 Jul 15, 2026
521a7e6
Make cargo doc happy.
ximon18 Jul 15, 2026
c401f24
Review feedback: Typo correction.
ximon18 Jul 15, 2026
148d1f2
Typo fix: exisitng -> existing.
ximon18 Jul 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions crates/api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -961,6 +961,8 @@ pub struct ServerPolicyInfo {
pub struct OutboundPolicyInfo {
pub provide_xfr_to: Vec<NameserverCommsPolicyInfo>,
pub send_notify_to: Vec<NameserverCommsPolicyInfo>,
pub max_diffs: usize,
pub max_diffs_size: usize,
}

#[derive(Deserialize, Serialize, Debug, Clone)]
Expand Down
5 changes: 5 additions & 0 deletions crates/cli/src/commands/policy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,13 +286,18 @@ fn print_server_policy(
cascade_api::OutboundPolicyInfo {
provide_xfr_to,
send_notify_to,
max_diffs,
max_diffs_size,
},
}: &ServerPolicyInfo,
) {
println!(" server:");
println!(" outbound:");
print_nameserver_comms_policy("provide XFR to", provide_xfr_to);
print_nameserver_comms_policy("send NOTIFY to", send_notify_to);
println!(
" max diffs: {max_diffs} totaling less than {max_diffs_size}% of the published record count"
);
}

fn print_review(ReviewPolicyInfo { mode, on_reject }: &ReviewPolicyInfo) {
Expand Down
6 changes: 3 additions & 3 deletions crates/zonedata/src/storage/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ mod transitions;
/// ║ │ │ stop_review ║
/// ║ │ ┌─────────────────────┐ stop_review ┌────────────────────┐ give_up ║
/// load ║ │ give_up │ CleanLoadedPending <──────────────────────────│ CleanWholePending <─────────────────────────────────────────────────────┐ ║
/// ║ │ └────▲───────────────┘ └────────────────────┘ │ ║
/// ▼ │ │ give_up │ ║
/// ╔═════════╧═╗ finish ╔══════════════════════════╗ start ╔═══════════════════╗ approve ╔════════════════════╗ complete ╔══▼═════════════╗ finish ╔═══════════════════════╗ start ╔══════════════════╗
/// ║ │ └────▲───────────────┘ └────────────────────┘ │ ║
/// ▼ │ │ give_up └───────────────────────────────────────────────┐ give_up │ ║
/// ╔═════════╧═╗ finish ╔══════════════════════════╗ start ╔═══════════════════╗ approve ╔════════════════════╗ complete ╔═══│════════════╗ finish ╔═══════════════════════╗ start ╔══════════════════╗
/// ║ Loading ╠════════▶ ReviewingLoadedPending ╠═══════▶ ReviewingLoaded ╠═════════▶ PersistingLoaded ╠══════════▶ Signing ╠════════▶ ReviewSignedPending ╠═══════▶ ReviewingSigned ║
/// ╚═══════════╝ ╚══════════════════════════╝ ╚═══════════════════╝ ╚════════════════════╝ ╚══╤═════════▲═══╝ ╚═══════════════════════╝ ╚════════╤═════════╝
/// retry │ │ complete retry │
Expand Down
3 changes: 3 additions & 0 deletions doc/manual/build/man/cascade-zone.1
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ Override a previous rejection of a zone review.
.TP
.B status
Get the status of a single zone.
.sp
Also reports any issues that occured with recent operations on the zone
and whether the zone is being restored from disk.
.UNINDENT
.INDENT 0.0
.TP
Expand Down
46 changes: 46 additions & 0 deletions doc/manual/build/man/cascaded-policy.toml.5
Original file line number Diff line number Diff line change
Expand Up @@ -739,6 +739,52 @@ Each nameserver must be specified as a string in the form:
.sp
\fI\(dq<IP>[^<TSIG_KEY_NAME>]\(dq\fP
.UNINDENT
.INDENT 0.0
.TP
.B max\-diffs = 5
The maximum number of \(dqsequences of differential information\(dq (diffs) that
the server may store per zone in order to respond to RFC 1995 Incremental
Zone Transfer (IXFR) requests.
.sp
A diff is created when changes to the input zone are received and when
re\-signing alters the signed records generated when signing last occurred.
.sp
Diffs are persisted to disk once approved and will be restored on next
start of the Cascade daemon.
.sp
Excess in\-memory diffs are discarded as soon as possible after approval of
a new diff, or on policy reload if the diff maximums were reduced compared
to previous version of the policy.
.sp
Persisted diffs are discarded periodically to reduce the impact of
persisted diff compaction. The number of persisted diffs may therefore
temporarily exceed the limit specified here until purging of persisted
diffs next occurs. Discarding of persisted diffs will be skipped for
zones that are in maintenance mode.
.sp
Note that while RFC 1955 section 5 suggests that diffs be purged based on
total IXFR response size and/or if older than the SOA expire period, Cascade
does not currently implement this behaviour. Cascade also currently has no
support for RFC 1995 section 6 condensation of multiple versions.
.sp
The default is to limits diffs to 5 per zone. If set to 0, storage of diffs
will be disabled.
.UNINDENT
.INDENT 0.0
.TP
.B max\-diffs\-size = 20
The maximum size allowed for in\-memory diffs for a single zone, defined as
a parcentage of the size of the last published version of the zone. If
storing a new diff would exceed the limit, diffs will be discarded to make
space starting with the oldest diffs first.
.sp
This setting has no impact on the number of diffs persisted to disk. See
max\-diffs to control that.
.sp
The default is 20%. If set to 0, or a value low enough such that the set of
diffs for a zone always exceed the limit, then no diffs will be stored in
memory and IXFR requests will be responded to with an AXFR instead.
.UNINDENT
.SH FILES
.INDENT 0.0
.TP
Expand Down
3 changes: 3 additions & 0 deletions doc/manual/source/man/cascade-zone.rst
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ Commands

Get the status of a single zone.

Also reports any issues that occured with recent operations on the zone
and whether the zone is being restored from disk.

.. subcmd:: reset

Reset the pipeline for a zone to get it out of a halted state.
Expand Down
51 changes: 51 additions & 0 deletions doc/manual/source/man/cascaded-policy.toml.rst
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,57 @@ The ``[server.outbound]`` section.

`"<IP>[^<TSIG_KEY_NAME>]"`

.. option:: max-diffs = 5

The maximum number of "sequences of differential information" (diffs) that
the server may store per zone in order to respond to RFC 1995 Incremental
Zone Transfer (IXFR) requests.

A diff is created when changes to the input zone are received and when
re-signing alters the signed records generated when signing last occurred.

Diffs are persisted to disk once approved and will be restored on next
start of the Cascade daemon.

Excess in-memory diffs are discarded as soon as possible after approval of
a new diff, or on policy reload if the diff maximums were reduced compared
to previous version of the policy.

Persisted diffs are discarded periodically to reduce the impact of
persisted diff compaction. The number of persisted diffs may therefore
temporarily exceed the limit specified here until purging of persisted
diffs next occurs. Discarding of persisted diffs will be skipped for
zones that are in maintenance mode.

Note that while RFC 1955 section 5 suggests that diffs be purged based on
total IXFR response size and/or if older than the SOA expire period, Cascade
does not currently implement this behaviour. Cascade also currently has no
support for RFC 1995 section 6 condensation of multiple versions.

The default is to limits diffs to 5 per zone. If set to 0, storage of diffs
will be disabled.

.. option:: max-diffs-size = 20

The maximum size allowed for in-memory diffs for a single zone, defined
as a percentage of the size of the last published version of the zone,
with zone size measured as the number of records in the zone and diff size
measured as the total number of records added by all diffs for the zone
plus the total number of records removed by all diffs for the zone.

If storing a new diff would exceed the limit, diffs will be discarded to
make space starting with the oldest diffs first.

This setting has no impact on the number of diffs persisted to disk. See
max-diffs to control that.

The default is 20%. If set to 0, or a value low enough such that the set of
diffs for a zone always exceed the limit, then no diffs will be stored in
memory and IXFR requests will be responded to with an AXFR instead.
Comment thread
ximon18 marked this conversation as resolved.

.. note:: The `max-diffs` limit is applied first, then additional diffs
will be discarded as needed to meet the `max-diffs-size` limit.

Files
-----

Expand Down
65 changes: 57 additions & 8 deletions doc/manual/source/zone-transfers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ based on the zone's SOA timers.
signed zone review hook could be used to AXFR the signed zone to
a file on disk to achieve this.

Using zone transfers with an upstream nameserver
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Requesting zone transfers from upstream nameservers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

To instruct Cascade to transfer a zone via the network instead of loading
it from a file you must supply an upstream nameserver IP address when
Expand All @@ -42,25 +42,73 @@ Cascade can be instructed to authenticate the upstream nameserver by use of a
TSIG key. The TSIG key to use must be provided to Cascade _before_ adding the
zone. See :program:`cascade` :subcmd:`tsig add`.

Providing zone transfers to a downstream server
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Providing zone transfers to downstream nameservers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

By default, Cascade allows downstream servers to access published zones by
zone transfer, no configuration is needed.

To restrict the downstream nameservers which may request transfer of the
zone use the ``server.outbound.provide-xfr-to`` policy setting. As with
upstream transfers, TSIG can also be used to authentication communication with
downstream nameservers.

To ensure timely update by secondaries, Cascade can be configured to send
:RFC:`1996` NOTIFY messages to specified secondaries. This is done via the
policy setting ``server.outbound.send-notify-to``, optionally specifying an
:RFC:8945` TSIG key to use to authenticate communication.
policy setting ``server.outbound.send-notify-to``, optionally specifying a
TSIG key to use to authenticate communication.

.. tip:: Remember to reload the policy file after changing it. See
:program:`cascade` :subcmd:`policy reload`.

.. tip:: Use :program:`cascade` :subcmd:`tsig add` to add a TSIG key to
Cascade _before_ reloading policy file changes.

Controlling automatic key rollover zone transfer settings
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Incremental zone transfers
~~~~~~~~~~~~~~~~~~~~~~~~~~

Cascade will automatically attempt to use IXFR with upstream nameservers,
and accumulates :RFC:`1995` "sequences of differential information" (diffs)
in memory in order to use them to respond to IXFR requests from downstream
nameservers.

.. tip:: Incremental diffs are also available from the Cascade review
nameservers, but only the difference between the current and previous
version of the zone. IXFR capable :doc:`review-hooks` could use this
to avoid requesting and processing the entire zone using AXFR and
instead review only the changes in the zone.

Diffs are persisted to disk so that if Cascade is restarted that it is still
able to respond to IXFR requests from downstream nameservers rather than
forcing them to costly fallback to AXFR transfers.

Cascade has two policy settings which limit the amount of memory and disk
space used per zone to store diffs:

.. code-block:: toml

[server.outbound]
max-diffs = 5
max-diffs-size = 20

``max_diffs`` specifies the maximum numer of IXFR diffs per zone that Cascade
may keep in memory and on disk.
Comment thread
ximon18 marked this conversation as resolved.

``max-diffs-size`` specifies the maximum number of records that all diffs for
a zone combined that may be stored in memory per zone as a percentage of the
number of records in the currently published version of the zone.

Note that diffs on-disk are (a) lazily removed, and so may persist longer than
expected, and (b) on-disk diffs may be needed to restore the published zone
on restart of Cascade and will then be removed once the persisted zone record
data has been compacted at which point it is safe for Cascade to delete them.

.. caution:: Do not manually remove on-disk diff files as doing so may prevent
Cascade restoring the last published version of the zone if the
daemon process is restarted.

Zone transfers and automated key rollover
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

When using automatic key rollover (the default) Cascade will attempt to verify
that certain key properties of the signed zone being served to consumers are
Expand All @@ -75,3 +123,4 @@ nameserver, or if a specific port number or TSIG key should be used
to request the transfer, you will also need to configure the Cascade
key manager to fetch the zone correctly. This can be done via the
``key-manager.publication-nameservers`` policy setting.

45 changes: 45 additions & 0 deletions etc/policy.template.toml
Original file line number Diff line number Diff line change
Expand Up @@ -478,3 +478,48 @@ mode = "off"
#
# If not specified, zone transfers will be provided to any nameserver.
#provide-xfr-to = ["127.0.0.1", "127.0.0.1^my-tsig-key"]

# The maximum number of "sequences of differential information" (diffs) that
# the server may store per zone **in-memory** in order to respond to RFC 1995
# Incremental Zone Transfer (IXFR) requests.
#
# A diff is created when changes to the input zone are received and when
# re-signing alters the signed records generated when signing last occurred.
#
# Diffs are persisted to disk once approved and will be restored on next start
# of the Cascade daemon.
#
# Excess **in-memory** diffs are discarded as soon as possible after approval
# of a new diff, or on policy reload if the diff maximums were reduced
# compared to previous version of the policy.
#
# Diffs persisted to disk will also be deleted if above the specified
# max-diffs limit, but not immediately, and if not needed to restore the
# zone on Cascade restart. Deletion of diffs on disk (so-called 'compaction')
# requires re-writing the entire zone to disk which for large zones could take
# time and for rapidly changing zones is thus not desirable to do on every
# change to the zone, and therefore is done periodically rather than on every
# change. Diffs that exceed the max-diffs limit but which are still required
# to restore the zone (because compaction has not occurred yet) will remain
# on disk until it is safe to delete them.
#
# Compaction will be skipped for zones that are in maintenance mode.
#
# Note that while RFC 1955 section 5 suggests that diffs be purged based on
# total IXFR response size and/or if older than the SOA expire period, Cascade
# does not currently implement this behaviour. Cascade also currently has no
# support for RFC 1995 section 6 condensation of multiple versions.
#max-diffs = 5

# The maximum size allowed for **in-memory** diffs for a single zone, defined
# as a percentage of the size (by record count) of the last published version
# of the zone. If storing a new diff would exceed the limit, **in-memory**
# diffs will be discarded to make space starting with the oldest diffs first.
#
# This setting has no impact on the number of diffs persisted to disk. See
# max-diffs to control that.
#
# The default is 20%. If set to 0, or a value low enough such that the set
# of diffs for a zone always exceed the limit, then no diffs will be stored
# **in-memory** and IXFR requests will be responded to with an AXFR instead.
#max-diffs-size = 20
Loading
Loading