From 49ce1154eb81e2a9fcceac811ddfadcff8601b5c Mon Sep 17 00:00:00 2001 From: Martin Bertschler Date: Tue, 16 Jun 2026 22:21:56 +0200 Subject: [PATCH] config: emit blake3sum_command for sftp so `--hash blake3` syncs work MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rclone's sftp backend only autodetects md5sum/sha1sum, never a BLAKE3 command, so every non-shallow sync — which runs `rclone copy --checksum --hash blake3` — aborted with "hash type not supported", even when the remote has b3sum installed. Render `blake3sum_command = b3sum` in every sftp section so rclone can compute server-side BLAKE3. --- config/config_test.go | 31 +++++++++++++++++++++++++++++++ config/destinations.go | 11 +++++++++-- sync/rclone_test.go | 1 + 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/config/config_test.go b/config/config_test.go index eec2e98..cc1ef3c 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -560,6 +560,7 @@ password2 = "obscured-salt" type = sftp host = host.example user = u +blake3sum_command = b3sum password = transport-pw [offsite-crypt] @@ -575,6 +576,36 @@ password2 = obscured-salt } } +// TestRcloneSectionSFTPEmitsBlake3sumCommand pins that every sftp section +// carries a blake3sum_command. rclone never autodetects one, so without it +// squirrel's `--hash blake3` syncs fail with "hash type not supported". The +// line is sftp-only: backends with a fixed provider checksum must not get it. +func TestRcloneSectionSFTPEmitsBlake3sumCommand(t *testing.T) { + p := writeConfig(t, ` +[destinations.nas] +type = "sftp" +host = "h" +user = "u" +root = "/r" + +[destinations.s3] +type = "s3" +provider = "AWS" +bucket = "b" +root = "/r" +`) + cfg, err := Load(p) + if err != nil { + t.Fatalf("Load: %v", err) + } + if got := cfg.Destinations["nas"].RcloneSection(); !strings.Contains(got, "blake3sum_command = b3sum") { + t.Fatalf("sftp section missing blake3sum_command:\n%s", got) + } + if got := cfg.Destinations["s3"].RcloneSection(); strings.Contains(got, "blake3sum_command") { + t.Fatalf("non-sftp section should not carry blake3sum_command:\n%s", got) + } +} + // TestRcloneSectionCryptOmitsEmptySalt: password2 is optional, mirroring // rclone's own crypt config, and an absent salt renders no password2 line. func TestRcloneSectionCryptOmitsEmptySalt(t *testing.T) { diff --git a/config/destinations.go b/config/destinations.go index 7edec9c..b995836 100644 --- a/config/destinations.go +++ b/config/destinations.go @@ -435,8 +435,15 @@ func (d *Destination) RcloneSection() string { fmt.Fprintf(&b, "%s = %s\n", key, v) } } - if d.Type == "sftp" && d.HashAlgo != "" { - fmt.Fprintf(&b, "hashes = %s\n", d.HashAlgo) + if d.Type == "sftp" { + // rclone's sftp backend only autodetects md5sum/sha1sum, so BLAKE3 + // must be named explicitly or squirrel's `--hash blake3` syncs abort + // with "hash type not supported". b3sum is the canonical BLAKE3 CLI + // and must be on the remote's PATH. + fmt.Fprintf(&b, "blake3sum_command = b3sum\n") + if d.HashAlgo != "" { + fmt.Fprintf(&b, "hashes = %s\n", d.HashAlgo) + } } for _, key := range sortedSubset(schema.secretFields) { if v, ok := d.Params[key]; ok { diff --git a/sync/rclone_test.go b/sync/rclone_test.go index 49a3c23..27a019f 100644 --- a/sync/rclone_test.go +++ b/sync/rclone_test.go @@ -325,6 +325,7 @@ password2 = "obscured-salt" type = sftp host = host.example user = u +blake3sum_command = b3sum password = transport-pw [offsite-crypt]