fix(ci): send rsync to "." not "/", which rrsync 3.4.4 rejects #2

Merged
oap merged 1 commit from fix/rrsync-destination into v5 2026-09-08 08:23:54 -07:00
Owner

The deploy now builds successfully and reaches the publish step, where it dies:

rrsync error: unsafe arg: / ['', '/var/www/kvartsi']
rsync: connection unexpectedly closed (0 bytes received so far) [sender]
rsync error: error in rsync protocol data stream (code 12) at io.c(232) [sender=3.4.4]

Why / is wrong

rrsync confines this login to /var/www/kvartsi and rewrites paths under it, so the destination is already relative to the web root — which makes / look like the way to name that root. It isn't. validated_arg() strips leading slashes before prepending the root:

arg = arg.lstrip('/')        # '/' becomes ''
if args.dir != '/':
    if arg.startswith('/'):  # '' no longer does, so the root is never prepended
        arg = args.dir + arg

The empty path then realpaths back to the root itself, which fails the containment test because it does not start with dir + "/". Hence the error, with the empty string visible in the diagnostic.

. is exempt from that check by name (arg != '.' guards the whole block) and is the form rrsync's own documentation uses.

On the earlier verification

I checked this before and it passed, because I tested the wrong version. nixpkgs#rrsync from a local registry resolves to 3.4.1, which accepts /; nas runs 3.4.4 from this flake, which does not. Rebuilt with --inputs-from to match the host and tested all three forms against it:

destination result
:/ fails, reproducing the error above exactly
:. succeeds, and --delete still removes stale files
: succeeds

The comment on that line now explains why . and not /, since a bare relative destination is otherwise a thing someone would "fix" back. It also answers the question raised on #1 about this line.

The deploy now builds successfully and reaches the publish step, where it dies: ``` rrsync error: unsafe arg: / ['', '/var/www/kvartsi'] rsync: connection unexpectedly closed (0 bytes received so far) [sender] rsync error: error in rsync protocol data stream (code 12) at io.c(232) [sender=3.4.4] ``` ### Why `/` is wrong rrsync confines this login to `/var/www/kvartsi` and rewrites paths under it, so the destination is already relative to the web root — which makes `/` look like the way to name that root. It isn't. `validated_arg()` strips leading slashes *before* prepending the root: ```python arg = arg.lstrip('/') # '/' becomes '' if args.dir != '/': if arg.startswith('/'): # '' no longer does, so the root is never prepended arg = args.dir + arg ``` The empty path then `realpath`s back to the root itself, which fails the containment test because it does not start with `dir + "/"`. Hence the error, with the empty string visible in the diagnostic. `.` is exempt from that check by name (`arg != '.'` guards the whole block) and is the form rrsync's own documentation uses. ### On the earlier verification I checked this before and it passed, because I tested the wrong version. `nixpkgs#rrsync` from a local registry resolves to **3.4.1**, which accepts `/`; nas runs **3.4.4** from this flake, which does not. Rebuilt with `--inputs-from` to match the host and tested all three forms against it: | destination | result | |---|---| | `:/` | fails, reproducing the error above exactly | | `:.` | succeeds, and `--delete` still removes stale files | | `:` | succeeds | The comment on that line now explains why `.` and not `/`, since a bare relative destination is otherwise a thing someone would "fix" back. It also answers the question raised on #1 about this line.
fix(ci): send rsync to "." not "/", which rrsync 3.4.4 rejects
All checks were successful
deploy / deploy (push) Successful in 4m8s
ecedbde68c
The deploy reached the publish step and died there:

    rrsync error: unsafe arg: / ['', '/var/www/kvartsi']
    rsync error: error in rsync protocol data stream (code 12)

rrsync confines this login to /var/www/kvartsi and rewrites paths under it, so
the destination is already relative to the web root and "/" reads like the way
to name that root. It is not. validated_arg() lstrips leading slashes *before*
prepending the root, so "/" becomes "" and the prepend branch never fires; the
empty path then realpaths back to the root itself, which fails the containment
test because it does not start with dir + "/".

"." is exempt from that check by name, and is the form rrsync's own docs use.

This was verified against the wrong version first. rrsync 3.4.1 accepts "/",
and that is what `nixpkgs#rrsync` resolves to from a local registry; nas runs
3.4.4 from this flake, which does not. Rebuilt with --inputs-from and both
forms tested against it: "/" reproduces the failure exactly, "." succeeds and
--delete still removes files.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
oap merged commit ecedbde68c into v5 2026-09-08 08:23:54 -07:00
oap deleted branch fix/rrsync-destination 2026-09-08 08:23:54 -07:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
oap/kvartsi!2
No description provided.