diff --git a/Cargo.lock b/Cargo.lock
index 020fd37..e9044a0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2871,7 +2871,7 @@ dependencies = [
  "log",
  "rustc-hash 1.1.0",
  "spirv",
- "strum",
+ "strum 0.26.3",
  "termcolor",
  "thiserror 2.0.12",
  "unicode-xid",
@@ -4556,9 +4556,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"
@@ -4572,6 +4578,19 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "strum_macros"
+version = "0.27.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "rustversion",
+ "syn",
+]
+
 [[package]]
 name = "subtle"
 version = "2.6.1"
@@ -6307,11 +6326,14 @@ dependencies = [
  "rayon",
  "reqwest",
  "serde",
+ "strum 0.27.1",
+ "strum_macros 0.27.1",
  "tokio",
  "toml",
  "tracing",
  "tracing-subscriber",
  "wxbox-ar2",
+ "wxbox-common",
  "wxbox-grib2",
  "wxbox-pal",
 ]
diff --git a/crates/client/assets/sw.js b/crates/client/assets/sw.js
index 48f3fc9..2ee0e8b 100644
--- a/crates/client/assets/sw.js
+++ b/crates/client/assets/sw.js
@@ -1,12 +1,15 @@
+/*
 var cacheName = 'egui-template-pwa';
+ */
+/*
 var filesToCache = [
     './',
     './index.html',
     './eframe_template.js',
     './eframe_template_bg.wasm',
 ];
-
-/* Start the service worker and cache all of the app's content */
+*/
+/* Start the service worker and cache all of the app's content *//*
 self.addEventListener('install', function (e) {
     e.waitUntil(
         caches.open(cacheName).then(function (cache) {
@@ -14,12 +17,13 @@ self.addEventListener('install', function (e) {
         })
     );
 });
-
-/* Serve cached content when offline */
+*/
+/* Serve cached content when offline *//*
 self.addEventListener('fetch', function (e) {
     e.respondWith(
         caches.match(e.request).then(function (response) {
             return response || fetch(e.request);
         })
     );
-});
\ No newline at end of file
+});
+*/
\ No newline at end of file
diff --git a/crates/client/src/app.rs b/crates/client/src/app.rs
index 35220bb..a123bf2 100644
--- a/crates/client/src/app.rs
+++ b/crates/client/src/app.rs
@@ -88,7 +88,7 @@ impl eframe::App for App {
                 &mut self.selected_layer,
             );
         }
