You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Status: Ready for review · related: #97 (Settings Profile), #62 (signed OTA upgrade)
The problem (audited, not assumed)
Gargoyle's backup exists but not all systems are included — and the audit shows it's worse than a few missing paths: there are two divergent hard-coded lists that disagree with each other.
usr/lib/gargoyle/create_backup.sh (the web Backup page) tars a static list that includes /etc/openvpn but not /etc/tor.
etc/uci-defaults/gargoyle-init writes /etc/sysupgrade.conf (what an in-place sysupgrade preserves) with a different list: /etc/tor yes, /etc/openvpn no, smbpasswd no.
Coverage matrix (✅ preserved / ❌ lost):
State
Web backup
sysupgrade
Consequence
/etc/openvpn (CA, server keys)
✅
❌
in-place upgrade silently loses your OpenVPN identity
/etc/tor
❌
✅
backup/restore loses tor state
/etc/samba/smbpasswd
✅
❌
upgrade loses share passwords
WireGuard (/etc/config/wireguard_gargoyle)
✅
✅
config survives — but nothing records that the plugin was installed, so a restore onto a fresh flash shows WireGuard "gone"
Web-UI TLS cert (/etc/uhttpd.crt/.key)
❌
❌
cert regenerated on restore; accepted/pinned certs break
/etc/gargoyle/ persistent state
❌
❌
future features lose state silently
Beyond paths:
No manifest. The tarball records nothing — no Gargoyle/OpenWrt version, no board_name, no installed-plugin list — and restore.sh performs zero version or board validation. An old-version tarball restores raw onto new firmware (the breakage class RFC: Settings Profile — configuration that survives major upgrades (export intent, flash clean, regenerate) #97 addresses), and a tarball from a different model restores its network config onto the wrong hardware.
The plugin set is invisible. Restore reinstates VPN configs for software that may not be installed; the user sees "OpenVPN/WireGuard gone" even though the config technically restored.
restore.sh wholesale-replaces /etc/rc.d/* with the backup's enable symlinks — dangling links for services the current image doesn't ship.
Root cause is structural: two duplicated static strings and no way for a plugin to register its state. Every new feature must remember to edit both lists; since 2008 they haven't. Missing paths are symptoms; unregisterable state is the disease.
Proposal
B1 — one declarative registry, two consumers (kills the divergence)
Adopt OpenWrt's own mechanism instead of inventing one: each package owning state outside /etc/config ships a fragment in /lib/upgrade/keep.d/gargoyle-<feature> (stock sysupgrade already reads keep.d, so the upgrade side becomes correct by the package existing). create_backup.sh is rewritten to take its list from the union of /etc/sysupgrade.conf + /lib/upgrade/keep.d/* plus backup-only extras (/tmp/data, /usr/data monitor state). The two paths can never diverge again, and a plugin's backup coverage ships with the plugin, not as an edit to core scripts someone must remember.
Two rules that keep the registry sound (both verified against sysupgrade's source):
keep.d is a plain path list — there is no exclusion syntax (sysupgrade just cats /etc/sysupgrade.conf /lib/upgrade/keep.d/* into one file list). So gargoyle-core must list the specific /etc/gargoyle/ subpaths to keep, never the whole directory — safe_apply/ snapshots are transactional state that must not survive into a different firmware context, and the only way to exclude them is to not list their parent.
gargoyle-backup-manifest.json in the tarball: Gargoyle + OpenWrt versions, board_name, creation time, the captured path list, the installed Gargoyle plugin list, and profile_version once #97 lands. Costs nothing; enables every safety feature below. Old manifest-less tarballs stay restorable (with the legacy warning).
B3 — restore pre-flight (blind restore ends)
Before extracting, restore.sh reads the manifest and gates:
Board mismatch → refuse by default (another model's network config is a lockout waiting to happen); expert override available.
Plugin diff → report "this backup used: openvpn, tor; this image lacks: tor" before restoring — "feature X gone" becomes an explanation and an action item instead of a mystery.
/etc/rc.d: restore enable-states only for init scripts that exist on the current image; report the rest.
B4 — immediate coverage fixes (independent of the registry)
Land now, small diffs: /etc/openvpn into sysupgrade.conf, /etc/tor into the backup list, align smbpasswd, add the web TLS cert/key to both, add /etc/gargoyle/ (excluding safe_apply/ transactional snapshots).
B5 — tests + a structural lint
New vnet phase (T-BAK-01…): per-feature round-trip asserted on the wire — after backup → factory reset → restore, the OpenVPN tunnel actually comes up, the WG peer list is intact, static leases serve, shares authenticate. Not a UCI diff; a behavior check ("backend applied" and "user's stuff works" are different claims — a lesson recently re-learned the hard way).
CI lint with teeth: any package writing state outside /etc/config must ship a keep.d fragment — enforced at build time, so the next OpenVPN-class omission fails the build instead of surfacing years later as someone's lost keys.
Complementary, not competing. Raw backup = full-fidelity snapshot for the same version (keys, certs, monitor data, hand-edits — zero translation). Profile = intent that survives cross-version/cross-device. The Backup page offers both, and B3's pre-flight is the traffic cop that routes each tarball to the right path using the manifest. The manifest's plugin list also feeds #97's Tier-B story (a reinstall checklist after a manual flash).
Full design doc: docs/backup-coverage-plan.md in ispyisail/gargoyle-tools.
Feedback welcome — especially on whether cross-board restore should be refused outright or allowed with the network configs stripped, and any other non-/etc/config state paths people know of that the audit missed.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Status: Ready for review · related: #97 (Settings Profile), #62 (signed OTA upgrade)
The problem (audited, not assumed)
Gargoyle's backup exists but not all systems are included — and the audit shows it's worse than a few missing paths: there are two divergent hard-coded lists that disagree with each other.
usr/lib/gargoyle/create_backup.sh(the web Backup page) tars a static list that includes/etc/openvpnbut not/etc/tor.etc/uci-defaults/gargoyle-initwrites/etc/sysupgrade.conf(what an in-placesysupgradepreserves) with a different list:/etc/toryes,/etc/openvpnno,smbpasswdno.Coverage matrix (✅ preserved / ❌ lost):
/etc/openvpn(CA, server keys)/etc/tor/etc/samba/smbpasswd/etc/config/wireguard_gargoyle)/etc/uhttpd.crt/.key)/etc/gargoyle/persistent stateBeyond paths:
board_name, no installed-plugin list — andrestore.shperforms zero version or board validation. An old-version tarball restores raw onto new firmware (the breakage class RFC: Settings Profile — configuration that survives major upgrades (export intent, flash clean, regenerate) #97 addresses), and a tarball from a different model restores its network config onto the wrong hardware.restore.shwholesale-replaces/etc/rc.d/*with the backup's enable symlinks — dangling links for services the current image doesn't ship.Proposal
B1 — one declarative registry, two consumers (kills the divergence)
Adopt OpenWrt's own mechanism instead of inventing one: each package owning state outside
/etc/configships a fragment in/lib/upgrade/keep.d/gargoyle-<feature>(stocksysupgradealready readskeep.d, so the upgrade side becomes correct by the package existing).create_backup.shis rewritten to take its list from the union of/etc/sysupgrade.conf+/lib/upgrade/keep.d/*plus backup-only extras (/tmp/data,/usr/datamonitor state). The two paths can never diverge again, and a plugin's backup coverage ships with the plugin, not as an edit to core scripts someone must remember.P1 fragments:
gargoyle-core(TLS cert/key + explicit/etc/gargoyle/subpaths),plugin-openvpn(/etc/openvpn),plugin-tor(/etc/tor),plugin-usb-storage(smbpasswd).Two rules that keep the registry sound (both verified against sysupgrade's source):
/etc/sysupgrade.conf /lib/upgrade/keep.d/*into one file list). Sogargoyle-coremust list the specific/etc/gargoyle/subpaths to keep, never the whole directory —safe_apply/snapshots are transactional state that must not survive into a different firmware context, and the only way to exclude them is to not list their parent./etc/configmaterial: keep.d paths are carried raw across every upgrade including cross-major, which is exactly the breakage class RFC: Settings Profile — configuration that survives major upgrades (export intent, flash clean, regenerate) #97 exists to prevent. The four P1 fragments all comply. (Related:/etc/rc.dis carried raw by today'ssysupgrade.confand suffers the same class of problem — long-term, service enable-states belong in the RFC: Settings Profile — configuration that survives major upgrades (export intent, flash clean, regenerate) #97 profile, not in raw carry.)B2 — a manifest inside every backup
gargoyle-backup-manifest.jsonin the tarball: Gargoyle + OpenWrt versions,board_name, creation time, the captured path list, the installed Gargoyle plugin list, andprofile_versiononce #97 lands. Costs nothing; enables every safety feature below. Old manifest-less tarballs stay restorable (with the legacy warning).B3 — restore pre-flight (blind restore ends)
Before extracting,
restore.shreads the manifest and gates:/etc/rc.d: restore enable-states only for init scripts that exist on the current image; report the rest.B4 — immediate coverage fixes (independent of the registry)
Land now, small diffs:
/etc/openvpnintosysupgrade.conf,/etc/torinto the backup list, alignsmbpasswd, add the web TLS cert/key to both, add/etc/gargoyle/(excludingsafe_apply/transactional snapshots).B5 — tests + a structural lint
/etc/configmust ship akeep.dfragment — enforced at build time, so the next OpenVPN-class omission fails the build instead of surfacing years later as someone's lost keys.Relationship to #97 (Settings Profile)
Complementary, not competing. Raw backup = full-fidelity snapshot for the same version (keys, certs, monitor data, hand-edits — zero translation). Profile = intent that survives cross-version/cross-device. The Backup page offers both, and B3's pre-flight is the traffic cop that routes each tarball to the right path using the manifest. The manifest's plugin list also feeds #97's Tier-B story (a reinstall checklist after a manual flash).
Phases
create_backup.shreads the union.Full design doc:
docs/backup-coverage-plan.mdinispyisail/gargoyle-tools.Feedback welcome — especially on whether cross-board restore should be refused outright or allowed with the network configs stripped, and any other non-
/etc/configstate paths people know of that the audit missed.All reactions