{"owner":"borgbackup","repo":"borg","hasSkills":true,"totalSkillsCount":1,"totalTokensCount":2600,"categories":["claude-rule"],"hasMcp":false,"mcpConfig":null,"found":[".claude/CLAUDE.md"],"skills":{".claude/CLAUDE.md":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\nMachine-specific settings (e.g. the local virtualenv path) live in the gitignored `CLAUDE.local.md`:\n\n@CLAUDE.local.md\n\n## Branch context\n\nThis checkout is on `master`, the main development branch — currently borg 2.x (a beta, see\n`borg --version`). New development happens here; the stable maintenance branch for the previous\nmajor is `1.4-maint`.\n\nborg 2 is not just newer code, it is a different on-disk world from borg 1.x:\n\n- The repository is a **borgstore** object store (`repository.py` wraps `borgstore.store.Store`),\n  not the borg 1.x segment/journal format. There are no `data/` segments, no `COMMIT` transaction\n  log, and no repository-side segment index.\n- The CLI changed (e.g. `borg repo-create`, `borg repo-list`, archive series, archive IDs) and the\n  crypto/key modes changed (AEAD: AES-256-OCB / ChaCha20-Poly1305, with HMAC-SHA256 or BLAKE3 ids).\n- `src/borg/archiver/` is a package, one `*_cmd.py` module per command group — not the single\n  monolithic `archiver.py` file that borg 1.x (and the `1.4-maint` branch) uses.\n\nDon't be surprised by these differences if you're comparing against borg 1.x docs or memory. Legacy\nborg 1.x repositories are only supported read-only via `src/borg/legacy/` (e.g. for `borg transfer`).\n\n## Build\n\nBorg has Cython/C extensions and must be built before it can run.\n\n```bash\nsource <project-virtualenv>/bin/activate  # see @CLAUDE.local.md for the local path\npip install -r requirements.d/development.txt\npip install -e .  # builds Cython/C extensions in place\n```\n\nRequires Python >=3.11, Cython >=3.0.3, pkgconfig, and dev headers for OpenSSL, lz4, zstd, xxhash.\nThe `borgstore` package (repository backend) is pulled in as a normal dependency.\n\nEnv vars to point the build at non-standard lib locations: `BORG_OPENSSL_PREFIX`, `BORG_LIBLZ4_PREFIX`.\n\nAfter editing any `.pyx`/`.c` file, rebuild the extensions:\n\n```bash\npython scripts/make.py clean && pip install -e .\n```\n\nIf you are requested to build a BINARY (the usual borg build does not build a binary as it is Python),\na pyinstaller-made binary is meant:\n\n- POSIX: `pyinstaller --clean --distpath=dist/binary/ scripts/borg.exe.spec`\n\n## Writing tests\n\nAlways write pytest-style tests unless unittest-style is explicitly required.\n\n## Writing docstrings, comments, documentation\n\nThe docs shall always be in sync with the current code, algorithms and data structures.\n\nThe docs must:\n- describe how it works, what it does, in its CURRENT state.\n- use \"TODO:\" comments to refer to future tasks\n\nThe docs must not:\n- describe what the code did in the past\n- describe something that the code is NOT doing in its current state.\n\n## Running code or tests\n\nAlways activate the project virtualenv first (see `@CLAUDE.local.md` for its local path).\n\n## Tests\n\n```bash\n# Run everything (never omit --benchmark-skip unless you specifically want the slow benchmarks).\n# Running all the tests without parallelizing them is very slow, the parallel variant should be preferred.\npytest --benchmark-skip -v -rs -n auto  # parallel variant\npytest --benchmark-skip -v -rs          # sequential (slow)\n\n# Single file / single test (test files are named *_test.py; see python_files in pyproject.toml)\npytest src/borg/testsuite/compress_test.py\npytest src/borg/testsuite/compress_test.py::TestClass::test_method\n# archiver command tests live in the src/borg/testsuite/archiver/ package\npytest src/borg/testsuite/archiver/create_test.py\n\n# Via tox (matches CI; needs fakeroot for full coverage, most tests still work without it)\nfakeroot -u tox                                 # all tests\nfakeroot -u tox --recreate                      # after changing tox.ini\nfakeroot -u tox -e py311                         # one Python version only\nfakeroot -u tox -e py311 -- -k 'locking'        # select tests by keyword (posargs after --)\n```\n\nTest env vars:\n- `BORG_TESTS_IGNORE_MODES` (disable mode/permission tests)\n- `BORG_FUSE_IMPL=mfusepy|llfuse|pyfuse3` (select FUSE implementation for mount tests).\n\nTests live in `src/borg/testsuite/`, see `python_files` in `pyproject.toml`.\n\nmacOS users without a Linux box can run the Linux test matrix via Podman: `./scripts/linux-run tox -e py311-pyfuse3`.\n\n## Lint\n\n```bash\ntox -e ruff        # or: ruff check .\npre-commit install # one-time, so pre-commit hooks run automatically on commit\n```\n\nStyle: PEP 8 at 120 columns (not 79). Comments/docstrings that are full sentences end with a period.\nPer-file ruff ignores (mostly `E501`/`F401`/legacy `E722`/`E741`) are listed in `pyproject.toml`\nunder `[tool.ruff.per-file-ignores]` — check there before \"fixing\" a lint warning in an old file;\nit may be intentionally exempted.\n\nPython code files must never have trailing blanks at the end of lines.\nPython code files must always end with exactly one linefeed character at the end of the file.\n\n## Committing\n\nThe working tree has a lot of stray untracked scratch files at the repo root — these are the user's\nown working notes, not build artifacts.\nNever do `git add -A` / `git add .`; stage only the files you intentionally changed.\n\n## Architecture\n\nBorg is a client encrypting/deduplicating/compressing data *before* it ever reaches the repository,\nso the repository storage layer never sees plaintext or has any notion of files/archives.\n\n**Layering, bottom to top:**\n\n- **`repository.py`** — `Repository`, a key→value object store built on top of **borgstore**\n  (`borgstore.store.Store`). Keys are 32-byte object IDs; the repo doesn't know what an \"archive\" or\n  \"file\" is. borgstore provides the actual storage backends and transport: local filesystem,\n  `ssh://` (borgstore REST backend talking HTTP over stdio over ssh to a remote `borg serve`,\n  optionally reusing `BORGSTORE_RSH`), `sftp://`, `http(s)://`, `rclone:`, `s3:`/`b2:`. This replaces the borg 1.x segment/journal format — read\n  `docs/internals/data-structures.rst` before touching repository/object/format code.\n- **`archiver/serve_cmd.py`** — `borg serve` provides the server side for `ssh://` repositories:\n  `borg serve --rest` runs the borgstore REST server on stdio for a current repository, plain\n  `borg serve` serves a legacy borg 1.x repository via the legacy RPC protocol (`--from-borg1`,\n  `legacy/remote.py`).\n- **`crypto/key.py`, `crypto/low_level.pyx`** — encryption/authentication (AEAD: AES-256-OCB or\n  ChaCha20-Poly1305; ids via HMAC-SHA256 or BLAKE3) and key file handling (keyfile vs repokey\n  storage). The `id_hash` used for deduplication depends on the key/encryption mode.\n- **`chunkers/`** (buzhash, buzhash64, fastcdc, reader — Cython/C) — content-defined chunking:\n  splitting the input into variable-length chunks at content-defined boundaries is what makes dedup\n  insensitive to shifted/inserted bytes.\n- **`compress.pyx`** — per-chunk compression (lz4/zstd/zlib/lzma/…), applied to each chunk before\n  it is encrypted and stored.\n- **`cache.py`** — the local chunks/files cache used to decide what needs uploading. borg2 does **not**\n  refcount chunks. The chunks index (`ChunkIndex` from `hashindex.pyx`/`_hashindex.c`) maps chunk ID →\n  `(flags, size, pack_id, obj_offset, obj_size)`. Because borg2 batches many objects into a single\n  **pack** and stores that pack as one borgstore object, the index also records where each chunk lives\n  inside its pack: the containing `pack_id` plus the object's offset and size within the pack. The\n  files cache short-circuits re-chunking unchanged files. See `docs/internals/packs.rst` for the pack\n  format.\n- **`archive.py`** (`Archive`) and **`manifest.py`** (`Manifest`, `Archives`) — the layer above the\n  flat KV store, turning it into archives made of `Item`s (files/dirs/links, see `item.pyx`)\n  referencing chunk lists. The `manifest` is largely a relic from borg 1.x; in borg2 the list of\n  archives is a **separate** structure (the `Archives` class in `manifest.py`), no longer embedded in\n  the manifest object.\n- **`archiver/`** — the CLI, one `do_<command>` method per subcommand spread across `*_cmd.py`\n  modules (e.g. `create_cmd.py`, `check_cmd.py`, `repo_create_cmd.py`). `build_parser` and the\n  argument-parsing entry points live in `archiver/__init__.py`. This is the package to search first\n  when changing or adding a CLI command/flag.\n- **`platform/`** — per-OS backends (Linux/Darwin/FreeBSD/NetBSD/Windows) for xattrs, ACLs, syncing,\n  process info; `platform/__init__.py` picks the right implementation at import time.\n- **`helpers/`** — grab-bag of utilities used across layers (arg/format parsing, msgpack helpers,\n  progress reporting, filesystem helpers); check here before writing a new utility, it may exist.\n\n**Client/server split:** most `do_*` archiver commands operate against a local or remote `Repository`\ntransparently — the choice of local vs. `ssh://` etc. is resolved by `Location` parsing in\n`helpers/` and borgstore backend selection, and doesn't otherwise change command logic. `borg serve`\non the remote end provides the server side.\n\n**Docs as source of truth for on-disk formats:** `docs/internals/data-structures.rst` (plus\n`docs/internals/packs.rst` for the pack format) document the repository/object/pack/archive/item/cache\nformats in detail and are more authoritative than guessing from code — read the relevant section\nbefore changing serialization or the on-disk layout.\nCLI usage docs (`docs/usage/`) and man pages are generated from the argparse definitions in\n`archiver/` (`python scripts/make.py build_usage`/`build_man`); don't hand-edit generated usage docs,\nand don't feel obligated to regenerate them for every CLI change — that's normally done at release time.\n\n## Logging conventions\n\nUse the correct log level (debug only for debugging; info/warning/error/critical as appropriate).\n\nWhen directly prompting the user (e.g. Y/N confirmations), write straight to stderr, not through the\nlogger and not to stdout (stdout may be piped). Info-level volume is controlled via flags like\n`--stats`/`--list` feeding topic loggers — see `_setup_implied_logging()` in `archiver/__init__.py`.\n\n"},"files":{".claude/CLAUDE.md":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\nMachine-specific settings (e.g. the local virtualenv path) live in the gitignored `CLAUDE.local.md`:\n\n@CLAUDE.local.md\n\n## Branch context\n\nThis checkout is on `master`, the main development branch — currently borg 2.x (a beta, see\n`borg --version`). New development happens here; the stable maintenance branch for the previous\nmajor is `1.4-maint`.\n\nborg 2 is not just newer code, it is a different on-disk world from borg 1.x:\n\n- The repository is a **borgstore** object store (`repository.py` wraps `borgstore.store.Store`),\n  not the borg 1.x segment/journal format. There are no `data/` segments, no `COMMIT` transaction\n  log, and no repository-side segment index.\n- The CLI changed (e.g. `borg repo-create`, `borg repo-list`, archive series, archive IDs) and the\n  crypto/key modes changed (AEAD: AES-256-OCB / ChaCha20-Poly1305, with HMAC-SHA256 or BLAKE3 ids).\n- `src/borg/archiver/` is a package, one `*_cmd.py` module per command group — not the single\n  monolithic `archiver.py` file that borg 1.x (and the `1.4-maint` branch) uses.\n\nDon't be surprised by these differences if you're comparing against borg 1.x docs or memory. Legacy\nborg 1.x repositories are only supported read-only via `src/borg/legacy/` (e.g. for `borg transfer`).\n\n## Build\n\nBorg has Cython/C extensions and must be built before it can run.\n\n```bash\nsource <project-virtualenv>/bin/activate  # see @CLAUDE.local.md for the local path\npip install -r requirements.d/development.txt\npip install -e .  # builds Cython/C extensions in place\n```\n\nRequires Python >=3.11, Cython >=3.0.3, pkgconfig, and dev headers for OpenSSL, lz4, zstd, xxhash.\nThe `borgstore` package (repository backend) is pulled in as a normal dependency.\n\nEnv vars to point the build at non-standard lib locations: `BORG_OPENSSL_PREFIX`, `BORG_LIBLZ4_PREFIX`.\n\nAfter editing any `.pyx`/`.c` file, rebuild the extensions:\n\n```bash\npython scripts/make.py clean && pip install -e .\n```\n\nIf you are requested to build a BINARY (the usual borg build does not build a binary as it is Python),\na pyinstaller-made binary is meant:\n\n- POSIX: `pyinstaller --clean --distpath=dist/binary/ scripts/borg.exe.spec`\n\n## Writing tests\n\nAlways write pytest-style tests unless unittest-style is explicitly required.\n\n## Writing docstrings, comments, documentation\n\nThe docs shall always be in sync with the current code, algorithms and data structures.\n\nThe docs must:\n- describe how it works, what it does, in its CURRENT state.\n- use \"TODO:\" comments to refer to future tasks\n\nThe docs must not:\n- describe what the code did in the past\n- describe something that the code is NOT doing in its current state.\n\n## Running code or tests\n\nAlways activate the project virtualenv first (see `@CLAUDE.local.md` for its local path).\n\n## Tests\n\n```bash\n# Run everything (never omit --benchmark-skip unless you specifically want the slow benchmarks).\n# Running all the tests without parallelizing them is very slow, the parallel variant should be preferred.\npytest --benchmark-skip -v -rs -n auto  # parallel variant\npytest --benchmark-skip -v -rs          # sequential (slow)\n\n# Single file / single test (test files are named *_test.py; see python_files in pyproject.toml)\npytest src/borg/testsuite/compress_test.py\npytest src/borg/testsuite/compress_test.py::TestClass::test_method\n# archiver command tests live in the src/borg/testsuite/archiver/ package\npytest src/borg/testsuite/archiver/create_test.py\n\n# Via tox (matches CI; needs fakeroot for full coverage, most tests still work without it)\nfakeroot -u tox                                 # all tests\nfakeroot -u tox --recreate                      # after changing tox.ini\nfakeroot -u tox -e py311                         # one Python version only\nfakeroot -u tox -e py311 -- -k 'locking'        # select tests by keyword (posargs after --)\n```\n\nTest env vars:\n- `BORG_TESTS_IGNORE_MODES` (disable mode/permission tests)\n- `BORG_FUSE_IMPL=mfusepy|llfuse|pyfuse3` (select FUSE implementation for mount tests).\n\nTests live in `src/borg/testsuite/`, see `python_files` in `pyproject.toml`.\n\nmacOS users without a Linux box can run the Linux test matrix via Podman: `./scripts/linux-run tox -e py311-pyfuse3`.\n\n## Lint\n\n```bash\ntox -e ruff        # or: ruff check .\npre-commit install # one-time, so pre-commit hooks run automatically on commit\n```\n\nStyle: PEP 8 at 120 columns (not 79). Comments/docstrings that are full sentences end with a period.\nPer-file ruff ignores (mostly `E501`/`F401`/legacy `E722`/`E741`) are listed in `pyproject.toml`\nunder `[tool.ruff.per-file-ignores]` — check there before \"fixing\" a lint warning in an old file;\nit may be intentionally exempted.\n\nPython code files must never have trailing blanks at the end of lines.\nPython code files must always end with exactly one linefeed character at the end of the file.\n\n## Committing\n\nThe working tree has a lot of stray untracked scratch files at the repo root — these are the user's\nown working notes, not build artifacts.\nNever do `git add -A` / `git add .`; stage only the files you intentionally changed.\n\n## Architecture\n\nBorg is a client encrypting/deduplicating/compressing data *before* it ever reaches the repository,\nso the repository storage layer never sees plaintext or has any notion of files/archives.\n\n**Layering, bottom to top:**\n\n- **`repository.py`** — `Repository`, a key→value object store built on top of **borgstore**\n  (`borgstore.store.Store`). Keys are 32-byte object IDs; the repo doesn't know what an \"archive\" or\n  \"file\" is. borgstore provides the actual storage backends and transport: local filesystem,\n  `ssh://` (borgstore REST backend talking HTTP over stdio over ssh to a remote `borg serve`,\n  optionally reusing `BORGSTORE_RSH`), `sftp://`, `http(s)://`, `rclone:`, `s3:`/`b2:`. This replaces the borg 1.x segment/journal format — read\n  `docs/internals/data-structures.rst` before touching repository/object/format code.\n- **`archiver/serve_cmd.py`** — `borg serve` provides the server side for `ssh://` repositories:\n  `borg serve --rest` runs the borgstore REST server on stdio for a current repository, plain\n  `borg serve` serves a legacy borg 1.x repository via the legacy RPC protocol (`--from-borg1`,\n  `legacy/remote.py`).\n- **`crypto/key.py`, `crypto/low_level.pyx`** — encryption/authentication (AEAD: AES-256-OCB or\n  ChaCha20-Poly1305; ids via HMAC-SHA256 or BLAKE3) and key file handling (keyfile vs repokey\n  storage). The `id_hash` used for deduplication depends on the key/encryption mode.\n- **`chunkers/`** (buzhash, buzhash64, fastcdc, reader — Cython/C) — content-defined chunking:\n  splitting the input into variable-length chunks at content-defined boundaries is what makes dedup\n  insensitive to shifted/inserted bytes.\n- **`compress.pyx`** — per-chunk compression (lz4/zstd/zlib/lzma/…), applied to each chunk before\n  it is encrypted and stored.\n- **`cache.py`** — the local chunks/files cache used to decide what needs uploading. borg2 does **not**\n  refcount chunks. The chunks index (`ChunkIndex` from `hashindex.pyx`/`_hashindex.c`) maps chunk ID →\n  `(flags, size, pack_id, obj_offset, obj_size)`. Because borg2 batches many objects into a single\n  **pack** and stores that pack as one borgstore object, the index also records where each chunk lives\n  inside its pack: the containing `pack_id` plus the object's offset and size within the pack. The\n  files cache short-circuits re-chunking unchanged files. See `docs/internals/packs.rst` for the pack\n  format.\n- **`archive.py`** (`Archive`) and **`manifest.py`** (`Manifest`, `Archives`) — the layer above the\n  flat KV store, turning it into archives made of `Item`s (files/dirs/links, see `item.pyx`)\n  referencing chunk lists. The `manifest` is largely a relic from borg 1.x; in borg2 the list of\n  archives is a **separate** structure (the `Archives` class in `manifest.py`), no longer embedded in\n  the manifest object.\n- **`archiver/`** — the CLI, one `do_<command>` method per subcommand spread across `*_cmd.py`\n  modules (e.g. `create_cmd.py`, `check_cmd.py`, `repo_create_cmd.py`). `build_parser` and the\n  argument-parsing entry points live in `archiver/__init__.py`. This is the package to search first\n  when changing or adding a CLI command/flag.\n- **`platform/`** — per-OS backends (Linux/Darwin/FreeBSD/NetBSD/Windows) for xattrs, ACLs, syncing,\n  process info; `platform/__init__.py` picks the right implementation at import time.\n- **`helpers/`** — grab-bag of utilities used across layers (arg/format parsing, msgpack helpers,\n  progress reporting, filesystem helpers); check here before writing a new utility, it may exist.\n\n**Client/server split:** most `do_*` archiver commands operate against a local or remote `Repository`\ntransparently — the choice of local vs. `ssh://` etc. is resolved by `Location` parsing in\n`helpers/` and borgstore backend selection, and doesn't otherwise change command logic. `borg serve`\non the remote end provides the server side.\n\n**Docs as source of truth for on-disk formats:** `docs/internals/data-structures.rst` (plus\n`docs/internals/packs.rst` for the pack format) document the repository/object/pack/archive/item/cache\nformats in detail and are more authoritative than guessing from code — read the relevant section\nbefore changing serialization or the on-disk layout.\nCLI usage docs (`docs/usage/`) and man pages are generated from the argparse definitions in\n`archiver/` (`python scripts/make.py build_usage`/`build_man`); don't hand-edit generated usage docs,\nand don't feel obligated to regenerate them for every CLI change — that's normally done at release time.\n\n## Logging conventions\n\nUse the correct log level (debug only for debugging; info/warning/error/critical as appropriate).\n\nWhen directly prompting the user (e.g. Y/N confirmations), write straight to stderr, not through the\nlogger and not to stdout (stdout may be piped). Info-level volume is controlled via flags like\n`--stats`/`--list` feeding topic loggers — see `_setup_implied_logging()` in `archiver/__init__.py`.\n\n"},"items":[{"name":"CLAUDE.md","path":".claude/CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/borgbackup/borg/HEAD/.claude/CLAUDE.md","title":".claude - Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\nMachine-specific settings (e.g. the local virtualenv path) live in the gitignored `CLAUDE.local.md`:\n\n@CLAUDE.local.md\n\n## Branch context\n\nThis checkout is on `master`, the main development branch — currently borg 2.x (a beta, see\n`borg --version`). New development happens here; the stable maintenance branch for the previous\nmajor is `1.4-maint`.\n\nborg 2 is not just newer code, it is a different on-disk world from borg 1.x:\n\n- The repository is a **borgstore** object store (`repository.py` wraps `borgstore.store.Store`),\n  not the borg 1.x segment/journal format. There are no `data/` segments, no `COMMIT` transaction\n  log, and no repository-side segment index.\n- The CLI changed (e.g. `borg repo-create`, `borg repo-list`, archive series, archive IDs) and the\n  crypto/key modes changed (AEAD: AES-256-OCB / ChaCha20-Poly1305, with HMAC-SHA256 or BLAKE3 ids).\n- `src/borg/archiver/` is a package, one `*_cmd.py` module per command group — not the single\n  monolithic `archiver.py` file that borg 1.x (and the `1.4-maint` branch) uses.\n\nDon't be surprised by these differences if you're comparing against borg 1.x docs or memory. Legacy\nborg 1.x repositories are only supported read-only via `src/borg/legacy/` (e.g. for `borg transfer`).\n\n## Build\n\nBorg has Cython/C extensions and must be built before it can run.\n\n```bash\nsource <project-virtualenv>/bin/activate  # see @CLAUDE.local.md for the local path\npip install -r requirements.d/development.txt\npip install -e .  # builds Cython/C extensions in place\n```\n\nRequires Python >=3.11, Cython >=3.0.3, pkgconfig, and dev headers for OpenSSL, lz4, zstd, xxhash.\nThe `borgstore` package (repository backend) is pulled in as a normal dependency.\n\nEnv vars to point the build at non-standard lib locations: `BORG_OPENSSL_PREFIX`, `BORG_LIBLZ4_PREFIX`.\n\nAfter editing any `.pyx`/`.c` file, rebuild the extensions:\n\n```bash\npython scripts/make.py clean && pip install -e .\n```\n\nIf you are requested to build a BINARY (the usual borg build does not build a binary as it is Python),\na pyinstaller-made binary is meant:\n\n- POSIX: `pyinstaller --clean --distpath=dist/binary/ scripts/borg.exe.spec`\n\n## Writing tests\n\nAlways write pytest-style tests unless unittest-style is explicitly required.\n\n## Writing docstrings, comments, documentation\n\nThe docs shall always be in sync with the current code, algorithms and data structures.\n\nThe docs must:\n- describe how it works, what it does, in its CURRENT state.\n- use \"TODO:\" comments to refer to future tasks\n\nThe docs must not:\n- describe what the code did in the past\n- describe something that the code is NOT doing in its current state.\n\n## Running code or tests\n\nAlways activate the project virtualenv first (see `@CLAUDE.local.md` for its local path).\n\n## Tests\n\n```bash\n# Run everything (never omit --benchmark-skip unless you specifically want the slow benchmarks).\n# Running all the tests without parallelizing them is very slow, the parallel variant should be preferred.\npytest --benchmark-skip -v -rs -n auto  # parallel variant\npytest --benchmark-skip -v -rs          # sequential (slow)\n\n# Single file / single test (test files are named *_test.py; see python_files in pyproject.toml)\npytest src/borg/testsuite/compress_test.py\npytest src/borg/testsuite/compress_test.py::TestClass::test_method\n# archiver command tests live in the src/borg/testsuite/archiver/ package\npytest src/borg/testsuite/archiver/create_test.py\n\n# Via tox (matches CI; needs fakeroot for full coverage, most tests still work without it)\nfakeroot -u tox                                 # all tests\nfakeroot -u tox --recreate                      # after changing tox.ini\nfakeroot -u tox -e py311                         # one Python version only\nfakeroot -u tox -e py311 -- -k 'locking'        # select tests by keyword (posargs after --)\n```\n\nTest env vars:\n- `BORG_TESTS_IGNORE_MODES` (disable mode/permission tests)\n- `BORG_FUSE_IMPL=mfusepy|llfuse|pyfuse3` (select FUSE implementation for mount tests).\n\nTests live in `src/borg/testsuite/`, see `python_files` in `pyproject.toml`.\n\nmacOS users without a Linux box can run the Linux test matrix via Podman: `./scripts/linux-run tox -e py311-pyfuse3`.\n\n## Lint\n\n```bash\ntox -e ruff        # or: ruff check .\npre-commit install # one-time, so pre-commit hooks run automatically on commit\n```\n\nStyle: PEP 8 at 120 columns (not 79). Comments/docstrings that are full sentences end with a period.\nPer-file ruff ignores (mostly `E501`/`F401`/legacy `E722`/`E741`) are listed in `pyproject.toml`\nunder `[tool.ruff.per-file-ignores]` — check there before \"fixing\" a lint warning in an old file;\nit may be intentionally exempted.\n\nPython code files must never have trailing blanks at the end of lines.\nPython code files must always end with exactly one linefeed character at the end of the file.\n\n## Committing\n\nThe working tree has a lot of stray untracked scratch files at the repo root — these are the user's\nown working notes, not build artifacts.\nNever do `git add -A` / `git add .`; stage only the files you intentionally changed.\n\n## Architecture\n\nBorg is a client encrypting/deduplicating/compressing data *before* it ever reaches the repository,\nso the repository storage layer never sees plaintext or has any notion of files/archives.\n\n**Layering, bottom to top:**\n\n- **`repository.py`** — `Repository`, a key→value object store built on top of **borgstore**\n  (`borgstore.store.Store`). Keys are 32-byte object IDs; the repo doesn't know what an \"archive\" or\n  \"file\" is. borgstore provides the actual storage backends and transport: local filesystem,\n  `ssh://` (borgstore REST backend talking HTTP over stdio over ssh to a remote `borg serve`,\n  optionally reusing `BORGSTORE_RSH`), `sftp://`, `http(s)://`, `rclone:`, `s3:`/`b2:`. This replaces the borg 1.x segment/journal format — read\n  `docs/internals/data-structures.rst` before touching repository/object/format code.\n- **`archiver/serve_cmd.py`** — `borg serve` provides the server side for `ssh://` repositories:\n  `borg serve --rest` runs the borgstore REST server on stdio for a current repository, plain\n  `borg serve` serves a legacy borg 1.x repository via the legacy RPC protocol (`--from-borg1`,\n  `legacy/remote.py`).\n- **`crypto/key.py`, `crypto/low_level.pyx`** — encryption/authentication (AEAD: AES-256-OCB or\n  ChaCha20-Poly1305; ids via HMAC-SHA256 or BLAKE3) and key file handling (keyfile vs repokey\n  storage). The `id_hash` used for deduplication depends on the key/encryption mode.\n- **`chunkers/`** (buzhash, buzhash64, fastcdc, reader — Cython/C) — content-defined chunking:\n  splitting the input into variable-length chunks at content-defined boundaries is what makes dedup\n  insensitive to shifted/inserted bytes.\n- **`compress.pyx`** — per-chunk compression (lz4/zstd/zlib/lzma/…), applied to each chunk before\n  it is encrypted and stored.\n- **`cache.py`** — the local chunks/files cache used to decide what needs uploading. borg2 does **not**\n  refcount chunks. The chunks index (`ChunkIndex` from `hashindex.pyx`/`_hashindex.c`) maps chunk ID →\n  `(flags, size, pack_id, obj_offset, obj_size)`. Because borg2 batches many objects into a single\n  **pack** and stores that pack as one borgstore object, the index also records where each chunk lives\n  inside its pack: the containing `pack_id` plus the object's offset and size within the pack. The\n  files cache short-circuits re-chunking unchanged files. See `docs/internals/packs.rst` for the pack\n  format.\n- **`archive.py`** (`Archive`) and **`manifest.py`** (`Manifest`, `Archives`) — the layer above the\n  flat KV store, turning it into archives made of `Item`s (files/dirs/links, see `item.pyx`)\n  referencing chunk lists. The `manifest` is largely a relic from borg 1.x; in borg2 the list of\n  archives is a **separate** structure (the `Archives` class in `manifest.py`), no longer embedded in\n  the manifest object.\n- **`archiver/`** — the CLI, one `do_<command>` method per subcommand spread across `*_cmd.py`\n  modules (e.g. `create_cmd.py`, `check_cmd.py`, `repo_create_cmd.py`). `build_parser` and the\n  argument-parsing entry points live in `archiver/__init__.py`. This is the package to search first\n  when changing or adding a CLI command/flag.\n- **`platform/`** — per-OS backends (Linux/Darwin/FreeBSD/NetBSD/Windows) for xattrs, ACLs, syncing,\n  process info; `platform/__init__.py` picks the right implementation at import time.\n- **`helpers/`** — grab-bag of utilities used across layers (arg/format parsing, msgpack helpers,\n  progress reporting, filesystem helpers); check here before writing a new utility, it may exist.\n\n**Client/server split:** most `do_*` archiver commands operate against a local or remote `Repository`\ntransparently — the choice of local vs. `ssh://` etc. is resolved by `Location` parsing in\n`helpers/` and borgstore backend selection, and doesn't otherwise change command logic. `borg serve`\non the remote end provides the server side.\n\n**Docs as source of truth for on-disk formats:** `docs/internals/data-structures.rst` (plus\n`docs/internals/packs.rst` for the pack format) document the repository/object/pack/archive/item/cache\nformats in detail and are more authoritative than guessing from code — read the relevant section\nbefore changing serialization or the on-disk layout.\nCLI usage docs (`docs/usage/`) and man pages are generated from the argparse definitions in\n`archiver/` (`python scripts/make.py build_usage`/`build_man`); don't hand-edit generated usage docs,\nand don't feel obligated to regenerate them for every CLI change — that's normally done at release time.\n\n## Logging conventions\n\nUse the correct log level (debug only for debugging; info/warning/error/critical as appropriate).\n\nWhen directly prompting the user (e.g. Y/N confirmations), write straight to stderr, not through the\nlogger and not to stdout (stdout may be piped). Info-level volume is controlled via flags like\n`--stats`/`--list` feeding topic loggers — see `_setup_implied_logging()` in `archiver/__init__.py`.\n\n","isInternal":false,"tokens":2600,"sizeBytes":10190}],"systemPromptSnippet":"<yakaai_skills repo=\"borgbackup/borg\">\n<!-- File: .claude/CLAUDE.md (Tokens: ~2600 | Category: claude-rule) -->\n# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\nMachine-specific settings (e.g. the local virtualenv path) live in the gitignored `CLAUDE.local.md`:\n\n@CLAUDE.local.md\n\n## Branch context\n\nThis checkout is on `master`, the main development branch — currently borg 2.x (a beta, see\n`borg --version`). New development happens here; the stable maintenance branch for the previous\nmajor is `1.4-maint`.\n\nborg 2 is not just newer code, it is a different on-disk world from borg 1.x:\n\n- The repository is a **borgstore** object store (`repository.py` wraps `borgstore.store.Store`),\n  not the borg 1.x segment/journal format. There are no `data/` segments, no `COMMIT` transaction\n  log, and no repository-side segment index.\n- The CLI changed (e.g. `borg repo-create`, `borg repo-list`, archive series, archive IDs) and the\n  crypto/key modes changed (AEAD: AES-256-OCB / ChaCha20-Poly1305, with HMAC-SHA256 or BLAKE3 ids).\n- `src/borg/archiver/` is a package, one `*_cmd.py` module per command group — not the single\n  monolithic `archiver.py` file that borg 1.x (and the `1.4-maint` branch) uses.\n\nDon't be surprised by these differences if you're comparing against borg 1.x docs or memory. Legacy\nborg 1.x repositories are only supported read-only via `src/borg/legacy/` (e.g. for `borg transfer`).\n\n## Build\n\nBorg has Cython/C extensions and must be built before it can run.\n\n```bash\nsource <project-virtualenv>/bin/activate  # see @CLAUDE.local.md for the local path\npip install -r requirements.d/development.txt\npip install -e .  # builds Cython/C extensions in place\n```\n\nRequires Python >=3.11, Cython >=3.0.3, pkgconfig, and dev headers for OpenSSL, lz4, zstd, xxhash.\nThe `borgstore` package (repository backend) is pulled in as a normal dependency.\n\nEnv vars to point the build at non-standard lib locations: `BORG_OPENSSL_PREFIX`, `BORG_LIBLZ4_PREFIX`.\n\nAfter editing any `.pyx`/`.c` file, rebuild the extensions:\n\n```bash\npython scripts/make.py clean && pip install -e .\n```\n\nIf you are requested to build a BINARY (the usual borg build does not build a binary as it is Python),\na pyinstaller-made binary is meant:\n\n- POSIX: `pyinstaller --clean --distpath=dist/binary/ scripts/borg.exe.spec`\n\n## Writing tests\n\nAlways write pytest-style tests unless unittest-style is explicitly required.\n\n## Writing docstrings, comments, documentation\n\nThe docs shall always be in sync with the current code, algorithms and data structures.\n\nThe docs must:\n- describe how it works, what it does, in its CURRENT state.\n- use \"TODO:\" comments to refer to future tasks\n\nThe docs must not:\n- describe what the code did in the past\n- describe something that the code is NOT doing in its current state.\n\n## Running code or tests\n\nAlways activate the project virtualenv first (see `@CLAUDE.local.md` for its local path).\n\n## Tests\n\n```bash\n# Run everything (never omit --benchmark-skip unless you specifically want the slow benchmarks).\n# Running all the tests without parallelizing them is very slow, the parallel variant should be preferred.\npytest --benchmark-skip -v -rs -n auto  # parallel variant\npytest --benchmark-skip -v -rs          # sequential (slow)\n\n# Single file / single test (test files are named *_test.py; see python_files in pyproject.toml)\npytest src/borg/testsuite/compress_test.py\npytest src/borg/testsuite/compress_test.py::TestClass::test_method\n# archiver command tests live in the src/borg/testsuite/archiver/ package\npytest src/borg/testsuite/archiver/create_test.py\n\n# Via tox (matches CI; needs fakeroot for full coverage, most tests still work without it)\nfakeroot -u tox                                 # all tests\nfakeroot -u tox --recreate                      # after changing tox.ini\nfakeroot -u tox -e py311                         # one Python version only\nfakeroot -u tox -e py311 -- -k 'locking'        # select tests by keyword (posargs after --)\n```\n\nTest env vars:\n- `BORG_TESTS_IGNORE_MODES` (disable mode/permission tests)\n- `BORG_FUSE_IMPL=mfusepy|llfuse|pyfuse3` (select FUSE implementation for mount tests).\n\nTests live in `src/borg/testsuite/`, see `python_files` in `pyproject.toml`.\n\nmacOS users without a Linux box can run the Linux test matrix via Podman: `./scripts/linux-run tox -e py311-pyfuse3`.\n\n## Lint\n\n```bash\ntox -e ruff        # or: ruff check .\npre-commit install # one-time, so pre-commit hooks run automatically on commit\n```\n\nStyle: PEP 8 at 120 columns (not 79). Comments/docstrings that are full sentences end with a period.\nPer-file ruff ignores (mostly `E501`/`F401`/legacy `E722`/`E741`) are listed in `pyproject.toml`\nunder `[tool.ruff.per-file-ignores]` — check there before \"fixing\" a lint warning in an old file;\nit may be intentionally exempted.\n\nPython code files must never have trailing blanks at the end of lines.\nPython code files must always end with exactly one linefeed character at the end of the file.\n\n## Committing\n\nThe working tree has a lot of stray untracked scratch files at the repo root — these are the user's\nown working notes, not build artifacts.\nNever do `git add -A` / `git add .`; stage only the files you intentionally changed.\n\n## Architecture\n\nBorg is a client encrypting/deduplicating/compressing data *before* it ever reaches the repository,\nso the repository storage layer never sees plaintext or has any notion of files/archives.\n\n**Layering, bottom to top:**\n\n- **`repository.py`** — `Repository`, a key→value object store built on top of **borgstore**\n  (`borgstore.store.Store`). Keys are 32-byte object IDs; the repo doesn't know what an \"archive\" or\n  \"file\" is. borgstore provides the actual storage backends and transport: local filesystem,\n  `ssh://` (borgstore REST backend talking HTTP over stdio over ssh to a remote `borg serve`,\n  optionally reusing `BORGSTORE_RSH`), `sftp://`, `http(s)://`, `rclone:`, `s3:`/`b2:`. This replaces the borg 1.x segment/journal format — read\n  `docs/internals/data-structures.rst` before touching repository/object/format code.\n- **`archiver/serve_cmd.py`** — `borg serve` provides the server side for `ssh://` repositories:\n  `borg serve --rest` runs the borgstore REST server on stdio for a current repository, plain\n  `borg serve` serves a legacy borg 1.x repository via the legacy RPC protocol (`--from-borg1`,\n  `legacy/remote.py`).\n- **`crypto/key.py`, `crypto/low_level.pyx`** — encryption/authentication (AEAD: AES-256-OCB or\n  ChaCha20-Poly1305; ids via HMAC-SHA256 or BLAKE3) and key file handling (keyfile vs repokey\n  storage). The `id_hash` used for deduplication depends on the key/encryption mode.\n- **`chunkers/`** (buzhash, buzhash64, fastcdc, reader — Cython/C) — content-defined chunking:\n  splitting the input into variable-length chunks at content-defined boundaries is what makes dedup\n  insensitive to shifted/inserted bytes.\n- **`compress.pyx`** — per-chunk compression (lz4/zstd/zlib/lzma/…), applied to each chunk before\n  it is encrypted and stored.\n- **`cache.py`** — the local chunks/files cache used to decide what needs uploading. borg2 does **not**\n  refcount chunks. The chunks index (`ChunkIndex` from `hashindex.pyx`/`_hashindex.c`) maps chunk ID →\n  `(flags, size, pack_id, obj_offset, obj_size)`. Because borg2 batches many objects into a single\n  **pack** and stores that pack as one borgstore object, the index also records where each chunk lives\n  inside its pack: the containing `pack_id` plus the object's offset and size within the pack. The\n  files cache short-circuits re-chunking unchanged files. See `docs/internals/packs.rst` for the pack\n  format.\n- **`archive.py`** (`Archive`) and **`manifest.py`** (`Manifest`, `Archives`) — the layer above the\n  flat KV store, turning it into archives made of `Item`s (files/dirs/links, see `item.pyx`)\n  referencing chunk lists. The `manifest` is largely a relic from borg 1.x; in borg2 the list of\n  archives is a **separate** structure (the `Archives` class in `manifest.py`), no longer embedded in\n  the manifest object.\n- **`archiver/`** — the CLI, one `do_<command>` method per subcommand spread across `*_cmd.py`\n  modules (e.g. `create_cmd.py`, `check_cmd.py`, `repo_create_cmd.py`). `build_parser` and the\n  argument-parsing entry points live in `archiver/__init__.py`. This is the package to search first\n  when changing or adding a CLI command/flag.\n- **`platform/`** — per-OS backends (Linux/Darwin/FreeBSD/NetBSD/Windows) for xattrs, ACLs, syncing,\n  process info; `platform/__init__.py` picks the right implementation at import time.\n- **`helpers/`** — grab-bag of utilities used across layers (arg/format parsing, msgpack helpers,\n  progress reporting, filesystem helpers); check here before writing a new utility, it may exist.\n\n**Client/server split:** most `do_*` archiver commands operate against a local or remote `Repository`\ntransparently — the choice of local vs. `ssh://` etc. is resolved by `Location` parsing in\n`helpers/` and borgstore backend selection, and doesn't otherwise change command logic. `borg serve`\non the remote end provides the server side.\n\n**Docs as source of truth for on-disk formats:** `docs/internals/data-structures.rst` (plus\n`docs/internals/packs.rst` for the pack format) document the repository/object/pack/archive/item/cache\nformats in detail and are more authoritative than guessing from code — read the relevant section\nbefore changing serialization or the on-disk layout.\nCLI usage docs (`docs/usage/`) and man pages are generated from the argparse definitions in\n`archiver/` (`python scripts/make.py build_usage`/`build_man`); don't hand-edit generated usage docs,\nand don't feel obligated to regenerate them for every CLI change — that's normally done at release time.\n\n## Logging conventions\n\nUse the correct log level (debug only for debugging; info/warning/error/critical as appropriate).\n\nWhen directly prompting the user (e.g. Y/N confirmations), write straight to stderr, not through the\nlogger and not to stdout (stdout may be piped). Info-level volume is controlled via flags like\n`--stats`/`--list` feeding topic loggers — see `_setup_implied_logging()` in `archiver/__init__.py`.\n</yakaai_skills>"}