-        //right_bar(ctx);
+        //right_bar(ctx, self);
 
         egui::CentralPanel::default().show(ctx, |ui| {
             ui.add(walkers::Map::new(
diff --git a/crates/client/src/ui/shell/main_menu.rs b/crates/client/src/ui/shell/main_menu.rs
index a56a9e3..c142129 100644
--- a/crates/client/src/ui/shell/main_menu.rs
+++ b/crates/client/src/ui/shell/main_menu.rs
@@ -14,6 +14,8 @@ use egui::{
 use std::collections::HashMap;
 use std::sync::Arc;
 use tracing::debug;
+use crate::map::sources::baselayers::BASELAYER_OSM;
+use crate::map::sources::datalayers::{NOAA_MRMS_MERGED_CREF_QC_CONUS, NOAA_MRMS_MERGED_RHOHV_3KM_CONUS};
 
 fn outer_rect(f: &Prepared) -> Rect {
     let content_rect = f.content_ui.min_rect();
@@ -102,6 +104,33 @@ pub fn main_menu(
                     },
                 );
 
+                let mut add = |id| {
+                        layer_manager.active_layers.insert(0, ActiveLayer {
+                            source_id: id,
+                            layer_id: LayerId::new(),
+                            visible: true,
+                        });
+                        *add_open = false;
+                };
+
+                ui.collapsing("Baselayers", |ui| {
+                    if ui.button("OpenStreetMap").clicked() {
+                        add(BASELAYER_OSM.source_id);
+                    }
+                });
+                ui.collapsing("Datalayers", |ui| {
+                    ui.collapsing("NOAA / MRMS", |ui| {
+                        ui.collapsing("Continental US", |ui| {
+                            for layer in &[&NOAA_MRMS_MERGED_CREF_QC_CONUS, &NOAA_MRMS_MERGED_RHOHV_3KM_CONUS] {
+                                if ui.button(&layer.display_name).clicked() {
+                                    add(layer.source_id);
+                                }
+                            }
+                        });
+                    });
+                });
+
+                /*
                 for (id, registered_layer) in &layer_manager.registered_sources {
                     // ghost button
 
@@ -221,6 +250,8 @@ pub fn main_menu(
                     }
                     frame.paint(ui);
                 }
+
+                 */
             });
     }
 }
diff --git a/crates/client/src/ui/shell/right_bar.rs b/crates/client/src/ui/shell/right_bar.rs
index 86a857c..3cacaf6 100644
--- a/crates/client/src/ui/shell/right_bar.rs
+++ b/crates/client/src/ui/shell/right_bar.rs
@@ -1,7 +1,8 @@
 use crate::ui::shell::bars::pane_header;
-use egui::{Align, Direction, Frame, Layout, RichText};
+use egui::{Align, Direction, FontFamily, Frame, Layout, RichText, Ui};
+use crate::app::App;
 
-pub fn right_bar(ctx: &egui::Context) {
+pub fn right_bar(ctx: &egui::Context, app: &mut App) {
     let mut frame = Frame::side_top_panel(&ctx.style());
     frame.inner_margin.top = 4;
     frame.inner_margin.left = 0;
@@ -10,19 +11,67 @@ pub fn right_bar(ctx: &egui::Context) {
     egui::SidePanel::right("right_panel")
         .frame(frame)
         .show(ctx, |ui| {
-            pane_header(
-                ui,
-                "Properties",
-                Some(egui_phosphor::regular::INFO),
-                false,
-                |ui| {},
-            );
+            if let Some(selected_layer_id) = app.selected_layer {
+                if let Some(active_layer) = app.layer_manager.active_layers.iter().find(|u| u.layer_id == selected_layer_id) {
+                    if let Some(source) = app.layer_manager.registered_sources.get(&active_layer.source_id) {
+                        pane_header(
+                            ui,
+                            &source.display_name,
+                            Some(source.type_hint.icon()),
+                            false,
+                            |ui| {}
+                        );
 
-            ui.with_layout(Layout::top_down_justified(Align::Center), |ui| {
-                ui.label(
-                    RichText::new("Select a layer in the Layers panel to modify it's properties")
-                        .italics(),
+                        egui::Grid::new("properties")
+                            .num_columns(2)
+                            .spacing([40.0, 4.0])
+                            .striped(true)
+                            .show(ui, |ui| {
+                                let mut draw_row = |title, content: Box<dyn Fn(&mut Ui)>| {
+                                    ui.horizontal(|ui| {
+                                        ui.add_space(8.0);
+                                        ui.label(
+                                            RichText::new(title)
+                                                .family(FontFamily::Name("semibold".into()))
+                                                .strong(),
+                                        );
+                                    });
+                                    ui.horizontal(|ui| {
+                                        content(ui);
+                                        ui.add_space(ui.available_width().max(8.0));
+                                    });
+                                    ui.end_row();
+                                };
+                                draw_row("Source", Box::new(|ui: &mut Ui| {
+                                    ui.label(&source.source);
+                                }));
+                                draw_row("Coverage", Box::new(|ui: &mut Ui| {
+                                    ui.label(&source.location);
+                                }));
+
+
+
+                            });
+                    }
+
+                }
+            } else {
+                pane_header(
+                    ui,
+                    "Properties",
+                    Some(egui_phosphor::regular::INFO),
+                    false,
+                    |ui| {},
                 );
-            });
+
+                ui.horizontal_centered(|ui| {
+                    ui.label(
+                        RichText::new("Select a layer in the Layers panel to modify it's properties")
+                            .italics(),
+                    );
+                });
+            }
+
+
         });
 }
diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs
index 9feb723..fdd6f6a 100644
--- a/crates/common/src/lib.rs
+++ b/crates/common/src/lib.rs
@@ -1,3 +1,4 @@
+use std::fmt::Debug;
 use serde::{Deserialize, Serialize};
 
 #[derive(Serialize, Deserialize)]
@@ -13,3 +14,34 @@ impl Default for TileRequestOptions {
         }
     }
 }
+
+#[derive(Deserialize, Serialize)]
+pub struct GribTileMetadata {
+    pub message_time: GribMessageTimeMetadata,
+    pub source_configuration: Grib2DataSource
+}
+#[derive(Deserialize, Serialize)]
+pub struct GribMessageTimeMetadata {
+    pub reference_time_significance: u8,
+    pub year: u16,
+    pub month: u8,
+    pub day: u8,
+    pub hour: u8,
+    pub minute: u8,
+    pub second: u8,
+}
+#[derive(Serialize, Deserialize, Clone)]
+pub struct Grib2DataSource {
+    pub from: String,
+    pub needs_gzip: bool,
+    pub valid_for: usize,
+    pub palette: String,
+    pub missing: Option<f64>,
+    pub no_coverage: Option<f64>,
+    pub range_folded: Option<f64>,
+}
+impl Debug for Grib2DataSource {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "<grib2 data source>")
+    }
+}
\ No newline at end of file
diff --git a/crates/tiler/Cargo.toml b/crates/tiler/Cargo.toml
index 0292aa1..c122dcb 100644
--- a/crates/tiler/Cargo.toml
+++ b/crates/tiler/Cargo.toml
@@ -36,4 +36,9 @@ wxbox-pal = { version = "0.1", path = "../pal" }
 
 # nexrad
 geographiclib = "0.1"
