diff --git a/main.js b/main.js
index d4841cec2..c1831379f 100644
--- a/main.js
+++ b/main.js
@@ -1,17 +1,22 @@
 /* eslint-disable no-undef */
 /* eslint-disable @typescript-eslint/no-var-requires */
 const { app, BrowserWindow, ipcMain } = require("electron");
-const url = require("url");
-const path = require("path");
+const electronStore = require("electron-store");
 const fs = require("fs");
-const Store = require("electron-store");
+const got = require('got');
+const path = require("path");
+const url = require("url");
+const stream = require('stream');
+const {promisify} = require('util');
+const progress = require('progress-stream');
 
-const store = new Store();
 const exec = require("child_process").exec;
 
+const store = new electronStore();
+
 const args = process.argv.slice(1);
-const dev = args.some((val) => val === "--serve");
 const big = args.some((val) => val === "--big");
+const dev = args.some((val) => val === "--serve");
 
 app.commandLine.appendSwitch("touch-events", "enabled");
 app.allowRendererProcessReuse = true;
@@ -68,10 +73,10 @@ function createWindow() {
     window.setFullScreen(true);
   }
 
-  // setTimeout(sendVersionInfo, 30 * 1000);
   activateAppInfoListener();
   activateScreenSleepListener();
   activateReloadListener();
+  activateUpdateListener();
 
   window.on("closed", () => {
     window = null;
@@ -109,6 +114,12 @@ function activateAppInfoListener() {
   });
 }
 
+function activateUpdateListener() {
+  ipcMain.on("update", (_, updateInfo) => {
+    downloadUpdate(updateInfo);
+  });
+}
+
 function sendCustomStyles() {
   fs.readFile(path.join(app.getPath("userData"), "custom-styles.css"), "utf-8", (err, data) => {
     if (err) {
@@ -135,6 +146,92 @@ function sendVersionInfo() {
   });
 }
 
+function downloadUpdate(updateInfo) {
+  const downloadPath = "/tmp/octodash.deb";
+
+  exec("arch", (err, stdout, stderr) => {
+    if (err || stderr) {
+      window.webContents.send("updateError", {
+        error: err ? err : { message: stderr },
+      });
+    }
+    got(updateInfo.assetsURL)
+    .then((releaseFiles) => {
+      const reducer = (accumulator, currentValue) => accumulator + currentValue;
+      let averageETA = [];
+      let downloadURL;
+      let packageSize;
+      for (let package of JSON.parse(releaseFiles.body)) {
+        if (package.name.includes(stdout.trim())) {
+          downloadURL = package.browser_download_url;
+          packageSize = package.size;
+        }
+      }
+      if (downloadURL) {
+        const downloadPipeline = promisify(stream.pipeline);
+        let downloadProgress = progress({
+          length: packageSize,
+          time: 300,
+        });
+
+        downloadProgress.on('progress', (progress) => {
+          averageETA.push(progress.eta);
+          if (averageETA.length > 4) averageETA.shift();
+          window.webContents.send("updateDownloadProgress", {
+            percentage: progress.percentage,
+            transferred: (progress.transferred / 100000).toFixed(1),
+            total: (progress.length / 1000000).toFixed(1),
+            remaining: (progress.remaining / 100000).toFixed(1),
+            eta: new Date(averageETA.reduce(reducer) * 1000).toISOString().substr(14, 5),
+            runtime: new Date(progress.runtime * 1000).toISOString().substr(14, 5),
+            delta: (progress.delta / 100000).toFixed(1),
+            speed: (progress.speed / 1000000).toFixed(2),
+          })
+        })
+
+        try {
+          if (fs.existsSync(downloadPath)) fs.unlinkSync(downloadPath)
+        } catch {
+          // no need to handle this properly
+        }
+
+        downloadPipeline(
+          got.stream(downloadURL),
+          downloadProgress,
+          fs.createWriteStream(downloadPath)
+        ).catch((error) => {
+          window.webContents.send("updateError", {
+            error: {
+              message: `Can't download package! ${error.message}.`
+            }
+          })
+        }).then(() => {
+          window.webContents.send("updateDownloadFinished");
+          exec('sudo ~/scripts/update-octodash', (err, _, stderr) => {
+            if (err || stderr) {
+              window.webContents.send("updateError", {
+                error: err ? err : { message: stderr },
+              });
+            } else {
+              window.webContents.send("updateInstalled");
+            }
+          })
+        })
+      } else {
+        window.webContents.send("updateError", {
+          error: {
+            message: `Can't find matching package for architecture ${stdout}.`
+          }
+        })
+      }
+    })
+    .catch((error) => {
+      error.message = `Can't load releases. ${error.message}`;
+      window.webContents.send("updateError", {error});
+    })
+  });
+}
+
 app.on("ready", createWindow);
 
 app.on("window-all-closed", () => {
diff --git a/package-lock.json b/package-lock.json
index 2d60b70be..dd2468ae9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1953,6 +1953,68 @@
         "sumchecker": "^3.0.1"
       },
       "dependencies": {
+        "@sindresorhus/is": {
+          "version": "0.14.0",
+          "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz",
+          "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==",
+          "dev": true
+        },
+        "@szmarczak/http-timer": {
+          "version": "1.1.2",
+          "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz",
+          "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==",
+          "dev": true,
+          "requires": {
+            "defer-to-connect": "^1.0.1"
+          }
+        },
+        "cacheable-request": {
+          "version": "6.1.0",
+          "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz",
+          "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==",
+          "dev": true,
+          "requires": {
+            "clone-response": "^1.0.2",
+            "get-stream": "^5.1.0",
+            "http-cache-semantics": "^4.0.0",
+            "keyv": "^3.0.0",
+            "lowercase-keys": "^2.0.0",
+            "normalize-url": "^4.1.0",
+            "responselike": "^1.0.2"
+          },
+          "dependencies": {
+            "get-stream": {
+              "version": "5.1.0",
+              "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz",
+              "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==",
+              "dev": true,
+              "requires": {
+                "pump": "^3.0.0"
+              }
+            },
+            "lowercase-keys": {
+              "version": "2.0.0",
+              "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
+              "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==",
+              "dev": true
+            }
+          }
+        },
+        "decompress-response": {
+          "version": "3.3.0",
+          "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz",
+          "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=",
+          "dev": true,
+          "requires": {
+            "mimic-response": "^1.0.0"
+          }
+        },
+        "defer-to-connect": {
+          "version": "1.1.3",
+          "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz",
+          "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==",
+          "dev": true
+        },
         "fs-extra": {
           "version": "8.1.0",
           "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
@@ -1963,6 +2025,73 @@
             "jsonfile": "^4.0.0",
             "universalify": "^0.1.0"
           }
+        },
+        "got": {
+          "version": "9.6.0",
+          "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz",
+          "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==",
+          "dev": true,
+          "requires": {
+            "@sindresorhus/is": "^0.14.0",
+            "@szmarczak/http-timer": "^1.1.2",
+            "cacheable-request": "^6.0.0",
+            "decompress-response": "^3.3.0",
+            "duplexer3": "^0.1.4",
+            "get-stream": "^4.1.0",
+            "lowercase-keys": "^1.0.1",
+            "mimic-response": "^1.0.1",
+            "p-cancelable": "^1.0.0",
+            "to-readable-stream": "^1.0.0",
+            "url-parse-lax": "^3.0.0"
+          }
+        },
+        "http-cache-semantics": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
+          "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==",
+          "dev": true
+        },
+        "json-buffer": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz",
+          "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=",
+          "dev": true
+        },
+        "keyv": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz",
+          "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==",
+          "dev": true,
+          "requires": {
+            "json-buffer": "3.0.0"
+          }
+        },
+        "lowercase-keys": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
+          "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==",
+          "dev": true
+        },
+        "normalize-url": {
+          "version": "4.5.0",
+          "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz",
+          "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==",
+          "dev": true
+        },
+        "p-cancelable": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz",
+          "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==",
+          "dev": true
+        },
+        "responselike": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz",
+          "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=",
+          "dev": true,
+          "requires": {
+            "lowercase-keys": "^1.0.0"
+          }
         }
       }
     },
