diff --git a/Cargo.lock b/Cargo.lock
index 1e26c619a77fd6cf2cb1e7ff0167416b3c0e1ac8..e7f6780c81e09818f4da169a0fbd7c08c88d184d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -55,6 +55,15 @@ dependencies = [
  "syn 2.0.95",
 ]
 
+[[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.4.0"
@@ -76,6 +85,15 @@ dependencies = [
  "windows-targets",
 ]
 
+[[package]]
+name = "bincode"
+version = "1.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
+dependencies = [
+ "serde",
+]
+
 [[package]]
 name = "bitflags"
 version = "1.3.2"
@@ -153,6 +171,12 @@ dependencies = [
  "windows",
 ]
 
+[[package]]
+name = "bytemuck"
+version = "1.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3"
+
 [[package]]
 name = "byteorder"
 version = "1.5.0"
@@ -324,6 +348,20 @@ dependencies = [
  "log",
 ]
 
+[[package]]
+name = "figment"
+version = "0.10.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3"
+dependencies = [
+ "atomic",
+ "pear",
+ "serde",
+ "toml",
+ "uncased",
+ "version_check",
+]
+
 [[package]]
 name = "fixedbitset"
 version = "0.4.2"
@@ -491,6 +529,12 @@ dependencies = [
  "hashbrown 0.15.2",
 ]
 
+[[package]]
+name = "inlinable_string"
+version = "0.1.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb"
+
 [[package]]
 name = "io-kit-sys"
 version = "0.4.1"
@@ -814,6 +858,29 @@ dependencies = [
  "windows-targets",
 ]
 
+[[package]]
+name = "pear"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bdeeaa00ce488657faba8ebf44ab9361f9365a97bd39ffb8a60663f57ff4b467"
+dependencies = [
+ "inlinable_string",
+ "pear_codegen",
+ "yansi",
+]
+
+[[package]]
+name = "pear_codegen"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4bab5b985dc082b345f812b7df84e1bef27e7207b39e448439ba8bd69c93f147"
+dependencies = [
+ "proc-macro2",
+ "proc-macro2-diagnostics",
+ "quote",
+ "syn 2.0.95",
+]
+
 [[package]]
 name = "petgraph"
 version = "0.6.5"
@@ -870,6 +937,19 @@ 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.95",
+ "version_check",
+ "yansi",
+]
+
 [[package]]
 name = "prost"
 version = "0.11.9"
@@ -1072,6 +1152,16 @@ dependencies = [
  "smallvec",
 ]
 
+[[package]]
+name = "rusqlite_migration"
+version = "1.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "923b42e802f7dc20a0a6b5e097ba7c83fe4289da07e49156fecf6af08aa9cd1c"
+dependencies = [
+ "log",
+ "rusqlite",
+]
+
 [[package]]
 name = "rustc-demangle"
 version = "0.1.24"
@@ -1156,6 +1246,15 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "serde_spanned"
+version = "0.6.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
+dependencies = [
+ "serde",
+]
+
 [[package]]
 name = "serialport"
 version = "4.6.1"
@@ -1383,6 +1482,40 @@ dependencies = [
  "tokio",
 ]
 
+[[package]]
+name = "toml"
+version = "0.8.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
+dependencies = [
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_edit",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.6.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.22.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
+dependencies = [
+ "indexmap",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "winnow",
+]
+
 [[package]]
 name = "tracing"
 version = "0.1.41"
@@ -1440,6 +1573,15 @@ dependencies = [
  "tracing-log",
 ]
 
+[[package]]
+name = "uncased"
+version = "0.9.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697"
+dependencies = [
+ "version_check",
+]
+
 [[package]]
 name = "unescaper"
 version = "0.1.5"
@@ -1673,6 +1815,15 @@ version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
 
+[[package]]
+name = "winnow"
+version = "0.6.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a"
+dependencies = [
+ "memchr",
+]
+
 [[package]]
 name = "xml-rs"
 version = "0.8.24"
@@ -1683,16 +1834,27 @@ checksum = "ea8b391c9a790b496184c29f7f93b9ed5b16abb306c05415b68bcc16e4d06432"
 name = "yamm"
 version = "0.1.0"
 dependencies = [
+ "bincode",
  "fern",
+ "figment",
  "humantime",
+ "lazy_static",
  "log",
  "meshtastic",
  "rusqlite",
+ "rusqlite_migration",
+ "serde",
  "tokio",
  "tracing",
  "tracing-subscriber",
 ]
 
+[[package]]
+name = "yansi"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
+
 [[package]]
 name = "zerocopy"
 version = "0.7.35"
diff --git a/Cargo.toml b/Cargo.toml
index 87e89615d2734295c545f65001517f4ae12f7ba3..6ee62428f42e162bb422c753d4ebec04180262da 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -11,4 +11,9 @@ meshtastic = "0.1.6"
 tokio = "1.42.0"
 tracing = "0.1.41"
 tracing-subscriber = "0.3.19"
-rusqlite = { version = "0.32.0", features = ["bundled"] }
\ No newline at end of file
+rusqlite = { version = "0.32.0", features = ["bundled"] }
+rusqlite_migration = "1.3.1"
+lazy_static = "1.5.0"
+figment =  {version = "0.10.19", features = ["toml", "env"]}
+serde = "1.0.217"
+bincode = "1.3.3"
diff --git a/README.md b/README.md
index b9c2fe5db508052691d217680d1af865aca67994..f9d7523c6e5c57b1f64ac3c54177fc896ee0d6c0 100644
--- a/README.md
+++ b/README.md
@@ -13,12 +13,12 @@
 
 **Y**et **A**nother **M**eshtastic **M**ap
 
-Inspired slightly by MeshInfo.
+Inspired slightly by [MeshInfo](https://github.com/MeshAddicts/meshinfo).
 
 
 ## What it does
 
-- Logs meshtastic broadcasts
+- Logs meshtastic packets
 - Saves them to a database
 - Serves the data to a future GUI
 - Serves the data to a public webpage
@@ -26,8 +26,8 @@ Inspired slightly by MeshInfo.
 ## What it doesn't
 
 - Runs meshtastic
-- Had a GUI (yet)
-- Lets you send messages (yet)
+- Had a GUI (yet?)
+- Lets you send messages (yet?)
 
 ## Installation
 
diff --git a/config.toml b/config.toml
index 8305c6f1db962f208e32132bab2e3bad66ef0265..f7875a7df010c7f50101d81867f16bd4aa8c2e38 100644
--- a/config.toml
+++ b/config.toml
@@ -1,18 +1,20 @@
-[config]
+[server]
+host = "0.0.0.0"
+port = 8080
 
-# Connection Type
-# Can be one of the following: Serial, Tcp, Mqtt
-connection_type = "Tcp"
+[database]
+db_name = "yamm.db"
 
-# Serial Port
-## If connection_type is Serial, use this device to connect
-serial_port = "/dev/something"
+[logging]
+level = "yamm=debug,rusqlite=debug,meshtastic=debug"
 
-# IP Port
-## If connection_type is Tcp, use this ip & port combo to connect
-ip_port = "127.0.0.1:4403"
+[meshconfig]
+# How should we connect to the meshtastic node? (Tcp, Serial, Mqtt)
+connection_type = "Tcp"
 
-# MQTT
-## If connection_type is MQTT, use this to connect.
-### TODO!
+## If connection_type is Serial, use this device to connect
+serial_port = "Set this from whatever 'available ports' says."
 
+## If connection_type is TCP or MQTT, use this ip & port combo to connect
+ip = "192.168.1.128"
+port = 4403
\ No newline at end of file
diff --git a/db/migrations/0001_initial.sql b/db/migrations/0001_initial.sql
new file mode 100644
index 0000000000000000000000000000000000000000..3e73ef9107486af051da2ff8e893d248c89c0921
--- /dev/null
+++ b/db/migrations/0001_initial.sql
@@ -0,0 +1,314 @@
+CREATE TABLE MyNodeInfo (
+    my_node_num INTEGER PRIMARY KEY,
+    reboot_count INTEGER NOT NULL,
+    min_app_version INTEGER NOT NULL
+);
+
+CREATE TABLE Node (
+    id INTEGER PRIMARY KEY,     -- internal yamm packet numbering
+    num INT,                    -- "node number" wtf does that mean???
+    user_id TEXT,               -- the typical !c6744jd whatever code
+    user_lname TEXT,            -- User's Long name
+    user_sname TEXT,            -- User's Short name
+    -- mac_addr BLOB            -- Deprecated in 2.1.x, esp32 only.
+    hw_model TEXT,
+    is_licensed INTEGER,
+    role TEXT,
+    latitude_i INTEGER,         -- multiply by 1e-7 to get degrees in float.
+    longitude_i INTEGER,        -- multiply by 1e-7 to get degrees in float.
+    altitude INTEGER,
+    -- time: INTEGER,           -- Disabled, only used for syncing time to note.
+    location_source TEXT,       -- Where does the location come from?
+    gps_timestamp INTEGER,      -- What time does the gps say it is?
+    altitude_source TEXT,       -- Where does the altitude come from?
+    pdop INTEGER,               -- Horizontal, Vertical, and Position Dilution of precision, in 1/100 units.
+    hdop INTEGER,
+    vdop INTEGER,
+    gps_accuracy INTEGER,       -- hardware constant in mm, used to calculate true accuracy (default to about 3m)
+    ground_speed INTEGER,       -- in m/s
+    ground_track INTEGER,       -- direction of motion
+    fix_quality INTEGER,        -- GPS Fix (something called NMEA GxGSA Statement)
+    fix_type INTEGER,           -- fix type, whatever that means
+    sats_in_view INTEGER,       -- How many satellites we can see
+    sensor_id INTEGER,          -- Just incase there's multiple gps devices on the node
+    next_update INTEGER,        -- How long until we ask for GPS (in seconds)
+    seq_number INTEGER,         -- Sequence number
+    precision_bits INTEGER,     -- How accurate the position is
+    snr REAL,                   -- Signal to Noise ratio of this message, measured by receiver
+    last_heard INTEGER,         -- Last time we heard the message
+    battery_level INTEGER,      -- Battery level, if it has one
+    voltage REAL,               -- Voltage from the power supply
+    channel_utilization REAL,   -- Utilization for the current channel, including well formed TX, RX and malformed RX (aka noise).
+    air_util_tx REAL,           -- Percent of airtime for transmission used within the last hour.
+    channel INTEGER,            -- The local channel we heard the node on.
+    via_mqtt INTEGER,           -- Did we see this node over MQTT instead of LoRA?
+    hops_away INTEGER           -- Number of hops away from us in the network. (0 is direct)
+);
+
+CREATE TABLE Channel (
+    channel_index INTEGER PRIMARY KEY,  -- Index of the channel
+    psk BLOB,                   -- Encryption of the channel
+    name TEXT,                  -- <12 bytes channel name.
+    id INTEGER,                 -- used to create global unique channel name, (name.id)
+    uplink_enabled INTEGER,     -- Are messages on the mesh sent to MQTT via other nodes?
+    downlink_enabled INTEGER,   -- Are messages seen on the internet sent over LoRA?
+    position_precision INTEGER, -- How many bits of precision are sent in position packets?
+    role TEXT                   -- General 'status' of the channel
+);
+
+CREATE TABLE DeviceConfig (
+    role INTEGER,               -- Node's Role
+    serial_enabled INTEGER,     -- Disables the SerialConsole
+    debug_log_enabled INTEGER,  -- defaults off after connection
+    button_gpio INTEGER,        -- lets you change the buttons
+    buzzer_gpio INTEGER,        -- pin for the buzzer
+    rebroadcast_mode INTEGER,   -- role of the node
+    node_info_broadcast_secs INTEGER,
+    double_tap_as_button_press INTEGER,
+    is_managed INTEGER,         -- if set, harder to config
+    disable_triple_click INTEGER
+);
+CREATE TABLE PositionConfig (
+    position_broadcast_secs INTEGER,
+    position_broadcast_smart_enabled INTEGER,
+    fixed_position INTEGER,
+    gps_enabled INTEGER,
+    gps_update_interval INTEGER,
+    gps_attempt_time INTEGER,
+    position_flags INTEGER,
+    rx_gpio INTEGER,
+    tx_gpio INTEGER,
+    broadcast_smart_minimum_distance INTEGER,
+    broadcast_smart_minimum_interval_secs INTEGER,
+    gps_en_gpio INTEGER,
+    gps_mode INTEGER
+);
+CREATE TABLE PowerConfig (
+    is_power_saving INTEGER,
+    on_battery_shutdown_after_secs INTEGER,
+    adc_multiplier_override REAL,
+    wait_bluetooth_secs INTEGER,
+    sds_secs INTEGER,
+    ls_secs INTEGER,
+    min_wake_secs INTEGER,
+    device_battery_ina_address INTEGER
+);
+CREATE TABLE NetworkConfig (
+    wifi_enabled INTEGER,
+    wifi_ssid TEXT,
+    wifi_psk TEXT,
+    ntp_server TEXT,
+    eth_enabled INTEGER,
+    address_mode INTEGER,
+    ip INTEGER,
+    gateway INTEGER,
+    subnet INTEGER,
+    dns INTEGER,
+    rsyslog_server TEXT
+);
+CREATE TABLE DisplayConfig (
+    screen_on_secs INTEGER,
+    gps_format INTEGER,
+    auto_screen_carousel_secs INTEGER,
+    compass_north_top INTEGER,
+    flip_screen INTEGER,
+    units INTEGER,
+    oled INTEGER,
+    displaymode INTEGER,
+    heading_bold INTEGER,
+    wake_on_tap_or_motion INTEGER
+);
+CREATE TABLE LoRaConfig (
+    use_preset INTEGER,
+    modem_preset INTEGER,
+    bandwidth INTEGER,
+    spread_factor INTEGER,
+    coding_rate INTEGER,
+    frequency_offset REAL,
+    region INTEGER,
+    hop_limit INTEGER,
+    tx_enabled INTEGER,
+    tx_power INTEGER,
+    channel_num INTEGER,
+    override_duty_cycle INTEGER,
+    sx126x_rx_boosted_gain INTEGER,
+    override_frequency REAL,
+    ignore_incoming BLOB,
+    ignore_mqtt INTEGER
+);
+CREATE TABLE BluetoothConfig (
+    enabled INTEGER,
+    mode INTEGER,
+    fixed_pin INTEGER
+);
+
+-- Module Configs --
+
+-- MQTT
+CREATE TABLE MqttConfig (
+    enabled INTEGER,
+    address TEXT,
+    username TEXT,
+    password TEXT,
+    encryption_enabled INTEGER,
+    json_enabled INTEGER,
+    tls_enabled INTEGER,
+    root TEXT,
+    proxy_to_client_enabled INTEGER,
+    map_reporting_enabled INTEGER,
+    publish_interval_secs INTEGER,
+    position_precision INTEGER
+);
+
+-- Serial
+CREATE TABLE SerialConfig (
+    enabled INTEGER,
+    echo INTEGER,
+    rxd INTEGER,
+    txd INTEGER,
+    baud INTEGER,
+    timeout INTEGER,
+    mode INTEGER,
+    override_console_serial_port INTEGER
+);
+
+-- External Notification
+CREATE TABLE ExternalNotificationConfig (
+    enabled INTEGER,
+    output_ms INTEGER,
+    output INTEGER,
+    output_vibra INTEGER,
+    output_buzzer INTEGER,
+    active INTEGER,
+    alert_message INTEGER,
+    alert_message_vibra INTEGER,
+    alert_message_buzzer INTEGER,
+    alert_bell INTEGER,
+    alert_bell_vibra INTEGER,
+    alert_bell_buzzer INTEGER,
+    use_pwm INTEGER,
+    nag_timeout INTEGER,
+    use_i2s_as_buzzer INTEGER
+);
+
+-- Store Forward
+CREATE TABLE StoreForwardConfig (
+    enabled INTEGER,
+    heartbeat INTEGER,
+    records INTEGER,
+    history_return_max INTEGER,
+    history_return_window INTEGER
+);
+
+-- Range Test
+CREATE TABLE RangeTestConfig (
+    enabled INTEGER,
+    sender INTEGER,
+    save INTEGER
+);
+
+-- Telemetry
+CREATE TABLE TelemetryConfig (
+    device_update_interval INTEGER,
+    environment_update_interval INTEGER,
+    environment_measurement_enabled INTEGER,
+    environment_screen_enabled INTEGER,
+    environment_display_fahrenheit INTEGER,
+    air_quality_enabled INTEGER,
+    air_quality_interval INTEGER,
+    power_measurement_enabled INTEGER,
+    power_update_interval INTEGER,
+    power_screen_enabled INTEGER
+);
+
+-- Canned Message
+CREATE TABLE CannedMessageConfig (
+    rotary1_enabled INTEGER,
+    inputbroker_pin_a INTEGER,
+    inputbroker_pin_b INTEGER,
+    inputbroker_pin_press INTEGER,
+    inputbroker_event_cw INTEGER,
+    inputbroker_event_ccw INTEGER,
+    inputbroker_event_press INTEGER,
+    updown1_enabled INTEGER,
+    enabled INTEGER,
+    allow_input_source TEXT,
+    send_bell INTEGER
+);
+
+-- Audio
+CREATE TABLE AudioConfig (
+    codec2_enabled INTEGER,
+    ptt_pin INTEGER,
+    bitrate INTEGER,
+    i2s_ws INTEGER,
+    i2s_sd INTEGER,
+    i2s_din INTEGER,
+    i2s_sck INTEGER
+);
+
+-- Remote Hardware
+CREATE TABLE RemoteHardwareConfig (
+    enabled INTEGER,
+    allow_undefined_pin_access INTEGER,
+    available_pins BLOB -- Use BLOB to store complex structures like a vector of structs
+);
+
+-- NeighborInfo
+CREATE TABLE NeighborInfoConfig (
+    enabled INTEGER,
+    update_interval INTEGER
+);
+
+-- AmbientLighting
+CREATE TABLE AmbientLightingConfig (
+    led_state INTEGER,
+    led_current INTEGER,
+    red INTEGER,
+    green INTEGER,
+    blue INTEGER
+);
+
+-- Detection Sensor (any gpio pin alarm)
+CREATE TABLE DetectionSensorConfig (
+    enabled INTEGER,
+    minimum_broadcast_secs INTEGER,
+    state_broadcast_secs INTEGER,
+    send_bell INTEGER,
+    name TEXT,
+    monitor_pin INTEGER,
+    detection_triggered_high INTEGER,
+    use_pullup INTEGER
+);
+
+-- Pax counter
+CREATE TABLE PaxcounterConfig (
+    enabled INTEGER,
+    paxcounter_update_interval INTEGER
+);
+
+-- MeshPacket Main Table
+CREATE TABLE MeshPacket (
+    primary_id INTEGER PRIMARY KEY AUTOINCREMENT,
+    message_from INTEGER,
+    message_to INTEGER,
+    channel INTEGER,
+    id INTEGER, -- Unique ID for this packet, but not unique enough to use as a primary key, unfortunately.
+    rx_time INTEGER,
+    rx_snr REAL,
+    hop_limit INTEGER,
+    want_ack INTEGER,
+    priority INTEGER,
+    rx_rssi INTEGER,
+    delayed INTEGER,
+    via_mqtt INTEGER,
+    hop_start INTEGER,
+    variant_payload_id INTEGER,
+    FOREIGN KEY (variant_payload_id) REFERENCES VariantPayload(variant_id)
+);
+
+-- VariantPayload Table
+CREATE TABLE VariantPayload (
+    variant_id INTEGER PRIMARY KEY AUTOINCREMENT,
+    
+);
diff --git a/src/config.rs b/src/config.rs
index 48882afc769c2a1fce91e6d39e05d85db3603431..5edec01335ab8de88ae8f27c5a792aa5aba734b3 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -1,12 +1,43 @@
-/// Full Config Structure to manage application settings
-pub struct Config {
+use serde::Deserialize;
+
+/// Holds the individual config segments.
+#[derive(Debug, Clone, Deserialize, Default)]
+pub struct AppConfig {
+    pub server: ServerConfig,
+    pub database: DatabaseConfig,
+    pub logging: LoggingConfig,
+    pub meshconfig: MeshConfig,
+}
+
+#[derive(Debug, Clone, Deserialize)]
+pub struct ServerConfig {
+    pub host: String,
+    pub port: u16,
+}
+
+/// Holds the config for the database
+#[derive(Debug, Clone, Deserialize)]
+pub struct DatabaseConfig {
+    pub db_name: String,
+}
+
+/// Holds the config for logging
+#[derive(Debug, Clone, Deserialize)]
+pub struct LoggingConfig {
+    pub level: String,
+}
+
+/// Holds the config for the meshtastic node.
+#[derive(Debug, Clone, Deserialize)]
+pub struct MeshConfig {
     pub connection_type: ConnectionType,
     pub serial_port: Option<String>,
-    pub ip_port: Option<String>,
+    pub ip: String,
+    pub port: u16
 }
 
-/// Enum to define the connection type
-#[derive(Default)]
+/// Enum to define the connection type, defaulting to TCP
+#[derive(Default, Debug, Clone, Deserialize)]
 pub enum ConnectionType {
     #[default]
     Tcp,
@@ -14,3 +45,41 @@ pub enum ConnectionType {
     Mqtt,
 }
 
+
+// Defaults
+
+impl Default for ServerConfig {
+    fn default() -> Self {
+        Self {
+            host: "localhost".to_string(),
+            port: 8080,
+        }
+    }
+}
+
+impl Default for DatabaseConfig {
+    fn default() -> Self {
+        Self {
+            db_name: "yamm.db".to_string(),
+        }
+    }
+}
+
+impl Default for LoggingConfig {
+    fn default() -> Self {
+        Self {
+            level: "INFO".to_string(),
+        }
+    }
+}
+
+impl Default for MeshConfig {
+    fn default() -> Self {
+        Self {
+            connection_type: ConnectionType::Tcp,
+            serial_port: None,
+            ip: "192.168.1.2".to_string(),
+            port: 4403,
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/db.rs b/src/db.rs
new file mode 100644
index 0000000000000000000000000000000000000000..6d6b918fe62fde002beddffa7af9027c823de6a4
--- /dev/null
+++ b/src/db.rs
@@ -0,0 +1,651 @@
+use std::path::{Path, PathBuf};
+use std::fs::File;
+use std::io;
+
+use meshtastic::protobufs::config::{BluetoothConfig, DeviceConfig, DisplayConfig, LoRaConfig, NetworkConfig, PayloadVariant, PositionConfig, PowerConfig};
+use meshtastic::protobufs::module_config::{AmbientLightingConfig, AudioConfig, CannedMessageConfig, DetectionSensorConfig, ExternalNotificationConfig, MqttConfig, NeighborInfoConfig, PaxcounterConfig, RangeTestConfig, RemoteHardwareConfig, SerialConfig, StoreForwardConfig, TelemetryConfig};
+use rusqlite::{Connection, Result};
+use rusqlite_migration::{Migrations, M};
+use serde::{Deserialize, Serialize};
+use tracing::{debug, error, info};
+use lazy_static::lazy_static;
+use meshtastic::protobufs::{Channel, MyNodeInfo, NodeInfo};
+
+use crate::CONFIG;
+
+pub(crate) struct Database {
+    conn: Connection,
+}
+
+// Migrations
+lazy_static! {
+    static ref MIGRATIONS: Migrations<'static> = Migrations::new(vec![
+        // Define your migrations here
+        M::up(include_str!("../db/migrations/0001_initial.sql")),
+    ]);
+}
+
+fn u32_vec_to_u8_vec(vec: &Vec<u32>) -> Vec<u8> {
+    vec.iter()
+        .flat_map(|x| x.to_be_bytes().to_vec())
+        .collect()
+}
+
+#[derive(Serialize, Deserialize)]
+struct RemoteHardwarePin {
+    gpio_pin: u32,
+    name: String,
+    r#type: i32,
+}
+
+/// This contains all the code for reading and writing from the database.
+/// Holy shit levels of boilerplate.
+/// This is done like this instead of just saving BLOBs because I really want to use SQL itself to quickly query data.
+/// Any parsing/indexing/long term statistics code I write isn't going to be even close to the level of optimisation
+/// that rusqlite has.
+impl Database {
+    /// Opens the database, running migrations and creating if it doesn't exist.
+    pub fn open() -> Result<Self> {
+        let db_path = PathBuf::from(&CONFIG.database.db_name);
+        // Pre-check, does the file exist?
+        if !db_path.exists() {
+            // If not, create the file.
+            match File::create(&db_path) {
+                Ok(_) => info!("Created database stub file."),
+                Err(e) => error!("Failed to create file: {:?}", e),
+            }
+        }
+
+        // Open it up, and run migrations!
+        let mut conn = Connection::open(&db_path)?;
+        // Apply migrations
+        MIGRATIONS.to_latest(&mut conn).expect("Migration failed");
+        Ok(Database { conn })
+    }
+
+    /// Updates the database with a new MyNodeInfo packet
+    pub fn update_mynodeinfo(my_node_info: &MyNodeInfo) -> Result<bool> {
+        // Open DB
+        let db = Database::open().expect("Failure to open database");
+
+        db.conn.execute(
+            "INSERT INTO MyNodeInfo (my_node_num, reboot_count, min_app_version) VALUES (?1, ?2, ?3)",
+            rusqlite::params![my_node_info.my_node_num, my_node_info.reboot_count, my_node_info.min_app_version],
+        )?;
+        debug!("Saved MyNodeInfo to database");
+        Ok(true)
+    }
+
+    /// Updates the database with a new NodeInfo packet
+    pub fn update_nodeinfo(node_info: &NodeInfo) -> Result<bool> {
+        // Open DB
+        let db = Database::open().expect("Failure to open database");
+
+        db.conn.execute(
+            "INSERT INTO Node (
+                num, user_id, user_lname, user_sname, hw_model, is_licensed, role, 
+                latitude_i, longitude_i, altitude, location_source, gps_timestamp, 
+                altitude_source, pdop, hdop, vdop, gps_accuracy, ground_speed, 
+                ground_track, fix_quality, fix_type, sats_in_view, sensor_id, 
+                next_update, seq_number, precision_bits, snr, last_heard, 
+                battery_level, voltage, channel_utilization, air_util_tx, 
+                channel, via_mqtt, hops_away
+            ) VALUES (
+                ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, 
+                ?15, ?16, ?17, ?18, ?19, ?20, ?21, ?22, ?23, ?24, ?25, ?26, 
+                ?27, ?28, ?29, ?30, ?31, ?32, ?33, ?34, ?35
+            )",
+            rusqlite::params![
+                node_info.num,
+                // This is gibberish to me, so lets explain.
+                // We're mapping the value user.id as the value, but chucking a NULL in if it's a None type.
+                node_info.user.as_ref().map(|user| user.id.clone()),
+                node_info.user.as_ref().map(|user| user.long_name.clone()),
+                node_info.user.as_ref().map(|user| user.short_name.clone()),
+                node_info.user.as_ref().map(|user| user.hw_model),
+                node_info.user.as_ref().map(|user| user.is_licensed),
+                node_info.user.as_ref().map(|user| user.role),
+                node_info.position.as_ref().map(|position| position.latitude_i),
+                node_info.position.as_ref().map(|position| position.longitude_i),
+                node_info.position.as_ref().map(|position| position.altitude),
+                node_info.position.as_ref().map(|position| position.location_source),
+                node_info.position.as_ref().map(|position| position.timestamp),
+                node_info.position.as_ref().map(|position| position.altitude_source),
+                node_info.position.as_ref().map(|position| position.pdop),
+                node_info.position.as_ref().map(|position| position.hdop),
+                node_info.position.as_ref().map(|position| position.vdop),
+                node_info.position.as_ref().map(|position| position.gps_accuracy),
+                node_info.position.as_ref().map(|position| position.ground_speed),
+                node_info.position.as_ref().map(|position| position.ground_track),
+                node_info.position.as_ref().map(|position| position.fix_quality),
+                node_info.position.as_ref().map(|position| position.fix_type),
+                node_info.position.as_ref().map(|position| position.sats_in_view),
+                node_info.position.as_ref().map(|position| position.sensor_id),
+                node_info.position.as_ref().map(|position| position.next_update),
+                node_info.position.as_ref().map(|position| position.seq_number),
+                node_info.position.as_ref().map(|position| position.precision_bits),
+                node_info.snr,
+                node_info.last_heard,
+                node_info.device_metrics.as_ref().map(|device_metrics| device_metrics.battery_level),
+                node_info.device_metrics.as_ref().map(|device_metrics| device_metrics.voltage),
+                node_info.device_metrics.as_ref().map(|device_metrics| device_metrics.channel_utilization),
+                node_info.device_metrics.as_ref().map(|device_metrics| device_metrics.air_util_tx),
+                node_info.channel,
+                node_info.via_mqtt,
+                node_info.hops_away
+            ]
+        )?;
+        debug!("Saved NodeInfo to database");
+        Ok(true)
+    }
+
+    /// Updates the database with a new Channel packet
+    pub fn update_channel(channel: &Channel) -> Result<bool> {
+        // Open DB
+        let db = Database::open().expect("Failure to open database");
+        db.conn.execute(
+            "INSERT INTO Channel (
+                channel_index, psk, name, id, uplink_enabled, downlink_enabled, position_precision, role
+            ) VALUES (
+                ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8
+            )",
+            rusqlite::params![
+                channel.index,
+                channel.settings.as_ref().map(|settings| settings.psk.clone()),
+                channel.settings.as_ref().map(|settings| settings.name.clone()),
+                channel.settings.as_ref().map(|settings| settings.id),
+                channel.settings.as_ref().map(|settings| settings.uplink_enabled),
+                channel.settings.as_ref().map(|settings| settings.downlink_enabled),
+                channel.settings.as_ref().and_then(|settings| settings.module_settings.as_ref()).map(|module_settings| module_settings.position_precision),
+                channel.role
+            ]
+        )?;
+        debug!("Saved Channel to database");
+        Ok(true)
+    }
+
+    pub fn update_deviceconfig(config: &DeviceConfig) -> Result<bool> {
+        let db = Database::open().expect("Failure to open database");
+        db.conn.execute(
+            "INSERT INTO DeviceConfig (
+                role, serial_enabled, debug_log_enabled, button_gpio, buzzer_gpio, rebroadcast_mode,
+                node_info_broadcast_secs, double_tap_as_button_press, is_managed, disable_triple_click
+            ) VALUES (
+                ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10
+            )",
+            rusqlite::params![
+                config.role,
+                config.serial_enabled,
+                config.debug_log_enabled,
+                config.button_gpio,
+                config.buzzer_gpio,
+                config.rebroadcast_mode,
+                config.node_info_broadcast_secs,
+                config.double_tap_as_button_press,
+                config.is_managed,
+                config.disable_triple_click
+            ]
+        )?;
+        debug!("Saved DeviceConfig to database");
+        Ok(true)
+    }
+
+    pub fn update_position_config(config: &PositionConfig) -> Result<bool> {
+        let db = Database::open().expect("Failure to open database");
+        db.conn.execute(
+            "INSERT INTO PositionConfig (
+                position_broadcast_secs, position_broadcast_smart_enabled, fixed_position, gps_enabled, 
+                gps_update_interval, gps_attempt_time, position_flags, rx_gpio, tx_gpio, 
+                broadcast_smart_minimum_distance, broadcast_smart_minimum_interval_secs, gps_en_gpio, 
+                gps_mode
+            ) VALUES (
+                ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13
+            )",
+            rusqlite::params![
+                config.position_broadcast_secs,
+                config.position_broadcast_smart_enabled,
+                config.fixed_position,
+                config.gps_enabled,
+                config.gps_update_interval,
+                config.gps_attempt_time,
+                config.position_flags,
+                config.rx_gpio,
+                config.tx_gpio,
+                config.broadcast_smart_minimum_distance,
+                config.broadcast_smart_minimum_interval_secs,
+                config.gps_en_gpio,
+                config.gps_mode
+            ]
+        )?;
+        debug!("Saved PositionConfig to database");
+        Ok(true)
+    }
+
+    pub fn update_power_config(config: &PowerConfig) -> Result<bool> {
+        let db = Database::open().expect("Failure to open database");
+        db.conn.execute(
+            "INSERT INTO PowerConfig (
+                is_power_saving, on_battery_shutdown_after_secs, adc_multiplier_override, wait_bluetooth_secs, 
+                sds_secs, ls_secs, min_wake_secs, device_battery_ina_address
+            ) VALUES (
+                ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8
+            )",
+            rusqlite::params![
+                config.is_power_saving,
+                config.on_battery_shutdown_after_secs,
+                config.adc_multiplier_override,
+                config.wait_bluetooth_secs,
+                config.sds_secs,
+                config.ls_secs,
+                config.min_wake_secs,
+                config.device_battery_ina_address
+            ]
+        )?;
+        debug!("Saved PowerConfig to database");
+        Ok(true)
+    }
+
+    pub fn update_network_config(config: &NetworkConfig) -> Result<bool> {
+        let db = Database::open().expect("Failure to open database");
+        db.conn.execute(
+            "INSERT INTO NetworkConfig (
+                wifi_enabled, wifi_ssid, wifi_psk, ntp_server, eth_enabled, address_mode,
+                ip, gateway, subnet, dns, rsyslog_server
+            ) VALUES (
+                ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11
+            )",
+            rusqlite::params![
+                config.wifi_enabled,
+                config.wifi_ssid,
+                config.wifi_psk,
+                config.ntp_server,
+                config.eth_enabled,
+                config.address_mode,
+                config.ipv4_config.as_ref().map(|ipv4_config| ipv4_config.ip),
+                config.ipv4_config.as_ref().map(|ipv4_config| ipv4_config.gateway),
+                config.ipv4_config.as_ref().map(|ipv4_config| ipv4_config.subnet),
+                config.ipv4_config.as_ref().map(|ipv4_config| ipv4_config.dns),
+                config.rsyslog_server
+            ]
+        )?;
+        debug!("Saved NetworkConfig to database");
+        Ok(true)
+    }
+
+
+    pub fn update_display_config(config: &DisplayConfig) -> Result<bool> {
+        let db = Database::open().expect("Failure to open database");
+        db.conn.execute(
+            "INSERT INTO DisplayConfig (
+                screen_on_secs, gps_format, auto_screen_carousel_secs, compass_north_top, 
+                flip_screen, units, oled, displaymode, heading_bold, wake_on_tap_or_motion
+            ) VALUES (
+                ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10
+            )",
+            rusqlite::params![
+                config.screen_on_secs,
+                config.gps_format,
+                config.auto_screen_carousel_secs,
+                config.compass_north_top,
+                config.flip_screen,
+                config.units,
+                config.oled,
+                config.displaymode,
+                config.heading_bold,
+                config.wake_on_tap_or_motion
+            ]
+        )?;
+        debug!("Saved DisplayConfig to database");
+        Ok(true)
+    }
+
+    pub fn update_lora_config(config: &LoRaConfig) -> Result<bool> {
+        let db = Database::open().expect("Failure to open database");
+        db.conn.execute(
+            "INSERT INTO LoRaConfig (
+                use_preset, modem_preset, bandwidth, spread_factor, coding_rate, 
+                frequency_offset, region, hop_limit, tx_enabled, tx_power, 
+                channel_num, override_duty_cycle, sx126x_rx_boosted_gain, 
+                override_frequency, ignore_incoming, ignore_mqtt
+            ) VALUES (
+                ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16
+            )",
+            rusqlite::params![
+                config.use_preset,
+                config.modem_preset,
+                config.bandwidth,
+                config.spread_factor,
+                config.coding_rate,
+                config.frequency_offset,
+                config.region,
+                config.hop_limit,
+                config.tx_enabled,
+                config.tx_power,
+                config.channel_num,
+                config.override_duty_cycle,
+                config.sx126x_rx_boosted_gain,
+                config.override_frequency,
+                // Rusqlite can't store a Vec<u32>, so we need to make it into Vec<u8>
+                u32_vec_to_u8_vec(&config.ignore_incoming),
+                config.ignore_mqtt
+            ]
+        )?;
+        debug!("Saved LoRaConfig to database");
+        Ok(true)
+    }
+
+    pub fn update_bluetooth_config(config: &BluetoothConfig) -> Result<bool> {
+        let db = Database::open().expect("Failure to open database");
+        db.conn.execute(
+            "INSERT INTO BluetoothConfig (
+                enabled, mode, fixed_pin
+            ) VALUES (
+                ?1, ?2, ?3
+            )",
+            rusqlite::params![
+                config.enabled,
+                config.mode,
+                config.fixed_pin
+            ]
+        )?;
+        debug!("Saved BluetoothConfig to database");
+        Ok(true)
+    }
+
+    pub fn update_mqtt_config(config: &MqttConfig) -> Result<bool> {
+        let db = Database::open().expect("Failure to open database");
+        db.conn.execute(
+            "INSERT INTO MqttConfig (
+                enabled, address, username, password, encryption_enabled, json_enabled, 
+                tls_enabled, root, proxy_to_client_enabled, map_reporting_enabled, 
+                publish_interval_secs, position_precision
+            ) VALUES (
+                ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12
+            )",
+            rusqlite::params![
+                config.enabled,
+                config.address,
+                config.username,
+                config.password,
+                config.encryption_enabled,
+                config.json_enabled,
+                config.tls_enabled,
+                config.root,
+                config.proxy_to_client_enabled,
+                config.map_reporting_enabled,
+                config.map_report_settings.as_ref().map(|map_report_settings| map_report_settings.publish_interval_secs),
+                config.map_report_settings.as_ref().map(|map_report_settings| map_report_settings.position_precision)
+            ]
+        )?;
+        debug!("Saved MqttConfig to database");
+        Ok(true)
+    }
+
+    pub fn update_serial_config(config: &SerialConfig) -> Result<bool> {
+        let db = Database::open().expect("Failure to open database");
+        db.conn.execute(
+            "INSERT INTO SerialConfig (
+                enabled, echo, rxd, txd, baud, timeout, mode, override_console_serial_port
+            ) VALUES (
+                ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9
+            )",
+            rusqlite::params![
+                config.enabled,
+                config.echo,
+                config.rxd,
+                config.txd,
+                config.baud,
+                config.timeout,
+                config.mode,
+                config.override_console_serial_port
+            ]
+        )?;
+        debug!("Saved SerialConfig to database");
+        Ok(true)
+    }
+
+    pub fn update_externalnotification_config(config: &ExternalNotificationConfig) -> Result<bool> {
+        let db = Database::open().expect("Failure to open database");
+        db.conn.execute(
+            "INSERT INTO ExternalNotificationConfig (
+                enabled, output_ms, output, output_vibra, output_buzzer, active, alert_message, 
+                alert_message_vibra, alert_message_buzzer, alert_bell, alert_bell_vibra, alert_bell_buzzer,
+                use_pwm, nag_timeout, use_i2c_as_buzzer
+            ) VALUES (
+                ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15
+            )",
+            rusqlite::params![
+                config.enabled,
+                config.output_ms,
+                config.output_vibra,
+                config.output_buzzer,
+                config.active,
+                config.alert_message,
+                config.alert_message_vibra,
+                config.alert_message_buzzer,
+                config.alert_bell,
+                config.alert_bell_vibra,
+                config.alert_bell_buzzer,
+                config.use_pwm,
+                config.nag_timeout,
+                config.use_i2s_as_buzzer
+            ]
+        )?;
+        debug!("Saved ExternalNotificationConfig to database");
+        Ok(true)
+    }
+
+    pub fn update_storeforward_config(config: &StoreForwardConfig) -> Result<bool> {
+        let db = Database::open().expect("Failure to open database");
+        db.conn.execute(
+            "INSERT INTO StoreForwardConfig (
+                enabled, heartbeat, records, history_return_max, history_return_window
+            ) VALUES (
+                ?1, ?2, ?3, ?4, ?5
+            )",
+            rusqlite::params![
+                config.enabled,
+                config.heartbeat,
+                config.records,
+                config.history_return_max,
+                config.history_return_window
+            ]
+        )?;
+        debug!("Saved StoreForwardConfig to database");
+        Ok(true)
+    }
+
+    pub fn update_rangetest_config(config: &RangeTestConfig) -> Result<bool> {
+        let db = Database::open().expect("Failure to open database");
+        db.conn.execute(
+            "INSERT INTO RangeTestConfig (
+                enabled, sender, save
+            ) VALUES (
+                ?1, ?2, ?3
+            )",
+            rusqlite::params![
+                config.enabled,
+                config.sender,
+                config.save
+            ]
+        )?;
+        debug!("Saved RangeTestConfig to database");
+        Ok(true)
+    }
+
+    pub fn update_telemetry_config(config: &TelemetryConfig) -> Result<bool> {
+        let db = Database::open().expect("Failure to open database");
+        db.conn.execute(
+            "INSERT INTO TelemetryConfig (
+                device_update_interval, environment_update_interval, environment_measurement_enabled,
+                environment_screen_enabled, environment_display_fahrenheit, air_quality_enabled,
+                air_quality_interval, power_measurement_enabled, power_update_interval
+                power_screen_enabled
+            ) VALUES (
+                ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10
+            )",
+            rusqlite::params![
+                config.device_update_interval,
+                config.environment_update_interval,
+                config.environment_measurement_enabled,
+                config.environment_screen_enabled,
+                config.environment_display_fahrenheit,
+                config.air_quality_enabled,
+                config.air_quality_interval,
+                config.power_measurement_enabled,
+                config.power_update_interval,
+                config.power_screen_enabled
+            ]
+        )?;
+        debug!("Saved TelemetryConfig to database");
+        Ok(true)
+    }
+
+    pub fn update_cannedmessage_config(config: &CannedMessageConfig) -> Result<bool> {
+        let db = Database::open().expect("Failure to open database");
+        db.conn.execute(
+            "INSERT INTO CannedMessageConfig (
+                rotary1_enabled, inputbroker_pin_a, inputbroker_pin_b, inputbroker_pin_press,
+                inputbroker_event_cw, inputbroker_event_ccw, inputbroker_event_press,
+                updown1_enabled, enabled, allow_input_source, send_bell
+            ) VALUES (
+                ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11
+            )",
+            rusqlite::params![
+                config.rotary1_enabled,
+                config.inputbroker_pin_a,
+                config.inputbroker_pin_b,
+                config.inputbroker_pin_press,
+                config.inputbroker_event_cw,
+                config.inputbroker_event_ccw,
+                config.inputbroker_event_press,
+                config.updown1_enabled,
+                config.enabled,
+                config.allow_input_source,
+                config.send_bell
+            ]
+        )?;
+        debug!("Saved CannedMessageConfig to database");
+        Ok(true)
+    }
+
+    pub fn update_audio_config(config: &AudioConfig) -> Result<bool> {
+        let db = Database::open().expect("Failure to open database");
+        db.conn.execute(
+            "INSERT INTO AudioConfig (
+                codec2_enabled, ptt_pin, bitrate, i2s_ws, i2s_sd, i2s_din, i2c_sck
+            ) VALUES (
+                ?1, ?2, ?3, ?4, ?5, ?6, ?7
+            )",
+            rusqlite::params![
+                config.codec2_enabled,
+                config.ptt_pin,
+                config.bitrate,
+                config.i2s_ws,
+                config.i2s_sd,
+                config.i2s_din,
+                config.i2s_sck
+            ]
+        )?;
+        debug!("Saved AudioConfig to database");
+        Ok(true)
+    }
+
+    pub fn update_remotehardware_config(config: &RemoteHardwareConfig) -> Result<bool> {
+        let db = Database::open().expect("Failure to open database");
+        let serialized_pins = bincode::serialize(&config.available_pins).unwrap_or(Vec::new());
+        db.conn.execute(
+            "INSERT INTO RemoteHardwareConfig (
+                enabled, allow_undefined_pin_access, available_pins
+            ) VALUES (
+                ?1, ?2, ?3
+            )",
+            rusqlite::params![
+                config.enabled,
+                config.allow_undefined_pin_access,
+                serialized_pins
+            ]
+        )?;
+        debug!("Saved RemoteHardwareConfig to database");
+        Ok(true)
+    }
+
+    pub fn update_neighbourinfo_config(config: &NeighborInfoConfig) -> Result<bool> {
+        let db = Database::open().expect("Failure to open database");
+        db.conn.execute(
+            "INSERT INTO NeighborInfoConfig (
+                enabled, update_interval
+            ) VALUES (
+                ?1, ?2
+            )",
+            rusqlite::params![
+                config.enabled,
+                config.update_interval
+            ]
+        )?;
+        debug!("Saved NeighborInfoConfig to database");
+        Ok(true)
+    }
+
+    pub fn update_ambientlighting_config(config: &AmbientLightingConfig) -> Result<bool> {
+        let db = Database::open().expect("Failure to open database");
+        db.conn.execute(
+            "INSERT INTO AmbientLightingConfig (
+                led_state, led_current, red, green, blue
+            ) VALUES (
+                ?1, ?2, ?3, ?4, ?5
+            )",
+            rusqlite::params![
+                config.led_state,
+                config.current,
+                config.red,
+                config.green,
+                config.blue
+            ]
+        )?;
+        debug!("Saved AmbientLightingConfig to database");
+        Ok(true)
+    }
+
+    pub fn update_detectionsensor_config(config: &DetectionSensorConfig) -> Result<bool> {
+        let db = Database::open().expect("Failure to open database");
+        db.conn.execute(
+            "INSERT INTO DetectionSensorConfig (
+                enabled, minimum_broadcast_secs, state_broadcast_secs, send_bell,
+                name, monitor_pin, detection_triggered_high, use_pullup
+
+            ) VALUES (
+                ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8
+            )",
+            rusqlite::params![
+                config.enabled,
+                config.minimum_broadcast_secs,
+                config.state_broadcast_secs,
+                config.send_bell,
+                config.name,
+                config.monitor_pin,
+                config.detection_triggered_high,
+                config.use_pullup
+            ]
+        )?;
+        debug!("Saved DetectionSensorConfig to database");
+        Ok(true)
+    }
+
+    pub fn update_paxcounter_config(config: &PaxcounterConfig) -> Result<bool> {
+        let db = Database::open().expect("Failure to open database");
+        db.conn.execute(
+            "INSERT INTO PaxcounterConfig (
+                enabled, paxcounter_update_interval
+            ) VALUES (
+                ?1, ?2
+            )",
+            rusqlite::params![
+                config.enabled,
+                config.paxcounter_update_interval
+            ]
+        )?;
+        debug!("Saved PaxcounterConfig to database");
+        Ok(true)
+    }
+}
\ No newline at end of file
diff --git a/src/main.rs b/src/main.rs
index 4cd35b60f8d406f951f14ff20d726264848237f3..cdc492f99c698345f9e4ce678cd050ca77af6333 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,17 +1,36 @@
+use figment::{Figment, providers::{Format, Toml, Env}};
 