-rayon = "1.10"
\ No newline at end of file
+rayon = "1.10"
+strum = "0.27"
+strum_macros = "0.27"
+
+# meta
+wxbox-common = { path = "../common" }
\ No newline at end of file
diff --git a/crates/tiler/config.toml b/crates/tiler/config.toml
index be3318f..cc00c4c 100644
--- a/crates/tiler/config.toml
+++ b/crates/tiler/config.toml
@@ -15,7 +15,7 @@ Color:  80   128  128  128
 missing = -99.0
 no_coverage = -999.0
 
-[data.nexrad.kcrp_ref_test]
+[data.nexrad.l2_reflectivity]
 from = "aaaa"
 palette = """
 Color: 5 0x40 0xe8 0xe3
diff --git a/crates/tiler/src/grib2.rs b/crates/tiler/src/grib2.rs
index c2c6e39..06e8273 100644
--- a/crates/tiler/src/grib2.rs
+++ b/crates/tiler/src/grib2.rs
@@ -18,6 +18,7 @@ use std::io;
 use std::io::{Cursor, ErrorKind};
 use std::num::TryFromIntError;
 use std::sync::Arc;
+use axum::Json;
 use tokio::io::AsyncReadExt;
 use tracing::{debug, info_span};
 use wxbox_grib2::GribMessage;
@@ -25,25 +26,48 @@ use wxbox_grib2::wgs84::LatLong;
 use wxbox_pal::{ColorPalette, Palette};
 
 use rayon::iter::ParallelIterator;
+use wxbox_common::{Grib2DataSource, GribMessageTimeMetadata, GribTileMetadata};
+
 pub type Grib2DataCache = Cache<DataId, Arc<GribMessage>>;
 pub type Grib2TileCache = Cache<TileId, Arc<Vec<u8>>>;
 
 pub type Grib2DataConfig = HashMap<String, Grib2DataSource>;
 
-#[derive(Serialize, Deserialize, Clone)]
-pub struct Grib2DataSource {
-    pub from: String,
-    pub needs_gzip: bool,
-    pub valid_for: usize,
-    pub palette: String,
-    pub missing: Option<f64>,
-    pub no_coverage: Option<f64>,
-    pub range_folded: Option<f64>,
-}
-impl Debug for Grib2DataSource {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(f, "<grib2 data source>")
-    }
+#[tracing::instrument(level = "info")]
+pub async fn grib2_metadata(
+    Path((source)): Path<(String)>,
+    State(state): State<AppState>,
+) -> Result<Json<wxbox_common::GribTileMetadata>, AppError> {
+    // is this even a valid data source?
+    let data_id = DataId {
+        source,
+    };
+    let Some(ds) = state.config.data.grib2.get(&data_id.source) else {
+        return Err(anyhow!("invalid/unknown grib2 state").into());
+    };
+
+    // ok, so we don't have a tile image yet
+    // this means we are going to have to kick off a task to put that in the cache
+    // lets check if we have the raw data
+    let data = if !state.grib2_data_cache.contains_key(&data_id) {
+        // we don't, so let's start by starting a task for that
+        load_grib2_data(state.grib2_data_cache, data_id, ds.clone()).await?
+    } else {
+        state.grib2_data_cache.get(&data_id).await.unwrap()
+    };
+
+    Ok(Json(GribTileMetadata {
+        message_time: GribMessageTimeMetadata {
+            reference_time_significance: data.identification.reference_time_significance,
+            year: data.identification.year,
+            month: data.identification.month,
+            day: data.identification.day,
+            hour: data.identification.hour,
+            minute: data.identification.minute,
+            second: data.identification.second,
+        },
+        source_configuration: ds.clone(),
+    }))
 }
 
 #[tracing::instrument(level = "info")]
diff --git a/crates/tiler/src/main.rs b/crates/tiler/src/main.rs
index 8f07a19..a8c99c3 100644
--- a/crates/tiler/src/main.rs
+++ b/crates/tiler/src/main.rs
@@ -5,7 +5,7 @@ mod nexrad;
 mod tiles;
 
 use crate::config::Config;
-use crate::grib2::{Grib2DataCache, Grib2TileCache, grib2_handler};
+use crate::grib2::{Grib2DataCache, Grib2TileCache, grib2_handler, grib2_metadata};
 use crate::nexrad::{NexradDataCache, NexradTileCache, nexrad_handler};
 use crate::tiles::{DataId, TileId};
 use axum::Router;
@@ -60,6 +60,7 @@ async fn main() -> anyhow::Result<()> {
 
     let app = Router::new()
         .route("/grib2/{source}/{z}/{x}/{y}", get(grib2_handler))
+        .route("/grib2/{source}/metadata", get(grib2_metadata))
         .route("/nexrad/{source}/{z}/{x}/{y}", get(nexrad_handler))
         .with_state(state);