diff --git a/.gitignore b/.gitignore
index b6582db..32e03d7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,32 +1,36 @@
 # src
 build
 target
 tests
 bin
 
 # maid
 .maid/cache
 .maid/temp
 .maid/server.toml
 
 # build output
 dist/
 assets/
 .astro/
 
 # dependencies
 node_modules/
 
 # logs
 npm-debug.log*
 yarn-debug.log*
 yarn-error.log*
 pnpm-debug.log*
 
 
 # environment variables
 .env
 .env.production
 
 # todo
-*.todo
\ No newline at end of file
+*.todo
+
+# jetbrains
+.idea
+.fleet
\ No newline at end of file
diff --git a/Cargo.lock b/Cargo.lock
index 714cd74..53811a6 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,3338 +1,3549 @@
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
 version = 3
 
 [[package]]
 name = "addr2line"
 version = "0.21.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
 dependencies = [
  "gimli",
 ]
 
 [[package]]
 name = "adler"
 version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
 
 [[package]]
 name = "aho-corasick"
 version = "1.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
 dependencies = [
  "memchr",
 ]
 
 [[package]]
 name = "android-tzdata"
 version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
 
 [[package]]
 name = "android_system_properties"
 version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
 dependencies = [
  "libc",
 ]
 
 [[package]]
 name = "ansi-str"
 version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1cf4578926a981ab0ca955dc023541d19de37112bc24c1a197bd806d3d86ad1d"
 dependencies = [
  "ansitok",
 ]
 
 [[package]]
 name = "ansitok"
 version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "220044e6a1bb31ddee4e3db724d29767f352de47445a6cd75e1a173142136c83"
 dependencies = [
  "nom",
  "vte",
 ]
 
 [[package]]
 name = "anstream"
 version = "0.6.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3fde6067df7359f2d6335ec1a50c1f8f825801687d10da0cc4c6b08e3f6afd15"
 dependencies = [
  "anstyle",
  "anstyle-parse",
  "anstyle-query",
  "anstyle-wincon",
  "colorchoice",
  "utf8parse",
 ]
 
 [[package]]
 name = "anstyle"
 version = "1.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
 
 [[package]]
 name = "anstyle-parse"
 version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
 dependencies = [
  "utf8parse",
 ]
 
 [[package]]
 name = "anstyle-query"
 version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
 dependencies = [
  "windows-sys 0.52.0",
 ]
 
 [[package]]
 name = "anstyle-wincon"
 version = "3.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
 dependencies = [
  "anstyle",
  "windows-sys 0.52.0",
 ]
 
 [[package]]
 name = "anyhow"
 version = "1.0.79"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
 
 [[package]]
 name = "arrayref"
 version = "0.3.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545"
 
 [[package]]
 name = "arrayvec"
 version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
 
 [[package]]
 name = "arrayvec"
 version = "0.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
 
 [[package]]
 name = "async-stream"
 version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51"
 dependencies = [
  "async-stream-impl",
  "futures-core",
  "pin-project-lite",
 ]
 
 [[package]]
 name = "async-stream-impl"
 version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
 dependencies = [
  "proc-macro2",
  "quote",
  "syn 2.0.48",
 ]
 
 [[package]]
 name = "async-trait"
 version = "0.1.77"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9"
 dependencies = [
  "proc-macro2",
  "quote",
  "syn 2.0.48",
 ]
 
 [[package]]
 name = "atomic"
 version = "0.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba"
 
 [[package]]
 name = "atomic"
 version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8d818003e740b63afc82337e3160717f4f63078720a810b7b903e70a5d1d2994"
 dependencies = [
  "bytemuck",
 ]
 
 [[package]]
 name = "autocfg"
 version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 
 [[package]]
 name = "backtrace"
 version = "0.3.69"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
 dependencies = [
  "addr2line",
  "cc",
  "cfg-if",
  "libc",
  "miniz_oxide",
  "object",
  "rustc-demangle",
 ]
 
 [[package]]
 name = "base64"
 version = "0.21.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
 
+[[package]]
+name = "base64"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+
 [[package]]
 name = "binascii"
 version = "0.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72"
 
 [[package]]
 name = "bitflags"
 version = "1.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
 name = "bitflags"
 version = "2.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
 dependencies = [
  "serde",
 ]
 
 [[package]]
 name = "blake3"
 version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0231f06152bf547e9c2b5194f247cd97aacf6dcd8b15d8e5ec0663f64580da87"
 dependencies = [
  "arrayref",
  "arrayvec 0.7.4",
  "cc",
  "cfg-if",
  "constant_time_eq",
 ]
 
 [[package]]
 name = "block-buffer"
 version = "0.10.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
 dependencies = [
  "generic-array",
 ]
 
 [[package]]
 name = "bstr"
 version = "1.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc"
 dependencies = [
  "memchr",
  "serde",
 ]
 
 [[package]]
 name = "bumpalo"
 version = "3.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
 
 [[package]]
 name = "bytecount"
 version = "0.6.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205"
 
 [[package]]
 name = "bytemuck"
 version = "1.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6"
 
 [[package]]
 name = "byteorder"
 version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
 
 [[package]]
 name = "bytes"
 version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
 
 [[package]]
 name = "camino"
 version = "1.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c"
 
 [[package]]
 name = "cc"
 version = "1.0.83"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
 dependencies = [
  "libc",
 ]
 
 [[package]]
 name = "cfg-if"
 version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
 name = "chrono"
 version = "0.4.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
 dependencies = [
  "android-tzdata",
  "iana-time-zone",
  "js-sys",
  "num-traits",
  "serde",
  "wasm-bindgen",
  "windows-targets 0.48.5",
 ]
 
 [[package]]
 name = "chrono-tz"
 version = "0.8.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "91d7b79e99bfaa0d47da0687c43aa3b7381938a62ad3a6498599039321f660b7"
 dependencies = [
  "chrono",
  "chrono-tz-build",
  "phf",
 ]
 
 [[package]]
 name = "chrono-tz-build"
 version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "433e39f13c9a060046954e0592a8d0a4bcb1040125cbf91cb8ee58964cfb350f"
 dependencies = [
  "parse-zoneinfo",
  "phf",
  "phf_codegen",
 ]
 
 [[package]]
 name = "clap"
 version = "4.4.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c"
 dependencies = [
  "clap_builder",
  "clap_derive",
 ]
 
 [[package]]
 name = "clap-verbosity-flag"
 version = "2.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b57f73ca21b17a0352944b9bb61803b6007bd911b6cccfef7153f7f0600ac495"
 dependencies = [
  "clap",
  "log",
 ]
 
 [[package]]
 name = "clap_builder"
 version = "4.4.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7"
 dependencies = [
  "anstream",
  "anstyle",
  "clap_lex",
  "strsim",
 ]
 
 [[package]]
 name = "clap_derive"
 version = "4.4.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
 dependencies = [
  "heck",
  "proc-macro2",
  "quote",
  "syn 2.0.48",
 ]
 
 [[package]]
 name = "clap_lex"
 version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
 
 [[package]]
 name = "codespan-reporting"
 version = "0.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
 dependencies = [
  "termcolor",
  "unicode-width",
 ]
 
 [[package]]
 name = "colorchoice"
 version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
 
 [[package]]
 name = "colored"
 version = "2.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8"
 dependencies = [
  "lazy_static",
  "windows-sys 0.48.0",
 ]
 
 [[package]]
 name = "constant_time_eq"
 version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2"
 
 [[package]]
 name = "cookie"
 version = "0.18.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3cd91cf61412820176e137621345ee43b3f4423e589e7ae4e50d601d93e35ef8"
 dependencies = [
  "percent-encoding",
  "time",
  "version_check",
 ]
 
 [[package]]
 name = "core-foundation"
 version = "0.9.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
 dependencies = [
  "core-foundation-sys",
  "libc",
 ]
 
 [[package]]
 name = "core-foundation-sys"
 version = "0.8.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
 
 [[package]]
 name = "cpufeatures"
 version = "0.2.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
 dependencies = [
  "libc",
 ]
 
 [[package]]
 name = "crc32fast"
 version = "1.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
 dependencies = [
  "cfg-if",
 ]
 
 [[package]]
 name = "crossbeam-deque"
 version = "0.8.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
 dependencies = [
  "crossbeam-epoch",
  "crossbeam-utils",
 ]
 
 [[package]]
 name = "crossbeam-epoch"
 version = "0.9.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
 dependencies = [
  "crossbeam-utils",
 ]
 
 [[package]]
 name = "crossbeam-utils"
 version = "0.8.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
 
+[[package]]
+name = "crossterm"
+version = "0.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67"
+dependencies = [
+ "bitflags 1.3.2",
+ "crossterm_winapi",
+ "libc",
+ "mio",
+ "parking_lot",
+ "signal-hook",
+ "signal-hook-mio",
+ "winapi",
+]
+
+[[package]]
+name = "crossterm_winapi"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
+dependencies = [
+ "winapi",
+]
+
 [[package]]
 name = "crypto-common"
 version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
 dependencies = [
  "generic-array",
  "typenum",
 ]
 
 [[package]]
 name = "cxx"
 version = "1.0.115"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8de00f15a6fa069c99b88c5c78c4541d0e7899a33b86f7480e23df2431fce0bc"
 dependencies = [
  "cc",
  "cxxbridge-flags",
  "cxxbridge-macro",
  "link-cplusplus",
 ]
 
 [[package]]
 name = "cxx-build"
 version = "1.0.115"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0a71e1e631fa2f2f5f92e8b0d860a00c198c6771623a6cefcc863e3554f0d8d6"
 dependencies = [
  "cc",
  "codespan-reporting",
  "once_cell",
  "proc-macro2",
  "quote",
  "scratch",
  "syn 2.0.48",
 ]
 
 [[package]]
 name = "cxxbridge-flags"
 version = "1.0.115"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6f3fed61d56ba497c4efef9144dfdbaa25aa58f2f6b3a7cf441d4591c583745c"
 
 [[package]]
 name = "cxxbridge-macro"
 version = "1.0.115"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8908e380a8efd42150c017b0cfa31509fc49b6d47f7cb6b33e93ffb8f4e3661e"
 dependencies = [
  "proc-macro2",
  "quote",
  "syn 2.0.48",
 ]
 
 [[package]]
 name = "darwin-libproc"
 version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9fb90051930c9a0f09e585762152048e23ac74d20c10590ef7cf01c0343c3046"
 dependencies = [
  "darwin-libproc-sys",
  "libc",
  "memchr",
 ]
 
 [[package]]
 name = "darwin-libproc-sys"
 version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "57cebb5bde66eecdd30ddc4b9cd208238b15db4982ccc72db59d699ea10867c1"
 dependencies = [
  "libc",
 ]
 
 [[package]]
 name = "deranged"
 version = "0.3.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
 dependencies = [
  "powerfmt",
 ]
 
 [[package]]
 name = "derive_more"
 version = "0.99.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
 dependencies = [
  "proc-macro2",
  "quote",
  "syn 1.0.109",
 ]
 
 [[package]]
 name = "deunicode"
 version = "1.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3ae2a35373c5c74340b79ae6780b498b2b183915ec5dacf263aac5a099bf485a"
 
 [[package]]
 name = "devise"
 version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d6eacefd3f541c66fc61433d65e54e0e46e0a029a819a7dbbc7a7b489e8a85f8"
 dependencies = [
  "devise_codegen",
  "devise_core",
 ]
 
 [[package]]
 name = "devise_codegen"
 version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9c8cf4b8dd484ede80fd5c547592c46c3745a617c8af278e2b72bea86b2dfed6"
 dependencies = [
  "devise_core",
  "quote",
 ]
 
 [[package]]
 name = "devise_core"
 version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "35b50dba0afdca80b187392b24f2499a88c336d5a8493e4b4ccfb608708be56a"
 dependencies = [
  "bitflags 2.4.2",
  "proc-macro2",
  "proc-macro2-diagnostics",
  "quote",
  "syn 2.0.48",
 ]
 
 [[package]]
 name = "digest"
 version = "0.10.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
 dependencies = [
  "block-buffer",
  "crypto-common",
 ]
 
+[[package]]
+name = "directories"
+version = "5.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35"
+dependencies = [
+ "dirs-sys",
+]
+
 [[package]]
 name = "dirs"
 version = "5.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
 dependencies = [
  "dirs-sys",
 ]
 
 [[package]]
 name = "dirs-sys"
 version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
 dependencies = [
  "libc",
  "option-ext",
  "redox_users",
  "windows-sys 0.48.0",
 ]
 
+[[package]]
+name = "dyn-clone"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125"
+
 [[package]]
 name = "either"
 version = "1.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
 
 [[package]]
 name = "encoding_rs"
 version = "0.8.33"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1"
 dependencies = [
  "cfg-if",
 ]
 
 [[package]]
 name = "env_logger"
 version = "0.10.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580"
 dependencies = [
  "humantime",
  "is-terminal",
  "log",
  "regex",
  "termcolor",
 ]
 
 [[package]]
 name = "equivalent"
 version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
 
 [[package]]
 name = "errno"
 version = "0.3.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
 dependencies = [
  "libc",
  "windows-sys 0.52.0",
 ]
 
 [[package]]
 name = "fastrand"
 version = "2.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
 
 [[package]]
 name = "figment"
 version = "0.10.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2b6e5bc7bd59d60d0d45a6ccab6cf0f4ce28698fb4e81e750ddf229c9b824026"
 dependencies = [
  "atomic 0.6.0",
  "pear",
  "serde",
  "toml",
  "uncased",
  "version_check",
 ]
 
 [[package]]
 name = "filetime"
 version = "0.2.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd"
 dependencies = [
  "cfg-if",
  "libc",
  "redox_syscall 0.4.1",
  "windows-sys 0.52.0",
 ]
 
 [[package]]
 name = "flate2"
 version = "1.0.28"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e"
 dependencies = [
  "crc32fast",
  "miniz_oxide",
 ]
 
 [[package]]
 name = "fnv"
 version = "1.0.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
 
 [[package]]
 name = "form_urlencoded"
 version = "1.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
 dependencies = [
  "percent-encoding",
 ]
 
 [[package]]
 name = "futures"
 version = "0.3.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
 dependencies = [
  "futures-channel",
  "futures-core",
  "futures-io",
  "futures-sink",
  "futures-task",
  "futures-util",
 ]
 
 [[package]]
 name = "futures-channel"
 version = "0.3.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
 dependencies = [
  "futures-core",
  "futures-sink",
 ]
 
 [[package]]
 name = "futures-core"
 version = "0.3.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
 
 [[package]]
 name = "futures-io"
 version = "0.3.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
 
 [[package]]
 name = "futures-sink"
 version = "0.3.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
 
 [[package]]
 name = "futures-task"
 version = "0.3.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
 
 [[package]]
 name = "futures-util"
 version = "0.3.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
 dependencies = [
  "futures-channel",
  "futures-core",
  "futures-io",
  "futures-sink",
  "futures-task",
  "memchr",
  "pin-project-lite",
  "pin-utils",
  "slab",
 ]
 
+[[package]]
+name = "fuzzy-matcher"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54614a3312934d066701a80f20f15fa3b56d67ac7722b39eea5b4c9dd1d66c94"
+dependencies = [
+ "thread_local",
+]
+
+[[package]]
+name = "fxhash"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
+dependencies = [
+ "byteorder",
+]
+
 [[package]]
 name = "generator"
 version = "0.7.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e"
 dependencies = [
  "cc",
  "libc",
  "log",
  "rustversion",
  "windows",
 ]
 
 [[package]]
 name = "generic-array"
 version = "0.14.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
 dependencies = [
  "typenum",
  "version_check",
 ]
 
 [[package]]
 name = "getrandom"
 version = "0.2.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
 dependencies = [
  "cfg-if",
  "libc",
  "wasi",
 ]
 
 [[package]]
 name = "gimli"
 version = "0.28.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
 
 [[package]]
 name = "glob"
 version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
 
 [[package]]
 name = "global_placeholders"
 version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d70af3f3fd800923fa445d6fa562e054d8abaf06df926f77dd6dbead1fefb275"
 dependencies = [
  "parking_lot",
 ]
 
 [[package]]
 name = "globset"
 version = "0.4.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1"
 dependencies = [
  "aho-corasick",
  "bstr",
  "log",
  "regex-automata 0.4.3",
  "regex-syntax 0.8.2",
 ]
 
 [[package]]
 name = "globwalk"
 version = "0.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc"
 dependencies = [
  "bitflags 1.3.2",
  "ignore",
  "walkdir",
 ]
 
 [[package]]
 name = "h2"
 version = "0.3.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9"
 dependencies = [
  "bytes",
  "fnv",
  "futures-core",
  "futures-sink",
  "futures-util",
  "http",
  "indexmap",
  "slab",
  "tokio",
  "tokio-util",
  "tracing",
 ]
 
 [[package]]
 name = "hashbrown"
 version = "0.14.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
 
 [[package]]
 name = "heck"
 version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
 
 [[package]]
 name = "hermit-abi"
 version = "0.3.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f"
 
 [[package]]
 name = "home"
 version = "0.5.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
 dependencies = [
  "windows-sys 0.52.0",
 ]
 
 [[package]]
 name = "http"
 version = "0.2.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb"
 dependencies = [
  "bytes",
  "fnv",
  "itoa",
 ]
 
 [[package]]
 name = "http-body"
 version = "0.4.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
 dependencies = [
  "bytes",
  "http",
  "pin-project-lite",
 ]
 
 [[package]]
 name = "httparse"
 version = "1.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
 
 [[package]]
 name = "httpdate"
 version = "1.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
 
 [[package]]
 name = "humansize"
 version = "2.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7"
 dependencies = [
  "libm",
 ]
 
 [[package]]
 name = "humantime"
 version = "2.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
 
 [[package]]
 name = "hyper"
 version = "0.14.28"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80"
 dependencies = [
  "bytes",
  "futures-channel",
  "futures-core",
  "futures-util",
  "h2",
  "http",
  "http-body",
  "httparse",
  "httpdate",
  "itoa",
  "pin-project-lite",
  "socket2",
  "tokio",
  "tower-service",
  "tracing",
  "want",
 ]
 
 [[package]]
 name = "hyper-rustls"
 version = "0.24.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590"
 dependencies = [
  "futures-util",
  "http",
  "hyper",
- "rustls",
+ "rustls 0.21.10",
  "tokio",
  "tokio-rustls",
 ]
 
 [[package]]
 name = "iana-time-zone"
 version = "0.1.59"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539"
 dependencies = [
  "android_system_properties",
  "core-foundation-sys",
  "iana-time-zone-haiku",
  "js-sys",
  "wasm-bindgen",
  "windows-core",
 ]
 
 [[package]]
 name = "iana-time-zone-haiku"
 version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
 dependencies = [
  "cc",
 ]
 
 [[package]]
 name = "idna"
 version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
 dependencies = [
  "unicode-bidi",
  "unicode-normalization",
 ]
 
 [[package]]
 name = "ignore"
 version = "0.4.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1"
 dependencies = [
  "crossbeam-deque",
  "globset",
  "log",
  "memchr",
  "regex-automata 0.4.3",
  "same-file",
  "walkdir",
  "winapi-util",
 ]
 
 [[package]]
 name = "include_dir"
 version = "0.7.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "18762faeff7122e89e0857b02f7ce6fcc0d101d5e9ad2ad7846cc01d61b7f19e"
 dependencies = [
  "include_dir_macros",
 ]
 
 [[package]]
 name = "include_dir_macros"
 version = "0.7.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f"
 dependencies = [
  "proc-macro2",
  "quote",
 ]
 
 [[package]]
 name = "indexmap"
 version = "2.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
 dependencies = [
  "equivalent",
  "hashbrown",
  "serde",
 ]
 
 [[package]]
 name = "inlinable_string"
 version = "0.1.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb"
 
