Two changes that remove manual steps from keeping a Setu install current. Today a new app version reaches the Updates tab only after an operator publishes its manifest again, and Setu itself is upgraded by re-running install.sh with the flags of the first install.
1. Apps follow their manifest URL
An app published from a URL follows that URL: Setu fetches it again on a schedule and publishes any new version it finds. Nothing is installed or updated on a site; a new version only appears in the site's Updates tab.
- Schema. Migration
0004_app_sources.sqladds a tableapp_sourceswith one row per app:
A separate table keeps every existingappsquery unchanged.app_id, the primary key, referencesapps (id)withON DELETE CASCADE;url, the manifest URL;checked_at, when the last check finished, andlast_error, its error or NULL.
- Publish.
POST /api/v1/appswithurlstores the URL as the app's source (replacing an earlier one). Publishing pasted TOML leaves the source as it is. - Check. One check of one app:
A check never replaces a version that is already published, even when the manifest at the URL changed: a manifest names binaries and their checksums, and an unattended re-publish must not swap what a running version installs. Re-publishing by hand still replaces it, as today.- fetches the URL with the publish guards (public addresses only, no redirects, 10 s, 1 MiB);
- parses and validates the manifest;
- refuses a manifest whose
app.idis not the followed app's; - when the version is not yet published, publishes it, audited as
app.publishwith no operator and the detail<app>/<version> (followed); - records
checked_atandlast_error.
- Schedule. The control plane checks every followed app every 6 hours (on the scheduler tick, the first check one tick after start). Failures are recorded and retried at the next round; they are not errors of the panel.
- API.
AppOutgainssource: { url, checked_at, last_error } | null.POST /api/v1/apps/{id}/check(operator and above) runs a check now and returns{ published: <version> | null, source }; 404 when the app follows no URL.DELETE /api/v1/apps/{id}/source(operator and above) stops following; 404 when there is none. Audited asapp.source_clear.
- UI. The Apps page shows, per app, "Follows <url> · checked <time>" (or the last error) with Check now and Stop following.
2. setu upgrade
setu upgrade [--version <ver>], run as root on any Setu server, moves the installed binary to a release:
- Resolves the version:
--version, else the latest release ofsetucp/setu(GitHub API). Already on it → prints so and exits 0. - Downloads
setu-<ver>-<target>.tar.gzandSHA256SUMSfrom the release, refuses an archive whose checksum does not match, extracts it (tar) and checks the new binary reportssetu <ver>. - On a control plane (
setu.serviceenabled): takes a control-plane backup into the self-backup directory (the same dump and rotation as the nightly one), and stops if it fails. - Installs the binary at
/opt/setu/setu(write.setu.new, rename). - Restarts
setu.service(migrations run on boot) and, when enabled,setu-agent.service. - On a control plane, waits up to 90 s for
/healthzon the configured listener and fails loudly if it never answers.
The config (setu.toml) is read from /etc/setu when the current directory has none, so setu upgrade and setu backup work from any directory. Downgrades are refused (migrations are forward-only) unless --allow-downgrade.
3. Docs
docs/OPERATIONS.md and docs/INSTALL.md describe setu upgrade; the backup command no longer depends on the current directory.
Not in scope
Automatic upgrades of Setu itself, upgrading other servers from the panel, and following URLs of private repositories (no auth headers).
Tests
- Follow: publishing from a URL stores the source; a check publishes a new version, leaves an existing one unchanged, refuses another app's id and records errors; stop following clears it. URL fetches in tests go through an injectable fetcher (the real one refuses local addresses).
- Upgrade: checksum parsing and matching, version comparison and the downgrade refusal, archive name per target; the install-and-restart path is covered by
scripts/test-install.sh.