test core for configure

This commit is contained in:
2026-10-30 11:01:57 -06:00
parent 38c03b0b69
commit c0df6537ca
2 changed files with 238 additions and 24 deletions
+1 -1
View File
@@ -218,7 +218,7 @@ configure
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, 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. You can add more Cores: run `configure core` again and choose "Add a Core". Each gets its own Superpanel, Nextcloud, email and files; Element, Tailscale and backups use your main Core (the first one, unless you choose another). "Remove a Core" takes it off this computer and revokes its app password, and leaves everything on the Core itself as it is. If you change your Core password (e.g. in Superpanel), run `configure core` and choose "Your Core password changed": email keeps your Core password, so it saves the new one there, and it signs in to Nextcloud with it, which turns the app passwords for Nextcloud, files and backups back on (Nextcloud turns them off when your password changes), then checks each of them.
- **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, 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. You can add more Cores: run `configure core` again and choose "Add a Core". Each gets its own Superpanel, Nextcloud, email and files; Element, Tailscale and backups use your main Core (the first one, unless you choose another). "Remove a Core" takes it off this computer and revokes its app password, and leaves everything on the Core itself as it is. If you change your Core password (e.g. in Superpanel), run `configure core` and choose "Your Core password changed": email keeps your Core password, so it saves the new one there, and it signs in to Nextcloud with it, which turns the app passwords for Nextcloud, files and backups back on (Nextcloud turns them off when your password changes), then checks each of them. To check that a Core still works from this computer, run `configure core` and choose "Test a Core": it signs in to Superpanel, Nextcloud, email, your files, Element's Matrix server and backups the way this computer does, says which work, and offers to fix those that don't (asking for your Core password if a fix needs it, e.g. to make a new app password for one that was revoked), then tests again.
- **Your Core's Tailscale network.** Joins this computer to it through headscale.example.com, so you can reach your other machines on it. Set up your Core first. You sign in through a link it shows you; if this computer is on another Tailscale network, it moves it to your Core's. To leave your Core's network, run `configure tailscale` again.
- **Daily backups of your home folder** to your Nextcloud files, in a .backup-{machine name} folder, with restic. 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, things that can be reinstalled, and WinBoat's Windows VM (~/winboat). To leave out more, list paths in ~/.config/coredesktop/backup-exclude, one per line. restic reaches Nextcloud directly through rclone with its own Nextcloud app password, so the backup doesn't depend on the /mnt mount. See "Restoring from a backup" below. To turn backups off, run `configure backup` again; it also offers to delete the backups from your Nextcloud files (they go to Nextcloud's trash bin).
- Signing in to Claude Code, opencode and Hermes Agent and choosing their model providers.
+237 -23
View File
@@ -218,13 +218,24 @@ EOF
echo "Added $CORE_EMAIL to Online Accounts."
}
# Save APP_PASSWORD for davfs2 to mount your files with. davfs2 only reads
# this file if no one else can.
save_files_secret() {
local secret
mkdir -p -m 700 "$HOME/.davfs2"
touch "$HOME/.davfs2/secrets"
chmod 600 "$HOME/.davfs2/secrets"
secret="${APP_PASSWORD//\\/\\\\}"
secret="${secret//\"/\\\"}"
sed -i "\\#^$MOUNT #d" "$HOME/.davfs2/secrets"
printf '%s %s "%s"\n' "$MOUNT" "$CORE_USER" "$secret" >> "$HOME/.davfs2/secrets"
}
# Mount your Nextcloud files at /mnt/<domain> with davfs2. You can mount it
# without sudo once it's in /etc/fstab with "user" and you're in Arch's davfs2
# group (network). It's mounted now and each time you log in.
setup_files() {
[ -n "$APP_PASSWORD" ] || { echo "This needs your Core password, and Nextcloud didn't accept it."; return 1; }
local secret
sudo install -d -o "$USER" -g "$(id -gn)" "$MOUNT" || return 1
if ! grep -qs " $MOUNT davfs " /etc/fstab; then
echo "$WEBDAV $MOUNT davfs rw,user,noauto,uid=$USER,_netdev 0 0" | sudo tee -a /etc/fstab > /dev/null || return 1
@@ -234,15 +245,7 @@ setup_files() {
if ! id -nG "$USER" | grep -qw network; then
sudo usermod -aG network "$USER" || return 1
fi
# davfs2 only reads this file if no one else can
mkdir -p -m 700 "$HOME/.davfs2"
touch "$HOME/.davfs2/secrets"
chmod 600 "$HOME/.davfs2/secrets"
secret="${APP_PASSWORD//\\/\\\\}"
secret="${secret//\"/\\\"}"
sed -i "\\#^$MOUNT #d" "$HOME/.davfs2/secrets"
printf '%s %s "%s"\n' "$MOUNT" "$CORE_USER" "$secret" >> "$HOME/.davfs2/secrets"
save_files_secret || return 1
mkdir -p "$HOME/.config/autostart"
cat > "$HOME/.config/autostart/core-$CORE_DOMAIN-files.desktop" << EOF
@@ -300,6 +303,25 @@ step() {
"setup_$name" || { echo -e "\e[31m$title was not set up.\e[0m"; STATUS=1; }
}
# Ask for your Core password until Nextcloud takes it, and with a name ($1),
# create an app password by that name (APP_PASSWORD). PASSWORD is left empty
# if Nextcloud says it's wrong three times, and kept, unchecked, if Nextcloud
# can't be reached.
ask_password() {
local tries
for tries in 1 2 3; do
read -rsp "Your Core password: " PASSWORD
echo
if [ -n "$1" ]; then nextcloud_app_password "$1"; else nextcloud_sign_in; fi
case $? in
0) return 0 ;;
2) echo "Nextcloud says that password is wrong."; PASSWORD="" ;;
*) echo "Couldn't reach Nextcloud at $NEXTCLOUD; trying the password for email anyway."; return 1 ;;
esac
done
return 1
}
# Set up the Core loaded with load_core, or a new one
setup_core() {
local url
@@ -324,17 +346,7 @@ setup_core() {
# Nextcloud, email and files need your Core password; Nextcloud checks it
if ! nextcloud_done || ! email_done || ! files_done; then
local tries
for tries in 1 2 3; do
read -rsp "Your Core password: " PASSWORD
echo
nextcloud_app_password "CoreDesktop on $HOSTNAME"
case $? in
0) break ;;
2) echo "Nextcloud says that password is wrong."; PASSWORD="" ;;
*) echo "Couldn't reach Nextcloud at $NEXTCLOUD; trying the password for email anyway."; break ;;
esac
done
ask_password "CoreDesktop on $HOSTNAME"
fi
echo
@@ -401,7 +413,7 @@ change_password() {
echo " Nextcloud in Online Accounts: working"
else
echo -e " \e[31mNextcloud in Online Accounts: can't sign in.\e[0m"
echo " Remove it in Settings > Online Accounts, then run: configure core"
echo " To fix it: configure core, then choose t) Test a Core"
STATUS=1
fi
fi
@@ -513,6 +525,204 @@ remove_core() {
fi
}
# Testing a Core checks each part this computer set up for it against the
# Core itself, then offers to fix what doesn't work. A test that fails adds a
# fix, if there is one, to FIXES (with its title in FIX_TITLES).
passed() {
echo -e " $1: \e[32mworking\e[0m"
}
# failed <title> <problem> [fix] [more]
failed() {
echo -e " \e[31m$1: $2\e[0m"
[ -n "$4" ] && echo " $4"
PROBLEMS=$((PROBLEMS + 1))
[ -n "$3" ] && FIXES+=("$3") FIX_TITLES+=("$1")
}
# Test an Online Accounts account: <title> <provider> <identity> <setup> <fix>
test_account() {
local error
if ! "$GOA_ACCOUNT" exists "$2" "$3" 2> /dev/null; then
failed "$1" "not set up" "$4"
elif error="$("$GOA_ACCOUNT" check "$2" "$3" 2>&1)"; then
passed "$1"
else
failed "$1" "can't sign in" "$5" "$error"
fi
}
test_files() {
local title="Nextcloud files at $MOUNT"
files_done || { failed "$title" "not set up" setup_files; return; }
nextcloud_sign_in "$(files_app_password)"
case $? in
0) if mountpoint -q "$MOUNT" && timeout 15 ls "$MOUNT" &> /dev/null; then
passed "$title"
else
failed "$title" "not mounted" remount_files
fi ;;
2) failed "$title" "Nextcloud doesn't accept its app password" fix_files \
"It may have been revoked in Nextcloud under Settings > Security." ;;
*) failed "$title" "couldn't check its app password" ;;
esac
}
test_element() {
local title="Element with your Core's Matrix server"
if ! element_done; then
failed "$title" "Element uses another server" setup_element
elif curl -sf -o /dev/null --max-time 15 "$MATRIX/_matrix/client/versions"; then
passed "$title"
else
failed "$title" "can't reach $MATRIX"
fi
}
test_backups() {
local webdav
webdav="$(source "$BACKUP_CONF" && echo "$BACKUP_WEBDAV")"
nextcloud_sign_in "$(secret-tool lookup coredesktop backup item nextcloud url "$webdav" 2> /dev/null)"
case $? in
0) passed "Backups" ;;
2) failed "Backups" "Nextcloud doesn't accept their app password" fix_backups ;;
*) failed "Backups" "couldn't check their app password" ;;
esac
}
# Test the Core loaded with load_core. Fails if Nextcloud can't be reached,
# as then nothing else can be tested.
test_core() {
local status
FIXES=() FIX_TITLES=() PROBLEMS=0
status="$(curl -sf --max-time 15 "$NEXTCLOUD/status.php")"
if [ -z "$status" ]; then
echo -e " \e[31mCouldn't reach Nextcloud at $NEXTCLOUD.\e[0m"
echo " Check this computer's internet connection, or whether your Core is down."
return 1
fi
if jq -e '.maintenance' <<< "$status" > /dev/null 2>&1; then
echo " Nextcloud is being updated (maintenance mode). Test again when it's done."
return 1
fi
if ! superpanel_done; then
failed "Superpanel in your applications" "not set up" setup_superpanel
elif curl -s -o /dev/null --max-time 15 "https://superpanel.$CORE_DOMAIN/"; then
passed "Superpanel"
else
failed "Superpanel" "can't reach https://superpanel.$CORE_DOMAIN"
fi
test_account "Nextcloud in Online Accounts" owncloud "$CORE_USER@nextcloud.$CORE_DOMAIN" \
setup_nextcloud fix_nextcloud
test_account "Email in Online Accounts" imap_smtp "$CORE_EMAIL" setup_email fix_email
test_files
[ "$CORE_DOMAIN" = "$MAIN_DOMAIN" ] && element_installed && test_element
backup_on "$CORE_DOMAIN" && test_backups
return 0
}
# Online Accounts gets a new app password
fix_nextcloud() {
[ -n "$APP_PASSWORD" ] || { echo "This needs your Core password, and Nextcloud didn't accept it."; return 1; }
jq -n --arg password "$APP_PASSWORD" '{password: $password}' \
| "$GOA_ACCOUNT" update owncloud "$CORE_USER@nextcloud.$CORE_DOMAIN" || return 1
echo "Nextcloud in Online Accounts has a new app password."
}
# Email gets your Core password. If the mail server still says no, its
# settings may have changed, so add it again with the server's settings now.
fix_email() {
[ -n "$PASSWORD" ] || { echo "This needs your Core password."; return 1; }
jq -n --arg password "$PASSWORD" '{"imap-password": $password, "smtp-password": $password}' \
| "$GOA_ACCOUNT" update imap_smtp "$CORE_EMAIL" 2> /dev/null
case $? in
0) echo "Email has your Core password." ;;
3) echo "The mail server still says no; adding your email again with its current settings."
"$GOA_ACCOUNT" remove imap_smtp "$CORE_EMAIL" && setup_email ;;
*) return 1 ;;
esac
}
# Unmount if it's stuck, then mount
remount_files() {
if mountpoint -q "$MOUNT"; then
umount "$MOUNT" 2> /dev/null || sudo umount "$MOUNT" || return 1
fi
mount "$MOUNT" 2> /dev/null || sudo -u "$USER" mount "$MOUNT" || return 1
echo "Your Nextcloud files are at $MOUNT."
}
# davfs2 gets a new app password
fix_files() {
[ -n "$APP_PASSWORD" ] || { echo "This needs your Core password, and Nextcloud didn't accept it."; return 1; }
save_files_secret && remount_files
}
# Backups get their own new app password, as configure backup gives them
fix_backups() {
local APP_PASSWORD webdav
[ -n "$PASSWORD" ] || { echo "This needs your Core password."; return 1; }
webdav="$(source "$BACKUP_CONF" && echo "$BACKUP_WEBDAV")"
nextcloud_app_password "CoreDesktop backup on $HOSTNAME" || return 1
printf '%s' "$APP_PASSWORD" | secret-tool store --label "CoreDesktop backup nextcloud ($webdav)" \
coredesktop backup item nextcloud url "$webdav" || return 1
echo "Backups have a new app password."
}
# Run the fixes test_core found, asking for your Core password once if they
# need it
fix_core() {
local fix i needs=""
for fix in "${FIXES[@]}"; do
case "$fix" in
setup_nextcloud|fix_nextcloud|setup_files|fix_files) needs=app ;;
setup_email|fix_email|fix_backups) needs="${needs:-password}" ;;
esac
done
case "$needs" in
app) ask_password "CoreDesktop on $HOSTNAME" ;;
password) ask_password ;;
esac
STATUS=0
for i in "${!FIXES[@]}"; do
echo
echo "${FIX_TITLES[$i]}:"
"${FIXES[$i]}" || { echo -e "\e[31m${FIX_TITLES[$i]} was not fixed.\e[0m"; STATUS=1; }
done
unset PASSWORD APP_PASSWORD
return $STATUS
}
check_core() {
load_core "$1"
echo "Testing $CORE_DOMAIN..."
test_core || return 1
if [ $PROBLEMS -eq 0 ]; then
echo "Everything works."
return 0
fi
echo
if [ ${#FIXES[@]} -eq 0 ]; then
echo "This computer can't fix that; it's on the Core's side, or the network's."
return 1
fi
confirm_yes "Fix what doesn't work?" || return 1
fix_core
echo
echo "Testing $CORE_DOMAIN again..."
test_core || return 1
if [ $PROBLEMS -eq 0 ]; then
echo "Everything works now."
else
echo "Some of it still doesn't work."
return 1
fi
}
run_configure() {
local choice i state
mapfile -t DOMAINS < <(core_domains)
@@ -536,6 +746,7 @@ run_configure() {
echo " Choose a number to finish or change its setup, or:"
echo " a) Add a Core"
[ ${#DOMAINS[@]} -gt 1 ] && echo " m) Make another Core the main one"
echo " t) Test a Core, and fix what doesn't work"
echo " p) Your Core password changed"
echo " r) Remove a Core"
echo " q) Leave it as it is"
@@ -552,6 +763,9 @@ run_configure() {
p|P)
pick_core "Which Core's password changed?" || { echo "Choose a number from the list."; return 1; }
change_password "$PICKED" ;;
t|T)
pick_core "Which Core should be tested?" || { echo "Choose a number from the list."; return 1; }
check_core "$PICKED" ;;
r|R)
pick_core "Which Core should be removed?" || { echo "Choose a number from the list."; return 1; }
remove_core "$PICKED" ;;