+[[package]]
+name = "inquire"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fddf93031af70e75410a2511ec04d49e758ed2f26dad3404a934e0fb45cc12a"
+dependencies = [
+ "bitflags 2.4.2",
+ "crossterm",
+ "dyn-clone",
+ "fuzzy-matcher",
+ "fxhash",
+ "newline-converter",
+ "once_cell",
+ "unicode-segmentation",
+ "unicode-width",
+]
+
 [[package]]
 name = "ipnet"
 version = "2.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
 
 [[package]]
 name = "is-terminal"
 version = "0.4.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455"
 dependencies = [
  "hermit-abi",
  "rustix",
  "windows-sys 0.52.0",
 ]
 
 [[package]]
 name = "itoa"
 version = "1.0.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
 
 [[package]]
 name = "js-sys"
 version = "0.3.67"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1"
 dependencies = [
  "wasm-bindgen",
 ]
 
 [[package]]
 name = "lazy_static"
 version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
 [[package]]
 name = "libc"
 version = "0.2.152"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
 
 [[package]]
 name = "libm"
 version = "0.2.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
 
 [[package]]
 name = "libredox"
 version = "0.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8"
 dependencies = [
  "bitflags 2.4.2",
  "libc",
  "redox_syscall 0.4.1",
 ]
 
 [[package]]
 name = "link-cplusplus"
 version = "1.0.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9d240c6f7e1ba3a28b0249f774e6a9dd0175054b52dfbb61b16eb8505c3785c9"
 dependencies = [
  "cc",
 ]
 
 [[package]]
 name = "linux-raw-sys"
 version = "0.4.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
 
 [[package]]
 name = "lock_api"
 version = "0.4.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
 dependencies = [
  "autocfg",
  "scopeguard",
 ]
 
 [[package]]
 name = "log"
 version = "0.4.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
 
 [[package]]
 name = "loom"
 version = "0.5.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5"
 dependencies = [
  "cfg-if",
  "generator",
  "scoped-tls",
  "serde",
  "serde_json",
  "tracing",
  "tracing-subscriber",
 ]
 
 [[package]]
 name = "mach2"
 version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709"
 dependencies = [
  "libc",
 ]
 
 [[package]]
 name = "macros-rs"
 version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d2c6d3c8d7adb9850f41a7797b7a9718784aefeee3d1fe5a84c09243703a49d0"
 
 [[package]]
 name = "matchers"
 version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
 dependencies = [
  "regex-automata 0.1.10",
 ]
 
 [[package]]
 name = "memchr"
 version = "2.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
 
 [[package]]
 name = "merkle_hash"
 version = "3.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c2019c22dd07fa00549b671a150d126929eb1af56a4f4808a2e62aca8676995d"
 dependencies = [
  "anyhow",
  "blake3",
  "camino",
  "rayon",
 ]
 
 [[package]]
 name = "mime"
 version = "0.3.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
 
 [[package]]
 name = "mime_guess"
 version = "2.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
 dependencies = [
  "mime",
  "unicase",
 ]
 
 [[package]]
 name = "minimal-lexical"
 version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
 
 [[package]]
 name = "miniz_oxide"
 version = "0.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
 dependencies = [
  "adler",
 ]
 
 [[package]]
 name = "mio"
 version = "0.8.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09"
 dependencies = [
  "libc",
+ "log",
  "wasi",
  "windows-sys 0.48.0",
 ]
 
 [[package]]
 name = "multer"
 version = "2.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "01acbdc23469fd8fe07ab135923371d5f5a422fbf9c522158677c8eb15bc51c2"
 dependencies = [
  "bytes",
  "encoding_rs",
  "futures-util",
  "http",
  "httparse",
  "log",
  "memchr",
  "mime",
  "spin",
  "tokio",
  "tokio-util",
  "version_check",
 ]
 
+[[package]]
+name = "newline-converter"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47b6b097ecb1cbfed438542d16e84fd7ad9b0c76c8a65b7f9039212a3d14dc7f"
+dependencies = [
+ "unicode-segmentation",
+]
+
 [[package]]
 name = "nix"
 version = "0.24.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069"
 dependencies = [
  "bitflags 1.3.2",
  "cfg-if",
  "libc",
 ]
 
 [[package]]
 name = "nix"
 version = "0.27.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
 dependencies = [
  "bitflags 2.4.2",
  "cfg-if",
  "libc",
 ]
 
 [[package]]
 name = "nom"
 version = "7.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
 dependencies = [
  "memchr",
  "minimal-lexical",
 ]
 
 [[package]]
 name = "nu-ansi-term"
 version = "0.46.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
 dependencies = [
  "overload",
  "winapi",
 ]
 
 [[package]]
 name = "num-traits"
 version = "0.2.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
 dependencies = [
  "autocfg",
 ]
 
 [[package]]
 name = "num_cpus"
 version = "1.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
 dependencies = [
  "hermit-abi",
  "libc",
 ]
 
 [[package]]
 name = "object"
 version = "0.32.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
 dependencies = [
  "memchr",
 ]
 
 [[package]]
 name = "once_cell"
 version = "1.19.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
 
 [[package]]
 name = "option-ext"
 version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
 
 [[package]]
 name = "overload"
 version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
 
 [[package]]
 name = "papergrid"
 version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9ad43c07024ef767f9160710b3a6773976194758c7919b17e63b863db0bdf7fb"
 dependencies = [
  "ansi-str",
  "ansitok",
  "bytecount",
  "fnv",
  "unicode-width",
 ]
 
 [[package]]
 name = "parking_lot"
 version = "0.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
 dependencies = [
  "lock_api",
  "parking_lot_core",
 ]
 
 [[package]]
 name = "parking_lot_core"
 version = "0.9.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
 dependencies = [
  "cfg-if",
  "libc",
  "redox_syscall 0.4.1",
  "smallvec",
  "windows-targets 0.48.5",
 ]
 
 [[package]]
 name = "parse-zoneinfo"
 version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c705f256449c60da65e11ff6626e0c16a0a0b96aaa348de61376b249bc340f41"
 dependencies = [
  "regex",
 ]
 
 [[package]]
 name = "pear"
 version = "0.2.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4ccca0f6c17acc81df8e242ed473ec144cbf5c98037e69aa6d144780aad103c8"
 dependencies = [
  "inlinable_string",
  "pear_codegen",
  "yansi",
 ]
 
 [[package]]
 name = "pear_codegen"
 version = "0.2.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2e22670e8eb757cff11d6c199ca7b987f352f0346e0be4dd23869ec72cb53c77"
 dependencies = [
  "proc-macro2",
  "proc-macro2-diagnostics",
  "quote",
  "syn 2.0.48",
 ]
 
 [[package]]
 name = "percent-encoding"
 version = "2.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
 
 [[package]]
 name = "pest"
 version = "2.7.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1f200d8d83c44a45b21764d1916299752ca035d15ecd46faca3e9a2a2bf6ad06"
 dependencies = [
  "memchr",
  "thiserror",
  "ucd-trie",
 ]
 
 [[package]]
 name = "pest_derive"
 version = "2.7.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bcd6ab1236bbdb3a49027e920e693192ebfe8913f6d60e294de57463a493cfde"
 dependencies = [
  "pest",
  "pest_generator",
 ]
 
 [[package]]
 name = "pest_generator"
 version = "2.7.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2a31940305ffc96863a735bef7c7994a00b325a7138fdbc5bda0f1a0476d3275"
 dependencies = [
  "pest",
  "pest_meta",
  "proc-macro2",
  "quote",
  "syn 2.0.48",
 ]
 
 [[package]]
 name = "pest_meta"
 version = "2.7.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a7ff62f5259e53b78d1af898941cdcdccfae7385cf7d793a6e55de5d05bb4b7d"
 dependencies = [
  "once_cell",
  "pest",
  "sha2",
 ]
 
 [[package]]
 name = "phf"
 version = "0.11.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
 dependencies = [
  "phf_shared",
 ]
 
 [[package]]
 name = "phf_codegen"
 version = "0.11.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a"
 dependencies = [
  "phf_generator",
  "phf_shared",
 ]
 
 [[package]]
 name = "phf_generator"
 version = "0.11.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0"
 dependencies = [
  "phf_shared",
  "rand",
 ]
 
 [[package]]
 name = "phf_shared"
 version = "0.11.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b"
 dependencies = [
  "siphasher",
 ]
 
 [[package]]
 name = "pin-project-lite"
 version = "0.2.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
 
 [[package]]
 name = "pin-utils"
 version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
 
 [[package]]
 name = "platforms"
 version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e8d0eef3571242013a0d5dc84861c3ae4a652e56e12adf8bdc26ff5f8cb34c94"
 dependencies = [
  "serde",
 ]
 
 [[package]]
 name = "pmc"
 version = "1.7.1"
 dependencies = [
  "anyhow",
  "bytes",
  "chrono",
  "clap",
  "clap-verbosity-flag",
  "colored",
  "cxx",
  "cxx-build",
  "env_logger",
  "flate2",
  "global_placeholders",
  "home",
  "include_dir",
+ "inquire",
  "lazy_static",
  "libc",
  "log",
  "macros-rs",
  "merkle_hash",
  "nix 0.27.1",
  "once_cell",
  "pretty_env_logger",
  "prometheus",
  "psutil",
  "regex",
  "reqwest",
  "rocket",
  "ron",
  "ryu",
  "serde",
  "serde_json",
  "simple-logging",
  "tabled",
  "tar",
  "tera",
  "termcolor",
  "tokio",
  "toml",
+ "update-informer",
  "utoipa",
  "utoipa-rapidoc",
  "utoipa-swagger-ui",
 ]
 
 [[package]]
 name = "powerfmt"
 version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
 
 [[package]]
 name = "ppv-lite86"
 version = "0.2.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
 
 [[package]]
 name = "pretty_env_logger"
 version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "865724d4dbe39d9f3dd3b52b88d859d66bcb2d6a0acfd5ea68a65fb66d4bdc1c"
 dependencies = [
  "env_logger",
  "log",
 ]
 
 [[package]]
 name = "proc-macro-error"
 version = "1.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
 dependencies = [
  "proc-macro-error-attr",
  "proc-macro2",
  "quote",
  "syn 1.0.109",
  "version_check",
 ]
 
 [[package]]
 name = "proc-macro-error-attr"
 version = "1.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
 dependencies = [
  "proc-macro2",
  "quote",
  "version_check",
 ]
 
 [[package]]
 name = "proc-macro2"
 version = "1.0.76"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
 dependencies = [
  "unicode-ident",
 ]
 
 [[package]]
 name = "proc-macro2-diagnostics"
 version = "0.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
 dependencies = [
  "proc-macro2",
  "quote",
  "syn 2.0.48",
  "version_check",
  "yansi",
 ]
 
 [[package]]
 name = "prometheus"
 version = "0.13.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c"
 dependencies = [
  "cfg-if",
  "fnv",
  "lazy_static",
  "memchr",
  "parking_lot",
  "protobuf",
  "thiserror",
 ]
 
 [[package]]
 name = "protobuf"
 version = "2.28.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94"
 
 [[package]]
 name = "psutil"
 version = "3.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5e617cc9058daa5e1fe5a0d23ed745773a5ee354111dad1ec0235b0cc16b6730"
 dependencies = [
  "cfg-if",
  "darwin-libproc",
  "derive_more",
  "glob",
  "mach2",
  "nix 0.24.3",
  "num_cpus",
  "once_cell",
  "platforms",
  "serde",
  "thiserror",
  "unescape",
 ]
 
 [[package]]
 name = "quote"
 version = "1.0.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
 dependencies = [
  "proc-macro2",
 ]
 
 [[package]]
 name = "rand"
 version = "0.8.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
 dependencies = [
  "libc",
  "rand_chacha",
  "rand_core",
 ]
 
 [[package]]
 name = "rand_chacha"
 version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
 dependencies = [
  "ppv-lite86",
  "rand_core",
 ]
 
 [[package]]
 name = "rand_core"
 version = "0.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
 dependencies = [
  "getrandom",
 ]
 
 [[package]]
 name = "rayon"
 version = "1.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051"
 dependencies = [
  "either",
  "rayon-core",
 ]
 
 [[package]]
 name = "rayon-core"
 version = "1.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
 dependencies = [
  "crossbeam-deque",
  "crossbeam-utils",
 ]
 
 [[package]]
 name = "redox_syscall"
 version = "0.1.57"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
 
 [[package]]
 name = "redox_syscall"
 version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
 dependencies = [
  "bitflags 1.3.2",
 ]
 
 [[package]]
 name = "redox_users"
 version = "0.4.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4"
 dependencies = [
  "getrandom",
  "libredox",
  "thiserror",
 ]
 
 [[package]]
 name = "ref-cast"
 version = "1.0.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c4846d4c50d1721b1a3bef8af76924eef20d5e723647333798c1b519b3a9473f"
 dependencies = [
  "ref-cast-impl",
 ]
 
 [[package]]
 name = "ref-cast-impl"
 version = "1.0.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5fddb4f8d99b0a2ebafc65a87a69a7b9875e4b1ae1f00db265d300ef7f28bccc"
 dependencies = [
  "proc-macro2",
  "quote",
  "syn 2.0.48",
 ]
 
 [[package]]
 name = "regex"
 version = "1.10.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
 dependencies = [
  "aho-corasick",
  "memchr",
  "regex-automata 0.4.3",
  "regex-syntax 0.8.2",
 ]
 
 [[package]]
 name = "regex-automata"
 version = "0.1.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
 dependencies = [
  "regex-syntax 0.6.29",
 ]
 
 [[package]]
 name = "regex-automata"
 version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
 dependencies = [
  "aho-corasick",
  "memchr",
  "regex-syntax 0.8.2",
 ]
 
 [[package]]
 name = "regex-syntax"
 version = "0.6.29"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
 
 [[package]]
 name = "regex-syntax"
 version = "0.8.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
 
 [[package]]
 name = "reqwest"
 version = "0.11.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41"
 dependencies = [
- "base64",
+ "base64 0.21.7",
  "bytes",
  "encoding_rs",
  "futures-core",
  "futures-util",
  "h2",
  "http",
  "http-body",
  "hyper",
  "hyper-rustls",
  "ipnet",
  "js-sys",
  "log",
  "mime",
  "once_cell",
  "percent-encoding",
  "pin-project-lite",
- "rustls",
+ "rustls 0.21.10",
  "rustls-pemfile",
  "serde",
  "serde_json",
  "serde_urlencoded",
  "system-configuration",
  "tokio",
  "tokio-rustls",
  "tower-service",
  "url",
  "wasm-bindgen",
  "wasm-bindgen-futures",
  "web-sys",
- "webpki-roots",
+ "webpki-roots 0.25.3",
  "winreg",
 ]
 
 [[package]]
 name = "ring"
 version = "0.17.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74"
 dependencies = [
  "cc",
  "getrandom",
  "libc",
  "spin",
  "untrusted",
  "windows-sys 0.48.0",
 ]
 
 [[package]]
 name = "rocket"
 version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9e7bb57ccb26670d73b6a47396c83139447b9e7878cab627fdfe9ea8da489150"
 dependencies = [
  "async-stream",
  "async-trait",
  "atomic 0.5.3",
  "binascii",
  "bytes",
  "either",
  "figment",
  "futures",
  "indexmap",
  "log",
  "memchr",
  "multer",
  "num_cpus",
  "parking_lot",
  "pin-project-lite",
  "rand",
  "ref-cast",
  "rocket_codegen",
  "rocket_http",
  "serde",
  "serde_json",
  "state",
  "tempfile",
  "time",
  "tokio",
  "tokio-stream",
  "tokio-util",
  "ubyte",
  "version_check",
  "yansi",
 ]
 
 [[package]]
 name = "rocket_codegen"
 version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a2238066abf75f21be6cd7dc1a09d5414a671f4246e384e49fe3f8a4936bd04c"
 dependencies = [
  "devise",
  "glob",
  "indexmap",
  "proc-macro2",
  "quote",
  "rocket_http",
  "syn 2.0.48",
  "unicode-xid",
  "version_check",
 ]
 
 [[package]]
 name = "rocket_http"
 version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "37a1663694d059fe5f943ea5481363e48050acedd241d46deb2e27f71110389e"
 dependencies = [
  "cookie",
  "either",
  "futures",
  "http",
  "hyper",
  "indexmap",
  "log",
  "memchr",
  "pear",
  "percent-encoding",
  "pin-project-lite",
  "ref-cast",
  "serde",
  "smallvec",
  "stable-pattern",
  "state",
  "time",
  "tokio",
  "uncased",
 ]
 
 [[package]]
 name = "ron"
 version = "0.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94"
 dependencies = [
- "base64",
+ "base64 0.21.7",
  "bitflags 2.4.2",
  "serde",
  "serde_derive",
 ]
 
 [[package]]
 name = "rust-embed"
 version = "8.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a82c0bbc10308ed323529fd3c1dce8badda635aa319a5ff0e6466f33b8101e3f"
 dependencies = [
  "rust-embed-impl",
  "rust-embed-utils",
  "walkdir",
 ]
 
 [[package]]
 name = "rust-embed-impl"
 version = "8.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6227c01b1783cdfee1bcf844eb44594cd16ec71c35305bf1c9fb5aade2735e16"
 dependencies = [
  "proc-macro2",
  "quote",
  "rust-embed-utils",
  "shellexpand",
  "syn 2.0.48",
  "walkdir",
 ]
 
 [[package]]
 name = "rust-embed-utils"
 version = "8.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8cb0a25bfbb2d4b4402179c2cf030387d9990857ce08a32592c6238db9fa8665"
 dependencies = [
  "sha2",
  "walkdir",
 ]
 
 [[package]]
 name = "rustc-demangle"
 version = "0.1.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
 
 [[package]]
 name = "rustix"
 version = "0.38.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca"
 dependencies = [
  "bitflags 2.4.2",
  "errno",
  "libc",
  "linux-raw-sys",
  "windows-sys 0.52.0",
 ]
 
 [[package]]
 name = "rustls"
 version = "0.21.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba"
 dependencies = [
  "log",
  "ring",
- "rustls-webpki",
+ "rustls-webpki 0.101.7",
  "sct",
 ]
 
