Skip to content

repo-create: always write config/defaults, so its removal is detected, refs #346 - #10441

Merged
ThomasWaldmann merged 1 commit into
borgbackup:masterfrom
ThomasWaldmann:repo-defaults-mandatory
Sep 27, 2026
Merged

ThomasWaldmann merged 1 commit into
borgbackup:masterfrom
ThomasWaldmann:repo-defaults-mandatory

Conversation

@ThomasWaldmann

Copy link
Copy Markdown
Member

Follow-up to #10438 (refs #346): make the repository defaults object mandatory, so that its removal is detected.

Problem

#10438 stores the repository defaults (compression, chunker params) in config/defaults, in the key's store object envelope, and claimed that nobody without the key can change them unnoticed. That holds for the content of the object, but not for its presence: a missing object meant "no defaults", so anybody with write access to the store could simply delete it, and the next borg create silently fell back to the built-in defaults - e.g. dropping an obfuscate compression. Reproduced: rm repo/config/defaults → borg create rc 0, archive stored with lz4 and the built-in chunker params, no warning.

Change

  • borg repo-create always writes config/defaults, also when no default was given (an empty dict).
  • A missing object is an error, like one that fails the authentication: Repository.DefaultsMissing (new rc 34, message with a hint). The commands that use the defaults (create, recreate, import-tar, transfer, repo-compress, debug put-obj, repo-info) refuse to run. Giving every default explicitly (--compression and --chunker-params) still works, as the object is not read then.
  • borg check verifies the object (present, authenticates, deserializes) and reports it; borg check --repair replaces a missing or corrupt object by empty defaults, so the repository can be used again (with the built-in defaults; the set defaults are lost - like the repository config, they can not be restored). The check lives in do_check (the command), not in Repository.check(): the latter is also used on API-created repositories without repo-create, which have no defaults object.
  • with_repository reads the defaults only after assert_secure() passed. (This also keeps the repository swap detection tests raising EncryptionMethodMismatch: the repository id is part of the envelope AAD, so after a swap the defaults object fails authentication first otherwise.)
  • The IntegrityError for a tampered object now carries the borg check --repair hint, too.

Impact on existing beta repositories

Repositories created before this change have no config/defaults: every command using the defaults fails with rc 34 until borg check --repair (or borg check --repair --repository-only, which still hashes all packs - server-side for ssh://) has stored empty defaults, or the repository is recreated. In line with the beta policy, there is no compatibility fallback: a fallback would be exactly the hole this PR closes.

Docs / tests

Docs: repo-create and check epilogs, data-structures.rst (config/ namespace, envelope paragraph, "Repository defaults"), the rc list in frontends.rst.

Tests: test_defaults_missing (create/repo-info refuse, explicit options still work), test_check_repository_defaults[missing|corrupt] (check reports, --repair restores, repository usable again). Full test suite passes locally (macOS, Python 3.11), except the known local test_migrate_lock_alive macFUSE failure.

Follow-ups (not in this PR)

  • There is still no command to set or change the defaults of an existing repository (only repo-create sets them, by decision).
  • check does not validate the values of the defaults (an invalid stored spec still fails at use time with rc 16).

🤖 Generated with Claude Code

@codecov

codecov Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.28571% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.68%. Comparing base (e893f13) to head (26ff43f).
⚠️ Report is 5 commits behind head on master.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/borg/archiver/check_cmd.py 90.00% 2 Missing ⚠️
src/borg/archiver/_common.py 50.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #10441      +/-   ##
==========================================
+ Coverage   88.66%   88.68%   +0.01%     
==========================================
  Files         103      103              
  Lines       19284    19305      +21     
  Branches     3004     3005       +1     
==========================================
+ Hits        17099    17120      +21     
  Misses       1515     1515              
  Partials      670      670              

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

…, refs borgbackup#346

Without the object, the commands silently fell back to the built-in defaults, so
anybody with write access to the store could remove a repository default (e.g. an
"obfuscate" compression) unnoticed: the key protects the content of the object,
not its presence.

Now "borg repo-create" always writes config/defaults (empty if no default was
given), and a missing object is an error (Repository.DefaultsMissing, rc 34) like
one that fails the authentication: the commands using the defaults refuse to run.
"borg check" reports such an object, "borg check --repair" replaces it by empty
defaults, so the repository can be used again (with the built-in defaults).

with_repository reads the defaults only after the security checks (assert_secure).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@ThomasWaldmann
ThomasWaldmann merged commit a68d943 into borgbackup:master Sep 27, 2026
26 checks passed
@ThomasWaldmann
ThomasWaldmann deleted the repo-defaults-mandatory branch September 27, 2026 15:43
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