Repository navigation
Backup and Restore
Two folders hold every choice you have made in the shell, and neither comes back from a reinstall:
| Folder | Holds |
|---|---|
~/.config/illogical-impulse |
config.json (all settings), presets, profile pictures, widget extensions, workspace profiles, preset-store links |
~/.local/state/quickshell |
states.json, your to-do list, notes, clipboard pins, keybind pages and commands, app-usage history, Email preferences and drafts, Bedtime records… |
Losing them is losing the desktop, not the shell. The Settings Backup packs both into one zip, restores it, and can hand the zip to your Google Drive backup. It is in Settings → Accounts & Backup → Settings Backup, and it is offered on the very first page of the Welcome app, so a fresh install can pull your whole setup back before anything else.
This is separate from the Preset Store — presets are strictly visual and share a look, never your personal data — and from Customization → Presets, which snapshot appearance only. A backup is everything.
-
Where backups are kept — choose a folder (a
zenityorkdialogchooser; the page tells you which to install if neither exists). - Back up now, or enable Back up automatically every (days) (7 by default) and Keep the newest backups (5 by default; older ones are deleted).
- Optionally Keep a copy in Google Drive: the backup folder is added to the folders the existing Google Drive backup already syncs, so the zip uploads with that schedule. There is no second uploader; if the Drive backup is off, the page says nothing is being uploaded yet.
The page shows the last backup (time, size, file count). Files are named ii-backup-<date>-<time>.zip and carry an ii-backup.json manifest (format, date, host, counts) at the root.
The automatic backup is not a clock. The shell is not always on, and a backup scheduled for 3 AM is a backup that never happens. Instead, 45 seconds after the shell starts it asks once whether the last backup is older than your interval — which is also true after a week with the computer off.
Everything that is either a build artifact or re-fetched on demand: .venv (the ~370 MB Python environment, recreated by setup), the git clones under preset-store/ (~50 MB, re-downloadable; the links.json beside them is kept), __pycache__, .git, .cache, node_modules, *.pyc, *.tmp, *.swp, *.lock, sockets, and the config.json.malformed-* copies the shell makes when it repairs a bad file. On a real system that took 145 MB + 383 MB down to about 100 MB and 260 files. Already-compressed files (images, video, fonts) are stored rather than deflated.
- Secrets. API keys, Gmail / Tasks / Calendar tokens and the OAuth client live in the system keyring, not in these folders; they must be connected again after restoring on a new machine (Google Cloud Setup).
-
ii/.env, vdirsyncer's token and rclone's config (they live elsewhere). - Your files: wallpapers outside the folders above, documents, the Drive backup itself.
- The shell's code — that comes from the repository.
Restore from a zip file… (or pick one from the list of backups in the folder). The shell first inspects the archive: a zip without the manifest is refused as Not a settings backup, or damaged, so a random zip can never spray files into ~/.config. Then:
- The row arms — the button turns into Overwrite my settings — because this is the one action on these pages you cannot undo by hand.
- Before extracting, the script writes your current settings next to the archive as a backup of their own (
…-before-restore.zip), so a restore can itself be undone. These count as backups in the list and in pruning. - Extraction merges into the two folders; it never wipes them first (clearing the state folder would delete the
.venvand break the shell). - While it runs, writes to
config.jsonand to the persistent state are held.Config.qmlwrites its file on its own debounce; without the hold, a setting changed seconds earlier would be written back over the restored one. This is the same guard the Preset Store uses, extended tostates.json; pending changes are flushed first.
On the Welcome app's first page, Restore sits in the bottom-left corner — the slot that is free because there is nowhere to go back to on page one. A fresh machine has no backup folder yet and nothing worth saving, so that path skips the safety copy.
Notes: a zip made on another computer brings that computer's backup-folder path with it (states.json); that is harmless. After a restore the shell's file watchers pick the new files up within a moment (the write hold is released 1.2 s after the restore finishes), so no restart is needed.
scripts/backup/shell_backup.py is the whole engine. Every subcommand prints exactly one JSON line and never raises — failure is {"ok": false, "error": "…"}:
python3 scripts/backup/shell_backup.py create --dest ~/Backups --keep 5 [--label before-upgrade]
python3 scripts/backup/shell_backup.py list --dest ~/Backups
python3 scripts/backup/shell_backup.py inspect --archive ~/Backups/ii-backup-20261012-093000.zip
python3 scripts/backup/shell_backup.py restore --archive … [--safety-dest ~/Backups]| Piece | Role |
|---|---|
scripts/backup/shell_backup.py |
Create, list, inspect, restore; skip rules; pruning |
services/ShellBackup.qml |
One job at a time, the config hold during restore, the 45-second due check, the Drive hand-off |
modules/settings/configs/widgets/ShellBackupConfig.qml |
The settings page |
modules/welcome/WelcomeRestoreDialog.qml |
The first-page restore |
Persistent.states.shellBackup |
enabled, folder, autoDrive, keepCount, intervalDays, lastBackup* — in persistent state because a backup folder is a fact about this machine, and a preset carrying someone else's path would be absurd |