+[[package]]
+name = "rustls"
+version = "0.22.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432"
+dependencies = [
+ "log",
+ "ring",
+ "rustls-pki-types",
+ "rustls-webpki 0.102.3",
+ "subtle",
+ "zeroize",
+]
+
 [[package]]
 name = "rustls-pemfile"
 version = "1.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
 dependencies = [
- "base64",
+ "base64 0.21.7",
 ]
 
+[[package]]
+name = "rustls-pki-types"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d"
+
 [[package]]
 name = "rustls-webpki"
 version = "0.101.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
 dependencies = [
  "ring",
  "untrusted",
 ]
 
+[[package]]
+name = "rustls-webpki"
+version = "0.102.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3bce581c0dd41bce533ce695a1437fa16a7ab5ac3ccfa99fe1a620a7885eabf"
+dependencies = [
+ "ring",
+ "rustls-pki-types",
+ "untrusted",
+]
+
 [[package]]
 name = "rustversion"
 version = "1.0.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
 
 [[package]]
 name = "ryu"
 version = "1.0.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
 
 [[package]]
 name = "same-file"
 version = "1.0.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
 dependencies = [
  "winapi-util",
 ]
 
 [[package]]
 name = "scoped-tls"
 version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
 
 [[package]]
 name = "scopeguard"
 version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
 
 [[package]]
 name = "scratch"
 version = "1.0.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a3cf7c11c38cb994f3d40e8a8cde3bbd1f72a435e4c49e85d6553d8312306152"
 
 [[package]]
 name = "sct"
 version = "0.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
 dependencies = [
  "ring",
  "untrusted",
 ]
 
+[[package]]
+name = "semver"
+version = "1.0.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
+
 [[package]]
 name = "serde"
 version = "1.0.195"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
 version = "1.0.195"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c"
 dependencies = [
  "proc-macro2",
  "quote",
  "syn 2.0.48",
 ]
 
 [[package]]
 name = "serde_json"
 version = "1.0.111"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4"
 dependencies = [
  "itoa",
  "ryu",
  "serde",
 ]
 
 [[package]]
 name = "serde_spanned"
 version = "0.6.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1"
 dependencies = [
  "serde",
 ]
 
 [[package]]
 name = "serde_urlencoded"
 version = "0.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
 dependencies = [
  "form_urlencoded",
  "itoa",
  "ryu",
  "serde",
 ]
 
 [[package]]
 name = "serde_yaml"
 version = "0.9.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b1bf28c79a99f70ee1f1d83d10c875d2e70618417fda01ad1785e027579d9d38"
 dependencies = [
  "indexmap",
  "itoa",
  "ryu",
  "serde",
  "unsafe-libyaml",
 ]
 
 [[package]]
 name = "sha2"
 version = "0.10.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
 dependencies = [
  "cfg-if",
  "cpufeatures",
  "digest",
 ]
 
 [[package]]
 name = "sharded-slab"
 version = "0.1.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
 dependencies = [
  "lazy_static",
 ]
 
 [[package]]
 name = "shellexpand"
 version = "3.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "da03fa3b94cc19e3ebfc88c4229c49d8f08cdbd1228870a45f0ffdf84988e14b"
 dependencies = [
  "dirs",
 ]
 
+[[package]]
+name = "signal-hook"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
+dependencies = [
+ "libc",
+ "signal-hook-registry",
+]
+
+[[package]]
+name = "signal-hook-mio"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
+dependencies = [
+ "libc",
+ "mio",
+ "signal-hook",
+]
+
 [[package]]
 name = "signal-hook-registry"
 version = "1.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
 dependencies = [
  "libc",
 ]
 
 [[package]]
 name = "simple-logging"
 version = "2.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b00d48e85675326bb182a2286ea7c1a0b264333ae10f27a937a72be08628b542"
 dependencies = [
  "lazy_static",
  "log",
  "thread-id",
 ]
 
 [[package]]
 name = "siphasher"
 version = "0.3.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
 
 [[package]]
 name = "slab"
 version = "0.4.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
 dependencies = [
  "autocfg",
 ]
 
 [[package]]
 name = "slug"
 version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3bd94acec9c8da640005f8e135a39fc0372e74535e6b368b7a04b875f784c8c4"
 dependencies = [
  "deunicode",
  "wasm-bindgen",
 ]
 
 [[package]]
 name = "smallvec"
 version = "1.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2593d31f82ead8df961d8bd23a64c2ccf2eb5dd34b0a34bfb4dd54011c72009e"
 
 [[package]]
 name = "socket2"
 version = "0.5.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9"
 dependencies = [
  "libc",
  "windows-sys 0.48.0",
 ]
 
 [[package]]
 name = "spin"
 version = "0.9.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
 
 [[package]]
 name = "stable-pattern"
 version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4564168c00635f88eaed410d5efa8131afa8d8699a612c80c455a0ba05c21045"
 dependencies = [
  "memchr",
 ]
 
 [[package]]
 name = "state"
 version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2b8c4a4445d81357df8b1a650d0d0d6fbbbfe99d064aa5e02f3e4022061476d8"
 dependencies = [
  "loom",
 ]
 
 [[package]]
 name = "strsim"
 version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
 
+[[package]]
+name = "subtle"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
+
 [[package]]
 name = "syn"
 version = "1.0.109"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
 dependencies = [
  "proc-macro2",
  "quote",
  "unicode-ident",
 ]
 
 [[package]]
 name = "syn"
 version = "2.0.48"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
 dependencies = [
  "proc-macro2",
  "quote",
  "unicode-ident",
 ]
 
 [[package]]
 name = "system-configuration"
 version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
 dependencies = [
  "bitflags 1.3.2",
  "core-foundation",
  "system-configuration-sys",
 ]
 
 [[package]]
 name = "system-configuration-sys"
 version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
 dependencies = [
  "core-foundation-sys",
  "libc",
 ]
 
 [[package]]
 name = "tabled"
 version = "0.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4c998b0c8b921495196a48aabaf1901ff28be0760136e31604f7967b0792050e"
 dependencies = [
  "ansi-str",
  "ansitok",
  "papergrid",
  "tabled_derive",
  "unicode-width",
 ]
 
 [[package]]
 name = "tabled_derive"
 version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4c138f99377e5d653a371cdad263615634cfc8467685dfe8e73e2b8e98f44b17"
 dependencies = [
  "heck",
  "proc-macro-error",
  "proc-macro2",
  "quote",
  "syn 1.0.109",
 ]
 
 [[package]]
 name = "tar"
 version = "0.4.40"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb"
 dependencies = [
  "filetime",
  "libc",
  "xattr",
 ]
 
 [[package]]
 name = "tempfile"
 version = "3.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa"
 dependencies = [
  "cfg-if",
  "fastrand",
  "redox_syscall 0.4.1",
  "rustix",
  "windows-sys 0.52.0",
 ]
 
 [[package]]
 name = "tera"
 version = "1.19.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "970dff17c11e884a4a09bc76e3a17ef71e01bb13447a11e85226e254fe6d10b8"
 dependencies = [
  "chrono",
  "chrono-tz",
  "globwalk",
  "humansize",
  "lazy_static",
  "percent-encoding",
  "pest",
  "pest_derive",
  "rand",
  "regex",
  "serde",
  "serde_json",
  "slug",
  "unic-segment",
 ]
 
 [[package]]
 name = "termcolor"
 version = "1.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
 dependencies = [
  "winapi-util",
 ]
 
 [[package]]
 name = "thiserror"
 version = "1.0.56"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad"
 dependencies = [
  "thiserror-impl",
 ]
 
 [[package]]
 name = "thiserror-impl"
 version = "1.0.56"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
 dependencies = [
  "proc-macro2",
  "quote",
  "syn 2.0.48",
 ]
 
 [[package]]
 name = "thread-id"
 version = "3.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c7fbf4c9d56b320106cd64fd024dadfa0be7cb4706725fc44a7d7ce952d820c1"
 dependencies = [
  "libc",
  "redox_syscall 0.1.57",
  "winapi",
 ]
 
 [[package]]
 name = "thread_local"
 version = "1.1.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152"
 dependencies = [
  "cfg-if",
  "once_cell",
 ]
 
 [[package]]
 name = "time"
 version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e"
 dependencies = [
  "deranged",
  "itoa",
  "powerfmt",
  "serde",
  "time-core",
  "time-macros",
 ]
 
 [[package]]
 name = "time-core"
 version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
 
 [[package]]
 name = "time-macros"
 version = "0.2.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f"
 dependencies = [
  "time-core",
 ]
 
 [[package]]
 name = "tinyvec"
 version = "1.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
 dependencies = [
  "tinyvec_macros",
 ]
 
 [[package]]
 name = "tinyvec_macros"
 version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
 
 [[package]]
 name = "tokio"
 version = "1.35.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104"
 dependencies = [
  "backtrace",
  "bytes",
  "libc",
  "mio",
  "num_cpus",
  "parking_lot",
  "pin-project-lite",
  "signal-hook-registry",
  "socket2",
  "tokio-macros",
  "windows-sys 0.48.0",
 ]
 
 [[package]]
 name = "tokio-macros"
 version = "2.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
 dependencies = [
  "proc-macro2",
  "quote",
  "syn 2.0.48",
 ]
 
 [[package]]
 name = "tokio-rustls"
 version = "0.24.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
 dependencies = [
- "rustls",
+ "rustls 0.21.10",
  "tokio",
 ]
 
 [[package]]
 name = "tokio-stream"
 version = "0.1.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842"
 dependencies = [
  "futures-core",
  "pin-project-lite",
  "tokio",
 ]
 
 [[package]]
 name = "tokio-util"
 version = "0.7.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
 dependencies = [
  "bytes",
  "futures-core",
  "futures-sink",
  "pin-project-lite",
  "tokio",
  "tracing",
 ]
 
 [[package]]
 name = "toml"
 version = "0.8.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35"
 dependencies = [
  "serde",
  "serde_spanned",
  "toml_datetime",
  "toml_edit",
 ]
 
 [[package]]
 name = "toml_datetime"
 version = "0.6.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
 dependencies = [
  "serde",
 ]
 
 [[package]]
 name = "toml_edit"
 version = "0.21.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03"
 dependencies = [
  "indexmap",
  "serde",
  "serde_spanned",
  "toml_datetime",
  "winnow",
 ]
 
 [[package]]
 name = "tower-service"
 version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
 
 [[package]]
 name = "tracing"
 version = "0.1.40"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
 dependencies = [
  "pin-project-lite",
  "tracing-attributes",
  "tracing-core",
 ]
 
 [[package]]
 name = "tracing-attributes"
 version = "0.1.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
 dependencies = [
  "proc-macro2",
  "quote",
  "syn 2.0.48",
 ]
 
 [[package]]
 name = "tracing-core"
 version = "0.1.32"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
 dependencies = [
  "once_cell",
  "valuable",
 ]
 
 [[package]]
 name = "tracing-log"
 version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
 dependencies = [
  "log",
  "once_cell",
  "tracing-core",
 ]
 
 [[package]]
 name = "tracing-subscriber"
 version = "0.3.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
 dependencies = [
  "matchers",
  "nu-ansi-term",
  "once_cell",
  "regex",
  "sharded-slab",
  "smallvec",
  "thread_local",
  "tracing",
  "tracing-core",
  "tracing-log",
 ]
 
 [[package]]
 name = "try-lock"
 version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
 
 [[package]]
 name = "typenum"
 version = "1.17.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
 
 [[package]]
 name = "ubyte"
 version = "0.10.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f720def6ce1ee2fc44d40ac9ed6d3a59c361c80a75a7aa8e75bb9baed31cf2ea"
 dependencies = [
  "serde",
 ]
 
 [[package]]
 name = "ucd-trie"
 version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
 
 [[package]]
 name = "uncased"
 version = "0.9.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9b9bc53168a4be7402ab86c3aad243a84dd7381d09be0eddc81280c1da95ca68"
 dependencies = [
  "serde",
  "version_check",
 ]
 
 [[package]]
 name = "unescape"
 version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ccb97dac3243214f8d8507998906ca3e2e0b900bf9bf4870477f125b82e68f6e"
 
 [[package]]
 name = "unic-char-property"
 version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221"
 dependencies = [
  "unic-char-range",
 ]
 
 [[package]]
 name = "unic-char-range"
 version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc"
 
 [[package]]
 name = "unic-common"
 version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc"
 
 [[package]]
 name = "unic-segment"
 version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e4ed5d26be57f84f176157270c112ef57b86debac9cd21daaabbe56db0f88f23"
 dependencies = [
  "unic-ucd-segment",
 ]
 
 [[package]]
 name = "unic-ucd-segment"
 version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2079c122a62205b421f499da10f3ee0f7697f012f55b675e002483c73ea34700"
 dependencies = [
  "unic-char-property",
  "unic-char-range",
  "unic-ucd-version",
 ]
 
 [[package]]
 name = "unic-ucd-version"
 version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4"
 dependencies = [
  "unic-common",
 ]
 
 [[package]]
 name = "unicase"
 version = "2.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
 dependencies = [
  "version_check",
 ]
 
 [[package]]
 name = "unicode-bidi"
 version = "0.3.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
 
 [[package]]
 name = "unicode-ident"
 version = "1.0.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
 
 [[package]]
 name = "unicode-normalization"
 version = "0.1.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
 dependencies = [
  "tinyvec",
 ]
 
+[[package]]
+name = "unicode-segmentation"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
+
 [[package]]
 name = "unicode-width"
 version = "0.1.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
 
 [[package]]
 name = "unicode-xid"
 version = "0.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
 
 [[package]]
 name = "unsafe-libyaml"
 version = "0.2.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b"
 
 [[package]]
 name = "untrusted"
 version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
 