@@ -2226,18 +2355,16 @@
       }
     },
     "@sindresorhus/is": {
-      "version": "0.14.0",
-      "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz",
-      "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==",
-      "dev": true
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-3.0.0.tgz",
+      "integrity": "sha512-kqA5I6Yun7PBHk8WN9BBP1c7FfN2SrD05GuVSEYPqDb4nerv7HqYfgBfMIKmT/EuejURkJKLZuLyGKGs6WEG9w=="
     },
     "@szmarczak/http-timer": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz",
-      "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==",
-      "dev": true,
+      "version": "4.0.5",
+      "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz",
+      "integrity": "sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==",
       "requires": {
-        "defer-to-connect": "^1.0.1"
+        "defer-to-connect": "^2.0.0"
       }
     },
     "@types/ajv": {
@@ -2249,6 +2376,17 @@
         "ajv": "*"
       }
     },
+    "@types/cacheable-request": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz",
+      "integrity": "sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==",
+      "requires": {
+        "@types/http-cache-semantics": "*",
+        "@types/keyv": "*",
+        "@types/node": "*",
+        "@types/responselike": "*"
+      }
+    },
     "@types/color-name": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
@@ -2286,6 +2424,11 @@
         "@types/node": "*"
       }
     },
+    "@types/http-cache-semantics": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz",
+      "integrity": "sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A=="
+    },
     "@types/json-schema": {
       "version": "7.0.4",
       "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz",
@@ -2298,6 +2441,14 @@
       "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
       "dev": true
     },
+    "@types/keyv": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz",
+      "integrity": "sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==",
+      "requires": {
+        "@types/node": "*"
+      }
+    },
     "@types/lodash": {
       "version": "4.14.158",
       "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.158.tgz",
@@ -2311,10 +2462,9 @@
       "dev": true
     },
     "@types/node": {
-      "version": "14.0.27",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.27.tgz",
-      "integrity": "sha512-kVrqXhbclHNHGu9ztnAwSncIgJv/FaxmzXJvGXNdcCpV1b8u1/Mi6z6m0vwy0LzKeXFTPLH0NzwmoJ3fNCIq0g==",
-      "dev": true
+      "version": "14.0.14",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.14.tgz",
+      "integrity": "sha512-syUgf67ZQpaJj01/tRTknkMNoBBLWJOBODF0Zm4NrXmiSuxjymFrxnTu1QVYRubhVkRcZLYZG8STTwJRdVm/WQ=="
     },
     "@types/q": {
       "version": "1.5.4",
@@ -2322,6 +2472,14 @@
       "integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==",
       "dev": true
     },
+    "@types/responselike": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz",
+      "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==",
+      "requires": {
+        "@types/node": "*"
+      }
+    },
     "@types/source-list-map": {
       "version": "0.1.2",
       "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz",
@@ -4064,26 +4222,29 @@
         "unset-value": "^1.0.0"
       }
     },
+    "cacheable-lookup": {
+      "version": "5.0.3",
+      "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.3.tgz",
+      "integrity": "sha512-W+JBqF9SWe18A72XFzN/V/CULFzPm7sBXzzR6ekkE+3tLG72wFZrBiBZhrZuDoYexop4PHJVdFAKb/Nj9+tm9w=="
+    },
     "cacheable-request": {
-      "version": "6.1.0",
-      "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz",
-      "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==",
-      "dev": true,
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz",
+      "integrity": "sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==",
       "requires": {
         "clone-response": "^1.0.2",
         "get-stream": "^5.1.0",
         "http-cache-semantics": "^4.0.0",
-        "keyv": "^3.0.0",
+        "keyv": "^4.0.0",
         "lowercase-keys": "^2.0.0",
         "normalize-url": "^4.1.0",
-        "responselike": "^1.0.2"
+        "responselike": "^2.0.0"
       },
       "dependencies": {
         "get-stream": {
           "version": "5.1.0",
           "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz",
           "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==",
-          "dev": true,
           "requires": {
             "pump": "^3.0.0"
           }
@@ -4091,20 +4252,12 @@
         "http-cache-semantics": {
           "version": "4.1.0",
           "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
-          "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==",
-          "dev": true
-        },
-        "lowercase-keys": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
-          "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==",
-          "dev": true
+          "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ=="
         },
         "normalize-url": {
           "version": "4.5.0",
           "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz",
-          "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==",
-          "dev": true
+          "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ=="
         }
       }
     },
@@ -4368,7 +4521,6 @@
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz",
       "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=",
-      "dev": true,
       "requires": {
         "mimic-response": "^1.0.0"
       }
@@ -4860,8 +5012,7 @@
     "core-util-is": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
-      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
-      "dev": true
+      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
     },
     "cosmiconfig": {
       "version": "5.2.1",
@@ -5317,12 +5468,18 @@
       "dev": true
     },
     "decompress-response": {
-      "version": "3.3.0",
-      "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz",
-      "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=",
-      "dev": true,
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
+      "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
       "requires": {
-        "mimic-response": "^1.0.0"
+        "mimic-response": "^3.1.0"
+      },
+      "dependencies": {
+        "mimic-response": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
+          "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="
+        }
       }
     },
     "deep-equal": {
@@ -5379,10 +5536,9 @@
       }
     },
     "defer-to-connect": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz",
