# Restray

A simple Go application that runs in the background/system tray, that can schedule and manage [restic](https://restic.net) backups and profiles easily. It can also use multiple profiles with different schedules and settings at once. It's also pretty configurable, see config below.

Restray is intended for desktops. Runs on MacOS, Windows, and Linux. If you're on GNOME, you will need to install [this extension](https://extensions.gnome.org/extension/615/appindicator-support) to interact with and use the app since GNOME doesn't natively support the system tray.

I needed a replacement for Time Machine, since it's painfully slow, only exclusive to Macs, and only supports SMB now. I quite like it's simplicity and ease-of-use though. Looking for other solutions, restic appeared to be a great fit, and I already use it for my server. However, I didn't find any option for scheduling restic backups that I actually liked. I did see [resticprofile](https://github.com/creativeprojects/resticprofile) which looks cool, and may be a better solution for some, please do check it out! But I'd still prefer a simple GUI personally. So... here's Restray which should hopefully solve that :)

This is new and somewhat work-in-progress/beta software. You might possibly encounter a bug or two that I haven't fixed yet, and if so, please make an issue. I don't really intend on adding many new features, but I will fix bugs and refine the codebase more over time.

| MacOS                                                     | Linux                                                     | Windows                                                       |
| --------------------------------------------------------- | --------------------------------------------------------- | ------------------------------------------------------------- |
| ![Preview of Restray on MacOS](.static/preview-macos.png) | ![Preview of Restray on Linux](.static/preview-linux.png) | ![Preview of Restray on Windows](.static/preview-windows.png) |

## Features

- Initialize a repository if one doesn't already exist
- Run backups on a schedule with cron expressions, or run the full schedule manually
  - Can detect battery power too and temporarily disable schedule
- Multiple backup profiles with different schedules and different settings
- Optional automatic pruning and checks after backups
- Operations menu for running individual operations (backup, prune, check, unlock, mount, shell, hooks)
- Optional automatic retry and removal of stale locks
- On Windows it can also download and update it's own self-managed restic if not found in PATH
- Open a shell with your profile's environment and restic binary loaded, for restoring or other CLI operations without friction
- Mount repository snapshots on macOS/Linux
  - Requires [FUSE-T](https://www.fuse-t.org) or [macFUSE](https://macfuse.github.io) on macOS. Requires [FUSE](https://github.com/libfuse/libfuse) on Linux
  - I'd recommend something like [restic-browser](https://github.com/emuell/restic-browser) to browse on Windows, or just open the Shell and use the CLI
- Full Disk Access detection and prompt on macOS
- Config file hot-reloading

## Installation

Grab a build from this repository's [tags](https://tangled.org/devins.page/restray/tags) (MacOS/Windows packaged, Linux as a binary). On MacOS you'll need to [disable Gatekeeper/remove quarantine](https://disable-gatekeeper.github.io) from the `.app`, since I can't pay Apple $100/year to sign it. Sorry.

Restray needs [restic](https://restic.net) installed to your PATH on MacOS/Linux (on MacOS, self-managed copy gets killed by code-signing requriment), so install it with your package manager (e.g. `brew install restic` or `apt install restic`). Package managers should also just pull it in as a dependency. On Windows, Restray can download and manage it's own restic binary itself if it isn't already in PATH.

### Building from source

On [Nix(OS)](https://nixos.org)/nix-darwin, run `nix build` (or add this repo to your flake and simply install the package). Otherwise, install the dependencies from `flake.nix`'s devshell and run `just build` or `just package`. Both default to your current platform. You can also pass a target to cross-compile (e.g. `just build linux amd64`, `just package windows`).

## Configuration

### `[general]`

| Key                | Type   | Default         | Description                                                                                                                                                           |
| ------------------ | ------ | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `check_updates`    | bool   | `true`          | **MacOS/Windows only.** Check for new Restray versions on startup and periodically. When an update is found, the version item in the menu changes to an update button |
| `manage_restic`    | bool   | `false`         | **Windows only.** If restic is not found in PATH, automatically downloads and updates a self-managed restic binary daily from GitHub.                                 |
| `notifications`    | string | `"none"`        | `"all"` (success + errors), `"errors"` (errors only), or `"none"` (silent)                                                                                            |
| `schedule_display` | string | `"description"` | `"description"` (human-readable, e.g. "every 6 hours"), `"cron"` (raw expression, e.g. "0 \* \* \* \*"), or `"none"`                                                  |
| `terminal`         | string | `""`            | Terminal emulator used in "Shell" and "View Log". If unset, MacOS will use `Terminal.app`. Linux will use default. Not used on Windows. e.g. "Ghostty"                |

### `[[profiles]]`

You can create multiple of these `[[profiles]]` sections for separate backup targets. Each profile has its own schedule, repository, environment, etc.

| Key          | Type   | Default                   | Description                                                                                                                                                                                              |
| ------------ | ------ | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`       | string | `"default"`               | Name for the profile                                                                                                                                                                                     |
| `env_file`   | string | `<config_dir>/<name>.env` | Path to the environment file with `RESTIC_REPOSITORY`, `RESTIC_PASSWORD`, etc.                                                                                                                           |
| `retry_lock` | string | `"2m"`                    | How long to retry acquiring a repository lock (e.g. `"5m"`, `"30s"`). Will automatically attempt to clear stale locks after this timeout. Set to `"0"` to disable                                        |
| `pre_hook`   | string | `""`                      | Shell command to run before a scheduled run. Inherits the profile's environment and restic binary. The schedule is aborted if the hook exits non-zero. Can also be run manually from the Operations menu |
| `post_hook`  | string | `""`                      | Shell command to run after a scheduled run. Inherits the profile's environment and restic binary. Can also be run manually from the Operations menu                                                      |

Hooks receive the following environment variables when run as part of a schedule:

| Variable             | Description                                                                            |
| -------------------- | -------------------------------------------------------------------------------------- |
| `RESTRAY_PROFILE`    | Name of the active profile (always set, including manual runs)                         |
| `RESTRAY_OPERATIONS` | Comma-separated list of operations enabled in the schedule (e.g. `backup,prune,check`) |
| `RESTRAY_SCHEDULED`  | `true`                                                                                 |
| `RESTRAY_ERROR`      | Error message if the schedule failed; unset on success (post-hook only)                |

#### `[profiles.schedule]`

| Key          | Type   | Default | Description                                                                                                                                        |
| ------------ | ------ | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `cron`       | string | `""`    | Set to an empty string to disable the schedule entirely. Cron expression for profile schedule (for help, see [crontab.guru](https://crontab.guru)) |
| `on_battery` | bool   | `false` | Whether to run schedule while on battery power                                                                                                     |
| `backup`     | bool   | `true`  | Run backup on schedule                                                                                                                             |
| `prune`      | bool   | `false` | Run prune on schedule                                                                                                                              |
| `check`      | bool   | `false` | Run check on schedule                                                                                                                              |

#### `[profiles.backup]`

| Key              | Type     | Default                                      | Description                                                               |
| ---------------- | -------- | -------------------------------------------- | ------------------------------------------------------------------------- |
| `paths`          | string[] | `[]`                                         | Paths to back up, e.g. `["/Users/user/Documents", "/home/user/Projects"]` |
| `args`           | string[] | `["--exclude-caches", "--exclude", "*.tmp"]` | Extra arguments passed to `restic backup`                                 |
| `args_scheduled` | string[] | `["--tag", "scheduled"]`                     | Extra arguments appended (not replaced) when running from schedule        |

#### `[profiles.prune]`

| Key    | Type     | Default                                                                                                          | Description                                       |
| ------ | -------- | ---------------------------------------------------------------------------------------------------------------- | ------------------------------------------------- |
| `args` | string[] | `["--keep-last", "4", "--keep-hourly", "24", "--keep-daily", "7", "--keep-weekly", "4", "--keep-monthly", "12"]` | Extra arguments passed to `restic forget --prune` |

#### `[profiles.check]`

| Key    | Type     | Default | Description                                                       |
| ------ | -------- | ------- | ----------------------------------------------------------------- |
| `args` | string[] | `[]`    | Extra arguments passed to `restic check` (e.g. `["--read-data"]`) |

#### `[profiles.mount]`

| Key    | Type     | Default | Description                                                         |
| ------ | -------- | ------- | ------------------------------------------------------------------- |
| `args` | string[] | `[]`    | Extra arguments passed to `restic mount` (e.g. `["--allow-other"]`) |

## Credits

- [github.com/fyne.io/systray](https://github.com/fyne-io/systray) - cross-platform system tray handler
- [github.com/BurntSushi/toml](https://github.com/BurntSushi/toml) - parse TOML
- [github.com/distatus/battery](https://github.com/distatus/battery) - battery status for schedule
- [github.com/fsnotify/fsnotify](https://github.com/fsnotify/fsnotify) - hot-reloading
- [github.com/gen2brain/beeep](https://github.com/gen2brain/beeep) - cross-platform notification handler
- [github.com/robfig/cron](https://github.com/robfig/cron) - cron scheduler
- [github.com/lnquy/cron](https://github.com/lnquy/cron) - convert cron to human-readable expression