+[[package]]
+name = "update-informer"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f8811797a24ff123db3c6e1087aa42551d03d772b3724be421ad063da1f5f3f"
+dependencies = [
+ "directories",
+ "reqwest",
+ "semver",
+ "serde",
+ "serde_json",
+ "ureq",
+]
+
+[[package]]
+name = "ureq"
+version = "2.9.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d11a831e3c0b56e438a28308e7c810799e3c118417f342d30ecec080105395cd"
+dependencies = [
+ "base64 0.22.1",
+ "flate2",
+ "log",
+ "once_cell",
+ "rustls 0.22.4",
+ "rustls-pki-types",
+ "rustls-webpki 0.102.3",
+ "serde",
+ "serde_json",
+ "url",
+ "webpki-roots 0.26.1",
+]
+
 [[package]]
 name = "url"
 version = "2.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
 dependencies = [
  "form_urlencoded",
  "idna",
  "percent-encoding",
 ]
 
 [[package]]
 name = "utf8parse"
 version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
 
 [[package]]
 name = "utoipa"
 version = "4.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "272ebdfbc99111033031d2f10e018836056e4d2c8e2acda76450ec7974269fa7"
 dependencies = [
  "indexmap",
  "serde",
  "serde_json",
  "serde_yaml",
  "utoipa-gen",
 ]
 
 [[package]]
 name = "utoipa-gen"
 version = "4.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d3c9f4d08338c1bfa70dde39412a040a884c6f318b3d09aaaf3437a1e52027fc"
 dependencies = [
  "proc-macro-error",
  "proc-macro2",
  "quote",
  "syn 2.0.48",
 ]
 
 [[package]]
 name = "utoipa-rapidoc"
 version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3d331839f6de584865f20c8138b73868d515ba62349ae4c8c1f78ffa54069e78"
 dependencies = [
  "serde",
  "serde_json",
  "utoipa",
 ]
 
 [[package]]
 name = "utoipa-swagger-ui"
 version = "5.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f839caa8e09dddc3ff1c3112a91ef7da0601075ba5025d9f33ae99c4cb9b6e51"
 dependencies = [
  "mime_guess",
  "regex",
  "rust-embed",
  "serde",
  "serde_json",
  "utoipa",
  "zip",
 ]
 
 [[package]]
 name = "valuable"
 version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
 
 [[package]]
 name = "version_check"
 version = "0.9.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
 
 [[package]]
 name = "vte"
 version = "0.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6cbce692ab4ca2f1f3047fcf732430249c0e971bfdd2b234cf2c47ad93af5983"
 dependencies = [
  "arrayvec 0.5.2",
  "utf8parse",
  "vte_generate_state_changes",
 ]
 
 [[package]]
 name = "vte_generate_state_changes"
 version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff"
 dependencies = [
  "proc-macro2",
  "quote",
 ]
 
 [[package]]
 name = "walkdir"
 version = "2.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
 dependencies = [
  "same-file",
  "winapi-util",
 ]
 
 [[package]]
 name = "want"
 version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
 dependencies = [
  "try-lock",
 ]
 
 [[package]]
 name = "wasi"
 version = "0.11.0+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
 [[package]]
 name = "wasm-bindgen"
 version = "0.2.90"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406"
 dependencies = [
  "cfg-if",
  "wasm-bindgen-macro",
 ]
 
 [[package]]
 name = "wasm-bindgen-backend"
 version = "0.2.90"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd"
 dependencies = [
  "bumpalo",
  "log",
  "once_cell",
  "proc-macro2",
  "quote",
  "syn 2.0.48",
  "wasm-bindgen-shared",
 ]
 
 [[package]]
 name = "wasm-bindgen-futures"
 version = "0.4.40"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461"
 dependencies = [
  "cfg-if",
  "js-sys",
  "wasm-bindgen",
  "web-sys",
 ]
 
 [[package]]
 name = "wasm-bindgen-macro"
 version = "0.2.90"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999"
 dependencies = [
  "quote",
  "wasm-bindgen-macro-support",
 ]
 
 [[package]]
 name = "wasm-bindgen-macro-support"
 version = "0.2.90"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7"
 dependencies = [
  "proc-macro2",
  "quote",
  "syn 2.0.48",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
 
 [[package]]
 name = "wasm-bindgen-shared"
 version = "0.2.90"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b"
 
 [[package]]
 name = "web-sys"
 version = "0.3.67"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed"
 dependencies = [
  "js-sys",
  "wasm-bindgen",
 ]
 
 [[package]]
 name = "webpki-roots"
 version = "0.25.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10"
 
+[[package]]
+name = "webpki-roots"
+version = "0.26.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009"
+dependencies = [
+ "rustls-pki-types",
+]
+
 [[package]]
 name = "winapi"
 version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
 dependencies = [
  "winapi-i686-pc-windows-gnu",
  "winapi-x86_64-pc-windows-gnu",
 ]
 
 [[package]]
 name = "winapi-i686-pc-windows-gnu"
 version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
 
 [[package]]
 name = "winapi-util"
 version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
 dependencies = [
  "winapi",
 ]
 
 [[package]]
 name = "winapi-x86_64-pc-windows-gnu"
 version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
 [[package]]
 name = "windows"
 version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
 dependencies = [
  "windows-targets 0.48.5",
 ]
 
 [[package]]
 name = "windows-core"
 version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
 dependencies = [
  "windows-targets 0.52.0",
 ]
 
 [[package]]
 name = "windows-sys"
 version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
 dependencies = [
  "windows-targets 0.48.5",
 ]
 
 [[package]]
 name = "windows-sys"
 version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
 dependencies = [
  "windows-targets 0.52.0",
 ]
 
 [[package]]
 name = "windows-targets"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
 dependencies = [
  "windows_aarch64_gnullvm 0.48.5",
  "windows_aarch64_msvc 0.48.5",
  "windows_i686_gnu 0.48.5",
  "windows_i686_msvc 0.48.5",
  "windows_x86_64_gnu 0.48.5",
  "windows_x86_64_gnullvm 0.48.5",
  "windows_x86_64_msvc 0.48.5",
 ]
 
 [[package]]
 name = "windows-targets"
 version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
 dependencies = [
  "windows_aarch64_gnullvm 0.52.0",
  "windows_aarch64_msvc 0.52.0",
  "windows_i686_gnu 0.52.0",
  "windows_i686_msvc 0.52.0",
  "windows_x86_64_gnu 0.52.0",
  "windows_x86_64_gnullvm 0.52.0",
  "windows_x86_64_msvc 0.52.0",
 ]
 
 [[package]]
 name = "windows_aarch64_gnullvm"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
 
 [[package]]
 name = "windows_aarch64_gnullvm"
 version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
 
 [[package]]
 name = "windows_aarch64_msvc"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
 
 [[package]]
 name = "windows_aarch64_msvc"
 version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
 
 [[package]]
 name = "windows_i686_gnu"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
 
 [[package]]
 name = "windows_i686_gnu"
 version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
 
 [[package]]
 name = "windows_i686_msvc"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
 
 [[package]]
 name = "windows_i686_msvc"
 version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
 
 [[package]]
 name = "windows_x86_64_gnu"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
 
 [[package]]
 name = "windows_x86_64_gnu"
 version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
 
 [[package]]
 name = "windows_x86_64_gnullvm"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
 
 [[package]]
 name = "windows_x86_64_gnullvm"
 version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
 
 [[package]]
 name = "windows_x86_64_msvc"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
 
 [[package]]
 name = "windows_x86_64_msvc"
 version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
 
 [[package]]
 name = "winnow"
 version = "0.5.34"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b7cf47b659b318dccbd69cc4797a39ae128f533dce7902a1096044d1967b9c16"
 dependencies = [
  "memchr",
 ]
 
 [[package]]
 name = "winreg"
 version = "0.50.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
 dependencies = [
  "cfg-if",
  "windows-sys 0.48.0",
 ]
 
 [[package]]
 name = "xattr"
 version = "1.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f"
 dependencies = [
  "libc",
  "linux-raw-sys",
  "rustix",
 ]
 
 [[package]]
 name = "yansi"
 version = "1.0.0-rc.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1367295b8f788d371ce2dbc842c7b709c73ee1364d30351dd300ec2203b12377"
 dependencies = [
  "is-terminal",
 ]
 
+[[package]]
+name = "zeroize"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
+
 [[package]]
 name = "zip"
 version = "0.6.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261"
 dependencies = [
  "byteorder",
  "crc32fast",
  "crossbeam-utils",
  "flate2",
 ]
diff --git a/Cargo.toml b/Cargo.toml
index 3b7a5cc..fa63262 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,70 +1,72 @@
 [package]
 name = "pmc"
 version = "1.7.1"
 edition = "2021"
 license = "MIT"
 repository = "https://lab.themackabu.dev/self/pmc"
 description = "PMC is a simple and easy to use PM2 alternative"
 
 [build-dependencies]
 tar = "0.4.40"
 chrono = "0.4.31"
 flate2 = "1.0.28"
 cxx-build = "1.0.112"
 
 [dependencies]
 ron = "0.8.1"
 log = "0.4.20"
 toml = "0.8.8"
 home = "0.5.9"
 ryu = "1.0.16"
 clap = "4.4.12"
 cxx = "1.0.112"
 bytes = "1.5.0"
 tera = "1.19.1"
 regex = "1.10.2"
 libc = "0.2.151"
 anyhow = "1.0.78"
 colored = "2.1.0"
 macros-rs = "0.5.0"
 termcolor = "1.4.0"
 once_cell = "1.19.0"
 env_logger = "0.10.1"
 merkle_hash = "3.5.0"
 lazy_static = "1.4.0"
 prometheus = "0.13.3"
 include_dir = "0.7.3"
 serde_json = "1.0.109"
 simple-logging = "2.0.2"
 utoipa-rapidoc = "2.0.0"
 pretty_env_logger = "0.5.0"
 utoipa-swagger-ui = "5.0.0"
 clap-verbosity-flag = "2.1.2"
 global_placeholders = "0.1.0"
 
 tokio = { version = "1.35.1", features = ["full"] }
 rocket = { version = "0.5.0", features = ["json"] }
 psutil = { version = "3.2.2", features = ["serde"] }
 tabled = { version = "0.15.0", features = ["ansi"] }
 chrono = { version = "0.4.31", features = ["serde"] }
 serde = { version = "1.0.193", features = ["derive"] }
 nix = { version = "0.27.1", features = ["process", "signal"] }
 utoipa = { version = "4.1.0", features = ["serde_yaml", "non_strict_integers"] }
+update-informer = "1.1.0"
+inquire = "0.7.5"
 
 
 [dependencies.reqwest]
 version = "0.11.23"
 default-features = false
 features = [
    "blocking", 
    "json", 
    "rustls-tls", 
 ]
 
 [build-dependencies.reqwest]
 version = "0.11.23"
 default-features = false
 features = [
    "blocking", 
    "rustls-tls", 
 ]
diff --git a/src/cli.rs b/src/cli/mod.rs
similarity index 97%
rename from src/cli.rs
rename to src/cli/mod.rs
index 886b7c7..5232fb3 100644
--- a/src/cli.rs
+++ b/src/cli/mod.rs
@@ -1,600 +1,607 @@
+pub(crate) mod server;
+
 use colored::Colorize;
 use macros_rs::{crashln, string, ternary};
 use psutil::process::{MemoryInfo, Process};
 use regex::Regex;
 use serde::Serialize;
 use serde_json::json;
 use std::env;
 
 use pmc::{
     config, file,
     helpers::{self, ColoredString},
     log,
     process::{http, ItemSingle, Runner},
 };
 
 use tabled::{
     settings::{
         object::{Columns, Rows},
         style::{BorderColor, Style},
         themes::Colorization,
         Color, Modify, Rotate, Width,
     },
     Table, Tabled,
 };
 
 #[derive(Clone, Debug)]
 pub enum Args {
     Id(usize),
     Script(String),
 }
 
 fn format(server_name: &String) -> (String, String) {
     let kind = ternary!(matches!(&**server_name, "internal" | "local"), "", "remote ").to_string();
     return (kind, server_name.to_string());
 }
 
 pub fn get_version(short: bool) -> String {
     return match short {
         true => format!("{} {}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")),
-        false => format!("{} ({} {}) [{}]", env!("CARGO_PKG_VERSION"), env!("GIT_HASH"), env!("BUILD_DATE"), env!("PROFILE")),
+        false => match env!("GIT_HASH") {
+            "" => format!("{} ({}) [{}]", env!("CARGO_PKG_VERSION"), env!("BUILD_DATE"), env!("PROFILE")),
+            hash => format!("{} ({} {hash}) [{}]", env!("CARGO_PKG_VERSION"), env!("BUILD_DATE"), env!("PROFILE")),
+        },
     };
 }
 
 pub fn start(name: &Option<String>, args: &Option<Args>, watch: &Option<String>, server_name: &String) {
     let mut runner = Runner::new();
     let config = config::read();
     let (kind, list_name) = format(server_name);
 
     match args {
         Some(Args::Id(id)) => {
             let runner: Runner = Runner::new();
             println!("{} Applying {kind}action restartProcess on ({id})", *helpers::SUCCESS);
 
             if matches!(&**server_name, "internal" | "local") {
                 let mut item = runner.get(*id);
 
                 match watch {
                     Some(path) => item.watch(path),
                     None => item.disable_watch(),
                 }
 
                 name.as_ref().map(|n| item.rename(n.trim().replace("\n", "")));
                 item.restart();
 
                 log!("process started (id={id})");
             } else {
                 let Some(servers) = config::servers().servers else {
                     crashln!("{} Failed to read servers", *helpers::FAIL)
                 };
 
                 if let Some(server) = servers.get(server_name) {
                     match Runner::connect(server_name.clone(), server.get(), false) {
                         Some(remote) => {
                             let mut item = remote.get(*id);
 
                             name.as_ref().map(|n| item.rename(n.trim().replace("\n", "")));
                             item.restart();
                         }
                         None => crashln!("{} Failed to connect (name={server_name}, address={})", *helpers::FAIL, server.address),
                     }
                 } else {
                     crashln!("{} Server '{server_name}' does not exist", *helpers::FAIL)
                 };
             }
 
             println!("{} restarted {kind}({id}) ✓", *helpers::SUCCESS);
             list(&string!("default"), &list_name);
         }
         Some(Args::Script(script)) => {
             let name = match name {
                 Some(name) => string!(name),
                 None => string!(script.split_whitespace().next().unwrap_or_default()),
             };
             if matches!(&**server_name, "internal" | "local") {
                 let pattern = Regex::new(r"(?m)^[a-zA-Z0-9]+(/[a-zA-Z0-9]+)*(\.js|\.ts)?$").unwrap();
 
                 if pattern.is_match(script) {
                     let script = format!("{} {script}", config.runner.node);
                     runner.start(&name, &script, file::cwd(), watch).save();
                 } else {
                     runner.start(&name, script, file::cwd(), watch).save();
                 }
 
                 log!("process created (name={name})");
             } else {
                 let Some(servers) = config::servers().servers else {
                     crashln!("{} Failed to read servers", *helpers::FAIL)
                 };
 
                 if let Some(server) = servers.get(server_name) {
                     match Runner::connect(server_name.clone(), server.get(), false) {
                         Some(mut remote) => remote.start(&name, script, file::cwd(), watch),
                         None => crashln!("{} Failed to connect (name={server_name}, address={})", *helpers::FAIL, server.address),
                     };
                 } else {
                     crashln!("{} Server '{server_name}' does not exist", *helpers::FAIL)
                 };
             }
 
             println!("{} Creating {kind}process with ({name})", *helpers::SUCCESS);
 
             println!("{} {kind}created ({name}) ✓", *helpers::SUCCESS);
             list(&string!("default"), &list_name);
         }
         None => {}
     }
 }
 
 pub fn stop(id: &usize, server_name: &String) {
     let mut runner: Runner = Runner::new();
     let (kind, list_name) = format(server_name);
     println!("{} Applying {kind}action stopProcess on ({id})", *helpers::SUCCESS);
 
     if !matches!(&**server_name, "internal" | "local") {
         let Some(servers) = config::servers().servers else {
             crashln!("{} Failed to read servers", *helpers::FAIL)
         };
 
         if let Some(server) = servers.get(server_name) {
             runner = match Runner::connect(server_name.clone(), server.get(), false) {
                 Some(remote) => remote,
                 None => crashln!("{} Failed to connect (name={server_name}, address={})", *helpers::FAIL, server.address),
             };
         } else {
             crashln!("{} Server '{server_name}' does not exist", *helpers::FAIL)
         };
     }
 
     runner.get(*id).stop();
     println!("{} stopped {kind}({id}) ✓", *helpers::SUCCESS);
     log!("process stopped {kind}(id={id})");
 
     list(&string!("default"), &list_name);
 }
 
 pub fn remove(id: &usize, server_name: &String) {
     let mut runner: Runner = Runner::new();
     let (kind, _) = format(server_name);
     println!("{} Applying {kind}action removeProcess on ({id})", *helpers::SUCCESS);
 
     if !matches!(&**server_name, "internal" | "local") {
         let Some(servers) = config::servers().servers else {
             crashln!("{} Failed to read servers", *helpers::FAIL)
         };
 
         if let Some(server) = servers.get(server_name) {
             runner = match Runner::connect(server_name.clone(), server.get(), false) {
                 Some(remote) => remote,
                 None => crashln!("{} Failed to remove (name={server_name}, address={})", *helpers::FAIL, server.address),
             };
         } else {
             crashln!("{} Server '{server_name}' does not exist", *helpers::FAIL)
         };
     }
 
     runner.remove(*id);
     println!("{} removed {kind}({id}) ✓", *helpers::SUCCESS);
     log!("process removed (id={id})");
 }
 
 pub fn info(id: &usize, format: &String, server_name: &String) {
     #[derive(Clone, Debug, Tabled)]
     struct Info {
         #[tabled(rename = "error log path ")]
         log_error: String,
         #[tabled(rename = "out log path")]
         log_out: String,
         #[tabled(rename = "cpu percent")]
         cpu_percent: String,
         #[tabled(rename = "memory usage")]
         memory_usage: String,
         #[tabled(rename = "path hash")]
         hash: String,
         #[tabled(rename = "watching")]
         watch: String,
         children: String,
         #[tabled(rename = "exec cwd")]
         path: String,
         #[tabled(rename = "script command ")]
         command: String,
         #[tabled(rename = "script id")]
         id: String,
         restarts: u64,
         uptime: String,
         pid: String,
         name: String,
         status: ColoredString,
     }
 
     impl Serialize for Info {
         fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
             let trimmed_json = json!({
                 "id": &self.id.trim(),
                 "pid": &self.pid.trim(),
                 "name": &self.name.trim(),
                 "path": &self.path.trim(),
                 "restarts": &self.restarts,
                 "hash": &self.hash.trim(),
                 "watch": &self.watch.trim(),
                 "children": &self.children,
                 "uptime": &self.uptime.trim(),
                 "status": &self.status.0.trim(),
                 "log_out": &self.log_out.trim(),
                 "cpu": &self.cpu_percent.trim(),
                 "command": &self.command.trim(),
                 "mem": &self.memory_usage.trim(),
                 "log_error": &self.log_error.trim(),
             });
 
             trimmed_json.serialize(serializer)
         }
     }
 
     let render_info = |data: Vec<Info>| {
         let table = Table::new(data.clone())
             .with(Rotate::Left)
             .with(Style::rounded().remove_horizontals())
             .with(Colorization::exact([Color::FG_CYAN], Columns::first()))
             .with(BorderColor::filled(Color::FG_BRIGHT_BLACK))
             .to_string();
 
         if let Ok(json) = serde_json::to_string(&data[0]) {
             match format.as_str() {
                 "raw" => println!("{:?}", data[0]),
                 "json" => println!("{json}"),
                 _ => {
                     println!("{}\n{table}\n", format!("Describing process with id ({id})").on_bright_white().black());
                     println!(" {}", format!("Use `pmc logs {id} [--lines <num>]` to display logs").white());
                     println!(" {}", format!("Use `pmc env {id}`  to display environment variables").white());
                 }
             };
         };
     };
 
     if matches!(&**server_name, "internal" | "local") {
         if let Some(home) = home::home_dir() {
             let config = config::read().runner;
             let mut runner = Runner::new();
             let item = runner.process(*id);
 
             let mut memory_usage: Option<MemoryInfo> = None;
             let mut cpu_percent: Option<f32> = None;
 
             let path = file::make_relative(&item.path, &home).to_string_lossy().into_owned();
             let children = if item.children.is_empty() { "none".to_string() } else { format!("{:?}", item.children) };
 
             if let Ok(mut process) = Process::new(item.pid as u32) {
                 memory_usage = process.memory_info().ok();
                 cpu_percent = process.cpu_percent().ok();
             }
 
             let cpu_percent = match cpu_percent {
                 Some(percent) => format!("{:.2}%", percent),
                 None => string!("0%"),
             };
 
             let memory_usage = match memory_usage {
                 Some(usage) => helpers::format_memory(usage.rss()),
                 None => string!("0b"),
             };
 
             let status = if item.running {
                 "online   ".green().bold()
             } else {
                 match item.crash.crashed {
                     true => "crashed   ",
                     false => "stopped   ",
                 }
                 .red()
                 .bold()
             };
 
             let data = vec![Info {
                 children,
                 cpu_percent,
                 memory_usage,
                 id: string!(id),
                 restarts: item.restarts,
                 name: item.name.clone(),
                 log_out: item.logs().out,
                 path: format!("{} ", path),
                 log_error: item.logs().error,
                 status: ColoredString(status),
                 pid: ternary!(item.running, format!("{}", item.pid), string!("n/a")),
                 command: format!("{} {} '{}'", config.shell, config.args.join(" "), item.script),
                 hash: ternary!(item.watch.enabled, format!("{}  ", item.watch.hash), string!("none  ")),
                 watch: ternary!(item.watch.enabled, format!("{path}/{}  ", item.watch.path), string!("disabled  ")),
                 uptime: ternary!(item.running, format!("{}", helpers::format_duration(item.started)), string!("none")),
             }];
 
             render_info(data)
         } else {
             crashln!("{} Impossible to get your home directory", *helpers::FAIL);
         }
     } else {
         let data: (pmc::process::Process, Runner);
         let Some(servers) = config::servers().servers else {
             crashln!("{} Failed to read servers", *helpers::FAIL)
         };
 
         if let Some(server) = servers.get(server_name) {
             data = match Runner::connect(server_name.clone(), server.get(), false) {
                 Some(mut remote) => (remote.process(*id).clone(), remote),
                 None => crashln!("{} Failed to connect (name={server_name}, address={})", *helpers::FAIL, server.address),
             };
         } else {
             crashln!("{} Server '{server_name}' does not exist", *helpers::FAIL)
         };
 
         let (item, remote) = data;
         let remote = remote.remote.unwrap();
         let info = http::info(&remote, *id);
         let path = item.path.to_string_lossy().into_owned();
 
         let status = if item.running {
             "online   ".green().bold()
         } else {
             match item.crash.crashed {
                 true => "crashed   ",
                 false => "stopped   ",
             }
             .red()
             .bold()
         };
 
         if let Ok(info) = info {
             let stats = info.json::<ItemSingle>().unwrap().stats;
             let children = if item.children.is_empty() { "none".to_string() } else { format!("{:?}", item.children) };
 
             let cpu_percent = match stats.cpu_percent {
                 Some(percent) => format!("{percent:.2}%"),
                 None => string!("0%"),
             };
 
             let memory_usage = match stats.memory_usage {
                 Some(usage) => helpers::format_memory(usage.rss),
                 None => string!("0b"),
             };
 
             let data = vec![Info {
                 children,
                 cpu_percent,
                 memory_usage,
                 id: string!(id),
                 path: path.clone(),
                 status: status.into(),
                 restarts: item.restarts,
                 name: item.name.clone(),
                 pid: ternary!(item.running, format!("{pid}", pid = item.pid), string!("n/a")),
                 log_out: format!("{}/{}-out.log", remote.config.log_path, item.name),
                 log_error: format!("{}/{}-error.log", remote.config.log_path, item.name),
                 hash: ternary!(item.watch.enabled, format!("{}  ", item.watch.hash), string!("none  ")),
                 command: format!("{} {} '{}'", remote.config.shell, remote.config.args.join(" "), item.script),
                 watch: ternary!(item.watch.enabled, format!("{path}/{}  ", item.watch.path), string!("disabled  ")),
                 uptime: ternary!(item.running, format!("{}", helpers::format_duration(item.started)), string!("none")),
             }];
 
             render_info(data)
         }
     }
 }
 
 pub fn logs(id: &usize, lines: &usize, server_name: &String) {
     let mut runner: Runner = Runner::new();
 
     if !matches!(&**server_name, "internal" | "local") {
         let Some(servers) = config::servers().servers else {
             crashln!("{} Failed to read servers", *helpers::FAIL)
         };
 
         if let Some(server) = servers.get(server_name) {
             runner = match Runner::connect(server_name.clone(), server.get(), false) {
                 Some(remote) => remote,
                 None => crashln!("{} Failed to connect (name={server_name}, address={})", *helpers::FAIL, server.address),
             };
         } else {
             crashln!("{} Server '{server_name}' does not exist", *helpers::FAIL)
         };
 
         let item = runner.info(*id).unwrap_or_else(|| crashln!("{} Process ({id}) not found", *helpers::FAIL));
         println!("{}", format!("Showing last {lines} lines for process [{id}] (change the value with --lines option)").yellow());
 
         for kind in vec!["error", "out"] {
             let logs = http::logs(&runner.remote.as_ref().unwrap(), *id, kind);
 
             if let Ok(log) = logs {
                 if log.lines.is_empty() {
                     println!("{} No logs found for {}/{kind}", *helpers::FAIL, item.name);
                     continue;
                 }
 
                 file::logs_internal(log.lines, *lines, log.path, *id, kind, &item.name)
             }
         }
     } else {
         let item = runner.info(*id).unwrap_or_else(|| crashln!("{} Process ({id}) not found", *helpers::FAIL));
         println!("{}", format!("Showing last {lines} lines for process [{id}] (change the value with --lines option)").yellow());
 
         file::logs(item, *lines, "error");
         file::logs(item, *lines, "out");
     }
 }
 
 pub fn env(id: &usize, server_name: &String) {
     let mut runner: Runner = Runner::new();
 
     if !matches!(&**server_name, "internal" | "local") {
         let Some(servers) = config::servers().servers else {
             crashln!("{} Failed to read servers", *helpers::FAIL)
         };
 
         if let Some(server) = servers.get(server_name) {
             runner = match Runner::connect(server_name.clone(), server.get(), false) {
                 Some(remote) => remote,
                 None => crashln!("{} Failed to connect (name={server_name}, address={})", *helpers::FAIL, server.address),
             };
         } else {
             crashln!("{} Server '{server_name}' does not exist", *helpers::FAIL)
         };
     }
 
     let item = runner.process(*id);
     item.env.iter().for_each(|(key, value)| println!("{}: {}", key, value.green()));
 }
 
 pub fn list(format: &String, server_name: &String) {
     let render_list = |runner: &mut Runner, internal: bool| {
         let mut processes: Vec<ProcessItem> = Vec::new();
 
         #[derive(Tabled, Debug)]
         struct ProcessItem {
             id: ColoredString,
             name: String,
             pid: String,
             uptime: String,
             #[tabled(rename = "↺")]
             restarts: String,
             status: ColoredString,
             cpu: String,
             mem: String,
             #[tabled(rename = "watching")]
             watch: String,
         }
 
         impl serde::Serialize for ProcessItem {
             fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
                 let trimmed_json = json!({
                     "cpu": &self.cpu.trim(),
                     "mem": &self.mem.trim(),
                     "id": &self.id.0.trim(),
                     "pid": &self.pid.trim(),
                     "name": &self.name.trim(),
                     "watch": &self.watch.trim(),
                     "uptime": &self.uptime.trim(),
                     "status": &self.status.0.trim(),
                     "restarts": &self.restarts.trim(),
                 });
                 trimmed_json.serialize(serializer)
             }
         }
 
         if runner.is_empty() {
             println!("{} Process table empty", *helpers::SUCCESS);
         } else {
             for (id, item) in runner.items() {
                 let mut cpu_percent: String = string!("0%");
                 let mut memory_usage: String = string!("0b");
 
                 if internal {
                     let mut usage_internals: (Option<f32>, Option<MemoryInfo>) = (None, None);
 
                     if let Ok(mut process) = Process::new(item.pid as u32) {
                         usage_internals = (process.cpu_percent().ok(), process.memory_info().ok());
                     }
 
                     cpu_percent = match usage_internals.0 {
                         Some(percent) => format!("{:.0}%", percent),
                         None => string!("0%"),
                     };
 
                     memory_usage = match usage_internals.1 {
                         Some(usage) => helpers::format_memory(usage.rss()),
                         None => string!("0b"),
                     };
                 } else {
                     let info = http::info(&runner.remote.as_ref().unwrap(), id);
 
                     if let Ok(info) = info {
                         let stats = info.json::<ItemSingle>().unwrap().stats;
 
                         cpu_percent = match stats.cpu_percent {
                             Some(percent) => format!("{:.2}%", percent),
                             None => string!("0%"),
                         };
 
                         memory_usage = match stats.memory_usage {
                             Some(usage) => helpers::format_memory(usage.rss),
                             None => string!("0b"),
                         };
                     }
                 }
 
                 let status = if item.running {
                     "online   ".green().bold()
                 } else {
                     match item.crash.crashed {
                         true => "crashed   ",
                         false => "stopped   ",
                     }
                     .red()
                     .bold()
                 };
 
                 processes.push(ProcessItem {
                     status: status.into(),
                     cpu: format!("{cpu_percent}   "),
                     mem: format!("{memory_usage}   "),
                     id: id.to_string().cyan().bold().into(),
                     restarts: format!("{}  ", item.restarts),
                     name: format!("{}   ", item.name.clone()),
                     pid: ternary!(item.running, format!("{}  ", item.pid), string!("n/a  ")),
                     watch: ternary!(item.watch.enabled, format!("{}  ", item.watch.path), string!("disabled  ")),
                     uptime: ternary!(item.running, format!("{}  ", helpers::format_duration(item.started)), string!("none  ")),
                 });
             }
 
             let table = Table::new(&processes)
                 .with(Style::rounded().remove_verticals())
                 .with(BorderColor::filled(Color::FG_BRIGHT_BLACK))
                 .with(Colorization::exact([Color::FG_BRIGHT_CYAN], Rows::first()))
                 .with(Modify::new(Columns::single(1)).with(Width::truncate(35).suffix("...  ")))
                 .to_string();
 
             if let Ok(json) = serde_json::to_string(&processes) {
                 match format.as_str() {
                     "raw" => println!("{:?}", processes),
                     "json" => println!("{json}"),
                     "default" => println!("{table}"),
                     _ => {}
                 };
             };
         }
     };
 
     if let Some(servers) = config::servers().servers {
         let mut failed: Vec<(String, String)> = vec![];
 
         if let Some(server) = servers.get(server_name) {
             match Runner::connect(server_name.clone(), server.get(), true) {
                 Some(mut remote) => render_list(&mut remote, false),
                 None => println!("{} Failed to fetch (name={server_name}, address={})", *helpers::FAIL, server.address),
             }
         } else {
-            if matches!(&**server_name, "internal" | "all" | "local") {
-                println!("{} Internal daemon", *helpers::SUCCESS);
+            if matches!(&**server_name, "internal" | "all" | "global" | "local") {
+                if *server_name == "all" || *server_name == "global" {
+                    println!("{} Internal daemon", *helpers::SUCCESS);
+                }
                 render_list(&mut Runner::new(), true);
             } else {
                 crashln!("{} Server '{server_name}' does not exist", *helpers::FAIL);
             }
         }
 
-        if *server_name == "all" {
+        if *server_name == "all" || *server_name == "global" {
             for (name, server) in servers {
                 match Runner::connect(name.clone(), server.get(), true) {
                     Some(mut remote) => render_list(&mut remote, false),
                     None => failed.push((name, server.address)),
                 }
             }
         }
 
         if !failed.is_empty() {
             println!("{} Failed servers:", *helpers::FAIL);
             failed
                 .iter()
                 .for_each(|server| println!(" {} {} {}", "-".yellow(), format!("{}", server.0), format!("[{}]", server.1).white()));
         }
     } else {
         render_list(&mut Runner::new(), true);
     }
 }
diff --git a/src/cli/server.rs b/src/cli/server.rs
new file mode 100644
index 0000000..53403da
--- /dev/null
+++ b/src/cli/server.rs
@@ -0,0 +1,143 @@
+use colored::Colorize;
+use inquire::{Confirm, Password, PasswordDisplayMode, Select, Text};
+use macros_rs::{crashln, string};
+use std::{collections::BTreeMap, fs::write};
+
+use pmc::{
+    config::{
+        self,
+        structs::{Server, Servers},
+    },
+    helpers,
+};
+
+fn save(servers: BTreeMap<String, Server>) {
+    match home::home_dir() {
+        Some(path) => {
+            let path = path.display();
+            let config_path = format!("{path}/.pmc/servers.toml");
+
+            let contents = match toml::to_string(&Servers { servers: Some(servers) }) {
+                Ok(contents) => contents,
+                Err(err) => crashln!("{} Cannot parse servers.\n{}", *helpers::FAIL, string!(err).white()),
+            };
+
+            if let Err(err) = write(&config_path, contents) {
+                crashln!("{} Error writing servers.\n{}", *helpers::FAIL, string!(err).white())
+            }
+        }
+        None => crashln!("{} Impossible to get your home directory", *helpers::FAIL),
+    }
+}
+
+#[derive(Debug)]
+struct ServerOption {
+    name: String,
+    formatted: String,
+}
+
+impl std::fmt::Display for ServerOption {
+    #[inline]
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(&self.formatted, f) }
+}
+
+pub fn list(format: &String, log_level: Option<log::Level>) {
+    let servers = config::servers().servers.take().unwrap_or_else(BTreeMap::new);
+
+    let options: Vec<_> = servers
+        .iter()
+        .map(|(key, server)| {
+            let verbose = match log_level {
+                Some(_) => format!("({})", server.address),
+                None => string!(),
+            };
+
+            ServerOption {
+                name: key.clone(),
+                formatted: format!("{} {}", format!("{key}").bright_yellow(), verbose.white()),
+            }
+        })
+        .collect();
+
+    match Select::new("Select a server:", options).prompt() {
+        Ok(server) => super::list(format, &server.name),
+        Err(_) => crashln!("{}", "Canceled...".white()),
+    }
+}
+
+pub fn new() {
+    let (name, address, token);
+    let mut servers = config::servers().servers.take().unwrap_or_else(BTreeMap::new);
+
+    match Text::new("Server Name:").prompt() {
+        Ok(ans) => name = ans,
+        Err(_) => crashln!("{}", "Canceled...".white()),
+    }
+
+    match Text::new("Server Address:").prompt() {
+        Ok(ans) => address = ans,
+        Err(_) => crashln!("{}", "Canceled...".white()),
+    }
+
+    match Password::new("Server Token:")
+        .with_display_toggle_enabled()
+        .with_formatter(&|_| String::from("[hidden]"))
+        .with_display_mode(PasswordDisplayMode::Masked)
+        .without_confirmation()
+        .prompt()
+    {
+        Ok(ans) => match ans.as_str() {
+            "" => token = None,
+            ans => token = Some(string!(ans)),
+        },
+        Err(_) => crashln!("{}", "Canceled...".white()),
+    }
+
+    match Confirm::new("Add server? (y/n)").prompt() {
+        Err(_) => crashln!("{}", "Canceled...".white()),
+        Ok(false) => {}
+        Ok(true) => {
+            if name == "" || address == "" {
+                crashln!("{} Failed to add new server", *helpers::FAIL)
+            } else {
+                servers.insert(name, Server { address, token });
+                save(servers);
+                println!("{} Added new server", *helpers::SUCCESS)
+            }
+        }
+    }
+}
+
+pub fn remove(name: &String) {
+    let mut servers = config::servers().servers.take().unwrap_or_else(BTreeMap::new);
+
+    if servers.contains_key(name) {
+        match Confirm::new(&format!("Remove server {name}? (y/n)")).prompt() {
+            Err(_) => crashln!("{}", "Canceled...".white()),
+            Ok(false) => {}
+            Ok(true) => {
+                servers.remove(name);
+                save(servers);
+                println!("{} Removed server (name={name})", *helpers::SUCCESS);
+            }
+        }
+    } else {
+        println!("{} Server {name} does not exist", *helpers::FAIL);
+    }
+}
+
+pub fn default(name: &Option<String>) {
+    let servers = config::servers().servers.take().unwrap_or_else(BTreeMap::new);
+
+    let name = match name {
+        Some(name) => name.as_str(),
+        None => "local",
+    };
+
+    if servers.contains_key(name) || name == "internal" || name == "local" {
+        config::read().set_default(string!(name)).save();
+        println!("{} Set default server to {name}", *helpers::SUCCESS)
+    } else {
+        println!("{} Server {name} does not exist", *helpers::FAIL);
+    }
+}
diff --git a/src/config/mod.rs b/src/config/mod.rs
index f9e7928..ecc3d13 100644
--- a/src/config/mod.rs
+++ b/src/config/mod.rs
@@ -1,112 +1,137 @@
 pub mod structs;
 
 use crate::{
     file::{self, Exists},
     helpers,
     process::RemoteConfig,
 };
 
 use colored::Colorize;
 use macros_rs::{crashln, fmtstr, string};
 use reqwest::blocking::Client;
 use reqwest::header::{HeaderMap, HeaderValue};
 use std::fs::write;
 use std::net::{IpAddr, Ipv4Addr};
 use structs::{Config, Daemon, Runner, Secure, Servers, Web};
 
 pub fn from(address: &str, token: Option<&str>) -> Result<RemoteConfig, anyhow::Error> {
     let client = Client::new();
     let mut headers = HeaderMap::new();
 
     if let Some(token) = token {
         headers.insert("token", HeaderValue::from_static(Box::leak(Box::from(token))));
     }
 
     let response = client.get(fmtstr!("{address}/daemon/config")).headers(headers).send()?;
     let json = response.json::<RemoteConfig>()?;
 
     Ok(json)
 }
 
 pub fn read() -> Config {
     match home::home_dir() {
         Some(path) => {
             let path = path.display();
 
             let config_path = format!("{path}/.pmc/config.toml");
 
             if !Exists::check(&config_path).file() {
                 let config = Config {
+                    default: string!("local"),
                     runner: Runner {
                         shell: string!("bash"),
                         args: vec![string!("-c")],
                         node: string!("node"),
                         log_path: format!("{path}/.pmc/logs"),
                     },
                     daemon: Daemon {
                         restarts: 10,
                         interval: 1000,
                         kind: string!("default"),
                         web: Web {
                             ui: false,
                             api: false,
                             address: string!("0.0.0.0"),
                             path: None,
                             port: 5630,
                             secure: Secure { enabled: false, token: string!("") },
                         },
                     },
                 };
 
                 let contents = match toml::to_string(&config) {
                     Ok(contents) => contents,
                     Err(err) => crashln!("{} Cannot parse config.\n{}", *helpers::FAIL, string!(err).white()),
                 };
 
                 if let Err(err) = write(&config_path, contents) {
                     crashln!("{} Error writing config.\n{}", *helpers::FAIL, string!(err).white())
                 }
                 log::info!("created config file");
             }
 
             file::read(config_path)
         }
         None => crashln!("{} Impossible to get your home directory", *helpers::FAIL),
     }
 }
 
 pub fn servers() -> Servers {
     match home::home_dir() {
         Some(path) => {
             let path = path.display();
             let config_path = format!("{path}/.pmc/servers.toml");
 
             if !Exists::check(&config_path).file() {
                 if let Err(err) = write(&config_path, "") {
                     crashln!("{} Error writing servers.\n{}", *helpers::FAIL, string!(err).white())
                 }
             }
 
             file::read(config_path)
         }
         None => crashln!("{} Impossible to get your home directory", *helpers::FAIL),
     }
 }
 
 impl Config {
     pub fn get_address(&self) -> rocket::figment::Figment {
         let config_split: Vec<u8> = match self.daemon.web.address.as_str() {
             "localhost" => vec![127, 0, 0, 1],
             _ => self.daemon.web.address.split('.').map(|part| part.parse().expect("Failed to parse address part")).collect(),
         };
 
         let ipv4_address: Ipv4Addr = Ipv4Addr::from([config_split[0], config_split[1], config_split[2], config_split[3]]);
         let ip_address: IpAddr = IpAddr::from(ipv4_address);
 
         rocket::Config::figment().merge(("port", self.daemon.web.port)).merge(("address", ip_address))
     }
 
+    pub fn save(&self) {
+        match home::home_dir() {
+            Some(path) => {
+                let path = path.display();
+                let config_path = format!("{path}/.pmc/config.toml");
+
+                let contents = match toml::to_string(&self) {
+                    Ok(contents) => contents,
+                    Err(err) => crashln!("{} Cannot parse config.\n{}", *helpers::FAIL, string!(err).white()),
+                };
+
+                if let Err(err) = write(&config_path, contents) {
+                    crashln!("{} Error writing config.\n{}", *helpers::FAIL, string!(err).white())
+                }
+            }
+            None => crashln!("{} Impossible to get your home directory", *helpers::FAIL),
+        }
+    }
+
+    pub fn set_default(mut self, name: String) -> Self {
+        self.default = string!(name);
+        self
+    }
+
     pub fn fmt_address(&self) -> String { format!("{}:{}", self.daemon.web.address.clone(), self.daemon.web.port) }
 
     pub fn get_path(&self) -> String { self.daemon.web.path.clone().unwrap_or(string!("/")) }
 }
