Skip to content

chore(release): prepare v0.22.0 - #302

Merged
lance0 merged 1 commit into
mainfrom
release/v0.22.0
Oct 22, 2026
Merged

lance0 merged 1 commit into
mainfrom
release/v0.22.0

Conversation

@lance0

@lance0 lance0 commented Oct 22, 2026

Copy link
Copy Markdown
Owner

Prepares the 0.22.0 release. just release-prepare 0.22.0 set the Cargo version, refreshed Cargo.lock (only the prefixd entry changed) and rolled [Unreleased] into ## [0.22.0], dated 2026-10-22 (UTC). The manual items follow:

  • The README Status line names v0.22.0 and links the new upgrade section.

  • docs/how-to/upgrading.md gains a v0.21.0 → 0.22.0 section. The upgrade has no migration and no adoption step, the configuration is unchanged, and rustbgpd stays at 0.79.0. The section covers:

    • re-copying grafana/prometheus-alerts.yml, for the new PrefixdBlackholeReplaced on the new prefixd_blackhole_replaced_total{pop} counter and the reworded export_policy text of PrefixdBlackholeNotExported;
    • the new historical field, and degraded health detail after the controller regains its fence;
    • the Compose and bare-metal steps, including recreating Prometheus to load the rules;
    • the checks and downgrading to 0.21.0.

    Its check heading is "Check the 0.22.0 upgrade", so the existing #check-the-upgrade links still reach the 0.21.0 section.

  • In the CHANGELOG, the section gains a summary paragraph, Upgrade notes and Known limitations. The upgrade notes link the guide section, pinned to blob/v0.22.0 as earlier releases were. The first Fixed entry now names the historical field and the degraded health effect.

  • /openapi.json takes info.version from CARGO_PKG_VERSION. The upgrade proof saw it report 0.22.0, as did /v1/health/detail, prefixd --version and prefixdctl --version. The CLI reference example moved to 0.22.0, and nothing else current pinned 0.21.0.

The section was reviewed against git log v0.21.0..HEAD --oneline (22 commits):

Gate

just gate ran on the release candidate from 00:14:59 to 00:28:36 UTC 2026-10-22 and exited 0. It ran pinned to 16 CPUs:

taskset -c 24-31,56-63 env CARGO_BUILD_JOBS=16 PREFIXD_TEST_CPUSET=24-31,56-63 VITEST_MAX_WORKERS=8 just gate

VITEST_MAX_WORKERS=8 follows the shared-host advice in CONTRIBUTING. The run covered:

  • fmt, ruff and the proto pin;
  • the alert rules: promtool found 5 rules, and the unit tests passed;
  • both Clippy and test feature sets: 1796 Rust tests passed, 0 failed;
  • MSRV and the release build;
  • the frontend: 41 files and 462 tests, plus the build;
  • cargo audit, clean apart from the documented RUSTSEC-2023-0071 exception;
  • bun audit, with no vulnerabilities;
  • cargo-deny: advisories, bans, licenses and sources all ok;
  • just speakers.

The edits after that run touch only the CHANGELOG and docs. git diff --check passes, as does an affected-link check covering relative links, anchors and the blob/v0.22.0 links.

Fixture pins: rustbgpd 0.79.0@sha256:b2054d0ce98f3c8299b8de5be1920cd88a1ab2c85e02766927e487be0c60f8ff (source 60cb5c99), the only entry in ci.yml's matrix. The rustbgpd speaker suites passed (12 and 4 tests), as did GoBGP (9 and 8).

Upgrade proof (v0.21.0 → this branch)

Setup. The database was real 0.21.0 data. The released ghcr.io/lance0/prefixd:0.21.0 (sha256:e37a0224…3a017) ran with Postgres 16 and rustbgpd 0.79.0@sha256:b2054d0c…, using the v0.21.0 checkout's Compose rustbgpd profile. It was isolated by project name, with unique container names, pinned CPUs and no host ports.

The shipped config was kept except for two changes: auth.mode: bearer, so the CLI could read health, and longer TTLs (limits and playbook steps), so rules outlived the window. All rows were in enforced mode:

  • manual police on 203.0.113.10 udp/53;
  • manual tcp discard on 203.0.113.20, ports 80 and 443;
  • a FastNetMon ban creating an icmp discard on .50, and another creating a syn police on .51;
  • a manual rule on .30, withdrawn.

Upgrade. The new guide section was then followed as written:

  1. Back up with pg_dump.
  2. Check out the new tree, keeping the operator's prefixd.yaml and playbooks.
  3. Replace only the controller with up -d --no-deps --no-build prefixd-rustbgpd, with PREFIXD_IMAGE set to a local image. rustbgpd was not touched.

The local image is the repository Dockerfile's runtime stage, built from the locked release binaries of the gate run above (cargo build --locked --release). This replaced a full multi-stage image build on the shared host. Prometheus and the dashboard were not part of the proof; just alerts covers the rules file.

