Skip to content

monitor: publish signed+sealed state reports for monitoring - #9788

Draft
ThomasWaldmann wants to merge 7 commits into
borgbackup:masterfrom
ThomasWaldmann:monitoring
Draft

monitor: publish signed+sealed state reports for monitoring#9788
ThomasWaldmann wants to merge 7 commits into
borgbackup:masterfrom
ThomasWaldmann:monitoring

Conversation

@ThomasWaldmann

@ThomasWaldmann ThomasWaldmann commented Jun 17, 2026

Copy link
Copy Markdown
Member

After create/prune, the borg client now publishes a small state report into the repository's new monitoring/ namespace. The report is signed (Ed25519) and sealed (HPKE, RFC 9180) using key material derived from the existing borg key, so the untrusted repository server can neither forge nor read it - it can only relay it.

The new borg monitor command reads, verifies and decrypts the reports from from the repository using the monitoring key, applies a freshness window (--max-age) and exits non-zero if the report is missing, stale, unsigned or unsuccessful.

borg monitor --key (which needs the unlocked borg key) derives and prints BORG_MONITORING_KEY for the monitoring host; that value only allows verifying and decrypting the state reports, not creating them. It also does not give access to other data or metadata in the repository.

Unencrypted (--encryption none) repos have no key to derive the monitoring key from, so their reports are published as unsigned, unencrypted plaintext and these reports are flagged untrusted on read.

borg monitor --json outputs report(s) in json format.

borg monitor --keep=N keeps the latest N status reports (default: 500), older reports are deleted. --keep=0 disables this.

borg monitor reads all reports and prints, per archive series (and per maintenance command), the latest status and freshness; --name / --command restrict the output. The exit code is the worst across all units.

Requires OpenSSL >= 3.2 (for the built-in HPKE API).

@codecov

codecov Bot commented Jun 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.54545% with 26 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.12%. Comparing base (a50eb6f) to head (73ef255).
⚠️ Report is 8 commits behind head on master.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/borg/monitoring.py 86.13% 11 Missing and 3 partials ⚠️
src/borg/archiver/monitor_cmd.py 93.13% 5 Missing and 2 partials ⚠️
src/borg/archiver/delete_cmd.py 66.66% 0 Missing and 2 partials ⚠️
src/borg/archiver/transfer_cmd.py 80.00% 2 Missing ⚠️
src/borg/archiver/undelete_cmd.py 83.33% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #9788      +/-   ##
==========================================
- Coverage   85.70%   83.12%   -2.58%     
==========================================
  Files          95       98       +3     
  Lines       16815    17113     +298     
  Branches     2577     2617      +40     
==========================================
- Hits        14411    14225     -186     
- Misses       1668     2168     +500     
+ Partials      736      720      -16     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

Comment thread src/borg/archiver/monitor_cmd.py Dismissed
@ThomasWaldmann
ThomasWaldmann force-pushed the monitoring branch 2 times, most recently from 2b1f6a1 to 5d10236 Compare June 18, 2026 21:50
ThomasWaldmann and others added 4 commits July 29, 2026 11:04
After create/prune, the borg client now publishes a small state report into the
repository's new `monitoring/` namespace. The report is signed (Ed25519) and
sealed (HPKE, RFC 9180) using key material derived from the existing borg key,
so the untrusted repository server can neither forge nor read it - it can only
relay it.

The new `borg monitor` command reads, verifies and decrypts the reports from
from the repository using the monitoring key, applies a freshness window
(--max-age) and exits non-zero if the report is missing, stale, unsigned or
unsuccessful.

`borg monitor --key` (which needs the unlocked borg key) derives and prints
BORG_MONITORING_KEY for the monitoring host; that value only allows verifying
and decrypting the state reports, not creating them. It also does not give
access to other data or metadata in the repository.

Unencrypted (`--encryption none`) repos have no key to derive the monitoring
key from, so their reports are published as unsigned, unencrypted plaintext
and these reports are flagged untrusted on read.

Requires OpenSSL >= 3.2 (for the built-in HPKE API).

borg monitor --json outputs report(s) in json format.

borg monitor --keep=N keeps the latest N status reports (default: 500),
older reports are deleted. --keep=0 disables this.

borg monitor reads all reports and prints, per archive series (and per
maintenance command), the latest status and freshness; --name / --command
restrict the output. The exit code is the worst across all units.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The low_level Ed25519/HPKE length-validation guards and the monitoring key
derivation hardcoded the byte sizes (32) that the exported ED25519_*/X25519_*
size constants already document. Reference the constants instead, which also puts
the two so-far-unused ones (ED25519_SEED_SIZE, X25519_PUBLIC_SIZE) to use. Sizes
are unchanged; C array dimensions stay literal as Cython requires.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ThomasWaldmann and others added 3 commits July 29, 2026 11:33
Reports now include the hostname and username of the backup (for create, the
archive's own host/user; for repo-wide commands like prune, the local host/user).
borg monitor groups reports by (host, user, command, archive series) and reports
each distinct backup job independently, so several hosts backing up the same
archive series name to one repository no longer mask each other. New --host and
--user options restrict the output (alongside --name and --command); the JSON
entries expose hostname/username/command/archive.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
borg delete, borg undelete and borg transfer now publish a monitoring report as
the last action while the store is open (skipped on --dry-run), with host/user
defaulting to the local machine like prune. Stats record the number of archives
deleted / undeleted / transferred (plus considered/skipped and transferred
bytes). These appear as their own units in borg monitor (command=delete /
undelete / transfer).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.

2 participants