diff --git a/src/config/structs.rs b/src/config/structs.rs
index c20ab31..879990f 100644
--- a/src/config/structs.rs
+++ b/src/config/structs.rs
@@ -1,60 +1,61 @@
 use serde::{Deserialize, Serialize};
 use std::collections::BTreeMap;
 
 #[derive(Debug, Deserialize, Serialize)]
 pub struct Config {
+    pub default: String,
     pub runner: Runner,
     pub daemon: Daemon,
 }
 
 #[derive(Debug, Deserialize, Serialize)]
 pub struct Runner {
     pub shell: String,
     pub args: Vec<String>,
     pub node: String,
     pub log_path: String,
 }
 
 #[derive(Debug, Deserialize, Serialize)]
 pub struct Daemon {
     pub restarts: u64,
     pub interval: u64,
     pub kind: String,
     pub web: Web,
 }
 
 #[derive(Debug, Deserialize, Serialize)]
 pub struct Web {
     pub ui: bool,
     pub api: bool,
     pub address: String,
     pub port: u64,
     pub secure: Secure,
     pub path: Option<String>,
 }
 
 #[derive(Debug, Deserialize, Serialize)]
 pub struct Secure {
     pub enabled: bool,
     pub token: String,
 }
 
 #[derive(Debug, Deserialize, Serialize)]
 pub struct Servers {
     pub servers: Option<BTreeMap<String, Server>>,
 }
 
 #[derive(Clone, Debug, Deserialize, Serialize)]
 pub struct Server {
     pub address: String,
     pub token: Option<String>,
 }
 
 impl Server {
     pub fn get(&self) -> Self {
         Self {
             token: self.token.clone(),
             address: self.address.trim_end_matches('/').to_string(),
         }
     }
 }
