So I made a basic sketch of my updating mechanism, freely available for anyone to check: https://github.com/aehimself/AEFramework   The only things you'll need are: - AE.Updater.Updater - AE.Misc.FileUtils - AE.Updater.UpdateFile - AE.Application.Settings - AE.Misc.ByteUtils   At the moment it is using the System.Zip2 unit but can be reverted easily to Delphi's built in one by changing it to System.Zip in AE.Updater.Updater. It was built and tested on Delphi 11, relies heavily on generics and other built-in components. File versioning is strictly Windows-only... for the time being I found no better way to determine file data than in AE.Misc.FileUtils. That could use a refactor, but as it works for the time being I didn't bother.   To test, have any type of web server ready and decide where you want to put your repository. Let's say our repository will be https://dev.lan/updates, locally available at D:\WWW_root\devlan\updates. I'll make the references accordingly. - Create a TAEUpdateFile instance and add a test product: updatefile := TAEUpdateFile.Create; Try  var fname = ParamStr(0);  updatefile.Product[FileProduct(fname)].URL := 'myproduct';  var pfile := updatefile.Product[FileProduct(fname)].ProjectFile[ExtractFileName(fname)];  pfile.LocalFileName := fname;  var ver = FileVersion(fname).VersionNumber;  var fver = pfile.Version[ver];  fver.ArchiveFileName := ChangeFileExt(ExtractFileName(fname), Format('_%s.zip', [FileVersionToString(ver)]));  fver.Changelog := 'Improved some stuff' + sLineBreak +    'Broke lots of things I don''t yet know about';  fver.DeploymentDate := 1; // Use your favorite UNIX timestamping method, just don't leave it on 0. 0 means undeployed and will not be considered when checking for updates  var ms := TMemoryStream.Create;  Try   updatefile.SaveToStream(ms);   ms.SaveToFile('D:\WWW_root\devlan\updates\update.dat');  Finally   ms.Free;  End; Finally  updatefile.Free; End; Deploying the actual update file is manual for the time being, just zip your .exe, rename it to "Project1_1.0.0.0.zip" (or whatever the original .EXE name and version number is) and copy it to D:\WWW_root\devlan\updates\myproduct. Basically right next to the update file there will be a bunch of folders (one for each product) and inside this folder there will be tons of .zip files, one for each version of each file. Later on this can be used to downgrade as long as the .zip is still available.   Updating is a lot easier: Var  upd: TAEUpdater;  s, fname: String;  ver: UInt64; Begin  upd := TAEUpdater.Create(nil);  Try   upd.UpdateFileURL := 'https://dev.lan/updates/updates.dat';   upd.UpdateFileEtag := _etag; // string var on form to minimize web traffic   upd.CheckForUpdates;   _etag := upd.UpdateFileEtag;   s := '';   For fname In upd.UpdateableFiles Do    Begin     s := s + fname + sLineBreak;     For ver In upd.UpdateableFileVersions[fname] Do      s := s + FileVersionToString(ver) + sLineBreak + upd.FileVersionChangelog[fname, ver] + sLineBreak + sLineBreak;     upd.Update(fname);    End;   If Not s.IsEmpty Then ShowMessage(s);  Finally   FreeAndNil(upd);  End; At the start of your application call TAEUpdater.Cleanup to remove the old version of files - if any.   Todo: Error checking and handling... empty product url will probably result in 404 (unless https://dev.lan/updates//file.zip is a valid URL - didn't check). Files in subfolders aren't yet supported, all will be placed right next to the executable. Files without version information are not yet supported. Hash checking to be implemented, messages to be added, plus a basic demo app to manipulate the update file... in the long run I might replace generics and allow a custom way to download the files so instead of TNetHTTPClient ICS or Indy can be used according to the users taste. Yeah, this is only a skeleton for the time being but it seems to work.   Any suggestion is greatly appreciated!