- Add an epilog to the main ArgumentParser in src/borg/archiver/__init__.py.
- Import process_epilog and use it to format and list additional help topics: patterns, match-archives, placeholders, compression.
- Add test_main_help_epilog to help_cmd_test.py.
- Fixes#3432
- Add --from-borg1 option to borg repo-list command.
- Add allow_v1 argument to @with_repository decorator.
- If allow_v1 is True and v1_legacy is requested, allow version 1 repositories and load Manifest with RepoObj1 object class.
- Support LegacyRemoteRepository in Manifest class.
- Add test_repo_list_from_borg1.
info.size is the on-disk pack file size, which equals the chunk size only
when N=1 (one chunk per pack). Extract it into a named variable with a
comment so the assumption is visible and easy to fix when N>1 is introduced.
Corruption at offset 123 lands inside meta_encrypted (header is 49 bytes),
causing extract_crypted_data to return a shifted slice whose first byte is
a random AES-OCB ciphertext byte. When that byte equals 0x02 (PlaintextKey
type) key detection silently selects the wrong key, leading to a flaky
IntegrityError in rebuild_archives.
Move the insertion point to offset 250, which is safely inside data_encrypted
for any realistic manifest size, so key detection always reads the correct
type byte and the corruption is caught by AEAD authentication instead.
_common.py had a hard-coded version check that only allowed v3.
Now that repository.py creates v4 repos, every archiver command
failed to open the repo. Extend the guard to (3, 4).
The --other-repo check (v1 or v3 for borg transfer source) is
intentionally left unchanged.
Wrap each pack file in a 13-byte header (magic + version + blob_len) so
packs are self-identifying and the [len][blob] unit extends to N>1 without
a format revision. Bump version 3->4: packs/ and 49-byte ObjHeader are
incompatible with version-3 readers. Fix test_extra_chunks chunk_id mismatch.
Introduces pack_id as the borgstore storage key (N=1: pack_id == chunk_id).
Chunks move from data/ to packs/ with single-level directory sharding (256
subdirs). check_object() validates the header chunk_id against the pack
filename. Adds packs/ to ns_config with levels=[1] and to the permissions
maps for no-delete and write-only modes.
Stores chunk_id unencrypted in the per-blob header so borg check can
rebuild the chunk_id -> pack location index without decryption. AEAD
uses chunk_id as additional data, making key-free recovery circular
without an explicit plaintext copy.
Header layout: OBJ_MAGIC(8) + version(1) + chunk_id(32) + meta_size(4)
+ data_size(4) = REPOOBJ_HEADER_SIZE = 49 bytes.
- always have a starting line with FILE_ID repoid
- store repkeys content-addressed, name is sha256(content)
- search by repo id on load
- add keyfile_format / keyfile_parse / is_keyfile helpers
When borg auto-selects the key file path (no BORG_KEY_FILE override),
the keyfile is now named sha256(file_contents).hexdigest() instead of
a location-derived name with optional .2/.3/... collision suffixes.
- On key change-passphrase, the old keyfile is securely erased and the
new file is written under its new hash-based name.
- borg key import likewise uses the hash-based name.
- Existing legacy-named keyfiles continue to work: _find_key_in_keys_dir
scans by file content, not filename.
- BORG_KEY_FILE still honors the explicit path verbatim.
Removes test_repo_create_keyfile_same_path_creates_new_keys which
tested the now-removed .N collision-suffix behavior.