diff --git a/src/daemon/api/routes.rs b/src/daemon/api/routes.rs
index cd71588..81e6400 100644
--- a/src/daemon/api/routes.rs
+++ b/src/daemon/api/routes.rs
@@ -1,834 +1,834 @@
 #![allow(non_snake_case)]
 
 use chrono::{DateTime, Utc};
 use global_placeholders::global;
 use macros_rs::{fmtstr, string, ternary, then};
 use prometheus::{Encoder, TextEncoder};
 use psutil::process::{MemoryInfo, Process};
 use reqwest::header::HeaderValue;
 use serde::Deserialize;
 use tera::{Context, Tera};
 use utoipa::ToSchema;
 
 use rocket::{
     get,
     http::{ContentType, Status},
     post,
     serde::{json::Json, Serialize},
     State,
 };
 
 use super::{
     helpers::{generic_error, not_found, GenericError, NotFound},
     structs::ErrorMessage,
     EnableWebUI, TeraState,
 };
 
 use pmc::{
     config, file, helpers,
     process::{dump, http::client, ItemSingle, ProcessItem, Runner},
 };
 
 use crate::daemon::{
     api::{HTTP_COUNTER, HTTP_REQ_HISTOGRAM},
     pid,
 };
 
 use std::{
     collections::BTreeMap,
     env,
     fs::{self, File},
     io::{self, BufRead, BufReader},
     path::PathBuf,
 };
 
 pub(crate) struct Token;
 type EnvList = Json<BTreeMap<String, String>>;
 
 #[allow(dead_code)]
 #[derive(ToSchema)]
 #[schema(as = MemoryInfo)]
 pub(crate) struct DocMemoryInfo {
     rss: u64,
     vms: u64,
     #[cfg(target_os = "linux")]
     shared: u64,
     #[cfg(target_os = "linux")]
     text: u64,
     #[cfg(target_os = "linux")]
     data: u64,
     #[cfg(target_os = "macos")]
     page_faults: u64,
     #[cfg(target_os = "macos")]
     pageins: u64,
 }
 
 #[derive(Serialize, Deserialize, ToSchema)]
 pub(crate) struct ActionBody {
     #[schema(example = "restart")]
     method: String,
 }
 
 #[derive(Serialize, ToSchema)]
 pub(crate) struct ConfigBody {
     #[schema(example = "bash")]
     shell: String,
     #[schema(min_items = 1, example = json!(["-c"]))]
     args: Vec<String>,
     #[schema(example = "/home/user/.pmc/logs")]
     log_path: String,
 }
 
 #[derive(Serialize, Deserialize, ToSchema)]
 pub(crate) struct CreateBody {
     #[schema(example = "app")]
     name: Option<String>,
     #[schema(example = "node index.js")]
     script: String,
     #[schema(value_type = String, example = "/projects/app")]
     path: PathBuf,
     #[schema(example = "src")]
     watch: Option<String>,
 }
 
 #[derive(Serialize, Deserialize, ToSchema)]
 pub(crate) struct ActionResponse {
     #[schema(example = true)]
     done: bool,
     #[schema(example = "name")]
     action: String,
 }
 
 #[derive(Serialize, Deserialize, ToSchema)]
 pub(crate) struct LogResponse {
     logs: Vec<String>,
 }
 
 #[derive(Serialize, ToSchema)]
 pub struct MetricsRoot {
     pub version: Version,
     pub daemon: Daemon,
 }
 
 #[derive(Serialize, ToSchema)]
 pub struct Version {
     #[schema(example = "v1.0.0")]
     pub pkg: String,
-    pub hash: &'static str,
+    pub hash: Option<&'static str>,
     #[schema(example = "2000-01-01")]
     pub build_date: &'static str,
     #[schema(example = "release")]
     pub target: &'static str,
 }
 
 #[derive(Serialize, ToSchema)]
 pub struct Daemon {
     pub pid: Option<i32>,
     #[schema(example = true)]
     pub running: bool,
     pub uptime: String,
     pub process_count: usize,
     #[schema(example = "default")]
     pub daemon_type: String,
     pub stats: Stats,
 }
 
 #[derive(Serialize, ToSchema)]
 pub struct Stats {
     pub memory_usage: String,
     pub cpu_percent: String,
 }
 
 fn attempt(done: bool, method: &str) -> ActionResponse {
     ActionResponse {
         done,
         action: ternary!(done, Box::leak(Box::from(method)), "DOES_NOT_EXIST").to_string(),
     }
 }
 
 fn render(name: &str, tmpl: &Tera, ctx: &Context) -> Result<String, NotFound> { tmpl.render(name, &ctx).or(Err(not_found("Page was not found"))) }
 
 #[get("/")]
 pub async fn dashboard(state: &State<TeraState>, _webui: EnableWebUI) -> Result<(ContentType, String), NotFound> {
     let mut ctx = Context::new();
 
     ctx.insert("base_path", &state.path);
     let payload = render("dashboard", &state.tera, &ctx)?;
     Ok((ContentType::HTML, payload))
 }
 
 #[get("/login")]
 pub async fn login(state: &State<TeraState>, _webui: EnableWebUI) -> Result<(ContentType, String), NotFound> {
     let mut ctx = Context::new();
 
     ctx.insert("base_path", &state.path);
     let payload = render("login", &state.tera, &ctx)?;
     Ok((ContentType::HTML, payload))
 }
 
 #[get("/view/<id>")]
 pub async fn view_process(id: usize, state: &State<TeraState>, _webui: EnableWebUI) -> Result<(ContentType, String), NotFound> {
     let mut ctx = Context::new();
 
     ctx.insert("base_path", &state.path);
     ctx.insert("process_id", &id);
 
     let payload = render("view", &state.tera, &ctx)?;
     Ok((ContentType::HTML, payload))
 }
 
 #[get("/daemon/prometheus")]
 #[utoipa::path(get, tag = "Daemon", path = "/daemon/prometheus", security((), ("api_key" = [])),
     responses(
         (
             description = "Get prometheus metrics", body = String, status = 200,
             example = json!("# HELP daemon_cpu_percentage The cpu usage graph of the daemon.\n# TYPE daemon_cpu_percentage histogram\ndaemon_cpu_percentage_bucket{le=\"0.005\"} 0"),
         ),
         (
             status = UNAUTHORIZED, description = "Authentication failed or not provided", body = ErrorMessage, 
             example = json!({"code": 401, "message": "Unauthorized"})
         )
     )
 )]
 pub async fn prometheus_handler(_t: Token) -> String {
     let encoder = TextEncoder::new();
     let mut buffer = Vec::<u8>::new();
     let metric_families = prometheus::gather();
 
     encoder.encode(&metric_families, &mut buffer).unwrap();
     String::from_utf8(buffer.clone()).unwrap()
 }
 
 #[get("/daemon/servers")]
 #[utoipa::path(get, tag = "Daemon", path = "/daemon/servers", security((), ("api_key" = [])),
     responses(
         (status = 200, description = "Get daemon servers successfully", body = [String]),
         (
             status = UNAUTHORIZED, description = "Authentication failed or not provided", body = ErrorMessage, 
             example = json!({"code": 401, "message": "Unauthorized"})
         )
     )
 )]
 pub async fn servers_handler(_t: Token) -> Result<Json<Vec<String>>, GenericError> {
     let timer = HTTP_REQ_HISTOGRAM.with_label_values(&["servers"]).start_timer();
 
     if let Some(servers) = config::servers().servers {
         HTTP_COUNTER.inc();
         timer.observe_duration();
 
         Ok(Json(servers.into_keys().collect()))
     } else {
         Err(generic_error(Status::BadRequest, string!("No servers have been added")))
     }
 }
 
 #[get("/remote/<name>/list")]
 #[utoipa::path(get, tag = "Remote", path = "/remote/{name}/list", security((), ("api_key" = [])),
     params(("name" = String, Path, description = "Name of remote daemon", example = "example"),),
     responses(
         (status = 200, description = "Get list from remote daemon successfully", body = [ProcessItem]),
         (status = NOT_FOUND, description = "Remote daemon does not exist", body = ErrorMessage),
         (
             status = UNAUTHORIZED, description = "Authentication failed or not provided", body = ErrorMessage, 
             example = json!({"code": 401, "message": "Unauthorized"})
         )
     )
 )]
 pub async fn remote_list(name: String, _t: Token) -> Result<Json<Vec<ProcessItem>>, GenericError> {
     let timer = HTTP_REQ_HISTOGRAM.with_label_values(&["list"]).start_timer();
 
     if let Some(servers) = config::servers().servers {
         let (address, (client, headers)) = match servers.get(&name) {
             Some(server) => (&server.address, client(&server.token).await),
             None => return Err(generic_error(Status::NotFound, string!("Server was not found"))),
         };
 
         HTTP_COUNTER.inc();
         timer.observe_duration();
 
         match client.get(fmtstr!("{address}/list")).headers(headers).send().await {
             Ok(data) => {
                 if data.status() != 200 {
                     let err = data.json::<ErrorMessage>().await.unwrap();
                     Err(generic_error(err.code, err.message))
                 } else {
                     Ok(Json(data.json::<Vec<ProcessItem>>().await.unwrap()))
                 }
             }
             Err(err) => Err(generic_error(Status::InternalServerError, err.to_string())),
         }
     } else {
         Err(generic_error(Status::BadRequest, string!("No servers have been added")))
     }
 }
 
 #[get("/remote/<name>/info/<id>")]
 #[utoipa::path(get, tag = "Remote", path = "/remote/{name}/info/{id}", security((), ("api_key" = [])),
     params(
         ("name" = String, Path, description = "Name of remote daemon", example = "example"),
         ("id" = usize, Path, description = "Process id to get information for", example = 0)
     ),
     responses(
         (status = 200, description = "Get process info from remote daemon successfully", body = [ProcessItem]),
         (status = NOT_FOUND, description = "Remote daemon does not exist", body = ErrorMessage),
         (
             status = UNAUTHORIZED, description = "Authentication failed or not provided", body = ErrorMessage, 
             example = json!({"code": 401, "message": "Unauthorized"})
         )
     )
 )]
 pub async fn remote_info(name: String, id: usize, _t: Token) -> Result<Json<ItemSingle>, GenericError> {
     let timer = HTTP_REQ_HISTOGRAM.with_label_values(&["info"]).start_timer();
 
     if let Some(servers) = config::servers().servers {
         let (address, (client, headers)) = match servers.get(&name) {
             Some(server) => (&server.address, client(&server.token).await),
             None => return Err(generic_error(Status::NotFound, string!("Server was not found"))),
         };
 
         HTTP_COUNTER.inc();
         timer.observe_duration();
 
         match client.get(fmtstr!("{address}/process/{id}/info")).headers(headers).send().await {
             Ok(data) => {
                 if data.status() != 200 {
                     let err = data.json::<ErrorMessage>().await.unwrap();
                     Err(generic_error(err.code, err.message))
                 } else {
                     Ok(Json(data.json::<ItemSingle>().await.unwrap()))
                 }
             }
             Err(err) => Err(generic_error(Status::InternalServerError, err.to_string())),
         }
     } else {
         Err(generic_error(Status::BadRequest, string!("No servers have been added")))
     }
 }
 
 #[get("/remote/<name>/logs/<id>/<kind>")]
 #[utoipa::path(get, tag = "Remote", path = "/remote/{name}/logs/{id}/{kind}", security((), ("api_key" = [])),
     params(
         ("name" = String, Path, description = "Name of remote daemon", example = "example"),
         ("id" = usize, Path, description = "Process id to get information for", example = 0),
         ("kind" = String, Path, description = "Log output type", example = "out")
     ),
     responses(
         (status = 200, description = "Remote process logs of {type} fetched", body = LogResponse),
         (status = NOT_FOUND, description = "Remote daemon does not exist", body = ErrorMessage),
         (
             status = UNAUTHORIZED, description = "Authentication failed or not provided", body = ErrorMessage, 
             example = json!({"code": 401, "message": "Unauthorized"})
         )
     )
 )]
 pub async fn remote_logs(name: String, id: usize, kind: String, _t: Token) -> Result<Json<LogResponse>, GenericError> {
     let timer = HTTP_REQ_HISTOGRAM.with_label_values(&["info"]).start_timer();
 
     if let Some(servers) = config::servers().servers {
         let (address, (client, headers)) = match servers.get(&name) {
             Some(server) => (&server.address, client(&server.token).await),
             None => return Err(generic_error(Status::NotFound, string!("Server was not found"))),
         };
 
         HTTP_COUNTER.inc();
         timer.observe_duration();
 
         match client.get(fmtstr!("{address}/process/{id}/logs/{kind}")).headers(headers).send().await {
             Ok(data) => {
                 if data.status() != 200 {
                     let err = data.json::<ErrorMessage>().await.unwrap();
                     Err(generic_error(err.code, err.message))
                 } else {
                     Ok(Json(data.json::<LogResponse>().await.unwrap()))
                 }
             }
             Err(err) => Err(generic_error(Status::InternalServerError, err.to_string())),
         }
     } else {
         Err(generic_error(Status::BadRequest, string!("No servers have been added")))
     }
 }
 
 #[post("/remote/<name>/rename/<id>", format = "text", data = "<body>")]
 #[utoipa::path(post, tag = "Remote", path = "/remote/{name}/rename/{id}", 
     security((), ("api_key" = [])),
     request_body(content = String, example = json!("example_name")), 
     params(
         ("id" = usize, Path, description = "Process id to rename", example = 0),
         ("name" = String, Path, description = "Name of remote daemon", example = "example"),
     ),
     responses(
         (
             description = "Remote rename process successful", body = ActionResponse,
             example = json!({"action": "rename", "done": true }), status = 200,
         ),
         (status = NOT_FOUND, description = "Process was not found", body = ErrorMessage),
         (
             status = UNAUTHORIZED, description = "Authentication failed or not provided", body = ErrorMessage, 
             example = json!({"code": 401, "message": "Unauthorized"})
         )
     )
 )]
 pub async fn remote_rename(name: String, id: usize, body: String, _t: Token) -> Result<Json<ActionResponse>, GenericError> {
     let timer = HTTP_REQ_HISTOGRAM.with_label_values(&["rename"]).start_timer();
 
     if let Some(servers) = config::servers().servers {
         let (address, (client, mut headers)) = match servers.get(&name) {
             Some(server) => (&server.address, client(&server.token).await),
             None => return Err(generic_error(Status::NotFound, string!("Server was not found"))),
         };
 
         HTTP_COUNTER.inc();
         timer.observe_duration();
         headers.insert("content-type", HeaderValue::from_static("text/plain"));
 
         match client.post(fmtstr!("{address}/process/{id}/rename")).body(body).headers(headers).send().await {
             Ok(data) => {
                 if data.status() != 200 {
                     let err = data.json::<ErrorMessage>().await.unwrap();
                     Err(generic_error(err.code, err.message))
                 } else {
                     Ok(Json(data.json::<ActionResponse>().await.unwrap()))
                 }
             }
             Err(err) => Err(generic_error(Status::InternalServerError, err.to_string())),
         }
     } else {
         Err(generic_error(Status::BadRequest, string!("No servers have been added")))
     }
 }
 
 #[post("/remote/<name>/action/<id>", format = "json", data = "<body>")]
 #[utoipa::path(post, tag = "Remote", path = "/remote/{name}/action/{id}", request_body = ActionBody,
     security((), ("api_key" = [])),
     params(
         ("id" = usize, Path, description = "Process id to run action on", example = 0),
         ("name" = String, Path, description = "Name of remote daemon", example = "example")
     ),
     responses(
         (status = 200, description = "Run action on remote process successful", body = ActionResponse),
         (status = NOT_FOUND, description = "Process/action was not found", body = ErrorMessage),
         (
             status = UNAUTHORIZED, description = "Authentication failed or not provided", body = ErrorMessage, 
             example = json!({"code": 401, "message": "Unauthorized"})
         )
     )
 )]
 pub async fn remote_action(name: String, id: usize, body: Json<ActionBody>, _t: Token) -> Result<Json<ActionResponse>, GenericError> {
     let timer = HTTP_REQ_HISTOGRAM.with_label_values(&["action"]).start_timer();
 
     if let Some(servers) = config::servers().servers {
         let (address, (client, headers)) = match servers.get(&name) {
             Some(server) => (&server.address, client(&server.token).await),
             None => return Err(generic_error(Status::NotFound, string!("Server was not found"))),
         };
 
         HTTP_COUNTER.inc();
         timer.observe_duration();
 
         match client.post(fmtstr!("{address}/process/{id}/action")).json(&body.0).headers(headers).send().await {
             Ok(data) => {
                 if data.status() != 200 {
                     let err = data.json::<ErrorMessage>().await.unwrap();
                     Err(generic_error(err.code, err.message))
                 } else {
                     Ok(Json(data.json::<ActionResponse>().await.unwrap()))
                 }
             }
             Err(err) => Err(generic_error(Status::InternalServerError, err.to_string())),
         }
     } else {
         Err(generic_error(Status::BadRequest, string!("No servers have been added")))
     }
 }
 
 #[get("/daemon/dump")]
 #[utoipa::path(get, tag = "Daemon", path = "/daemon/dump", security((), ("api_key" = [])),
     responses(
         (status = 200, description = "Dump processes successfully", body = [u8]),
         (
             status = UNAUTHORIZED, description = "Authentication failed or not provided", body = ErrorMessage, 
             example = json!({"code": 401, "message": "Unauthorized"})
         )
     )
 )]
 pub async fn dump_handler(_t: Token) -> Vec<u8> {
     let timer = HTTP_REQ_HISTOGRAM.with_label_values(&["dump"]).start_timer();
 
     HTTP_COUNTER.inc();
     timer.observe_duration();
 
     dump::raw()
 }
 
 #[get("/daemon/config")]
 #[utoipa::path(get, tag = "Daemon", path = "/daemon/config", security((), ("api_key" = [])),
     responses(
         (status = 200, description = "Get daemon config successfully", body = ConfigBody),
         (
             status = UNAUTHORIZED, description = "Authentication failed or not provided", body = ErrorMessage, 
             example = json!({"code": 401, "message": "Unauthorized"})
         )
     )
 )]
 pub async fn config_handler(_t: Token) -> Json<ConfigBody> {
     let timer = HTTP_REQ_HISTOGRAM.with_label_values(&["dump"]).start_timer();
     let config = config::read().runner;
 
     HTTP_COUNTER.inc();
     timer.observe_duration();
 
     Json(ConfigBody {
         shell: config.shell,
         args: config.args,
         log_path: config.log_path,
     })
 }
 
 #[get("/list")]
 #[utoipa::path(get, path = "/list", tag = "Process", security((), ("api_key" = [])),
     responses(
         (status = 200, description = "List processes successfully", body = [ProcessItem]),
         (
             status = UNAUTHORIZED, description = "Authentication failed or not provided", body = ErrorMessage, 
             example = json!({"code": 401, "message": "Unauthorized"})
         )
     )
 )]
 pub async fn list_handler(_t: Token) -> Json<Vec<ProcessItem>> {
     let timer = HTTP_REQ_HISTOGRAM.with_label_values(&["list"]).start_timer();
     let data = Runner::new().fetch();
 
     HTTP_COUNTER.inc();
     timer.observe_duration();
 
     Json(data)
 }
 
 #[get("/process/<id>/logs/<kind>")]
 #[utoipa::path(get, tag = "Process", path = "/process/{id}/logs/{kind}", 
     security((), ("api_key" = [])),
     params(
         ("id" = usize, Path, description = "Process id to get logs for", example = 0),
         ("kind" = String, Path, description = "Log output type", example = "out")
     ),
     responses(
         (status = 200, description = "Process logs of {type} fetched", body = LogResponse),
         (status = NOT_FOUND, description = "Process was not found", body = ErrorMessage),
         (
             status = UNAUTHORIZED, description = "Authentication failed or not provided", body = ErrorMessage, 
             example = json!({"code": 401, "message": "Unauthorized"})
         )
     )
 )]
 pub async fn logs_handler(id: usize, kind: String, _t: Token) -> Result<Json<LogResponse>, NotFound> {
     let timer = HTTP_REQ_HISTOGRAM.with_label_values(&["log"]).start_timer();
 
     HTTP_COUNTER.inc();
     match Runner::new().info(id) {
         Some(item) => {
             let log_file = match kind.as_str() {
                 "out" | "stdout" => item.logs().out,
                 "error" | "stderr" => item.logs().error,
                 _ => item.logs().out,
             };
 
             match File::open(log_file) {
                 Ok(data) => {
                     let reader = BufReader::new(data);
                     let logs: Vec<String> = reader.lines().collect::<io::Result<_>>().unwrap();
 
                     timer.observe_duration();
                     Ok(Json(LogResponse { logs }))
                 }
                 Err(_) => Ok(Json(LogResponse { logs: vec![] })),
             }
         }
         None => {
             timer.observe_duration();
             Err(not_found("Process was not found"))
         }
     }
 }
 
 #[get("/process/<id>/logs/<kind>/raw")]
 #[utoipa::path(get, tag = "Process", path = "/process/{id}/logs/{kind}/raw", 
     security((), ("api_key" = [])),
     params(
         ("id" = usize, Path, description = "Process id to get logs for", example = 0),
         ("kind" = String, Path, description = "Log output type", example = "out")
     ),
     responses(
         (
             description = "Process logs of {type} fetched raw", body = String, status = 200,
             example = json!("# PATH path/of/file.log\nserver started on port 3000")
         ),
         (status = NOT_FOUND, description = "Process was not found", body = ErrorMessage),
         (
             status = UNAUTHORIZED, description = "Authentication failed or not provided", body = ErrorMessage, 
             example = json!({"code": 401, "message": "Unauthorized"})
         )
     )
 )]
 pub async fn logs_raw_handler(id: usize, kind: String, _t: Token) -> Result<String, NotFound> {
     let timer = HTTP_REQ_HISTOGRAM.with_label_values(&["log"]).start_timer();
 
     HTTP_COUNTER.inc();
     match Runner::new().info(id) {
         Some(item) => {
             let log_file = match kind.as_str() {
                 "out" | "stdout" => item.logs().out,
                 "error" | "stderr" => item.logs().error,
                 _ => item.logs().out,
             };
 
             let data = match fs::read_to_string(&log_file) {
                 Ok(data) => format!("# PATH {log_file}\n{data}"),
                 Err(err) => err.to_string(),
             };
 
             timer.observe_duration();
             Ok(data)
         }
         None => {
             timer.observe_duration();
             Err(not_found("Process was not found"))
         }
     }
 }
 
 #[get("/process/<id>/info")]
 #[utoipa::path(get, tag = "Process", path = "/process/{id}/info", security((), ("api_key" = [])),
     params(("id" = usize, Path, description = "Process id to get information for", example = 0)),
     responses(
         (status = 200, description = "Current process info retrieved", body = ItemSingle),
         (status = NOT_FOUND, description = "Process was not found", body = ErrorMessage),
         (
             status = UNAUTHORIZED, description = "Authentication failed or not provided", body = ErrorMessage, 
             example = json!({"code": 401, "message": "Unauthorized"})
         )
     )
 )]
 pub async fn info_handler(id: usize, _t: Token) -> Result<Json<ItemSingle>, NotFound> {
     let timer = HTTP_REQ_HISTOGRAM.with_label_values(&["info"]).start_timer();
     let runner = Runner::new();
 
     if runner.exists(id) {
         let item = runner.get(id);
         HTTP_COUNTER.inc();
         timer.observe_duration();
         Ok(Json(item.fetch()))
     } else {
         Err(not_found("Process was not found"))
     }
 }
 
 #[post("/process/create", format = "json", data = "<body>")]
 #[utoipa::path(post, tag = "Process", path = "/process/create", request_body(content = CreateBody), 
     security((), ("api_key" = [])),
     responses(
         (
             description = "Create process successful", body = ActionResponse,
             example = json!({"action": "create", "done": true }), status = 200,
         ),
         (status = INTERNAL_SERVER_ERROR, description = "Failed to create process", body = ErrorMessage),
         (
             status = UNAUTHORIZED, description = "Authentication failed or not provided", body = ErrorMessage, 
             example = json!({"code": 401, "message": "Unauthorized"})
         )
     )
 )]
 pub async fn create_handler(body: Json<CreateBody>, _t: Token) -> Result<Json<ActionResponse>, ()> {
     let timer = HTTP_REQ_HISTOGRAM.with_label_values(&["create"]).start_timer();
     let mut runner = Runner::new();
 
     HTTP_COUNTER.inc();
 
     let name = match &body.name {
         Some(name) => string!(name),
         None => string!(body.script.split_whitespace().next().unwrap_or_default()),
     };
 
     runner.start(&name, &body.script, body.path.clone(), &body.watch).save();
     timer.observe_duration();
 
     Ok(Json(attempt(true, "create")))
 }
 
 #[post("/process/<id>/rename", format = "text", data = "<body>")]
 #[utoipa::path(post, tag = "Process", path = "/process/{id}/rename", 
     security((), ("api_key" = [])),
     request_body(content = String, example = json!("example_name")), 
     params(("id" = usize, Path, description = "Process id to rename", example = 0)),
     responses(
         (
             description = "Rename process successful", body = ActionResponse,
             example = json!({"action": "rename", "done": true }), status = 200,
         ),
         (status = NOT_FOUND, description = "Process was not found", body = ErrorMessage),
         (
             status = UNAUTHORIZED, description = "Authentication failed or not provided", body = ErrorMessage, 
             example = json!({"code": 401, "message": "Unauthorized"})
         )
     )
 )]
 pub async fn rename_handler(id: usize, body: String, _t: Token) -> Result<Json<ActionResponse>, NotFound> {
     let timer = HTTP_REQ_HISTOGRAM.with_label_values(&["rename"]).start_timer();
     let runner = Runner::new();
 
     match runner.clone().info(id) {
         Some(process) => {
             HTTP_COUNTER.inc();
             let mut item = runner.get(id);
             item.rename(body.trim().replace("\n", ""));
             then!(process.running, item.restart());
             timer.observe_duration();
             Ok(Json(attempt(true, "rename")))
         }
         None => {
             timer.observe_duration();
             Err(not_found("Process was not found"))
         }
     }
 }
 
 #[get("/process/<id>/env")]
 #[utoipa::path(get, tag = "Process", path = "/process/{id}/env",
     params(("id" = usize, Path, description = "Process id to fetch env from", example = 0)),
     responses(
         (
             description = "Current process env", body = HashMap<String, String>,
             example = json!({"ENV_TEST_VALUE": "example_value"}), status = 200
         ),
         (status = NOT_FOUND, description = "Process was not found", body = ErrorMessage),
         (
             status = UNAUTHORIZED, description = "Authentication failed or not provided", body = ErrorMessage, 
             example = json!({"code": 401, "message": "Unauthorized"})
         )
     )
 )]
 pub async fn env_handler(id: usize, _t: Token) -> Result<EnvList, NotFound> {
     let timer = HTTP_REQ_HISTOGRAM.with_label_values(&["env"]).start_timer();
 
     HTTP_COUNTER.inc();
     match Runner::new().info(id) {
         Some(item) => {
             timer.observe_duration();
             Ok(Json(item.clone().env))
         }
         None => {
             timer.observe_duration();
             Err(not_found("Process was not found"))
         }
     }
 }
 
 #[post("/process/<id>/action", format = "json", data = "<body>")]
 #[utoipa::path(post, tag = "Process", path = "/process/{id}/action", request_body = ActionBody,
     security((), ("api_key" = [])),
     params(("id" = usize, Path, description = "Process id to run action on", example = 0)),
     responses(
         (status = 200, description = "Run action on process successful", body = ActionResponse),
         (status = NOT_FOUND, description = "Process/action was not found", body = ErrorMessage),
         (
             status = UNAUTHORIZED, description = "Authentication failed or not provided", body = ErrorMessage, 
             example = json!({"code": 401, "message": "Unauthorized"})
         )
     )
 )]
 pub async fn action_handler(id: usize, body: Json<ActionBody>, _t: Token) -> Result<Json<ActionResponse>, NotFound> {
     let timer = HTTP_REQ_HISTOGRAM.with_label_values(&["action"]).start_timer();
     let mut runner = Runner::new();
     let method = body.method.as_str();
 
     if runner.exists(id) {
         HTTP_COUNTER.inc();
         match method {
             "start" | "restart" => {
                 runner.restart(id, false);
                 timer.observe_duration();
                 Ok(Json(attempt(true, method)))
             }
             "stop" | "kill" => {
                 runner.get(id).stop();
                 timer.observe_duration();
                 Ok(Json(attempt(true, method)))
             }
             "remove" | "delete" => {
                 runner.remove(id);
                 timer.observe_duration();
                 Ok(Json(attempt(true, method)))
             }
             _ => {
                 timer.observe_duration();
                 Err(not_found("Process was not found"))
             }
         }
     } else {
         Err(not_found("Process was not found"))
     }
 }
 
 #[get("/daemon/metrics")]
 #[utoipa::path(get, tag = "Daemon", path = "/daemon/metrics", security((), ("api_key" = [])),
     responses(
         (status = 200, description = "Get daemon metrics", body = MetricsRoot),
         (
             status = UNAUTHORIZED, description = "Authentication failed or not provided", body = ErrorMessage, 
             example = json!({"code": 401, "message": "Unauthorized"})
         )
     )
 )]
 pub async fn metrics_handler(_t: Token) -> Json<MetricsRoot> {
     let timer = HTTP_REQ_HISTOGRAM.with_label_values(&["metrics"]).start_timer();
     let mut pid: Option<i32> = None;
     let mut cpu_percent: Option<f32> = None;
     let mut uptime: Option<DateTime<Utc>> = None;
     let mut memory_usage: Option<MemoryInfo> = None;
     let mut runner: Runner = file::read_object(global!("pmc.dump"));
 
     HTTP_COUNTER.inc();
     if pid::exists() {
         if let Ok(process_id) = pid::read() {
             if let Ok(mut process) = Process::new(process_id as u32) {
                 pid = Some(process_id);
                 uptime = Some(pid::uptime().unwrap());
                 memory_usage = process.memory_info().ok();
                 cpu_percent = process.cpu_percent().ok();
             }
         }
     }
 
     let memory_usage = match memory_usage {
         Some(usage) => helpers::format_memory(usage.rss()),
         None => string!("0b"),
     };
 
     let cpu_percent = match cpu_percent {
         Some(percent) => format!("{:.2}%", percent),
         None => string!("0%"),
     };
 
     let uptime = match uptime {
         Some(uptime) => helpers::format_duration(uptime),
         None => string!("none"),
     };
 
     timer.observe_duration();
     Json(MetricsRoot {
         version: Version {
-            pkg: format!("v{}", env!("CARGO_PKG_VERSION")),
-            hash: env!("GIT_HASH_FULL"),
-            build_date: env!("BUILD_DATE"),
             target: env!("PROFILE"),
+            build_date: env!("BUILD_DATE"),
+            pkg: format!("v{}", env!("CARGO_PKG_VERSION")),
+            hash: ternary!(env!("GIT_HASH_FULL") == "", None, Some(env!("GIT_HASH_FULL"))),
         },
         daemon: Daemon {
             pid,
             uptime,
             running: pid::exists(),
             process_count: runner.count(),
             daemon_type: global!("pmc.daemon.kind"),
             stats: Stats { memory_usage, cpu_percent },
         },
     })
 }