Outcome:

  • Migrations apply: startup logged versions [1, …, 18]. There is no new migration, and prefixdctl migrations lists 18.
  • Adoption runs once: no adoption step is needed for this pair, and none ran. controller fence acquired was logged once, and the upgrade wrote no audit entries.
  • Authenticated health is healthy: prefixdctl status showed healthy, daemon 0.79.0, database connected and peer observation connected. Health detail reported blackhole_exports.historical: false. /v1/ready returned ready, and prefixd_controller_fence_active was 1.
  • First reconciliation has no repairs: four successful passes, with no repairing local announcement, reconciliation failed, WARN or ERROR lines. prefixd_unknown_local_routes{kind="flowspec"} stayed at 0.
  • The same mitigation IDs stay active: the same 4 IDs are active. Every mitigation row (status, action, victim, origin_mode, match, action parameters and expiry) is identical to the 0.21.0 baseline. The withdrawn row stayed withdrawn, with 0 pending withdrawals.
  • The speaker RIB is unchanged: the normalized ipv4 FlowSpec RIB (components, actions and extended communities) matched the 0.21.0 baseline exactly.

Everything started for the proof was removed: containers, volumes, network and the local image.

The CHANGELOG date is 2026-10-22. Re-date it if the merge happens on a later UTC day.

Known limitations

These are stated in the CHANGELOG's Known limitations:

  • TTL expiry needs the running controller. Unleased FlowSpec rules and blackholes stay announced if prefixd stops while the speaker stays up. The leased modes are opt-in and rustbgpd-only.
  • PrefixdBlackholeReplaced latches: it keeps firing until prefixd restarts or the alert is silenced. It records a past replacement, not an active conflict.
  • The 0.21.0 known limitations otherwise still apply.

Release notes preview

scripts/release-notes.py 0.22.0 exits 0. Its output follows.

Release body

prefixd 0.22.0 lets operators choose the dashboard's display timezone, counts and alerts on unleased blackhole admissions that replaced an untracked local route, and marks blackhole export evidence retained from before a lost controller fence as historical so health, metrics and the interface no longer present it as current. It adds no migration, keeps rustbgpd 0.79.0 and loads an unchanged 0.21.0 configuration.

Upgrade notes

  • Follow v0.21.0 → 0.22.0 in the upgrade guide: replace the controller and dashboard. No migration or adoption step runs, the configuration is unchanged and rustbgpd stays at 0.79.0.
  • Copy the new grafana/prometheus-alerts.yml to your Prometheus, or recreate the Compose Prometheus. It adds PrefixdBlackholeReplaced on the new prefixd_blackhole_replaced_total{pop} counter and rewords the export_policy text of PrefixdBlackholeNotExported.
  • With rustbgpd, mitigation export_evidence and health detail's speaker.blackhole_exports add historical. After the controller loses and regains its fence, GET /v1/health/detail reports degraded until a fresh complete read replaces each retained blackhole's evidence. Automation that treats degraded as a database or speaker fault should read speaker.blackhole_exports.historical. /v1/ready is unchanged.

Known limitations

  • TTL expiry needs the running controller. Unleased FlowSpec rules and blackholes stay announced if prefixd stops while the speaker stays up; the leased modes, which bound that to one lease, are opt-in and rustbgpd-only.
  • PrefixdBlackholeReplaced latches: the counter is local to the process, so the alert keeps firing until prefixd restarts or the alert is silenced. It records a past replacement, not an active conflict.
  • The 0.21.0 known limitations otherwise still apply.

Added

  • Choose a dashboard display timezone in Preferences. The browser-local choice updates absolute times, charts and tooltips; UTC remains the default, and API timestamps, exports, TTLs and notification quiet hours stay unchanged.
  • Count unleased blackhole admissions that replace an untracked local route with prefixd_blackhole_replaced_total{pop} and alert with PrefixdBlackholeReplaced. The alert latches historical replacement evidence until the process counter resets; admission and leased ownership behavior are unchanged.

Changed

  • Check the Prometheus alert rules and run their promtool unit tests with pinned Prometheus v3.15.0 in just alerts, just gate and a CI job. Nightly and main-push Interop runs no longer cancel each other; a newer PR commit still cancels its superseded Interop run.
  • Run the FRR blackhole scenario with rustbgpd and amplification scenarios with both speakers in Interop CI alongside FlowSpec, retaining separate receipts for each speaker, scenario and workflow attempt, including failed runs.

Fixed

  • Retained blackhole export evidence is explicitly historical after controller ownership is lost, including after ownership returns until a fresh complete read. Mitigation export_evidence and speaker.blackhole_exports report it with historical: true, and health detail is degraded while it remains after ownership returns. Detail preserves historical peer rows and their timestamp on a failed refresh; health and the interface no longer present them as current export. An observation that spans an ownership loss is discarded.
  • Historical blackhole export evidence no longer feeds prefixd_blackhole_export_peers or prefixd_blackhole_unexported, so a failed refresh after ownership returns cannot report the previous owner's committed peers or keep PrefixdBlackholeNotExported firing; it counts as a read error until a fresh complete read.
  • Rotate blackhole export reads between reconciliation passes so the shared observation budget does not repeatedly leave the same mitigations unread. Each pass resumes at the first read the budget cut off, so every blackhole is read within as many passes as it takes the budget to cover the set.
  • Blackhole export-policy warnings now direct operators to the speaker's export policy without guessing that RPKI validation caused the denial.

@lance0
lance0 marked this pull request as ready for review October 22, 2026 00:50
@lance0
lance0 merged commit 243e8ad into main Oct 22, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant