From ac23ce32a1376fd0358e1dc654b0940befbef7cd Mon Sep 17 00:00:00 2001
From: Patrick Pacher <patrick.pacher@gmail.com>
Date: Wed, 20 Mar 2024 11:10:32 +0100
Subject: [PATCH] Migrate notifier from portmaster-ui to cmds/notifier, remove
 some duplicated code, move assets to assets/data and add a small go package
 in assets to allow embedding icons

---
 Earthfile                                     |   9 +-
 .../{ => data}/fonts/Roboto-300/LICENSE.txt   |   0
 .../fonts/Roboto-300/Roboto-300.eot           | Bin
 .../fonts/Roboto-300/Roboto-300.svg           |   0
 .../fonts/Roboto-300/Roboto-300.ttf           | Bin
 .../fonts/Roboto-300/Roboto-300.woff          | Bin
 .../fonts/Roboto-300/Roboto-300.woff2         | Bin
 .../fonts/Roboto-300italic/LICENSE.txt        |   0
 .../Roboto-300italic/Roboto-300italic.eot     | Bin
 .../Roboto-300italic/Roboto-300italic.svg     |   0
 .../Roboto-300italic/Roboto-300italic.ttf     | Bin
 .../Roboto-300italic/Roboto-300italic.woff    | Bin
 .../Roboto-300italic/Roboto-300italic.woff2   | Bin
 .../{ => data}/fonts/Roboto-500/LICENSE.txt   |   0
 .../fonts/Roboto-500/Roboto-500.eot           | Bin
 .../fonts/Roboto-500/Roboto-500.svg           |   0
 .../fonts/Roboto-500/Roboto-500.ttf           | Bin
 .../fonts/Roboto-500/Roboto-500.woff          | Bin
 .../fonts/Roboto-500/Roboto-500.woff2         | Bin
 .../fonts/Roboto-500italic/LICENSE.txt        |   0
 .../Roboto-500italic/Roboto-500italic.eot     | Bin
 .../Roboto-500italic/Roboto-500italic.svg     |   0
 .../Roboto-500italic/Roboto-500italic.ttf     | Bin
 .../Roboto-500italic/Roboto-500italic.woff    | Bin
 .../Roboto-500italic/Roboto-500italic.woff2   | Bin
 .../{ => data}/fonts/Roboto-700/LICENSE.txt   |   0
 .../fonts/Roboto-700/Roboto-700.eot           | Bin
 .../fonts/Roboto-700/Roboto-700.svg           |   0
 .../fonts/Roboto-700/Roboto-700.ttf           | Bin
 .../fonts/Roboto-700/Roboto-700.woff          | Bin
 .../fonts/Roboto-700/Roboto-700.woff2         | Bin
 .../fonts/Roboto-700italic/LICENSE.txt        |   0
 .../Roboto-700italic/Roboto-700italic.eot     | Bin
 .../Roboto-700italic/Roboto-700italic.svg     |   0
 .../Roboto-700italic/Roboto-700italic.ttf     | Bin
 .../Roboto-700italic/Roboto-700italic.woff    | Bin
 .../Roboto-700italic/Roboto-700italic.woff2   | Bin
 .../fonts/Roboto-italic/LICENSE.txt           |   0
 .../fonts/Roboto-italic/Roboto-italic.eot     | Bin
 .../fonts/Roboto-italic/Roboto-italic.svg     |   0
 .../fonts/Roboto-italic/Roboto-italic.ttf     | Bin
 .../fonts/Roboto-italic/Roboto-italic.woff    | Bin
 .../fonts/Roboto-italic/Roboto-italic.woff2   | Bin
 .../fonts/Roboto-regular/LICENSE.txt          |   0
 .../fonts/Roboto-regular/Roboto-regular.eot   | Bin
 .../fonts/Roboto-regular/Roboto-regular.svg   |   0
 .../fonts/Roboto-regular/Roboto-regular.ttf   | Bin
 .../fonts/Roboto-regular/Roboto-regular.woff  | Bin
 .../fonts/Roboto-regular/Roboto-regular.woff2 | Bin
 assets/{ => data}/fonts/roboto-slimfix.css    |   0
 assets/{ => data}/fonts/roboto.css            |   0
 assets/{ => data}/icons/README.md             |   0
 assets/{ => data}/icons/pm_dark_128.png       | Bin
 assets/{ => data}/icons/pm_dark_256.png       | Bin
 assets/{ => data}/icons/pm_dark_512.ico       | Bin
 assets/{ => data}/icons/pm_dark_512.png       | Bin
 assets/{ => data}/icons/pm_dark_blue_128.png  | Bin
 assets/{ => data}/icons/pm_dark_blue_256.png  | Bin
 assets/{ => data}/icons/pm_dark_blue_512.ico  | Bin
 assets/{ => data}/icons/pm_dark_blue_512.png  | Bin
 assets/{ => data}/icons/pm_dark_green_128.png | Bin
 assets/{ => data}/icons/pm_dark_green_256.png | Bin
 assets/{ => data}/icons/pm_dark_green_512.ico | Bin
 assets/{ => data}/icons/pm_dark_green_512.png | Bin
 assets/{ => data}/icons/pm_dark_red_128.png   | Bin
 assets/{ => data}/icons/pm_dark_red_256.png   | Bin
 assets/{ => data}/icons/pm_dark_red_512.ico   | Bin
 assets/{ => data}/icons/pm_dark_red_512.png   | Bin
 .../{ => data}/icons/pm_dark_yellow_128.png   | Bin
 .../{ => data}/icons/pm_dark_yellow_256.png   | Bin
 .../{ => data}/icons/pm_dark_yellow_512.ico   | Bin
 .../{ => data}/icons/pm_dark_yellow_512.png   | Bin
 assets/{ => data}/icons/pm_light_128.png      | Bin
 assets/{ => data}/icons/pm_light_256.png      | Bin
 assets/{ => data}/icons/pm_light_512.ico      | Bin
 assets/{ => data}/icons/pm_light_512.png      | Bin
 assets/{ => data}/icons/pm_light_blue_128.png | Bin
 assets/{ => data}/icons/pm_light_blue_256.png | Bin
 assets/{ => data}/icons/pm_light_blue_512.ico | Bin
 assets/{ => data}/icons/pm_light_blue_512.png | Bin
 .../{ => data}/icons/pm_light_green_128.png   | Bin
 .../{ => data}/icons/pm_light_green_256.png   | Bin
 .../{ => data}/icons/pm_light_green_512.ico   | Bin
 .../{ => data}/icons/pm_light_green_512.png   | Bin
 assets/{ => data}/icons/pm_light_red_128.png  | Bin
 assets/{ => data}/icons/pm_light_red_256.png  | Bin
 assets/{ => data}/icons/pm_light_red_512.ico  | Bin
 assets/{ => data}/icons/pm_light_red_512.png  | Bin
 .../{ => data}/icons/pm_light_yellow_128.png  | Bin
 .../{ => data}/icons/pm_light_yellow_256.png  | Bin
 .../{ => data}/icons/pm_light_yellow_512.ico  | Bin
 .../{ => data}/icons/pm_light_yellow_512.png  | Bin
 assets/{ => data}/img/Mobile.svg              |   0
 assets/{ => data}/img/flags/AD.png            | Bin
 assets/{ => data}/img/flags/AE.png            | Bin
 assets/{ => data}/img/flags/AF.png            | Bin
 assets/{ => data}/img/flags/AG.png            | Bin
 assets/{ => data}/img/flags/AI.png            | Bin
 assets/{ => data}/img/flags/AL.png            | Bin
 assets/{ => data}/img/flags/AM.png            | Bin
 assets/{ => data}/img/flags/AN.png            | Bin
 assets/{ => data}/img/flags/AO.png            | Bin
 assets/{ => data}/img/flags/AQ.png            | Bin
 assets/{ => data}/img/flags/AR.png            | Bin
 assets/{ => data}/img/flags/AS.png            | Bin
 assets/{ => data}/img/flags/AT.png            | Bin
 assets/{ => data}/img/flags/AU.png            | Bin
 assets/{ => data}/img/flags/AW.png            | Bin
 assets/{ => data}/img/flags/AX.png            | Bin
 assets/{ => data}/img/flags/AZ.png            | Bin
 assets/{ => data}/img/flags/BA.png            | Bin
 assets/{ => data}/img/flags/BB.png            | Bin
 assets/{ => data}/img/flags/BD.png            | Bin
 assets/{ => data}/img/flags/BE.png            | Bin
 assets/{ => data}/img/flags/BF.png            | Bin
 assets/{ => data}/img/flags/BG.png            | Bin
 assets/{ => data}/img/flags/BH.png            | Bin
 assets/{ => data}/img/flags/BI.png            | Bin
 assets/{ => data}/img/flags/BJ.png            | Bin
 assets/{ => data}/img/flags/BL.png            | Bin
 assets/{ => data}/img/flags/BM.png            | Bin
 assets/{ => data}/img/flags/BN.png            | Bin
 assets/{ => data}/img/flags/BO.png            | Bin
 assets/{ => data}/img/flags/BR.png            | Bin
 assets/{ => data}/img/flags/BS.png            | Bin
 assets/{ => data}/img/flags/BT.png            | Bin
 assets/{ => data}/img/flags/BW.png            | Bin
 assets/{ => data}/img/flags/BY.png            | Bin
 assets/{ => data}/img/flags/BZ.png            | Bin
 assets/{ => data}/img/flags/CA.png            | Bin
 assets/{ => data}/img/flags/CC.png            | Bin
 assets/{ => data}/img/flags/CD.png            | Bin
 assets/{ => data}/img/flags/CF.png            | Bin
 assets/{ => data}/img/flags/CG.png            | Bin
 assets/{ => data}/img/flags/CH.png            | Bin
 assets/{ => data}/img/flags/CI.png            | Bin
 assets/{ => data}/img/flags/CK.png            | Bin
 assets/{ => data}/img/flags/CL.png            | Bin
 assets/{ => data}/img/flags/CM.png            | Bin
 assets/{ => data}/img/flags/CN.png            | Bin
 assets/{ => data}/img/flags/CO.png            | Bin
 assets/{ => data}/img/flags/CR.png            | Bin
 assets/{ => data}/img/flags/CT.png            | Bin
 assets/{ => data}/img/flags/CU.png            | Bin
 assets/{ => data}/img/flags/CV.png            | Bin
 assets/{ => data}/img/flags/CW.png            | Bin
 assets/{ => data}/img/flags/CX.png            | Bin
 assets/{ => data}/img/flags/CY.png            | Bin
 assets/{ => data}/img/flags/CZ.png            | Bin
 assets/{ => data}/img/flags/DE.png            | Bin
 assets/{ => data}/img/flags/DJ.png            | Bin
 assets/{ => data}/img/flags/DK.png            | Bin
 assets/{ => data}/img/flags/DM.png            | Bin
 assets/{ => data}/img/flags/DO.png            | Bin
 assets/{ => data}/img/flags/DZ.png            | Bin
 assets/{ => data}/img/flags/EC.png            | Bin
 assets/{ => data}/img/flags/EE.png            | Bin
 assets/{ => data}/img/flags/EG.png            | Bin
 assets/{ => data}/img/flags/EH.png            | Bin
 assets/{ => data}/img/flags/ER.png            | Bin
 assets/{ => data}/img/flags/ES.png            | Bin
 assets/{ => data}/img/flags/ET.png            | Bin
 assets/{ => data}/img/flags/EU.png            | Bin
 assets/{ => data}/img/flags/FI.png            | Bin
 assets/{ => data}/img/flags/FJ.png            | Bin
 assets/{ => data}/img/flags/FK.png            | Bin
 assets/{ => data}/img/flags/FM.png            | Bin
 assets/{ => data}/img/flags/FO.png            | Bin
 assets/{ => data}/img/flags/FR.png            | Bin
 assets/{ => data}/img/flags/GA.png            | Bin
 assets/{ => data}/img/flags/GB.png            | Bin
 assets/{ => data}/img/flags/GD.png            | Bin
 assets/{ => data}/img/flags/GE.png            | Bin
 assets/{ => data}/img/flags/GG.png            | Bin
 assets/{ => data}/img/flags/GH.png            | Bin
 assets/{ => data}/img/flags/GI.png            | Bin
 assets/{ => data}/img/flags/GL.png            | Bin
 assets/{ => data}/img/flags/GM.png            | Bin
 assets/{ => data}/img/flags/GN.png            | Bin
 assets/{ => data}/img/flags/GQ.png            | Bin
 assets/{ => data}/img/flags/GR.png            | Bin
 assets/{ => data}/img/flags/GS.png            | Bin
 assets/{ => data}/img/flags/GT.png            | Bin
 assets/{ => data}/img/flags/GU.png            | Bin
 assets/{ => data}/img/flags/GW.png            | Bin
 assets/{ => data}/img/flags/GY.png            | Bin
 assets/{ => data}/img/flags/HK.png            | Bin
 assets/{ => data}/img/flags/HN.png            | Bin
 assets/{ => data}/img/flags/HR.png            | Bin
 assets/{ => data}/img/flags/HT.png            | Bin
 assets/{ => data}/img/flags/HU.png            | Bin
 assets/{ => data}/img/flags/IC.png            | Bin
 assets/{ => data}/img/flags/ID.png            | Bin
 assets/{ => data}/img/flags/IE.png            | Bin
 assets/{ => data}/img/flags/IL.png            | Bin
 assets/{ => data}/img/flags/IM.png            | Bin
 assets/{ => data}/img/flags/IN.png            | Bin
 assets/{ => data}/img/flags/IQ.png            | Bin
 assets/{ => data}/img/flags/IR.png            | Bin
 assets/{ => data}/img/flags/IS.png            | Bin
 assets/{ => data}/img/flags/IT.png            | Bin
 assets/{ => data}/img/flags/JE.png            | Bin
 assets/{ => data}/img/flags/JM.png            | Bin
 assets/{ => data}/img/flags/JO.png            | Bin
 assets/{ => data}/img/flags/JP.png            | Bin
 assets/{ => data}/img/flags/KE.png            | Bin
 assets/{ => data}/img/flags/KG.png            | Bin
 assets/{ => data}/img/flags/KH.png            | Bin
 assets/{ => data}/img/flags/KI.png            | Bin
 assets/{ => data}/img/flags/KM.png            | Bin
 assets/{ => data}/img/flags/KN.png            | Bin
 assets/{ => data}/img/flags/KP.png            | Bin
 assets/{ => data}/img/flags/KR.png            | Bin
 assets/{ => data}/img/flags/KW.png            | Bin
 assets/{ => data}/img/flags/KY.png            | Bin
 assets/{ => data}/img/flags/KZ.png            | Bin
 assets/{ => data}/img/flags/LA.png            | Bin
 assets/{ => data}/img/flags/LB.png            | Bin
 assets/{ => data}/img/flags/LC.png            | Bin
 assets/{ => data}/img/flags/LI.png            | Bin
 assets/{ => data}/img/flags/LICENSE.txt       |   0
 assets/{ => data}/img/flags/LK.png            | Bin
 assets/{ => data}/img/flags/LR.png            | Bin
 assets/{ => data}/img/flags/LS.png            | Bin
 assets/{ => data}/img/flags/LT.png            | Bin
 assets/{ => data}/img/flags/LU.png            | Bin
 assets/{ => data}/img/flags/LV.png            | Bin
 assets/{ => data}/img/flags/LY.png            | Bin
 assets/{ => data}/img/flags/MA.png            | Bin
 assets/{ => data}/img/flags/MC.png            | Bin
 assets/{ => data}/img/flags/MD.png            | Bin
 assets/{ => data}/img/flags/ME.png            | Bin
 assets/{ => data}/img/flags/MF.png            | Bin
 assets/{ => data}/img/flags/MG.png            | Bin
 assets/{ => data}/img/flags/MH.png            | Bin
 assets/{ => data}/img/flags/MK.png            | Bin
 assets/{ => data}/img/flags/ML.png            | Bin
 assets/{ => data}/img/flags/MM.png            | Bin
 assets/{ => data}/img/flags/MN.png            | Bin
 assets/{ => data}/img/flags/MO.png            | Bin
 assets/{ => data}/img/flags/MP.png            | Bin
 assets/{ => data}/img/flags/MQ.png            | Bin
 assets/{ => data}/img/flags/MR.png            | Bin
 assets/{ => data}/img/flags/MS.png            | Bin
 assets/{ => data}/img/flags/MT.png            | Bin
 assets/{ => data}/img/flags/MU.png            | Bin
 assets/{ => data}/img/flags/MV.png            | Bin
 assets/{ => data}/img/flags/MW.png            | Bin
 assets/{ => data}/img/flags/MX.png            | Bin
 assets/{ => data}/img/flags/MY.png            | Bin
 assets/{ => data}/img/flags/MZ.png            | Bin
 assets/{ => data}/img/flags/NA.png            | Bin
 assets/{ => data}/img/flags/NC.png            | Bin
 assets/{ => data}/img/flags/NE.png            | Bin
 assets/{ => data}/img/flags/NF.png            | Bin
 assets/{ => data}/img/flags/NG.png            | Bin
 assets/{ => data}/img/flags/NI.png            | Bin
 assets/{ => data}/img/flags/NL.png            | Bin
 assets/{ => data}/img/flags/NO.png            | Bin
 assets/{ => data}/img/flags/NP.png            | Bin
 assets/{ => data}/img/flags/NR.png            | Bin
 assets/{ => data}/img/flags/NU.png            | Bin
 assets/{ => data}/img/flags/NZ.png            | Bin
 assets/{ => data}/img/flags/OM.png            | Bin
 assets/{ => data}/img/flags/PA.png            | Bin
 assets/{ => data}/img/flags/PE.png            | Bin
 assets/{ => data}/img/flags/PF.png            | Bin
 assets/{ => data}/img/flags/PG.png            | Bin
 assets/{ => data}/img/flags/PH.png            | Bin
 assets/{ => data}/img/flags/PK.png            | Bin
 assets/{ => data}/img/flags/PL.png            | Bin
 assets/{ => data}/img/flags/PN.png            | Bin
 assets/{ => data}/img/flags/PR.png            | Bin
 assets/{ => data}/img/flags/PS.png            | Bin
 assets/{ => data}/img/flags/PT.png            | Bin
 assets/{ => data}/img/flags/PW.png            | Bin
 assets/{ => data}/img/flags/PY.png            | Bin
 assets/{ => data}/img/flags/QA.png            | Bin
 assets/{ => data}/img/flags/RE.png            | Bin
 assets/{ => data}/img/flags/RO.png            | Bin
 assets/{ => data}/img/flags/RS.png            | Bin
 assets/{ => data}/img/flags/RU.png            | Bin
 assets/{ => data}/img/flags/RW.png            | Bin
 assets/{ => data}/img/flags/SA.png            | Bin
 assets/{ => data}/img/flags/SB.png            | Bin
 assets/{ => data}/img/flags/SC.png            | Bin
 assets/{ => data}/img/flags/SD.png            | Bin
 assets/{ => data}/img/flags/SE.png            | Bin
 assets/{ => data}/img/flags/SG.png            | Bin
 assets/{ => data}/img/flags/SH.png            | Bin
 assets/{ => data}/img/flags/SI.png            | Bin
 assets/{ => data}/img/flags/SK.png            | Bin
 assets/{ => data}/img/flags/SL.png            | Bin
 assets/{ => data}/img/flags/SM.png            | Bin
 assets/{ => data}/img/flags/SN.png            | Bin
 assets/{ => data}/img/flags/SO.png            | Bin
 assets/{ => data}/img/flags/SR.png            | Bin
 assets/{ => data}/img/flags/SS.png            | Bin
 assets/{ => data}/img/flags/ST.png            | Bin
 assets/{ => data}/img/flags/SV.png            | Bin
 assets/{ => data}/img/flags/SX.png            | Bin
 assets/{ => data}/img/flags/SY.png            | Bin
 assets/{ => data}/img/flags/SZ.png            | Bin
 assets/{ => data}/img/flags/TC.png            | Bin
 assets/{ => data}/img/flags/TD.png            | Bin
 assets/{ => data}/img/flags/TF.png            | Bin
 assets/{ => data}/img/flags/TG.png            | Bin
 assets/{ => data}/img/flags/TH.png            | Bin
 assets/{ => data}/img/flags/TJ.png            | Bin
 assets/{ => data}/img/flags/TK.png            | Bin
 assets/{ => data}/img/flags/TL.png            | Bin
 assets/{ => data}/img/flags/TM.png            | Bin
 assets/{ => data}/img/flags/TN.png            | Bin
 assets/{ => data}/img/flags/TO.png            | Bin
 assets/{ => data}/img/flags/TR.png            | Bin
 assets/{ => data}/img/flags/TT.png            | Bin
 assets/{ => data}/img/flags/TV.png            | Bin
 assets/{ => data}/img/flags/TW.png            | Bin
 assets/{ => data}/img/flags/TZ.png            | Bin
 assets/{ => data}/img/flags/UA.png            | Bin
 assets/{ => data}/img/flags/UG.png            | Bin
 assets/{ => data}/img/flags/US.png            | Bin
 assets/{ => data}/img/flags/UY.png            | Bin
 assets/{ => data}/img/flags/UZ.png            | Bin
 assets/{ => data}/img/flags/VA.png            | Bin
 assets/{ => data}/img/flags/VC.png            | Bin
 assets/{ => data}/img/flags/VE.png            | Bin
 assets/{ => data}/img/flags/VG.png            | Bin
 assets/{ => data}/img/flags/VI.png            | Bin
 assets/{ => data}/img/flags/VN.png            | Bin
 assets/{ => data}/img/flags/VU.png            | Bin
 assets/{ => data}/img/flags/WF.png            | Bin
 assets/{ => data}/img/flags/WS.png            | Bin
 assets/{ => data}/img/flags/YE.png            | Bin
 assets/{ => data}/img/flags/YT.png            | Bin
 assets/{ => data}/img/flags/ZA.png            | Bin
 assets/{ => data}/img/flags/ZM.png            | Bin
 assets/{ => data}/img/flags/ZW.png            | Bin
 assets/{ => data}/img/flags/_abkhazia.png     | Bin
 .../{ => data}/img/flags/_basque-country.png  | Bin
 .../flags/_british-antarctic-territory.png    | Bin
 assets/{ => data}/img/flags/_commonwealth.png | Bin
 assets/{ => data}/img/flags/_england.png      | Bin
 assets/{ => data}/img/flags/_gosquared.png    | Bin
 assets/{ => data}/img/flags/_kosovo.png       | Bin
 assets/{ => data}/img/flags/_mars.png         | Bin
 .../img/flags/_nagorno-karabakh.png           | Bin
 assets/{ => data}/img/flags/_nato.png         | Bin
 .../{ => data}/img/flags/_northern-cyprus.png | Bin
 assets/{ => data}/img/flags/_olympics.png     | Bin
 assets/{ => data}/img/flags/_red-cross.png    | Bin
 assets/{ => data}/img/flags/_scotland.png     | Bin
 assets/{ => data}/img/flags/_somaliland.png   | Bin
 .../{ => data}/img/flags/_south-ossetia.png   | Bin
 .../{ => data}/img/flags/_united-nations.png  | Bin
 assets/{ => data}/img/flags/_unknown.png      | Bin
 assets/{ => data}/img/flags/_wales.png        | Bin
 assets/{ => data}/img/linux.svg               |   0
 assets/{ => data}/img/mac.svg                 |   0
 assets/{ => data}/img/plants1-br.png          | Bin
 assets/{ => data}/img/plants1.png             | Bin
 .../access-regional-content-easily.png        | Bin
 .../built-from-the-ground-up.png              | Bin
 .../img/spn-feature-carousel/bye-bye-vpns.png | Bin
 .../easily-control-your-privacy.png           | Bin
 .../multiple-identities-for-each-app.png      | Bin
 assets/{ => data}/img/spn-login.png           | Bin
 assets/{ => data}/img/windows.svg             |   0
 assets/{ => data}/world-50m.json              |   0
 assets/icons.go                               |   8 +
 assets/icons_default.go                       | 102 +++++++
 assets/icons_windows.go                       |  41 +++
 cmds/notifier/.gitignore                      |  34 +++
 cmds/notifier/README.md                       |   5 +
 cmds/notifier/http_api.go                     |  65 ++++
 cmds/notifier/icons.go                        |  25 ++
 cmds/notifier/main.go                         | 286 ++++++++++++++++++
 cmds/notifier/notification.go                 |  36 +++
 cmds/notifier/notify.go                       | 103 +++++++
 cmds/notifier/notify_linux.go                 | 154 ++++++++++
 cmds/notifier/notify_windows.go               | 184 +++++++++++
 cmds/notifier/shutdown.go                     |  50 +++
 cmds/notifier/snoretoast-guid.patch           |  15 +
 cmds/notifier/spn.go                          | 103 +++++++
 cmds/notifier/subsystems.go                   | 122 ++++++++
 cmds/notifier/tray.go                         | 218 +++++++++++++
 .../notifier/wintoast/notification_builder.go |  90 ++++++
 cmds/notifier/wintoast/wintoast.go            | 217 +++++++++++++
 desktop/angular/assets                        |   2 +-
 go.mod                                        |   5 +-
 go.sum                                        |   8 +
 runtime/.gitkeep                              |   1 -
 392 files changed, 1879 insertions(+), 4 deletions(-)
 rename assets/{ => data}/fonts/Roboto-300/LICENSE.txt (100%)
 rename assets/{ => data}/fonts/Roboto-300/Roboto-300.eot (100%)
 rename assets/{ => data}/fonts/Roboto-300/Roboto-300.svg (100%)
 rename assets/{ => data}/fonts/Roboto-300/Roboto-300.ttf (100%)
 rename assets/{ => data}/fonts/Roboto-300/Roboto-300.woff (100%)
 rename assets/{ => data}/fonts/Roboto-300/Roboto-300.woff2 (100%)
 rename assets/{ => data}/fonts/Roboto-300italic/LICENSE.txt (100%)
 rename assets/{ => data}/fonts/Roboto-300italic/Roboto-300italic.eot (100%)
 rename assets/{ => data}/fonts/Roboto-300italic/Roboto-300italic.svg (100%)
 rename assets/{ => data}/fonts/Roboto-300italic/Roboto-300italic.ttf (100%)
 rename assets/{ => data}/fonts/Roboto-300italic/Roboto-300italic.woff (100%)
 rename assets/{ => data}/fonts/Roboto-300italic/Roboto-300italic.woff2 (100%)
 rename assets/{ => data}/fonts/Roboto-500/LICENSE.txt (100%)
 rename assets/{ => data}/fonts/Roboto-500/Roboto-500.eot (100%)
 rename assets/{ => data}/fonts/Roboto-500/Roboto-500.svg (100%)
 rename assets/{ => data}/fonts/Roboto-500/Roboto-500.ttf (100%)
 rename assets/{ => data}/fonts/Roboto-500/Roboto-500.woff (100%)
 rename assets/{ => data}/fonts/Roboto-500/Roboto-500.woff2 (100%)
 rename assets/{ => data}/fonts/Roboto-500italic/LICENSE.txt (100%)
 rename assets/{ => data}/fonts/Roboto-500italic/Roboto-500italic.eot (100%)
 rename assets/{ => data}/fonts/Roboto-500italic/Roboto-500italic.svg (100%)
 rename assets/{ => data}/fonts/Roboto-500italic/Roboto-500italic.ttf (100%)
 rename assets/{ => data}/fonts/Roboto-500italic/Roboto-500italic.woff (100%)
 rename assets/{ => data}/fonts/Roboto-500italic/Roboto-500italic.woff2 (100%)
 rename assets/{ => data}/fonts/Roboto-700/LICENSE.txt (100%)
 rename assets/{ => data}/fonts/Roboto-700/Roboto-700.eot (100%)
 rename assets/{ => data}/fonts/Roboto-700/Roboto-700.svg (100%)
 rename assets/{ => data}/fonts/Roboto-700/Roboto-700.ttf (100%)
 rename assets/{ => data}/fonts/Roboto-700/Roboto-700.woff (100%)
 rename assets/{ => data}/fonts/Roboto-700/Roboto-700.woff2 (100%)
 rename assets/{ => data}/fonts/Roboto-700italic/LICENSE.txt (100%)
 rename assets/{ => data}/fonts/Roboto-700italic/Roboto-700italic.eot (100%)
 rename assets/{ => data}/fonts/Roboto-700italic/Roboto-700italic.svg (100%)
 rename assets/{ => data}/fonts/Roboto-700italic/Roboto-700italic.ttf (100%)
 rename assets/{ => data}/fonts/Roboto-700italic/Roboto-700italic.woff (100%)
 rename assets/{ => data}/fonts/Roboto-700italic/Roboto-700italic.woff2 (100%)
 rename assets/{ => data}/fonts/Roboto-italic/LICENSE.txt (100%)
 rename assets/{ => data}/fonts/Roboto-italic/Roboto-italic.eot (100%)
 rename assets/{ => data}/fonts/Roboto-italic/Roboto-italic.svg (100%)
 rename assets/{ => data}/fonts/Roboto-italic/Roboto-italic.ttf (100%)
 rename assets/{ => data}/fonts/Roboto-italic/Roboto-italic.woff (100%)
 rename assets/{ => data}/fonts/Roboto-italic/Roboto-italic.woff2 (100%)
 rename assets/{ => data}/fonts/Roboto-regular/LICENSE.txt (100%)
 rename assets/{ => data}/fonts/Roboto-regular/Roboto-regular.eot (100%)
 rename assets/{ => data}/fonts/Roboto-regular/Roboto-regular.svg (100%)
 rename assets/{ => data}/fonts/Roboto-regular/Roboto-regular.ttf (100%)
 rename assets/{ => data}/fonts/Roboto-regular/Roboto-regular.woff (100%)
 rename assets/{ => data}/fonts/Roboto-regular/Roboto-regular.woff2 (100%)
 rename assets/{ => data}/fonts/roboto-slimfix.css (100%)
 rename assets/{ => data}/fonts/roboto.css (100%)
 rename assets/{ => data}/icons/README.md (100%)
 rename assets/{ => data}/icons/pm_dark_128.png (100%)
 rename assets/{ => data}/icons/pm_dark_256.png (100%)
 rename assets/{ => data}/icons/pm_dark_512.ico (100%)
 rename assets/{ => data}/icons/pm_dark_512.png (100%)
 rename assets/{ => data}/icons/pm_dark_blue_128.png (100%)
 rename assets/{ => data}/icons/pm_dark_blue_256.png (100%)
 rename assets/{ => data}/icons/pm_dark_blue_512.ico (100%)
 rename assets/{ => data}/icons/pm_dark_blue_512.png (100%)
 rename assets/{ => data}/icons/pm_dark_green_128.png (100%)
 rename assets/{ => data}/icons/pm_dark_green_256.png (100%)
 rename assets/{ => data}/icons/pm_dark_green_512.ico (100%)
 rename assets/{ => data}/icons/pm_dark_green_512.png (100%)
 rename assets/{ => data}/icons/pm_dark_red_128.png (100%)
 rename assets/{ => data}/icons/pm_dark_red_256.png (100%)
 rename assets/{ => data}/icons/pm_dark_red_512.ico (100%)
 rename assets/{ => data}/icons/pm_dark_red_512.png (100%)
 rename assets/{ => data}/icons/pm_dark_yellow_128.png (100%)
 rename assets/{ => data}/icons/pm_dark_yellow_256.png (100%)
 rename assets/{ => data}/icons/pm_dark_yellow_512.ico (100%)
 rename assets/{ => data}/icons/pm_dark_yellow_512.png (100%)
 rename assets/{ => data}/icons/pm_light_128.png (100%)
 rename assets/{ => data}/icons/pm_light_256.png (100%)
 rename assets/{ => data}/icons/pm_light_512.ico (100%)
 rename assets/{ => data}/icons/pm_light_512.png (100%)
 rename assets/{ => data}/icons/pm_light_blue_128.png (100%)
 rename assets/{ => data}/icons/pm_light_blue_256.png (100%)
 rename assets/{ => data}/icons/pm_light_blue_512.ico (100%)
 rename assets/{ => data}/icons/pm_light_blue_512.png (100%)
 rename assets/{ => data}/icons/pm_light_green_128.png (100%)
 rename assets/{ => data}/icons/pm_light_green_256.png (100%)
 rename assets/{ => data}/icons/pm_light_green_512.ico (100%)
 rename assets/{ => data}/icons/pm_light_green_512.png (100%)
 rename assets/{ => data}/icons/pm_light_red_128.png (100%)
 rename assets/{ => data}/icons/pm_light_red_256.png (100%)
 rename assets/{ => data}/icons/pm_light_red_512.ico (100%)
 rename assets/{ => data}/icons/pm_light_red_512.png (100%)
 rename assets/{ => data}/icons/pm_light_yellow_128.png (100%)
 rename assets/{ => data}/icons/pm_light_yellow_256.png (100%)
 rename assets/{ => data}/icons/pm_light_yellow_512.ico (100%)
 rename assets/{ => data}/icons/pm_light_yellow_512.png (100%)
 rename assets/{ => data}/img/Mobile.svg (100%)
 rename assets/{ => data}/img/flags/AD.png (100%)
 rename assets/{ => data}/img/flags/AE.png (100%)
 rename assets/{ => data}/img/flags/AF.png (100%)
 rename assets/{ => data}/img/flags/AG.png (100%)
 rename assets/{ => data}/img/flags/AI.png (100%)
 rename assets/{ => data}/img/flags/AL.png (100%)
 rename assets/{ => data}/img/flags/AM.png (100%)
 rename assets/{ => data}/img/flags/AN.png (100%)
 rename assets/{ => data}/img/flags/AO.png (100%)
 rename assets/{ => data}/img/flags/AQ.png (100%)
 rename assets/{ => data}/img/flags/AR.png (100%)
 rename assets/{ => data}/img/flags/AS.png (100%)
 rename assets/{ => data}/img/flags/AT.png (100%)
 rename assets/{ => data}/img/flags/AU.png (100%)
 rename assets/{ => data}/img/flags/AW.png (100%)
 rename assets/{ => data}/img/flags/AX.png (100%)
 rename assets/{ => data}/img/flags/AZ.png (100%)
 rename assets/{ => data}/img/flags/BA.png (100%)
 rename assets/{ => data}/img/flags/BB.png (100%)
 rename assets/{ => data}/img/flags/BD.png (100%)
 rename assets/{ => data}/img/flags/BE.png (100%)
 rename assets/{ => data}/img/flags/BF.png (100%)
 rename assets/{ => data}/img/flags/BG.png (100%)
 rename assets/{ => data}/img/flags/BH.png (100%)
 rename assets/{ => data}/img/flags/BI.png (100%)
 rename assets/{ => data}/img/flags/BJ.png (100%)
 rename assets/{ => data}/img/flags/BL.png (100%)
 rename assets/{ => data}/img/flags/BM.png (100%)
 rename assets/{ => data}/img/flags/BN.png (100%)
 rename assets/{ => data}/img/flags/BO.png (100%)
 rename assets/{ => data}/img/flags/BR.png (100%)
 rename assets/{ => data}/img/flags/BS.png (100%)
 rename assets/{ => data}/img/flags/BT.png (100%)
 rename assets/{ => data}/img/flags/BW.png (100%)
 rename assets/{ => data}/img/flags/BY.png (100%)
 rename assets/{ => data}/img/flags/BZ.png (100%)
 rename assets/{ => data}/img/flags/CA.png (100%)
 rename assets/{ => data}/img/flags/CC.png (100%)
 rename assets/{ => data}/img/flags/CD.png (100%)
 rename assets/{ => data}/img/flags/CF.png (100%)
 rename assets/{ => data}/img/flags/CG.png (100%)
 rename assets/{ => data}/img/flags/CH.png (100%)
 rename assets/{ => data}/img/flags/CI.png (100%)
 rename assets/{ => data}/img/flags/CK.png (100%)
 rename assets/{ => data}/img/flags/CL.png (100%)
 rename assets/{ => data}/img/flags/CM.png (100%)
 rename assets/{ => data}/img/flags/CN.png (100%)
 rename assets/{ => data}/img/flags/CO.png (100%)
 rename assets/{ => data}/img/flags/CR.png (100%)
 rename assets/{ => data}/img/flags/CT.png (100%)
 rename assets/{ => data}/img/flags/CU.png (100%)
 rename assets/{ => data}/img/flags/CV.png (100%)
 rename assets/{ => data}/img/flags/CW.png (100%)
 rename assets/{ => data}/img/flags/CX.png (100%)
 rename assets/{ => data}/img/flags/CY.png (100%)
 rename assets/{ => data}/img/flags/CZ.png (100%)
 rename assets/{ => data}/img/flags/DE.png (100%)
 rename assets/{ => data}/img/flags/DJ.png (100%)
 rename assets/{ => data}/img/flags/DK.png (100%)
 rename assets/{ => data}/img/flags/DM.png (100%)
 rename assets/{ => data}/img/flags/DO.png (100%)
 rename assets/{ => data}/img/flags/DZ.png (100%)
 rename assets/{ => data}/img/flags/EC.png (100%)
 rename assets/{ => data}/img/flags/EE.png (100%)
 rename assets/{ => data}/img/flags/EG.png (100%)
 rename assets/{ => data}/img/flags/EH.png (100%)
 rename assets/{ => data}/img/flags/ER.png (100%)
 rename assets/{ => data}/img/flags/ES.png (100%)
 rename assets/{ => data}/img/flags/ET.png (100%)
 rename assets/{ => data}/img/flags/EU.png (100%)
 rename assets/{ => data}/img/flags/FI.png (100%)
 rename assets/{ => data}/img/flags/FJ.png (100%)
 rename assets/{ => data}/img/flags/FK.png (100%)
 rename assets/{ => data}/img/flags/FM.png (100%)
 rename assets/{ => data}/img/flags/FO.png (100%)
 rename assets/{ => data}/img/flags/FR.png (100%)
 rename assets/{ => data}/img/flags/GA.png (100%)
 rename assets/{ => data}/img/flags/GB.png (100%)
 rename assets/{ => data}/img/flags/GD.png (100%)
 rename assets/{ => data}/img/flags/GE.png (100%)
 rename assets/{ => data}/img/flags/GG.png (100%)
 rename assets/{ => data}/img/flags/GH.png (100%)
 rename assets/{ => data}/img/flags/GI.png (100%)
 rename assets/{ => data}/img/flags/GL.png (100%)
 rename assets/{ => data}/img/flags/GM.png (100%)
 rename assets/{ => data}/img/flags/GN.png (100%)
 rename assets/{ => data}/img/flags/GQ.png (100%)
 rename assets/{ => data}/img/flags/GR.png (100%)
 rename assets/{ => data}/img/flags/GS.png (100%)
 rename assets/{ => data}/img/flags/GT.png (100%)
 rename assets/{ => data}/img/flags/GU.png (100%)
 rename assets/{ => data}/img/flags/GW.png (100%)
 rename assets/{ => data}/img/flags/GY.png (100%)
 rename assets/{ => data}/img/flags/HK.png (100%)
 rename assets/{ => data}/img/flags/HN.png (100%)
 rename assets/{ => data}/img/flags/HR.png (100%)
 rename assets/{ => data}/img/flags/HT.png (100%)
 rename assets/{ => data}/img/flags/HU.png (100%)
 rename assets/{ => data}/img/flags/IC.png (100%)
 rename assets/{ => data}/img/flags/ID.png (100%)
 rename assets/{ => data}/img/flags/IE.png (100%)
 rename assets/{ => data}/img/flags/IL.png (100%)
 rename assets/{ => data}/img/flags/IM.png (100%)
 rename assets/{ => data}/img/flags/IN.png (100%)
 rename assets/{ => data}/img/flags/IQ.png (100%)
 rename assets/{ => data}/img/flags/IR.png (100%)
 rename assets/{ => data}/img/flags/IS.png (100%)
 rename assets/{ => data}/img/flags/IT.png (100%)
 rename assets/{ => data}/img/flags/JE.png (100%)
 rename assets/{ => data}/img/flags/JM.png (100%)
 rename assets/{ => data}/img/flags/JO.png (100%)
 rename assets/{ => data}/img/flags/JP.png (100%)
 rename assets/{ => data}/img/flags/KE.png (100%)
 rename assets/{ => data}/img/flags/KG.png (100%)
 rename assets/{ => data}/img/flags/KH.png (100%)
 rename assets/{ => data}/img/flags/KI.png (100%)
 rename assets/{ => data}/img/flags/KM.png (100%)
 rename assets/{ => data}/img/flags/KN.png (100%)
 rename assets/{ => data}/img/flags/KP.png (100%)
 rename assets/{ => data}/img/flags/KR.png (100%)
 rename assets/{ => data}/img/flags/KW.png (100%)
 rename assets/{ => data}/img/flags/KY.png (100%)
 rename assets/{ => data}/img/flags/KZ.png (100%)
 rename assets/{ => data}/img/flags/LA.png (100%)
 rename assets/{ => data}/img/flags/LB.png (100%)
 rename assets/{ => data}/img/flags/LC.png (100%)
 rename assets/{ => data}/img/flags/LI.png (100%)
 rename assets/{ => data}/img/flags/LICENSE.txt (100%)
 rename assets/{ => data}/img/flags/LK.png (100%)
 rename assets/{ => data}/img/flags/LR.png (100%)
 rename assets/{ => data}/img/flags/LS.png (100%)
 rename assets/{ => data}/img/flags/LT.png (100%)
 rename assets/{ => data}/img/flags/LU.png (100%)
 rename assets/{ => data}/img/flags/LV.png (100%)
 rename assets/{ => data}/img/flags/LY.png (100%)
 rename assets/{ => data}/img/flags/MA.png (100%)
 rename assets/{ => data}/img/flags/MC.png (100%)
 rename assets/{ => data}/img/flags/MD.png (100%)
 rename assets/{ => data}/img/flags/ME.png (100%)
 rename assets/{ => data}/img/flags/MF.png (100%)
 rename assets/{ => data}/img/flags/MG.png (100%)
 rename assets/{ => data}/img/flags/MH.png (100%)
 rename assets/{ => data}/img/flags/MK.png (100%)
 rename assets/{ => data}/img/flags/ML.png (100%)
 rename assets/{ => data}/img/flags/MM.png (100%)
 rename assets/{ => data}/img/flags/MN.png (100%)
 rename assets/{ => data}/img/flags/MO.png (100%)
 rename assets/{ => data}/img/flags/MP.png (100%)
 rename assets/{ => data}/img/flags/MQ.png (100%)
 rename assets/{ => data}/img/flags/MR.png (100%)
 rename assets/{ => data}/img/flags/MS.png (100%)
 rename assets/{ => data}/img/flags/MT.png (100%)
 rename assets/{ => data}/img/flags/MU.png (100%)
 rename assets/{ => data}/img/flags/MV.png (100%)
 rename assets/{ => data}/img/flags/MW.png (100%)
 rename assets/{ => data}/img/flags/MX.png (100%)
 rename assets/{ => data}/img/flags/MY.png (100%)
 rename assets/{ => data}/img/flags/MZ.png (100%)
 rename assets/{ => data}/img/flags/NA.png (100%)
 rename assets/{ => data}/img/flags/NC.png (100%)
 rename assets/{ => data}/img/flags/NE.png (100%)
 rename assets/{ => data}/img/flags/NF.png (100%)
 rename assets/{ => data}/img/flags/NG.png (100%)
 rename assets/{ => data}/img/flags/NI.png (100%)
 rename assets/{ => data}/img/flags/NL.png (100%)
 rename assets/{ => data}/img/flags/NO.png (100%)
 rename assets/{ => data}/img/flags/NP.png (100%)
 rename assets/{ => data}/img/flags/NR.png (100%)
 rename assets/{ => data}/img/flags/NU.png (100%)
 rename assets/{ => data}/img/flags/NZ.png (100%)
 rename assets/{ => data}/img/flags/OM.png (100%)
 rename assets/{ => data}/img/flags/PA.png (100%)
 rename assets/{ => data}/img/flags/PE.png (100%)
 rename assets/{ => data}/img/flags/PF.png (100%)
 rename assets/{ => data}/img/flags/PG.png (100%)
 rename assets/{ => data}/img/flags/PH.png (100%)
 rename assets/{ => data}/img/flags/PK.png (100%)
 rename assets/{ => data}/img/flags/PL.png (100%)
 rename assets/{ => data}/img/flags/PN.png (100%)
 rename assets/{ => data}/img/flags/PR.png (100%)
 rename assets/{ => data}/img/flags/PS.png (100%)
 rename assets/{ => data}/img/flags/PT.png (100%)
 rename assets/{ => data}/img/flags/PW.png (100%)
 rename assets/{ => data}/img/flags/PY.png (100%)
 rename assets/{ => data}/img/flags/QA.png (100%)
 rename assets/{ => data}/img/flags/RE.png (100%)
 rename assets/{ => data}/img/flags/RO.png (100%)
 rename assets/{ => data}/img/flags/RS.png (100%)
 rename assets/{ => data}/img/flags/RU.png (100%)
 rename assets/{ => data}/img/flags/RW.png (100%)
 rename assets/{ => data}/img/flags/SA.png (100%)
 rename assets/{ => data}/img/flags/SB.png (100%)
 rename assets/{ => data}/img/flags/SC.png (100%)
 rename assets/{ => data}/img/flags/SD.png (100%)
 rename assets/{ => data}/img/flags/SE.png (100%)
 rename assets/{ => data}/img/flags/SG.png (100%)
 rename assets/{ => data}/img/flags/SH.png (100%)
 rename assets/{ => data}/img/flags/SI.png (100%)
 rename assets/{ => data}/img/flags/SK.png (100%)
 rename assets/{ => data}/img/flags/SL.png (100%)
 rename assets/{ => data}/img/flags/SM.png (100%)
 rename assets/{ => data}/img/flags/SN.png (100%)
 rename assets/{ => data}/img/flags/SO.png (100%)
 rename assets/{ => data}/img/flags/SR.png (100%)
 rename assets/{ => data}/img/flags/SS.png (100%)
 rename assets/{ => data}/img/flags/ST.png (100%)
 rename assets/{ => data}/img/flags/SV.png (100%)
 rename assets/{ => data}/img/flags/SX.png (100%)
 rename assets/{ => data}/img/flags/SY.png (100%)
 rename assets/{ => data}/img/flags/SZ.png (100%)
 rename assets/{ => data}/img/flags/TC.png (100%)
 rename assets/{ => data}/img/flags/TD.png (100%)
 rename assets/{ => data}/img/flags/TF.png (100%)
 rename assets/{ => data}/img/flags/TG.png (100%)
 rename assets/{ => data}/img/flags/TH.png (100%)
 rename assets/{ => data}/img/flags/TJ.png (100%)
 rename assets/{ => data}/img/flags/TK.png (100%)
 rename assets/{ => data}/img/flags/TL.png (100%)
 rename assets/{ => data}/img/flags/TM.png (100%)
 rename assets/{ => data}/img/flags/TN.png (100%)
 rename assets/{ => data}/img/flags/TO.png (100%)
 rename assets/{ => data}/img/flags/TR.png (100%)
 rename assets/{ => data}/img/flags/TT.png (100%)
 rename assets/{ => data}/img/flags/TV.png (100%)
 rename assets/{ => data}/img/flags/TW.png (100%)
 rename assets/{ => data}/img/flags/TZ.png (100%)
 rename assets/{ => data}/img/flags/UA.png (100%)
 rename assets/{ => data}/img/flags/UG.png (100%)
 rename assets/{ => data}/img/flags/US.png (100%)
 rename assets/{ => data}/img/flags/UY.png (100%)
 rename assets/{ => data}/img/flags/UZ.png (100%)
 rename assets/{ => data}/img/flags/VA.png (100%)
 rename assets/{ => data}/img/flags/VC.png (100%)
 rename assets/{ => data}/img/flags/VE.png (100%)
 rename assets/{ => data}/img/flags/VG.png (100%)
 rename assets/{ => data}/img/flags/VI.png (100%)
 rename assets/{ => data}/img/flags/VN.png (100%)
 rename assets/{ => data}/img/flags/VU.png (100%)
 rename assets/{ => data}/img/flags/WF.png (100%)
 rename assets/{ => data}/img/flags/WS.png (100%)
 rename assets/{ => data}/img/flags/YE.png (100%)
 rename assets/{ => data}/img/flags/YT.png (100%)
 rename assets/{ => data}/img/flags/ZA.png (100%)
 rename assets/{ => data}/img/flags/ZM.png (100%)
 rename assets/{ => data}/img/flags/ZW.png (100%)
 rename assets/{ => data}/img/flags/_abkhazia.png (100%)
 rename assets/{ => data}/img/flags/_basque-country.png (100%)
 rename assets/{ => data}/img/flags/_british-antarctic-territory.png (100%)
 rename assets/{ => data}/img/flags/_commonwealth.png (100%)
 rename assets/{ => data}/img/flags/_england.png (100%)
 rename assets/{ => data}/img/flags/_gosquared.png (100%)
 rename assets/{ => data}/img/flags/_kosovo.png (100%)
 rename assets/{ => data}/img/flags/_mars.png (100%)
 rename assets/{ => data}/img/flags/_nagorno-karabakh.png (100%)
 rename assets/{ => data}/img/flags/_nato.png (100%)
 rename assets/{ => data}/img/flags/_northern-cyprus.png (100%)
 rename assets/{ => data}/img/flags/_olympics.png (100%)
 rename assets/{ => data}/img/flags/_red-cross.png (100%)
 rename assets/{ => data}/img/flags/_scotland.png (100%)
 rename assets/{ => data}/img/flags/_somaliland.png (100%)
 rename assets/{ => data}/img/flags/_south-ossetia.png (100%)
 rename assets/{ => data}/img/flags/_united-nations.png (100%)
 rename assets/{ => data}/img/flags/_unknown.png (100%)
 rename assets/{ => data}/img/flags/_wales.png (100%)
 rename assets/{ => data}/img/linux.svg (100%)
 rename assets/{ => data}/img/mac.svg (100%)
 rename assets/{ => data}/img/plants1-br.png (100%)
 rename assets/{ => data}/img/plants1.png (100%)
 rename assets/{ => data}/img/spn-feature-carousel/access-regional-content-easily.png (100%)
 rename assets/{ => data}/img/spn-feature-carousel/built-from-the-ground-up.png (100%)
 rename assets/{ => data}/img/spn-feature-carousel/bye-bye-vpns.png (100%)
 rename assets/{ => data}/img/spn-feature-carousel/easily-control-your-privacy.png (100%)
 rename assets/{ => data}/img/spn-feature-carousel/multiple-identities-for-each-app.png (100%)
 rename assets/{ => data}/img/spn-login.png (100%)
 rename assets/{ => data}/img/windows.svg (100%)
 rename assets/{ => data}/world-50m.json (100%)
 create mode 100644 assets/icons.go
 create mode 100644 assets/icons_default.go
 create mode 100644 assets/icons_windows.go
 create mode 100644 cmds/notifier/.gitignore
 create mode 100644 cmds/notifier/README.md
 create mode 100644 cmds/notifier/http_api.go
 create mode 100644 cmds/notifier/icons.go
 create mode 100644 cmds/notifier/main.go
 create mode 100644 cmds/notifier/notification.go
 create mode 100644 cmds/notifier/notify.go
 create mode 100644 cmds/notifier/notify_linux.go
 create mode 100644 cmds/notifier/notify_windows.go
 create mode 100644 cmds/notifier/shutdown.go
 create mode 100644 cmds/notifier/snoretoast-guid.patch
 create mode 100644 cmds/notifier/spn.go
 create mode 100644 cmds/notifier/subsystems.go
 create mode 100644 cmds/notifier/tray.go
 create mode 100644 cmds/notifier/wintoast/notification_builder.go
 create mode 100644 cmds/notifier/wintoast/wintoast.go
 delete mode 100644 runtime/.gitkeep

diff --git a/Earthfile b/Earthfile
index 3638f0d6..c0da3562 100644
--- a/Earthfile
+++ b/Earthfile
@@ -35,6 +35,13 @@ go-base:
     COPY service ./service
     COPY spn ./spn
 
+    # The cmds/notifier embeds some icons but go:embed is not allowed
+    # to leave the package directory so there's a small go-package in
+    # assets. Once we drop the notify in favor of the tauri replacement
+    # we can remove the following line and also remove all go-code from
+    # ./assets
+    COPY assets ./assets
+
 # mod-tidy runs 'go mod tidy', saving go.mod and go.sum locally.
 mod-tidy:
     FROM +go-base
@@ -140,7 +147,7 @@ angular-deps:
 
     COPY desktop/angular/package.json .
     COPY desktop/angular/package-lock.json .
-    COPY assets/ ./assets
+    COPY assets/data ./assets
 
     RUN npm install
 
diff --git a/assets/fonts/Roboto-300/LICENSE.txt b/assets/data/fonts/Roboto-300/LICENSE.txt
similarity index 100%
rename from assets/fonts/Roboto-300/LICENSE.txt
rename to assets/data/fonts/Roboto-300/LICENSE.txt
diff --git a/assets/fonts/Roboto-300/Roboto-300.eot b/assets/data/fonts/Roboto-300/Roboto-300.eot
similarity index 100%
rename from assets/fonts/Roboto-300/Roboto-300.eot
rename to assets/data/fonts/Roboto-300/Roboto-300.eot
diff --git a/assets/fonts/Roboto-300/Roboto-300.svg b/assets/data/fonts/Roboto-300/Roboto-300.svg
similarity index 100%
rename from assets/fonts/Roboto-300/Roboto-300.svg
rename to assets/data/fonts/Roboto-300/Roboto-300.svg
diff --git a/assets/fonts/Roboto-300/Roboto-300.ttf b/assets/data/fonts/Roboto-300/Roboto-300.ttf
similarity index 100%
rename from assets/fonts/Roboto-300/Roboto-300.ttf
rename to assets/data/fonts/Roboto-300/Roboto-300.ttf
diff --git a/assets/fonts/Roboto-300/Roboto-300.woff b/assets/data/fonts/Roboto-300/Roboto-300.woff
similarity index 100%
rename from assets/fonts/Roboto-300/Roboto-300.woff
rename to assets/data/fonts/Roboto-300/Roboto-300.woff
diff --git a/assets/fonts/Roboto-300/Roboto-300.woff2 b/assets/data/fonts/Roboto-300/Roboto-300.woff2
similarity index 100%
rename from assets/fonts/Roboto-300/Roboto-300.woff2
rename to assets/data/fonts/Roboto-300/Roboto-300.woff2
diff --git a/assets/fonts/Roboto-300italic/LICENSE.txt b/assets/data/fonts/Roboto-300italic/LICENSE.txt
similarity index 100%
rename from assets/fonts/Roboto-300italic/LICENSE.txt
rename to assets/data/fonts/Roboto-300italic/LICENSE.txt
diff --git a/assets/fonts/Roboto-300italic/Roboto-300italic.eot b/assets/data/fonts/Roboto-300italic/Roboto-300italic.eot
similarity index 100%
rename from assets/fonts/Roboto-300italic/Roboto-300italic.eot
rename to assets/data/fonts/Roboto-300italic/Roboto-300italic.eot
diff --git a/assets/fonts/Roboto-300italic/Roboto-300italic.svg b/assets/data/fonts/Roboto-300italic/Roboto-300italic.svg
similarity index 100%
rename from assets/fonts/Roboto-300italic/Roboto-300italic.svg
rename to assets/data/fonts/Roboto-300italic/Roboto-300italic.svg
diff --git a/assets/fonts/Roboto-300italic/Roboto-300italic.ttf b/assets/data/fonts/Roboto-300italic/Roboto-300italic.ttf
similarity index 100%
rename from assets/fonts/Roboto-300italic/Roboto-300italic.ttf
rename to assets/data/fonts/Roboto-300italic/Roboto-300italic.ttf
diff --git a/assets/fonts/Roboto-300italic/Roboto-300italic.woff b/assets/data/fonts/Roboto-300italic/Roboto-300italic.woff
similarity index 100%
rename from assets/fonts/Roboto-300italic/Roboto-300italic.woff
rename to assets/data/fonts/Roboto-300italic/Roboto-300italic.woff
diff --git a/assets/fonts/Roboto-300italic/Roboto-300italic.woff2 b/assets/data/fonts/Roboto-300italic/Roboto-300italic.woff2
similarity index 100%
rename from assets/fonts/Roboto-300italic/Roboto-300italic.woff2
rename to assets/data/fonts/Roboto-300italic/Roboto-300italic.woff2
diff --git a/assets/fonts/Roboto-500/LICENSE.txt b/assets/data/fonts/Roboto-500/LICENSE.txt
similarity index 100%
rename from assets/fonts/Roboto-500/LICENSE.txt
rename to assets/data/fonts/Roboto-500/LICENSE.txt
diff --git a/assets/fonts/Roboto-500/Roboto-500.eot b/assets/data/fonts/Roboto-500/Roboto-500.eot
similarity index 100%
rename from assets/fonts/Roboto-500/Roboto-500.eot
rename to assets/data/fonts/Roboto-500/Roboto-500.eot
diff --git a/assets/fonts/Roboto-500/Roboto-500.svg b/assets/data/fonts/Roboto-500/Roboto-500.svg
similarity index 100%
rename from assets/fonts/Roboto-500/Roboto-500.svg
rename to assets/data/fonts/Roboto-500/Roboto-500.svg
diff --git a/assets/fonts/Roboto-500/Roboto-500.ttf b/assets/data/fonts/Roboto-500/Roboto-500.ttf
similarity index 100%
rename from assets/fonts/Roboto-500/Roboto-500.ttf
rename to assets/data/fonts/Roboto-500/Roboto-500.ttf
diff --git a/assets/fonts/Roboto-500/Roboto-500.woff b/assets/data/fonts/Roboto-500/Roboto-500.woff
similarity index 100%
rename from assets/fonts/Roboto-500/Roboto-500.woff
rename to assets/data/fonts/Roboto-500/Roboto-500.woff
diff --git a/assets/fonts/Roboto-500/Roboto-500.woff2 b/assets/data/fonts/Roboto-500/Roboto-500.woff2
similarity index 100%
rename from assets/fonts/Roboto-500/Roboto-500.woff2
rename to assets/data/fonts/Roboto-500/Roboto-500.woff2
diff --git a/assets/fonts/Roboto-500italic/LICENSE.txt b/assets/data/fonts/Roboto-500italic/LICENSE.txt
similarity index 100%
rename from assets/fonts/Roboto-500italic/LICENSE.txt
rename to assets/data/fonts/Roboto-500italic/LICENSE.txt
diff --git a/assets/fonts/Roboto-500italic/Roboto-500italic.eot b/assets/data/fonts/Roboto-500italic/Roboto-500italic.eot
similarity index 100%
rename from assets/fonts/Roboto-500italic/Roboto-500italic.eot
rename to assets/data/fonts/Roboto-500italic/Roboto-500italic.eot
diff --git a/assets/fonts/Roboto-500italic/Roboto-500italic.svg b/assets/data/fonts/Roboto-500italic/Roboto-500italic.svg
similarity index 100%
rename from assets/fonts/Roboto-500italic/Roboto-500italic.svg
rename to assets/data/fonts/Roboto-500italic/Roboto-500italic.svg
diff --git a/assets/fonts/Roboto-500italic/Roboto-500italic.ttf b/assets/data/fonts/Roboto-500italic/Roboto-500italic.ttf
similarity index 100%
rename from assets/fonts/Roboto-500italic/Roboto-500italic.ttf
rename to assets/data/fonts/Roboto-500italic/Roboto-500italic.ttf
diff --git a/assets/fonts/Roboto-500italic/Roboto-500italic.woff b/assets/data/fonts/Roboto-500italic/Roboto-500italic.woff
similarity index 100%
rename from assets/fonts/Roboto-500italic/Roboto-500italic.woff
rename to assets/data/fonts/Roboto-500italic/Roboto-500italic.woff
diff --git a/assets/fonts/Roboto-500italic/Roboto-500italic.woff2 b/assets/data/fonts/Roboto-500italic/Roboto-500italic.woff2
similarity index 100%
rename from assets/fonts/Roboto-500italic/Roboto-500italic.woff2
rename to assets/data/fonts/Roboto-500italic/Roboto-500italic.woff2
diff --git a/assets/fonts/Roboto-700/LICENSE.txt b/assets/data/fonts/Roboto-700/LICENSE.txt
similarity index 100%
rename from assets/fonts/Roboto-700/LICENSE.txt
rename to assets/data/fonts/Roboto-700/LICENSE.txt
diff --git a/assets/fonts/Roboto-700/Roboto-700.eot b/assets/data/fonts/Roboto-700/Roboto-700.eot
similarity index 100%
rename from assets/fonts/Roboto-700/Roboto-700.eot
rename to assets/data/fonts/Roboto-700/Roboto-700.eot
diff --git a/assets/fonts/Roboto-700/Roboto-700.svg b/assets/data/fonts/Roboto-700/Roboto-700.svg
similarity index 100%
rename from assets/fonts/Roboto-700/Roboto-700.svg
rename to assets/data/fonts/Roboto-700/Roboto-700.svg
diff --git a/assets/fonts/Roboto-700/Roboto-700.ttf b/assets/data/fonts/Roboto-700/Roboto-700.ttf
similarity index 100%
rename from assets/fonts/Roboto-700/Roboto-700.ttf
rename to assets/data/fonts/Roboto-700/Roboto-700.ttf
diff --git a/assets/fonts/Roboto-700/Roboto-700.woff b/assets/data/fonts/Roboto-700/Roboto-700.woff
similarity index 100%
rename from assets/fonts/Roboto-700/Roboto-700.woff
rename to assets/data/fonts/Roboto-700/Roboto-700.woff
diff --git a/assets/fonts/Roboto-700/Roboto-700.woff2 b/assets/data/fonts/Roboto-700/Roboto-700.woff2
similarity index 100%
rename from assets/fonts/Roboto-700/Roboto-700.woff2
rename to assets/data/fonts/Roboto-700/Roboto-700.woff2
diff --git a/assets/fonts/Roboto-700italic/LICENSE.txt b/assets/data/fonts/Roboto-700italic/LICENSE.txt
similarity index 100%
rename from assets/fonts/Roboto-700italic/LICENSE.txt
rename to assets/data/fonts/Roboto-700italic/LICENSE.txt
diff --git a/assets/fonts/Roboto-700italic/Roboto-700italic.eot b/assets/data/fonts/Roboto-700italic/Roboto-700italic.eot
similarity index 100%
rename from assets/fonts/Roboto-700italic/Roboto-700italic.eot
rename to assets/data/fonts/Roboto-700italic/Roboto-700italic.eot
diff --git a/assets/fonts/Roboto-700italic/Roboto-700italic.svg b/assets/data/fonts/Roboto-700italic/Roboto-700italic.svg
similarity index 100%
rename from assets/fonts/Roboto-700italic/Roboto-700italic.svg
rename to assets/data/fonts/Roboto-700italic/Roboto-700italic.svg
diff --git a/assets/fonts/Roboto-700italic/Roboto-700italic.ttf b/assets/data/fonts/Roboto-700italic/Roboto-700italic.ttf
similarity index 100%
rename from assets/fonts/Roboto-700italic/Roboto-700italic.ttf
rename to assets/data/fonts/Roboto-700italic/Roboto-700italic.ttf
diff --git a/assets/fonts/Roboto-700italic/Roboto-700italic.woff b/assets/data/fonts/Roboto-700italic/Roboto-700italic.woff
similarity index 100%
rename from assets/fonts/Roboto-700italic/Roboto-700italic.woff
rename to assets/data/fonts/Roboto-700italic/Roboto-700italic.woff
diff --git a/assets/fonts/Roboto-700italic/Roboto-700italic.woff2 b/assets/data/fonts/Roboto-700italic/Roboto-700italic.woff2
similarity index 100%
rename from assets/fonts/Roboto-700italic/Roboto-700italic.woff2
rename to assets/data/fonts/Roboto-700italic/Roboto-700italic.woff2
diff --git a/assets/fonts/Roboto-italic/LICENSE.txt b/assets/data/fonts/Roboto-italic/LICENSE.txt
similarity index 100%
rename from assets/fonts/Roboto-italic/LICENSE.txt
rename to assets/data/fonts/Roboto-italic/LICENSE.txt
diff --git a/assets/fonts/Roboto-italic/Roboto-italic.eot b/assets/data/fonts/Roboto-italic/Roboto-italic.eot
similarity index 100%
rename from assets/fonts/Roboto-italic/Roboto-italic.eot
rename to assets/data/fonts/Roboto-italic/Roboto-italic.eot
diff --git a/assets/fonts/Roboto-italic/Roboto-italic.svg b/assets/data/fonts/Roboto-italic/Roboto-italic.svg
similarity index 100%
rename from assets/fonts/Roboto-italic/Roboto-italic.svg
rename to assets/data/fonts/Roboto-italic/Roboto-italic.svg
diff --git a/assets/fonts/Roboto-italic/Roboto-italic.ttf b/assets/data/fonts/Roboto-italic/Roboto-italic.ttf
similarity index 100%
rename from assets/fonts/Roboto-italic/Roboto-italic.ttf
rename to assets/data/fonts/Roboto-italic/Roboto-italic.ttf
diff --git a/assets/fonts/Roboto-italic/Roboto-italic.woff b/assets/data/fonts/Roboto-italic/Roboto-italic.woff
similarity index 100%
rename from assets/fonts/Roboto-italic/Roboto-italic.woff
rename to assets/data/fonts/Roboto-italic/Roboto-italic.woff
diff --git a/assets/fonts/Roboto-italic/Roboto-italic.woff2 b/assets/data/fonts/Roboto-italic/Roboto-italic.woff2
similarity index 100%
rename from assets/fonts/Roboto-italic/Roboto-italic.woff2
rename to assets/data/fonts/Roboto-italic/Roboto-italic.woff2
diff --git a/assets/fonts/Roboto-regular/LICENSE.txt b/assets/data/fonts/Roboto-regular/LICENSE.txt
similarity index 100%
rename from assets/fonts/Roboto-regular/LICENSE.txt
rename to assets/data/fonts/Roboto-regular/LICENSE.txt
diff --git a/assets/fonts/Roboto-regular/Roboto-regular.eot b/assets/data/fonts/Roboto-regular/Roboto-regular.eot
similarity index 100%
rename from assets/fonts/Roboto-regular/Roboto-regular.eot
rename to assets/data/fonts/Roboto-regular/Roboto-regular.eot
diff --git a/assets/fonts/Roboto-regular/Roboto-regular.svg b/assets/data/fonts/Roboto-regular/Roboto-regular.svg
similarity index 100%
rename from assets/fonts/Roboto-regular/Roboto-regular.svg
rename to assets/data/fonts/Roboto-regular/Roboto-regular.svg
diff --git a/assets/fonts/Roboto-regular/Roboto-regular.ttf b/assets/data/fonts/Roboto-regular/Roboto-regular.ttf
similarity index 100%
rename from assets/fonts/Roboto-regular/Roboto-regular.ttf
rename to assets/data/fonts/Roboto-regular/Roboto-regular.ttf
diff --git a/assets/fonts/Roboto-regular/Roboto-regular.woff b/assets/data/fonts/Roboto-regular/Roboto-regular.woff
similarity index 100%
rename from assets/fonts/Roboto-regular/Roboto-regular.woff
rename to assets/data/fonts/Roboto-regular/Roboto-regular.woff
diff --git a/assets/fonts/Roboto-regular/Roboto-regular.woff2 b/assets/data/fonts/Roboto-regular/Roboto-regular.woff2
similarity index 100%
rename from assets/fonts/Roboto-regular/Roboto-regular.woff2
rename to assets/data/fonts/Roboto-regular/Roboto-regular.woff2
diff --git a/assets/fonts/roboto-slimfix.css b/assets/data/fonts/roboto-slimfix.css
similarity index 100%
rename from assets/fonts/roboto-slimfix.css
rename to assets/data/fonts/roboto-slimfix.css
diff --git a/assets/fonts/roboto.css b/assets/data/fonts/roboto.css
similarity index 100%
rename from assets/fonts/roboto.css
rename to assets/data/fonts/roboto.css
diff --git a/assets/icons/README.md b/assets/data/icons/README.md
similarity index 100%
rename from assets/icons/README.md
rename to assets/data/icons/README.md
diff --git a/assets/icons/pm_dark_128.png b/assets/data/icons/pm_dark_128.png
similarity index 100%
rename from assets/icons/pm_dark_128.png
rename to assets/data/icons/pm_dark_128.png
diff --git a/assets/icons/pm_dark_256.png b/assets/data/icons/pm_dark_256.png
similarity index 100%
rename from assets/icons/pm_dark_256.png
rename to assets/data/icons/pm_dark_256.png
diff --git a/assets/icons/pm_dark_512.ico b/assets/data/icons/pm_dark_512.ico
similarity index 100%
rename from assets/icons/pm_dark_512.ico
rename to assets/data/icons/pm_dark_512.ico
diff --git a/assets/icons/pm_dark_512.png b/assets/data/icons/pm_dark_512.png
similarity index 100%
rename from assets/icons/pm_dark_512.png
rename to assets/data/icons/pm_dark_512.png
diff --git a/assets/icons/pm_dark_blue_128.png b/assets/data/icons/pm_dark_blue_128.png
similarity index 100%
rename from assets/icons/pm_dark_blue_128.png
rename to assets/data/icons/pm_dark_blue_128.png
diff --git a/assets/icons/pm_dark_blue_256.png b/assets/data/icons/pm_dark_blue_256.png
similarity index 100%
rename from assets/icons/pm_dark_blue_256.png
rename to assets/data/icons/pm_dark_blue_256.png
diff --git a/assets/icons/pm_dark_blue_512.ico b/assets/data/icons/pm_dark_blue_512.ico
similarity index 100%
rename from assets/icons/pm_dark_blue_512.ico
rename to assets/data/icons/pm_dark_blue_512.ico
diff --git a/assets/icons/pm_dark_blue_512.png b/assets/data/icons/pm_dark_blue_512.png
similarity index 100%
rename from assets/icons/pm_dark_blue_512.png
rename to assets/data/icons/pm_dark_blue_512.png
diff --git a/assets/icons/pm_dark_green_128.png b/assets/data/icons/pm_dark_green_128.png
similarity index 100%
rename from assets/icons/pm_dark_green_128.png
rename to assets/data/icons/pm_dark_green_128.png
diff --git a/assets/icons/pm_dark_green_256.png b/assets/data/icons/pm_dark_green_256.png
similarity index 100%
rename from assets/icons/pm_dark_green_256.png
rename to assets/data/icons/pm_dark_green_256.png
diff --git a/assets/icons/pm_dark_green_512.ico b/assets/data/icons/pm_dark_green_512.ico
similarity index 100%
rename from assets/icons/pm_dark_green_512.ico
rename to assets/data/icons/pm_dark_green_512.ico
diff --git a/assets/icons/pm_dark_green_512.png b/assets/data/icons/pm_dark_green_512.png
similarity index 100%
rename from assets/icons/pm_dark_green_512.png
rename to assets/data/icons/pm_dark_green_512.png
diff --git a/assets/icons/pm_dark_red_128.png b/assets/data/icons/pm_dark_red_128.png
similarity index 100%
rename from assets/icons/pm_dark_red_128.png
rename to assets/data/icons/pm_dark_red_128.png
diff --git a/assets/icons/pm_dark_red_256.png b/assets/data/icons/pm_dark_red_256.png
similarity index 100%
rename from assets/icons/pm_dark_red_256.png
rename to assets/data/icons/pm_dark_red_256.png
diff --git a/assets/icons/pm_dark_red_512.ico b/assets/data/icons/pm_dark_red_512.ico
similarity index 100%
rename from assets/icons/pm_dark_red_512.ico
rename to assets/data/icons/pm_dark_red_512.ico
diff --git a/assets/icons/pm_dark_red_512.png b/assets/data/icons/pm_dark_red_512.png
similarity index 100%
rename from assets/icons/pm_dark_red_512.png
rename to assets/data/icons/pm_dark_red_512.png
diff --git a/assets/icons/pm_dark_yellow_128.png b/assets/data/icons/pm_dark_yellow_128.png
similarity index 100%
rename from assets/icons/pm_dark_yellow_128.png
rename to assets/data/icons/pm_dark_yellow_128.png
diff --git a/assets/icons/pm_dark_yellow_256.png b/assets/data/icons/pm_dark_yellow_256.png
similarity index 100%
rename from assets/icons/pm_dark_yellow_256.png
rename to assets/data/icons/pm_dark_yellow_256.png
diff --git a/assets/icons/pm_dark_yellow_512.ico b/assets/data/icons/pm_dark_yellow_512.ico
similarity index 100%
rename from assets/icons/pm_dark_yellow_512.ico
rename to assets/data/icons/pm_dark_yellow_512.ico
diff --git a/assets/icons/pm_dark_yellow_512.png b/assets/data/icons/pm_dark_yellow_512.png
similarity index 100%
rename from assets/icons/pm_dark_yellow_512.png
rename to assets/data/icons/pm_dark_yellow_512.png
diff --git a/assets/icons/pm_light_128.png b/assets/data/icons/pm_light_128.png
similarity index 100%
rename from assets/icons/pm_light_128.png
rename to assets/data/icons/pm_light_128.png
diff --git a/assets/icons/pm_light_256.png b/assets/data/icons/pm_light_256.png
similarity index 100%
rename from assets/icons/pm_light_256.png
rename to assets/data/icons/pm_light_256.png
diff --git a/assets/icons/pm_light_512.ico b/assets/data/icons/pm_light_512.ico
similarity index 100%
rename from assets/icons/pm_light_512.ico
rename to assets/data/icons/pm_light_512.ico
diff --git a/assets/icons/pm_light_512.png b/assets/data/icons/pm_light_512.png
similarity index 100%
rename from assets/icons/pm_light_512.png
rename to assets/data/icons/pm_light_512.png
diff --git a/assets/icons/pm_light_blue_128.png b/assets/data/icons/pm_light_blue_128.png
similarity index 100%
rename from assets/icons/pm_light_blue_128.png
rename to assets/data/icons/pm_light_blue_128.png
diff --git a/assets/icons/pm_light_blue_256.png b/assets/data/icons/pm_light_blue_256.png
similarity index 100%
rename from assets/icons/pm_light_blue_256.png
rename to assets/data/icons/pm_light_blue_256.png
diff --git a/assets/icons/pm_light_blue_512.ico b/assets/data/icons/pm_light_blue_512.ico
similarity index 100%
rename from assets/icons/pm_light_blue_512.ico
rename to assets/data/icons/pm_light_blue_512.ico
diff --git a/assets/icons/pm_light_blue_512.png b/assets/data/icons/pm_light_blue_512.png
similarity index 100%
rename from assets/icons/pm_light_blue_512.png
rename to assets/data/icons/pm_light_blue_512.png
diff --git a/assets/icons/pm_light_green_128.png b/assets/data/icons/pm_light_green_128.png
similarity index 100%
rename from assets/icons/pm_light_green_128.png
rename to assets/data/icons/pm_light_green_128.png
diff --git a/assets/icons/pm_light_green_256.png b/assets/data/icons/pm_light_green_256.png
similarity index 100%
rename from assets/icons/pm_light_green_256.png
rename to assets/data/icons/pm_light_green_256.png
diff --git a/assets/icons/pm_light_green_512.ico b/assets/data/icons/pm_light_green_512.ico
similarity index 100%
rename from assets/icons/pm_light_green_512.ico
rename to assets/data/icons/pm_light_green_512.ico
diff --git a/assets/icons/pm_light_green_512.png b/assets/data/icons/pm_light_green_512.png
similarity index 100%
rename from assets/icons/pm_light_green_512.png
rename to assets/data/icons/pm_light_green_512.png
diff --git a/assets/icons/pm_light_red_128.png b/assets/data/icons/pm_light_red_128.png
similarity index 100%
rename from assets/icons/pm_light_red_128.png
rename to assets/data/icons/pm_light_red_128.png
diff --git a/assets/icons/pm_light_red_256.png b/assets/data/icons/pm_light_red_256.png
similarity index 100%
rename from assets/icons/pm_light_red_256.png
rename to assets/data/icons/pm_light_red_256.png
diff --git a/assets/icons/pm_light_red_512.ico b/assets/data/icons/pm_light_red_512.ico
similarity index 100%
rename from assets/icons/pm_light_red_512.ico
rename to assets/data/icons/pm_light_red_512.ico
diff --git a/assets/icons/pm_light_red_512.png b/assets/data/icons/pm_light_red_512.png
similarity index 100%
rename from assets/icons/pm_light_red_512.png
rename to assets/data/icons/pm_light_red_512.png
diff --git a/assets/icons/pm_light_yellow_128.png b/assets/data/icons/pm_light_yellow_128.png
similarity index 100%
rename from assets/icons/pm_light_yellow_128.png
rename to assets/data/icons/pm_light_yellow_128.png
diff --git a/assets/icons/pm_light_yellow_256.png b/assets/data/icons/pm_light_yellow_256.png
similarity index 100%
rename from assets/icons/pm_light_yellow_256.png
rename to assets/data/icons/pm_light_yellow_256.png
diff --git a/assets/icons/pm_light_yellow_512.ico b/assets/data/icons/pm_light_yellow_512.ico
similarity index 100%
rename from assets/icons/pm_light_yellow_512.ico
rename to assets/data/icons/pm_light_yellow_512.ico
diff --git a/assets/icons/pm_light_yellow_512.png b/assets/data/icons/pm_light_yellow_512.png
similarity index 100%
rename from assets/icons/pm_light_yellow_512.png
rename to assets/data/icons/pm_light_yellow_512.png
diff --git a/assets/img/Mobile.svg b/assets/data/img/Mobile.svg
similarity index 100%
rename from assets/img/Mobile.svg
rename to assets/data/img/Mobile.svg
diff --git a/assets/img/flags/AD.png b/assets/data/img/flags/AD.png
similarity index 100%
rename from assets/img/flags/AD.png
rename to assets/data/img/flags/AD.png
diff --git a/assets/img/flags/AE.png b/assets/data/img/flags/AE.png
similarity index 100%
rename from assets/img/flags/AE.png
rename to assets/data/img/flags/AE.png
diff --git a/assets/img/flags/AF.png b/assets/data/img/flags/AF.png
similarity index 100%
rename from assets/img/flags/AF.png
rename to assets/data/img/flags/AF.png
diff --git a/assets/img/flags/AG.png b/assets/data/img/flags/AG.png
similarity index 100%
rename from assets/img/flags/AG.png
rename to assets/data/img/flags/AG.png
diff --git a/assets/img/flags/AI.png b/assets/data/img/flags/AI.png
similarity index 100%
rename from assets/img/flags/AI.png
rename to assets/data/img/flags/AI.png
diff --git a/assets/img/flags/AL.png b/assets/data/img/flags/AL.png
similarity index 100%
rename from assets/img/flags/AL.png
rename to assets/data/img/flags/AL.png
diff --git a/assets/img/flags/AM.png b/assets/data/img/flags/AM.png
similarity index 100%
rename from assets/img/flags/AM.png
rename to assets/data/img/flags/AM.png
diff --git a/assets/img/flags/AN.png b/assets/data/img/flags/AN.png
similarity index 100%
rename from assets/img/flags/AN.png
rename to assets/data/img/flags/AN.png
diff --git a/assets/img/flags/AO.png b/assets/data/img/flags/AO.png
similarity index 100%
rename from assets/img/flags/AO.png
rename to assets/data/img/flags/AO.png
diff --git a/assets/img/flags/AQ.png b/assets/data/img/flags/AQ.png
similarity index 100%
rename from assets/img/flags/AQ.png
rename to assets/data/img/flags/AQ.png
diff --git a/assets/img/flags/AR.png b/assets/data/img/flags/AR.png
similarity index 100%
rename from assets/img/flags/AR.png
rename to assets/data/img/flags/AR.png
diff --git a/assets/img/flags/AS.png b/assets/data/img/flags/AS.png
similarity index 100%
rename from assets/img/flags/AS.png
rename to assets/data/img/flags/AS.png
diff --git a/assets/img/flags/AT.png b/assets/data/img/flags/AT.png
similarity index 100%
rename from assets/img/flags/AT.png
rename to assets/data/img/flags/AT.png
diff --git a/assets/img/flags/AU.png b/assets/data/img/flags/AU.png
similarity index 100%
rename from assets/img/flags/AU.png
rename to assets/data/img/flags/AU.png
diff --git a/assets/img/flags/AW.png b/assets/data/img/flags/AW.png
similarity index 100%
rename from assets/img/flags/AW.png
rename to assets/data/img/flags/AW.png
diff --git a/assets/img/flags/AX.png b/assets/data/img/flags/AX.png
similarity index 100%
rename from assets/img/flags/AX.png
rename to assets/data/img/flags/AX.png
diff --git a/assets/img/flags/AZ.png b/assets/data/img/flags/AZ.png
similarity index 100%
rename from assets/img/flags/AZ.png
rename to assets/data/img/flags/AZ.png
diff --git a/assets/img/flags/BA.png b/assets/data/img/flags/BA.png
similarity index 100%
rename from assets/img/flags/BA.png
rename to assets/data/img/flags/BA.png
diff --git a/assets/img/flags/BB.png b/assets/data/img/flags/BB.png
similarity index 100%
rename from assets/img/flags/BB.png
rename to assets/data/img/flags/BB.png
diff --git a/assets/img/flags/BD.png b/assets/data/img/flags/BD.png
similarity index 100%
rename from assets/img/flags/BD.png
rename to assets/data/img/flags/BD.png
diff --git a/assets/img/flags/BE.png b/assets/data/img/flags/BE.png
similarity index 100%
rename from assets/img/flags/BE.png
rename to assets/data/img/flags/BE.png
diff --git a/assets/img/flags/BF.png b/assets/data/img/flags/BF.png
similarity index 100%
rename from assets/img/flags/BF.png
rename to assets/data/img/flags/BF.png
diff --git a/assets/img/flags/BG.png b/assets/data/img/flags/BG.png
similarity index 100%
rename from assets/img/flags/BG.png
rename to assets/data/img/flags/BG.png
diff --git a/assets/img/flags/BH.png b/assets/data/img/flags/BH.png
similarity index 100%
rename from assets/img/flags/BH.png
rename to assets/data/img/flags/BH.png
diff --git a/assets/img/flags/BI.png b/assets/data/img/flags/BI.png
similarity index 100%
rename from assets/img/flags/BI.png
rename to assets/data/img/flags/BI.png
diff --git a/assets/img/flags/BJ.png b/assets/data/img/flags/BJ.png
similarity index 100%
rename from assets/img/flags/BJ.png
rename to assets/data/img/flags/BJ.png
diff --git a/assets/img/flags/BL.png b/assets/data/img/flags/BL.png
similarity index 100%
rename from assets/img/flags/BL.png
rename to assets/data/img/flags/BL.png
diff --git a/assets/img/flags/BM.png b/assets/data/img/flags/BM.png
similarity index 100%
rename from assets/img/flags/BM.png
rename to assets/data/img/flags/BM.png
diff --git a/assets/img/flags/BN.png b/assets/data/img/flags/BN.png
similarity index 100%
rename from assets/img/flags/BN.png
rename to assets/data/img/flags/BN.png
diff --git a/assets/img/flags/BO.png b/assets/data/img/flags/BO.png
similarity index 100%
rename from assets/img/flags/BO.png
rename to assets/data/img/flags/BO.png
diff --git a/assets/img/flags/BR.png b/assets/data/img/flags/BR.png
similarity index 100%
rename from assets/img/flags/BR.png
rename to assets/data/img/flags/BR.png
diff --git a/assets/img/flags/BS.png b/assets/data/img/flags/BS.png
similarity index 100%
rename from assets/img/flags/BS.png
rename to assets/data/img/flags/BS.png
diff --git a/assets/img/flags/BT.png b/assets/data/img/flags/BT.png
similarity index 100%
rename from assets/img/flags/BT.png
rename to assets/data/img/flags/BT.png
diff --git a/assets/img/flags/BW.png b/assets/data/img/flags/BW.png
similarity index 100%
rename from assets/img/flags/BW.png
rename to assets/data/img/flags/BW.png
diff --git a/assets/img/flags/BY.png b/assets/data/img/flags/BY.png
similarity index 100%
rename from assets/img/flags/BY.png
rename to assets/data/img/flags/BY.png
diff --git a/assets/img/flags/BZ.png b/assets/data/img/flags/BZ.png
similarity index 100%
rename from assets/img/flags/BZ.png
rename to assets/data/img/flags/BZ.png
diff --git a/assets/img/flags/CA.png b/assets/data/img/flags/CA.png
similarity index 100%
rename from assets/img/flags/CA.png
rename to assets/data/img/flags/CA.png
diff --git a/assets/img/flags/CC.png b/assets/data/img/flags/CC.png
similarity index 100%
rename from assets/img/flags/CC.png
rename to assets/data/img/flags/CC.png
diff --git a/assets/img/flags/CD.png b/assets/data/img/flags/CD.png
similarity index 100%
rename from assets/img/flags/CD.png
rename to assets/data/img/flags/CD.png
diff --git a/assets/img/flags/CF.png b/assets/data/img/flags/CF.png
similarity index 100%
rename from assets/img/flags/CF.png
rename to assets/data/img/flags/CF.png
diff --git a/assets/img/flags/CG.png b/assets/data/img/flags/CG.png
similarity index 100%
rename from assets/img/flags/CG.png
rename to assets/data/img/flags/CG.png
diff --git a/assets/img/flags/CH.png b/assets/data/img/flags/CH.png
similarity index 100%
rename from assets/img/flags/CH.png
rename to assets/data/img/flags/CH.png
diff --git a/assets/img/flags/CI.png b/assets/data/img/flags/CI.png
similarity index 100%
rename from assets/img/flags/CI.png
rename to assets/data/img/flags/CI.png
diff --git a/assets/img/flags/CK.png b/assets/data/img/flags/CK.png
similarity index 100%
rename from assets/img/flags/CK.png
rename to assets/data/img/flags/CK.png
diff --git a/assets/img/flags/CL.png b/assets/data/img/flags/CL.png
similarity index 100%
rename from assets/img/flags/CL.png
rename to assets/data/img/flags/CL.png
diff --git a/assets/img/flags/CM.png b/assets/data/img/flags/CM.png
similarity index 100%
rename from assets/img/flags/CM.png
rename to assets/data/img/flags/CM.png
diff --git a/assets/img/flags/CN.png b/assets/data/img/flags/CN.png
similarity index 100%
rename from assets/img/flags/CN.png
rename to assets/data/img/flags/CN.png
diff --git a/assets/img/flags/CO.png b/assets/data/img/flags/CO.png
similarity index 100%
rename from assets/img/flags/CO.png
rename to assets/data/img/flags/CO.png
diff --git a/assets/img/flags/CR.png b/assets/data/img/flags/CR.png
similarity index 100%
rename from assets/img/flags/CR.png
rename to assets/data/img/flags/CR.png
diff --git a/assets/img/flags/CT.png b/assets/data/img/flags/CT.png
similarity index 100%
rename from assets/img/flags/CT.png
rename to assets/data/img/flags/CT.png
diff --git a/assets/img/flags/CU.png b/assets/data/img/flags/CU.png
similarity index 100%
rename from assets/img/flags/CU.png
rename to assets/data/img/flags/CU.png
diff --git a/assets/img/flags/CV.png b/assets/data/img/flags/CV.png
similarity index 100%
rename from assets/img/flags/CV.png
rename to assets/data/img/flags/CV.png
diff --git a/assets/img/flags/CW.png b/assets/data/img/flags/CW.png
similarity index 100%
rename from assets/img/flags/CW.png
rename to assets/data/img/flags/CW.png
diff --git a/assets/img/flags/CX.png b/assets/data/img/flags/CX.png
similarity index 100%
rename from assets/img/flags/CX.png
rename to assets/data/img/flags/CX.png
diff --git a/assets/img/flags/CY.png b/assets/data/img/flags/CY.png
similarity index 100%
rename from assets/img/flags/CY.png
rename to assets/data/img/flags/CY.png
diff --git a/assets/img/flags/CZ.png b/assets/data/img/flags/CZ.png
similarity index 100%
rename from assets/img/flags/CZ.png
rename to assets/data/img/flags/CZ.png
diff --git a/assets/img/flags/DE.png b/assets/data/img/flags/DE.png
similarity index 100%
rename from assets/img/flags/DE.png
rename to assets/data/img/flags/DE.png
diff --git a/assets/img/flags/DJ.png b/assets/data/img/flags/DJ.png
similarity index 100%
rename from assets/img/flags/DJ.png
rename to assets/data/img/flags/DJ.png
diff --git a/assets/img/flags/DK.png b/assets/data/img/flags/DK.png
similarity index 100%
rename from assets/img/flags/DK.png
rename to assets/data/img/flags/DK.png
diff --git a/assets/img/flags/DM.png b/assets/data/img/flags/DM.png
similarity index 100%
rename from assets/img/flags/DM.png
rename to assets/data/img/flags/DM.png
diff --git a/assets/img/flags/DO.png b/assets/data/img/flags/DO.png
similarity index 100%
rename from assets/img/flags/DO.png
rename to assets/data/img/flags/DO.png
diff --git a/assets/img/flags/DZ.png b/assets/data/img/flags/DZ.png
similarity index 100%
rename from assets/img/flags/DZ.png
rename to assets/data/img/flags/DZ.png
diff --git a/assets/img/flags/EC.png b/assets/data/img/flags/EC.png
similarity index 100%
rename from assets/img/flags/EC.png
rename to assets/data/img/flags/EC.png
diff --git a/assets/img/flags/EE.png b/assets/data/img/flags/EE.png
similarity index 100%
rename from assets/img/flags/EE.png
rename to assets/data/img/flags/EE.png
diff --git a/assets/img/flags/EG.png b/assets/data/img/flags/EG.png
similarity index 100%
rename from assets/img/flags/EG.png
rename to assets/data/img/flags/EG.png
diff --git a/assets/img/flags/EH.png b/assets/data/img/flags/EH.png
similarity index 100%
rename from assets/img/flags/EH.png
rename to assets/data/img/flags/EH.png
diff --git a/assets/img/flags/ER.png b/assets/data/img/flags/ER.png
similarity index 100%
rename from assets/img/flags/ER.png
rename to assets/data/img/flags/ER.png
diff --git a/assets/img/flags/ES.png b/assets/data/img/flags/ES.png
similarity index 100%
rename from assets/img/flags/ES.png
rename to assets/data/img/flags/ES.png
diff --git a/assets/img/flags/ET.png b/assets/data/img/flags/ET.png
similarity index 100%
rename from assets/img/flags/ET.png
rename to assets/data/img/flags/ET.png
diff --git a/assets/img/flags/EU.png b/assets/data/img/flags/EU.png
similarity index 100%
rename from assets/img/flags/EU.png
rename to assets/data/img/flags/EU.png
diff --git a/assets/img/flags/FI.png b/assets/data/img/flags/FI.png
similarity index 100%
rename from assets/img/flags/FI.png
rename to assets/data/img/flags/FI.png
diff --git a/assets/img/flags/FJ.png b/assets/data/img/flags/FJ.png
similarity index 100%
rename from assets/img/flags/FJ.png
rename to assets/data/img/flags/FJ.png
diff --git a/assets/img/flags/FK.png b/assets/data/img/flags/FK.png
similarity index 100%
rename from assets/img/flags/FK.png
rename to assets/data/img/flags/FK.png
diff --git a/assets/img/flags/FM.png b/assets/data/img/flags/FM.png
similarity index 100%
rename from assets/img/flags/FM.png
rename to assets/data/img/flags/FM.png
diff --git a/assets/img/flags/FO.png b/assets/data/img/flags/FO.png
similarity index 100%
rename from assets/img/flags/FO.png
rename to assets/data/img/flags/FO.png
diff --git a/assets/img/flags/FR.png b/assets/data/img/flags/FR.png
similarity index 100%
rename from assets/img/flags/FR.png
rename to assets/data/img/flags/FR.png
diff --git a/assets/img/flags/GA.png b/assets/data/img/flags/GA.png
similarity index 100%
rename from assets/img/flags/GA.png
rename to assets/data/img/flags/GA.png
diff --git a/assets/img/flags/GB.png b/assets/data/img/flags/GB.png
similarity index 100%
rename from assets/img/flags/GB.png
rename to assets/data/img/flags/GB.png
diff --git a/assets/img/flags/GD.png b/assets/data/img/flags/GD.png
similarity index 100%
rename from assets/img/flags/GD.png
rename to assets/data/img/flags/GD.png
diff --git a/assets/img/flags/GE.png b/assets/data/img/flags/GE.png
similarity index 100%
rename from assets/img/flags/GE.png
rename to assets/data/img/flags/GE.png
diff --git a/assets/img/flags/GG.png b/assets/data/img/flags/GG.png
similarity index 100%
rename from assets/img/flags/GG.png
rename to assets/data/img/flags/GG.png
diff --git a/assets/img/flags/GH.png b/assets/data/img/flags/GH.png
similarity index 100%
rename from assets/img/flags/GH.png
rename to assets/data/img/flags/GH.png
diff --git a/assets/img/flags/GI.png b/assets/data/img/flags/GI.png
similarity index 100%
rename from assets/img/flags/GI.png
rename to assets/data/img/flags/GI.png
diff --git a/assets/img/flags/GL.png b/assets/data/img/flags/GL.png
similarity index 100%
rename from assets/img/flags/GL.png
rename to assets/data/img/flags/GL.png
diff --git a/assets/img/flags/GM.png b/assets/data/img/flags/GM.png
similarity index 100%
rename from assets/img/flags/GM.png
rename to assets/data/img/flags/GM.png
diff --git a/assets/img/flags/GN.png b/assets/data/img/flags/GN.png
similarity index 100%
rename from assets/img/flags/GN.png
rename to assets/data/img/flags/GN.png
diff --git a/assets/img/flags/GQ.png b/assets/data/img/flags/GQ.png
similarity index 100%
rename from assets/img/flags/GQ.png
rename to assets/data/img/flags/GQ.png
diff --git a/assets/img/flags/GR.png b/assets/data/img/flags/GR.png
similarity index 100%
rename from assets/img/flags/GR.png
rename to assets/data/img/flags/GR.png
diff --git a/assets/img/flags/GS.png b/assets/data/img/flags/GS.png
similarity index 100%
rename from assets/img/flags/GS.png
rename to assets/data/img/flags/GS.png
diff --git a/assets/img/flags/GT.png b/assets/data/img/flags/GT.png
similarity index 100%
rename from assets/img/flags/GT.png
rename to assets/data/img/flags/GT.png
diff --git a/assets/img/flags/GU.png b/assets/data/img/flags/GU.png
similarity index 100%
rename from assets/img/flags/GU.png
rename to assets/data/img/flags/GU.png
diff --git a/assets/img/flags/GW.png b/assets/data/img/flags/GW.png
similarity index 100%
rename from assets/img/flags/GW.png
rename to assets/data/img/flags/GW.png
diff --git a/assets/img/flags/GY.png b/assets/data/img/flags/GY.png
similarity index 100%
rename from assets/img/flags/GY.png
rename to assets/data/img/flags/GY.png
diff --git a/assets/img/flags/HK.png b/assets/data/img/flags/HK.png
similarity index 100%
rename from assets/img/flags/HK.png
rename to assets/data/img/flags/HK.png
diff --git a/assets/img/flags/HN.png b/assets/data/img/flags/HN.png
similarity index 100%
rename from assets/img/flags/HN.png
rename to assets/data/img/flags/HN.png
diff --git a/assets/img/flags/HR.png b/assets/data/img/flags/HR.png
similarity index 100%
rename from assets/img/flags/HR.png
rename to assets/data/img/flags/HR.png
diff --git a/assets/img/flags/HT.png b/assets/data/img/flags/HT.png
similarity index 100%
rename from assets/img/flags/HT.png
rename to assets/data/img/flags/HT.png
diff --git a/assets/img/flags/HU.png b/assets/data/img/flags/HU.png
similarity index 100%
rename from assets/img/flags/HU.png
rename to assets/data/img/flags/HU.png
diff --git a/assets/img/flags/IC.png b/assets/data/img/flags/IC.png
similarity index 100%
rename from assets/img/flags/IC.png
rename to assets/data/img/flags/IC.png
diff --git a/assets/img/flags/ID.png b/assets/data/img/flags/ID.png
similarity index 100%
rename from assets/img/flags/ID.png
rename to assets/data/img/flags/ID.png
diff --git a/assets/img/flags/IE.png b/assets/data/img/flags/IE.png
similarity index 100%
rename from assets/img/flags/IE.png
rename to assets/data/img/flags/IE.png
diff --git a/assets/img/flags/IL.png b/assets/data/img/flags/IL.png
similarity index 100%
rename from assets/img/flags/IL.png
rename to assets/data/img/flags/IL.png
diff --git a/assets/img/flags/IM.png b/assets/data/img/flags/IM.png
similarity index 100%
rename from assets/img/flags/IM.png
rename to assets/data/img/flags/IM.png
diff --git a/assets/img/flags/IN.png b/assets/data/img/flags/IN.png
similarity index 100%
rename from assets/img/flags/IN.png
rename to assets/data/img/flags/IN.png
diff --git a/assets/img/flags/IQ.png b/assets/data/img/flags/IQ.png
similarity index 100%
rename from assets/img/flags/IQ.png
rename to assets/data/img/flags/IQ.png
diff --git a/assets/img/flags/IR.png b/assets/data/img/flags/IR.png
similarity index 100%
rename from assets/img/flags/IR.png
rename to assets/data/img/flags/IR.png
diff --git a/assets/img/flags/IS.png b/assets/data/img/flags/IS.png
similarity index 100%
rename from assets/img/flags/IS.png
rename to assets/data/img/flags/IS.png
diff --git a/assets/img/flags/IT.png b/assets/data/img/flags/IT.png
similarity index 100%
rename from assets/img/flags/IT.png
rename to assets/data/img/flags/IT.png
diff --git a/assets/img/flags/JE.png b/assets/data/img/flags/JE.png
similarity index 100%
rename from assets/img/flags/JE.png
rename to assets/data/img/flags/JE.png
diff --git a/assets/img/flags/JM.png b/assets/data/img/flags/JM.png
similarity index 100%
rename from assets/img/flags/JM.png
rename to assets/data/img/flags/JM.png
diff --git a/assets/img/flags/JO.png b/assets/data/img/flags/JO.png
similarity index 100%
rename from assets/img/flags/JO.png
rename to assets/data/img/flags/JO.png
diff --git a/assets/img/flags/JP.png b/assets/data/img/flags/JP.png
similarity index 100%
rename from assets/img/flags/JP.png
rename to assets/data/img/flags/JP.png
diff --git a/assets/img/flags/KE.png b/assets/data/img/flags/KE.png
similarity index 100%
rename from assets/img/flags/KE.png
rename to assets/data/img/flags/KE.png
diff --git a/assets/img/flags/KG.png b/assets/data/img/flags/KG.png
similarity index 100%
rename from assets/img/flags/KG.png
rename to assets/data/img/flags/KG.png
diff --git a/assets/img/flags/KH.png b/assets/data/img/flags/KH.png
similarity index 100%
rename from assets/img/flags/KH.png
rename to assets/data/img/flags/KH.png
diff --git a/assets/img/flags/KI.png b/assets/data/img/flags/KI.png
similarity index 100%
rename from assets/img/flags/KI.png
rename to assets/data/img/flags/KI.png
diff --git a/assets/img/flags/KM.png b/assets/data/img/flags/KM.png
similarity index 100%
rename from assets/img/flags/KM.png
rename to assets/data/img/flags/KM.png
diff --git a/assets/img/flags/KN.png b/assets/data/img/flags/KN.png
similarity index 100%
rename from assets/img/flags/KN.png
rename to assets/data/img/flags/KN.png
diff --git a/assets/img/flags/KP.png b/assets/data/img/flags/KP.png
similarity index 100%
rename from assets/img/flags/KP.png
rename to assets/data/img/flags/KP.png
diff --git a/assets/img/flags/KR.png b/assets/data/img/flags/KR.png
similarity index 100%
rename from assets/img/flags/KR.png
rename to assets/data/img/flags/KR.png
diff --git a/assets/img/flags/KW.png b/assets/data/img/flags/KW.png
similarity index 100%
rename from assets/img/flags/KW.png
rename to assets/data/img/flags/KW.png
diff --git a/assets/img/flags/KY.png b/assets/data/img/flags/KY.png
similarity index 100%
rename from assets/img/flags/KY.png
rename to assets/data/img/flags/KY.png
diff --git a/assets/img/flags/KZ.png b/assets/data/img/flags/KZ.png
similarity index 100%
rename from assets/img/flags/KZ.png
rename to assets/data/img/flags/KZ.png
diff --git a/assets/img/flags/LA.png b/assets/data/img/flags/LA.png
similarity index 100%
rename from assets/img/flags/LA.png
rename to assets/data/img/flags/LA.png
diff --git a/assets/img/flags/LB.png b/assets/data/img/flags/LB.png
similarity index 100%
rename from assets/img/flags/LB.png
rename to assets/data/img/flags/LB.png
diff --git a/assets/img/flags/LC.png b/assets/data/img/flags/LC.png
similarity index 100%
rename from assets/img/flags/LC.png
rename to assets/data/img/flags/LC.png
diff --git a/assets/img/flags/LI.png b/assets/data/img/flags/LI.png
similarity index 100%
rename from assets/img/flags/LI.png
rename to assets/data/img/flags/LI.png
diff --git a/assets/img/flags/LICENSE.txt b/assets/data/img/flags/LICENSE.txt
similarity index 100%
rename from assets/img/flags/LICENSE.txt
rename to assets/data/img/flags/LICENSE.txt
diff --git a/assets/img/flags/LK.png b/assets/data/img/flags/LK.png
similarity index 100%
rename from assets/img/flags/LK.png
rename to assets/data/img/flags/LK.png
diff --git a/assets/img/flags/LR.png b/assets/data/img/flags/LR.png
similarity index 100%
rename from assets/img/flags/LR.png
rename to assets/data/img/flags/LR.png
diff --git a/assets/img/flags/LS.png b/assets/data/img/flags/LS.png
similarity index 100%
rename from assets/img/flags/LS.png
rename to assets/data/img/flags/LS.png
diff --git a/assets/img/flags/LT.png b/assets/data/img/flags/LT.png
similarity index 100%
rename from assets/img/flags/LT.png
rename to assets/data/img/flags/LT.png
diff --git a/assets/img/flags/LU.png b/assets/data/img/flags/LU.png
similarity index 100%
rename from assets/img/flags/LU.png
rename to assets/data/img/flags/LU.png
diff --git a/assets/img/flags/LV.png b/assets/data/img/flags/LV.png
similarity index 100%
rename from assets/img/flags/LV.png
rename to assets/data/img/flags/LV.png
diff --git a/assets/img/flags/LY.png b/assets/data/img/flags/LY.png
similarity index 100%
rename from assets/img/flags/LY.png
rename to assets/data/img/flags/LY.png
diff --git a/assets/img/flags/MA.png b/assets/data/img/flags/MA.png
similarity index 100%
rename from assets/img/flags/MA.png
rename to assets/data/img/flags/MA.png
diff --git a/assets/img/flags/MC.png b/assets/data/img/flags/MC.png
similarity index 100%
rename from assets/img/flags/MC.png
rename to assets/data/img/flags/MC.png
diff --git a/assets/img/flags/MD.png b/assets/data/img/flags/MD.png
similarity index 100%
rename from assets/img/flags/MD.png
rename to assets/data/img/flags/MD.png
diff --git a/assets/img/flags/ME.png b/assets/data/img/flags/ME.png
similarity index 100%
rename from assets/img/flags/ME.png
rename to assets/data/img/flags/ME.png
diff --git a/assets/img/flags/MF.png b/assets/data/img/flags/MF.png
similarity index 100%
rename from assets/img/flags/MF.png
rename to assets/data/img/flags/MF.png
diff --git a/assets/img/flags/MG.png b/assets/data/img/flags/MG.png
similarity index 100%
rename from assets/img/flags/MG.png
rename to assets/data/img/flags/MG.png
diff --git a/assets/img/flags/MH.png b/assets/data/img/flags/MH.png
similarity index 100%
rename from assets/img/flags/MH.png
rename to assets/data/img/flags/MH.png
diff --git a/assets/img/flags/MK.png b/assets/data/img/flags/MK.png
similarity index 100%
rename from assets/img/flags/MK.png
rename to assets/data/img/flags/MK.png
diff --git a/assets/img/flags/ML.png b/assets/data/img/flags/ML.png
similarity index 100%
rename from assets/img/flags/ML.png
rename to assets/data/img/flags/ML.png
diff --git a/assets/img/flags/MM.png b/assets/data/img/flags/MM.png
similarity index 100%
rename from assets/img/flags/MM.png
rename to assets/data/img/flags/MM.png
diff --git a/assets/img/flags/MN.png b/assets/data/img/flags/MN.png
similarity index 100%
rename from assets/img/flags/MN.png
rename to assets/data/img/flags/MN.png
diff --git a/assets/img/flags/MO.png b/assets/data/img/flags/MO.png
similarity index 100%
rename from assets/img/flags/MO.png
rename to assets/data/img/flags/MO.png
diff --git a/assets/img/flags/MP.png b/assets/data/img/flags/MP.png
similarity index 100%
rename from assets/img/flags/MP.png
rename to assets/data/img/flags/MP.png
diff --git a/assets/img/flags/MQ.png b/assets/data/img/flags/MQ.png
similarity index 100%
rename from assets/img/flags/MQ.png
rename to assets/data/img/flags/MQ.png
diff --git a/assets/img/flags/MR.png b/assets/data/img/flags/MR.png
similarity index 100%
rename from assets/img/flags/MR.png
rename to assets/data/img/flags/MR.png
diff --git a/assets/img/flags/MS.png b/assets/data/img/flags/MS.png
similarity index 100%
rename from assets/img/flags/MS.png
rename to assets/data/img/flags/MS.png
diff --git a/assets/img/flags/MT.png b/assets/data/img/flags/MT.png
similarity index 100%
rename from assets/img/flags/MT.png
rename to assets/data/img/flags/MT.png
diff --git a/assets/img/flags/MU.png b/assets/data/img/flags/MU.png
similarity index 100%
rename from assets/img/flags/MU.png
rename to assets/data/img/flags/MU.png
diff --git a/assets/img/flags/MV.png b/assets/data/img/flags/MV.png
similarity index 100%
rename from assets/img/flags/MV.png
rename to assets/data/img/flags/MV.png
diff --git a/assets/img/flags/MW.png b/assets/data/img/flags/MW.png
similarity index 100%
rename from assets/img/flags/MW.png
rename to assets/data/img/flags/MW.png
diff --git a/assets/img/flags/MX.png b/assets/data/img/flags/MX.png
similarity index 100%
rename from assets/img/flags/MX.png
rename to assets/data/img/flags/MX.png
diff --git a/assets/img/flags/MY.png b/assets/data/img/flags/MY.png
similarity index 100%
rename from assets/img/flags/MY.png
rename to assets/data/img/flags/MY.png
diff --git a/assets/img/flags/MZ.png b/assets/data/img/flags/MZ.png
similarity index 100%
rename from assets/img/flags/MZ.png
rename to assets/data/img/flags/MZ.png
diff --git a/assets/img/flags/NA.png b/assets/data/img/flags/NA.png
similarity index 100%
rename from assets/img/flags/NA.png
rename to assets/data/img/flags/NA.png
diff --git a/assets/img/flags/NC.png b/assets/data/img/flags/NC.png
similarity index 100%
rename from assets/img/flags/NC.png
rename to assets/data/img/flags/NC.png
diff --git a/assets/img/flags/NE.png b/assets/data/img/flags/NE.png
similarity index 100%
rename from assets/img/flags/NE.png
rename to assets/data/img/flags/NE.png
diff --git a/assets/img/flags/NF.png b/assets/data/img/flags/NF.png
similarity index 100%
rename from assets/img/flags/NF.png
rename to assets/data/img/flags/NF.png
diff --git a/assets/img/flags/NG.png b/assets/data/img/flags/NG.png
similarity index 100%
rename from assets/img/flags/NG.png
rename to assets/data/img/flags/NG.png
diff --git a/assets/img/flags/NI.png b/assets/data/img/flags/NI.png
similarity index 100%
rename from assets/img/flags/NI.png
rename to assets/data/img/flags/NI.png
diff --git a/assets/img/flags/NL.png b/assets/data/img/flags/NL.png
similarity index 100%
rename from assets/img/flags/NL.png
rename to assets/data/img/flags/NL.png
diff --git a/assets/img/flags/NO.png b/assets/data/img/flags/NO.png
similarity index 100%
rename from assets/img/flags/NO.png
rename to assets/data/img/flags/NO.png
diff --git a/assets/img/flags/NP.png b/assets/data/img/flags/NP.png
similarity index 100%
rename from assets/img/flags/NP.png
rename to assets/data/img/flags/NP.png
diff --git a/assets/img/flags/NR.png b/assets/data/img/flags/NR.png
similarity index 100%
rename from assets/img/flags/NR.png
rename to assets/data/img/flags/NR.png
diff --git a/assets/img/flags/NU.png b/assets/data/img/flags/NU.png
similarity index 100%
rename from assets/img/flags/NU.png
rename to assets/data/img/flags/NU.png
diff --git a/assets/img/flags/NZ.png b/assets/data/img/flags/NZ.png
similarity index 100%
rename from assets/img/flags/NZ.png
rename to assets/data/img/flags/NZ.png
diff --git a/assets/img/flags/OM.png b/assets/data/img/flags/OM.png
similarity index 100%
rename from assets/img/flags/OM.png
rename to assets/data/img/flags/OM.png
diff --git a/assets/img/flags/PA.png b/assets/data/img/flags/PA.png
similarity index 100%
rename from assets/img/flags/PA.png
rename to assets/data/img/flags/PA.png
diff --git a/assets/img/flags/PE.png b/assets/data/img/flags/PE.png
similarity index 100%
rename from assets/img/flags/PE.png
rename to assets/data/img/flags/PE.png
diff --git a/assets/img/flags/PF.png b/assets/data/img/flags/PF.png
similarity index 100%
rename from assets/img/flags/PF.png
rename to assets/data/img/flags/PF.png
diff --git a/assets/img/flags/PG.png b/assets/data/img/flags/PG.png
similarity index 100%
rename from assets/img/flags/PG.png
rename to assets/data/img/flags/PG.png
diff --git a/assets/img/flags/PH.png b/assets/data/img/flags/PH.png
similarity index 100%
rename from assets/img/flags/PH.png
rename to assets/data/img/flags/PH.png
diff --git a/assets/img/flags/PK.png b/assets/data/img/flags/PK.png
similarity index 100%
rename from assets/img/flags/PK.png
rename to assets/data/img/flags/PK.png
diff --git a/assets/img/flags/PL.png b/assets/data/img/flags/PL.png
similarity index 100%
rename from assets/img/flags/PL.png
rename to assets/data/img/flags/PL.png
diff --git a/assets/img/flags/PN.png b/assets/data/img/flags/PN.png
similarity index 100%
rename from assets/img/flags/PN.png
rename to assets/data/img/flags/PN.png
diff --git a/assets/img/flags/PR.png b/assets/data/img/flags/PR.png
similarity index 100%
rename from assets/img/flags/PR.png
rename to assets/data/img/flags/PR.png
diff --git a/assets/img/flags/PS.png b/assets/data/img/flags/PS.png
similarity index 100%
rename from assets/img/flags/PS.png
rename to assets/data/img/flags/PS.png
diff --git a/assets/img/flags/PT.png b/assets/data/img/flags/PT.png
similarity index 100%
rename from assets/img/flags/PT.png
rename to assets/data/img/flags/PT.png
diff --git a/assets/img/flags/PW.png b/assets/data/img/flags/PW.png
similarity index 100%
rename from assets/img/flags/PW.png
rename to assets/data/img/flags/PW.png
diff --git a/assets/img/flags/PY.png b/assets/data/img/flags/PY.png
similarity index 100%
rename from assets/img/flags/PY.png
rename to assets/data/img/flags/PY.png
diff --git a/assets/img/flags/QA.png b/assets/data/img/flags/QA.png
similarity index 100%
rename from assets/img/flags/QA.png
rename to assets/data/img/flags/QA.png
diff --git a/assets/img/flags/RE.png b/assets/data/img/flags/RE.png
similarity index 100%
rename from assets/img/flags/RE.png
rename to assets/data/img/flags/RE.png
diff --git a/assets/img/flags/RO.png b/assets/data/img/flags/RO.png
similarity index 100%
rename from assets/img/flags/RO.png
rename to assets/data/img/flags/RO.png
diff --git a/assets/img/flags/RS.png b/assets/data/img/flags/RS.png
similarity index 100%
rename from assets/img/flags/RS.png
rename to assets/data/img/flags/RS.png
diff --git a/assets/img/flags/RU.png b/assets/data/img/flags/RU.png
similarity index 100%
rename from assets/img/flags/RU.png
rename to assets/data/img/flags/RU.png
diff --git a/assets/img/flags/RW.png b/assets/data/img/flags/RW.png
similarity index 100%
rename from assets/img/flags/RW.png
rename to assets/data/img/flags/RW.png
diff --git a/assets/img/flags/SA.png b/assets/data/img/flags/SA.png
similarity index 100%
rename from assets/img/flags/SA.png
rename to assets/data/img/flags/SA.png
diff --git a/assets/img/flags/SB.png b/assets/data/img/flags/SB.png
similarity index 100%
rename from assets/img/flags/SB.png
rename to assets/data/img/flags/SB.png
diff --git a/assets/img/flags/SC.png b/assets/data/img/flags/SC.png
similarity index 100%
rename from assets/img/flags/SC.png
rename to assets/data/img/flags/SC.png
diff --git a/assets/img/flags/SD.png b/assets/data/img/flags/SD.png
similarity index 100%
rename from assets/img/flags/SD.png
rename to assets/data/img/flags/SD.png
diff --git a/assets/img/flags/SE.png b/assets/data/img/flags/SE.png
similarity index 100%
rename from assets/img/flags/SE.png
rename to assets/data/img/flags/SE.png
diff --git a/assets/img/flags/SG.png b/assets/data/img/flags/SG.png
similarity index 100%
rename from assets/img/flags/SG.png
rename to assets/data/img/flags/SG.png
diff --git a/assets/img/flags/SH.png b/assets/data/img/flags/SH.png
similarity index 100%
rename from assets/img/flags/SH.png
rename to assets/data/img/flags/SH.png
diff --git a/assets/img/flags/SI.png b/assets/data/img/flags/SI.png
similarity index 100%
rename from assets/img/flags/SI.png
rename to assets/data/img/flags/SI.png
diff --git a/assets/img/flags/SK.png b/assets/data/img/flags/SK.png
similarity index 100%
rename from assets/img/flags/SK.png
rename to assets/data/img/flags/SK.png
diff --git a/assets/img/flags/SL.png b/assets/data/img/flags/SL.png
similarity index 100%
rename from assets/img/flags/SL.png
rename to assets/data/img/flags/SL.png
diff --git a/assets/img/flags/SM.png b/assets/data/img/flags/SM.png
similarity index 100%
rename from assets/img/flags/SM.png
rename to assets/data/img/flags/SM.png
diff --git a/assets/img/flags/SN.png b/assets/data/img/flags/SN.png
similarity index 100%
rename from assets/img/flags/SN.png
rename to assets/data/img/flags/SN.png
diff --git a/assets/img/flags/SO.png b/assets/data/img/flags/SO.png
similarity index 100%
rename from assets/img/flags/SO.png
rename to assets/data/img/flags/SO.png
diff --git a/assets/img/flags/SR.png b/assets/data/img/flags/SR.png
similarity index 100%
rename from assets/img/flags/SR.png
rename to assets/data/img/flags/SR.png
diff --git a/assets/img/flags/SS.png b/assets/data/img/flags/SS.png
similarity index 100%
rename from assets/img/flags/SS.png
rename to assets/data/img/flags/SS.png
diff --git a/assets/img/flags/ST.png b/assets/data/img/flags/ST.png
similarity index 100%
rename from assets/img/flags/ST.png
rename to assets/data/img/flags/ST.png
diff --git a/assets/img/flags/SV.png b/assets/data/img/flags/SV.png
similarity index 100%
rename from assets/img/flags/SV.png
rename to assets/data/img/flags/SV.png
diff --git a/assets/img/flags/SX.png b/assets/data/img/flags/SX.png
similarity index 100%
rename from assets/img/flags/SX.png
rename to assets/data/img/flags/SX.png
diff --git a/assets/img/flags/SY.png b/assets/data/img/flags/SY.png
similarity index 100%
rename from assets/img/flags/SY.png
rename to assets/data/img/flags/SY.png
diff --git a/assets/img/flags/SZ.png b/assets/data/img/flags/SZ.png
similarity index 100%
rename from assets/img/flags/SZ.png
rename to assets/data/img/flags/SZ.png
diff --git a/assets/img/flags/TC.png b/assets/data/img/flags/TC.png
similarity index 100%
rename from assets/img/flags/TC.png
rename to assets/data/img/flags/TC.png
diff --git a/assets/img/flags/TD.png b/assets/data/img/flags/TD.png
similarity index 100%
rename from assets/img/flags/TD.png
rename to assets/data/img/flags/TD.png
diff --git a/assets/img/flags/TF.png b/assets/data/img/flags/TF.png
similarity index 100%
rename from assets/img/flags/TF.png
rename to assets/data/img/flags/TF.png
diff --git a/assets/img/flags/TG.png b/assets/data/img/flags/TG.png
similarity index 100%
rename from assets/img/flags/TG.png
rename to assets/data/img/flags/TG.png
diff --git a/assets/img/flags/TH.png b/assets/data/img/flags/TH.png
similarity index 100%
rename from assets/img/flags/TH.png
rename to assets/data/img/flags/TH.png
diff --git a/assets/img/flags/TJ.png b/assets/data/img/flags/TJ.png
similarity index 100%
rename from assets/img/flags/TJ.png
rename to assets/data/img/flags/TJ.png
diff --git a/assets/img/flags/TK.png b/assets/data/img/flags/TK.png
similarity index 100%
rename from assets/img/flags/TK.png
rename to assets/data/img/flags/TK.png
diff --git a/assets/img/flags/TL.png b/assets/data/img/flags/TL.png
similarity index 100%
rename from assets/img/flags/TL.png
rename to assets/data/img/flags/TL.png
diff --git a/assets/img/flags/TM.png b/assets/data/img/flags/TM.png
similarity index 100%
rename from assets/img/flags/TM.png
rename to assets/data/img/flags/TM.png
diff --git a/assets/img/flags/TN.png b/assets/data/img/flags/TN.png
similarity index 100%
rename from assets/img/flags/TN.png
rename to assets/data/img/flags/TN.png
diff --git a/assets/img/flags/TO.png b/assets/data/img/flags/TO.png
similarity index 100%
rename from assets/img/flags/TO.png
rename to assets/data/img/flags/TO.png
diff --git a/assets/img/flags/TR.png b/assets/data/img/flags/TR.png
similarity index 100%
rename from assets/img/flags/TR.png
rename to assets/data/img/flags/TR.png
diff --git a/assets/img/flags/TT.png b/assets/data/img/flags/TT.png
similarity index 100%
rename from assets/img/flags/TT.png
rename to assets/data/img/flags/TT.png
diff --git a/assets/img/flags/TV.png b/assets/data/img/flags/TV.png
similarity index 100%
rename from assets/img/flags/TV.png
rename to assets/data/img/flags/TV.png
diff --git a/assets/img/flags/TW.png b/assets/data/img/flags/TW.png
similarity index 100%
rename from assets/img/flags/TW.png
rename to assets/data/img/flags/TW.png
diff --git a/assets/img/flags/TZ.png b/assets/data/img/flags/TZ.png
similarity index 100%
rename from assets/img/flags/TZ.png
rename to assets/data/img/flags/TZ.png
diff --git a/assets/img/flags/UA.png b/assets/data/img/flags/UA.png
similarity index 100%
rename from assets/img/flags/UA.png
rename to assets/data/img/flags/UA.png
diff --git a/assets/img/flags/UG.png b/assets/data/img/flags/UG.png
similarity index 100%
rename from assets/img/flags/UG.png
rename to assets/data/img/flags/UG.png
diff --git a/assets/img/flags/US.png b/assets/data/img/flags/US.png
similarity index 100%
rename from assets/img/flags/US.png
rename to assets/data/img/flags/US.png
diff --git a/assets/img/flags/UY.png b/assets/data/img/flags/UY.png
similarity index 100%
rename from assets/img/flags/UY.png
rename to assets/data/img/flags/UY.png
diff --git a/assets/img/flags/UZ.png b/assets/data/img/flags/UZ.png
similarity index 100%
rename from assets/img/flags/UZ.png
rename to assets/data/img/flags/UZ.png
diff --git a/assets/img/flags/VA.png b/assets/data/img/flags/VA.png
similarity index 100%
rename from assets/img/flags/VA.png
rename to assets/data/img/flags/VA.png
diff --git a/assets/img/flags/VC.png b/assets/data/img/flags/VC.png
similarity index 100%
rename from assets/img/flags/VC.png
rename to assets/data/img/flags/VC.png
diff --git a/assets/img/flags/VE.png b/assets/data/img/flags/VE.png
similarity index 100%
rename from assets/img/flags/VE.png
rename to assets/data/img/flags/VE.png
diff --git a/assets/img/flags/VG.png b/assets/data/img/flags/VG.png
similarity index 100%
rename from assets/img/flags/VG.png
rename to assets/data/img/flags/VG.png
diff --git a/assets/img/flags/VI.png b/assets/data/img/flags/VI.png
similarity index 100%
rename from assets/img/flags/VI.png
rename to assets/data/img/flags/VI.png
diff --git a/assets/img/flags/VN.png b/assets/data/img/flags/VN.png
similarity index 100%
rename from assets/img/flags/VN.png
rename to assets/data/img/flags/VN.png
diff --git a/assets/img/flags/VU.png b/assets/data/img/flags/VU.png
similarity index 100%
rename from assets/img/flags/VU.png
rename to assets/data/img/flags/VU.png
diff --git a/assets/img/flags/WF.png b/assets/data/img/flags/WF.png
similarity index 100%
rename from assets/img/flags/WF.png
rename to assets/data/img/flags/WF.png
diff --git a/assets/img/flags/WS.png b/assets/data/img/flags/WS.png
similarity index 100%
rename from assets/img/flags/WS.png
rename to assets/data/img/flags/WS.png
diff --git a/assets/img/flags/YE.png b/assets/data/img/flags/YE.png
similarity index 100%
rename from assets/img/flags/YE.png
rename to assets/data/img/flags/YE.png
diff --git a/assets/img/flags/YT.png b/assets/data/img/flags/YT.png
similarity index 100%
rename from assets/img/flags/YT.png
rename to assets/data/img/flags/YT.png
diff --git a/assets/img/flags/ZA.png b/assets/data/img/flags/ZA.png
similarity index 100%
rename from assets/img/flags/ZA.png
rename to assets/data/img/flags/ZA.png
diff --git a/assets/img/flags/ZM.png b/assets/data/img/flags/ZM.png
similarity index 100%
rename from assets/img/flags/ZM.png
rename to assets/data/img/flags/ZM.png
diff --git a/assets/img/flags/ZW.png b/assets/data/img/flags/ZW.png
similarity index 100%
rename from assets/img/flags/ZW.png
rename to assets/data/img/flags/ZW.png
diff --git a/assets/img/flags/_abkhazia.png b/assets/data/img/flags/_abkhazia.png
similarity index 100%
rename from assets/img/flags/_abkhazia.png
rename to assets/data/img/flags/_abkhazia.png
diff --git a/assets/img/flags/_basque-country.png b/assets/data/img/flags/_basque-country.png
similarity index 100%
rename from assets/img/flags/_basque-country.png
rename to assets/data/img/flags/_basque-country.png
diff --git a/assets/img/flags/_british-antarctic-territory.png b/assets/data/img/flags/_british-antarctic-territory.png
similarity index 100%
rename from assets/img/flags/_british-antarctic-territory.png
rename to assets/data/img/flags/_british-antarctic-territory.png
diff --git a/assets/img/flags/_commonwealth.png b/assets/data/img/flags/_commonwealth.png
similarity index 100%
rename from assets/img/flags/_commonwealth.png
rename to assets/data/img/flags/_commonwealth.png
diff --git a/assets/img/flags/_england.png b/assets/data/img/flags/_england.png
similarity index 100%
rename from assets/img/flags/_england.png
rename to assets/data/img/flags/_england.png
diff --git a/assets/img/flags/_gosquared.png b/assets/data/img/flags/_gosquared.png
similarity index 100%
rename from assets/img/flags/_gosquared.png
rename to assets/data/img/flags/_gosquared.png
diff --git a/assets/img/flags/_kosovo.png b/assets/data/img/flags/_kosovo.png
similarity index 100%
rename from assets/img/flags/_kosovo.png
rename to assets/data/img/flags/_kosovo.png
diff --git a/assets/img/flags/_mars.png b/assets/data/img/flags/_mars.png
similarity index 100%
rename from assets/img/flags/_mars.png
rename to assets/data/img/flags/_mars.png
diff --git a/assets/img/flags/_nagorno-karabakh.png b/assets/data/img/flags/_nagorno-karabakh.png
similarity index 100%
rename from assets/img/flags/_nagorno-karabakh.png
rename to assets/data/img/flags/_nagorno-karabakh.png
diff --git a/assets/img/flags/_nato.png b/assets/data/img/flags/_nato.png
similarity index 100%
rename from assets/img/flags/_nato.png
rename to assets/data/img/flags/_nato.png
diff --git a/assets/img/flags/_northern-cyprus.png b/assets/data/img/flags/_northern-cyprus.png
similarity index 100%
rename from assets/img/flags/_northern-cyprus.png
rename to assets/data/img/flags/_northern-cyprus.png
diff --git a/assets/img/flags/_olympics.png b/assets/data/img/flags/_olympics.png
similarity index 100%
rename from assets/img/flags/_olympics.png
rename to assets/data/img/flags/_olympics.png
diff --git a/assets/img/flags/_red-cross.png b/assets/data/img/flags/_red-cross.png
similarity index 100%
rename from assets/img/flags/_red-cross.png
rename to assets/data/img/flags/_red-cross.png
diff --git a/assets/img/flags/_scotland.png b/assets/data/img/flags/_scotland.png
similarity index 100%
rename from assets/img/flags/_scotland.png
rename to assets/data/img/flags/_scotland.png
diff --git a/assets/img/flags/_somaliland.png b/assets/data/img/flags/_somaliland.png
similarity index 100%
rename from assets/img/flags/_somaliland.png
rename to assets/data/img/flags/_somaliland.png
diff --git a/assets/img/flags/_south-ossetia.png b/assets/data/img/flags/_south-ossetia.png
similarity index 100%
rename from assets/img/flags/_south-ossetia.png
rename to assets/data/img/flags/_south-ossetia.png
diff --git a/assets/img/flags/_united-nations.png b/assets/data/img/flags/_united-nations.png
similarity index 100%
rename from assets/img/flags/_united-nations.png
rename to assets/data/img/flags/_united-nations.png
diff --git a/assets/img/flags/_unknown.png b/assets/data/img/flags/_unknown.png
similarity index 100%
rename from assets/img/flags/_unknown.png
rename to assets/data/img/flags/_unknown.png
diff --git a/assets/img/flags/_wales.png b/assets/data/img/flags/_wales.png
similarity index 100%
rename from assets/img/flags/_wales.png
rename to assets/data/img/flags/_wales.png
diff --git a/assets/img/linux.svg b/assets/data/img/linux.svg
similarity index 100%
rename from assets/img/linux.svg
rename to assets/data/img/linux.svg
diff --git a/assets/img/mac.svg b/assets/data/img/mac.svg
similarity index 100%
rename from assets/img/mac.svg
rename to assets/data/img/mac.svg
diff --git a/assets/img/plants1-br.png b/assets/data/img/plants1-br.png
similarity index 100%
rename from assets/img/plants1-br.png
rename to assets/data/img/plants1-br.png
diff --git a/assets/img/plants1.png b/assets/data/img/plants1.png
similarity index 100%
rename from assets/img/plants1.png
rename to assets/data/img/plants1.png
diff --git a/assets/img/spn-feature-carousel/access-regional-content-easily.png b/assets/data/img/spn-feature-carousel/access-regional-content-easily.png
similarity index 100%
rename from assets/img/spn-feature-carousel/access-regional-content-easily.png
rename to assets/data/img/spn-feature-carousel/access-regional-content-easily.png
diff --git a/assets/img/spn-feature-carousel/built-from-the-ground-up.png b/assets/data/img/spn-feature-carousel/built-from-the-ground-up.png
similarity index 100%
rename from assets/img/spn-feature-carousel/built-from-the-ground-up.png
rename to assets/data/img/spn-feature-carousel/built-from-the-ground-up.png
diff --git a/assets/img/spn-feature-carousel/bye-bye-vpns.png b/assets/data/img/spn-feature-carousel/bye-bye-vpns.png
similarity index 100%
rename from assets/img/spn-feature-carousel/bye-bye-vpns.png
rename to assets/data/img/spn-feature-carousel/bye-bye-vpns.png
diff --git a/assets/img/spn-feature-carousel/easily-control-your-privacy.png b/assets/data/img/spn-feature-carousel/easily-control-your-privacy.png
similarity index 100%
rename from assets/img/spn-feature-carousel/easily-control-your-privacy.png
rename to assets/data/img/spn-feature-carousel/easily-control-your-privacy.png
diff --git a/assets/img/spn-feature-carousel/multiple-identities-for-each-app.png b/assets/data/img/spn-feature-carousel/multiple-identities-for-each-app.png
similarity index 100%
rename from assets/img/spn-feature-carousel/multiple-identities-for-each-app.png
rename to assets/data/img/spn-feature-carousel/multiple-identities-for-each-app.png
diff --git a/assets/img/spn-login.png b/assets/data/img/spn-login.png
similarity index 100%
rename from assets/img/spn-login.png
rename to assets/data/img/spn-login.png
diff --git a/assets/img/windows.svg b/assets/data/img/windows.svg
similarity index 100%
rename from assets/img/windows.svg
rename to assets/data/img/windows.svg
diff --git a/assets/world-50m.json b/assets/data/world-50m.json
similarity index 100%
rename from assets/world-50m.json
rename to assets/data/world-50m.json
diff --git a/assets/icons.go b/assets/icons.go
new file mode 100644
index 00000000..7ffa780d
--- /dev/null
+++ b/assets/icons.go
@@ -0,0 +1,8 @@
+package assets
+
+import (
+	_ "embed"
+)
+
+//go:embed data/icons/pm_light_512.png
+var PNG []byte
diff --git a/assets/icons_default.go b/assets/icons_default.go
new file mode 100644
index 00000000..2c1b6eb1
--- /dev/null
+++ b/assets/icons_default.go
@@ -0,0 +1,102 @@
+//go:build !windows
+
+package assets
+
+import (
+	"bytes"
+	_ "embed"
+	"fmt"
+	"image"
+	"image/png"
+
+	"github.com/safing/portbase/log"
+	"golang.org/x/image/draw"
+)
+
+// Colored Icon IDs.
+const (
+	GreenID  = 0
+	YellowID = 1
+	RedID    = 2
+	BlueID   = 3
+)
+
+// Icons.
+var (
+	//go:embed data/icons/pm_light_green_512.png
+	GreenPNG []byte
+
+	//go:embed data/icons/pm_light_yellow_512.png
+	YellowPNG []byte
+
+	//go:embed data/icons/pm_light_red_512.png
+	RedPNG []byte
+
+	//go:embed data/icons/pm_light_blue_512.png
+	BluePNG []byte
+
+	// ColoredIcons holds all the icons as .PNGs
+	ColoredIcons [4][]byte
+)
+
+func init() {
+	setColoredIcons()
+}
+
+func setColoredIcons() {
+	ColoredIcons = [4][]byte{
+		GreenID:  GreenPNG,
+		YellowID: YellowPNG,
+		RedID:    RedPNG,
+		BlueID:   BluePNG,
+	}
+}
+
+// ScaleColoredIconsTo scales all colored icons to the given size.
+// It must be called before any colored icons are used.
+// It does nothing on Windows.
+func ScaleColoredIconsTo(pixelSize int) {
+	// Scale colored icons only.
+	GreenPNG = quickScalePNG(GreenPNG, pixelSize)
+	YellowPNG = quickScalePNG(YellowPNG, pixelSize)
+	RedPNG = quickScalePNG(RedPNG, pixelSize)
+	BluePNG = quickScalePNG(BluePNG, pixelSize)
+
+	// Repopulate colored icons.
+	setColoredIcons()
+}
+
+func quickScalePNG(imgData []byte, pixelSize int) []byte {
+	scaledImage, err := scalePNGTo(imgData, pixelSize)
+	if err != nil {
+		log.Warningf("failed to scale image (using original): %s", err)
+		return imgData
+	}
+	return scaledImage
+}
+
+func scalePNGTo(imgData []byte, pixelSize int) ([]byte, error) {
+	img, err := png.Decode(bytes.NewReader(imgData))
+	if err != nil {
+		return nil, fmt.Errorf("failed to decode image: %w", err)
+	}
+
+	// Return data unprocessed if image already has the correct size.
+	if img.Bounds().Dx() == pixelSize {
+		return imgData, nil
+	}
+
+	// Scale image to given size.
+	rectangle := image.Rect(0, 0, pixelSize, pixelSize)
+	scaledImage := image.NewRGBA(rectangle)
+	draw.CatmullRom.Scale(scaledImage, rectangle, img, img.Bounds(), draw.Over, nil)
+
+	// Encode scaled image.
+	scaledImgBuffer := new(bytes.Buffer)
+	err = png.Encode(scaledImgBuffer, scaledImage)
+	if err != nil {
+		return nil, fmt.Errorf("failed to encode image: %w", err)
+	}
+
+	return scaledImgBuffer.Bytes(), nil
+}
diff --git a/assets/icons_windows.go b/assets/icons_windows.go
new file mode 100644
index 00000000..83f5db2e
--- /dev/null
+++ b/assets/icons_windows.go
@@ -0,0 +1,41 @@
+package assets
+
+import (
+	_ "embed"
+)
+
+// Colored Icon IDs.
+const (
+	GreenID  = 0
+	YellowID = 1
+	RedID    = 2
+	BlueID   = 3
+)
+
+// Icons.
+var (
+	//go:embed data/icons/pm_light_green_512.ico
+	GreenICO []byte
+
+	//go:embed data/icons/pm_light_yellow_512.ico
+	YellowICO []byte
+
+	//go:embed data/icons/pm_light_red_512.ico
+	RedICO []byte
+
+	//go:embed data/icons/pm_light_blue_512.ico
+	BlueICO []byte
+
+	// ColoredIcons holds all the icons as .ICOs
+	ColoredIcons = [4][]byte{
+		GreenID:  GreenICO,
+		YellowID: YellowICO,
+		RedID:    RedICO,
+		BlueID:   BlueICO,
+	}
+)
+
+// ScaleColoredIconsTo scales all colored icons to the given size.
+// It must be called before any colored icons are used.
+// It does nothing on Windows.
+func ScaleColoredIconsTo(pixelSize int) {}
diff --git a/cmds/notifier/.gitignore b/cmds/notifier/.gitignore
new file mode 100644
index 00000000..602ad23c
--- /dev/null
+++ b/cmds/notifier/.gitignore
@@ -0,0 +1,34 @@
+# Compiled binaries
+notifier
+notifier.exe
+
+# Go vendor
+vendor
+
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof
+
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
diff --git a/cmds/notifier/README.md b/cmds/notifier/README.md
new file mode 100644
index 00000000..bdfcece8
--- /dev/null
+++ b/cmds/notifier/README.md
@@ -0,0 +1,5 @@
+### Development Dependencies
+
+sudo apt install libgtk-3-dev libayatana-appindicator3-dev libwebkitgtk-3.0-dev libgl1-mesa-dev libglu1-mesa-dev libnotify-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
+
+sudo pacman -S libappindicator-gtk3 
diff --git a/cmds/notifier/http_api.go b/cmds/notifier/http_api.go
new file mode 100644
index 00000000..bb0eebf3
--- /dev/null
+++ b/cmds/notifier/http_api.go
@@ -0,0 +1,65 @@
+package main
+
+import (
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"net/http/cookiejar"
+	"strings"
+	"time"
+
+	"github.com/safing/portbase/log"
+)
+
+const (
+	apiBaseURL          = "http://127.0.0.1:817/api/v1/"
+	apiShutdownEndpoint = "core/shutdown"
+)
+
+var (
+	httpApiClient *http.Client
+)
+
+func init() {
+	// Make cookie jar.
+	jar, err := cookiejar.New(nil)
+	if err != nil {
+		log.Warningf("http-api: failed to create cookie jar: %s", err)
+		jar = nil
+	}
+
+	// Create client.
+	httpApiClient = &http.Client{
+		Jar:     jar,
+		Timeout: 3 * time.Second,
+	}
+}
+
+func httpApiAction(endpoint string) (response string, err error) {
+	// Make action request.
+	resp, err := httpApiClient.Post(apiBaseURL+endpoint, "", nil)
+	if err != nil {
+		return "", fmt.Errorf("request failed: %w", err)
+	}
+
+	// Read the response body.
+	defer resp.Body.Close()
+	respData, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return "", fmt.Errorf("failed to read data: %w", err)
+	}
+	response = strings.TrimSpace(string(respData))
+
+	// Check if the request was successful on the server.
+	if resp.StatusCode >= 200 && resp.StatusCode < 300 {
+		return response, fmt.Errorf("server failed with %s: %s", resp.Status, response)
+	}
+
+	return response, nil
+}
+
+// TriggerShutdown triggers a shutdown via the APi.
+func TriggerShutdown() error {
+	_, err := httpApiAction(apiShutdownEndpoint)
+	return err
+}
diff --git a/cmds/notifier/icons.go b/cmds/notifier/icons.go
new file mode 100644
index 00000000..93b3db74
--- /dev/null
+++ b/cmds/notifier/icons.go
@@ -0,0 +1,25 @@
+package main
+
+import (
+	"os"
+	"path/filepath"
+	"sync"
+
+	icons "github.com/safing/portmaster/assets"
+)
+
+var (
+	appIconEnsureOnce sync.Once
+	appIconPath       string
+)
+
+func ensureAppIcon() (location string, err error) {
+	appIconEnsureOnce.Do(func() {
+		if appIconPath == "" {
+			appIconPath = filepath.Join(dataDir, "exec", "portmaster.png")
+		}
+		err = os.WriteFile(appIconPath, icons.PNG, 0o0644)
+	})
+
+	return appIconPath, err
+}
diff --git a/cmds/notifier/main.go b/cmds/notifier/main.go
new file mode 100644
index 00000000..8bd95b3b
--- /dev/null
+++ b/cmds/notifier/main.go
@@ -0,0 +1,286 @@
+package main
+
+import (
+	"context"
+	"errors"
+	"flag"
+	"fmt"
+	"os"
+	"os/signal"
+	"path/filepath"
+	"runtime"
+	"runtime/pprof"
+	"strings"
+	"sync"
+	"syscall"
+	"time"
+
+	"github.com/tevino/abool"
+
+	"github.com/safing/portbase/api/client"
+	"github.com/safing/portbase/dataroot"
+	"github.com/safing/portbase/info"
+	"github.com/safing/portbase/log"
+	"github.com/safing/portbase/modules"
+	"github.com/safing/portbase/updater"
+	"github.com/safing/portbase/utils"
+	"github.com/safing/portmaster/service/updates/helper"
+)
+
+var (
+	dataDir          string
+	printStackOnExit bool
+	showVersion      bool
+
+	apiClient    = client.NewClient("127.0.0.1:817")
+	connected    = abool.New()
+	shuttingDown = abool.New()
+	restarting   = abool.New()
+
+	mainCtx, cancelMainCtx = context.WithCancel(context.Background())
+	mainWg                 = &sync.WaitGroup{}
+
+	dataRoot *utils.DirStructure
+	// Create registry.
+	registry = &updater.ResourceRegistry{
+		Name: "updates",
+		UpdateURLs: []string{
+			"https://updates.safing.io",
+		},
+		DevMode: false,
+		Online:  false, // disable download of resources (this is job for the core).
+	}
+)
+
+func init() {
+	flag.StringVar(&dataDir, "data", "", "set data directory")
+	flag.BoolVar(&printStackOnExit, "print-stack-on-exit", false, "prints the stack before of shutting down")
+	flag.BoolVar(&showVersion, "version", false, "show version and exit")
+
+	runtime.GOMAXPROCS(2)
+}
+
+func main() {
+	// parse flags
+	flag.Parse()
+
+	// set meta info
+	info.Set("Portmaster Notifier", "0.3.6", "GPLv3", false)
+
+	// check if meta info is ok
+	err := info.CheckVersion()
+	if err != nil {
+		fmt.Println("compile error: please compile using the provided build script")
+		os.Exit(1)
+	}
+
+	// print help
+	if modules.HelpFlag {
+		flag.Usage()
+		os.Exit(0)
+	}
+
+	if showVersion {
+		fmt.Println(info.FullVersion())
+		os.Exit(0)
+	}
+
+	// auto detect
+	if dataDir == "" {
+		dataDir = detectDataDir()
+	}
+
+	// check data dir
+	if dataDir == "" {
+		fmt.Fprintln(os.Stderr, "please set the data directory using --data=/path/to/data/dir")
+		os.Exit(1)
+	}
+
+	// switch to safe exec dir
+	err = os.Chdir(filepath.Join(dataDir, "exec"))
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "warning: failed to switch to safe exec dir: %s\n", err)
+	}
+
+	// start log writer
+	err = log.Start()
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "failed to start logging: %s\n", err)
+		os.Exit(1)
+	}
+
+	// load registry
+	err = configureRegistry(true)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "failed to load registry: %s\n", err)
+		os.Exit(1)
+	}
+
+	// connect to API
+	go apiClient.StayConnected()
+	go apiStatusMonitor()
+
+	// start subsystems
+	go tray()
+	go subsystemsClient()
+	go spnStatusClient()
+	go notifClient()
+	go startShutdownEventListener()
+
+	// Shutdown
+	// catch interrupt for clean shutdown
+	signalCh := make(chan os.Signal, 1)
+	signal.Notify(
+		signalCh,
+		os.Interrupt,
+		syscall.SIGHUP,
+		syscall.SIGINT,
+		syscall.SIGTERM,
+		syscall.SIGQUIT,
+	)
+
+	// wait for shutdown
+	select {
+	case <-signalCh:
+		fmt.Println(" <INTERRUPT>")
+		log.Warning("program was interrupted, shutting down")
+	case <-mainCtx.Done():
+		log.Warning("program is shutting down")
+	}
+
+	if printStackOnExit {
+		fmt.Println("=== PRINTING STACK ===")
+		_ = pprof.Lookup("goroutine").WriteTo(os.Stdout, 2)
+		fmt.Println("=== END STACK ===")
+	}
+	go func() {
+		time.Sleep(10 * time.Second)
+		fmt.Println("===== TAKING TOO LONG FOR SHUTDOWN - PRINTING STACK TRACES =====")
+		_ = pprof.Lookup("goroutine").WriteTo(os.Stdout, 2)
+		os.Exit(1)
+	}()
+
+	// clear all notifications
+	clearNotifications()
+
+	// shutdown
+	cancelMainCtx()
+	mainWg.Wait()
+
+	apiClient.Shutdown()
+	exitTray()
+	log.Shutdown()
+
+	os.Exit(0)
+}
+
+func apiStatusMonitor() {
+	for {
+		// Wait for connection.
+		<-apiClient.Online()
+		connected.Set()
+		triggerTrayUpdate()
+
+		// Wait for lost connection.
+		<-apiClient.Offline()
+		connected.UnSet()
+		triggerTrayUpdate()
+	}
+}
+
+func detectDataDir() string {
+	// get path of executable
+	binPath, err := os.Executable()
+	if err != nil {
+		return ""
+	}
+	// get directory
+	binDir := filepath.Dir(binPath)
+	// check if we in the updates directory
+	identifierDir := filepath.Join("updates", runtime.GOOS+"_"+runtime.GOARCH, "notifier")
+	// check if there is a match and return data dir
+	if strings.HasSuffix(binDir, identifierDir) {
+		return filepath.Clean(strings.TrimSuffix(binDir, identifierDir))
+	}
+	return ""
+}
+
+func configureRegistry(mustLoadIndex bool) error {
+	// If dataDir is not set, check the environment variable.
+	if dataDir == "" {
+		dataDir = os.Getenv("PORTMASTER_DATA")
+	}
+
+	// If it's still empty, try to auto-detect it.
+	if dataDir == "" {
+		dataDir = detectInstallationDir()
+	}
+
+	// Finally, if it's still empty, the user must provide it.
+	if dataDir == "" {
+		return errors.New("please set the data directory using --data=/path/to/data/dir")
+	}
+
+	// Remove left over quotes.
+	dataDir = strings.Trim(dataDir, `\"`)
+	// Initialize data root.
+	err := dataroot.Initialize(dataDir, 0o0755)
+	if err != nil {
+		return fmt.Errorf("failed to initialize data root: %w", err)
+	}
+	dataRoot = dataroot.Root()
+
+	// Initialize registry.
+	err = registry.Initialize(dataRoot.ChildDir("updates", 0o0755))
+	if err != nil {
+		return err
+	}
+
+	return updateRegistryIndex(mustLoadIndex)
+}
+
+func detectInstallationDir() string {
+	exePath, err := filepath.Abs(os.Args[0])
+	if err != nil {
+		return ""
+	}
+
+	parent := filepath.Dir(exePath)                                    // parent should be "...\updates\windows_amd64\notifier"
+	stableJSONFile := filepath.Join(parent, "..", "..", "stable.json") // "...\updates\stable.json"
+	stat, err := os.Stat(stableJSONFile)
+	if err != nil {
+		return ""
+	}
+
+	if stat.IsDir() {
+		return ""
+	}
+
+	return parent
+}
+
+func updateRegistryIndex(mustLoadIndex bool) error {
+	// Set indexes based on the release channel.
+	warning := helper.SetIndexes(registry, "", false, false, false)
+	if warning != nil {
+		log.Warningf("%q", warning)
+	}
+
+	// Load indexes from disk or network, if needed and desired.
+	err := registry.LoadIndexes(context.Background())
+	if err != nil {
+		log.Warningf("error loading indexes %q", warning)
+		if mustLoadIndex {
+			return err
+		}
+	}
+
+	// Load versions from disk to know which others we have and which are available.
+	err = registry.ScanStorage("")
+	if err != nil {
+		log.Warningf("error during storage scan: %q\n", err)
+	}
+
+	registry.SelectVersions()
+	return nil
+}
diff --git a/cmds/notifier/notification.go b/cmds/notifier/notification.go
new file mode 100644
index 00000000..fc37690b
--- /dev/null
+++ b/cmds/notifier/notification.go
@@ -0,0 +1,36 @@
+package main
+
+import (
+	"fmt"
+
+	pbnotify "github.com/safing/portbase/notifications"
+)
+
+// Notification represents a notification that is to be delivered to the user.
+type Notification struct {
+	pbnotify.Notification
+
+	// systemID holds the ID returned by the dbus interface on Linux or by WinToast library on Windows.
+	systemID NotificationID
+}
+
+// IsSupported returns whether the action is supported on this system.
+func IsSupportedAction(a pbnotify.Action) bool {
+	switch a.Type {
+	case pbnotify.ActionTypeNone:
+		return true
+	default:
+		return false
+	}
+}
+
+// SelectAction sends an action back to the portmaster.
+func (n *Notification) SelectAction(action string) {
+	new := &pbnotify.Notification{
+		EventID:          n.EventID,
+		SelectedActionID: action,
+	}
+
+	// FIXME: check response
+	apiClient.Update(fmt.Sprintf("%s%s", dbNotifBasePath, new.EventID), new, nil)
+}
diff --git a/cmds/notifier/notify.go b/cmds/notifier/notify.go
new file mode 100644
index 00000000..1b271b67
--- /dev/null
+++ b/cmds/notifier/notify.go
@@ -0,0 +1,103 @@
+package main
+
+import (
+	"fmt"
+	"strings"
+	"sync"
+	"time"
+
+	"github.com/safing/portbase/api/client"
+	"github.com/safing/portbase/formats/dsd"
+	"github.com/safing/portbase/log"
+
+	pbnotify "github.com/safing/portbase/notifications"
+)
+
+const (
+	dbNotifBasePath = "notifications:all/"
+)
+
+var (
+	notifications     = make(map[string]*Notification)
+	notificationsLock sync.Mutex
+)
+
+func notifClient() {
+	notifOp := apiClient.Qsub(fmt.Sprintf("query %s where ShowOnSystem is true", dbNotifBasePath), handleNotification)
+	notifOp.EnableResuscitation()
+
+	// start the action listener and block
+	// until it's closed.
+	actionListener()
+}
+
+func handleNotification(m *client.Message) {
+	notificationsLock.Lock()
+	defer notificationsLock.Unlock()
+
+	log.Tracef("received %s msg: %s", m.Type, m.Key)
+
+	switch m.Type {
+	case client.MsgError:
+	case client.MsgDone:
+	case client.MsgSuccess:
+	case client.MsgOk, client.MsgUpdate, client.MsgNew:
+
+		n := &Notification{}
+		_, err := dsd.Load(m.RawValue, &n.Notification)
+		if err != nil {
+			log.Warningf("notify: failed to parse new notification: %s", err)
+			return
+		}
+
+		// copy existing system values
+		existing, ok := notifications[n.EventID]
+		if ok {
+			existing.Lock()
+			n.systemID = existing.systemID
+			existing.Unlock()
+		}
+
+		// save
+		notifications[n.EventID] = n
+
+		// Handle notification.
+		switch {
+		case existing != nil:
+			// Cancel existing notification if not active, else ignore.
+			if n.State != pbnotify.Active {
+				existing.Cancel()
+			}
+			return
+		case n.State == pbnotify.Active:
+			// Show new notifications that are active.
+			n.Show()
+		default:
+			// Ignore new notifications that are not active.
+		}
+
+	case client.MsgDelete:
+
+		n, ok := notifications[strings.TrimPrefix(m.Key, dbNotifBasePath)]
+		if ok {
+			n.Cancel()
+			delete(notifications, n.EventID)
+		}
+
+	case client.MsgWarning:
+	case client.MsgOffline:
+	}
+}
+
+func clearNotifications() {
+	notificationsLock.Lock()
+	defer notificationsLock.Unlock()
+
+	for _, n := range notifications {
+		n.Cancel()
+	}
+
+	// Wait for goroutines that cancel notifications.
+	// TODO: Revamp to use a waitgroup.
+	time.Sleep(1 * time.Second)
+}
diff --git a/cmds/notifier/notify_linux.go b/cmds/notifier/notify_linux.go
new file mode 100644
index 00000000..bcf650cf
--- /dev/null
+++ b/cmds/notifier/notify_linux.go
@@ -0,0 +1,154 @@
+package main
+
+import (
+	"context"
+	"sync"
+
+	notify "github.com/dhaavi/go-notify"
+	"github.com/safing/portbase/log"
+)
+
+type NotificationID uint32
+
+var (
+	capabilities notify.Capabilities
+	notifsByID   sync.Map
+)
+
+func init() {
+	var err error
+	capabilities, err = notify.GetCapabilities()
+	if err != nil {
+		log.Errorf("failed to get notification system capabilities: %s", err)
+	}
+}
+
+func handleActions(ctx context.Context, actions chan notify.Signal) {
+	mainWg.Add(1)
+	defer mainWg.Done()
+
+listenForNotifications:
+	for {
+		select {
+		case <-ctx.Done():
+			return
+		case sig := <-actions:
+			if sig.Name != "org.freedesktop.Notifications.ActionInvoked" {
+				// we don't care for anything else (dismissed, closed)
+				continue listenForNotifications
+			}
+
+			// get notification by system ID
+			n, ok := notifsByID.LoadAndDelete(NotificationID(sig.ID))
+
+			if !ok {
+				continue listenForNotifications
+			}
+
+			notification := n.(*Notification)
+
+			log.Tracef("notify: received signal: %+v", sig)
+			if sig.ActionKey != "" {
+				// send action
+				if ok {
+					notification.Lock()
+					notification.SelectAction(sig.ActionKey)
+					notification.Unlock()
+				}
+			} else {
+				log.Tracef("notify: notification clicked: %+v", sig)
+				// Global action invoked, start the app
+				launchApp()
+			}
+		}
+	}
+
+}
+
+func actionListener() {
+	actions := make(chan notify.Signal, 100)
+
+	go handleActions(mainCtx, actions)
+
+	err := notify.SignalNotify(mainCtx, actions)
+	if err != nil && err != context.Canceled {
+		log.Errorf("notify: signal listener failed: %s", err)
+	}
+}
+
+// Show shows the notification.
+func (n *Notification) Show() {
+	sysN := notify.NewNotification("Portmaster", n.Message)
+	// see https://developer.gnome.org/notification-spec/
+
+	// The optional name of the application sending the notification.
+	// Can be blank.
+	sysN.AppName = "Portmaster"
+
+	// The optional notification ID that this notification replaces.
+	sysN.ReplacesID = uint32(n.systemID)
+
+	// The optional program icon of the calling application.
+	// sysN.AppIcon string
+
+	// The summary text briefly describing the notification.
+	// Summary string (arg 1)
+
+	// The optional detailed body text.
+	// Body string (arg 2)
+
+	// The actions send a request message back to the notification client
+	// when invoked.
+	// sysN.Actions []string
+	if capabilities.Actions {
+		sysN.Actions = make([]string, 0, len(n.AvailableActions)*2)
+		for _, action := range n.AvailableActions {
+			if IsSupportedAction(*action) {
+				sysN.Actions = append(sysN.Actions, action.ID)
+				sysN.Actions = append(sysN.Actions, action.Text)
+			}
+		}
+	}
+
+	// Set Portmaster icon.
+	iconLocation, err := ensureAppIcon()
+	if err != nil {
+		log.Warningf("notify: failed to write icon: %s", err)
+	}
+	sysN.AppIcon = iconLocation
+
+	// TODO: Use hints to display icon of affected app.
+	// Hints are a way to provide extra data to a notification server.
+	// sysN.Hints = make(map[string]interface{})
+
+	// The timeout time in milliseconds since the display of the
+	// notification at which the notification should automatically close.
+	// sysN.Timeout int32
+
+	newID, err := sysN.Show()
+	if err != nil {
+		log.Warningf("notify: failed to show notification %s", n.EventID)
+		return
+	}
+
+	notifsByID.Store(NotificationID(newID), n)
+
+	n.Lock()
+	defer n.Unlock()
+	n.systemID = NotificationID(newID)
+}
+
+// Cancel cancels the notification.
+func (n *Notification) Cancel() {
+	n.Lock()
+	defer n.Unlock()
+
+	// TODO: could a ID of 0 be valid?
+	if n.systemID != 0 {
+		err := notify.CloseNotification(uint32(n.systemID))
+		if err != nil {
+			log.Warningf("notify: failed to close notification %s/%d", n.EventID, n.systemID)
+		}
+		notifsByID.Delete(n.systemID)
+	}
+}
diff --git a/cmds/notifier/notify_windows.go b/cmds/notifier/notify_windows.go
new file mode 100644
index 00000000..abb56be0
--- /dev/null
+++ b/cmds/notifier/notify_windows.go
@@ -0,0 +1,184 @@
+package main
+
+import (
+	"fmt"
+	"sync"
+
+	"github.com/safing/portbase/log"
+	"github.com/safing/portmaster/cmds/notifier/wintoast"
+	"github.com/safing/portmaster/service/updates/helper"
+)
+
+type NotificationID int64
+
+const (
+	appName              = "Portmaster"
+	appUserModelID       = "io.safing.portmaster.2"
+	originalShortcutPath = "C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Portmaster\\Portmaster.lnk"
+)
+
+const (
+	SoundDefault = 0
+	SoundSilent  = 1
+	SoundLoop    = 2
+)
+
+const (
+	SoundPathDefault = 0
+	// see notification_glue.h if you need more types
+)
+
+var (
+	initOnce           sync.Once
+	lib                *wintoast.WinToast
+	notificationsByIDs sync.Map
+)
+
+func getLib() *wintoast.WinToast {
+	initOnce.Do(func() {
+		dllPath, err := getDllPath()
+		if err != nil {
+			log.Errorf("notify: failed to get dll path: %s", err)
+			return
+		}
+		// Load dll and all the functions
+		newLib, err := wintoast.New(dllPath)
+		if err != nil {
+			log.Errorf("notify: failed to load library: %s", err)
+			return
+		}
+
+		// Initialize. This will create or update application shortcut. C:\Users\<user>\AppData\Roaming\Microsoft\Windows\Start Menu\Programs
+		// and it will be of the originalShortcutPath with no CLSID and different AUMI
+		err = newLib.Initialize(appName, appUserModelID, originalShortcutPath)
+		if err != nil {
+			log.Errorf("notify: failed to load library: %s", err)
+			return
+		}
+
+		// library was initialized successfully
+		lib = newLib
+
+		// Set callbacks
+
+		err = lib.SetCallbacks(notificationActivatedCallback, notificationDismissedCallback, notificationDismissedCallback)
+		if err != nil {
+			log.Warningf("notify: failed to set callbacks: %s", err)
+			return
+		}
+	})
+
+	return lib
+}
+
+// Show shows the notification.
+func (n *Notification) Show() {
+	// Lock notification
+	n.Lock()
+	defer n.Unlock()
+
+	// Create new notification object
+	builder, err := getLib().NewNotification(n.Title, n.Message)
+	if err != nil {
+		log.Errorf("notify: failed to create notification: %s", err)
+		return
+	}
+	// Make sure memory is freed when done
+	defer builder.Delete()
+
+	// if needed set notification icon
+	// _ = builder.SetImage(iconLocation)
+
+	// Leaving the default value for the sound
+	// _ = builder.SetSound(SoundDefault, SoundPathDefault)
+
+	// Set all the required actions.
+	for _, action := range n.AvailableActions {
+		err = builder.AddButton(action.Text)
+		if err != nil {
+			log.Warningf("notify: failed to add button: %s", err)
+		}
+	}
+
+	// Show notification.
+	id, err := builder.Show()
+	if err != nil {
+		log.Errorf("notify: failed to show notification: %s", err)
+		return
+	}
+	n.systemID = NotificationID(id)
+
+	// Link system id to the notification object
+	notificationsByIDs.Store(NotificationID(id), n)
+
+	log.Debugf("notify: showing notification %q: %d", n.Title, n.systemID)
+}
+
+// Cancel cancels the notification.
+func (n *Notification) Cancel() {
+	// Lock notification
+	n.Lock()
+	defer n.Unlock()
+
+	// No need to check for errors. If it fails it is probably already dismissed
+	_ = getLib().HideNotification(int64(n.systemID))
+
+	notificationsByIDs.Delete(n.systemID)
+	log.Debugf("notify: notification canceled %q: %d", n.Title, n.systemID)
+}
+
+func notificationActivatedCallback(id int64, actionIndex int32) {
+	if actionIndex == -1 {
+		// The user clicked on the notification (not a button), open the portmaster and delete
+		launchApp()
+		notificationsByIDs.Delete(NotificationID(id))
+		log.Debugf("notify: notification clicked %d", id)
+		return
+	}
+
+	// The user click one of the buttons
+
+	// Get notified object
+	n, ok := notificationsByIDs.LoadAndDelete(NotificationID(id))
+	if !ok {
+		return
+	}
+
+	notification := n.(*Notification)
+
+	notification.Lock()
+	defer notification.Unlock()
+
+	// Set selected action
+	actionID := notification.AvailableActions[actionIndex].ID
+	notification.SelectAction(actionID)
+
+	log.Debugf("notify: notification button cliecked %d button id: %d", id, actionIndex)
+}
+
+func notificationDismissedCallback(id int64, reason int32) {
+	// Failure or user dismissed the notification
+	if reason == 0 {
+		notificationsByIDs.Delete(NotificationID(id))
+		log.Debugf("notify: notification dissmissed %d", id)
+	}
+}
+
+func getDllPath() (string, error) {
+	if dataDir == "" {
+		return "", fmt.Errorf("dataDir is empty")
+	}
+
+	// Aks the registry for the dll path
+	identifier := helper.PlatformIdentifier("notifier/portmaster-wintoast.dll")
+	file, err := registry.GetFile(identifier)
+	if err != nil {
+		return "", err
+	}
+	return file.Path(), nil
+}
+
+func actionListener() {
+	// initialize the library
+	_ = getLib()
+}
diff --git a/cmds/notifier/shutdown.go b/cmds/notifier/shutdown.go
new file mode 100644
index 00000000..f943938d
--- /dev/null
+++ b/cmds/notifier/shutdown.go
@@ -0,0 +1,50 @@
+package main
+
+import (
+	"github.com/safing/portbase/api/client"
+	"github.com/safing/portbase/log"
+)
+
+func startShutdownEventListener() {
+	shutdownNotifOp := apiClient.Sub("query runtime:modules/core/event/shutdown", handleShutdownEvent)
+	shutdownNotifOp.EnableResuscitation()
+
+	restartNotifOp := apiClient.Sub("query runtime:modules/core/event/restart", handleRestartEvent)
+	restartNotifOp.EnableResuscitation()
+}
+
+func handleShutdownEvent(m *client.Message) {
+	switch m.Type {
+	case client.MsgOk, client.MsgUpdate, client.MsgNew:
+		shuttingDown.Set()
+		triggerTrayUpdate()
+
+		log.Warningf("shutdown: received shutdown event, shutting down now")
+
+		// wait for the API client connection to die
+		<-apiClient.Offline()
+		shuttingDown.UnSet()
+
+		cancelMainCtx()
+
+	case client.MsgWarning, client.MsgError:
+		log.Errorf("shutdown: event subscription error: %s", string(m.RawValue))
+	}
+}
+
+func handleRestartEvent(m *client.Message) {
+	switch m.Type {
+	case client.MsgOk, client.MsgUpdate, client.MsgNew:
+		restarting.Set()
+		triggerTrayUpdate()
+
+		log.Warningf("restart: received restart event")
+
+		// wait for the API client connection to die
+		<-apiClient.Offline()
+		restarting.UnSet()
+		triggerTrayUpdate()
+	case client.MsgWarning, client.MsgError:
+		log.Errorf("shutdown: event subscription error: %s", string(m.RawValue))
+	}
+}
diff --git a/cmds/notifier/snoretoast-guid.patch b/cmds/notifier/snoretoast-guid.patch
new file mode 100644
index 00000000..1a050e5f
--- /dev/null
+++ b/cmds/notifier/snoretoast-guid.patch
@@ -0,0 +1,15 @@
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index 498226a..446ba5e 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -2,7 +2,9 @@ cmake_minimum_required(VERSION 3.4)
+ 
+ project(snoretoast VERSION 0.6.0)
+ # Always change the guid when the version is changed SNORETOAST_CALLBACK_GUID
+-set(SNORETOAST_CALLBACK_GUID eb1fdd5b-8f70-4b5a-b230-998a2dc19303)
++#We keep it fixed!
++set(SNORETOAST_CALLBACK_GUID 7F00FB48-65D5-4BA8-A35B-F194DA7E1A51)
++
+ 
+ set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/)
+ 
diff --git a/cmds/notifier/spn.go b/cmds/notifier/spn.go
new file mode 100644
index 00000000..1da5639d
--- /dev/null
+++ b/cmds/notifier/spn.go
@@ -0,0 +1,103 @@
+package main
+
+import (
+	"sync"
+	"time"
+
+	"github.com/safing/portbase/api/client"
+	"github.com/safing/portbase/formats/dsd"
+	"github.com/safing/portbase/log"
+	"github.com/tevino/abool"
+)
+
+const (
+	spnModuleKey = "config:spn/enable"
+	spnStatusKey = "runtime:spn/status"
+)
+
+var (
+	spnEnabled = abool.New()
+
+	spnStatusCache     *SPNStatus
+	spnStatusCacheLock sync.Mutex
+)
+
+// SPNStatus holds SPN status information.
+type SPNStatus struct {
+	Status             string
+	HomeHubID          string
+	HomeHubName        string
+	ConnectedIP        string
+	ConnectedTransport string
+	ConnectedSince     *time.Time
+}
+
+// GetSPNStatus returns the SPN status.
+func GetSPNStatus() *SPNStatus {
+	spnStatusCacheLock.Lock()
+	defer spnStatusCacheLock.Unlock()
+
+	return spnStatusCache
+}
+
+func updateSPNStatus(s *SPNStatus) {
+	spnStatusCacheLock.Lock()
+	defer spnStatusCacheLock.Unlock()
+
+	spnStatusCache = s
+}
+
+func spnStatusClient() {
+	moduleQueryOp := apiClient.Qsub("query "+spnModuleKey, handleSPNModuleUpdate)
+	moduleQueryOp.EnableResuscitation()
+
+	statusQueryOp := apiClient.Qsub("query "+spnStatusKey, handleSPNStatusUpdate)
+	statusQueryOp.EnableResuscitation()
+}
+
+func handleSPNModuleUpdate(m *client.Message) {
+	switch m.Type {
+	case client.MsgOk, client.MsgUpdate, client.MsgNew:
+		var cfg struct {
+			Value bool `json:"Value"`
+		}
+		_, err := dsd.Load(m.RawValue, &cfg)
+		if err != nil {
+			log.Warningf("config: failed to parse config: %s", err)
+			return
+		}
+		log.Infof("config: received update to SPN module: enabled=%v", cfg.Value)
+
+		spnEnabled.SetTo(cfg.Value)
+		triggerTrayUpdate()
+
+	default:
+	}
+}
+
+func handleSPNStatusUpdate(m *client.Message) {
+	switch m.Type {
+	case client.MsgOk, client.MsgUpdate, client.MsgNew:
+		newStatus := &SPNStatus{}
+		_, err := dsd.Load(m.RawValue, newStatus)
+		if err != nil {
+			log.Warningf("config: failed to parse config: %s", err)
+			return
+		}
+		log.Infof("config: received update to SPN status: %+v", newStatus)
+
+		updateSPNStatus(newStatus)
+		triggerTrayUpdate()
+
+	default:
+	}
+}
+
+func ToggleSPN() {
+	var cfg struct {
+		Value bool `json:"Value"`
+	}
+	cfg.Value = !spnEnabled.IsSet()
+
+	apiClient.Update(spnModuleKey, &cfg, nil)
+}
diff --git a/cmds/notifier/subsystems.go b/cmds/notifier/subsystems.go
new file mode 100644
index 00000000..f810538c
--- /dev/null
+++ b/cmds/notifier/subsystems.go
@@ -0,0 +1,122 @@
+package main
+
+import (
+	"fmt"
+	"sync"
+
+	"github.com/safing/portbase/api/client"
+	"github.com/safing/portbase/formats/dsd"
+	"github.com/safing/portbase/log"
+)
+
+const (
+	subsystemsKeySpace = "runtime:subsystems/"
+
+	// Module Failure Status Values
+	// FailureNone    = 0 // unused
+	// FailureHint    = 1 // unused
+	FailureWarning = 2
+	FailureError   = 3
+)
+
+var (
+	subsystems     = make(map[string]*Subsystem)
+	subsystemsLock sync.Mutex
+)
+
+// Subsystem describes a subset of modules that represent a part of a
+// service or program to the user. Subsystems can be (de-)activated causing
+// all related modules to be brought down or up.
+type Subsystem struct { //nolint:maligned // not worth the effort
+	// ID is a unique identifier for the subsystem.
+	ID string
+
+	// Name holds a human readable name of the subsystem.
+	Name string
+
+	// Description may holds an optional description of
+	// the subsystem's purpose.
+	Description string
+
+	// Modules contains all modules that are related to the subsystem.
+	// Note that this slice also contains a reference to the subsystem
+	// module itself.
+	Modules []*ModuleStatus
+
+	// FailureStatus is the worst failure status that is currently
+	// set in one of the subsystem's dependencies.
+	FailureStatus uint8
+}
+
+// ModuleStatus describes the status of a module.
+type ModuleStatus struct {
+	Name          string
+	Enabled       bool
+	Status        uint8
+	FailureStatus uint8
+	FailureID     string
+	FailureMsg    string
+}
+
+// GetFailure returns the worst of all subsystem failures.
+func GetFailure() (failureStatus uint8, failureMsg string) {
+	subsystemsLock.Lock()
+	defer subsystemsLock.Unlock()
+
+	for _, subsystem := range subsystems {
+		for _, module := range subsystem.Modules {
+			if failureStatus < module.FailureStatus {
+				failureStatus = module.FailureStatus
+				failureMsg = module.FailureMsg
+			}
+		}
+	}
+
+	return
+}
+
+func updateSubsystem(s *Subsystem) {
+	subsystemsLock.Lock()
+	defer subsystemsLock.Unlock()
+
+	subsystems[s.ID] = s
+}
+
+func clearSubsystems() {
+	subsystemsLock.Lock()
+	defer subsystemsLock.Unlock()
+
+	for key := range subsystems {
+		delete(subsystems, key)
+	}
+}
+
+func subsystemsClient() {
+	subsystemsOp := apiClient.Qsub(fmt.Sprintf("query %s", subsystemsKeySpace), handleSubsystem)
+	subsystemsOp.EnableResuscitation()
+}
+
+func handleSubsystem(m *client.Message) {
+	switch m.Type {
+	case client.MsgError:
+	case client.MsgDone:
+	case client.MsgSuccess:
+	case client.MsgOk, client.MsgUpdate, client.MsgNew:
+
+		newSubsystem := &Subsystem{}
+		_, err := dsd.Load(m.RawValue, newSubsystem)
+		if err != nil {
+			log.Warningf("subsystems: failed to parse new subsystem: %s", err)
+			return
+		}
+		updateSubsystem(newSubsystem)
+		triggerTrayUpdate()
+
+	case client.MsgDelete:
+	case client.MsgWarning:
+	case client.MsgOffline:
+
+		clearSubsystems()
+
+	}
+}
diff --git a/cmds/notifier/tray.go b/cmds/notifier/tray.go
new file mode 100644
index 00000000..5766611f
--- /dev/null
+++ b/cmds/notifier/tray.go
@@ -0,0 +1,218 @@
+package main
+
+import (
+	"flag"
+	"os/exec"
+	"path/filepath"
+	"runtime"
+	"strings"
+	"sync"
+	"time"
+
+	"fyne.io/systray"
+
+	"github.com/safing/portbase/log"
+	icons "github.com/safing/portmaster/assets"
+)
+
+const (
+	shortenStatusMsgTo = 40
+)
+
+var (
+	trayLock sync.Mutex
+
+	scaleColoredIconsTo int
+
+	activeIconID    int = -1
+	activeStatusMsg     = ""
+	activeSPNStatus     = ""
+	activeSPNSwitch     = ""
+
+	menuItemStatusMsg *systray.MenuItem
+	menuItemSPNStatus *systray.MenuItem
+	menuItemSPNSwitch *systray.MenuItem
+)
+
+func init() {
+	flag.IntVar(&scaleColoredIconsTo, "scale-icons", 32, "scale colored icons to given size in pixels")
+
+	// lock until ready
+	trayLock.Lock()
+}
+
+func tray() {
+	if scaleColoredIconsTo > 0 {
+		icons.ScaleColoredIconsTo(scaleColoredIconsTo)
+	}
+
+	systray.Run(onReady, onExit)
+}
+
+func exitTray() {
+	systray.Quit()
+}
+
+func onReady() {
+	// unlock when ready
+	defer trayLock.Unlock()
+
+	// icon
+	systray.SetIcon(icons.ColoredIcons[icons.RedID])
+	if runtime.GOOS == "windows" {
+		// systray.SetTitle("Portmaster Notifier") // Don't set title, as it may be displayed in full in the menu/tray bar. (Ubuntu)
+		systray.SetTooltip("Portmaster Notifier")
+	}
+
+	// menu: open app
+	if dataDir != "" {
+		menuItemOpenApp := systray.AddMenuItem("Open App", "")
+		go clickListener(menuItemOpenApp, launchApp)
+		systray.AddSeparator()
+	}
+
+	// menu: status
+
+	menuItemStatusMsg = systray.AddMenuItem("Loading...", "")
+	menuItemStatusMsg.Disable()
+	systray.AddSeparator()
+
+	// menu: SPN
+
+	menuItemSPNStatus = systray.AddMenuItem("Loading...", "")
+	menuItemSPNStatus.Disable()
+	menuItemSPNSwitch = systray.AddMenuItem("Loading...", "")
+	go clickListener(menuItemSPNSwitch, func() {
+		ToggleSPN()
+	})
+	systray.AddSeparator()
+
+	// menu: quit
+	systray.AddSeparator()
+	closeTray := systray.AddMenuItem("Close Tray Notifier", "")
+	go clickListener(closeTray, func() {
+		cancelMainCtx()
+	})
+	shutdownPortmaster := systray.AddMenuItem("Shut Down Portmaster", "")
+	go clickListener(shutdownPortmaster, func() {
+		_ = TriggerShutdown()
+		time.Sleep(1 * time.Second)
+		cancelMainCtx()
+	})
+}
+
+func onExit() {
+
+}
+
+func triggerTrayUpdate() {
+	// TODO: Deduplicate triggers.
+	go updateTray()
+}
+
+// updateTray update the state of the tray depending on the currently available information.
+func updateTray() {
+	// Get current information.
+	spnStatus := GetSPNStatus()
+	failureID, failureMsg := GetFailure()
+
+	trayLock.Lock()
+	defer trayLock.Unlock()
+
+	// Select icon and status message to show.
+	newIconID := icons.GreenID
+	newStatusMsg := "Secure"
+	switch {
+	case shuttingDown.IsSet():
+		newIconID = icons.RedID
+		newStatusMsg = "Shutting Down Portmaster"
+
+	case restarting.IsSet():
+		newIconID = icons.YellowID
+		newStatusMsg = "Restarting Portmaster"
+
+	case !connected.IsSet():
+		newIconID = icons.RedID
+		newStatusMsg = "Waiting for Portmaster Core Service"
+
+	case failureID == FailureError:
+		newIconID = icons.RedID
+		newStatusMsg = failureMsg
+
+	case failureID == FailureWarning:
+		newIconID = icons.YellowID
+		newStatusMsg = failureMsg
+
+	case spnEnabled.IsSet():
+		newIconID = icons.BlueID
+	}
+
+	// Set icon if changed.
+	if newIconID != activeIconID {
+		activeIconID = newIconID
+		systray.SetIcon(icons.ColoredIcons[activeIconID])
+	}
+
+	// Set message if changed.
+	if newStatusMsg != activeStatusMsg {
+		activeStatusMsg = newStatusMsg
+
+		// Shorten message if too long.
+		shortenedMsg := activeStatusMsg
+		if len(shortenedMsg) > shortenStatusMsgTo && strings.Contains(shortenedMsg, ". ") {
+			shortenedMsg = strings.SplitN(shortenedMsg, ". ", 2)[0]
+		}
+		if len(shortenedMsg) > shortenStatusMsgTo {
+			shortenedMsg = shortenedMsg[:shortenStatusMsgTo] + "..."
+		}
+
+		menuItemStatusMsg.SetTitle("Status: " + shortenedMsg)
+	}
+
+	// Set SPN status if changed.
+	if spnStatus != nil && activeSPNStatus != spnStatus.Status {
+		activeSPNStatus = spnStatus.Status
+		menuItemSPNStatus.SetTitle("SPN: " + strings.Title(activeSPNStatus))
+	}
+
+	// Set SPN switch if changed.
+	newSPNSwitch := "Enable SPN"
+	if spnEnabled.IsSet() {
+		newSPNSwitch = "Disable SPN"
+	}
+	if activeSPNSwitch != newSPNSwitch {
+		activeSPNSwitch = newSPNSwitch
+		menuItemSPNSwitch.SetTitle(activeSPNSwitch)
+	}
+}
+
+func clickListener(item *systray.MenuItem, fn func()) {
+	for range item.ClickedCh {
+		fn()
+	}
+}
+
+func launchApp() {
+	// build path to app
+	pmStartPath := filepath.Join(dataDir, "portmaster-start")
+	if runtime.GOOS == "windows" {
+		pmStartPath += ".exe"
+	}
+
+	// start app
+	cmd := exec.Command(pmStartPath, "app", "--data", dataDir)
+	err := cmd.Start()
+	if err != nil {
+		log.Warningf("failed to start app: %s", err)
+		return
+	}
+
+	// Use cmd.Wait() instead of cmd.Process.Release() to properly release its resources.
+	// See https://github.com/golang/go/issues/36534
+	go func() {
+		err := cmd.Wait()
+		if err != nil {
+			log.Warningf("failed to wait/release app process: %s", err)
+		}
+	}()
+}
diff --git a/cmds/notifier/wintoast/notification_builder.go b/cmds/notifier/wintoast/notification_builder.go
new file mode 100644
index 00000000..89eca798
--- /dev/null
+++ b/cmds/notifier/wintoast/notification_builder.go
@@ -0,0 +1,90 @@
+//go:build windows
+
+package wintoast
+
+import (
+	"unsafe"
+
+	"golang.org/x/sys/windows"
+)
+
+type NotificationBuilder struct {
+	templatePointer uintptr
+	lib             *WinToast
+}
+
+func newNotification(lib *WinToast, title string, message string) (*NotificationBuilder, error) {
+	lib.Lock()
+	defer lib.Unlock()
+
+	titleUTF, _ := windows.UTF16PtrFromString(title)
+	messageUTF, _ := windows.UTF16PtrFromString(message)
+	titleP := unsafe.Pointer(titleUTF)
+	messageP := unsafe.Pointer(messageUTF)
+
+	ptr, _, err := lib.createNotification.Call(uintptr(titleP), uintptr(messageP))
+	if ptr == 0 {
+		return nil, err
+	}
+
+	return &NotificationBuilder{ptr, lib}, nil
+}
+
+func (n *NotificationBuilder) Delete() {
+	if n == nil {
+		return
+	}
+
+	n.lib.Lock()
+	defer n.lib.Unlock()
+
+	_, _, _ = n.lib.deleteNotification.Call(n.templatePointer)
+}
+
+func (n *NotificationBuilder) AddButton(text string) error {
+	n.lib.Lock()
+	defer n.lib.Unlock()
+	textUTF, _ := windows.UTF16PtrFromString(text)
+	textP := unsafe.Pointer(textUTF)
+
+	rc, _, err := n.lib.addButton.Call(n.templatePointer, uintptr(textP))
+	if rc != 1 {
+		return err
+	}
+	return nil
+}
+
+func (n *NotificationBuilder) SetImage(iconPath string) error {
+	n.lib.Lock()
+	defer n.lib.Unlock()
+	pathUTF, _ := windows.UTF16PtrFromString(iconPath)
+	pathP := unsafe.Pointer(pathUTF)
+
+	rc, _, err := n.lib.setImage.Call(n.templatePointer, uintptr(pathP))
+	if rc != 1 {
+		return err
+	}
+	return nil
+}
+
+func (n *NotificationBuilder) SetSound(option int, path int) error {
+	n.lib.Lock()
+	defer n.lib.Unlock()
+
+	rc, _, err := n.lib.setSound.Call(n.templatePointer, uintptr(option), uintptr(path))
+	if rc != 1 {
+		return err
+	}
+	return nil
+}
+
+func (n *NotificationBuilder) Show() (int64, error) {
+	n.lib.Lock()
+	defer n.lib.Unlock()
+
+	id, _, err := n.lib.showNotification.Call(n.templatePointer)
+	if int64(id) == -1 {
+		return -1, err
+	}
+	return int64(id), nil
+}
diff --git a/cmds/notifier/wintoast/wintoast.go b/cmds/notifier/wintoast/wintoast.go
new file mode 100644
index 00000000..5d9a3380
--- /dev/null
+++ b/cmds/notifier/wintoast/wintoast.go
@@ -0,0 +1,217 @@
+//go:build windows
+
+package wintoast
+
+import (
+	"fmt"
+	"sync"
+	"unsafe"
+
+	"github.com/tevino/abool"
+
+	"golang.org/x/sys/windows"
+)
+
+// WinNotify holds the DLL handle.
+type WinToast struct {
+	sync.RWMutex
+
+	dll *windows.DLL
+
+	initialized *abool.AtomicBool
+
+	initialize           *windows.Proc
+	isInitialized        *windows.Proc
+	createNotification   *windows.Proc
+	deleteNotification   *windows.Proc
+	addButton            *windows.Proc
+	setImage             *windows.Proc
+	setSound             *windows.Proc
+	showNotification     *windows.Proc
+	hideNotification     *windows.Proc
+	setActivatedCallback *windows.Proc
+	setDismissedCallback *windows.Proc
+	setFailedCallback    *windows.Proc
+}
+
+func New(dllPath string) (*WinToast, error) {
+	if dllPath == "" {
+		return nil, fmt.Errorf("winnotifiy: path to dll not specified")
+	}
+
+	libraryObject := &WinToast{}
+	libraryObject.initialized = abool.New()
+
+	// load dll
+	var err error
+	libraryObject.dll, err = windows.LoadDLL(dllPath)
+	if err != nil {
+		return nil, fmt.Errorf("winnotifiy: failed to load notifier dll %w", err)
+	}
+
+	// load functions
+	libraryObject.initialize, err = libraryObject.dll.FindProc("PortmasterToastInitialize")
+	if err != nil {
+		return nil, fmt.Errorf("winnotifiy: PortmasterToastInitialize not found %w", err)
+	}
+
+	libraryObject.isInitialized, err = libraryObject.dll.FindProc("PortmasterToastIsInitialized")
+	if err != nil {
+		return nil, fmt.Errorf("winnotifiy: PortmasterToastIsInitialized not found %w", err)
+	}
+
+	libraryObject.createNotification, err = libraryObject.dll.FindProc("PortmasterToastCreateNotification")
+	if err != nil {
+		return nil, fmt.Errorf("winnotifiy: PortmasterToastCreateNotification not found %w", err)
+	}
+
+	libraryObject.deleteNotification, err = libraryObject.dll.FindProc("PortmasterToastDeleteNotification")
+	if err != nil {
+		return nil, fmt.Errorf("winnotifiy: PortmasterToastDeleteNotification not found %w", err)
+	}
+
+	libraryObject.addButton, err = libraryObject.dll.FindProc("PortmasterToastAddButton")
+	if err != nil {
+		return nil, fmt.Errorf("winnotifiy: PortmasterToastAddButton not found %w", err)
+	}
+
+	libraryObject.setImage, err = libraryObject.dll.FindProc("PortmasterToastSetImage")
+	if err != nil {
+		return nil, fmt.Errorf("winnotifiy: PortmasterToastSetImage not found %w", err)
+	}
+
+	libraryObject.setSound, err = libraryObject.dll.FindProc("PortmasterToastSetSound")
+	if err != nil {
+		return nil, fmt.Errorf("winnotifiy: PortmasterToastSetSound not found %w", err)
+	}
+
+	libraryObject.showNotification, err = libraryObject.dll.FindProc("PortmasterToastShow")
+	if err != nil {
+		return nil, fmt.Errorf("winnotifiy: PortmasterToastShow not found %w", err)
+	}
+
+	libraryObject.setActivatedCallback, err = libraryObject.dll.FindProc("PortmasterToastActivatedCallback")
+	if err != nil {
+		return nil, fmt.Errorf("winnotifiy: PortmasterActivatedCallback not found %w", err)
+	}
+
+	libraryObject.setDismissedCallback, err = libraryObject.dll.FindProc("PortmasterToastDismissedCallback")
+	if err != nil {
+		return nil, fmt.Errorf("winnotifiy: PortmasterToastDismissedCallback not found %w", err)
+	}
+
+	libraryObject.setFailedCallback, err = libraryObject.dll.FindProc("PortmasterToastFailedCallback")
+	if err != nil {
+		return nil, fmt.Errorf("winnotifiy: PortmasterToastFailedCallback not found %w", err)
+	}
+
+	libraryObject.hideNotification, err = libraryObject.dll.FindProc("PortmasterToastHide")
+	if err != nil {
+		return nil, fmt.Errorf("winnotifiy: PortmasterToastHide not found %w", err)
+	}
+
+	return libraryObject, nil
+}
+
+func (lib *WinToast) Initialize(appName, aumi, originalShortcutPath string) error {
+	if lib == nil {
+		return fmt.Errorf("wintoast: lib object was nil")
+	}
+
+	lib.Lock()
+	defer lib.Unlock()
+
+	// Initialize all necessary string for the notification meta data
+	appNameUTF, _ := windows.UTF16PtrFromString(appName)
+	aumiUTF, _ := windows.UTF16PtrFromString(aumi)
+	linkUTF, _ := windows.UTF16PtrFromString(originalShortcutPath)
+
+	// They are needed as unsafe pointers
+	appNameP := unsafe.Pointer(appNameUTF)
+	aumiP := unsafe.Pointer(aumiUTF)
+	linkP := unsafe.Pointer(linkUTF)
+
+	// Initialize notifications
+	rc, _, err := lib.initialize.Call(uintptr(appNameP), uintptr(aumiP), uintptr(linkP))
+	if rc != 0 {
+		return fmt.Errorf("wintoast: failed to initialize library rc = %d, %w", rc, err)
+	}
+
+	// Check if if the initialization was successfully
+	rc, _, _ = lib.isInitialized.Call()
+	if rc == 1 {
+		lib.initialized.Set()
+	} else {
+		return fmt.Errorf("wintoast: initialized flag was not set: rc = %d", rc)
+	}
+
+	return nil
+}
+
+func (lib *WinToast) SetCallbacks(activated func(id int64, actionIndex int32), dismissed func(id int64, reason int32), failed func(id int64, reason int32)) error {
+	if lib == nil {
+		return fmt.Errorf("wintoast: lib object was nil")
+	}
+
+	if lib.initialized.IsNotSet() {
+		return fmt.Errorf("winnotifiy: library not initialized")
+	}
+
+	// Initialize notification activated callback
+	callback := windows.NewCallback(func(id int64, actionIndex int32) uint64 {
+		activated(id, actionIndex)
+		return 0
+	})
+	rc, _, err := lib.setActivatedCallback.Call(callback)
+	if rc != 1 {
+		return fmt.Errorf("winnotifiy: failed to initialize activated callback %w", err)
+	}
+
+	// Initialize notification dismissed callback
+	callback = windows.NewCallback(func(id int64, actionIndex int32) uint64 {
+		dismissed(id, actionIndex)
+		return 0
+	})
+	rc, _, err = lib.setDismissedCallback.Call(callback)
+	if rc != 1 {
+		return fmt.Errorf("winnotifiy: failed to initialize dismissed callback %w", err)
+	}
+
+	// Initialize notification failed callback
+	callback = windows.NewCallback(func(id int64, actionIndex int32) uint64 {
+		failed(id, actionIndex)
+		return 0
+	})
+	rc, _, err = lib.setFailedCallback.Call(callback)
+	if rc != 1 {
+		return fmt.Errorf("winnotifiy: failed to initialize failed callback %s", err)
+	}
+
+	return nil
+}
+
+// NewNotification starts a creation of new notification. NotificationBuilder.Delete should allays be called when done using the object or there will be memory leeks
+func (lib *WinToast) NewNotification(title string, content string) (*NotificationBuilder, error) {
+	if lib == nil {
+		return nil, fmt.Errorf("wintoast: lib object was nil")
+	}
+	return newNotification(lib, title, content)
+}
+
+// HideNotification hides notification
+func (lib *WinToast) HideNotification(id int64) error {
+	if lib == nil {
+		return fmt.Errorf("wintoast: lib object was nil")
+	}
+
+	lib.Lock()
+	defer lib.Unlock()
+
+	rc, _, _ := lib.hideNotification.Call(uintptr(id))
+
+	if rc != 1 {
+		return fmt.Errorf("wintoast: failed to hide notification %d", id)
+	}
+
+	return nil
+}
diff --git a/desktop/angular/assets b/desktop/angular/assets
index 41aef43f..21dab851 120000
--- a/desktop/angular/assets
+++ b/desktop/angular/assets
@@ -1 +1 @@
-../../assets
\ No newline at end of file
+../../assets/data
\ No newline at end of file
diff --git a/go.mod b/go.mod
index 6a1fc4e3..f1b496f5 100644
--- a/go.mod
+++ b/go.mod
@@ -8,11 +8,13 @@ toolchain go1.21.2
 replace github.com/tc-hib/winres => github.com/dhaavi/winres v0.2.2
 
 require (
+	fyne.io/systray v1.10.0
 	github.com/Xuanwo/go-locale v1.1.0
 	github.com/agext/levenshtein v1.2.3
 	github.com/awalterschulze/gographviz v2.0.3+incompatible
 	github.com/cilium/ebpf v0.12.3
 	github.com/coreos/go-iptables v0.7.0
+	github.com/dhaavi/go-notify v0.0.0-20190209221809-c404b1f22435
 	github.com/florianl/go-conntrack v0.4.0
 	github.com/florianl/go-nfqueue v1.3.1
 	github.com/fogleman/gg v1.3.0
@@ -43,6 +45,7 @@ require (
 	github.com/umahmood/haversine v0.0.0-20151105152445-808ab04add26
 	github.com/vincent-petithory/dataurl v1.0.0
 	golang.org/x/exp v0.0.0-20240110193028-0dcbfd608b1e
+	golang.org/x/image v0.15.0
 	golang.org/x/net v0.20.0
 	golang.org/x/sync v0.6.0
 	golang.org/x/sys v0.16.0
@@ -65,6 +68,7 @@ require (
 	github.com/fxamacker/cbor v1.5.1 // indirect
 	github.com/fxamacker/cbor/v2 v2.5.0 // indirect
 	github.com/go-ole/go-ole v1.3.0 // indirect
+	github.com/godbus/dbus v4.1.0+incompatible // indirect
 	github.com/gofrs/uuid v4.4.0+incompatible // indirect
 	github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
 	github.com/golang/gddo v0.0.0-20210115222349-20d68f94ee1f // indirect
@@ -106,7 +110,6 @@ require (
 	github.com/zeebo/blake3 v0.2.3 // indirect
 	go.etcd.io/bbolt v1.3.8 // indirect
 	golang.org/x/crypto v0.18.0 // indirect
-	golang.org/x/image v0.15.0 // indirect
 	golang.org/x/mod v0.14.0 // indirect
 	golang.org/x/text v0.14.0 // indirect
 	golang.org/x/time v0.5.0 // indirect
diff --git a/go.sum b/go.sum
index e4c9c316..60ead25b 100644
--- a/go.sum
+++ b/go.sum
@@ -1,4 +1,6 @@
 cloud.google.com/go v0.16.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+fyne.io/systray v1.10.0 h1:Yr1D9Lxeiw3+vSuZWPlaHC8BMjIHZXJKkek706AfYQk=
+fyne.io/systray v1.10.0/go.mod h1:oM2AQqGJ1AMo4nNqZFYU8xYygSBZkW2hmdJ7n4yjedE=
 github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M=
 github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
@@ -42,6 +44,8 @@ github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x
 github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE=
 github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8=
 github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA=
+github.com/dhaavi/go-notify v0.0.0-20190209221809-c404b1f22435 h1:AnwbdEI8eV3GzLM3SlrJlYmYa6OB5X8RwY4A8QJOCP0=
+github.com/dhaavi/go-notify v0.0.0-20190209221809-c404b1f22435/go.mod h1:EMJ8XWTopp8OLRBMUm9vHE8Wn48CNpU21HM817OKNrc=
 github.com/dhaavi/winres v0.2.2 h1:SUago7FwhgLSMyDdeuV6enBZ+ZQSl0KwcnbWzvlfBls=
 github.com/dhaavi/winres v0.2.2/go.mod h1:1NTs+/DtKP1BplIL1+XQSoq4X1PUfLczexS7gf3x9T4=
 github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
@@ -70,6 +74,9 @@ github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiU
 github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
 github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
 github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/godbus/dbus v4.1.0+incompatible h1:WqqLRTsQic3apZUK9qC5sGNfXthmPXzUZ7nQPrNITa4=
+github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
+github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
 github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
 github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
 github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
@@ -354,6 +361,7 @@ golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201118182958-a01c418693c7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
diff --git a/runtime/.gitkeep b/runtime/.gitkeep
deleted file mode 100644
index 0d64ac96..00000000
--- a/runtime/.gitkeep
+++ /dev/null
@@ -1 +0,0 @@
-The new portbase should land here.
\ No newline at end of file