-      "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==",
-      "dev": true
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.0.tgz",
+      "integrity": "sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg=="
     },
     "define-properties": {
       "version": "1.1.3",
@@ -6217,7 +6373,6 @@
       "version": "1.4.4",
       "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
       "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
-      "dev": true,
       "requires": {
         "once": "^1.4.0"
       }
@@ -7710,22 +7865,21 @@
       }
     },
     "got": {
-      "version": "9.6.0",
-      "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz",
-      "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==",
-      "dev": true,
-      "requires": {
-        "@sindresorhus/is": "^0.14.0",
-        "@szmarczak/http-timer": "^1.1.2",
-        "cacheable-request": "^6.0.0",
-        "decompress-response": "^3.3.0",
-        "duplexer3": "^0.1.4",
-        "get-stream": "^4.1.0",
-        "lowercase-keys": "^1.0.1",
-        "mimic-response": "^1.0.1",
-        "p-cancelable": "^1.0.0",
-        "to-readable-stream": "^1.0.0",
-        "url-parse-lax": "^3.0.0"
+      "version": "11.5.0",
+      "resolved": "https://registry.npmjs.org/got/-/got-11.5.0.tgz",
+      "integrity": "sha512-vOZEcEaK0b6x11uniY0HcblZObKPRO75Jvz53VKuqGSaKCM/zEt0sj2LGYVdqDYJzO3wYdG+FPQQ1hsgoXy7vQ==",
+      "requires": {
+        "@sindresorhus/is": "^3.0.0",
+        "@szmarczak/http-timer": "^4.0.5",
+        "@types/cacheable-request": "^6.0.1",
+        "@types/responselike": "^1.0.0",
+        "cacheable-lookup": "^5.0.3",
+        "cacheable-request": "^7.0.1",
+        "decompress-response": "^6.0.0",
+        "http2-wrapper": "^1.0.0-beta.4.8",
+        "lowercase-keys": "^2.0.0",
+        "p-cancelable": "^2.0.0",
+        "responselike": "^2.0.0"
       }
     },
     "graceful-fs": {
@@ -8136,6 +8290,15 @@
         "sshpk": "^1.7.0"
       }
     },
+    "http2-wrapper": {
+      "version": "1.0.0-beta.5.2",
+      "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.0-beta.5.2.tgz",
+      "integrity": "sha512-xYz9goEyBnC8XwXDTuC/MZ6t+MrKVQZOk4s7+PaDkwIsQd8IwqvM+0M6bA/2lvG8GHXcPdf+MejTUeO2LCPCeQ==",
+      "requires": {
+        "quick-lru": "^5.1.1",
+        "resolve-alpn": "^1.0.0"
+      }
+    },
     "https-browserify": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
@@ -8305,8 +8468,7 @@
     "inherits": {
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
-      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
-      "dev": true
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
     },
     "ini": {
       "version": "1.3.5",
@@ -8774,8 +8936,7 @@
     "isarray": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-      "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
-      "dev": true
+      "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
     },
     "isbinaryfile": {
       "version": "4.0.6",
@@ -8895,10 +9056,9 @@
       "dev": true
     },
     "json-buffer": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz",
-      "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=",
-      "dev": true
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+      "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="
     },
     "json-parse-better-errors": {
       "version": "1.0.2",
@@ -8986,12 +9146,11 @@
       }
     },
     "keyv": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz",
-      "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==",
-      "dev": true,
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.1.tgz",
+      "integrity": "sha512-xz6Jv6oNkbhrFCvCP7HQa8AaII8y8LRpoSm661NOKLr4uHuBwhX4epXrPQgF3+xdJnN4Esm5X0xwY4bOlALOtw==",
       "requires": {
-        "json-buffer": "3.0.0"
+        "json-buffer": "3.0.1"
       }
     },
     "killable": {
@@ -9226,10 +9385,9 @@
       }
     },
     "lowercase-keys": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
-      "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==",
-      "dev": true
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
+      "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA=="
     },
     "lru-cache": {
       "version": "5.1.1",
@@ -9505,8 +9663,7 @@
     "mimic-response": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
-      "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==",
-      "dev": true
+      "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="
     },
     "mini-css-extract-plugin": {
       "version": "0.9.0",
@@ -10333,7 +10490,6 @@
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
       "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
-      "dev": true,
       "requires": {
         "wrappy": "1"
       }
@@ -10508,10 +10664,9 @@
       }
     },
     "p-cancelable": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz",
-      "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==",
-      "dev": true
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz",
+      "integrity": "sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg=="
     },
     "p-finally": {
       "version": "1.0.0",
@@ -10568,6 +10723,137 @@
         "registry-auth-token": "^4.0.0",
         "registry-url": "^5.0.0",
         "semver": "^6.2.0"
+      },
+      "dependencies": {
+        "@sindresorhus/is": {
+          "version": "0.14.0",
+          "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz",
+          "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==",
+          "dev": true
+        },
+        "@szmarczak/http-timer": {
+          "version": "1.1.2",
+          "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz",
+          "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==",
+          "dev": true,
+          "requires": {
+            "defer-to-connect": "^1.0.1"
+          }
+        },
+        "cacheable-request": {
+          "version": "6.1.0",
+          "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz",
+          "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==",
+          "dev": true,
+          "requires": {
+            "clone-response": "^1.0.2",
+            "get-stream": "^5.1.0",
+            "http-cache-semantics": "^4.0.0",
+            "keyv": "^3.0.0",
+            "lowercase-keys": "^2.0.0",
+            "normalize-url": "^4.1.0",
+            "responselike": "^1.0.2"
+          },
+          "dependencies": {
+            "get-stream": {
+              "version": "5.1.0",
+              "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz",
+              "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==",
+              "dev": true,
+              "requires": {
+                "pump": "^3.0.0"
+              }
+            },
+            "lowercase-keys": {
+              "version": "2.0.0",
+              "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
+              "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==",
+              "dev": true
+            }
+          }
+        },
+        "decompress-response": {
+          "version": "3.3.0",
+          "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz",
+          "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=",
+          "dev": true,
+          "requires": {
+            "mimic-response": "^1.0.0"
+          }
+        },
+        "defer-to-connect": {
+          "version": "1.1.3",
+          "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz",
+          "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==",
+          "dev": true
+        },
+        "got": {
+          "version": "9.6.0",
+          "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz",
+          "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==",
+          "dev": true,
+          "requires": {
+            "@sindresorhus/is": "^0.14.0",
+            "@szmarczak/http-timer": "^1.1.2",
+            "cacheable-request": "^6.0.0",
+            "decompress-response": "^3.3.0",
+            "duplexer3": "^0.1.4",
+            "get-stream": "^4.1.0",
+            "lowercase-keys": "^1.0.1",
+            "mimic-response": "^1.0.1",
+            "p-cancelable": "^1.0.0",
+            "to-readable-stream": "^1.0.0",
+            "url-parse-lax": "^3.0.0"
+          }
+        },
+        "http-cache-semantics": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
+          "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==",
+          "dev": true
+        },
+        "json-buffer": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz",
+          "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=",
+          "dev": true
+        },
+        "keyv": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz",
+          "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==",
+          "dev": true,
+          "requires": {
+            "json-buffer": "3.0.0"
+          }
+        },
+        "lowercase-keys": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
+          "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==",
+          "dev": true
+        },
+        "normalize-url": {
+          "version": "4.5.0",
+          "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz",
+          "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==",
+          "dev": true
+        },
+        "p-cancelable": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz",
+          "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==",
+          "dev": true
+        },
+        "responselike": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz",
+          "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=",
+          "dev": true,
+          "requires": {
+            "lowercase-keys": "^1.0.0"
+          }
+        }
       }
     },
     "pacote": {
@@ -11633,8 +11919,7 @@
     "process-nextick-args": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
-      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
-      "dev": true
+      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
     },
     "progress": {
       "version": "2.0.3",
@@ -11642,6 +11927,25 @@
       "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
       "dev": true
     },
