Skip to content

Command line

arrowloop sync     -left <path> -right <path> -state <db>   run one pair once
arrowloop run      -config <file> <job>                     run one named job now
arrowloop web      -config <file>                           serve the interface, schedules included
arrowloop daemon   -config <file>                           run every scheduled job, no interface
arrowloop jobs     -config <file>                           what is configured, and when each last worked
arrowloop history  -config <file> [-job <name>]             what the runs did
arrowloop service  [-config <file>] [-os <goos>]            the service file for this system
arrowloop version                                           which build this is

Every command takes -h for its own flags.

sync

The shape for setting a job up: nothing to write to disk first, and -dry-run to see what would happen before anything does.

arrowloop sync -left /data/Photos -right sftp:backup/photos -state photos.db -dry-run

-dry-run prints the plan and changes nothing. Both sides accept anything rclone accepts, so sftp:backup/photos and s3:bucket/photos work alongside plain paths.

flag default meaning
-quiet-period 5s how long a file must sit unchanged before it is touched, 0 disables
-exclude - glob of paths to leave alone entirely, repeatable
-no-default-excludes off also sync half-written files such as *.part and Office owner files
-transfers 4 how many files may be copied at the same time
-bwlimit - bandwidth limit in rclone syntax, 1M or a timetable like 08:00,512k 19:00,off
-empty-dirs off also carry folders that hold no files
-metadata off carry permissions, ownership and extended attributes where both sides can
-brake-percent 50 refuse a run deleting more than this share of known files, 0 disables
-change-percent 50 refuse a run overwriting more than this share of known files, 0 disables
-brake-floor 10 never trip a brake below this many deletions or overwrites
-mod-window 2s how far modification times may differ and still count as equal
-v off let rclone report what it is doing underneath

The modification window only ever applies when a side cannot produce a hash. Where hashes exist the comparison is exact, because with a two-second window a file edited twice within two seconds and left at the same length would count as unchanged.

jobs

Reports when each job last succeeded, not when it last ran.

"When did this last run" is the easy question. A job failing every quarter of an hour for a week looks busy in a log while being of no use at all.

web and daemon

web serves the interface and runs the schedules in the same process. One process rather than two: a browser tab that could start a job but could not see the scheduled ones would be lying by omission, and two processes sharing one state database is the sort of arrangement that works until the day both happen to run the same job.

It listens on loopback unless told otherwise. The interface can start a job that deletes files and asks for no password until one is set under Settings, Security, or through ARROWLOOP_PASSWORD_HASH, so making it reachable has to be a decision somebody took on purpose. What the interface sets is kept in security.json beside the configuration file. Until there is a password it answers only on an IP address or a local name, so a web page elsewhere cannot point a name of its own at it; installing lists the names.

daemon is the same scheduler with no interface, for a machine where nobody is looking. It trims the run log once as it starts, keeping what historyKeep in the configuration asks for; -keep-history sets another age for that start, and 0 keeps every record.

service

Prints the file this operating system wants in order to keep the daemon alive, and the one command that switches it on: a systemd unit file on Linux, a LaunchAgent plist on macOS, and an sc.exe line on Windows. It prints rather than installs: registering a service means writing outside your own files and, on two of the three systems, asking for administrative rights.

arrowloop service -config /etc/arrowloop/arrowloop.json
arrowloop service -os windows

What it tells you that is otherwise found out the hard way:

  • A Windows service runs as LocalSystem, which has no mapped network drives. A job pointing at a UNC path works; one pointing at a drive letter does not.
  • LocalSystem also runs the jobs' before and after commands. Register a copy of the program under Program Files and a configuration file an administrator created, not the desktop app's copy under AppData or a file in a user's profile: whoever can change either file can run anything as LocalSystem.
  • A Linux user service needs loginctl enable-linger to survive a logout.
  • macOS asks for permission the first time the agent touches Documents, and until that is granted the job fails with an ordinary permission error that looks like a bug and is not one.

hash-password

Prints the value ARROWLOOP_PASSWORD_HASH wants. The interface sets its own password without it; the variable is for a password managed from outside, such as in a container template, and it wins over the one set in the interface, which makes it the way back in after that one is forgotten.

The hash is made under the key of the install whose configuration -config names, so run it where that install runs; in the container that is -config /config/arrowloop.json. Without a configuration there it refuses, unless ARROWLOOP_APP_KEY names the key.

At a terminal it asks for the password twice without echoing it; piped, it takes the first line.

arrowloop hash-password -config /config/arrowloop.json
printf '%s\n' "$PASSWORD" | arrowloop hash-password -config /config/arrowloop.json

The password never goes on the command line, where it would end up in a shell history and in the process list.

version

Prints which build this binary is, and nothing else, so it can be read from a script.

arrowloop version

A release build prints its tag, a build from a commit prints the commit, and a build from an edited tree adds -dirty, because a binary from uncommitted changes is not the commit it names. An unstamped local go build prints dev.

The desktop application answers the same question in two more places: under Product version in the Windows file properties dialog, and on its About card. All three read one stamp, so they cannot disagree. This command exists because a Go binary carries no version resource, so the properties dialog has nothing to show for this one.