diff --git a/src/globals.rs b/src/globals.rs
index 84a2844..393c0d0 100644
--- a/src/globals.rs
+++ b/src/globals.rs
@@ -1,37 +1,44 @@
 use global_placeholders::init;
 use macros_rs::crashln;
 use pmc::{config, file::Exists, helpers};
 use std::fs;
 
-pub fn init() {
+pub(crate) fn init() {
     match home::home_dir() {
         Some(path) => {
             let path = path.display();
             if !Exists::check(&format!("{path}/.pmc/")).folder() {
                 fs::create_dir_all(format!("{path}/.pmc/")).unwrap();
                 log::info!("created pmc base dir");
             }
 
             let config = config::read();
             if !Exists::check(&config.runner.log_path).folder() {
                 fs::create_dir_all(&config.runner.log_path).unwrap();
                 log::info!("created pmc log dir");
             }
 
             init!("pmc.base", format!("{path}/.pmc/"));
             init!("pmc.log", format!("{path}/.pmc/pmc.log"));
             init!("pmc.pid", format!("{path}/.pmc/daemon.pid"));
             init!("pmc.dump", format!("{path}/.pmc/process.dump"));
 
             init!("pmc.daemon.kind", config.daemon.kind);
             init!("pmc.daemon.log", format!("{path}/.pmc/daemon.log"));
 
             let out = format!("{}/{{}}-out.log", config.runner.log_path);
             let error = format!("{}/{{}}-error.log", config.runner.log_path);
 
             init!("pmc.logs.out", out);
             init!("pmc.logs.error", error);
         }
         None => crashln!("{} Impossible to get your home directory", *helpers::FAIL),
     }
 }
+
+pub(crate) fn defaults(name: &Option<String>) -> String {
+    match name {
+        Some(name) => name.clone(),
+        None => config::read().default,
+    }
+}
diff --git a/src/helpers.rs b/src/helpers.rs
index 58fa211..5e57b29 100644
--- a/src/helpers.rs
+++ b/src/helpers.rs
@@ -1,57 +1,58 @@
 use chrono::{DateTime, Utc};
 use colored::Colorize;
 use core::fmt;
 use once_cell::sync::Lazy;
 use regex::Regex;
 
 pub static SUCCESS: Lazy<colored::ColoredString> = Lazy::new(|| "[PMC]".green());
 pub static FAIL: Lazy<colored::ColoredString> = Lazy::new(|| "[PMC]".red());
+pub static WARN: Lazy<colored::ColoredString> = Lazy::new(|| "[PMC]".yellow());
 
 #[derive(Clone, Debug)]
 pub struct ColoredString(pub colored::ColoredString);
 
 impl serde::Serialize for ColoredString {
     fn serialize<S: serde::ser::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
         let re = Regex::new(r"\x1B\[([0-9;]+)m").unwrap();
         let colored_string = &self.0;
         let stripped_string = re.replace_all(colored_string, "").to_string();
         serializer.serialize_str(&stripped_string)
     }
 }
 
 impl From<colored::ColoredString> for ColoredString {
     fn from(cs: colored::ColoredString) -> Self { ColoredString(cs) }
 }
 
 impl fmt::Display for ColoredString {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) }
 }
 
 pub fn format_duration(datetime: DateTime<Utc>) -> String {
     let current_time = Utc::now();
     let duration = current_time.signed_duration_since(datetime);
 
     match duration.num_seconds() {
         s if s >= 86400 => format!("{}d", s / 86400),
         s if s >= 3600 => format!("{}h", s / 3600),
         s if s >= 60 => format!("{}m", s / 60),
         s => format!("{}s", s),
     }
 }
 
 pub fn format_memory(bytes: u64) -> String {
     const UNIT: f64 = 1024.0;
     const SUFFIX: [&str; 4] = ["b", "kb", "mb", "gb"];
 
     let size = bytes as f64;
     let base = size.log10() / UNIT.log10();
 
     if size <= 0.0 {
         return "0b".to_string();
     }
 
     let mut buffer = ryu::Buffer::new();
     let result = buffer.format((UNIT.powf(base - base.floor()) * 10.0).round() / 10.0).trim_end_matches(".0");
 
     [result, SUFFIX[base.floor() as usize]].join("")
 }