+    "progress-stream": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/progress-stream/-/progress-stream-2.0.0.tgz",
+      "integrity": "sha1-+sY6Cz0R3qy7CWmrzJOyFLzhntU=",
+      "requires": {
+        "speedometer": "~1.0.0",
+        "through2": "~2.0.3"
+      }
+    },
+    "promise": {
+      "version": "7.3.1",
+      "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
+      "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
+      "dev": true,
+      "optional": true,
+      "requires": {
+        "asap": "~2.0.3"
+      }
+    },
     "promise-inflight": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
@@ -11730,7 +12034,6 @@
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
       "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
-      "dev": true,
       "requires": {
         "end-of-stream": "^1.1.0",
         "once": "^1.3.1"
@@ -11813,6 +12116,11 @@
       "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==",
       "dev": true
     },
+    "quick-lru": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
+      "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA=="
+    },
     "randombytes": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
@@ -12021,7 +12329,6 @@
       "version": "2.3.7",
       "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
       "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
-      "dev": true,
       "requires": {
         "core-util-is": "~1.0.0",
         "inherits": "~2.0.3",
@@ -12249,6 +12556,11 @@
         "path-parse": "^1.0.6"
       }
     },
+    "resolve-alpn": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.0.0.tgz",
+      "integrity": "sha512-rTuiIEqFmGxne4IovivKSDzld2lWW9QCjqv80SYjPgf+gS35eaCAjaP54CCwGAwBtnCsvNLYtqxe1Nw+i6JEmA=="
+    },
     "resolve-cwd": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz",
@@ -12343,12 +12655,11 @@
       }
     },
     "responselike": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz",
-      "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=",
-      "dev": true,
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz",
+      "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==",
       "requires": {
-        "lowercase-keys": "^1.0.0"
+        "lowercase-keys": "^2.0.0"
       }
     },
     "restore-cursor": {
@@ -12506,8 +12817,7 @@
     "safe-buffer": {
       "version": "5.1.2",
       "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
-      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
-      "dev": true
+      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
     },
     "safe-regex": {
       "version": "1.1.0",
@@ -13313,6 +13623,11 @@
         "chalk": "^2.0.1"
       }
     },
+    "speedometer": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/speedometer/-/speedometer-1.0.0.tgz",
+      "integrity": "sha1-zWccsGdSwivKM3Di8zREC+T8YuI="
+    },
     "split-string": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
@@ -13506,7 +13821,6 @@
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
       "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
-      "dev": true,
       "requires": {
         "safe-buffer": "~5.1.0"
       }
@@ -13884,7 +14198,6 @@
       "version": "2.0.5",
       "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
       "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
