diff --git a/Cargo.lock b/Cargo.lock
index fb809d9..ab4c1ab 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -31,7 +31,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
 dependencies = [
  "cfg-if",
- "getrandom 0.2.15",
+ "getrandom 0.2.16",
  "once_cell",
  "version_check",
  "zerocopy 0.7.35",
@@ -173,14 +173,15 @@ dependencies = [
 
 [[package]]
 name = "async-executor"
-version = "1.13.1"
+version = "1.13.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec"
+checksum = "bb812ffb58524bdd10860d7d974e2f01cc0950c2438a74ee5ec2e2280c6c4ffa"
 dependencies = [
  "async-task",
  "concurrent-queue",
  "fastrand",
  "futures-lite",
+ "pin-project-lite",
  "slab",
 ]
 
@@ -300,7 +301,7 @@ dependencies = [
  "anyhow",
  "arrayvec",
  "log",
- "nom",
+ "nom 7.1.3",
  "num-rational",
  "v_frame",
 ]
@@ -398,9 +399,9 @@ checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
 
 [[package]]
 name = "bytemuck"
-version = "1.22.0"
+version = "1.23.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540"
+checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c"
 dependencies = [
  "bytemuck_derive",
 ]
@@ -456,9 +457,9 @@ dependencies = [
 
 [[package]]
 name = "cc"
-version = "1.2.19"
+version = "1.2.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362"
+checksum = "04da6a0d40b948dfc4fa8f5bbf402b0fc1a64a28dbf7d12ffd683550f2c1b63a"
 dependencies = [
  "jobserver",
  "libc",
@@ -505,14 +506,15 @@ dependencies = [
 
 [[package]]
 name = "chrono"
-version = "0.4.40"
+version = "0.4.41"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c"
+checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
 dependencies = [
  "android-tzdata",
  "iana-time-zone",
  "js-sys",
  "num-traits",
+ "serde",
  "wasm-bindgen",
  "windows-link",
 ]
@@ -553,6 +555,15 @@ dependencies = [
  "crossbeam-utils",
 ]
 
+[[package]]
+name = "convert_case"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7"
+dependencies = [
+ "unicode-segmentation",
+]
+
 [[package]]
 name = "core-foundation"
 version = "0.9.4"
@@ -563,6 +574,16 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "core-foundation"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
 [[package]]
 name = "core-foundation-sys"
 version = "0.8.7"
@@ -576,8 +597,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081"
 dependencies = [
  "bitflags 1.3.2",
- "core-foundation",
- "core-graphics-types",
+ "core-foundation 0.9.4",
+ "core-graphics-types 0.1.3",
  "foreign-types",
  "libc",
 ]
@@ -589,7 +610,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf"
 dependencies = [
  "bitflags 1.3.2",
- "core-foundation",
+ "core-foundation 0.9.4",
+ "libc",
+]
+
+[[package]]
+name = "core-graphics-types"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb"
+dependencies = [
+ "bitflags 2.9.0",
+ "core-foundation 0.10.0",
  "libc",
 ]
 
@@ -648,6 +680,34 @@ version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991"
 
+[[package]]
+name = "derive_more"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678"
+dependencies = [
+ "derive_more-impl",
+]
+
+[[package]]
+name = "derive_more-impl"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
+dependencies = [
+ "convert_case",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "unicode-xid",
+]
+
+[[package]]
+name = "detect-desktop-environment"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff7f16599c3ba6b4ab9a3195bcd9413a7ee8d51e7651f3e9efd6a5db5315008d"
+
 [[package]]
 name = "diff"
 version = "0.1.13"
@@ -660,6 +720,16 @@ version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
 
+[[package]]
+name = "dispatch2"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec"
+dependencies = [
+ "bitflags 2.9.0",
+ "objc2 0.6.1",
+]
+
 [[package]]
 name = "dlib"
 version = "0.5.2"
@@ -849,9 +919,9 @@ dependencies = [
 
 [[package]]
 name = "getrandom"
-version = "0.2.15"
+version = "0.2.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
+checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
 dependencies = [
  "cfg-if",
  "libc",
@@ -940,7 +1010,7 @@ dependencies = [
  "log",
  "presser",
  "thiserror 1.0.69",
- "windows",
+ "windows 0.58.0",
 ]
 
 [[package]]
@@ -977,9 +1047,9 @@ dependencies = [
 
 [[package]]
 name = "hashbrown"
-version = "0.15.2"
+version = "0.15.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
+checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3"
 dependencies = [
  "allocator-api2",
  "equivalent",
@@ -1010,6 +1080,28 @@ version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
 
+[[package]]
+name = "hidapi"
+version = "2.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03b876ecf37e86b359573c16c8366bc3eba52b689884a0fc42ba3f67203d2a8b"
+dependencies = [
+ "cc",
+ "cfg-if",
+ "libc",
+ "pkg-config",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "humansize"
+version = "2.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7"
+dependencies = [
+ "libm",
+]
+
 [[package]]
 name = "iana-time-zone"
 version = "0.1.63"
@@ -1022,7 +1114,7 @@ dependencies = [
  "js-sys",
  "log",
  "wasm-bindgen",
- "windows-core",
+ "windows-core 0.61.0",
 ]
 
 [[package]]
@@ -1094,6 +1186,15 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "isolang"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe50d48c77760c55188549098b9a7f6e37ae980c586a24693d6b01c3b2010c3c"
+dependencies = [
+ "phf",
+]
+
 [[package]]
 name = "itertools"
 version = "0.12.1"
@@ -1103,6 +1204,15 @@ dependencies = [
  "either",
 ]
 
+[[package]]
+name = "itertools"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
+dependencies = [
+ "either",
+]
+
 [[package]]
 name = "itoa"
 version = "1.0.15"
@@ -1188,9 +1298,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
 
 [[package]]
 name = "libc"
-version = "0.2.171"
+version = "0.2.172"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
+checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
 
 [[package]]
 name = "libfuzzer-sys"
@@ -1214,9 +1324,9 @@ dependencies = [
 
 [[package]]
 name = "libm"
-version = "0.2.11"
+version = "0.2.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa"
+checksum = "c9627da5196e5d8ed0b0495e61e518847578da83483c37288316d9b2e03a7f72"
 
 [[package]]
 name = "libredox"
@@ -1317,7 +1427,22 @@ checksum = "f569fb946490b5743ad69813cb19629130ce9374034abe31614a36402d18f99e"
 dependencies = [
  "bitflags 2.9.0",
  "block",
- "core-graphics-types",
+ "core-graphics-types 0.1.3",
+ "foreign-types",
+ "log",
+ "objc",
+ "paste",
+]
+
+[[package]]
+name = "metal"
+version = "0.32.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00c15a6f673ff72ddcc22394663290f870fb224c1bfce55734a75c414150e605"
+dependencies = [
+ "bitflags 2.9.0",
+ "block",
+ "core-graphics-types 0.2.0",
  "foreign-types",
  "log",
  "objc",
@@ -1361,7 +1486,7 @@ dependencies = [
  "petgraph",
  "rustc-hash",
  "spirv",
- "strum",
+ "strum 0.26.3",
  "thiserror 2.0.12",
  "unicode-ident",
 ]
@@ -1421,12 +1546,30 @@ dependencies = [
  "minimal-lexical",
 ]
 
+[[package]]
+name = "nom"
+version = "8.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405"
+dependencies = [
+ "memchr",
+]
+
 [[package]]
 name = "noop_proc_macro"
 version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
 
+[[package]]
+name = "ntapi"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4"
+dependencies = [
+ "winapi",
+]
+
 [[package]]
 name = "nu-ansi-term"
 version = "0.46.0"
@@ -1536,9 +1679,9 @@ dependencies = [
 
 [[package]]
 name = "objc2"
-version = "0.6.0"
+version = "0.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3531f65190d9cff863b77a99857e74c314dd16bf56c538c4b57c7cbc3f3a6e59"
+checksum = "88c6597e14493ab2e44ce58f2fdecf095a51f12ca57bec060a11c57332520551"
 dependencies = [
  "objc2-encode",
 ]
@@ -1597,12 +1740,13 @@ dependencies = [
 
 [[package]]
 name = "objc2-core-foundation"
-version = "0.3.0"
+version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "daeaf60f25471d26948a1c2f840e3f7d86f4109e3af4e8e4b5cd70c39690d925"
+checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166"
 dependencies = [
  "bitflags 2.9.0",
- "objc2 0.6.0",
+ "dispatch2",
+ "objc2 0.6.1",
 ]
 
 [[package]]
@@ -1650,12 +1794,12 @@ dependencies = [
 
 [[package]]
 name = "objc2-foundation"
-version = "0.3.0"
+version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3a21c6c9014b82c39515db5b396f91645182611c97d24637cf56ac01e5f8d998"
+checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c"
 dependencies = [
  "bitflags 2.9.0",
- "objc2 0.6.0",
+ "objc2 0.6.1",
  "objc2-core-foundation",
 ]
 
@@ -1685,13 +1829,13 @@ dependencies = [
 
 [[package]]
 name = "objc2-metal"
-version = "0.3.0"
+version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "01c41bc8b0e50ea7a5304a56f25e0066f526e99641b46fd7b9ad4421dd35bff6"
+checksum = "7f246c183239540aab1782457b35ab2040d4259175bd1d0c58e46ada7b47a874"
 dependencies = [
  "bitflags 2.9.0",
- "objc2 0.6.0",
- "objc2-foundation 0.3.0",
+ "objc2 0.6.1",
+ "objc2-foundation 0.3.1",
 ]
 
 [[package]]
@@ -1709,15 +1853,15 @@ dependencies = [
 
 [[package]]
 name = "objc2-quartz-core"
-version = "0.3.0"
+version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6fb3794501bb1bee12f08dcad8c61f2a5875791ad1c6f47faa71a0f033f20071"
+checksum = "90ffb6a0cd5f182dc964334388560b12a57f7b74b3e2dec5e2722aa2dfb2ccd5"
 dependencies = [
  "bitflags 2.9.0",
- "objc2 0.6.0",
+ "objc2 0.6.1",
  "objc2-core-foundation",
- "objc2-foundation 0.3.0",
- "objc2-metal 0.3.0",
+ "objc2-foundation 0.3.1",
+ "objc2-metal 0.3.1",
 ]
 
 [[package]]
@@ -1867,6 +2011,24 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "phf"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
+dependencies = [
+ "phf_shared",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
+dependencies = [
+ "siphasher",
+]
+
 [[package]]
 name = "pin-project"
 version = "1.1.10"
@@ -1950,7 +2112,7 @@ version = "0.2.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
 dependencies = [
- "zerocopy 0.8.24",
+ "zerocopy 0.8.25",
 ]
 
 [[package]]
@@ -1980,9 +2142,9 @@ dependencies = [
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.94"
+version = "1.0.95"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
+checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
 dependencies = [
  "unicode-ident",
 ]
@@ -2023,9 +2185,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
 
 [[package]]
 name = "quick-xml"
-version = "0.37.4"
+version = "0.37.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4ce8c88de324ff838700f36fb6ab86c96df0e3c4ab6ef3a9b2044465cce1369"
+checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb"
 dependencies = [
  "memchr",
 ]
@@ -2072,7 +2234,7 @@ version = "0.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
 dependencies = [
- "getrandom 0.2.15",
+ "getrandom 0.2.16",
 ]
 
 [[package]]
@@ -2095,7 +2257,7 @@ dependencies = [
  "built",
  "cfg-if",
  "interpolate_name",
- "itertools",
+ "itertools 0.12.1",
  "libc",
  "libfuzzer-sys",
  "log",
@@ -2143,10 +2305,10 @@ version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "40d213455a5f1dc59214213c7330e074ddf8114c9a42411eb890c767357ce135"
 dependencies = [
- "objc2 0.6.0",
+ "objc2 0.6.1",
  "objc2-core-foundation",
- "objc2-foundation 0.3.0",
- "objc2-quartz-core 0.3.0",
+ "objc2-foundation 0.3.1",
+ "objc2-quartz-core 0.3.1",
 ]
 
 [[package]]
@@ -2370,9 +2532,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
 
 [[package]]
 name = "signal-hook-registry"
-version = "1.4.2"
+version = "1.4.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
+checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410"
 dependencies = [
  "libc",
 ]
@@ -2392,6 +2554,12 @@ dependencies = [
  "quote",
 ]
 
+[[package]]
+name = "siphasher"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
+
 [[package]]
 name = "slab"
 version = "0.4.9"
@@ -2500,9 +2668,15 @@ version = "0.26.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
 dependencies = [
- "strum_macros",
+ "strum_macros 0.26.4",
 ]
 
+[[package]]
+name = "strum"
+version = "0.27.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32"
+
 [[package]]
 name = "strum_macros"
 version = "0.26.4"
@@ -2517,16 +2691,52 @@ dependencies = [
 ]
 
 [[package]]
-name = "syn"
-version = "2.0.100"
+name = "strum_macros"
+version = "0.27.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
+checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8"
+dependencies = [
+ "heck 0.5.0",
+ "proc-macro2",
+ "quote",
+ "rustversion",
+ "syn",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.101"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
 dependencies = [
  "proc-macro2",
  "quote",
  "unicode-ident",
 ]
 
+[[package]]
+name = "sys-locale"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "sysinfo"
+version = "0.34.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4b93974b3d3aeaa036504b8eefd4c039dced109171c1ae973f1dc63b2c7e4b2"
+dependencies = [
+ "libc",
+ "memchr",
+ "ntapi",
+ "objc2-core-foundation",
+ "serde",
+ "windows 0.57.0",
+]
+
 [[package]]
 name = "system-deps"
 version = "6.2.2"
@@ -2546,6 +2756,30 @@ version = "0.12.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
 
+[[package]]
+name = "telemetry"
+version = "0.1.0"
+dependencies = [
+ "ash",
+ "chrono",
+ "derive_more",
+ "detect-desktop-environment",
+ "hidapi",
+ "humansize",
+ "metal 0.32.0",
+ "serde",
+ "serde_json",
+ "strum 0.27.1",
+ "strum_macros 0.27.1",
+ "sys-locale",
+ "sysinfo",
+ "thiserror 2.0.12",
+ "timeago",
+ "versions",
+ "wgpu",
+ "winit",
+]
+
 [[package]]
 name = "termcolor"
 version = "1.4.1"
@@ -2622,6 +2856,16 @@ dependencies = [
  "weezl",
 ]
 
+[[package]]
+name = "timeago"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1710e589de0a76aaf295cd47a6699f6405737dbfd3cf2b75c92d000b548d0e6"
+dependencies = [
+ "chrono",
+ "isolang",
+]
+
 [[package]]
 name = "tiny-skia"
 version = "0.11.4"
@@ -2658,9 +2902,9 @@ dependencies = [
 
 [[package]]
 name = "toml"
-version = "0.8.20"
+version = "0.8.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148"
+checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae"
 dependencies = [
  "serde",
  "serde_spanned",
@@ -2670,18 +2914,18 @@ dependencies = [
 
 [[package]]
 name = "toml_datetime"
-version = "0.6.8"
+version = "0.6.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
+checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3"
 dependencies = [
  "serde",
 ]
 
 [[package]]
 name = "toml_edit"
-version = "0.22.24"
+version = "0.22.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474"
+checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e"
 dependencies = [
  "indexmap",
  "serde",
@@ -2775,6 +3019,12 @@ version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
 
+[[package]]
+name = "unicode-xid"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
+
 [[package]]
 name = "v_frame"
 version = "0.3.8"
@@ -2804,6 +3054,17 @@ version = "0.9.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
 
+[[package]]
+name = "versions"
+version = "7.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "80a7e511ce1795821207a837b7b1c8d8aca0c648810966ad200446ae58f6667f"
+dependencies = [
+ "itertools 0.14.0",
+ "nom 8.0.0",
+ "serde",
+]
+
 [[package]]
 name = "vk-parse"
 version = "0.15.0"
@@ -2827,7 +3088,7 @@ dependencies = [
  "heck 0.4.1",
  "indexmap",
  "libloading",
- "nom",
+ "nom 7.1.3",
  "once_cell",
  "parking_lot",
  "proc-macro2",
@@ -2955,9 +3216,9 @@ dependencies = [
 
 [[package]]
 name = "wayland-backend"
-version = "0.3.8"
+version = "0.3.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b7208998eaa3870dad37ec8836979581506e0c5c64c20c9e79e9d2a10d6f47bf"
+checksum = "fe770181423e5fc79d3e2a7f4410b7799d5aab1de4372853de3c6aa13ca24121"
 dependencies = [
  "cc",
  "downcast-rs",
@@ -2969,9 +3230,9 @@ dependencies = [
 
 [[package]]
 name = "wayland-client"
-version = "0.31.8"
+version = "0.31.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2120de3d33638aaef5b9f4472bff75f07c56379cf76ea320bd3a3d65ecaf73f"
+checksum = "978fa7c67b0847dbd6a9f350ca2569174974cd4082737054dbb7fbb79d7d9a61"
 dependencies = [
  "bitflags 2.9.0",
  "rustix",
@@ -2992,9 +3253,9 @@ dependencies = [
 
 [[package]]
 name = "wayland-cursor"
-version = "0.31.8"
+version = "0.31.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a93029cbb6650748881a00e4922b076092a6a08c11e7fbdb923f064b23968c5d"
+checksum = "a65317158dec28d00416cb16705934070aef4f8393353d41126c54264ae0f182"
 dependencies = [
  "rustix",
  "wayland-client",
@@ -3003,9 +3264,9 @@ dependencies = [
 
 [[package]]
 name = "wayland-protocols"
-version = "0.32.6"
+version = "0.32.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0781cf46869b37e36928f7b432273c0995aa8aed9552c556fb18754420541efc"
+checksum = "779075454e1e9a521794fed15886323ea0feda3f8b0fc1390f5398141310422a"
 dependencies = [
  "bitflags 2.9.0",
  "wayland-backend",
@@ -3015,9 +3276,9 @@ dependencies = [
 
 [[package]]
 name = "wayland-protocols-plasma"
-version = "0.3.6"
+version = "0.3.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ccaacc76703fefd6763022ac565b590fcade92202492381c95b2edfdf7d46b3"
+checksum = "4fd38cdad69b56ace413c6bcc1fbf5acc5e2ef4af9d5f8f1f9570c0c83eae175"
 dependencies = [
  "bitflags 2.9.0",
  "wayland-backend",
@@ -3028,9 +3289,9 @@ dependencies = [
 
 [[package]]
 name = "wayland-protocols-wlr"
-version = "0.3.6"
+version = "0.3.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "248a02e6f595aad796561fa82d25601bd2c8c3b145b1c7453fc8f94c1a58f8b2"
+checksum = "1cb6cdc73399c0e06504c437fe3cf886f25568dd5454473d565085b36d6a8bbf"
 dependencies = [
  "bitflags 2.9.0",
  "wayland-backend",
@@ -3190,7 +3451,7 @@ dependencies = [
  "bytemuck",
  "cfg-if",
  "cfg_aliases",
- "core-graphics-types",
+ "core-graphics-types 0.1.3",
  "glow",
  "glutin_wgl_sys",
  "gpu-alloc",
@@ -3202,7 +3463,7 @@ dependencies = [
  "libc",
  "libloading",
  "log",
- "metal",
+ "metal 0.31.0",
  "naga",
  "ndk-sys 0.5.0+25.2.9519653",
  "objc",
@@ -3218,8 +3479,8 @@ dependencies = [
  "wasm-bindgen",
  "web-sys",
  "wgpu-types",
- "windows",
- "windows-core",
+ "windows 0.58.0",
+ "windows-core 0.58.0",
 ]
 
 [[package]]
@@ -3267,13 +3528,35 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
+[[package]]
+name = "windows"
+version = "0.57.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143"
+dependencies = [
+ "windows-core 0.57.0",
+ "windows-targets 0.52.6",
+]
+
 [[package]]
 name = "windows"
 version = "0.58.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6"
 dependencies = [
- "windows-core",
+ "windows-core 0.58.0",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-core"
+version = "0.57.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d"
+dependencies = [
+ "windows-implement 0.57.0",
+ "windows-interface 0.57.0",
+ "windows-result 0.1.2",
  "windows-targets 0.52.6",
 ]
 
@@ -3283,13 +3566,37 @@ version = "0.58.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99"
 dependencies = [
- "windows-implement",
- "windows-interface",
- "windows-result",
- "windows-strings",
+ "windows-implement 0.58.0",
+ "windows-interface 0.58.0",
+ "windows-result 0.2.0",
+ "windows-strings 0.1.0",
  "windows-targets 0.52.6",
 ]
 
+[[package]]
+name = "windows-core"
+version = "0.61.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
+dependencies = [
+ "windows-implement 0.60.0",
+ "windows-interface 0.59.1",
+ "windows-link",
+ "windows-result 0.3.2",
+ "windows-strings 0.4.0",
+]
+
+[[package]]
+name = "windows-implement"
+version = "0.57.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "windows-implement"
 version = "0.58.0"
@@ -3301,6 +3608,28 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "windows-implement"
+version = "0.60.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "windows-interface"
+version = "0.57.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "windows-interface"
 version = "0.58.0"
@@ -3312,12 +3641,32 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "windows-interface"
+version = "0.59.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "windows-link"
 version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
 
+[[package]]
+name = "windows-result"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
 [[package]]
 name = "windows-result"
 version = "0.2.0"
@@ -3327,16 +3676,34 @@ dependencies = [
  "windows-targets 0.52.6",
 ]
 
+[[package]]
+name = "windows-result"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252"
+dependencies = [
+ "windows-link",
+]
+
 [[package]]
 name = "windows-strings"
 version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
 dependencies = [
- "windows-result",
+ "windows-result 0.2.0",
  "windows-targets 0.52.6",
 ]
 
+[[package]]
+name = "windows-strings"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97"
+dependencies = [
+ "windows-link",
+]
+
 [[package]]
 name = "windows-sys"
 version = "0.45.0"
@@ -3346,6 +3713,15 @@ dependencies = [
  "windows-targets 0.42.2",
 ]
 
+[[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"
@@ -3544,9 +3920,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
 
 [[package]]
 name = "winit"
-version = "0.30.9"
+version = "0.30.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a809eacf18c8eca8b6635091543f02a5a06ddf3dad846398795460e6e0ae3cc0"
+checksum = "b0d05bd8908e14618c9609471db04007e644fd9cce6529756046cfc577f9155e"
 dependencies = [
  "ahash",
  "android-activity",
@@ -3557,7 +3933,7 @@ dependencies = [
  "calloop",
  "cfg_aliases",
  "concurrent-queue",
- "core-foundation",
+ "core-foundation 0.9.4",
  "core-graphics",
  "cursor-icon",
  "dpi",
@@ -3596,9 +3972,9 @@ dependencies = [
 
 [[package]]
 name = "winnow"
-version = "0.7.6"
+version = "0.7.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10"
+checksum = "9e27d6ad3dac991091e4d35de9ba2d2d00647c5d0fc26c5496dee55984ae111b"
 dependencies = [
  "memchr",
 ]
@@ -3689,6 +4065,8 @@ dependencies = [
  "bytemuck",
  "cgmath",
  "image",
+ "serde",
+ "serde_json",
  "smol",
  "terminator",
  "thiserror 2.0.12",
@@ -3712,11 +4090,11 @@ dependencies = [
 
 [[package]]
 name = "zerocopy"
-version = "0.8.24"
+version = "0.8.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879"
+checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb"
 dependencies = [
- "zerocopy-derive 0.8.24",
+ "zerocopy-derive 0.8.25",
 ]
 
 [[package]]
@@ -3732,9 +4110,9 @@ dependencies = [
 
 [[package]]
 name = "zerocopy-derive"
-version = "0.8.24"
+version = "0.8.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be"
+checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
 dependencies = [
  "proc-macro2",
  "quote",
diff --git a/Cargo.toml b/Cargo.toml
index 732a166..f29ab09 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -15,7 +15,7 @@ crate-type = ["cdylib"]
 
 [workspace]
 resolver = "2"
-members = ["subcrates/renderer", "subcrates/zlog"]
+members = ["subcrates/renderer", "subcrates/telemetry", "subcrates/zlog"]
 
 [workspace.dependencies]
 zlog = { path = "subcrates/zlog" }
@@ -55,6 +55,8 @@ vulkano = "0.35.1"
 wgpu = { version = "25.0.0", features = ["spirv"] }
 zlog.workspace = true
 allocator-api2 = "0.2.21"
+serde_json = "1.0.140"
+serde = "1.0.219"
 
 [target.aarch64-linux-android.dependencies]
 winit = { version = "0.30.9", features = ["android-native-activity"] }
diff --git a/src/collections/sparse_set.rs b/src/collections/sparse_set.rs
index 5e0ec84..72c7381 100644
--- a/src/collections/sparse_set.rs
+++ b/src/collections/sparse_set.rs
@@ -236,7 +236,10 @@ mod tests {
 
         assert_eq!(sparse_set.remove(SPARSE_PAGESIZE + 2).unwrap(), 3);
         assert_eq!(sparse_set.sparse[1].as_ref().unwrap().1, 2);
-        assert_eq!(sparse_set.keys(), [10, 11, 12, SPARSE_PAGESIZE, SPARSE_PAGESIZE + 1]);
+        assert_eq!(
+            sparse_set.keys(),
+            [10, 11, 12, SPARSE_PAGESIZE, SPARSE_PAGESIZE + 1]
+        );
         assert_eq!(sparse_set.values(), [1, 2, 2, 1, 2]);
 
         assert_eq!(sparse_set.remove(SPARSE_PAGESIZE + 1).unwrap(), 2);
@@ -249,25 +252,44 @@ mod tests {
         assert_eq!(sparse_set.keys(), [10, 11, 12]);
         assert_eq!(sparse_set.values(), [1, 2, 2]);
 
-
         sparse_set.insert(SPARSE_PAGESIZE, 1);
         sparse_set.insert(SPARSE_PAGESIZE + 1, 2);
         sparse_set.insert(SPARSE_PAGESIZE + 2, 3);
 
         assert_eq!(sparse_set.remove(10).unwrap(), 1);
         assert_eq!(sparse_set.sparse[0].as_ref().unwrap().1, 2);
-        // swap-remove 
-        assert_eq!(sparse_set.keys(), [SPARSE_PAGESIZE + 2, 11, 12, SPARSE_PAGESIZE, SPARSE_PAGESIZE + 1]);
+        // swap-remove
+        assert_eq!(
+            sparse_set.keys(),
+            [
+                SPARSE_PAGESIZE + 2,
+                11,
+                12,
+                SPARSE_PAGESIZE,
+                SPARSE_PAGESIZE + 1
+            ]
+        );
         assert_eq!(sparse_set.values(), [3, 2, 2, 1, 2]);
 
         assert_eq!(sparse_set.remove(11).unwrap(), 2);
         assert_eq!(sparse_set.sparse[0].as_ref().unwrap().1, 1);
-        assert_eq!(sparse_set.keys(), [SPARSE_PAGESIZE + 2, SPARSE_PAGESIZE + 1, 12, SPARSE_PAGESIZE]);
+        assert_eq!(
+            sparse_set.keys(),
+            [
+                SPARSE_PAGESIZE + 2,
+                SPARSE_PAGESIZE + 1,
+                12,
+                SPARSE_PAGESIZE
+            ]
+        );
         assert_eq!(sparse_set.values(), [3, 2, 2, 1]);
 
         assert_eq!(sparse_set.remove(12).unwrap(), 2);
         assert!(sparse_set.sparse[0].is_none());
-        assert_eq!(sparse_set.keys(), [SPARSE_PAGESIZE + 2, SPARSE_PAGESIZE + 1, SPARSE_PAGESIZE]);
+        assert_eq!(
+            sparse_set.keys(),
+            [SPARSE_PAGESIZE + 2, SPARSE_PAGESIZE + 1, SPARSE_PAGESIZE]
+        );
         assert_eq!(sparse_set.values(), [3, 2, 1]);
     }
 }
diff --git a/subcrates/telemetry/Cargo.toml b/subcrates/telemetry/Cargo.toml
new file mode 100644
index 0000000..7a483ae
--- /dev/null
+++ b/subcrates/telemetry/Cargo.toml
@@ -0,0 +1,27 @@
+[package]
+name = "telemetry"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+ash = "0.38.0"
+chrono = { version = "0.4.40", features = ["serde"] }
+derive_more = { version = "2.0.1", features = ["add", "add_assign", "as_ref", "deref_mut", "from", "from_str", "full", "into", "mul", "mul_assign", "not", "sum", "try_from", "try_into", "try_unwrap", "unwrap"] }
+detect-desktop-environment = "1.2.0"
+hidapi = "2.6.3"
+humansize = "2.1.3"
+metal = { version = "0.32.0", optional = true }
+serde = { version = "1.0.219", features = ["derive"] }
+serde_json = "1.0.140"
+strum = "0.27.1"
+strum_macros = "0.27.1"
+sys-locale = "0.3.2"
+sysinfo = { version = "0.34.2", features = ["serde"] }
+thiserror = "2.0.12"
+timeago = "0.4.2"
+versions = { version = "7.0.0", features = ["serde"] }
+wgpu = "25.0.0"
+winit = "0.30.9"
+
+[features]
+metal = ["dep:metal"]
diff --git a/subcrates/telemetry/build.rs b/subcrates/telemetry/build.rs
new file mode 100644
index 0000000..d012c62
--- /dev/null
+++ b/subcrates/telemetry/build.rs
@@ -0,0 +1,21 @@
+use std::{env, fs::File, io::Write, process::Command};
+
+fn main() {
+    let out_dir = env::var("OUT_DIR").unwrap();
+    let mut f = File::create(format!("{}/built.rs", out_dir)).unwrap();
+
+    let commit_hash = Command::new("git")
+        .args(["rev-parse", "HEAD"])
+        .output()
+        .ok()
+        .and_then(|o| String::from_utf8(o.stdout).ok())
+        .unwrap_or_else(|| "unknown".into());
+
+    writeln!(f, "#[allow(dead_code)]").unwrap();
+    writeln!(
+        f,
+        "pub const GIT_COMMIT_HASH: &str = \"{}\";",
+        commit_hash.trim()
+    )
+    .unwrap();
+}
diff --git a/subcrates/telemetry/src/lib.rs b/subcrates/telemetry/src/lib.rs
new file mode 100644
index 0000000..4b1b333
--- /dev/null
+++ b/subcrates/telemetry/src/lib.rs
@@ -0,0 +1,70 @@
+use serde::{Serialize, Deserialize};
+use serde_json::{Value, json};
+#[macro_use]
+mod modules;
+pub use crate::modules::{cpu, devices, external, gpu, memory, meta, network, os, storage, uptime};
+use thiserror::Error;
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct TelemetryInfo {
+    telemetry: AllInfo,
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct AllInfo {
+    gpu: gpu::GPUInfo,
+    cpu: Vec<cpu::CPUInfo>,
+    memory: memory::MemoryInfo,
+    os: os::OSInfo,
+    storage: Vec<storage::StorageInfo>,
+    network: network::NetworkInfo,
+    external: external::ExternalInfo,
+    devices: Vec<devices::DeviceInfo>,
+    meta: meta::MetaInfo,
+    uptime: uptime::UptimeInfo,
+}
+
+#[derive(Debug, Error)]
+pub enum TelemetryError {
+    // #[error("Failed to get GPU information")]
+    // GPUError(#[from] gpu::GPUError),
+    #[error("Failed to get CPU information")]
+    CPUError(#[from] cpu::CPUError),
+    #[error("Failed to get OS information")]
+    OSError(#[from] os::OSError),
+    #[error("Failed to get storage information")]
+    StorageError(#[from] storage::StorageError),
+    // #[error("Failed to get external information")]
+    // ExternalError(#[from] external::ExternalError),
+    #[error("Failed to get devices information")]
+    DevicesError(#[from] devices::DevicesError),
+    #[error("Failed to get meta information")]
+    MetaError(#[from] meta::MetaError),
+    #[error("Failed to get uptime information")]
+    UptimeError(#[from] uptime::UptimeError),
+    #[error("Failed to build JSON for TelemetryInfo")]
+    JsonError(#[from] serde_json::Error),
+}
+
+#[allow(dead_code)]
+pub fn get_struct() -> Result<TelemetryInfo, TelemetryError> {
+    Ok(TelemetryInfo {
+        telemetry: AllInfo {
+            gpu: gpu::get_struct(),
+            cpu: cpu::get_struct()?,
+            memory: memory::get_struct(),
+            os: os::get_struct()?,
+            storage: storage::get_list()?,
+            network: network::get_struct(),
+            external: external::get_struct(),
+            devices: devices::get_struct()?,
+            meta: meta::get_struct()?,
+            uptime: uptime::get_struct()?,
+        },
+    })
+}
+
+#[allow(dead_code)]
+pub fn get_json() -> Result<Value, TelemetryError> {
+    Ok(json!(get_struct()?))
+}
diff --git a/subcrates/telemetry/src/main.rs b/subcrates/telemetry/src/main.rs
new file mode 100644
index 0000000..37405d5
--- /dev/null
+++ b/subcrates/telemetry/src/main.rs
@@ -0,0 +1,13 @@
+use serde_json;
+
+fn main() {
+    //println!("{:#?}", telemetry::get_struct());
+    //println!("{:#?}", telemetry::os::get_json());
+    //telemetry::os::get_json();
+
+    match telemetry::get_struct() {
+        //Ok(telemetry) => println!("{:#?}", serde_json::from_value(telemetry)),
+        Ok(telemetry) => println!("{:#?}", telemetry),
+        Err(e) => eprintln!("Error: {}", e),
+    }
+}
diff --git a/subcrates/telemetry/src/modules/cpu.rs b/subcrates/telemetry/src/modules/cpu.rs
new file mode 100644
index 0000000..7548feb
--- /dev/null
+++ b/subcrates/telemetry/src/modules/cpu.rs
@@ -0,0 +1,67 @@
+use crate::modules::{FmtCores, FmtThreads};
+use serde::{Serialize, Deserialize};
+use serde_json::{Value, json};
+use std::collections::HashMap;
+use sysinfo::{CpuRefreshKind, RefreshKind, System};
+use thiserror::Error;
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct CPUInfo {
+    vendor: String,
+    brand: String,
+    total_system_cores: FmtCores,
+    threads: FmtThreads,
+    architecture: String,
+    byte_order: String,
+}
+
+#[derive(Debug, Error)]
+pub enum CPUError {
+    #[error("No CPU information available")]
+    NoCpusFound,
+    #[error("Failed to build JSON for Vec<CPUInfo>")]
+    JsonError(#[from] serde_json::Error),
+}
+
+#[allow(dead_code)]
+pub fn get_struct() -> Result<Vec<CPUInfo>, CPUError> {
+    let mut system =
+        System::new_with_specifics(RefreshKind::nothing().with_cpu(CpuRefreshKind::everything()));
+    system.refresh_cpu_all();
+
+    let mut processor_map: HashMap<String, CPUInfo> = HashMap::new();
+
+    for cpu in system.cpus() {
+        let brand = cpu.brand().trim().to_string();
+        let entry = processor_map
+            .entry(brand.clone())
+            .or_insert_with(|| CPUInfo {
+                vendor: cpu.vendor_id().trim().to_string(),
+                brand: brand.clone(),
+                total_system_cores: System::physical_core_count()
+                    .map(|cores| cores.into())
+                    .unwrap_or_else(|| 0.into()),
+                threads: 0.into(),
+                architecture: System::cpu_arch().trim().to_string(),
+                byte_order: if cfg!(target_endian = "little") {
+                    String::from("little-endian")
+                } else {
+                    String::from("big-endian")
+                },
+            });
+        
+        entry.threads += 1.into();
+    }
+
+    let cpus: Vec<_> = processor_map.into_values().collect();
+    if cpus.is_empty() {
+        return Err(CPUError::NoCpusFound);
+    }
+
+    Ok(cpus)
+}
+
+#[allow(dead_code)]
+pub fn get_json() -> Result<Value, CPUError> {
+    Ok(json!(get_struct()?))
+}
diff --git a/subcrates/telemetry/src/modules/devices.rs b/subcrates/telemetry/src/modules/devices.rs
new file mode 100644
index 0000000..6a74d2f
--- /dev/null
+++ b/subcrates/telemetry/src/modules/devices.rs
@@ -0,0 +1,59 @@
+use super::UNKNOWN;
+use serde::{Serialize, Deserialize};
+use serde_json::{Value, json};
+use thiserror::Error;
+extern crate hidapi;
+use hidapi::{HidApi, HidError};
+use std::collections::HashMap;
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct DeviceInfo {
+    manufacturer: String,
+    products: Vec<String>,
+}
+
+#[derive(Debug, Error)]
+pub enum DevicesError {
+    #[error("Failed to initialize HID API: {0}")]
+    HidApiInitError(#[from] HidError),
+    #[error("Failed to build JSON for Vec<DeviceInfo>")]
+    JsonError(#[from] serde_json::Error),
+}
+
+#[allow(dead_code)]
+pub fn get_struct() -> Result<Vec<DeviceInfo>, DevicesError> {
+    let api = HidApi::new()?;
+    let mut grouped: HashMap<String, Vec<String>> = HashMap::new();
+
+    for device in api.device_list() {
+        let manufacturer = device
+            .manufacturer_string()
+            .unwrap_or(UNKNOWN)
+            .to_ascii_lowercase();
+        let product = device.product_string().unwrap_or(UNKNOWN).to_string();
+
+        if !manufacturer.trim().is_empty() && !product.trim().is_empty() {
+            grouped.entry(manufacturer).or_default().push(product)
+        }
+    }
+
+    let mut sorted_devices: Vec<_> = grouped.into_iter().collect();
+    sorted_devices.sort_by(|a, b| a.0.cmp(&b.0));
+
+    Ok(sorted_devices
+        .into_iter()
+        .map(|(manufacturer, mut products)| {
+            products.sort(); // optional: sort product names
+            products.dedup(); // remove duplicates
+            DeviceInfo {
+                manufacturer,
+                products,
+            }
+        })
+        .collect())
+}
+
+#[allow(dead_code)]
+pub fn get_json() -> Result<Value, DevicesError> {
+    Ok(json!(get_struct()?))
+}
diff --git a/subcrates/telemetry/src/modules/external.rs b/subcrates/telemetry/src/modules/external.rs
new file mode 100644
index 0000000..a89e9d5
--- /dev/null
+++ b/subcrates/telemetry/src/modules/external.rs
@@ -0,0 +1,63 @@
+use serde::{Serialize, Deserialize};
+use serde_json::{Value, json};
+use std::{cmp::Ordering::Equal, collections::HashMap};
+use sysinfo::{System, Users};
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct SoftwareInfo {
+    name: String,
+    count: usize,
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct ExternalInfo {
+    softwares: Vec<SoftwareInfo>,
+}
+
+#[allow(dead_code)]
+pub fn get_struct() -> ExternalInfo {
+    let mut system = System::new_all();
+    system.refresh_all();
+
+    let users = &Users::new_with_refreshed_list();
+    let mut grouped_processes: HashMap<String, Vec<i32>> = HashMap::new();
+
+    for process in system.processes().values() {
+        let name = process.name().to_str().unwrap().to_string();
+        let user_id = process.user_id();
+        let is_user_owned = user_id.is_some_and(|uid| {
+            users
+                .list()
+                .iter()
+                .any(|u| u.id() == uid && u.name() != "root")
+        });
+
+        if is_user_owned && !name.trim().is_empty() && !name.starts_with('[') {
+            grouped_processes
+                .entry(name)
+                .or_default()
+                .push(process.pid().as_u32() as i32);
+        }
+    }
+
+    let mut softwares = Vec::new();
+    let mut grouped_vec: Vec<_> = grouped_processes.into_iter().collect();
+    grouped_vec.sort_by(|a, b| match b.1.len().cmp(&a.1.len()) {
+        Equal => a.0.to_lowercase().cmp(&b.0.to_lowercase()),
+        other => other,
+    });
+
+    for (name, pids) in grouped_vec {
+        softwares.push(SoftwareInfo {
+            name,
+            count: pids.len(),
+        });
+    }
+
+    ExternalInfo { softwares }
+}
+
+#[allow(dead_code)]
+pub fn get_json() -> Value {
+    json!(get_struct())
+}
diff --git a/subcrates/telemetry/src/modules/formatted/architecture.rs b/subcrates/telemetry/src/modules/formatted/architecture.rs
new file mode 100644
index 0000000..fad7499
--- /dev/null
+++ b/subcrates/telemetry/src/modules/formatted/architecture.rs
@@ -0,0 +1,69 @@
+use serde::{Serialize, Serializer, Deserialize, Deserializer, de::Error as DeError};
+use std::{
+    fmt::{Debug, Display, Formatter, Result as FmtResult},
+    ops::Deref,
+};
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Serialize, Deserialize)]
+pub enum TargetPointerWidth {
+    W32Bit,
+    W64Bit,
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub struct FmtOSArchitecture(pub TargetPointerWidth);
+
+impl FmtOSArchitecture {
+    pub const W32_BIT: Self = FmtOSArchitecture(TargetPointerWidth::W32Bit);
+    pub const W64_BIT: Self = FmtOSArchitecture(TargetPointerWidth::W64Bit);
+}
+
+impl Display for FmtOSArchitecture {
+    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
+        write!(
+            f,
+            "{}",
+            match self.0 {
+                TargetPointerWidth::W32Bit => "32 Bit",
+                TargetPointerWidth::W64Bit => "64 Bit",
+            }
+        )
+    }
+}
+
+impl Debug for FmtOSArchitecture {
+    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
+        Display::fmt(self, f)
+    }
+}
+
+impl Serialize for FmtOSArchitecture {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        serializer.serialize_str(&self.to_string())
+    }
+}
+
+impl<'de> Deserialize<'de> for FmtOSArchitecture {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        let s = String::deserialize(deserializer)?;
+        match s.as_str() {
+            "32 Bit" => Ok(FmtOSArchitecture(TargetPointerWidth::W32Bit)),
+            "64 Bit" => Ok(FmtOSArchitecture(TargetPointerWidth::W64Bit)),
+            _ => Err(DeError::custom(format!("Invalid architecture: {}", s))),
+        }
+    }
+}
+
+impl Deref for FmtOSArchitecture {
+    type Target = TargetPointerWidth;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
diff --git a/subcrates/telemetry/src/modules/formatted/date_time.rs b/subcrates/telemetry/src/modules/formatted/date_time.rs
new file mode 100644
index 0000000..efd8bbe
--- /dev/null
+++ b/subcrates/telemetry/src/modules/formatted/date_time.rs
@@ -0,0 +1,144 @@
+use chrono::{DateTime, Local, TimeZone, Utc};
+use serde::{Serialize, Serializer, Deserialize, Deserializer};
+use std::{
+    fmt::{Debug, Display, Formatter, Result as FMTResult},
+    ops::Deref,
+};
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct DateTimeInfo {
+    date: String,
+    time: String,
+}
+
+#[derive(Clone)]
+pub struct FmtDateTime<Tz: TimeZone>(pub DateTime<Tz>);
+
+impl<Tz: TimeZone> Display for FmtDateTime<Tz>
+where
+    Tz: TimeZone,
+    Tz::Offset: std::fmt::Display,
+{
+    fn fmt(&self, f: &mut Formatter<'_>) -> FMTResult {
+        write!(
+            f,
+            "{:#?}",
+            DateTimeInfo {
+                date: self.0.format("%Y-%m-%d").to_string(),
+                time: self.0.format("%H:%M:%S").to_string()
+            }
+        )
+    }
+}
+
+impl<Tz: TimeZone> Debug for FmtDateTime<Tz>
+where
+    Tz::Offset: std::fmt::Display,
+{
+    fn fmt(&self, f: &mut Formatter<'_>) -> FMTResult {
+        Display::fmt(self, f)
+    }
+}
+
+impl<Tz: TimeZone> Serialize for FmtDateTime<Tz>
+where
+    DateTime<Tz>: Display,
+{
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        serializer.serialize_str(&self.to_string())
+    }
+}
+
+impl<'de, Tz> Deserialize<'de> for FmtDateTime<Tz>
+where
+    Tz: TimeZone,
+    DateTime<Tz>: ToString + std::str::FromStr,
+    <DateTime<Tz> as std::str::FromStr>::Err: std::fmt::Display,
+{
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        // 1. Pull a String out of the deserializer
+        let s = String::deserialize(deserializer)?;
+        // 2. Parse it, mapping any parse-error into a Serde error
+        <DateTime<Tz> as std::str::FromStr>::from_str(&s)     // explicit, no ambiguity
+            .map(FmtDateTime)
+            .map_err(serde::de::Error::custom)
+    }
+}
+
+impl<Tz: TimeZone> Deref for FmtDateTime<Tz> {
+    type Target = DateTime<Tz>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+impl<Tz: TimeZone> Eq for FmtDateTime<Tz> where DateTime<Tz>: Eq {}
+impl<Tz: TimeZone> PartialEq for FmtDateTime<Tz>
+where
+    DateTime<Tz>: PartialEq,
+{
+    fn eq(&self, other: &Self) -> bool {
+        self.0 == other.0
+    }
+}
+
+impl<Tz: TimeZone> PartialOrd for FmtDateTime<Tz>
+where
+    DateTime<Tz>: PartialOrd,
+{
+    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+impl<Tz: TimeZone> Ord for FmtDateTime<Tz>
+where
+    DateTime<Tz>: Ord,
+{
+    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+        self.0.cmp(&other.0)
+    }
+}
+
+impl From<FmtDateTime<Utc>> for FmtDateTime<Local> {
+    fn from(dt: FmtDateTime<Utc>) -> Self {
+        FmtDateTime(dt.0.with_timezone(&Local))
+    }
+}
+
+impl From<FmtDateTime<Local>> for FmtDateTime<Utc> {
+    fn from(dt: FmtDateTime<Local>) -> Self {
+        FmtDateTime(dt.0.with_timezone(&Utc))
+    }
+}
+
+#[allow(dead_code)]
+pub trait IntoDateTime {
+    fn into_utc(self) -> FmtDateTime<Utc>;
+    fn into_local(self) -> FmtDateTime<Local>;
+}
+
+impl IntoDateTime for FmtDateTime<Local> {
+    fn into_utc(self) -> FmtDateTime<Utc> {
+        FmtDateTime(self.0.with_timezone(&Utc))
+    }
+    fn into_local(self) -> FmtDateTime<Local> {
+        self
+    }
+}
+
+impl IntoDateTime for FmtDateTime<Utc> {
+    fn into_local(self) -> FmtDateTime<Local> {
+        FmtDateTime(self.0.with_timezone(&Local))
+    }
+    fn into_utc(self) -> FmtDateTime<Utc> {
+        self
+    }
+}
diff --git a/subcrates/telemetry/src/modules/formatted/desktop_environment.rs b/subcrates/telemetry/src/modules/formatted/desktop_environment.rs
new file mode 100644
index 0000000..5876441
--- /dev/null
+++ b/subcrates/telemetry/src/modules/formatted/desktop_environment.rs
@@ -0,0 +1,44 @@
+use std::str::FromStr;
+
+use serde::{Serialize, Deserialize};
+use detect_desktop_environment::DesktopEnvironment;
+use strum_macros::EnumString;
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, EnumString)]
+#[strum(serialize_all = "PascalCase", ascii_case_insensitive)]
+#[non_exhaustive]
+pub enum FmtDE {
+    Cinnamon,
+    Cosmic,
+    CosmicEpoch,
+    Dde,
+    Ede,
+    Endless,
+    Enlightenment,
+    Gnome,
+    Hyprland,
+    Kde,
+    Lxde,
+    Lxqt,
+    MacOs,
+    Mate,
+    Old,
+    Pantheon,
+    Razor,
+    Rox,
+    Sway,
+    Tde,
+    Unity,
+    Windows,
+    Xfce,
+}
+
+impl FmtDE {
+    pub fn detect() -> Option<Self> {
+        DesktopEnvironment::detect().and_then(|inner_de| {
+            let s = format!("{:?}", inner_de);
+            
+            FmtDE::from_str(&s).ok()
+        })
+    }
+}
\ No newline at end of file
diff --git a/subcrates/telemetry/src/modules/formatted/mod.rs b/subcrates/telemetry/src/modules/formatted/mod.rs
new file mode 100644
index 0000000..3eeb56b
--- /dev/null
+++ b/subcrates/telemetry/src/modules/formatted/mod.rs
@@ -0,0 +1,7 @@
+pub mod architecture;
+pub mod date_time;
+pub mod desktop_environment;
+pub mod offset_time;
+pub mod relative_time;
+pub mod version;
+pub mod numbers;
\ No newline at end of file
diff --git a/subcrates/telemetry/src/modules/formatted/numbers.rs b/subcrates/telemetry/src/modules/formatted/numbers.rs
new file mode 100644
index 0000000..7948537
--- /dev/null
+++ b/subcrates/telemetry/src/modules/formatted/numbers.rs
@@ -0,0 +1,54 @@
+use derive_more::{
+    Add, AddAssign, Deref, Div, DivAssign, From, Mul, MulAssign, Sub, SubAssign,
+};
+use serde::{Serialize, Deserialize};
+use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
+
+enum DisplayKind {
+    Plural(&'static str, &'static str),
+    HumanBytes,
+}
+
+macro_rules! define_fmt_wrapper {
+    ($name:ident, $ty:ty, $display_kind:expr) => {
+        #[derive(
+            Copy, Clone, PartialEq, Eq, PartialOrd, Ord,
+            Add, Sub, Mul, Div,
+            AddAssign, SubAssign, MulAssign, DivAssign,
+            From, Deref, Serialize, Deserialize
+        )]
+        pub struct $name(pub $ty);
+
+        impl $name {
+            #[allow(dead_code)]
+            pub fn new(value: $ty) -> Self {
+                Self(value)
+            }
+        }
+
+        impl Display for $name {
+            fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
+                match $display_kind {
+                    DisplayKind::Plural(singular, plural) => {
+                        let label = if self.0 == 1 { singular } else { plural };
+                        write!(f, "{} {}", self.0, label)
+                    },
+                    DisplayKind::HumanBytes => {
+                        use humansize::{format_size, DECIMAL};
+                        write!(f, "{}", format_size(self.0, DECIMAL))
+                    },
+                }
+            }
+        }
+
+        impl Debug for $name {
+            fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
+                Display::fmt(self, f)
+            }
+        }
+    };
+}
+
+define_fmt_wrapper!(FmtThreads, u16, DisplayKind::Plural("Thread", "Threads"));
+define_fmt_wrapper!(FmtCores, usize, DisplayKind::Plural("Core", "Cores"));
+define_fmt_wrapper!(FmtBytes, u64, DisplayKind::HumanBytes);
\ No newline at end of file
diff --git a/subcrates/telemetry/src/modules/formatted/offset_time.rs b/subcrates/telemetry/src/modules/formatted/offset_time.rs
new file mode 100644
index 0000000..37e4fab
--- /dev/null
+++ b/subcrates/telemetry/src/modules/formatted/offset_time.rs
@@ -0,0 +1,66 @@
+use chrono::{DateTime, Local, Offset};
+use derive_more::{Deref, Display, From};
+use serde::{Serialize, Serializer, Deserialize, Deserializer};
+use std::{
+    cmp::Ordering,
+    fmt::{Debug, Display, Formatter, Result as FmtResult},
+};
+
+#[derive(Copy, Clone, From, Deref, Display)]
+#[display("UTC {}", "_0.offset()")]
+pub struct FmtOffsetTime(pub DateTime<Local>);
+
+impl Debug for FmtOffsetTime {
+    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
+        Display::fmt(self, f)
+    }
+}
+
+impl Serialize for FmtOffsetTime {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        serializer.serialize_str(&self.to_string())
+    }
+}
+
+impl<'de> Deserialize<'de> for FmtOffsetTime {
+    fn deserialize<D>(deserializer: D) -> Result<FmtOffsetTime, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        // Deserialize the input as a string
+        let s = String::deserialize(deserializer)?;
+
+        // Attempt to parse the string into a DateTime<Local>
+        match DateTime::parse_from_rfc3339(&s) {
+            Ok(dt) => Ok(FmtOffsetTime(dt.with_timezone(&Local))),
+            Err(e) => Err(serde::de::Error::custom(format!(
+                "Failed to parse DateTime: {}",
+                e
+            ))),
+        }
+    }
+}
+
+impl Eq for FmtOffsetTime {}
+impl PartialEq for FmtOffsetTime {
+    fn eq(&self, other: &Self) -> bool {
+        self.0.offset().fix() == other.0.offset().fix()
+    }
+}
+
+impl PartialOrd for FmtOffsetTime {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        Some(
+            self.0.offset().fix().local_minus_utc().cmp(&other.0.offset().fix().local_minus_utc()),
+        )
+    }
+}
+
+impl Ord for FmtOffsetTime {
+    fn cmp(&self, other: &Self) -> Ordering {
+        self.0.offset().fix().local_minus_utc().cmp(&other.0.offset().fix().local_minus_utc())
+    }
+}
diff --git a/subcrates/telemetry/src/modules/formatted/relative_time.rs b/subcrates/telemetry/src/modules/formatted/relative_time.rs
new file mode 100644
index 0000000..2933fea
--- /dev/null
+++ b/subcrates/telemetry/src/modules/formatted/relative_time.rs
@@ -0,0 +1,50 @@
+use derive_more::{From, Deref, with_trait::Display as Display};
+use serde::{Serialize, Serializer, Deserialize, Deserializer};
+use std::{
+    fmt::{Debug, Formatter, Result as FmtResult},
+    time::Duration,
+};
+
+#[derive(
+    Clone,
+    PartialEq,
+    Eq,
+    PartialOrd,
+    Ord,
+    From,
+    Deref,
+)]
+pub struct FmtRelativeTime(pub Duration);
+
+impl Display for FmtRelativeTime {
+    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
+        let formatter = timeago::Formatter::new();
+
+        write!(f, "{}", formatter.convert(self.0))
+    }
+}
+
+impl Debug for FmtRelativeTime {
+    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
+        Display::fmt(self, f)
+    }
+}
+
+impl Serialize for FmtRelativeTime {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        serializer.serialize_u64(self.0.as_secs())
+    }
+}
+
+impl<'de> Deserialize<'de> for FmtRelativeTime {
+    fn deserialize<D>(deserializer: D) -> Result<FmtRelativeTime, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        let secs = u64::deserialize(deserializer)?;
+        Ok(FmtRelativeTime(Duration::from_secs(secs)))
+    }
+}
\ No newline at end of file
diff --git a/subcrates/telemetry/src/modules/formatted/version.rs b/subcrates/telemetry/src/modules/formatted/version.rs
new file mode 100644
index 0000000..b83d888
--- /dev/null
+++ b/subcrates/telemetry/src/modules/formatted/version.rs
@@ -0,0 +1,38 @@
+use derive_more::{Display, From, Deref, with_trait::Display as TDisplay};
+use serde::{Serialize, Serializer, Deserialize, Deserializer};
+use std::{
+    fmt::{Debug, Formatter, Result as FmtResult},
+    str::FromStr,
+};
+use versions::Versioning;
+
+#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, From, Deref, Display)]
+#[display("{_0}")]
+pub struct FmtVersion(pub Versioning);
+
+impl Debug for FmtVersion {
+    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
+        TDisplay::fmt(self, f)
+    }
+}
+
+impl Serialize for FmtVersion {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        serializer.serialize_str(&self.to_string())
+    }
+}
+
+impl<'de> Deserialize<'de> for FmtVersion {
+    fn deserialize<D>(deserializer: D) -> Result<FmtVersion, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        let s = String::deserialize(deserializer)?;
+        Versioning::from_str(&s)
+            .map(FmtVersion)
+            .map_err(serde::de::Error::custom)
+    }
+}
\ No newline at end of file
diff --git a/subcrates/telemetry/src/modules/gpu/mod.rs b/subcrates/telemetry/src/modules/gpu/mod.rs
new file mode 100644
index 0000000..f1206ad
--- /dev/null
+++ b/subcrates/telemetry/src/modules/gpu/mod.rs
@@ -0,0 +1,131 @@
+use super::UNKNOWN;
+use serde::{Serialize, Deserialize};
+use serde_json::{Value, json};
+use wgpu;
+use winit::{
+    application::ApplicationHandler,
+    event::WindowEvent,
+    event_loop::{ActiveEventLoop, EventLoop},
+    window::WindowId,
+};
+
+mod vram;
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct DriverInfo {
+    version: String,
+    name: String,
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct AdapterInfo {
+    vendor: String,
+    model: String,
+    driver: DriverInfo,
+    vram: String,
+    display: DisplayInfo,
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct GPUInfo {
+    supported_backends: Vec<String>,
+    gpus: Vec<AdapterInfo>,
+}
+
+#[derive(Debug, Serialize, Deserialize, Default)]
+pub struct DisplayInfo {
+    resolution: String,
+    refresh_rate: String,
+}
+
+impl ApplicationHandler for DisplayInfo {
+    fn resumed(&mut self, event_loop: &ActiveEventLoop) {
+        if let Some(monitor) = event_loop.primary_monitor() {
+            let size = monitor.size();
+            let refresh_rate = monitor.refresh_rate_millihertz();
+
+            self.resolution = format!("{}x{}", size.width, size.height);
+            self.refresh_rate = if let Some(refresh) = refresh_rate {
+                format!("{} hz", refresh / 1000)
+            } else {
+                UNKNOWN.to_string()
+            }
+        } else {
+            self.resolution = UNKNOWN.to_string();
+            self.refresh_rate = UNKNOWN.to_string();
+        }
+
+        event_loop.exit();
+    }
+
+    fn window_event(
+        &mut self,
+        _event_loop: &ActiveEventLoop,
+        _window_id: WindowId,
+        _event: WindowEvent,
+    ) {
+    }
+}
+
+fn vendor_name(vendor: u32) -> &'static str {
+    match vendor {
+        0x10DE => "NVIDIA",
+        0x1002 => "AMD(Advanced Micro Devices), Inc.",
+        0x8086 => "Intel(integrated electronics)",
+        0x13B5 => "ARM(Advanced RISC Machines)",
+        0x5143 => "Qualcomm(Quality Communications)",
+        0x1010 => "Apple Inc.",
+        _ => UNKNOWN,
+    }
+}
+
+#[allow(dead_code)]
+pub fn get_struct() -> GPUInfo {
+    let mut gpu_data: Vec<AdapterInfo> = Vec::new();
+    let mut backends: Vec<String> = Vec::new();
+    let instance_descriptor = wgpu::InstanceDescriptor {
+        backends: wgpu::Backends::all(),
+        flags: wgpu::InstanceFlags::empty(),
+        backend_options: Default::default(),
+    };
+    let instance = wgpu::Instance::new(&instance_descriptor);
+    let event_loop = EventLoop::new().unwrap();
+    let mut app = DisplayInfo::default();
+
+    event_loop.run_app(&mut app).unwrap();
+
+    for adapter in instance.enumerate_adapters(wgpu::Backends::all()) {
+        let info = adapter.get_info();
+
+        if !backends.contains(&info.backend.to_string()) {
+            backends.push(info.backend.to_string());
+        }
+        if info.driver.is_empty() || info.driver_info.is_empty() {
+            continue;
+        }
+
+        gpu_data.push(AdapterInfo {
+            vendor: vendor_name(info.vendor).to_string(),
+            model: info.name,
+            driver: DriverInfo {
+                version: info.driver_info,
+                name: info.driver,
+            },
+            vram: vram::get(info.vendor, info.device),
+            display: DisplayInfo {
+                resolution: app.resolution.to_string(),
+                refresh_rate: app.refresh_rate.to_string(),
+            },
+        });
+    }
+
+    GPUInfo {
+        supported_backends: backends,
+        gpus: gpu_data,
+    }
+}
+
+#[allow(dead_code)]
+pub fn get_json() -> Value {
+    json!(get_struct())
+}
diff --git a/subcrates/telemetry/src/modules/gpu/vram.rs b/subcrates/telemetry/src/modules/gpu/vram.rs
new file mode 100644
index 0000000..3044104
--- /dev/null
+++ b/subcrates/telemetry/src/modules/gpu/vram.rs
@@ -0,0 +1,67 @@
+use super::super::UNKNOWN;
+use ash::vk::{API_VERSION_1_2, ApplicationInfo, InstanceCreateInfo, MemoryHeapFlags};
+use humansize::{DECIMAL, make_format};
+
+#[cfg(not(target_os = "macos"))]
+pub fn get_metal() -> u64 {
+    0
+}
+
+#[cfg(target_os = "macos")]
+pub fn get_metal() -> u64 {
+    use metal::Device as MetalDevice;
+
+    let device = MetalDevice::system_default().expect("No Metal-compatible GPU found");
+    device.recommended_max_working_set_size()
+}
+
+pub fn get_vulkan(device_id: u32) -> u64 {
+    let entry = unsafe { ash::Entry::load().unwrap() };
+    let app_info = ApplicationInfo {
+        p_application_name: std::ptr::null(),
+        application_version: 0,
+        p_engine_name: std::ptr::null(),
+        engine_version: 0,
+        api_version: API_VERSION_1_2,
+        ..Default::default()
+    };
+
+    let create_info = InstanceCreateInfo {
+        p_application_info: &app_info,
+        ..Default::default()
+    };
+
+    let instance = unsafe { entry.create_instance(&create_info, None).unwrap() };
+
+    let physical_devices = unsafe { instance.enumerate_physical_devices().unwrap() };
+    let mut total_vram = 0;
+
+    for device in physical_devices {
+        let memory_properties = unsafe { instance.get_physical_device_memory_properties(device) };
+        let device_properties = unsafe { instance.get_physical_device_properties(device) };
+
+        if device_id != device_properties.device_id {
+            continue;
+        }
+
+        for heap in memory_properties.memory_heaps {
+            if heap.flags.contains(MemoryHeapFlags::DEVICE_LOCAL) {
+                total_vram += heap.size;
+            }
+        }
+
+        break;
+    }
+
+    total_vram
+}
+
+pub fn get(vendor: u32, device_id: u32) -> String {
+    let formatter = make_format(DECIMAL);
+
+    match vendor {
+        0x10DE | 0x1002 | 0x8086 | 0x5143 => formatter(get_vulkan(device_id)),
+        0x1010 => formatter(get_metal()),
+        _ => UNKNOWN.to_string(),
+    }
+}
diff --git a/subcrates/telemetry/src/modules/memory.rs b/subcrates/telemetry/src/modules/memory.rs
new file mode 100644
index 0000000..9888d3a
--- /dev/null
+++ b/subcrates/telemetry/src/modules/memory.rs
@@ -0,0 +1,50 @@
+use crate::modules::FmtBytes;
+use serde::{Serialize, Deserialize};
+use serde_json::{Value, json};
+use sysinfo::System;
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct PhysicalInfo {
+    total: FmtBytes,
+    used: FmtBytes,
+    free: FmtBytes,
+    available: FmtBytes,
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct VirtualInfo {
+    total: FmtBytes,
+    used: FmtBytes,
+    free: FmtBytes,
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct MemoryInfo {
+    physical: PhysicalInfo,
+    virtual_swap: VirtualInfo,
+}
+
+#[allow(dead_code)]
+pub fn get_struct() -> MemoryInfo {
+    let mut system = System::new_all();
+    system.refresh_memory();
+
+    MemoryInfo {
+        physical: PhysicalInfo {
+            total: FmtBytes(system.total_memory()),
+            used: FmtBytes(system.used_memory()),
+            free: FmtBytes(system.free_memory()),
+            available: FmtBytes(system.available_memory()),
+        },
+        virtual_swap: VirtualInfo {
+            total: FmtBytes(system.total_swap()),
+            used: FmtBytes(system.used_swap()),
+            free: FmtBytes(system.free_swap()),
+        },
+    }
+}
+
+#[allow(dead_code)]
+pub fn get_json() -> Value {
+    json!(get_struct())
+}
diff --git a/subcrates/telemetry/src/modules/meta.rs b/subcrates/telemetry/src/modules/meta.rs
new file mode 100644
index 0000000..60d94b3
--- /dev/null
+++ b/subcrates/telemetry/src/modules/meta.rs
@@ -0,0 +1,40 @@
+use crate::modules::FmtVersion;
+use serde::{Serialize, Deserialize};
+use serde_json::{Value, json};
+use sys_locale::get_locale;
+use thiserror::Error;
+use versions::Versioning;
+
+include!(concat!(env!("OUT_DIR"), "/built.rs"));
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct MetaInfo {
+    version: FmtVersion,
+    commit_hash: String,
+    locale: String,
+}
+
+#[derive(Debug, Error)]
+pub enum MetaError {
+    #[error("Invalid Semver version")]
+    InvalidVersion,
+    #[error("Failed to build JSON for MetaInfo")]
+    JsonError(#[from] serde_json::Error),
+}
+
+#[allow(dead_code)]
+pub fn get_struct() -> Result<MetaInfo, MetaError> {
+    let locale = get_locale().unwrap_or_else(|| String::from("en-US"));
+    let version = Versioning::new(env!("CARGO_PKG_VERSION")).ok_or(MetaError::InvalidVersion)?;
+
+    Ok(MetaInfo {
+        version: FmtVersion(version),
+        commit_hash: GIT_COMMIT_HASH.to_string(),
+        locale,
+    })
+}
+
+#[allow(dead_code)]
+pub fn get_json() -> Result<Value, MetaError> {
+    Ok(json!(get_struct()?))
+}
diff --git a/subcrates/telemetry/src/modules/mod.rs b/subcrates/telemetry/src/modules/mod.rs
new file mode 100644
index 0000000..d714f7b
--- /dev/null
+++ b/subcrates/telemetry/src/modules/mod.rs
@@ -0,0 +1,26 @@
+pub mod cpu;
+pub mod devices;
+pub mod external;
+pub mod formatted;
+pub mod gpu;
+pub mod memory;
+pub mod meta;
+pub mod network;
+pub mod os;
+pub mod storage;
+pub mod uptime;
+
+#[allow(unused_imports)]
+pub use formatted::{
+    architecture::FmtOSArchitecture,
+    numbers::FmtBytes,
+    date_time::{FmtDateTime, IntoDateTime},
+    desktop_environment::FmtDE,
+    offset_time::FmtOffsetTime,
+    relative_time::FmtRelativeTime,
+    version::FmtVersion,
+    numbers::FmtCores,
+    numbers::FmtThreads,
+};
+
+pub const UNKNOWN: &str = "Unknown";
diff --git a/subcrates/telemetry/src/modules/network.rs b/subcrates/telemetry/src/modules/network.rs
new file mode 100644
index 0000000..02155d9
--- /dev/null
+++ b/subcrates/telemetry/src/modules/network.rs
@@ -0,0 +1,21 @@
+use serde::{Serialize, Deserialize};
+use serde_json::{Value, json};
+use sysinfo::Networks;
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct NetworkInfo {
+    adapters: Vec<String>,
+}
+
+#[allow(dead_code)]
+pub fn get_struct() -> NetworkInfo {
+    let networks = Networks::new_with_refreshed_list();
+    let adapters = networks.iter().map(|(name, _)| name.to_string()).collect();
+
+    NetworkInfo { adapters }
+}
+
+#[allow(dead_code)]
+pub fn get_json() -> Value {
+    json!(get_struct())
+}
diff --git a/subcrates/telemetry/src/modules/os.rs b/subcrates/telemetry/src/modules/os.rs
new file mode 100644
index 0000000..8a121a7
--- /dev/null
+++ b/subcrates/telemetry/src/modules/os.rs
@@ -0,0 +1,59 @@
+use super::UNKNOWN;
+use crate::modules::{FmtDE, FmtOSArchitecture};
+use serde::{Serialize, Deserialize};
+use serde_json::{Value, json};
+use sysinfo::System;
+use thiserror::Error;
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct OSInfo {
+    name: String,
+    edition: String,
+    version: String,
+    architecture: FmtOSArchitecture,
+    kernel: Option<String>,
+    desktop_environment: Option<FmtDE>,
+}
+
+#[derive(Debug, Error)]
+pub enum OSError {
+    #[error("Unsupported pointer width: 16 bit systems are not supported")]
+    UnsupportedPointerWidth,
+    #[error("Failed to build JSON for OSInfo")]
+    JsonError(#[from] serde_json::Error),
+}
+
+#[allow(dead_code)]
+pub fn get_struct() -> Result<OSInfo, OSError> {
+    let architecture = match std::mem::size_of::<usize>() {
+        8 => Ok(FmtOSArchitecture::W64_BIT),
+        4 => Ok(FmtOSArchitecture::W32_BIT),
+        _ => Err(OSError::UnsupportedPointerWidth),
+    }?;
+
+    let kernel = if cfg!(target_os = "linux") || cfg!(target_os = "macos") {
+        Some(System::kernel_long_version())
+    } else {
+        None
+    };
+
+    let desktop_environment = if cfg!(target_os = "linux") {
+        FmtDE::detect()
+    } else {
+        None
+    };
+
+    Ok(OSInfo {
+        name: System::name().unwrap_or(UNKNOWN.to_string()),
+        edition: System::long_os_version().unwrap_or(UNKNOWN.to_string()),
+        version: System::os_version().unwrap_or(UNKNOWN.to_string()),
+        architecture,
+        kernel,
+        desktop_environment,
+    })
+}
+
+#[allow(dead_code)]
+pub fn get_json() -> Result<Value, OSError> {
+    Ok(json!(get_struct()?))
+}
diff --git a/subcrates/telemetry/src/modules/storage.rs b/subcrates/telemetry/src/modules/storage.rs
new file mode 100644
index 0000000..625e611
--- /dev/null
+++ b/subcrates/telemetry/src/modules/storage.rs
@@ -0,0 +1,49 @@
+use crate::modules::FmtBytes;
+use serde::{Serialize, Deserialize};
+use serde_json::{Value, json};
+pub use sysinfo::DiskKind;
+use sysinfo::Disks;
+use thiserror::Error;
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct StorageInfo {
+    name: String,
+    mount_point: String,
+    disk_kind: DiskKind,
+    space_left: FmtBytes,
+    total: FmtBytes,
+}
+
+#[derive(Debug, Error)]
+pub enum StorageError {
+    #[error("No storage drives found")]
+    NoDisksFound,
+    #[error("Failed to build JSON for Vec<StorageInfo>")]
+    JsonError(#[from] serde_json::Error),
+}
+
+#[allow(dead_code)]
+pub fn get_list() -> Result<Vec<StorageInfo>, StorageError> {
+    let disks = Disks::new_with_refreshed_list();
+    let disk_list = disks.list();
+
+    if disk_list.is_empty() {
+        return Err(StorageError::NoDisksFound);
+    }
+
+    Ok(disk_list
+        .iter()
+        .map(|disk| StorageInfo {
+            name: disk.name().to_string_lossy().into_owned(),
+            mount_point: disk.mount_point().to_string_lossy().replace('\\', ""),
+            disk_kind: disk.kind(),
+            space_left: FmtBytes(disk.available_space()),
+            total: FmtBytes(disk.total_space()),
+        })
+        .collect())
+}
+
+#[allow(dead_code)]
+pub fn get_json() -> Result<Value, StorageError> {
+    Ok(json!(get_list()?))
+}
diff --git a/subcrates/telemetry/src/modules/uptime.rs b/subcrates/telemetry/src/modules/uptime.rs
new file mode 100644
index 0000000..c6bed58
--- /dev/null
+++ b/subcrates/telemetry/src/modules/uptime.rs
@@ -0,0 +1,71 @@
+use crate::modules::{FmtDateTime, FmtOffsetTime, FmtRelativeTime};
+use chrono::{DateTime, Local, Utc, TimeZone};
+use serde::{Serialize, Serializer, Deserialize};
+use serde_json::{Value, json};
+use std::{
+    time::Duration as StdDuration,
+    fmt::Display,
+};
+use sysinfo::System;
+use thiserror::Error;
+
+fn serialize_datetime<S, Tz>(date: &DateTime<Tz>, serializer: S) -> Result<S::Ok, S::Error>
+where
+    S: Serializer,
+    Tz::Offset: Display,
+    Tz: TimeZone,
+{
+    let formatted = date.format("%+").to_string(); // Format as RFC 3339
+    serializer.serialize_str(&formatted)
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+struct DateInfo {
+    #[serde(serialize_with = "serialize_datetime")]
+    time_offset: FmtOffsetTime,
+    #[serde(serialize_with = "serialize_datetime")]
+    local_date_time: FmtDateTime<Local>,
+    #[serde(serialize_with = "serialize_datetime")]
+    utc_date_time: FmtDateTime<Utc>,
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct UptimeInfo {
+    boot: DateInfo,
+    now: DateInfo,
+    relative: FmtRelativeTime,
+}
+
+#[derive(Debug, Error)]
+pub enum UptimeError {
+    #[error("Invalid or out of range timestamp: check seconds < 8_220_000_000_000")]
+    InvalidTimestamp,
+    #[error("Failed to build JSON for UptimeInfo")]
+    JsonError(#[from] serde_json::Error),
+}
+
+#[allow(dead_code)]
+pub fn get_struct() -> Result<UptimeInfo, UptimeError> {
+    let boot_time_utc = DateTime::<Utc>::from_timestamp(System::boot_time() as i64, 0)
+        .ok_or(UptimeError::InvalidTimestamp)?;
+    let boot_time_local: DateTime<Local> = boot_time_utc.with_timezone(&Local);
+    let relative_time =
+        StdDuration::from_secs((Local::now() - boot_time_local).num_seconds() as u64);
+
+    let make_info = |utc_dt: DateTime<Utc>| DateInfo {
+        time_offset: FmtOffsetTime(Local::now()),
+        local_date_time: FmtDateTime(utc_dt.with_timezone(&Local)),
+        utc_date_time: FmtDateTime(utc_dt),
+    };
+    
+    Ok(UptimeInfo {
+        boot: make_info(boot_time_utc),
+        now: make_info(Utc::now()),
+        relative: FmtRelativeTime(relative_time),
+    })
+}
+
+#[allow(dead_code)]
+pub fn get_json() -> Result<Value, UptimeError> {
+    Ok(json!(get_struct()?))
+}
diff --git a/subcrates/zlog/src/config.rs b/subcrates/zlog/src/config.rs
index dc50fbe..b540573 100644
--- a/subcrates/zlog/src/config.rs
+++ b/subcrates/zlog/src/config.rs
@@ -74,7 +74,7 @@ impl LoggerConfig {
         self.log_json_show_message = i;
         self
     }
-    
+
     pub fn log_json_show_additional_fields(mut self, i: bool) -> Self {
         self.log_json_show_additional_fields = i;
         self
@@ -96,7 +96,7 @@ impl Default for LoggerConfig {
             log_json_show_timestamp: false,
             log_json_show_level: false,
             log_json_show_message: false,
-            log_json_show_additional_fields: false
+            log_json_show_additional_fields: false,
         }
     }
 }
diff --git a/subcrates/zlog/src/lib.rs b/subcrates/zlog/src/lib.rs
index e6b64a4..0399099 100644
--- a/subcrates/zlog/src/lib.rs
+++ b/subcrates/zlog/src/lib.rs
@@ -113,7 +113,7 @@ where
             level,
             message,
             #[cfg(feature = "json")]
-            additional_fields
+            additional_fields,
         });
 
         if let LogEvent::Log(ref entry) = log_entry {
@@ -212,13 +212,7 @@ impl Logger {
                 for msg in rx {
                     match msg {
                         LogEvent::Log(mut entry) => {
-                            println!(
-                                "{}",
-                                format_entry(
-                                    &mut entry,
-                                    &config_clone
-                                )
-                            );
+                            println!("{}", format_entry(&mut entry, &config_clone));
                         }
                         LogEvent::Shutdown => break,
                     }
@@ -298,12 +292,12 @@ fn format_entry(entry: &mut LogEntry, log_config: &LoggerConfig) -> String {
     if log_config.log_use_json {
         return format_entry_json(entry, log_config);
     }
-    
+
     if log_config.log_to_stdout || log_config.log_to_file {
         return format_entry_string(entry, log_config);
     } else {
         return String::new();
-    } 
+    }
 }
 
 fn format_entry_string(entry: &LogEntry, log_config: &LoggerConfig) -> String {
@@ -327,7 +321,10 @@ fn format_entry_json(entry: &mut LogEntry, log_config: &LoggerConfig) -> String
     let mut json_object = serde_json::Map::new();
 
     if log_config.log_json_show_timestamp {
-        json_object.insert("timestamp".to_string(), Value::String(DateTime::<Utc>::from(entry.timestamp).to_rfc3339()));
+        json_object.insert(
+            "timestamp".to_string(),
+            Value::String(DateTime::<Utc>::from(entry.timestamp).to_rfc3339()),
+        );
     }
 
     if log_config.log_json_show_level {
@@ -335,7 +332,10 @@ fn format_entry_json(entry: &mut LogEntry, log_config: &LoggerConfig) -> String
     }
 
     if log_config.log_json_show_message {
-        json_object.insert("message".to_string(), Value::String(entry.message.to_string()));
+        json_object.insert(
+            "message".to_string(),
+            Value::String(entry.message.to_string()),
+        );
     }
 
     if log_config.log_json_show_additional_fields {
@@ -343,4 +343,4 @@ fn format_entry_json(entry: &mut LogEntry, log_config: &LoggerConfig) -> String
     }
 
     serde_json::to_string(&json_object).unwrap()
-}
\ No newline at end of file
+}
diff --git a/subcrates/zlog/src/tests.rs b/subcrates/zlog/src/tests.rs
index 13c90ab..930b909 100644
--- a/subcrates/zlog/src/tests.rs
+++ b/subcrates/zlog/src/tests.rs
@@ -1,7 +1,7 @@
 use pretty_assertions::assert_eq;
-use tracing::Level;
+use serde::{Deserialize, Serialize};
 use serde_json::Map;
-use serde::{Serialize, Deserialize};
+use tracing::Level;
 
 use super::*;
 
@@ -126,9 +126,15 @@ fn test_logger_sequential_consistency_json() {
 
     for log in logger.get_logs(LogQuery::All) {
         let mut json_object = serde_json::Map::new();
-        json_object.insert("timestamp".to_string(), Value::String(DateTime::<Utc>::from(log.timestamp).to_rfc3339()));
+        json_object.insert(
+            "timestamp".to_string(),
+            Value::String(DateTime::<Utc>::from(log.timestamp).to_rfc3339()),
+        );
         json_object.insert("level".to_string(), Value::String(log.level.to_string()));
-        json_object.insert("message".to_string(), Value::String(log.message.to_string()));
+        json_object.insert(
+            "message".to_string(),
+            Value::String(log.message.to_string()),
+        );
 
         log_json.push(json_object);
     }
@@ -136,4 +142,4 @@ fn test_logger_sequential_consistency_json() {
     for log in log_json {
         serde_json::to_string(&log).unwrap();
     }
-}
\ No newline at end of file
+}