Configuring a job¶
A shell history is not a place to keep jobs. Somebody with a photo folder, a
documents folder and a server backup has three of them, each with its own
schedule, its own filters and its own idea of what may be deleted, so everything
past arrowloop sync works from a configuration file.
Jobs live in arrowloop.json. The Jobs page in the interface writes the same
file, through the same validator, so neither way can produce something the other
refuses. A complete example is
arrowloop.example.json.
{
"bwlimit": "",
"parallelJobs": 1,
"notify": {
"matrix": { "homeserver": "https://matrix.example.org", "room": "!abc:example.org", "token": "syt_..." },
"onSuccess": false
},
"jobs": [
{
"name": "photos",
"left": "/data/Photos",
"right": "sftp:backup/photos",
"state": "state/photos.db",
"schedule": "*/15 * * * *",
"watch": true,
"exclude": ["*.xmp", "**/.thumbnails/**"],
"emptyDirs": true
}
]
}
Everything is checked when the file is read, including cron expressions,
durations and misspelled field names. A program that runs unattended has to
fail on a typo when it starts, not at three in the morning on the one job that
mattered. JSON normally ignores a field it does not recognise, so "excludes"
instead of "exclude" would leave the filter empty and sync exactly the files
you thought you had excluded; an unknown field is refused instead. The one
exception is a retired field that older versions wrote, such as firstRun,
which is dropped rather than refused. Two engine-wide durations, retry.wait
and historyKeep, fall back to their defaults instead, so a typo there cannot
switch off retries or the trimming of the run log.
Paths resolve against the file itself, not against whatever directory a service manager happened to start the process in.
Per job¶
| Field | Default | What it does |
|---|---|---|
name |
required | How the job is asked for and how its history is kept apart. Two jobs cannot share one. |
left, right |
required | A local path or any rclone remote. Which is which makes no difference to the engine. Both naming one place is refused, also through a symlink, an alias or two remotes set up for the same server, bucket or drive. |
state |
required | Where this job remembers what the two sides agreed on. |
direction |
both ways | leftToRight or rightToLeft makes one side the source and never writes to it. Anything else, including an empty field, is both ways. |
mode |
sync |
What a one-way job does beyond copying: sync, mirror or move. The last two need a one-way direction; see Copy only, mirror or move. |
schedule |
none | A cron expression. Left out, the job takes the default schedule; written as "", it only runs when somebody asks, whatever the default says. |
watch |
false |
Run when a local side changes. Needs a schedule as well; see below. |
watchSettle |
2s |
How long the tree must go quiet before a change counts. |
runAtStart |
false |
Run once as soon as the program starts, before waiting for the schedule. Fires once per start and never on a configuration reload. |
disabled |
false |
Keeps the job in the file without running it on its own. It can still be started by hand. |
exclude |
none | Globs of paths to leave alone entirely; see Exclude patterns. |
excludeSets |
none | Names of shared exclude lists. Their patterns are added to exclude. |
noDefaultExcludes |
false |
Also sync half-written files such as *.part and Office owner files. |
quietPeriod |
5s |
How long a file must sit unchanged before it is touched. |
modWindow |
2s |
How far modification times may differ and still count as equal. Only applies where a side cannot produce a hash. |
transfers |
4 |
How many files may be copied at once. |
emptyDirs |
false |
Carry folders that hold no files. |
metadata |
false |
Carry permissions, ownership and extended attributes where both sides can. |
keepVersions |
0 |
Keep the last N contents of a file that gets overwritten; see Keeping old versions. |
foldCase |
ask the backends | true treats names that differ only in case as one file, false as two; see Upper and lower case. |
noTrash |
false |
Delete outright instead of moving into the side's own trash. See the note below before switching it on. |
newerWins |
false |
Settle a conflict for the version changed last and move the other into the trash, instead of keeping both. Two versions whose times lie within modWindow of each other still keep both. Only a job that writes both ways has conflicts. |
reportOnly |
false |
Compare on every automatic turn and apply nothing. A run somebody starts by hand still applies. |
brakePercent |
50 |
Hold a run deleting more than this share of known files. 0 switches the brake off. |
changePercent |
50 |
Hold a run overwriting more than this share of known files. 0 switches the brake off. |
brakeFloor |
10 |
Never trip either brake below this many deletions or overwrites. |
before |
none | A shell command run before every run. If it fails, the run does not start. See below. |
after |
none | A shell command run after every run, successful or not. If it fails, the run is marked failed. |
noTrash makes a deletion final
The trash is what makes the promise at the top of Safety hold:
nothing this program does destroys anything by itself. Switching it off
withdraws that promise for this job, and it also applies to the losing side
of a conflict, which is the case people forget. What you get back is the
.arrowloop folder never being created, which matters on a share other
people can see. Reasonable for a folder of downloads. A bad idea for
documents.
The field is spelled as the negative so that a configuration written before it existed, or one where somebody forgot the line, keeps its trash. In the interface the switch reads the other way round and says "keep a bin", since a switch labelled with a negative is one people set backwards.
Zero is not the same as leaving it out
brakePercent, changePercent and brakeFloor distinguish an explicit 0
from an absent field on purpose. Switching off a brake has to be something
you typed, never something you got by forgetting a line.
A one-way job restores, it does not skip
Both sides are still compared, because comparing is how the engine knows
what changed. What the direction decides is what may be done with the
answer. A file edited on the destination is restored from the source and a
file deleted there is copied back, so the destination is made to agree.
Skipping those changes instead would report them again on every run and the
two sides would drift further apart for ever. What the source never had is
left alone, since nothing there says it should exist, unless the job's
mode is mirror.
Copy only, mirror or move¶
mode says what a one-way job does beyond copying. The job form offers it as
Mode, with Copy only, Mirror and Move, and it can also be set
in the defaults.
sync, the default and Copy only in the interface, copies what the source
has and removes nothing. A file only the destination has stays there, and a
file deleted on the source keeps its copy on the destination while the record
lets go of it.
mirror makes the destination an exact copy of the source. A file the source
does not have is deleted there, whether the source deleted it or never had it,
and with emptyDirs a folder gone from the source goes too. Each deletion is an
ordinary one: it goes into that side's trash unless the job has noTrash, and
it counts towards the mass-delete brake, in case the two sides were set up the
wrong way round. A source that lists no files at all while the destination
holds some is refused, since mirroring it would empty the destination.
move copies a file across and then takes it off the source. Both happen as
one action, so the original only leaves once the copy has landed, and it goes
into the source side's trash like every other removal. A file both sides already
hold leaves the source as well, but only when the two checksums match. If both
sides changed a file, the source's version wins, and the destination's is kept
beside it under a conflict name rather than overwritten. A deletion on the
source is not carried over: the destination keeps its copy, as with sync.
mirror and move both decide which side is right, so a job that runs both
ways cannot use either and is refused when the file is read. So is any value
other than these three, because two of them delete, and a misspelt one must not
quietly run as a plain copy.
Exclude patterns¶
Patterns work much like gitignore. Without a slash they match the file name
at any depth, so *.tmp catches a/b/c.tmp; with a slash they match the whole
path relative to the side's root. ** crosses directory separators, * and ?
do not, and naming a directory hides everything under it.
An exclude hides a path from the job entirely, on both sides and in the record, which is why adding one never deletes anything; see Safety. The names programs use while still writing are excluded by default, and Safety lists them.
Shared exclude lists¶
A list of patterns that several jobs need is written once, at the top of the
file under excludeSets, and asked for by name. The alternative is the same
twenty lines pasted into every job, drifting apart until two jobs that were
meant to ignore the same things quietly stop doing so.
{
"excludeSets": {
"junk": ["Thumbs.db", ".DS_Store", "**/.thumbnails/**"]
},
"jobs": [
{
"name": "photos",
"left": "/data/Photos",
"right": "sftp:backup/photos",
"state": "state/photos.db",
"exclude": ["*.xmp"],
"excludeSets": ["junk"]
}
]
}
A list's patterns are added to the job's own exclude rather than replacing
it. A job asking for a list that does not exist is refused when the file is
read, because a filter that silently matches nothing breaks nothing anybody
would notice; it syncs the thing somebody asked it to leave alone. In the
interface the lists are kept on the Sync tile under Settings.
Keeping old versions¶
"keepVersions": 5 keeps the last five contents of a file that a run
overwrites. Before the overwrite, the old file is moved into
.arrowloop/versions/ on that side, which the scanner skips just as it skips
the trash, so a kept version never travels. If the old file cannot be kept, it
is not overwritten either: the file is postponed and the next run tries again.
Only overwrites are kept. A conflict already keeps both versions, and a rename
only ever lands on a free name. It is off by default, since most trees are never
edited in place and a copy of every overwrite would double them. 1 keeps only
the version about to be overwritten.
It is set in the file or in the defaults; the
interface has no control for it, and arrowloop sync has no flag for it.
Upper and lower case¶
Whether a job matches names case-insensitively is read from its two backends:
if either cannot tell Bild.jpg from bild.jpg, the job folds case on both
ends. How it decides explains why it
has to be one answer for the whole job.
A backend can be wrong about itself. A Windows share mounted on Linux reports
itself case-sensitive and is not, and the pair then grows by one file per run.
"foldCase": true makes the job treat names that differ only in case as one
file, and false as two, whatever the backends say. Leaving it out asks the
backends. It can be set on a job or in the defaults, where the interface calls
it Upper and lower case, with Ask the sides, Always one file and
Always two.
Watching¶
"watch": true starts a run when a local side changes, instead of waiting for
the next tick. The reason is not latency: a schedule has to list both sides in
full on every tick, which on a large tree or over a network is most of what a
run costs, and watching lets the engine sit still until there is a reason not
to.
It never replaces the schedule, and a watching job without one is refused. Only a local side can be watched, most remote backends have no way to tell anyone anything, and a watcher that missed an event has no way to know it did. The schedule is what eventually notices what the watcher did not.
In the interface, real time is one of the schedules rather than a switch beside
them. Picking Real time in the schedule strip sets "watch": true and writes
a backstop as well: an hourly schedule, shown beside it and open to change.
Events are collected over a settle window, watchSettle, because copying a
folder in produces one event per file and the interesting fact is that something
changed. On Linux and macOS a new folder is watched as it appears, since the
kernel reports on a directory's own entries rather than its whole subtree; on
Windows one watch covers each side's whole tree. The tool's own trash and every
excluded path are ignored, and a job's watcher is muted around its own runs, or
the engine would spend its life answering its own writes.
A side whose folder is not there, because its drive is unplugged or the folder has not been made yet, is left out and the other side is watched meanwhile. The engine looks again every minute and starts watching the folder once it turns up, and the same happens to a folder that goes away while watched. The log says once that a folder is missing and once that it is watched. A watcher that will not start for any other reason is tried again after a minute, then after twice as long each time, up to an hour.
Running at start¶
"runAtStart": true runs the job once as soon as the program starts, before its
schedule is next due.
It exists because of what a schedule cannot say. A machine that was off overnight missed every turn a daily job had, and the job's next run is tomorrow: the two sides stay apart for a whole day for no reason other than the clock. This is also the setting that makes autostart worth switching on, since a program that starts with the session and then sits there until 03:00 has not helped anybody who has just turned their computer on.
Three things it does not do. It fires once per program start
and never on a configuration reload, so saving one job in the interface does not
set every job in the file running. A disabled job does not run, because
disabled has to mean disabled everywhere. And the runs go one after another in
file order rather than all at once, for the same reason ordinary runs are
serialised: they share one uplink and one disk, and a start-up burst is where
that matters most.
A job never overlaps itself¶
A job set to run every fifteen minutes that takes twenty lets the next turn go
by, with a line in the log saying so. Queueing instead would let a job that is
merely too slow build an unbounded backlog of itself. Jobs run one at a time by
default (parallelJobs), because two of them share one uplink and one disk.
After a failure¶
A scheduled job that fails stays due until it succeeds, and retry limits how
hard it tries. It gets attempts further tries, three by default. The first
waits for wait, five minutes by default, and each one after that waits twice
as long as the one before, up to six hours. With the defaults the tries wait
five, ten and twenty minutes, so a nightly job still has the night to finish in.
After the last one the job runs once at each scheduled time, and a success
starts the count again. 0 attempts means no further tries, and a wait that
cannot be read counts as five minutes.
It is one setting for the whole file rather than per job, because how patient to be after a failure depends on the machine, not on a folder pair. In the interface it sits on the Sync tile under Settings, in Retries, with the wait in minutes.
On a computer or in a container the scheduler looks once a minute for a failed job whose wait is over. On Android the same rule decides what runs when the app wakes the engine, and without the limit a failed job would run at every wake-up, spending a night's battery on a remote that is still down.
Anomalies¶
"anomalies": { "off": false, "failingAfter": 3, "overdueDays": 2, "unusual": true, "notifyNotes": false }
A run a brake refuses holds its job until somebody answers it; see
A refused run holds the job. The brakes
themselves are set per job, with brakePercent, changePercent and
brakeFloor, because what is implausible depends on the folder: a download
folder is emptied on purpose, a photo archive is not. In the interface a job's
own brakes are set on the job's page, in its Brakes card, with
Override.
anomalies sets the notes that hold nothing. failingAfter is how many
failed runs in a row make one, and overdueDays how many days late a scheduled
run may be. The days count from the scheduled run that was missed, not from the
last run, so a job that runs once a week is overdue only when its weekly run is
that many days late. 0 switches either off. unusual says whether a run far
outside what its job usually does makes a note, which it does unless set to
false; see Unusual runs. "off": true switches
all three off together with the Anomalies page. The brakes keep holding then, since they
are set on their own; a held job is answered from its own status. notifyHeld and notifyNotes
say whether a held job and a note are sent where runs report to; a held job is
by default and a note is not. The desktop app also shows them as a notification
of the system. On Windows and macOS it plays ArrowLoop's own sound, which keeps
quiet while Windows does not disturb; on Linux it asks for the warning sound of
the desktop's sound theme.
The findings are kept in anomalies.json beside the configuration, a closed
one for thirty days. In the interface they have a page of their own.
Commands before and after a run¶
{
"name": "database",
"left": "/srv/dump",
"right": "b2:backup/dump",
"state": "state/database.db",
"before": "pg_dump -U app app > /srv/dump/app.sql",
"after": "/usr/local/bin/report-backup.sh"
}
before runs before the two sides are compared. It is the place to write a
database dump, stop a service that holds files open, or mount a drive. If it
exits with anything other than zero, the run does not start, and the run record
carries the last lines the command printed, since that is usually where a
script says why it gave up.
after runs once the run is over, whether it went well or not, so it can
restart what before stopped. It learns how the run went from its environment:
| Variable | What it holds |
|---|---|
ARROWLOOP_JOB |
The job's name. Also set for before. |
ARROWLOOP_LEFT, ARROWLOOP_RIGHT |
The two sides as written in the file. Also set for before. |
ARROWLOOP_RESULT |
ok or failed. |
ARROWLOOP_ERROR |
Why the run failed, empty otherwise. |
ARROWLOOP_COPIED, ARROWLOOP_MOVED, ARROWLOOP_DELETED |
How many files the run copied, moved and deleted. |
ARROWLOOP_CONFLICTS, ARROWLOOP_SKIPPED |
How many files were in conflict and how many were left for later. |
A command that exits with an error after a successful run marks the run failed,
so a notification goes out for it. The commands run through sh -c, or
cmd /C on Windows, in the program's working directory. Each may take fifteen
minutes; after that it is stopped, because a hanging command would hold its
job's place and every job queued behind it. A command that is stopped, at that
limit or because the run was stopped, takes every program it started with it.
A program that a command which finished cleanly left running in the background,
such as an SSH tunnel, keeps running.
Only the file can set them
Anybody who can reach the interface could otherwise run anything on this machine, with the rights of the process. So the interface shows the commands but cannot set or change them: every save from the interface puts back what the file on disk says for a job of the same name. A job created or renamed in the interface starts without commands. The interface and the file agree on everything else.
Every run is written down¶
The run log keeps successes as well as failures. A crash is loud; the failure
worth guarding against is a job that has been failing quietly since Tuesday
because a path changed. That is also why arrowloop jobs shows when each job
last succeeded rather than when it last ran; see
the command line.
A machine nobody looks at keeps writing records, and a watching job writes one
per change, so the program trims the log once a day while it runs. A record is
kept for ninety days unless historyKeep says otherwise, as a duration in hours
such as "720h" for thirty days, since there is no unit for days. "0" keeps
every record. A value that cannot be read counts as ninety days, so a typo never
switches trimming off. arrowloop daemon also trims once as it starts, by the
same value unless its -keep-history flag gives another.
Whole file¶
| Field | Default | What it does |
|---|---|---|
bwlimit |
none | rclone syntax, 1M or a timetable like 08:00,512k 19:00,off. |
parallelJobs |
1 |
How many jobs may run at once. |
history |
beside the file | Where run records go. A change takes effect the next time the program starts. |
historyKeep |
ninety days | How long a run record is kept, such as "720h". "0" keeps every record; see Every run is written down. |
notify.on |
failure |
When a message goes out: never, failure or always; see Notifications. |
notify.webhook |
none | An address a message is posted to, shaped by notify.webhookFormat: generic, discord, slack, gotify or ntfy. |
notify.matrix |
none | A room to post into: homeserver, room id, access token. |
notify.smtp |
none | A mail server and the addresses a plain-text mail goes from and to. |
notify.apprise |
none | The notify endpoint of an Apprise API server, and optional tags. |
notify.healthchecks |
none | A Healthchecks.io check every job pings, and under jobs a check of a job's own. |
notify.onSuccess |
false |
The older way to say "on": "always". |
retry.attempts |
3 |
How many further tries a failed scheduled run gets before the job waits for its next scheduled time. 0 means none; see After a failure. |
retry.wait |
5m |
How long to wait before the first further try. Each try after that waits twice as long, up to six hours. |
anomalies.failingAfter |
3 |
How many failed runs in a row make a note. 0 means never; see Anomalies. |
anomalies.off |
false |
Switch the notes and the Anomalies page off. The brakes keep holding. |
anomalies.unusual |
true |
Note a run far outside what its job usually does; see Unusual runs. |
anomalies.overdueDays |
2 |
How many days late a scheduled run may be, counted from the run that was missed, before it makes a note. 0 means never. |
anomalies.notifyHeld |
true |
Report a job a brake holds where runs report to. |
anomalies.notifyNotes |
false |
Report the notes there as well. |
defaults |
none | Values for every job that does not set its own; see below. |
excludeSets |
none | Named lists of exclude patterns; see Shared exclude lists. |
keepJobOrder |
false |
List the jobs in the order they stand in jobs rather than by name. The interface sets it the first time a job is moved, and a new job goes to the end. |
autoBackup.off |
false |
Switch the automatic backups off; see Automatic backups. |
autoBackup.everyDays |
1 |
How many days pass between two automatic backups, up to 365. |
autoBackup.at |
03:00 |
The time of day an automatic backup is due, as HH:MM on the engine's clock. |
autoBackup.keep |
7 |
How many automatic backups stay in the folder, up to 1000. |
autoBackup.folder |
backups beside the file |
Where the automatic backups go. A relative path is relative to the file. |
The bandwidth limit is not per job
rclone's token bucket is process-wide, so a per-job limit would be a promise the mechanism underneath cannot keep. Two jobs on one machine share one uplink either way.
The limit is checked when the file is read, like everything else, and a limit saved on the General tile applies to the running process at once. Everything else is per job, and each job gets its own copy of rclone's settings, so a job asking for eight transfers does not change what another job is doing.
Notifications¶
"notify": {
"on": "failure",
"webhook": "https://ntfy.sh/my-arrowloop-topic",
"webhookFormat": "ntfy",
"matrix": { "homeserver": "https://matrix.example.org", "room": "!abc:example.org", "token": "syt_..." },
"smtp": {
"host": "mail.example.org", "port": 587, "tls": "starttls",
"username": "arrowloop", "password": "...",
"from": "arrowloop@example.org", "to": "me@example.org, backup@example.org"
},
"apprise": { "url": "http://apprise:8000/notify/arrowloop", "tags": "backups" },
"healthchecks": {
"url": "https://hc-ping.com/<uuid>",
"jobs": { "photos": "https://hc-ping.com/<another uuid>" }
}
}
on says when a message goes out: never, failure or always. The default
is failure, because a tool that announces every successful sync teaches you to
ignore it, and then the one message that mattered is ignored with the rest. A
file written before on existed says "onSuccess": true for always, which
still works; when both are set, on decides.
A failed run is reported, a successful one only under always. A job a brake
holds is reported unless anomalies.notifyHeld is false, and the notes about
a job that keeps failing, a job that is overdue and an unusual run are reported
when anomalies.notifyNotes is true. never silences all of it, Healthchecks
included.
Each channel has an enabled switch, which turns it off and keeps what it
holds. A channel without the switch sends once it is filled in, so a file from
before the switch keeps working unchanged.
| Channel | Fields | What it sends |
|---|---|---|
| Webhook | webhook, webhookFormat, webhookEnabled |
A POST to the address. generic (the default) sends {"subject", "body", "ok"}, discord a content and slack a text. gotify sends a title, the message and priority 8 for bad news or 5 otherwise; the address carries the app token as ?token=. ntfy posts the text to the topic's address with a Title header and high priority for bad news. |
| Matrix | matrix.homeserver, matrix.room, matrix.token, matrix.enabled |
A message in the room, posted as the account the token belongs to. The account has to be in the room. |
smtp.host, smtp.port, smtp.tls, smtp.username, smtp.password, smtp.from, smtp.to, smtp.enabled |
A plain-text mail. tls is starttls (the default, port 587), tls for a connection encrypted from the start (port 465) or none. Under starttls a server that does not offer STARTTLS is refused rather than sent the password in clear. to takes several addresses separated by commas or semicolons, and ArrowLoop logs in only when username is set. |
|
| Apprise | apprise.url, apprise.tags, apprise.enabled |
A POST to the notify endpoint of an Apprise API server you run, which passes it on to whatever its key is set up for. tags limits a shared key to some of its targets. |
| Healthchecks.io | healthchecks.url, healthchecks.jobs, healthchecks.enabled |
A ping to <url>/start when a run begins, then <url> when it succeeds or <url>/fail when it does not, with the run's summary as the body. jobs gives a job its own check, by job name; every other job pings url. Any server that speaks the same protocol works. |
A job held by a brake has not synced, so its check gets /fail, and so does a
run whose drive went missing after the start was pinged. A drive that is
missing before anything starts is no run, and the check hears nothing; it goes
late on its own schedule if that keeps happening. A check under jobs follows
the name, so a renamed job pings the shared check until its entry is renamed
too.
Every send is limited to fifteen seconds, the channels are sent to at the same
time, and a failure at one does not stop the others. A message that cannot be
delivered is written to the log and changes nothing about the run. The log
names the channel and its host and says what went wrong, a refused connection or
the status the server answered, but never the address itself, since a webhook,
an Apprise key and a Healthchecks check all carry their secret in it. A
password written into an address in a run's error, such as
sftp://user:secret@host, is replaced by *** before the message leaves.
The Matrix token, the webhook address, the mail password, the Apprise address
and every Healthchecks address stay on the server. The Notifications card on
Safety net shows them as Saved with Change; the settings API and
the bug report hand out ******** in their place, and saving the page back
keeps what is stored. Every channel on the card has Send a test, which sends
through that channel as the card shows it, saved or not, ignoring on and the
channel's switch, and says why when it fails. The phone has the same channels
under Global sync settings, since its engine is the same program and sends them itself.
No Unraid notifications
Unraid's own notifications come from its notify script, a PHP script that
reads the host's configuration and runs the host's mail and agent setup. A
container cannot run it. Writing notification files into the host's
/tmp/notifications through a mount would skip the agents and the mail
Unraid is set up for, and the Unraid API needs an API key, which is a
credential for the host. Apprise or ntfy reach the same phone without
either.
Defaults and the settings tiles¶
The settings at the top of the file have a place in the interface as well as in
the file, each on a tile under Settings. The bandwidth limit, how many jobs
may run at once and where the run log lives are on General; the defaults
below, what happens after a failure and the shared exclude lists are on
Sync; the two brakes, the anomaly notes and who gets told are on Safety
net. historyKeep has no control there and is set in the file.
What is usually the same for every job lives in defaults, and a job keeps only
what makes it different, so the job form stays short:
defaults takes direction, mode, schedule, quietPeriod, modWindow,
transfers, emptyDirs, metadata, brakePercent, changePercent, brakeFloor,
keepVersions and foldCase, with the meaning they have on a job. They are
filled in when the file is read and before it is checked, so a value from the
defaults goes through the same rules as one written on the job.
An empty text or a zero number on a job counts as unset and takes the default,
apart from schedule, where "" means the job runs only when asked. The
switches and the brakes, which can be written as false or 0, keep what the
job states, so a job can switch off what the defaults switch on.
keepVersions cannot be switched off that way: zero means both off and unset,
so a job cannot turn versioning off against a default that turns it on. watch
and noTrash have no default, since a default that switched either on could not
be switched off for a single job.
Changing a default changes every job that never disagreed with it, and none of the jobs that did. In the interface a job shows each of these values locked until Override gives it one of its own, and Exceptions on the Sync and Safety net tiles lists the jobs that have one.
Backup and restore¶
Settings backup, on the General tile under Settings, has two rows. Back up all jobs and all settings exports them to a single JSON file, and Restore all jobs and all settings imports one back, so a new machine is one restore away. The phone's card uses the same two rows. It writes to Downloads unless another folder is typed or chosen with Choose a folder, and Choose a file to import opens Android's own file picker, which also reaches the files in a cloud app.
Importing replaces everything and asks first, with the file's name in the
question, because the one thing nobody can do afterwards is get the old setup
back. The imported file is checked like every other write, so a file the
program would refuse leaves the old setup in place. Like every save from the
interface, it keeps the before and after commands the file on disk has for a
job of the same name and gives every other job none. The password, the second
factor and the passkeys live in security.json and are not part of the backup.
Automatic backups¶
Automatic backups, the card below, keeps copies of arrowloop.json without
anybody having to think of it. It is on from the start: every night at 03:00
the engine looks at the file and, if it changed since the newest copy, writes a
new one into the backups folder beside it, named after the moment it was
made, such as arrowloop-auto-20261107-031500.json. A file that has not
changed is not copied again, so the copies reach back as far as the edits do.
Right before an import replaces everything, or one from FolderSync or
luckyBackup adds to it, the engine keeps the setup as it was the same way,
whatever the day's copy did. If that copy fails, the import still goes ahead
and the card shows the failure.
Days between backups, Time of day, Backups to keep and Backup folder set how often, when, how many and where. Choose a folder beside the folder opens the folder picker on the machine the engine runs on. The days are counted on the calendar and the time is read off the clock of the machine the engine runs on, the same way the jobs' schedules read it, so a backup stays at its time when the clocks change for summer. A computer or phone that is off or asleep at that time makes the backup as soon as it is back, once, and the next one comes at the usual time again. Once there are more copies than you keep, the oldest goes. Only files named like the engine's own copies are ever removed, so an export you save in the same folder stays. A folder the engine cannot write to is refused when you enter it, and a backup that fails later is shown on the card and tried again an hour after.
Each copy is the file byte for byte, the same thing Back up all jobs and all
settings downloads, so it carries the notification secrets as the export does
and is written readable by the account running ArrowLoop alone. The password and
the targets' own settings in rclone.conf are not part of it. Latest backup
shows when the newest one was made. To put a copy back, choose it with Import
settings, which asks first like any import. The phone app has the same card under Settings.
On the phone the copies stay inside the app unless you choose a folder in
Download or Documents, which needs access to all files.
Moving over from FolderSync or luckyBackup¶
The same Import settings button takes two files from other programs and tells them apart by what is in them:
- FolderSync: the JSON file that Export configuration under Settings › Advanced writes into FolderSync's backup folder. FolderSync's documentation lists it for the Business edition. Its Backup database file is a database rather than a configuration and cannot be read.
- luckyBackup: a
.profilefile from~/.luckyBackup/profiles/. A profile from before luckyBackup 0.4.2 is binary; open it in a newer luckyBackup and save it once.
Nothing is replaced. Before anything is saved, a window lists the jobs and targets that would be added, what still has to be filled in, and every setting that stays behind because ArrowLoop has nothing like it, quoted as the file spells it so it can be found there. A name that is already taken gets a number.
| From | Becomes |
|---|---|
| FolderSync folder pair, luckyBackup task | a job; one-way or two-way as before |
| FolderSync account | a target: SFTP, FTP, SMB, WebDAV (Nextcloud, ownCloud), S3, Dropbox, Google Drive, OneDrive, pCloud, Box, Yandex, HiDrive, Koofr, MEGA, Proton Drive, Google Cloud Storage; the phone's own storage stays a folder |
| luckyBackup SSH destination or source | an SFTP target with the host, user, port and key file |
| FolderSync sync interval, or a schedule in standard cron | the job's schedule |
| FolderSync instant sync, folder monitoring | watching, with an hourly schedule behind it |
| FolderSync "delete after sync", move mode | mode move |
| FolderSync deletions on a one-way pair, luckyBackup "delete files on the destination" | mode mirror |
| FolderSync "overwrite oldest" on a conflict | newerWins |
| FolderSync name and folder filters, luckyBackup excludes and its exclude check boxes | exclude patterns |
| luckyBackup snapshots | keepVersions, one fewer than the snapshots |
No password, key passphrase, secret key or token is carried over, even where the file holds one. Each target that needs one is named in the window, and is filled in or signed in again on Targets. Every imported job arrives paused, since its folders come from another machine; look over each one and resume it. luckyBackup keeps its schedule in cron, outside the profile, so its jobs follow the default schedule.
What stays behind includes FolderSync's webhooks, notifications, network and
charging conditions, time-stamped backup folders, include filters and filters
by size, age or regular expression, and luckyBackup's commands before and after
a task, include lists, extra rsync options and rsync daemon modules. The
commands stay behind because ArrowLoop runs a command only when it is written
into arrowloop.json by hand.
Services that sign in through a browser¶
Every backend rclone ships is compiled in. The services that only sign in
through a browser, such as OneDrive, Google Drive and Dropbox, take a token made
once with rclone authorize, because the engine often runs on a machine that
has no browser to open.
Run rclone authorize "<backend>" on a computer that has a browser. It opens
the provider's sign-in page and prints a token when you are done; paste that
whole line into the token field when you add the service with Connect
storage and clouds on Targets. The
client id and secret below it are optional and only matter if you registered
your own application with the provider.