-      "dev": true,
       "requires": {
         "readable-stream": "~2.3.6",
         "xtend": "~4.0.1"
@@ -14491,8 +14804,7 @@
     "util-deprecate": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
-      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
-      "dev": true
+      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
     },
     "util-promisify": {
       "version": "2.1.0",
@@ -15685,8 +15997,7 @@
     "wrappy": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
-      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
-      "dev": true
+      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
     },
     "write": {
       "version": "1.0.3",
@@ -15727,8 +16038,7 @@
     "xtend": {
       "version": "4.0.2",
       "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
-      "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
-      "dev": true
+      "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
     },
     "y18n": {
       "version": "4.0.0",
diff --git a/package.json b/package.json
index 9fb7bbf18..9e408d121 100644
--- a/package.json
+++ b/package.json
@@ -54,6 +54,7 @@
     "lint": "eslint ./src/ -c .eslintrc.js --ext .ts",
     "lint:fix": "eslint ./src/ -c .eslintrc.js --ext .ts --fix",
     "electron": "electron .",
+    "electron:dev": "electron . --serve --big",
     "electron:serve": "wait-on http-get://localhost:4200/ && electron . --serve",
     "electron:serve:big": "wait-on http-get://localhost:4200/ && electron . --serve --big",
     "pack": "npm run ng:build && electron-builder build -l",
@@ -81,6 +82,7 @@
     "electron-store": "^6.0.0",
     "lodash": "^4.17.19",
     "ngx-spinner": "^10.0.1",
+    "progress-stream": "^2.0.0",
     "rxjs": "~6.6.2",
     "tslib": "^2.0.0",
     "zone.js": "~0.10.3"
diff --git a/scripts/install.sh b/scripts/install.sh
index cd9277472..464abd05e 100755
--- a/scripts/install.sh
+++ b/scripts/install.sh
@@ -757,6 +757,25 @@ EOF
     echo "OctoDash will start automatically on next reboot. Please ensure that auto-login is enabled!"
 fi
 
+list_input "Should I setup the update script? This will allow installing '~/tmp/octodash.deb' without sudo or root access. For more info visit the Update section of the wiki. " yes_no update
+if [ $update == 'yes' ]; then
+    mkdir -p ~/scripts
+    echo "Setting up update script ..."
+    cat <<EOF > ~/scripts/update-octodash
+#!/bin/bash
+
+dpkg -i /tmp/octodash.deb
+rm /tmp/octodash.deb
+EOF
+
+    sudo chmod +x ~/scripts/update-octodash
+
+    sudo bash -c 'cat >> /etc/sudoers.d/update-octodash' <<EOF
+pi ALL=NOPASSWD: /home/pi/scripts/update-octodash
+EOF
+fi
+
+
 list_input "Shall I reboot your Pi now?" yes_no reboot
 echo "OctoDash has been successfully installed! :)"
 if [ $reboot == 'yes' ]; then
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index ca944e8e6..cdefd7777 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -5,6 +5,15 @@ import _ from "lodash";
 import { AppService } from "./app.service";
 import { ConfigService } from "./config/config.service";
 
+declare global {
+  interface Window {
+    // eslint-disable-next-line @typescript-eslint/no-explicit-any
+    require: any;
+    // eslint-disable-next-line @typescript-eslint/no-explicit-any
+    process: any;
+  }
+}
+
 @Component({
   selector: "app-root",
   templateUrl: "./app.component.html",
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 25a333234..cd8c126bd 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -32,6 +32,7 @@ import { PrinterStatusComponent } from "./printer-status/printer-status.componen
 import { PrinterService } from "./printer.service";
 import { SettingsComponent } from "./settings/settings.component";
 import { StandbyComponent } from "./standby/standby.component";
+import { UpdateComponent } from "./update/update.component";
 import { URLSafePipe } from "./url.pipe";
 
 @NgModule({
@@ -54,6 +55,7 @@ import { URLSafePipe } from "./url.pipe";
     SettingsComponent,
     URLSafePipe,
     StandbyComponent,
+    UpdateComponent,
   ],
   imports: [
     BrowserModule,
diff --git a/src/app/app.service.ts b/src/app/app.service.ts
index 14a70c27f..3a622db51 100644
--- a/src/app/app.service.ts
+++ b/src/app/app.service.ts
@@ -12,28 +12,28 @@ export class AppService {
   private loadedFile = false;
   // eslint-disable-next-line @typescript-eslint/no-explicit-any
   private ipc: any;
-  private version: string;
-  private latestVersion: string;
+  public version: string;
+  public latestVersion: string;
+  private latestVersionAssetsURL: string;
+  public updateAvailable = false;
 
   public constructor(
     private configService: ConfigService,
     private notificationService: NotificationService,
     private http: HttpClient
   ) {
-    if (window.require) {
-      try {
-        this.ipc = window.require("electron").ipcRenderer;
-        this.enableVersionListener();
-        this.enableCustomCSSListener();
-        setTimeout(() => {
-          this.ipc.send("appInfo");
-        }, 0);
-      } catch (e) {
-        this.notificationService.setError(
-          "Can't retrieve version information",
-          "Please open an issue for GitHub as this shouldn't happen."
-        );
-      }
+    try {
+      this.ipc = window.require("electron").ipcRenderer;
+      this.enableVersionListener();
+      this.enableCustomCSSListener();
+      setTimeout(() => {
+        this.ipc.send("appInfo");
+      }, 0);
+    } catch (e) {
+      this.notificationService.setError(
+        "Can't connect to backend",
+        "Please restart your system. If the issue persists open an issue on GitHub."
+      );
     }
     this.updateError = [];
   }
@@ -62,24 +62,23 @@ export class AppService {
     });
 
     this.ipc.on("customStylesError", (_, customCSSError: string): void => {
-      this.notificationService.setError("Can't get custom styles!", customCSSError);
+      this.notificationService.setError("Can't load custom styles!", customCSSError);
     });
   }
 
   private checkUpdate(): void {
     this.http.get("https://api.github.com/repos/UnchartedBull/OctoDash/releases/latest").subscribe(
       (data: GitHubReleaseInformation): void => {
-        if (this.version !== data.name.replace("v", "")) {
-          this.notificationService.setUpdate(
-            "It's time for an update",
-            `Version ${data.name} is available now, while you're on v${this.version}. Consider updating :)`
-          );
+        //FIXME
+        if (this.version !== data.name.replace("va", "")) {
+          this.updateAvailable = true;
         }
         this.latestVersion = data.name.replace("v", "");
+        this.latestVersionAssetsURL = data.assets_url;
       },
       (): void => null
     );
-    setTimeout(this.checkUpdate.bind(this), 21.6 * 1000000);
+    setTimeout(this.checkUpdate.bind(this), 3600000);
   }
 
   public getVersion(): string {
@@ -114,6 +113,10 @@ export class AppService {
     return this.loadedFile;
   }
 
+  public getLatestVersionAssetsURL(): string {
+    return this.latestVersionAssetsURL;
+  }
+
   public convertByteToMegabyte(byte: number): string {
     return (byte / 1000000).toFixed(1);
   }
@@ -136,16 +139,8 @@ export class AppService {
     return roundedHours + ":" + ("0" + roundedMinutes).slice(-2);
   }
 
-  public convertFilamentLengthToAmount(filamentLength: number): number {
-    return (
-      Math.round(
-        (Math.PI *
-          (this.configService.getFilamentThickness() / 2) *
-          filamentLength *
-          this.configService.getFilamentDensity()) /
-          100
-      ) / 10
-    );
+  public convertFilamentVolumeToWeight(filamentVolume: number): number {
+    return Math.round(filamentVolume * this.configService.getFilamentDensity() * 10) / 10;
   }
 }
 
@@ -155,5 +150,7 @@ interface VersionInformation {
 
 interface GitHubReleaseInformation {
   name: string;
+  // eslint-disable-next-line camelcase
+  assets_url: string;
   [key: string]: string;
 }
diff --git a/src/app/config/config.service.ts b/src/app/config/config.service.ts
index 2a4d8ce0f..a43153e70 100644
--- a/src/app/config/config.service.ts
+++ b/src/app/config/config.service.ts
@@ -1,18 +1,9 @@
-import { HttpClient, HttpHeaders } from "@angular/common/http";
+import { HttpHeaders } from "@angular/common/http";
 import { Injectable } from "@angular/core";
 import Ajv from "ajv";
 import _ from "lodash";
 
-import { environment } from "../../environments/environment";
-
-declare global {
-  interface Window {
-    // eslint-disable-next-line @typescript-eslint/no-explicit-any
-    require: any;
-    // eslint-disable-next-line @typescript-eslint/no-explicit-any
-    process: any;
-  }
-}
+import { NotificationService } from "../notification/notification.service";
 
 @Injectable({
   providedIn: "root",
@@ -29,20 +20,18 @@ export class ConfigService {
 
   private httpHeaders: HttpHeader;
 
-  public constructor(private http: HttpClient) {
+  public constructor(private notificationService: NotificationService) {
     const ajv = new Ajv({ allErrors: true });
     this.validator = ajv.compile(schema);
-    if (window && window.process && window.process.type) {
+    try {
       const Store = window.require("electron-store");
       this.store = new Store();
       this.initialize(this.store.get("config"));
-    } else {
-      console.warn(
-        "Detected non-electron environment. Fallback to assets/config.json. Any changes are non-persistent!"
+    } catch (e) {
+      this.notificationService.setError(
+        "Can't read config file!",
+        "Please restart your system. If the issue persists open an issue on GitHub."
       );
-      this.http.get(environment.config).subscribe((config: Config): void => {
-        this.initialize(config);
-      });
     }
   }
 
diff --git a/src/app/files.service.ts b/src/app/files.service.ts
index ad9c1bb35..a032dcade 100644
--- a/src/app/files.service.ts
+++ b/src/app/files.service.ts
@@ -67,7 +67,7 @@ export class FilesService {
                 let filamentLength = 0;
                 if (fileOrFolder.gcodeAnalysis) {
                   _.forEach(fileOrFolder.gcodeAnalysis.filament, (tool): void => {
-                    filamentLength += tool.length;
+                    filamentLength += tool.volume;
                   });
                 }
 
@@ -88,7 +88,7 @@ export class FilesService {
                   ...(fileOrFolder.gcodeAnalysis
                     ? {
                         printTime: this.service.convertSecondsToHours(fileOrFolder.gcodeAnalysis.estimatedPrintTime),
-                        filamentWeight: this.service.convertFilamentLengthToAmount(filamentLength),
+                        filamentWeight: this.service.convertFilamentVolumeToWeight(filamentLength),
                       }
                     : {}),
                 } as unknown) as File);
@@ -148,7 +148,7 @@ export class FilesService {
             let filamentLength = 0;
             if (data.gcodeAnalysis) {
               _.forEach(data.gcodeAnalysis.filament, (tool): void => {
-                filamentLength += tool.length;
+                filamentLength += tool.volume;
               });
             }
             const file = ({
@@ -160,7 +160,7 @@ export class FilesService {
                 ? {
                     date: this.service.convertDateToString(new Date(data.date * 1000)),
                     printTime: this.service.convertSecondsToHours(data.gcodeAnalysis.estimatedPrintTime),
-                    filamentWeight: this.service.convertFilamentLengthToAmount(filamentLength),
+                    filamentWeight: this.service.convertFilamentVolumeToWeight(filamentLength),
                   }
                 : {}),
               thumbnail: data.thumbnail
diff --git a/src/app/job.service.ts b/src/app/job.service.ts
index b083bca5a..50cea574f 100644
--- a/src/app/job.service.ts
+++ b/src/app/job.service.ts
@@ -53,7 +53,7 @@ export class JobService {
                     progress: Math.round((data.progress.filepos / data.job.file.size) * 100),
                     ...(data.job.filament !== null
                       ? {
-                          filamentAmount: this.service.convertFilamentLengthToAmount(
+                          filamentAmount: this.service.convertFilamentVolumeToWeight(
                             this.getTotalAmountOfFilament(data.job.filament)
                           ),
                         }
@@ -102,10 +102,10 @@ export class JobService {
     let filamentLength = 0;
     for (const property in filamentAmount) {
       if (
-        Object.prototype.hasOwnProperty.call(filamentAmount, "property") &&
-        Object.prototype.hasOwnProperty.call(filamentAmount[property], "length")
+        Object.prototype.hasOwnProperty.call(filamentAmount, property) &&
+        Object.prototype.hasOwnProperty.call(filamentAmount[property], "volume")
       ) {
-        filamentLength += filamentAmount[property].length;
+        filamentLength += filamentAmount[property].volume;
       }
     }
     return filamentLength;
diff --git a/src/app/main-menu/main-menu.component.html b/src/app/main-menu/main-menu.component.html
index b232f5dae..6482e15b9 100644
--- a/src/app/main-menu/main-menu.component.html
+++ b/src/app/main-menu/main-menu.component.html
@@ -3,6 +3,7 @@
     >OctoDash<span class="main-menu__heading" style="color: #1ec02c; display: inline;">.</span></span
   >
   <img src="assets/settings.svg" class="main-menu__settings-icon" (click)="showSettings()" />
+  <div class="main-menu__update-notifier" *ngIf="service.updateAvailable"></div>
   <table class="main-menu__options">
     <tr>
       <td routerLink="/files" matRipple [matRippleUnbounded]="false" [matRippleCentered]="true">
@@ -20,4 +21,4 @@
     </tr>
   </table>
 </div>
-<app-settings *ngIf="settings" (closeFunction)="hideSettings($event)"></app-settings>
+<app-settings *ngIf="settings" (closeFunction)="hideSettings()"></app-settings>
diff --git a/src/app/main-menu/main-menu.component.scss b/src/app/main-menu/main-menu.component.scss
index d2e79c192..cf8658a0e 100644
--- a/src/app/main-menu/main-menu.component.scss
+++ b/src/app/main-menu/main-menu.component.scss
@@ -51,4 +51,14 @@
       margin-bottom: 1.7vw;
     }
   }
+
+  &__update-notifier {
+    width: 2.2vw;
+    height: 2.2vw;
+    border-radius: 4vw;
+    background-color: #e84118;
+    position: absolute;
+    right: 1.9vw;
+    top: 13vh;
+  }
 }
diff --git a/src/app/main-menu/main-menu.component.ts b/src/app/main-menu/main-menu.component.ts
index 546c54f10..bb3e87465 100644
--- a/src/app/main-menu/main-menu.component.ts
+++ b/src/app/main-menu/main-menu.component.ts
@@ -1,11 +1,15 @@
 import { Component } from "@angular/core";
 
+import { AppService } from "../app.service";
+
 @Component({
   selector: "app-main-menu",
   templateUrl: "./main-menu.component.html",
   styleUrls: ["./main-menu.component.scss"],
 })
 export class MainMenuComponent {
+  public constructor(public service: AppService) {}
+
   public settings = false;
 
   public showSettings(): void {
diff --git a/src/app/settings/settings.component.html b/src/app/settings/settings.component.html
index e506c93c9..7d3b277f2 100644
--- a/src/app/settings/settings.component.html
+++ b/src/app/settings/settings.component.html
@@ -5,17 +5,23 @@
       <div class="settings__content" #settingsMain>
         <span class="settings__heading">settings</span>
         <ul class="settings__list">
-          <li (click)="changePage(1, 0, 'forward')">General <fa-icon [icon]="['fas', 'chevron-right']"></fa-icon></li>
-          <li (click)="changePage(2, 0, 'forward')">OctoDash <fa-icon [icon]="['fas', 'chevron-right']"></fa-icon></li>
-          <li (click)="changePage(3, 0, 'forward')">Plugins <fa-icon [icon]="['fas', 'chevron-right']"></fa-icon></li>
-          <li (click)="changePage(4, 0, 'forward')">Credits <fa-icon [icon]="['fas', 'chevron-right']"></fa-icon></li>
+          <li (click)="changePage(1, 0, 'forward')">general <fa-icon [icon]="['fas', 'chevron-right']"></fa-icon></li>
+          <li (click)="changePage(2, 0, 'forward')">octodash <fa-icon [icon]="['fas', 'chevron-right']"></fa-icon></li>
+          <li (click)="changePage(3, 0, 'forward')">plugins <fa-icon [icon]="['fas', 'chevron-right']"></fa-icon></li>
+          <li (click)="changePage(4, 0, 'forward')">about <fa-icon [icon]="['fas', 'chevron-right']"></fa-icon></li>
         </ul>
+        <span
+          class="settings__update-available"
+          (click)="changePage(4, 0, 'forward')"
+          [ngStyle]="{ opacity: service.updateAvailable ? '0.7' : '0' }"
+          >update available!</span
+        >
         <span class="settings__save" (click)="updateConfig()">save</span>
         <span class="settings__made"
-          >Made with <fa-icon [icon]="['fas', 'heart']" class="settings__made-heart"> </fa-icon> by
+          >made with <fa-icon [icon]="['fas', 'heart']" class="settings__made-heart"> </fa-icon> by
           /u/UnchartedBull</span
         >
-        <span class="settings__version">OctoDash v{{ version }}</span>
+        <!-- <span class="settings__version">OctoDash v{{ version }}</span> -->
       </div>
       <div class="settings__content settings__content-inactive" #settingsGeneral>
         <span class="settings__heading" (click)="changePage(0, 1, 'backward')">
@@ -537,9 +543,22 @@
       </div>
       <div class="settings__content settings__content-inactive" #settingsCredits>
         <span class="settings__heading" (click)="changePage(0, 4, 'backward')">
-          <fa-icon [icon]="['fas', 'chevron-left']" class="settings__heading-back"> </fa-icon>credits
+          <fa-icon [icon]="['fas', 'chevron-left']" class="settings__heading-back"> </fa-icon>about
         </span>
         <div class="settings__scroll">
+          <img src="assets/icon/icon-main-title.svg" class="settings__about-icon" />
+          <span class="settings__about-version">v{{ service.version }}</span>
+          <button class="settings__about-update settings__about-update-not-available" *ngIf="!service.updateAvailable">
+            no update available
+          </button>
+          <button
+            class="settings__about-update settings__about-update-available"
+            *ngIf="service.updateAvailable"
+            (click)="showUpdate()"
+          >
+            install v{{ service.latestVersion }}
+          </button>
+          <span class="settings__about-copyright">&#169; 2019-2020 UnchartedBull</span>
           <span class="settings__heading-2">License</span>
           <p class="settings__text">
             Licensed under the Apache 2.0 License. <br />
@@ -587,3 +606,4 @@
     </div>
   </div>
 </div>
+<app-update *ngIf="update" (closeFunction)="hideUpdate()"></app-update>
diff --git a/src/app/settings/settings.component.scss b/src/app/settings/settings.component.scss
index c4582d02a..f35c85338 100644
--- a/src/app/settings/settings.component.scss
+++ b/src/app/settings/settings.component.scss
@@ -18,7 +18,7 @@
     position: absolute;
     top: 10vh;
     left: 20vw;
-    height: 80vh;
+    height: 81vh;
     width: 60vw;
     background-color: #353b48;
     border-radius: 2vw;
@@ -271,6 +271,56 @@
       padding-left: 4vw;
     }
   }
+
+  &__update-available {
+    font-size: 0.4rem;
+    display: block;
+    margin-top: -1vh;
+    margin-bottom: -2.5vh;
+  }
+
+  &__about {
+    &-icon {
+      height: 35vh;
+      margin: auto;
+      display: block;
+    }
+
+    &-version {
+      display: block;
+      text-align: center;
+      font-size: 0.45em;
+      margin-top: 1.5vh;
+      opacity: 0.7;
+    }
+
+    &-update {
+      display: block;
+      padding: 1.4vh 2vw;
+      border-radius: 1vw;
+      box-shadow: 0 10px 19px -8px rgba(0, 0, 0, 0.75);
+      font-size: 0.7rem;
+      outline: 0;
+      border: 0;
+      margin: 3vh auto;
+
+      &-not-available {
+        background-color: #718093;
+        opacity: 0.8;
+      }
+
+      &-available {
+        background-color: #44bd32;
+      }
+    }
+
+    &-copyright {
+      display: block;
+      text-align: center;
+      font-size: 0.4rem;
+      margin-top: 5vh;
+    }
+  }
 }
 
 .filament-feed-speed-info {
diff --git a/src/app/settings/settings.component.ts b/src/app/settings/settings.component.ts
index b2a361879..01e4f07d6 100644
--- a/src/app/settings/settings.component.ts
+++ b/src/app/settings/settings.component.ts
@@ -10,7 +10,7 @@ import { NotificationService } from "../notification/notification.service";
   styleUrls: ["./settings.component.scss"],
 })
 export class SettingsComponent implements OnInit {
-  @Output() private closeFunction = new EventEmitter<string>();
+  @Output() closeFunction = new EventEmitter<void>();
   @ViewChild("settingsMain") private settingsMain: ElementRef;
   @ViewChild("settingsGeneral") private settingsGeneral: ElementRef;
   @ViewChild("settingsOctoDash") private settingsOctoDash: ElementRef;
@@ -27,36 +27,26 @@ export class SettingsComponent implements OnInit {
     "Bottom Left",
     "Bottom Right",
   ];
-  public version: string;
   private overwriteNoSave = false;
   private pages = [];
   // eslint-disable-next-line @typescript-eslint/no-explicit-any
   private ipc: any;
+  public update = false;
 
   public constructor(
     private configService: ConfigService,
     private notificationService: NotificationService,
-    private service: AppService
+    public service: AppService
   ) {
     this.config = this.configService.getCurrentConfig();
     this.config = this.configService.revertConfigForInput(this.config);
-    this.getVersion();
-    if (window.require) {
-      try {
-        this.ipc = window.require("electron").ipcRenderer;
-      } catch (e) {
-        this.notificationService.setError(
-          "Can't connect to backend",
-          "Please open an issue for GitHub as this shouldn't happen."
-        );
-      }
-    }
-  }
-
-  private getVersion(): void {
-    this.version = this.service.getVersion();
-    if (this.version === undefined) {
-      setTimeout(this.getVersion.bind(this), 3500);
+    try {
+      this.ipc = window.require("electron").ipcRenderer;
+    } catch (e) {
+      this.notificationService.setError(
+        "Can't connect to backend",
+        "Please restart your system. If the issue persists open an issue on GitHub."
+      );
     }
   }
 
@@ -114,4 +104,12 @@ export class SettingsComponent implements OnInit {
     this.configService.updateConfig();
     this.ipc.send("reload", "");
   }
+
+  public showUpdate(): void {
+    this.update = true;
+  }
+
+  public hideUpdate(): void {
+    this.update = false;
+  }
 }
diff --git a/src/app/update/update.component.html b/src/app/update/update.component.html
new file mode 100644
index 000000000..290c9087d
--- /dev/null
+++ b/src/app/update/update.component.html
@@ -0,0 +1,31 @@
+<div class="update-container">
+  <div *ngIf="page === 1">
+    <span class="update-heading">downloading update ...</span>
+    <div class="update-progress-bar__wrapper">
+      <div
+        class="update-progress-bar"
+        [ngStyle]="{ width: updateProgress.percentage / 2 + 'vw' }"
+        id="updateDownloadProgress"
+      >
+        {{ updateProgress.speed }} MB/s&nbsp;
+      </div>
+    </div>
+    <span class="update-size__total">{{ updateProgress.total }} MB</span>
+    <span class="update-time__remaining">{{ updateProgress.eta }} minutes left</span>
+  </div>
+  <div *ngIf="page === 2">
+    <span class="update-heading">installing update ...</span>
+    <div class="update-progress-bar__wrapper">
+      <div class="update-progress-bar update-progress-bar-no-percentage" id="installUpdateProgress"></div>
+    </div>
+    <span class="update-notice">this might take a while</span>
+  </div>
+  <div *ngIf="page === 3">
+    <span class="update-heading">v{{ service.version }} installed</span>
+    <span class="update-restart">would you like to reboot now to activate the latest version?</span>
+    <div class="update-restart-button__wrapper">
+      <button class="update-restart-button update-restart-button__no" (click)="closeUpdateWindow()">no</button>
+      <button class="update-restart-button update-restart-button__yes" (click)="reboot()">yes</button>
+    </div>
+  </div>
+</div>
diff --git a/src/app/update/update.component.scss b/src/app/update/update.component.scss
new file mode 100644
index 000000000..bc4a39535
--- /dev/null
+++ b/src/app/update/update.component.scss
@@ -0,0 +1,101 @@
+.update {
+  &-container {
+    position: absolute;
+    z-index: 20;
+    top: 10vh;
+    left: 20vw;
+    height: 81vh;
+    width: 60vw;
+    background-color: #353b48;
+    border-radius: 2vw;
+  }
+
+  &-progress-bar {
+    height: 5.5vh;
+    border-radius: 2vh;
+    background-color: #44bd32;
+    width: 25vw;
+    transition: width 0.7s ease-in-out;
+    font-size: 0.5rem;
+    text-align: right;
+    padding-top: 1.5vh;
+    overflow: visible;
+    white-space: nowrap;
+
+    &-no-percentage {
+      width: 10vw;
+      transition: margin-left 2s ease-in-out;
+      margin-left: 0;
+    }
+
+    &__wrapper {
+      width: 50vw;
+      margin: 14vh auto 3vh;
+      display: block;
+      height: 7vh;
+      background-color: transparent;
+      border: 3px solid #f5f6fa;
+      border-radius: 3vh;
+    }
+  }
+
+  &-heading {
+    display: block;
+    text-align: center;
+    margin-top: 12vh;
+  }
+
+  &-size__total {
+    display: block;
+    text-align: right;
+    font-size: 0.6rem;
+    margin-right: 5vw;
+    margin-top: -2vh;
+    opacity: 0.6;
+  }
+
+  &-time__remaining {
+    display: block;
+    text-align: center;
+    margin-top: 10vh;
+  }
+
+  &-notice {
+    display: block;
+    text-align: center;
+    font-size: 0.5rem;
+    margin-top: 20vh;
+  }
+
+  &-restart {
+    display: block;
+    text-align: center;
+    font-size: 0.65rem;
+    margin-top: 10vh;
+    margin-bottom: 8vw;
+
+    &-button {
+      padding: 1.4vh 2vw;
+      border-radius: 1vw;
+      box-shadow: 0 10px 19px -8px rgba(0, 0, 0, 0.75);
+      font-size: 0.7rem;
+      outline: 0;
+      border: 0;
+      margin: 7vh 2vw;
+
+      &__no {
+        background-color: #718093;
+        opacity: 0.8;
+      }
+
+      &__yes {
+        background-color: #44bd32;
+      }
+
+      &__wrapper {
+        display: block;
+        text-align: center;
+      }
+    }
+  }
+}
diff --git a/src/app/update/update.component.ts b/src/app/update/update.component.ts
new file mode 100644
index 000000000..50bf316f1
--- /dev/null
+++ b/src/app/update/update.component.ts
@@ -0,0 +1,121 @@
+import { ChangeDetectorRef, Component, EventEmitter, OnInit, Output } from "@angular/core";
+
+import { AppService } from "../app.service";
+import { NotificationService } from "../notification/notification.service";
+import { OctoprintService } from "../octoprint.service";
+
+@Component({
+  selector: "app-update",
+  templateUrl: "./update.component.html",
+  styleUrls: ["./update.component.scss"],
+})
+export class UpdateComponent implements OnInit {
+  @Output() closeFunction = new EventEmitter<void>(true);
+
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any
+  private ipc: any;
+  private installationAnimationInterval: number;
+  public updateProgress: UpdateDownloadProgress = {
+    percentage: 0,
+    transferred: 0,
+    total: "--.-",
+    remaining: 0,
+    eta: "--:--",
+    runtime: "--:--",
+    delta: 0,
+    speed: "--.-",
+  };
+  public page = 1;
+
+  constructor(
+    public service: AppService,
+    private notificationService: NotificationService,
+    private octoprintService: OctoprintService,
+    private changeDetector: ChangeDetectorRef
+  ) {
+    try {
+      this.ipc = window.require("electron").ipcRenderer;
+    } catch (e) {
+      this.notificationService.setError(
+        "Can't connect to backend",
+        "Please restart your system. If the issue persists open an issue on GitHub."
+      );
+    }
+  }
+
+  ngOnInit(): void {
+    if (!this.service.latestVersion || !this.service.getLatestVersionAssetsURL()) {
+      this.notificationService.setWarning(
+        "Can't initiate update!",
+        "Some information is missing, please try again in an hour or update manually."
+      );
+      this.closeUpdateWindow();
+    } else {
+      this.setupListeners();
+      this.update(this.service.getLatestVersionAssetsURL());
+    }
+  }
+
+  private setupListeners(): void {
+    this.ipc.on("updateError", (_, updateError: UpdateError): void => {
+      this.notificationService.setError("Can't install update!", updateError.error.message);
+      this.closeUpdateWindow();
+    });
+
+    this.ipc.on("updateDownloadProgress", (_, updateDownloadProgress: UpdateDownloadProgress): void => {
+      this.updateProgress = updateDownloadProgress;
+      this.changeDetector.detectChanges();
+    });
+
+    this.ipc.on("updateDownloadFinished", (): void => {
+      this.page = 2;
+      this.changeDetector.detectChanges();
+      setTimeout(() => {
+        const updateProgressBar = document.getElementById("installUpdateProgress");
+        updateProgressBar.style.marginLeft = "40vw";
+        this.installationAnimationInterval = setInterval(() => {
+          updateProgressBar.style.marginLeft = updateProgressBar.style.marginLeft === "0vw" ? "40vw" : "0vw";
+        }, 2050);
+      }, 250);
+    });
+
+    this.ipc.on("updateInstalled", (): void => {
+      this.page = 3;
+      this.changeDetector.detectChanges();
+    });
+  }
+
+  private closeUpdateWindow(): void {
+    this.page = 1;
+    clearInterval(this.installationAnimationInterval);
+    this.closeFunction.emit();
+  }
+
+  private update(assetsURL: string): void {
+    this.ipc.send("update", {
+      assetsURL: assetsURL,
+    });
+  }
+
+  public reboot(): void {
+    this.octoprintService.sendSystemCommand("reboot");
+  }
+}
+
+interface UpdateError {
+  error: {
+    message: string;
+    stack?: string;
+  };
+}
+
+interface UpdateDownloadProgress {
+  percentage: number;
+  transferred: number;
+  total: number | string;
+  remaining: number;
+  eta: string;
+  runtime: string;
+  delta: number;
+  speed: number | string;
+}
diff --git a/src/assets/made-in-berlin.png b/src/assets/made-in-berlin.png
index e3dd57868..d901c07db 100644
Binary files a/src/assets/made-in-berlin.png and b/src/assets/made-in-berlin.png differ