Package: notsotuf
notsotufA simple software updater for stand-alone Pythonapplications.Thenotsotufpackage is built on top ofpython-tuf, which is the reference implementation forTUF(The Update Framework).The initial implementation is focused on Windows.
The package can be used on other platforms, but these are not actively supported.AboutThenotsotufpackage was inspired byPyUpdater, and uses a general approach to updating that is directly based on PyUpdater's implementation.However, whereas PyUpdater implements acustomsecurity mechanism to ensure authenticity (and integrity) of downloaded update files,notsotufis built on top of the security mechanisms implemented in thepython-tufpackage, a.k.a.tuf.
By entrusting the design of security measures to the security professionals,notsotufcan focus on high-level tools.Althoughtufsupports highly complex security infrastructures, it also offers sufficient flexibility to allow application developers to tailor the security level to their use case.
For details and best practices, refer to thetuf docs.A detailed discussion of the intricacies of TUF adoption can be found inPEP458.OverviewBorrowingtufterminology, we have tools for the repository (repo) side and tools for theclientside.Therepotools are used by the app developer to:create update files (e.g. using PyInstaller)sign the resulting files (cryptographically)deploy these files to a serverTheclienttools are used by the app itself to:check for updatesdownload update filesapply the update filesThetufpackage is used under the hood to check for updates and download update files in a secure manner, sonotsotufcan safely apply the update.
See thetuf docsfor more information.Archives and patchesNotsotuf works witharchives(e.g. gzipped PyInstaller bundles) andpatches(binary differences between subsequent archives).
Each archive, except the first one, must have a corresponding patch file.Archive filenames and patch filenames follow the pattern<name>-<version><suffix>wherenameis a short string that may contain alphanumeric characters, underscores, and hyphens,versionis a version string according to thePEP440specification, andsuffixis either'.tar.gz'(including'.tar.gz') or'.patch'.Patches are typically smaller than archives, so the notsotuf client will always attempt to update using one or more patches.
However, if the total amount of patch data is greater than the desired full archive file, a full update will be performed.How updates are created (repo-side)When a new release of your application is ready, the following steps need to be taken to enable clients to update to that new release:Create an application archive for the new release (e.g. a zipped PyInstaller bundle).Create a patch from the current archive to the new archive.Add hashes for the newly created archive file and patch file to thetufmetadata.Sign the modifiedtufmetadata files.Upload the new target files, i.e. archive and patch, and the updated metadata files, to the update server.The signed metadata and hashes ensure both authenticity and integrity of the update files (seetuf docs).
In order to sign the metadata, we need access to the private key files for the applicabletufroles.Thenotsotuf.tools.repomodule provides a convenient way to streamline the above procedure, based on thetufbasic repo example.How updates are applied (client-side)Updates are applied by replacing all files in the current app installation path with files from the latest archive.
The latest archive is either downloaded in full (as described above), or it is derived from the current archive by applying one or more downloaded patches.
Once the latest archive is available, it is decompressed to a temporary location.
From there, a script is started that clears the current app installation dir, and moves the new files into place.
After starting the script, the currently running process will exit.Migrating from other update frameworksHere's one way to migrate from another update framework, such aspyupdater, tonotsotuf:Addnotsotufto your main application environment as a core dependency, and movepyupdaterfrom core dependencies to development dependencies.Replace allpyupdaterclient code (and configuration) in your application by thenotsotufclient.Build, package, and sign usingpyupdater, and deploy to your server, as usual.
This will ensure yourpyupdaterclients currently in the field will be able to update to the newnotsotufclient.
From here on, new updates will be deployed usingnotsotuf.Set up yournotsotufrepository (on the same server or another server), but keep thepyupdaterrepository in place as long as necessary to allow all clients to update.From now on, build, package, sign and deploy usingnotsotuf, as described elsewhere in this document.Platform supportThenotsotufpackage is aimed primarily atWindowsapplications.
Basicnotsotuf.clientfunctionality should also work on Mac or Linux, provided a custommove_and_exithook is specified inClient.update().
However, these platforms are not actively supported.
Moreover, Linux applications are probably better off using native packaging solutions, or solutions such as Flatpak or Snapcraft.Read thePython packaging overviewfor more information.