powerful but friendly backup program that runs in your tray, powered by restic devins.page/restray
go restic system-tray
2

Configure Feed

Select the types of activity you want to include in your feed.

feat: rewrite and fix up restic management

also add a neat little download animation

intergrav (Jun 28, 2026, 11:02 PM EDT) 13aae5b6 92a24df4

+38 -21
+1
.gitignore
··· 4 4 dist/ 5 5 result 6 6 Restray 7 + Restray.exe 7 8 restray_windows_*.syso
+2 -1
config.go
··· 434 434 go func() { 435 435 var debounce *time.Timer 436 436 for event := range watcher.Events { 437 - if event.Name == logPath() { 437 + ext := filepath.Ext(event.Name) 438 + if ext != ".toml" && ext != ".env" { 438 439 continue 439 440 } 440 441 action := event.Has(fsnotify.Write) || event.Has(fsnotify.Create) ||
icons/download.png

This is a binary file and will not be displayed.

icons/generated/linux-download-00.png

This is a binary file and will not be displayed.

icons/generated/linux-download-01.png

This is a binary file and will not be displayed.

icons/generated/linux-download-02.png

This is a binary file and will not be displayed.

icons/generated/linux-download-03.png

This is a binary file and will not be displayed.

icons/generated/linux-download-04.png

This is a binary file and will not be displayed.

icons/generated/linux-download-05.png

This is a binary file and will not be displayed.

icons/generated/linux-download-06.png

This is a binary file and will not be displayed.

icons/generated/linux-download-07.png

This is a binary file and will not be displayed.

icons/generated/linux-download-08.png

This is a binary file and will not be displayed.

icons/generated/linux-download-09.png

This is a binary file and will not be displayed.

icons/generated/linux-download-10.png

This is a binary file and will not be displayed.

icons/generated/linux-download-11.png

This is a binary file and will not be displayed.

icons/generated/linux-download-12.png

This is a binary file and will not be displayed.

icons/generated/linux-download-13.png

This is a binary file and will not be displayed.

icons/generated/linux-download-14.png

This is a binary file and will not be displayed.

icons/generated/linux-download-15.png

This is a binary file and will not be displayed.

icons/generated/linux-download-16.png

This is a binary file and will not be displayed.

icons/generated/linux-download-17.png

This is a binary file and will not be displayed.

icons/generated/linux-download-18.png

This is a binary file and will not be displayed.

icons/generated/linux-download-19.png

This is a binary file and will not be displayed.

icons/generated/linux-download-20.png

This is a binary file and will not be displayed.

icons/generated/linux-download-21.png

This is a binary file and will not be displayed.

icons/generated/linux-download-22.png

This is a binary file and will not be displayed.

icons/generated/linux-download-23.png

This is a binary file and will not be displayed.

icons/generated/linux-download-24.png

This is a binary file and will not be displayed.

icons/generated/linux-download-25.png

This is a binary file and will not be displayed.

icons/generated/linux-download-26.png

This is a binary file and will not be displayed.

icons/generated/linux-download-27.png

This is a binary file and will not be displayed.

icons/generated/linux-download-28.png

This is a binary file and will not be displayed.

icons/generated/linux-download-29.png

This is a binary file and will not be displayed.

icons/generated/linux-download-30.png

This is a binary file and will not be displayed.

icons/generated/linux-download-31.png

This is a binary file and will not be displayed.

icons/generated/macos-download-00.png

This is a binary file and will not be displayed.

icons/generated/macos-download-01.png

This is a binary file and will not be displayed.

icons/generated/macos-download-02.png

This is a binary file and will not be displayed.

icons/generated/macos-download-03.png

This is a binary file and will not be displayed.

icons/generated/macos-download-04.png

This is a binary file and will not be displayed.

icons/generated/macos-download-05.png

This is a binary file and will not be displayed.

icons/generated/macos-download-06.png

This is a binary file and will not be displayed.

icons/generated/macos-download-07.png

This is a binary file and will not be displayed.

icons/generated/macos-download-08.png

This is a binary file and will not be displayed.

icons/generated/macos-download-09.png

This is a binary file and will not be displayed.

icons/generated/macos-download-10.png

This is a binary file and will not be displayed.

icons/generated/macos-download-11.png

This is a binary file and will not be displayed.

icons/generated/macos-download-12.png

This is a binary file and will not be displayed.

icons/generated/macos-download-13.png

This is a binary file and will not be displayed.

icons/generated/macos-download-14.png

This is a binary file and will not be displayed.

icons/generated/macos-download-15.png

This is a binary file and will not be displayed.

icons/generated/macos-download-16.png

This is a binary file and will not be displayed.

icons/generated/macos-download-17.png

This is a binary file and will not be displayed.

icons/generated/macos-download-18.png

This is a binary file and will not be displayed.

icons/generated/macos-download-19.png

This is a binary file and will not be displayed.

icons/generated/macos-download-20.png

This is a binary file and will not be displayed.

icons/generated/macos-download-21.png

This is a binary file and will not be displayed.

icons/generated/macos-download-22.png

This is a binary file and will not be displayed.

icons/generated/macos-download-23.png

This is a binary file and will not be displayed.

icons/generated/macos-download-24.png

This is a binary file and will not be displayed.

icons/generated/macos-download-25.png

This is a binary file and will not be displayed.

icons/generated/macos-download-26.png

This is a binary file and will not be displayed.

icons/generated/macos-download-27.png

This is a binary file and will not be displayed.

icons/generated/macos-download-28.png

This is a binary file and will not be displayed.

icons/generated/macos-download-29.png

This is a binary file and will not be displayed.

icons/generated/macos-download-30.png

This is a binary file and will not be displayed.

icons/generated/macos-download-31.png

This is a binary file and will not be displayed.

icons/generated/windows-download-00.ico

This is a binary file and will not be displayed.

icons/generated/windows-download-01.ico

This is a binary file and will not be displayed.

icons/generated/windows-download-02.ico

This is a binary file and will not be displayed.

icons/generated/windows-download-03.ico

This is a binary file and will not be displayed.

icons/generated/windows-download-04.ico

This is a binary file and will not be displayed.

icons/generated/windows-download-05.ico

This is a binary file and will not be displayed.

icons/generated/windows-download-06.ico

This is a binary file and will not be displayed.

icons/generated/windows-download-07.ico

This is a binary file and will not be displayed.

icons/generated/windows-download-08.ico

This is a binary file and will not be displayed.

icons/generated/windows-download-09.ico

This is a binary file and will not be displayed.

icons/generated/windows-download-10.ico

This is a binary file and will not be displayed.

icons/generated/windows-download-11.ico

This is a binary file and will not be displayed.

icons/generated/windows-download-12.ico

This is a binary file and will not be displayed.

icons/generated/windows-download-13.ico

This is a binary file and will not be displayed.

icons/generated/windows-download-14.ico

This is a binary file and will not be displayed.

icons/generated/windows-download-15.ico

This is a binary file and will not be displayed.

icons/generated/windows-download-16.ico

This is a binary file and will not be displayed.

icons/generated/windows-download-17.ico

This is a binary file and will not be displayed.

icons/generated/windows-download-18.ico

This is a binary file and will not be displayed.

icons/generated/windows-download-19.ico

This is a binary file and will not be displayed.

icons/generated/windows-download-20.ico

This is a binary file and will not be displayed.

icons/generated/windows-download-21.ico

This is a binary file and will not be displayed.

icons/generated/windows-download-22.ico

This is a binary file and will not be displayed.

icons/generated/windows-download-23.ico

This is a binary file and will not be displayed.

icons/generated/windows-download-24.ico

This is a binary file and will not be displayed.

icons/generated/windows-download-25.ico

This is a binary file and will not be displayed.

icons/generated/windows-download-26.ico

This is a binary file and will not be displayed.

icons/generated/windows-download-27.ico

This is a binary file and will not be displayed.

icons/generated/windows-download-28.ico

This is a binary file and will not be displayed.

icons/generated/windows-download-29.ico

This is a binary file and will not be displayed.

icons/generated/windows-download-30.ico

This is a binary file and will not be displayed.

icons/generated/windows-download-31.ico

This is a binary file and will not be displayed.

+1 -1
justfile
··· 9 9 out="icons/generated" 10 10 rm -rf "$out" 11 11 mkdir -p "$out" 12 - for sheet in idle busy fail; do 12 + for sheet in idle busy fail download; do 13 13 src="icons/$sheet.png" 14 14 [ -f "$src" ] || continue 15 15 dims=$(magick identify -format "%w %h" "$src")
+7
main.go
··· 3 3 import ( 4 4 "log" 5 5 "os" 6 + "path/filepath" 6 7 7 8 "fyne.io/systray" 8 9 ) ··· 17 18 return 18 19 } 19 20 defer lock.Close() 21 + 22 + if temps, err := filepath.Glob(filepath.Join(configDir(), downloadTmpPattern)); err == nil { 23 + for _, t := range temps { 24 + os.Remove(t) 25 + } 26 + } 20 27 21 28 f, err := os.OpenFile(logPath(), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600) 22 29 if err == nil {
+26 -19
restic.go
··· 26 26 resticOnce sync.Once 27 27 resticPath string 28 28 resticManaged bool 29 + downloading sync.Mutex 29 30 ) 30 31 31 32 func findRestic() (path string, managed bool) { ··· 172 173 } 173 174 174 175 func downloadRestic(mStatus prefixedMenuItem) error { 176 + if !downloading.TryLock() { 177 + return nil 178 + } 179 + defer downloading.Unlock() 180 + 175 181 mStatus.SetTitle("Fetching latest version...") 176 182 version, err := latestResticVersion() 177 183 if err != nil { ··· 206 212 if runtime.GOOS == "windows" { 207 213 err = extractResticZip(resp.Body) 208 214 } else { 209 - err = extractResticBz2(resp.Body) 215 + err = installBinary(bzip2.NewReader(resp.Body)) 210 216 } 211 217 if err == nil { 212 218 log.Printf("installed restic %s", version) ··· 215 221 return err 216 222 } 217 223 218 - func extractResticBz2(r io.Reader) error { 224 + const downloadTmpPattern = "restic-download-*" 225 + 226 + func installBinary(r io.Reader) error { 219 227 p := localResticPath() 220 - os.Remove(p) 221 - f, err := os.OpenFile(p, os.O_CREATE|os.O_WRONLY, 0755) 228 + tmp, err := os.CreateTemp(filepath.Dir(p), downloadTmpPattern) 222 229 if err != nil { 223 230 return err 224 231 } 225 - if _, err := io.Copy(f, bzip2.NewReader(r)); err != nil { 226 - f.Close() 232 + tmpName := tmp.Name() 233 + if _, err := io.Copy(tmp, r); err != nil { 234 + tmp.Close() 235 + os.Remove(tmpName) 227 236 return err 228 237 } 229 - f.Close() 238 + tmp.Close() 239 + if err := os.Chmod(tmpName, 0755); err != nil { 240 + os.Remove(tmpName) 241 + return err 242 + } 243 + os.Remove(p) 244 + if err := os.Rename(tmpName, p); err != nil { 245 + os.Remove(tmpName) 246 + return err 247 + } 230 248 clearQuarantine(p) 231 249 return nil 232 250 } ··· 256 274 return err 257 275 } 258 276 defer rc.Close() 259 - p := localResticPath() 260 - os.Remove(p) 261 - f, err := os.OpenFile(p, os.O_CREATE|os.O_WRONLY, 0755) 262 - if err != nil { 263 - return err 264 - } 265 - defer f.Close() 266 - if _, err = io.Copy(f, rc); err != nil { 267 - return err 268 - } 269 - clearQuarantine(p) 270 - return nil 277 + return installBinary(rc) 271 278 } 272 279 } 273 280 return fmt.Errorf("restic binary not found in zip")
+1
tray.go
··· 206 206 } 207 207 208 208 doDownload := func() { 209 + setIconAnimated("download") 209 210 err := downloadRestic(mGlobalStatus) 210 211 if err != nil { 211 212 setIconAnimated("fail")