Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Changelog Guidelines for Zebra

Zebra has two types of changelogs with different audiences:

ChangelogAudienceLocation
zebrad (binary)Node operatorsCHANGELOG.md (root)
Crates (libraries)Rust developers<crate>/CHANGELOG.md

This document covers guidelines common to both, then specific guidance for each.

CHANGELOG.md vs GitHub Release

These serve different purposes:

CHANGELOG.mdGitHub Release
PurposeHistorical record of changesRelease announcement
Question answered“What changed?”“Should I upgrade? How?”
ContentChange entries with PR linksChangelog plus operational context
Audience focusWhat is differentWhat to do about it

The GitHub release wraps CHANGELOG.md content with operational context:

GitHub Release
  - Update Priority (who should upgrade)
  - Highlights (TL;DR)
  - CHANGELOG.md content (Breaking Changes, Added, etc.)
  - How to Upgrade (per-platform steps)
  - Compatibility (OS, protocol, MSRV)

See Part 4: GitHub Release Format for templates.

Part 1: Common Guidelines

These apply to all Zebra changelogs regardless of audience.

One entry per distinct change

Each distinct user-visible change gets one bullet entry (one - item, which may wrap across lines). A PR that makes several independent changes gets one entry for each. Do not split a single change across multiple entries, and do not group multiple PRs in one entry.

Good:

