include backup functionality
This commit is contained in:
@@ -213,10 +213,25 @@ configure
|
|||||||
It covers:
|
It covers:
|
||||||
|
|
||||||
- **Your Federated Computer Core.** Give it your Superpanel's address (e.g. https://superpanel.example.com), your Core email, name and password, and it adds Superpanel to your applications, joins this computer to your Core's Tailscale network, adds your Nextcloud (calendar, contacts and files) and email to GNOME's Online Accounts, and mounts your Nextcloud files at /mnt/example.com ("example.com" in the Files sidebar), and points Element at your Core's Matrix server. For Nextcloud and the files it creates a Nextcloud app password, which you can revoke in Nextcloud under Settings > Security.
|
- **Your Federated Computer Core.** Give it your Superpanel's address (e.g. https://superpanel.example.com), your Core email, name and password, and it adds Superpanel to your applications, joins this computer to your Core's Tailscale network, adds your Nextcloud (calendar, contacts and files) and email to GNOME's Online Accounts, and mounts your Nextcloud files at /mnt/example.com ("example.com" in the Files sidebar), and points Element at your Core's Matrix server. For Nextcloud and the files it creates a Nextcloud app password, which you can revoke in Nextcloud under Settings > Security.
|
||||||
|
- **Daily backups of your home folder** to your Nextcloud files, in /mnt/example.com/.backup-{machine name}, with borg. Set up your Core first. The backup is encrypted with a passphrase that `configure` shows you once: save it somewhere other than this computer, such as Bitwarden. It keeps 7 daily, 4 weekly and 6 monthly backups, and leaves out caches and things that can be reinstalled. To leave out more, list paths in ~/.config/coredesktop/backup-exclude, one per line. See "Restoring from a backup" below.
|
||||||
- Signing in to Claude Code, opencode and Hermes Agent and choosing their model providers.
|
- Signing in to Claude Code, opencode and Hermes Agent and choosing their model providers.
|
||||||
|
|
||||||
To set up one thing, name it, e.g. `configure hermes`; `configure --list` shows the names.
|
To set up one thing, name it, e.g. `configure hermes`; `configure --list` shows the names.
|
||||||
|
|
||||||
|
### Restoring from a backup
|
||||||
|
|
||||||
|
To see the backups and get files back:
|
||||||
|
|
||||||
|
```
|
||||||
|
export BORG_REPO=/mnt/example.com/.backup-$HOSTNAME
|
||||||
|
export BORG_PASSCOMMAND="secret-tool lookup coredesktop backup repo $BORG_REPO"
|
||||||
|
borg list
|
||||||
|
mkdir ~/restored && cd ~/restored
|
||||||
|
borg extract ::{backup name} home/{you}/Documents
|
||||||
|
```
|
||||||
|
|
||||||
|
On a new computer, set up your Core with `configure core` first. Then point `BORG_REPO` at the old computer's .backup-{name} folder and leave out `BORG_PASSCOMMAND`: borg asks for the passphrase you saved. `systemctl --user list-timers coredesktop-backup.timer` shows when the next backup runs, and `journalctl --user -u coredesktop-backup` shows how the last ones went.
|
||||||
|
|
||||||
## Sandboxed AI agents
|
## Sandboxed AI agents
|
||||||
|
|
||||||
CoreDesktop installs the AI coding agents opencode, Hermes Agent and Claude Code, and runs them in a sandbox. In the sandbox, an agent:
|
CoreDesktop installs the AI coding agents opencode, Hermes Agent and Claude Code, and runs them in a sandbox. In the sandbox, an agent:
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
TITLE="Backup"
|
||||||
|
DESCRIPTION="Back up your home folder daily to your Nextcloud files"
|
||||||
|
|
||||||
|
CORE_CONF="$HOME/.config/coredesktop/core.conf"
|
||||||
|
BACKUP_CONF="$HOME/.config/coredesktop/backup.conf"
|
||||||
|
UNITS="$HOME/.config/systemd/user"
|
||||||
|
[ -f "$CORE_CONF" ] && source "$CORE_CONF"
|
||||||
|
[ -f "$BACKUP_CONF" ] && source "$BACKUP_CONF"
|
||||||
|
|
||||||
|
is_installed() {
|
||||||
|
command -v borg > /dev/null && command -v secret-tool > /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
# The passphrase is in the keyring under the repository's path, so the
|
||||||
|
# backup can run without asking
|
||||||
|
passphrase() {
|
||||||
|
secret-tool lookup coredesktop backup repo "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
is_configured() {
|
||||||
|
[ -n "$BORG_REPO" ] && [ -n "$(passphrase "$BORG_REPO")" ] \
|
||||||
|
&& systemctl --user is-enabled -q coredesktop-backup.timer
|
||||||
|
}
|
||||||
|
|
||||||
|
# system/backup uses borg 1.2 to 1.4; borg 2 changed its commands
|
||||||
|
borg_supported() {
|
||||||
|
local version
|
||||||
|
version="$(borg --version | awk '{print $2}')"
|
||||||
|
[[ "$version" =~ ^1\.([2-9]|[1-9][0-9])\. ]] && return 0
|
||||||
|
echo "This needs borg 1.2 to 1.4, and borg $version is installed."
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
save_passphrase() {
|
||||||
|
printf '%s' "$2" | secret-tool store --label "CoreDesktop backup ($1)" coredesktop backup repo "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
run_configure() {
|
||||||
|
local mount="/mnt/$CORE_DOMAIN" repo pass
|
||||||
|
borg_supported || return 1
|
||||||
|
if [ -z "$CORE_DOMAIN" ] || ! mountpoint -q "$mount"; then
|
||||||
|
echo "Backups go to your Nextcloud files, so set those up first (configure core), and check they're mounted."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
repo="$mount/.backup-$HOSTNAME"
|
||||||
|
|
||||||
|
if [ -f "$repo/config" ]; then
|
||||||
|
# Already there, e.g. after reinstalling: it needs the passphrase from then
|
||||||
|
pass="$(passphrase "$repo")"
|
||||||
|
if [ -z "$pass" ] || ! BORG_PASSPHRASE="$pass" borg info "$repo" &> /dev/null; then
|
||||||
|
echo "There's already a backup at $repo."
|
||||||
|
read -rsp "Its passphrase: " pass
|
||||||
|
echo
|
||||||
|
BORG_PASSPHRASE="$pass" borg info "$repo" > /dev/null || { echo "borg couldn't open it with that passphrase."; return 1; }
|
||||||
|
save_passphrase "$repo" "$pass" || return 1
|
||||||
|
fi
|
||||||
|
echo "Using the backup at $repo."
|
||||||
|
else
|
||||||
|
pass="$(head -c 24 /dev/urandom | base64)"
|
||||||
|
echo "Your backup is encrypted with this passphrase:"
|
||||||
|
echo
|
||||||
|
echo " $pass"
|
||||||
|
echo
|
||||||
|
echo "Save it somewhere other than this computer, such as Bitwarden. Without it, the"
|
||||||
|
echo "backup can't be read if this computer is lost. It's kept in your keyring so the"
|
||||||
|
echo "daily backup can run."
|
||||||
|
until confirm "Have you saved it?"; do :; done
|
||||||
|
save_passphrase "$repo" "$pass" || return 1
|
||||||
|
echo "Creating the backup at $repo..."
|
||||||
|
BORG_PASSPHRASE="$pass" borg init --encryption=repokey-blake2 "$repo" || return 1
|
||||||
|
# davfs2 copies each file to a local cache before uploading it, and some
|
||||||
|
# servers refuse large uploads, so keep borg's files to 50 MB, not 500
|
||||||
|
BORG_PASSPHRASE="$pass" borg config "$repo" max_segment_size 52428800 || return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$(dirname "$BACKUP_CONF")"
|
||||||
|
{
|
||||||
|
printf 'BORG_REPO=%q\n' "$repo"
|
||||||
|
printf 'BACKUP_MOUNT=%q\n' "$mount"
|
||||||
|
} > "$BACKUP_CONF"
|
||||||
|
|
||||||
|
mkdir -p "$UNITS"
|
||||||
|
cp "$COREDESKTOP"/system/systemd/coredesktop-backup.{service,timer} "$UNITS/" || return 1
|
||||||
|
systemctl --user daemon-reload
|
||||||
|
systemctl --user enable --now coredesktop-backup.timer || return 1
|
||||||
|
echo "Your home folder will be backed up daily to $repo."
|
||||||
|
|
||||||
|
if confirm_yes "Start the first backup now? It runs in the background and can take hours."; then
|
||||||
|
systemctl --user start --no-block coredesktop-backup.service
|
||||||
|
echo "Started. To follow it: journalctl --user -fu coredesktop-backup"
|
||||||
|
fi
|
||||||
|
}
|
||||||
Executable
+75
@@ -0,0 +1,75 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Purpose : Back up your home folder with borg to your Nextcloud files, in
|
||||||
|
# /mnt/<domain>/.backup-<hostname>. Run daily by
|
||||||
|
# coredesktop-backup.timer; `configure backup` sets it up.
|
||||||
|
#
|
||||||
|
# Keeps 7 daily, 4 weekly and 6 monthly backups. To leave out more, list
|
||||||
|
# paths or borg patterns in ~/.config/coredesktop/backup-exclude, one per line.
|
||||||
|
|
||||||
|
# Wrapped in { ...; exit; } so bash reads the whole script up front; update
|
||||||
|
# replaces this file (git pull) while a backup may be running.
|
||||||
|
{
|
||||||
|
|
||||||
|
BACKUP_CONF="$HOME/.config/coredesktop/backup.conf"
|
||||||
|
USER_EXCLUDE="$HOME/.config/coredesktop/backup-exclude"
|
||||||
|
|
||||||
|
# Tell the desktop when a backup fails, as no one watches the timer
|
||||||
|
fail() {
|
||||||
|
echo "$1" >&2
|
||||||
|
notify-send --app-name=CoreDesktop --icon=dialog-error "Backup failed" "$1" 2> /dev/null
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
[ -f "$BACKUP_CONF" ] || fail "Backups aren't set up. Run: configure backup"
|
||||||
|
source "$BACKUP_CONF"
|
||||||
|
|
||||||
|
# The files mount comes up at login, perhaps before the network; give it a
|
||||||
|
# few minutes rather than writing into the empty folder underneath
|
||||||
|
for try in {1..10}; do
|
||||||
|
mountpoint -q "$BACKUP_MOUNT" && break
|
||||||
|
mount "$BACKUP_MOUNT" 2> /dev/null && break
|
||||||
|
sleep 30
|
||||||
|
done
|
||||||
|
mountpoint -q "$BACKUP_MOUNT" || fail "Your Nextcloud files aren't mounted at $BACKUP_MOUNT."
|
||||||
|
|
||||||
|
export BORG_REPO
|
||||||
|
export BORG_PASSCOMMAND="secret-tool lookup coredesktop backup repo $BORG_REPO"
|
||||||
|
|
||||||
|
# Rebuilt by borg, or by reinstalling, so not worth the space
|
||||||
|
excludes=(
|
||||||
|
"$HOME/.cache"
|
||||||
|
"$HOME/.davfs2/cache"
|
||||||
|
"$HOME/.local/share/Trash"
|
||||||
|
"$HOME/.local/share/containers"
|
||||||
|
"$HOME/.local/share/flatpak"
|
||||||
|
"$HOME/.local/share/siblings"
|
||||||
|
"$HOME/.local/share/coredesktop"
|
||||||
|
"$HOME/.var/app/*/cache"
|
||||||
|
"$HOME/.npm"
|
||||||
|
"$HOME/.cargo/registry"
|
||||||
|
"$HOME/go/pkg/mod"
|
||||||
|
)
|
||||||
|
exclude_args=(--exclude-caches)
|
||||||
|
for path in "${excludes[@]}"; do
|
||||||
|
exclude_args+=(--exclude "sh:$path")
|
||||||
|
done
|
||||||
|
[ -f "$USER_EXCLUDE" ] && exclude_args+=(--exclude-from "$USER_EXCLUDE")
|
||||||
|
|
||||||
|
echo "Backing up $HOME to $BORG_REPO..."
|
||||||
|
# --one-file-system leaves out other mounts and btrfs subvolumes inside home
|
||||||
|
borg create --one-file-system --compression zstd --stats \
|
||||||
|
"${exclude_args[@]}" "::{hostname}-{now:%Y-%m-%dT%H:%M}" "$HOME"
|
||||||
|
status=$?
|
||||||
|
# 1 is a warning, e.g. a file changed while it was read; the backup still exists
|
||||||
|
(( status <= 1 )) || fail "borg couldn't back up $HOME (exit $status). See: journalctl --user -u coredesktop-backup"
|
||||||
|
|
||||||
|
echo "Removing old backups..."
|
||||||
|
borg prune --glob-archives '{hostname}-*' \
|
||||||
|
--keep-daily 7 --keep-weekly 4 --keep-monthly 6 \
|
||||||
|
|| fail "borg couldn't remove old backups. See: journalctl --user -u coredesktop-backup"
|
||||||
|
borg compact || fail "borg couldn't free space from old backups. See: journalctl --user -u coredesktop-backup"
|
||||||
|
|
||||||
|
echo "Backup finished."
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Back up your home folder to your Nextcloud files
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=%h/.local/share/coredesktop/system/backup
|
||||||
|
# Stay out of the way of whatever you're doing
|
||||||
|
Nice=19
|
||||||
|
IOSchedulingClass=idle
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Back up your home folder daily
|
||||||
|
|
||||||
|
[Timer]
|
||||||
|
OnCalendar=daily
|
||||||
|
RandomizedDelaySec=1h
|
||||||
|
# Catch up at the next login if the computer was off or you weren't logged in
|
||||||
|
Persistent=true
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=timers.target
|
||||||
Reference in New Issue
Block a user