-use std::io::{self, BufRead};
-use std::time::SystemTime;
-
-use meshtastic::api::StreamApi;
+use lazy_static::lazy_static;
+use meshtastic::api::{StreamApi, StreamHandle};
 use meshtastic::utils;
 use meshtastic::protobufs::*;
 
+use packets::parse;
+use tokio::{net::TcpStream, sync::mpsc::UnboundedReceiver};
 use tracing::{debug, error, info, Level};
-use tracing_subscriber::{fmt, FmtSubscriber};
-use tracing_subscriber::prelude::*;
+use tracing_subscriber::FmtSubscriber;
 
 mod nodes;
 mod packets;
+mod db;
+mod config;
+use crate::config::AppConfig;
+
+// Setup the config globally because I can't figure out how to pass it to functions I don't call directly.
+// This is evaluated at runtime, and not compilation. \o/
+// Also, we try to merge configs. so envvars > config > defaults > error
+lazy_static! {
+    pub static ref CONFIG: AppConfig = {
+        let config = Figment::new()
+            .merge(Toml::file("config.toml"))
+            .merge(Env::prefixed("yamm_"))
+            .extract()
+            .unwrap_or_default();
+        tracing::info!("Loaded Config!");
+        tracing::debug!("full config: {:?}", config);
+        config
+    };
+}
 
 #[tokio::main]
 async fn main() -> Result<(), Box<dyn std::error::Error>> {
@@ -23,37 +42,53 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
     tracing::subscriber::set_global_default(subscriber)
         .expect("setting default subscriber failed");
 
+    // Hello World!
     info!("Y(et) A(nother) M(eshtastic) M(ap) - v0.1.0");
 
     // set up new connection
-    // TODO: setup multiple types
     let stream_api = StreamApi::new();
 
-    // Set Address and port
-    // TODO: read from config
-    let address = "127.0.0.1:4403".to_string();
-
-    // Connect over TCP
-    let tcp_stream = utils::stream::build_tcp_stream(address).await?;
-    let (mut decoded_listener, stream_api) = stream_api.connect(tcp_stream).await;
-    let config_id = utils::generate_rand_id();
-    let stream_api = stream_api.configure(config_id).await?;
-
-    // Read loop, exit with Ctrl+C or disconnecting node.
-    while let Some(decoded) = decoded_listener.recv().await {
-        // New Message!
-        debug!("Received: {:?}", decoded);
-        // Parse message
-        
+    // Different connection types have different setups.
+    let mut tcp_stream: StreamHandle<TcpStream>;
+    //let mut serial_stream: StreamHandle<SerialStream>; // SerialStream isn't a valid type???
+    match &CONFIG.meshconfig.connection_type {
+        config::ConnectionType::Tcp => {
+            // Build the tcp stream object from config.
+            let address = &CONFIG.meshconfig.ip;
+            let port = &CONFIG.meshconfig.port.to_string();
+            let tcp = format!("{}:{}", address, port);
 
-        // Save to DB
+            // Connect over TCP
+            let tcp_stream = utils::stream::build_tcp_stream(tcp).await?;
+            let (mut decoded_listener, stream_api) = stream_api.connect(tcp_stream).await;
+            let config_id = utils::generate_rand_id();
+            let stream_api = stream_api.configure(config_id).await?;
+
+            // Read loop, exit with Ctrl+C or disconnecting node.
+            while let Some(decoded) = decoded_listener.recv().await {
+                // New Message!
+                debug!("Received: {:?}", decoded);
+                // Parse message
+                parse(&decoded);
+            }
+        
+            // Note that in this specific example, this will only be called when
+            // the radio is disconnected, as the above loop will never exit.
+            // Typically you would allow the user to manually kill the loop,
+            // for example with tokio::select!.
+            let _stream_api = stream_api.disconnect().await?;
+        },
+        config::ConnectionType::Serial => {
+            let available_ports = utils::stream::available_serial_ports()?;
+            info!("Available ports, please set in config.: {:?}", available_ports);
+            todo!("Not yet Implemented. See https://git.volkor.me/Volkor/yamm/-/issues/2")
+        },
+        config::ConnectionType::Mqtt => {
+            todo!("Not yet Implemented. See https://git.volkor.me/Volkor/yamm/-/issues/3")
+        },
     }
 
-    // Note that in this specific example, this will only be called when
-    // the radio is disconnected, as the above loop will never exit.
-    // Typically you would allow the user to manually kill the loop,
-    // for example with tokio::select!.
-    let _stream_api = stream_api.disconnect().await?;
+
 
     Ok(())
 }
\ No newline at end of file
diff --git a/src/packets.rs b/src/packets.rs
index 78a4c368915e7ebbe63553e3f4f4dd2f53722078..eab690a1bbe8c6ab80140ace85282784424a2f5c 100644
--- a/src/packets.rs
+++ b/src/packets.rs
@@ -1,39 +1,51 @@
+use std::path::Path;
+
+use config::device_config;
 use from_radio::PayloadVariant;
 use meshtastic::protobufs::*;
 use tracing::{debug, error};
 
+use crate::db::Database;
+
 /// Parses all incoming packets, handing them out to the correct handler.
 pub fn parse(message: &FromRadio) -> bool {
-    debug!("Parsing Message from radio");
+    // debug!("Parsing Message from radio: {:?}", &message);
 
     // Handle all types of messages from the radio here.
     match message.payload_variant {
         // A normal message
         Some(PayloadVariant::Packet(ref packet)) => {
+            debug!("New Message Packet!");
             parse_packet(packet);
         },
         // Information about the local node
         Some(PayloadVariant::MyInfo(ref info)) => {
+            debug!("New MyNodeInfo Packet");
             parse_myinfo(info);
         },
         // Information about a remote node
         Some(PayloadVariant::NodeInfo(ref node_info)) => {
+            debug!("New NodeInfo Packet!");
             parse_nodeinfo(node_info);
         },
         // Information about the local node's config
         Some(PayloadVariant::Config(ref config)) => {
+            debug!("New Config Packet!");
             parse_config(config);
         },
         // Information about the local node's module config
         Some(PayloadVariant::ModuleConfig(ref module_config)) => {
+            debug!("New ModuleConfig Packet!");
             parse_module_config(module_config);
         },
         // Information about one of the 8 channels in (potential) use.
         Some(PayloadVariant::Channel(ref channel_info)) => {
+            debug!("New Channel Packet!");
             parse_channel_info(channel_info);
         },
         // Information about the queue (messages to send soon)
         Some(PayloadVariant::QueueStatus(ref queue_status)) => {
+            debug!("New Message Packet!");
             parse_queue_info(queue_status);
         },
 
@@ -49,35 +61,110 @@ pub fn parse(message: &FromRadio) -> bool {
 
 fn parse_packet(packet: &MeshPacket) {
     // Handle packet data
+    debug!("Parsing MeshPacket Packet: {:#?}", &packet);
     todo!()
 }
 
 fn parse_myinfo(packet: &MyNodeInfo) {
-    // Check the last MyNodeInfo packet from the DB. If it's different, save it.
-    todo!()
+    debug!("Parsing MyNodeInfo Packet: {:#?}", &packet);
+    let _ = Database::update_mynodeinfo(packet);
 }
 
 fn parse_nodeinfo(node_info: &NodeInfo) {
     // Handle NodeInfo data
-    todo!()
+    debug!("Parsing MyNodeInfo Packet: {:#?}", &node_info);
+    let _ = Database::update_nodeinfo(node_info);
 }
 
 fn parse_config(config: &Config) {
     // Handle Config data
-    todo!()
+    debug!("Parsing Config Packet: {:#?}", &config);
+    
+    // Match and destructure the payload_variant
+    if let Some(payload_variant) = config.payload_variant.clone() {
+        match payload_variant {
+            config::PayloadVariant::Device(device_config) => {
+                let _ = Database::update_deviceconfig(&device_config);
+            },
+            config::PayloadVariant::Position(position_config) => {
+                let _ = Database::update_position_config(&position_config);
+            },
+            config::PayloadVariant::Power(power_config) => {
+                let _ = Database::update_power_config(&power_config);
+            },
+            config::PayloadVariant::Network(network_config) => {
+                let _ = Database::update_network_config(&network_config);
+            },
+            config::PayloadVariant::Display(display_config) => {
+                let _ = Database::update_display_config(&display_config);
+            },
+            config::PayloadVariant::Lora(lora_config) => {
+                let _ = Database::update_lora_config(&lora_config);
+            },
+            config::PayloadVariant::Bluetooth(bluetooth_config) => {
+                let _ = Database::update_bluetooth_config(&bluetooth_config);
+            },
+        }
+    }
 }
 
 fn parse_module_config(module_config: &ModuleConfig) {
+    debug!("Parsing Module Packet: {:#?}", &module_config);
     // Handle ModuleConfig data
-    todo!()
+    // Match and destructure the payload_variant
+    if let Some(payload_variant) = module_config.payload_variant.clone() {
+        match payload_variant {
+            module_config::PayloadVariant::Mqtt(mqtt_config) => {
+                let _ = Database::update_mqtt_config(&mqtt_config);
+            },
+            module_config::PayloadVariant::Serial(serial_config) => {
+                let _ = Database::update_serial_config(&serial_config);
+            },
+            module_config::PayloadVariant::ExternalNotification(external_notification_config) => {
+                let _ = Database::update_externalnotification_config(&external_notification_config);
+            },
+            module_config::PayloadVariant::StoreForward(store_forward_config) => {
+                let _ = Database::update_storeforward_config(&store_forward_config);
+            },
+            module_config::PayloadVariant::RangeTest(range_test_config) => {
+                let _ = Database::update_rangetest_config(&range_test_config);
+            },
+            module_config::PayloadVariant::Telemetry(telemetry_config) => {
+                let _ = Database::update_telemetry_config(&telemetry_config);
+            },
+            module_config::PayloadVariant::CannedMessage(canned_message_config) => {
+                let _ = Database::update_cannedmessage_config(&canned_message_config);
+            },
+            module_config::PayloadVariant::Audio(audio_config) => {
+                let _ = Database::update_audio_config(&audio_config);
+            },
+            module_config::PayloadVariant::RemoteHardware(remote_hardware_config) => {
+                let _ = Database::update_remotehardware_config(&remote_hardware_config);
+            },
+            module_config::PayloadVariant::NeighborInfo(neighbor_info_config) => {
+                let _ = Database::update_neighbourinfo_config(&neighbor_info_config);
+            },
+            module_config::PayloadVariant::AmbientLighting(ambient_lighting_config) => {
+                let _ = Database::update_ambientlighting_config(&ambient_lighting_config);
+            },
+            module_config::PayloadVariant::DetectionSensor(detection_sensor_config) => {
+                let _ = Database::update_detectionsensor_config(&detection_sensor_config);
+            },
+            module_config::PayloadVariant::Paxcounter(paxcounter_config) => {
+                let _ = Database::update_paxcounter_config(&paxcounter_config);
+            },
+        }
+    }
 }
 
 fn parse_channel_info(channel_info: &Channel) {
+    debug!("Parsing Channel Packet: {:#?}", &channel_info);
     // Handle ChannelInfo data
-    todo!()
+    let _ = Database::update_channel(channel_info);
 }
 
 fn parse_queue_info(queue_info: &QueueStatus) {
+    debug!("Parsing Queue Packet: {:#?}", &queue_info);
     // Handle QueueInfo data
     todo!()
 }
\ No newline at end of file