- New `Bounded` vec type for compile-time size constraints ([#10056](link))
- Prometheus metrics for RocksDB I/O latency and compaction ([#10181](link))

Bad, one change split across entries:

- Added mempool standardness checks ([#10224](link))
- Added `max_datacarrier_bytes` config option ([#10224](link))

Bad, multiple PRs on one line:

- Prometheus metrics for monitoring ([#10175](link), [#10179](link), [#10181](link))

Section order

Use Keep a Changelog section order:

## [Version X.Y.Z](link) - YYYY-MM-DD

[2-4 sentence summary]

### Breaking Changes
### Added
### Changed
### Deprecated
### Removed
### Fixed
### Security

Only include sections that have entries.

Section priority

When a single change touches multiple categories, place it in one section only based on the most impactful aspect. Priority order:

  1. Breaking Changes
  2. Security
  3. Removed
  4. Changed
  5. Deprecated
  6. Added
  7. Fixed

Exception for crate changelogs: a change that is both breaking and additive (for example, adding a variant to a public enum that is not #[non_exhaustive]) is listed under Breaking Changes for its impact and under Added for the new capability, so consumers see both signals.

Fixed vs Changed: use Fixed only when the fix is invisible, meaning the bug is gone but output looks the same. If users see anything different (error messages, logs, behavior), use Changed.

Tone and language

Write plainly and factually. Avoid:

  • Hyperbolic adjectives: “comprehensive”, “significant”, “major”, “awesome”, “powerful”
  • Vague intensifiers: “greatly improved”, “much better”, “highly optimized”
  • Marketing language: “exciting new feature”, “game-changing”, “cutting-edge”
  • Unnecessary hedging: “helps to”, “aims to”, “tries to”

Frame by experience, not code

Describe what users experience, not what the code does.

Code-focused (bad)Experience-focused (good)
“Added mempool standardness checks”“Mempool now rejects non-standard transactions”
“Implemented new error type”“Error messages now show specific failure reason”
“Refactored config parsing”“Config files now support X format”
“Changed function signature”parse_block() now returns Result<Block, ParseError>

Part 2: zebrad Changelog (Node Operators)

File: CHANGELOG.md (repository root)

Audience: people who run zebrad: deploy nodes, write config files, use RPC endpoints, monitor via metrics, run Docker containers. May not be Rust developers.

What is breaking for node operators?

A change is breaking if upgrading zebrad could cause existing setups to fail:

CategoryExampleWhy it breaks
Config field removedRemoving [rpc].some_fieldExisting config files fail to parse
Config field renamedcache_dir to state_dirExisting config files fail to parse
Config field type changedport: "8233" to port: 8233Existing config files may fail
Environment variable renamedZEBRA_RPC_PORT to ZEBRA_RPC__LISTEN_PORTScripts and systemd units stop working
CLI argument removed/renamed--config to --config-fileScripts fail
RPC response format changedtimestamp: "2025-01-01" to timestamp: 1735689600RPC consumers break
RPC endpoint removedRemoving getinfoRPC consumers break
Database format (no downgrade)New state versionCannot revert to previous version
Default behavior changeMempool rejects previously accepted txMining/wallet software may break

What is NOT breaking for node operators

CategoryExampleWhy it is safe
New optional config fieldAdding max_datacarrier_bytes with defaultExisting configs work
New RPC endpointAdding getmempoolinfoExisting consumers unaffected
New RPC response fieldAdding pingtime to getpeerinfoConsumers ignore new fields
Library API changespub const to pub(crate) constOperators do not use Rust APIs
Internal refactorsChanging error types internallyNo external interface change
Performance improvementsAvoiding clones, faster verificationSame behavior, just faster
New metricsAdding Prometheus metricsOpt-in observability

What NOT to include in the zebrad changelog

TypeWhy exclude
Library API changesPut in crate CHANGELOG
Internal refactorsNo operator-visible effect
CI/workflow changesNo runtime impact
Test changesNo runtime impact
Dependency bumpsUnless security fixes

Red flags an entry does not belong:

  • “No changes required to existing deployments”
  • Describing implementation without operator impact
  • Entry only makes sense to someone reading code

Release summary (zebrad)

  • 2-4 sentences maximum
  • Lead with the most impactful change
  • Name config options, RPC methods, environment variables
  • Focus on what operators need to know or do

Good:

Mempool now enforces zcashd-compatible standardness checks; transactions with
non-standard scripts are rejected. Configure OP_RETURN limits with
`max_datacarrier_bytes` in `[mempool]`. This release also adds Prometheus
metrics for value pools, sync progress, and RPC latencies.

Bad:

This release refactors internal error handling, updates dependencies, and
improves code organization across multiple crates.

Entry examples (zebrad)

Good:

- Mempool now rejects transactions with non-standard transparent scripts.
  Configure OP_RETURN limits with `max_datacarrier_bytes` in `[mempool]`
  (default: 83 bytes) ([#10224](link))

Bad:

- Added mempool standardness checks (#10224)

Breaking change format:

### Breaking Changes

- Environment variables now use `ZEBRA_SECTION__KEY` format (double underscore).
  Update scripts: `ZEBRA_RPC_PORT` to `ZEBRA_RPC__LISTEN_ADDR` ([#9768](link))

Network upgrade releases

For releases activating a network upgrade, use a dedicated section:

## [Zebra X.Y.Z](link) - YYYY-MM-DD

**This release activates [NU Name] on Zcash mainnet.**

| | |
|-|-|
| Activation Height | X,XXX,XXX |
| Expected Date | ~YYYY-MM-DD |
| Specifications | [ZIP 2XX](https://zips.z.cash/zip-02xx), [ZIP 3XX](https://zips.z.cash/zip-03xx) |

Upgrade before block X,XXX,XXX to remain on the Zcash network.

### Network Upgrade

- Implements [ZIP 2XX: Title](https://zips.z.cash/zip-02xx) ([#XXXX](link))
- Implements [ZIP 3XX: Title](https://zips.z.cash/zip-03xx) ([#YYYY](link))

### Added
...

Always include:

  • Activation height
  • Approximate activation date
  • Links to all ZIPs being deployed
  • Clear upgrade deadline

ZIP references

Always link to ZIPs when implementing consensus or protocol changes:

- Implements [ZIP 317: Proportional Transfer Fee Mechanism](https://zips.z.cash/zip-0317) ([#9876](link))

Use the canonical URL format: https://zips.z.cash/zip-XXXX

Deprecations

For features being phased out, document the timeline:

### Deprecated

- `getinfo` RPC is deprecated and will be removed in v5.0.0. Use `getblockchaininfo`,
  `getnetworkinfo`, and `getwalletinfo` instead ([#10XXX](link))

If following a staged deprecation (like zcashd):

  1. Stage 1: enabled by default, can disable with flag
  2. Stage 2: disabled by default, can enable with flag
  3. Stage 3: removed entirely

Security releases

For releases addressing security issues:

## [Zebra X.Y.Z](link) - YYYY-MM-DD - Security Release

**This is a security release. Immediate upgrade is recommended.**

This release addresses security issues reported through our disclosure process.
Details will be published after sufficient upgrade adoption.

| Severity | Affected Versions |
|----------|-------------------|
| High | < X.Y.Z |

### Security

- Fixed denial-of-service vulnerability in peer message handling ([#XXXX](link))

Minimize technical details in the public changelog. Link to the security advisory after the disclosure period.

Part 3: Crate Changelogs (Library Consumers)

Files: zebra-chain/CHANGELOG.md, zebra-consensus/CHANGELOG.md, and so on.

Audience: Rust developers building on Zebra crates (Zaino, Zallet, other wallets and tools). These are technical users who read Rust code.

What is breaking for library consumers?

A change is breaking if code using the crate will fail to compile or behave differently:

CategoryExampleMigration required
Public type removedRemoving pub struct BlockHashFind replacement or copy type
Public type renamedBlockHash to BlockHeaderHashUpdate all usages
Function signature changedfn parse(data: &[u8]) to fn parse(data: Vec<u8>)Update call sites
Return type changedfn get() -> Hash to fn get() -> Option<Hash>Handle new return type
Trait bounds addedfn process<T>() to fn process<T: Clone>()Ensure types implement trait
Public field removedRemoving block.headerUse accessor method
Visibility reducedpub const X to pub(crate) const XCopy constant or request re-export
Error type changedParseError to BlockParseErrorUpdate error handling
Feature flag requiredFunction now behind #[cfg(feature = "x")]Enable feature in Cargo.toml
MSRV increased1.70 to 1.75Update toolchain

Adding a variant to a public enum that is not marked #[non_exhaustive] is also breaking: downstream match expressions that were exhaustive stop compiling.

What is NOT breaking for library consumers

CategoryExampleWhy it is safe
New public typeAdding pub struct NewTypeExisting code unaffected
New public functionAdding pub fn new_helper()Existing code unaffected
New trait implimpl Display for BlockExisting code unaffected
New optional parameterAdding Option<Config> with defaultExisting calls work
Performance improvementsInternal optimizationSame API, faster
Documentation changesBetter rustdocNo code changes needed

Release summary (crates)

  • 2-4 sentences maximum
  • Lead with breaking changes if any
  • Name specific types, traits, functions affected
  • Focus on what code changes are needed

Good:

`FundingStreamReceiver` variants renamed for clarity: `Ecc` to `Bootstrap`,
`ZcashFoundation` to `Foundation`. The `subsidy` module constants are now
`pub(crate)`; use the provided accessor functions instead.

Bad:

This release improves code organization and internal structure.

Entry examples (crates)

Good, breaking change:

### Breaking Changes

- `subsidy::MAX_BLOCK_SUBSIDY` is now `pub(crate)`. Use `Block::max_subsidy()`
  instead ([#10185](link))

Good, API addition:

### Added

- `impl From<BlockHeader> for BlockHeaderHash` for convenient conversion ([#10056](link))

Good, type change:

### Changed

- `AdjustedDifficulty::new()` now takes `Bounded<CompactDifficulty, 128>` instead
  of `Vec<CompactDifficulty>` for compile-time size validation ([#10056](link))

Crate releases vs zebrad releases

Crate releases and zebrad releases are independent. A single PR may appear in both changelogs, documented differently for each audience.

Example: PR #10224 adds mempool standardness checks.

In zebra-chain/CHANGELOG.md (when zebra-chain 1.x.0 releases):

### Added
- `StandardScript` type for validating transparent script standardness ([#10224](link))

In CHANGELOG.md (when zebrad 4.1.0 releases):

### Changed
- Mempool now rejects transactions with non-standard transparent scripts.
  Configure OP_RETURN limits with `max_datacarrier_bytes` in `[mempool]` ([#10224](link))

Same PR, different perspectives:

  • Crate changelog: what API changed (for developers updating their code)
  • zebrad changelog: what behavior changed (for operators updating their setup)

Deciding where to document

Change typeCrate CHANGELOGzebrad CHANGELOG
Config option added/changed/removedNoYes
RPC endpoint added/changed/removedNoYes
CLI argument changedNoYes
Public Rust API changedYesOnly if operator impact
Public type/trait changedYesOnly if operator impact
Internal refactorNoNo
Performance improvementIf API-relevantIf user-noticeable
Bug fixIf API-relevantIf operator-noticeable

A change can appear in both if it affects both audiences. Document it appropriately for each:

  • Crate: focus on types, functions, migration code
  • zebrad: focus on config, behavior, what operators see

If unsure, ask “Who needs to take action?”:

  • Developers changing Rust code: crate CHANGELOG
  • Operators changing config/scripts: zebrad CHANGELOG
  • Both: both changelogs, written for each audience

Part 4: GitHub Release Format

GitHub releases wrap CHANGELOG.md content with operational context. Do not just copy CHANGELOG.md: add the information operators need to decide whether and how to upgrade.

Update priority

Add an Update Priority section for releases with varying urgency:

## Update Priority

| User Type | Priority | Reason |
|-----------|----------|--------|
| Mining pools | **High** | Mempool policy changes affect block templates |
| Exchanges | Medium | New RPC fields available |
| General operators | Medium | Performance improvements |

Priority levels:

  • Critical: security fix, upgrade immediately
  • High: breaking changes or consensus updates
  • Medium: new features or notable fixes
  • Low: minor improvements, upgrade at convenience

How to upgrade

Include platform-specific upgrade instructions:

## How to Upgrade

### Binary/Package

1. Stop zebrad: `systemctl stop zebrad` or `Ctrl+C`
2. Wait for clean shutdown (check logs for "shutdown complete")
3. Replace binary or update via package manager
4. Start zebrad: `systemctl start zebrad`

### Docker

```bash
docker pull zfnd/zebra:X.Y.Z
# or update your compose file and:
docker compose pull && docker compose up -d
```

Building from source:

git fetch && git checkout vX.Y.Z
cargo build --release

Compatibility

Document what this release works with:

## Compatibility

| Component | Supported |
|-----------|-----------|
| Operating Systems | Linux (glibc 2.31+), macOS 13+ |
| Zcash Protocol | All upgrades through NU6.1 |
| zcashd RPC Compatibility | 5.x compatible |
| Minimum Rust (source builds) | 1.75+ |

Release templates

Standard release:

## Zebra X.Y.Z

[2-4 sentence summary from CHANGELOG]

## Update Priority

| User Type | Priority | Reason |
|-----------|----------|--------|
| ... | ... | ... |

---

[CHANGELOG.md content: Breaking Changes, Added, Changed, Fixed sections]

---

## How to Upgrade

[Platform-specific instructions]

## Compatibility

[Compatibility table]

## Contributors

[From CHANGELOG]

Network upgrade release:

## Zebra X.Y.Z - [NU Name] Activation

**This release activates [NU Name] on Zcash mainnet at block X,XXX,XXX (~YYYY-MM-DD).**

⚠️ **All node operators must upgrade before the activation height.**

## Update Priority

| User Type | Priority |
|-----------|----------|
| All operators | **Critical** |

## Network Upgrade Details

| | |
|-|-|
| Activation Height | X,XXX,XXX |
| Expected Date | ~YYYY-MM-DD |
| Testnet Activation | Block Y,YYY,YYY (active since vA.B.C) |

### Implemented ZIPs

- [ZIP XXX: Title](https://zips.z.cash/zip-0xxx)
- [ZIP YYY: Title](https://zips.z.cash/zip-0yyy)

---

[CHANGELOG.md content]

---

## How to Upgrade

[Platform-specific instructions]

## Compatibility

[Compatibility table]

Security release:

## Zebra X.Y.Z - Security Release

⚠️ **This is a security release. Immediate upgrade is recommended for all users.**

## Update Priority

| User Type | Priority |
|-----------|----------|
| All operators | **Critical** |

## Security

This release addresses [N] security issue(s) reported through our security
disclosure process.

| Issue | Severity | Affected Versions |
|-------|----------|-------------------|
| [Brief description] | High/Medium/Low | < X.Y.Z |

Full details will be published in a security advisory after sufficient upgrade
adoption.

---

[Other CHANGELOG.md content if any]

---

## How to Upgrade

[Platform-specific instructions, emphasize urgency]

What NOT to include in a GitHub release

  • Raw git log or commit lists
  • Internal implementation details
  • Entries that only matter to contributors
  • Duplicate content (link to docs instead of repeating)