Configuration
BCMR reads configuration from ~/.config/bcmr/config.toml (or config.yaml). All settings are optional — defaults are used when a key is absent.
Full Example
[progress]
style = "fancy" # "fancy" (default) or "plain" (same as --tui flag)
[progress.theme]
bar_gradient = ["#CABBE9", "#7E6EAC"] # Hex color stops for the progress bar
bar_complete_char = "█"
bar_incomplete_char = "░"
text_color = "reset" # "reset", named color, or "#RRGGBB"
border_color = "#9E8BCA"
title_color = "#9E8BCA"
[progress.layout]
box_style = "rounded" # "rounded" (default), "double", "heavy", "single"
[copy]
reflink = "auto" # "auto" (default), "force", or "disable"
sparse = "auto" # "auto" (default), "force", or "disable"
update_check = "off" # "off" (default, no network), "quiet", or "notify"
[scp]
parallel_transfers = 4 # concurrent SSH transfers (default: 4)
compression = "auto" # "auto" (default), "force", or "off"
[transfer]
fallback_warning = true # warn on stderr when serve fast path fails
# and we fall back to legacy SSH (default: true)
Progress Settings
progress.style
| Value | Description |
|---|---|
"fancy" | TUI box with gradient bar, ETA, speed, per-file bar (default) |
"plain" | 3-line text output, no box drawing |
progress.theme
bar_gradient— Array of hex colors. The progress bar interpolates between them. Default:["#CABBE9", "#7E6EAC"](Morandi purple).bar_complete_char/bar_incomplete_char— Characters for filled and empty portions.text_color— Named color ("red","green", etc.), hex ("#RRGGBB"), or"reset"for terminal default.border_color/title_color— Same format astext_color.
progress.layout.box_style
| Value | Preview |
|---|---|
"rounded" | ╭──╮ ╰──╯ |
"single" | ┌──┐ └──┘ |
"double" | ╔══╗ ╚══╝ |
"heavy" | ┏━━┓ ┗━━┛ |
Copy Settings
copy.reflink
Controls copy-on-write (reflink) behavior. Can be overridden per-command with --reflink.
| Value | Description |
|---|---|
"auto" | Try reflink, fall back to regular copy (default) |
"force" | Require reflink; fail if unsupported |
"disable" | Never attempt reflink |
Note: The config file also accepts
"never"as an alias for"disable".
copy.sparse
Controls sparse file detection. Can be overridden per-command with --sparse.
| Value | Description |
|---|---|
"auto" | Detect zero blocks ≥ 4KB and create holes (default) |
"force" | Always write sparse output, even for non-sparse sources |
"disable" | Write all data, no hole detection |
Note: The config file also accepts
"never"as an alias for"disable".
SCP Settings
scp.parallel_transfers
Number of concurrent SSH file transfers for remote copy. Can be overridden per-command with -P.
| Value | Description |
|---|---|
4 | Default — 4 parallel SSH streams |
1 | Sequential transfer (no parallelism) |
N | Any positive integer |
scp.compression
Controls SSH transport compression for remote transfers.
| Value | Description |
|---|---|
"auto" | Smart: enable if >30% of bytes are compressible by extension (default) |
"force" | Always enable SSH compression (-o Compression=yes) |
"off" | Never compress |
In auto mode, known compressed extensions (.gz, .zip, .mp4, .jpg, etc.) are treated as incompressible. Compression is enabled only when a significant portion of the data would benefit.
Update Check
Controls whether BCMR checks for new versions in the background when running any command.
| Value | Description |
|---|---|
"notify" | Check and print update notification to stderr (default) |
"quiet" | No notification |
"off" | Skip update check entirely |
Config File Locations
BCMR checks these paths in order:
~/.config/bcmr/config.toml~/.config/bcmr/config.yaml- Platform-specific config directory (via
directoriescrate):- macOS:
~/Library/Application Support/com.bcmr.bcmr/ - Windows:
%APPDATA%\bcmr\bcmr\
- macOS:
Path Bookmarks
Long, repeated remote destinations get tedious. The [paths] table maps short @aliases to full host:path strings; bcmr substitutes them at startup so every downstream code path sees the resolved destination.
[paths]
proj = "lab:/data/projects/myrepo/"
backup = "nas:/backups/"
logs = "archive:/var/log/myapp/"
bcmr copy ./project/ @proj # → lab:/data/projects/myrepo/
bcmr copy db.gz @proj/backups/ # → lab:/data/projects/myrepo/backups/
bcmr copy report.pdf @backup/today/ # → nas:/backups/today/
Rules:
- Alias names must match
[A-Za-z_][A-Za-z0-9_-]*. Invalid names error at use. - The trailing slash on the configured target is preserved for the bare-alias form (
@projreturns the target verbatim) and normalised for the suffixed form (@proj/foojoins with exactly one/between). - An unknown
@aliaserrors with adid you mean ...?suggestion (Levenshtein-based) and lists all known aliases. Bcmr does not silently fall through to a literal-file lookup. - To copy a literal file whose name starts with
@, prefix with./— e.g.bcmr copy ./@weird-filename ./dst/.
Aliases work in every position that accepts a path: source list, destination, bcmr check, bcmr remove.
Per-Host Default Flags
Apply a fixed set of flags whenever a transfer involves a particular host:
[host."lab"]
default_args = ["-p", "--compress", "zstd", "--reflink", "force"]
[host."slow_link"]
default_args = ["--bwlimit", "200K"]
bcmr copy ./project/ lab:dst/
# resolves to: bcmr copy -p --compress zstd --reflink force ./project/ lab:dst/
The host name matched is whatever appears in the host: portion of any source or destination argument (first one found wins). Explicit CLI flags appear after the injected defaults, so a user-typed --compress none overrides a host's default_args = ["--compress", "zstd"].
Profiles
Named bundles activated by --profile <name> or BCMR_PROFILE=<name>:
[profile.work]
default_args = ["-p", "-V"]
[profile.home]
default_args = ["-p"]
bcmr --profile work copy ./report.pdf host:dst/
# resolves to: bcmr copy -p -V ./report.pdf host:dst/
BCMR_PROFILE=home bcmr copy ./report.pdf host:dst/
# resolves to: bcmr copy -p ./report.pdf host:dst/
Precedence (left wins): explicit user flags > host defaults > profile defaults > built-in defaults. The injection ordering is profile, host, user, all left-to-right; clap parses earlier-then-later, so user flags win.
Unknown --profile <name> (no matching [profile.<name>] table) is a silent no-op — the rest of the command runs unchanged.
Environment Variables
| Variable | Default | Description |
|---|---|---|
BCMR_CAS_DIR | $XDG_DATA_HOME/bcmr/cas | Override the content-addressed store location used by the remote dedup path. Also honoured by the integration tests — they point it at a tempdir for isolation. |
BCMR_CAS_CAP_MB | 1024 (1 GiB) | Soft byte cap on the CAS, enforced by LRU eviction before each dedup-enabled PUT. Set to 0 to disable the cap and let the store grow unbounded. Values are whole megabytes. |
CAS paths by platform (when BCMR_CAS_DIR is unset):
- Linux:
~/.local/share/bcmr/cas/ - macOS:
~/Library/Application Support/bcmr/cas/ - Windows:
%APPDATA%\bcmr\cas\
Block files live under a two-level hex prefix
(<aa>/<bb>/<rest>.blk) so no single directory accumulates more
than ~65k entries on typical workloads. Clearing the store is
safe: rm -rf the cas directory and the next dedup-enabled PUT
rebuilds it from the wire.