diff --git a/src/main.rs b/src/main.rs
index fdad3b7..f7d2ef2 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,176 +1,222 @@
 mod cli;
 mod daemon;
 mod globals;
 mod webui;
 
-use crate::cli::Args;
+use crate::{cli::Args, globals::defaults};
 use clap::{Parser, Subcommand};
 use clap_verbosity_flag::{LogLevel, Verbosity};
 use macros_rs::{str, string, then};
+use update_informer::{registry, Check};
 
 fn validate_id_script(s: &str) -> Result<Args, String> {
     if let Ok(id) = s.parse::<usize>() {
         Ok(Args::Id(id))
     } else {
         Ok(Args::Script(s.to_owned()))
     }
 }
 
 #[derive(Copy, Clone, Debug, Default)]
 struct NoneLevel;
 impl LogLevel for NoneLevel {
     fn default() -> Option<log::Level> { None }
 }
 
 #[derive(Parser)]
 #[command(version = str!(cli::get_version(false)))]
 struct Cli {
-    /// test
     #[command(subcommand)]
     command: Commands,
     #[clap(flatten)]
     verbose: Verbosity<NoneLevel>,
 }
 
 #[derive(Subcommand)]
 enum Daemon {
     /// Reset process index
     #[command(visible_alias = "clean")]
     Reset,
     /// Stop daemon
     #[command(visible_alias = "kill")]
     Stop,
     /// Restart daemon
     #[command(visible_alias = "restart", visible_alias = "start")]
     Restore {
         /// Daemon api
         #[arg(long)]
         api: bool,
         /// WebUI using api
         #[arg(long)]
         webui: bool,
     },
     /// Check daemon
     #[command(visible_alias = "info", visible_alias = "status")]
     Health {
         /// Format output
         #[arg(long, default_value_t = string!("default"))]
         format: String,
     },
 }
 
+#[derive(Subcommand)]
+enum Server {
+    /// Add new server
+    #[command(visible_alias = "add")]
+    New,
+    /// List servers
+    #[command(visible_alias = "ls")]
+    List {
+        /// Format output
+        #[arg(long, default_value_t = string!("default"))]
+        format: String,
+    },
+    /// Remove server
+    #[command(visible_alias = "rm")]
+    Remove {
+        /// Server name
+        name: String,
+    },
+    /// Set default server
+    #[command(visible_alias = "set")]
+    Default {
+        /// Server name
+        name: Option<String>,
+    },
+}
+
 // add pmc restore command
 #[derive(Subcommand)]
 enum Commands {
     /// Start/Restart a process
     #[command(visible_alias = "restart")]
     Start {
         /// Process name
         #[arg(long)]
         name: Option<String>,
         #[clap(value_parser = validate_id_script)]
         args: Option<Args>,
         /// Watch to reload path
         #[arg(long)]
         watch: Option<String>,
         /// Server
-        #[arg(short, long, default_value_t = string!("internal"))]
-        server: String,
+        #[arg(short, long)]
+        server: Option<String>,
     },
 
     /// Stop/Kill a process
     #[command(visible_alias = "kill")]
     Stop {
         id: usize,
         /// Server
-        #[arg(short, long, default_value_t = string!("internal"))]
-        server: String,
+        #[arg(short, long)]
+        server: Option<String>,
     },
 
     /// Stop then remove a process
     #[command(visible_alias = "rm")]
     Remove {
         id: usize,
         /// Server
-        #[arg(short, long, default_value_t = string!("internal"))]
-        server: String,
+        #[arg(short, long)]
+        server: Option<String>,
     },
 
     /// Get env of a process
     #[command(visible_alias = "cmdline")]
     Env {
         id: usize,
         /// Server
-        #[arg(short, long, default_value_t = string!("internal"))]
-        server: String,
+        #[arg(short, long)]
+        server: Option<String>,
     },
 
     /// Get information of a process
     #[command(visible_alias = "info")]
     Details {
         id: usize,
         /// Format output
         #[arg(long, default_value_t = string!("default"))]
         format: String,
         /// Server
-        #[arg(short, long, default_value_t = string!("internal"))]
-        server: String,
+        #[arg(short, long)]
+        server: Option<String>,
     },
 
     /// List all processes
     #[command(visible_alias = "ls")]
     List {
         /// Format output
         #[arg(long, default_value_t = string!("default"))]
         format: String,
         /// Server
-        #[arg(short, long, default_value_t = string!("all"))]
-        server: String,
+        #[arg(short, long)]
+        server: Option<String>,
     },
 
     /// Get logs from a process
     Logs {
         id: usize,
         #[arg(long, default_value_t = 15, help = "")]
         lines: usize,
         /// Server
-        #[arg(short, long, default_value_t = string!("internal"))]
-        server: String,
+        #[arg(short, long)]
+        server: Option<String>,
     },
 
     /// Daemon management
+    #[command(visible_alias = "agent", visible_alias = "bgd")]
     Daemon {
         #[command(subcommand)]
         command: Daemon,
     },
+
+    /// Server management
+    #[command(visible_alias = "remote", visible_alias = "srv")]
+    Server {
+        #[command(subcommand)]
+        command: Server,
+    },
 }
 
 fn main() {
     let cli = Cli::parse();
     let mut env = env_logger::Builder::new();
     let level = cli.verbose.log_level_filter();
+    let informer = update_informer::new(registry::Crates, "pmc", env!("CARGO_PKG_VERSION"));
+
+    if let Some(version) = informer.check_version().ok().flatten() {
+        println!("{} New version is available: {version}", *pmc::helpers::WARN);
+    }
 
     globals::init();
     env.filter_level(level).init();
 
     match &cli.command {
-        Commands::Start { name, args, watch, server } => cli::start(name, args, watch, server),
-        Commands::Stop { id, server } => cli::stop(id, server),
-        Commands::Remove { id, server } => cli::remove(id, server),
-        Commands::Env { id, server } => cli::env(id, server),
-        Commands::Details { id, format, server } => cli::info(id, format, server),
-        Commands::List { format, server } => cli::list(format, server),
-        Commands::Logs { id, lines, server } => cli::logs(id, lines, server),
+        Commands::Start { name, args, watch, server } => cli::start(name, args, watch, &defaults(server)),
+        Commands::Stop { id, server } => cli::stop(id, &defaults(server)),
+        Commands::Remove { id, server } => cli::remove(id, &defaults(server)),
+        Commands::Env { id, server } => cli::env(id, &defaults(server)),
+        Commands::Details { id, format, server } => cli::info(id, format, &defaults(server)),
+        Commands::List { format, server } => cli::list(format, &defaults(server)),
+        Commands::Logs { id, lines, server } => cli::logs(id, lines, &defaults(server)),
 
         Commands::Daemon { command } => match command {
             Daemon::Stop => daemon::stop(),
             Daemon::Reset => daemon::reset(),
             Daemon::Health { format } => daemon::health(format),
             Daemon::Restore { api, webui } => daemon::restart(api, webui, level.as_str() != "OFF"),
         },
+
+        Commands::Server { command } => match command {
+            Server::New => cli::server::new(),
+            Server::Remove { name } => cli::server::remove(name),
+            Server::Default { name } => cli::server::default(name),
+            Server::List { format } => cli::server::list(format, cli.verbose.log_level()),
+        },
     };
 
-    if !matches!(&cli.command, Commands::Daemon { .. }) {
+    if !matches!(&cli.command, Commands::Daemon { .. }) && !matches!(&cli.command, Commands::Server { .. }) {
         then!(!daemon::pid::exists(), daemon::restart(&false, &false, false));
     }
 }
diff --git a/src/process/unix.rs b/src/process/unix.rs
index 26594b0..9f1cb98 100644
--- a/src/process/unix.rs
+++ b/src/process/unix.rs
@@ -1,47 +1,48 @@
 use std::ffi::{CStr, OsString};
 use std::os::unix::prelude::OsStringExt;
 
 pub struct Vars {
     inner: std::vec::IntoIter<OsString>,
 }
+
 impl Iterator for Vars {
     type Item = String;
     fn next(&mut self) -> Option<String> { self.inner.next().map(|var| var.into_string().unwrap()) }
     fn size_hint(&self) -> (usize, Option<usize>) { self.inner.size_hint() }
 }
 
 #[cfg(target_os = "macos")]
 unsafe fn environ() -> *mut *const *const libc::c_char { libc::_NSGetEnviron() as *mut *const *const libc::c_char }
 
 #[cfg(not(target_os = "macos"))]
 unsafe fn environ() -> *mut *const *const libc::c_char {
     extern "C" {
         static mut environ: *const *const libc::c_char;
     }
     ptr::addr_of_mut!(environ)
 }
 
 pub fn env() -> Vec<String> {
     unsafe {
         let mut environ = *environ();
         let mut result = Vec::new();
 
         if !environ.is_null() {
             while !(*environ).is_null() {
                 if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
                     result.push(key_value);
                 }
                 environ = environ.add(1);
             }
         }
 
         return Vars { inner: result.into_iter() }.collect();
     }
 
     fn parse(input: &[u8]) -> Option<OsString> {
         if input.is_empty() {
             return None;
         }
         Some(OsString::from_vec(input.to_vec()))
     }
 }