Installing¶
As a container¶
docker run -d --name arrowloop -p 8422:8422 \
-v /mnt/user/appdata/arrowloop:/config \
-v /mnt/user:/data \
junkerderprovinz/arrowloop:latest
/config holds arrowloop.json, one state database per job, and the run log.
/config has to survive the container
Those state databases are what let the engine tell a new file from a deleted one. Lose them and every job forgets what the two sides agreed on and treats every file on both sides as new: nothing is destroyed, but a great deal is copied, and a deletion made while the record was missing is propagated to nobody.
A first start on an empty /config writes a starter configuration with no
jobs, so the interface comes up and can be edited rather than
crash-looping on a missing file.
A password for the interface¶
The container listens on every address, so anyone on the network who reaches port 8422 can start a job. Set a password under Settings, Security, and from then on every page but the login asks for it.
The same section sets up a second factor: six digits from an authenticator app on top of the password. Setting it up shows eight recovery codes once, and each of them signs in a single time in place of a code.
It also registers passkeys, a key on a phone, a laptop or a security stick used
instead of typing the password. A browser offers one only on a host name over
HTTPS with a certificate it trusts, or on localhost, so on
http://192.168.1.5:8422 the section explains why it cannot. Behind a reverse
proxy with a real name they work, and each key belongs to the address it was
registered on.
All three are kept in /config/security.json, readable by its owner only, and
never in arrowloop.json. That file is downloaded whole as a settings backup and
replaced whole by a restore, so a hash in it would leave with every backup and
restoring an old one would switch the password off.
The environment variable¶
ARROWLOOP_PASSWORD_HASH still works, and it wins over the password set in the
interface, which then shows the password as set from outside and offers no form
for it. That makes it the way back in after a forgotten password. Have the image
hash one and hand the result back:
docker run --rm -it junkerderprovinz/arrowloop:latest hash-password
docker run -d --name arrowloop -p 8422:8422 \
-e ARROWLOOP_PASSWORD_HASH='$2a$10$...' \
...
The single quotes matter: the hash is full of $, which a shell would otherwise
read as variables. In a Compose file, write every $ as $$.
If the authenticator app and the recovery codes are both gone, stop the
container and delete /config/security.json. That removes the password, the
second factor and the passkeys together. A damaged security.json stops the
container from starting rather than letting it come up without a password; the
log names the file, and deleting it has the same effect.
On Unraid¶
The template is
templates/my-ArrowLoop.xml.
Drop it into /boot/config/plugins/dockerMan/templates-user/ and it appears in
the Docker tab's Add Container list, with every field editable. Most installs
set the password in the interface; the template's masked password hash field
is the environment variable above, and its text says how to fill it from the
container's console.
Mount the data path read-write. A two-way job writes to both sides by definition, and a read-only mount produces a job that fails on every run for a reason that reads like a bug.
As a desktop application¶
Windows, Linux and macOS builds are on the
releases page. Windows
gets an installer that gives it an entry under Apps. It installs for all
users under C:\Program Files\ArrowLoop and asks for an administrator once,
while it installs; later updates ask nobody (see Updates). A page
asks whether you want a Start menu entry and a desktop shortcut, both ticked at
first, and both go to every user on the computer. The next install, silent or
not, starts from your answer and removes a shortcut you left out.
The installer also cleans up after older versions. The copy 1.2.0 installed for
you alone under AppData\Local\Programs goes, with its shortcuts and its entry
under Apps, and so does an installation for all users from 1.1.0 or earlier. It
never touches your settings and jobs in %APPDATA%\ArrowLoop. A 1.2.0
installation that you leave alone keeps updating itself where it is; running
the new installer once moves it.
Run as installer.exe /S /relaunch, it installs silently and starts ArrowLoop
again, as the person signed in, when it is done. A computer with an ARM
processor, such as a Snapdragon laptop, takes the one named windows-arm64; the
amd64 installer would run there too, but through emulation and slower.
It is not a client talking to a server. The scheduler, the run log and the API all live in the same process, and the window is a webview pointed at them. Nothing listens on the network at all, which is the difference between a desktop application and a server nobody asked to run.
The schedules run for as long as the window is open. For a machine that should
sync while nobody is looking, use arrowloop daemon and arrowloop service
instead; see the command line.
Starting with the system¶
Settings, General, Starting has a switch that registers ArrowLoop to start when
you sign in. It is a per-user entry (a value under HKCU on Windows, a
.desktop file under ~/.config/autostart on Linux, a LaunchAgent in your own
Library on macOS), so it needs no administrator rights and affects nobody else on
the machine.
The switch reads its state back from the system rather than from a settings file, so removing the entry with the Task Manager's own Startup tab turns the switch off too, instead of leaving it claiming something that is no longer true.
Autostart on its own only gets the program running. A job that should sync
because the machine just came on wants runAtStart as well, or it will sit
there until its schedule is next due; see
the configuration reference.
The notification area¶
It puts an icon in the notification area. A click on it opens a small window beside the icon with what is running now, the last five runs, and buttons to pause, to sync everything and to open ArrowLoop. A second click or a click elsewhere closes it, and a double click opens the main window. The right-click menu has:
- Open, which brings the main window back.
- Force sync, which starts every job straight away, as if you had started each one by hand, so no pause and no battery setting holds it.
- Pause sync or Resume sync. A pause holds every scheduled and watching run until you resume, stops the runs already going, and stays after a restart. The icon turns grey with a pause badge while it lasts.
- Quit.
The menu and the small window speak the language the interface is set to.
Settings, General decides what the window buttons do: whether closing quits or hides, and whether minimising goes to the taskbar or to the notification area. Closing quits by default, because a close button that quietly leaves a program running is the kind of surprise somebody finds a week later. Both choices switch off together with the icon, since a window that hides with nothing left to bring it back is gone. Switching the icon off or on applies at once.
Starting it a second time does not start a second copy. It brings the running window back, which is what a second double-click means when the first one is sitting in the notification area.
Updates¶
The desktop app updates itself from GitHub. It asks for the newest release,
downloads the file for your system in the background and checks it against the
release's checksums.txt. The new version starts the next time you start
ArrowLoop, so a sync that is running is never cut off, and a note in the corner
of the window says when it is ready. Pre-releases are never installed.
Update automatically under Settings, General, Updates turns this off; it is
on from the start. The container and the phone do not show the switch: a
container is updated the way it was deployed, and the phone through its store
or a new APK.
The installed copy under Program Files cannot replace itself, since no user
may write there. A scheduled task named ArrowLoop Update does it instead,
once a day and five minutes after the computer starts, whether ArrowLoop is
open or not. It replaces ArrowLoop.exe, sets the version shown under Apps and
writes what it did, or why it did not, to %ProgramData%\ArrowLoop\update.log.
There is one installation, so the switch applies to everyone on the computer:
it lives in %ProgramData%\ArrowLoop\settings.json, which every user may
change, and the task skips its run while it is off. An open window notices
within a few minutes when the task has put a new version in place and shows
the note. The replaced program waits beside the new one as ArrowLoop.exe.old
until a later run removes it.
A portable copy, such as the arrowloop-windows-amd64-portable.exe from the
releases page, updates itself while it runs: a minute after it starts and once
a day after that, in its own folder, and it stays portable. The app on macOS
and Linux does the same. A folder it cannot write to, or a macOS app your
account cannot change, stays as it is. Its log is update.log beside the
configuration file.
Why the task runs as SYSTEM¶
Program Files belongs to the administrators. A program that replaces itself
there without asking anyone needs an account that may write there, and a
scheduled task under the system account is how Windows provides one; Firefox's
maintenance service and Chrome's updater task work the same way. The task
writes to three places and nowhere else: the installation folder,
C:\Program Files\ArrowLoop; the entry under Apps, in
HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall\ArrowLoop; and
%ProgramData%\ArrowLoop, for its log. It reads nothing from any user's
profile. The folder under ProgramData belongs to the administrators, and only
settings.json in it is open to other users. The installer has no page for
choosing another folder. Together that leaves no file a user could swap for
one the system account would run or write to. It downloads only from GitHub
over HTTPS and installs a file only when it matches checksums.txt.
Uninstalling¶
Apps, ArrowLoop, Uninstall asks for an administrator and removes the program,
its shortcuts and its entry, the scheduled task and %ProgramData%\ArrowLoop
with the switch and the log. The webview cache of the person uninstalling goes
as well. Every user's settings and jobs in %APPDATA%\ArrowLoop stay, so a
later install picks them up.
Trying it locally¶
A build from source is not a release and never updates itself. Only a build of a
version tag, stamped by scripts/desktop.mjs as the release workflow makes it,
does. Built with -tags updatetest, the app reads ARROWLOOP_UPDATE_API, a
stand-in for https://api.github.com, and ARROWLOOP_UPDATE_DELAY, the wait
before the first check (such as 15s), so the whole path can be tried locally.
node scripts/desktop.mjs --installer --updatetest builds an installer for
such a build. It installs as ArrowLoop Test, with a task and a folder under
ProgramData of that name, beside a real installation. Its task starts without
your environment and reads the stand-in's address from updatetest-api in
%ProgramData%\ArrowLoop Test. Release builds leave the tag out, so nothing on
the computer can change where an update comes from.
The builds are not signed
Windows shows its blue warning on first start (More info, then Run anyway),
and macOS needs a right-click and Open the first time. That is a deliberate
trade for now: a certificate is a recurring cost, and it is worth paying
once there are users to pay it for. For the same reason an update is
checked only against the release's checksums.txt, which proves the file
is the one the release published, not who built it.
As a single binary¶
The result needs nothing else installed. It carries the interface, the engine and every storage backend it supports.
Syncing between two places over the internet¶
ArrowLoop connects to the other side itself, over SFTP, SMB, WebDAV or a cloud's own interface. It has no relay and no account of its own, so a laptop away from home reaches the server at home only if that server can be reached. Opening its SSH port to the internet is the way not to do that.
Put both machines in one private network instead:
- Tailscale is the quickest. Install it on both machines and they reach each other by name, from anywhere, with no port forwarded. It needs a Tailscale account, its coordination servers introduce the machines, and its relays carry the traffic when no direct path exists. That is the same trade Syncthing and Resilio make, taken once for every program rather than per program. Headscale runs the coordination on your own server.
- WireGuard on its own needs nobody else's server, but one UDP port forwarded on the router at home.
Then add the server in ArrowLoop as usual, under Targets, Add a server or
share, with its name or address inside that network as the host: with
Tailscale, the machine name it shows (server or
server.your-tailnet.ts.net); with WireGuard, the address the tunnel gives it.
A job between the laptop and that target then runs the same at home and away.
While the laptop has no connection, its scheduled runs fail and say so, and
the next one that gets through catches up.