Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions docs/faq.rst
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,18 @@ Also, you must not run borg against multiple instances of the same repo

See also: :ref:`faq_corrupt_repo`

Prepare for borg2 "Related repositories" and borg transfer
----------------------------------------------------------

A related repository is a repository that shares the same deduplication
secrets (``id_key`` and ``chunk_seed``) as another repository, but uses
its own independent encryption keys.

This will allow archives to be transferred between related repositories (e.g.
using ``borg transfer`` in Borg 2.0) without breaking deduplication.

For more information and detailed instructions, see :ref:`borg_key_export-related-secrets`.

"this is either an attack or unsafe" warning
--------------------------------------------

Expand Down
15 changes: 9 additions & 6 deletions docs/man/borg-init.1
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "borg-init" "1" "2026-03-18" "" "borg backup tool"
.TH "borg-init" "1" "2026-05-15" "" "borg backup tool"
.SH Name
borg-init \- Initialize an empty repository
.SH SYNOPSIS
Expand Down Expand Up @@ -276,11 +276,11 @@ BLAKE2b\-256 hash from the other BLAKE2b modes. This mode is only
compatible with Borg 1.1 and later.
.sp
\fBnone\fP mode uses no encryption and no authentication. It uses SHA256
as chunk ID hash. This mode is not recommended. You should instead
consider using an authenticated or authenticated/encrypted mode. This
mode has possible denial\-of\-service issues when running \fBborg create\fP
on contents controlled by an attacker. See above for alternatives.
This mode is compatible with all Borg versions.
as chunk ID hash. This mode is not recommended
as it is vulnerable to DoS attacks by an attacker (for example,
crafting content that causes hash index collisions). Do not use it if
untrusted clients use the repository. See \fIinternals_hashindex\fP for
details. This mode is compatible with all Borg versions.
.SH OPTIONS
.sp
See \fIborg\-common(1)\fP for common options of Borg commands.
Expand All @@ -304,6 +304,9 @@ Set storage quota of the new repository (e.g. 5G, 1.5T). Default: no quota.
.TP
.B \-\-make\-parent\-dirs
create the parent directories of the repository directory, if they are missing.
.TP
.BI \-\-import\-related\-secrets \ PATH
import related secrets from PATH
.UNINDENT
.SH EXAMPLES
.INDENT 0.0
Expand Down
109 changes: 109 additions & 0 deletions docs/man/borg-key-export-related-secrets.1
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
.\" Man page generated from reStructuredText
.\" by the Docutils 0.22.4 manpage writer.
.
.
.nr rst2man-indent-level 0
.
.de1 rstReportMargin
\\$1 \\n[an-margin]
level \\n[rst2man-indent-level]
level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
-
\\n[rst2man-indent0]
\\n[rst2man-indent1]
\\n[rst2man-indent2]
..
.de1 INDENT
.\" .rstReportMargin pre:
. RS \\$1
. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
. nr rst2man-indent-level +1
.\" .rstReportMargin post:
..
.de UNINDENT
. RE
.\" indent \\n[an-margin]
.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
.nr rst2man-indent-level -1
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "borg-key-export-related-secrets" "1" "2026-05-15" "" "borg backup tool"
.SH Name
borg-key-export-related-secrets \- Export secrets for creating related repositories
.SH SYNOPSIS
.sp
borg [common options] key export\-related\-secrets [options] [REPOSITORY] [PATH]
.SH DESCRIPTION
.sp
This command exports the deduplication secrets (\fBid_key\fP and \fBchunk_seed\fP)
of a repository. These secrets can be used to initialize a \fBrelated repository\fP\&.
.sp
Related repositories share the same deduplication metadata but have their own
independent encryption keys. This is useful for:
.INDENT 0.0
.IP 1. 3
Creating independent backup targets that still benefit from being
\(dqcompatible\(dq for future archive transfers.
.IP 2. 3
Preparing for a migration to Borg 2.0, where archives can be transferred
between related repositories using \fBborg transfer\fP\&.
.UNINDENT
.sp
The exported secrets are stored in a JSON file. This file contains sensitive
information and should be deleted immediately after usage.
.sp
Examples:
.INDENT 0.0
.INDENT 3.5
.sp
.EX
# Export secrets from an existing repository
$ borg key export\-related\-secrets /path/to/repo1 secrets.json

# Initialize a new related repository using these secrets
$ borg init \-\-import\-related\-secrets=secrets.json \-\-encryption=repokey /path/to/repo2
$ rm secrets.json
.EE
.UNINDENT
.UNINDENT
.sp
\fBImportant:\fP
.INDENT 0.0
.INDENT 3.5
When initializing a related repository using \fBborg init \-\-import\-related\-secrets\fP,
the new repository must use the same ID hash algorithm (either both HMAC\-SHA256
or both BLAKE2) as the original repository.
.INDENT 0.0
.IP \(bu 2
HMAC\-SHA256: \fBrepokey\fP, \fBkeyfile\fP, \fBauthenticated\fP
.IP \(bu 2
BLAKE2: \fBrepokey\-blake2\fP, \fBkeyfile\-blake2\fP, \fBauthenticated\-blake2\fP
.UNINDENT
.UNINDENT
.UNINDENT
.sp
\fBWarning:\fP
.INDENT 0.0
.INDENT 3.5
Please note that future Borg 2.0 versions might remove support for BLAKE2
in new repositories (see #8867).
.UNINDENT
.UNINDENT
.SH OPTIONS
.sp
See \fIborg\-common(1)\fP for common options of Borg commands.
.SS arguments
.sp
REPOSITORY
.INDENT 0.0
.TP
.B PATH
where to store the secrets
.UNINDENT
.SH SEE ALSO
.sp
\fIborg\-common(1)\fP
.SH Author
The Borg Collective
.\" End of generated man page.
13 changes: 8 additions & 5 deletions docs/usage/init.rst.inc
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ borg init
+-------------------------------------------------------+------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| | ``--make-parent-dirs`` | create the parent directories of the repository directory, if they are missing. |
+-------------------------------------------------------+------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| | ``--import-related-secrets PATH`` | import related secrets from PATH |
+-------------------------------------------------------+------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| .. class:: borg-common-opt-ref |
| |
| :ref:`common_options` |
Expand All @@ -51,6 +53,7 @@ borg init
--append-only create an append-only mode repository. Note that this only affects the low level structure of the repository, and running `delete` or `prune` will still be allowed. See :ref:`append_only_mode` in Additional Notes for more details.
--storage-quota QUOTA Set storage quota of the new repository (e.g. 5G, 1.5T). Default: no quota.
--make-parent-dirs create the parent directories of the repository directory, if they are missing.
--import-related-secrets PATH import related secrets from PATH


:ref:`common_options`
Expand Down Expand Up @@ -265,8 +268,8 @@ BLAKE2b-256 hash from the other BLAKE2b modes. This mode is only
compatible with Borg 1.1 and later.

``none`` mode uses no encryption and no authentication. It uses SHA256
as chunk ID hash. This mode is not recommended. You should instead
consider using an authenticated or authenticated/encrypted mode. This
mode has possible denial-of-service issues when running ``borg create``
on contents controlled by an attacker. See above for alternatives.
This mode is compatible with all Borg versions.
as chunk ID hash. This mode is not recommended
as it is vulnerable to DoS attacks by an attacker (for example,
crafting content that causes hash index collisions). Do not use it if
untrusted clients use the repository. See :ref:`internals_hashindex` for
details. This mode is compatible with all Borg versions.
4 changes: 4 additions & 0 deletions docs/usage/key.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,7 @@ Fully automated using environment variables:
.. include:: key_export.rst.inc

.. include:: key_import.rst.inc

This command can be used to create a related repository:

.. include:: key_export-related-secrets.rst.inc
1 change: 1 addition & 0 deletions docs/usage/key_export-related-secrets.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.. include:: key_export-related-secrets.rst.inc
82 changes: 82 additions & 0 deletions docs/usage/key_export-related-secrets.rst.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
.. IMPORTANT: this file is auto-generated from borg's built-in help, do not edit!

.. _borg_key_export-related-secrets:

borg key export-related-secrets
-------------------------------
.. code-block:: none

borg [common options] key export-related-secrets [options] [REPOSITORY] [PATH]

.. only:: html

.. class:: borg-options-table

+-------------------------------------------------------+----------------+----------------------------+
| **positional arguments** |
+-------------------------------------------------------+----------------+----------------------------+
| | ``REPOSITORY`` | |
+-------------------------------------------------------+----------------+----------------------------+
| | ``PATH`` | where to store the secrets |
+-------------------------------------------------------+----------------+----------------------------+
| .. class:: borg-common-opt-ref |
| |
| :ref:`common_options` |
+-------------------------------------------------------+----------------+----------------------------+

.. raw:: html

<script type='text/javascript'>
$(document).ready(function () {
$('.borg-options-table colgroup').remove();
})
</script>

.. only:: latex

REPOSITORY

PATH
where to store the secrets


:ref:`common_options`
|

Description
~~~~~~~~~~~

This command exports the deduplication secrets (``id_key`` and ``chunk_seed``)
of a repository. These secrets can be used to initialize a **related repository**.

Related repositories share the same deduplication metadata but have their own
independent encryption keys. This is useful for:

1. Creating independent backup targets that still benefit from being
"compatible" for future archive transfers.
2. Preparing for a migration to Borg 2.0, where archives can be transferred
between related repositories using ``borg transfer``.

The exported secrets are stored in a JSON file. This file contains sensitive
information and should be deleted immediately after usage.

Examples::

# Export secrets from an existing repository
$ borg key export-related-secrets /path/to/repo1 secrets.json

# Initialize a new related repository using these secrets
$ borg init --import-related-secrets=secrets.json --encryption=repokey /path/to/repo2
$ rm secrets.json

.. IMPORTANT::
When initializing a related repository using ``borg init --import-related-secrets``,
the new repository must use the same ID hash algorithm (either both HMAC-SHA256
or both BLAKE2) as the original repository.

- HMAC-SHA256: ``repokey``, ``keyfile``, ``authenticated``
- BLAKE2: ``repokey-blake2``, ``keyfile-blake2``, ``authenticated-blake2``

.. WARNING::
Please note that future Borg 2.0 versions might remove support for BLAKE2
in new repositories (see :issue:`8867`).
78 changes: 77 additions & 1 deletion src/borg/archiver.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,8 +333,21 @@ def do_init(self, args, repository):
"""Initialize an empty repository"""
path = args.location.canonical_path()
logger.info('Initializing repository at "%s"' % path)
related_secrets = None
if args.import_related_secrets:
with dash_open(args.import_related_secrets, 'r') as fd:
try:
related_secrets = json.load(fd)
except ValueError:
raise CommandError(f"Invalid JSON in related secrets file: {args.import_related_secrets}")
if related_secrets.get('version') != 1:
raise CommandError(f"Unsupported related secrets version: {related_secrets.get('version')}")
try:
related_secrets['id_key'] = hex_to_bin(related_secrets['id_key'])
except (KeyError, ValueError):
raise CommandError(f"Invalid id_key in related secrets file: {args.import_related_secrets}")
try:
key = key_creator(repository, args)
key = key_creator(repository, args, related_secrets=related_secrets)
except (EOFError, KeyboardInterrupt):
repository.destroy()
raise CancelledByUser()
Expand Down Expand Up @@ -413,6 +426,19 @@ def do_change_passphrase(self, args, repository, manifest, key):
# print key location to make backing it up easier
logger.info('Key location: %s', key.find_key())

@with_repository(manifest=True, compatibility=(Manifest.Operation.READ,))
def do_key_export_related_secrets(self, args, repository, manifest, key):
"""Export secrets for creating related repositories"""
secrets = {
'version': 1,
'id_key': bin_to_hex(key.id_key),
'chunk_seed': key.chunk_seed,
'key_name': key.NAME,
}
with dash_open(args.path, 'w') as fd:
json.dump(secrets, fd, indent=4)
fd.write('\n')

@with_repository(lock=False, exclusive=False, manifest=False, cache=False)
def do_key_export(self, args, repository):
"""Export the repository key for backup"""
Expand Down Expand Up @@ -4784,6 +4810,8 @@ def diff_sort_spec_validator(s):
help='Set storage quota of the new repository (e.g. 5G, 1.5T). Default: no quota.')
subparser.add_argument('--make-parent-dirs', dest='make_parent_dirs', action='store_true',
help='create the parent directories of the repository directory, if they are missing.')
subparser.add_argument('--import-related-secrets', metavar='PATH', dest='import_related_secrets',
type=PathSpec, help='import related secrets from PATH')

# borg key
subparser = subparsers.add_parser('key', parents=[mid_common_parser], add_help=False,
Expand Down Expand Up @@ -4851,6 +4879,54 @@ def diff_sort_spec_validator(s):
subparser.add_argument('--qr-html', dest='qr', action='store_true',
help='Create an html file suitable for printing and later type-in or qr scan')

export_related_secrets_epilog = process_epilog("""
This command exports the deduplication secrets (``id_key`` and ``chunk_seed``)
of a repository. These secrets can be used to initialize a **related repository**.

Related repositories share the same deduplication metadata but have their own
independent encryption keys. This is useful for:

1. Creating independent backup targets that still benefit from being
"compatible" for future archive transfers.
2. Preparing for a migration to Borg 2.0, where archives can be transferred
between related repositories using ``borg transfer``.

The exported secrets are stored in a JSON file. This file contains sensitive
information and should be deleted immediately after usage.

Examples::

# Export secrets from an existing repository
$ borg key export-related-secrets /path/to/repo1 secrets.json

# Initialize a new related repository using these secrets
$ borg init --import-related-secrets=secrets.json --encryption=repokey /path/to/repo2
$ rm secrets.json

.. IMPORTANT::
When initializing a related repository using ``borg init --import-related-secrets``,
the new repository must use the same ID hash algorithm (either both HMAC-SHA256
or both BLAKE2) as the original repository.

- HMAC-SHA256: ``repokey``, ``keyfile``, ``authenticated``
- BLAKE2: ``repokey-blake2``, ``keyfile-blake2``, ``authenticated-blake2``

.. WARNING::
Please note that future Borg 2.0 versions might remove support for BLAKE2
in new repositories (see :issue:`8867`).
""")

subparser = key_parsers.add_parser('export-related-secrets', parents=[common_parser], add_help=False,
description=self.do_key_export_related_secrets.__doc__,
epilog=export_related_secrets_epilog,
formatter_class=argparse.RawDescriptionHelpFormatter,
help='export related secrets for related repositories')
subparser.set_defaults(func=self.do_key_export_related_secrets)
subparser.add_argument('location', metavar='REPOSITORY', nargs='?', default='',
type=location_validator(archive=False))
subparser.add_argument('path', metavar='PATH', nargs='?', type=PathSpec,
help='where to store the secrets')

key_import_epilog = process_epilog("""
This command restores a key previously backed up with the export command.

Expand Down
Loading
Loading