From 51165037f7c3cc848fa21d0dfd378b809c69493b Mon Sep 17 00:00:00 2001
From: karencfv <karencfv@users.noreply.github.com>
Date: Tue, 22 Apr 2025 18:37:48 +1200
Subject: [PATCH 01/11] [inventory] record caboose SIGN value

---
 dev-tools/omdb/src/bin/omdb/db.rs             |  6 +++++
 nexus/db-model/src/inventory.rs               |  3 +++
 nexus/db-model/src/schema_versions.rs         |  3 ++-
 .../db-queries/src/db/datastore/inventory.rs  |  6 ++++-
 nexus/db-schema/src/schema.rs                 |  1 +
 nexus/inventory/src/builder.rs                |  7 +++---
 nexus/inventory/src/collector.rs              |  4 ++--
 nexus/inventory/src/examples.rs               |  2 +-
 .../tests/output/collector_basic.txt          | 24 +++++++++----------
 .../tests/output/collector_errors.txt         | 24 +++++++++----------
 .../output/collector_sled_agent_errors.txt    | 24 +++++++++----------
 nexus/types/src/inventory.rs                  |  2 ++
 schema/crdb/caboose-sign-value/up01.sql       |  2 ++
 schema/crdb/dbinit.sql                        |  5 ++--
 14 files changed, 67 insertions(+), 46 deletions(-)
 create mode 100644 schema/crdb/caboose-sign-value/up01.sql

diff --git a/dev-tools/omdb/src/bin/omdb/db.rs b/dev-tools/omdb/src/bin/omdb/db.rs
index 822fa5fdfa6..9ffc424874c 100644
--- a/dev-tools/omdb/src/bin/omdb/db.rs
+++ b/dev-tools/omdb/src/bin/omdb/db.rs
@@ -6755,6 +6755,8 @@ async fn cmd_db_inventory_cabooses(
         git_commit: String,
         name: String,
         version: String,
+        #[tabled(display_with = "option_impl_display")]
+        sign: Option<String>,
     }
 
     use nexus_db_schema::schema::sw_caboose::dsl;
@@ -6773,6 +6775,7 @@ async fn cmd_db_inventory_cabooses(
         name: caboose.name,
         version: caboose.version,
         git_commit: caboose.git_commit,
+        sign: caboose.sign,
     });
     let table = tabled::Table::new(rows)
         .with(tabled::settings::Style::empty())
@@ -7121,6 +7124,8 @@ async fn inv_collection_print_devices(
             name: &'a str,
             version: &'a str,
             git_commit: &'a str,
+            #[tabled(display_with = "option_impl_display")]
+            sign: &'a Option<String>,
         }
 
         println!("    cabooses:");
@@ -7134,6 +7139,7 @@ async fn inv_collection_print_devices(
                 name: &found_caboose.caboose.name,
                 version: &found_caboose.caboose.version,
                 git_commit: &found_caboose.caboose.git_commit,
+                sign: &found_caboose.caboose.sign,
             })
             .collect();
         let table = tabled::Table::new(caboose_rows)
diff --git a/nexus/db-model/src/inventory.rs b/nexus/db-model/src/inventory.rs
index 71ebe4e145d..62abfacdab2 100644
--- a/nexus/db-model/src/inventory.rs
+++ b/nexus/db-model/src/inventory.rs
@@ -458,6 +458,7 @@ pub struct SwCaboose {
     pub git_commit: String,
     pub name: String,
     pub version: String,
+    pub sign: Option<String>,
 }
 
 impl From<Caboose> for SwCaboose {
@@ -468,6 +469,7 @@ impl From<Caboose> for SwCaboose {
             git_commit: c.git_commit,
             name: c.name,
             version: c.version,
+            sign: c.sign,
         }
     }
 }
@@ -479,6 +481,7 @@ impl From<SwCaboose> for Caboose {
             git_commit: row.git_commit,
             name: row.name,
             version: row.version,
+            sign: row.sign,
         }
     }
 }
diff --git a/nexus/db-model/src/schema_versions.rs b/nexus/db-model/src/schema_versions.rs
index fa3f8f49352..7a86e1e8bc9 100644
--- a/nexus/db-model/src/schema_versions.rs
+++ b/nexus/db-model/src/schema_versions.rs
@@ -16,7 +16,7 @@ use std::{collections::BTreeMap, sync::LazyLock};
 ///
 /// This must be updated when you change the database schema.  Refer to
 /// schema/crdb/README.adoc in the root of this repository for details.
-pub const SCHEMA_VERSION: Version = Version::new(138, 0, 0);
+pub const SCHEMA_VERSION: Version = Version::new(139, 0, 0);
 
 /// List of all past database schema versions, in *reverse* order
 ///
@@ -28,6 +28,7 @@ static KNOWN_VERSIONS: LazyLock<Vec<KnownVersion>> = LazyLock::new(|| {
         // |  leaving the first copy as an example for the next person.
         // v
         // KnownVersion::new(next_int, "unique-dirname-with-the-sql-files"),
+        KnownVersion::new(139, "caboose-sign-value"),
         KnownVersion::new(138, "saga-abandoned-state"),
         KnownVersion::new(137, "oximeter-read-policy"),
         KnownVersion::new(136, "do-not-provision-flag-for-crucible-dataset"),
diff --git a/nexus/db-queries/src/db/datastore/inventory.rs b/nexus/db-queries/src/db/datastore/inventory.rs
index 0d5242bee32..2a9d1688710 100644
--- a/nexus/db-queries/src/db/datastore/inventory.rs
+++ b/nexus/db-queries/src/db/datastore/inventory.rs
@@ -596,7 +596,8 @@ impl DataStore {
             //         AND sw_caboose.board = ...
             //         AND sw_caboose.git_commit = ...
             //         AND sw_caboose.name = ...
-            //         AND sw_caboose.version = ...;
+            //         AND sw_caboose.version = ...
+            //         AND sw_caboose.sign = ...;
             //
             // Again, the whole point is to avoid back-and-forth between the
             // client and the database.  Those back-and-forth interactions can
@@ -642,6 +643,9 @@ impl DataStore {
                                     )
                                     .and(dsl_sw_caboose::version.eq(
                                         found_caboose.caboose.version.clone(),
+                                    ))
+                                    .and(dsl_sw_caboose::sign.eq(
+                                        found_caboose.caboose.sign.clone(),
                                     )),
                             ),
                         )
diff --git a/nexus/db-schema/src/schema.rs b/nexus/db-schema/src/schema.rs
index 04da8f8129f..f14ba51b1d3 100644
--- a/nexus/db-schema/src/schema.rs
+++ b/nexus/db-schema/src/schema.rs
@@ -1475,6 +1475,7 @@ table! {
         git_commit -> Text,
         name -> Text,
         version -> Text,
+        sign -> Nullable<Text>,
     }
 }
 
diff --git a/nexus/inventory/src/builder.rs b/nexus/inventory/src/builder.rs
index 3d4e94be1db..960d4aa0879 100644
--- a/nexus/inventory/src/builder.rs
+++ b/nexus/inventory/src/builder.rs
@@ -717,6 +717,7 @@ mod test {
             git_commit: String::from("git_commit_1"),
             name: String::from("name_1"),
             version: String::from("version_1"),
+            sign: Some(String::from("sign_1")),
         };
         for bb in &common_caboose_baseboards {
             let _ = collection.sps.get(*bb).unwrap();
@@ -1105,7 +1106,7 @@ mod test {
             git_commit: String::from("git_commit1"),
             name: String::from("name1"),
             version: String::from("version1"),
-            sign: None,
+            sign: Some(String::from("sign1")),
             epoch: None,
         };
         assert!(
@@ -1125,7 +1126,7 @@ mod test {
             "reporting caboose for unknown baseboard: \
             BaseboardId { part_number: \"p1\", serial_number: \"bogus\" } \
             (Caboose { board: \"board1\", git_commit: \"git_commit1\", \
-            name: \"name1\", version: \"version1\" })"
+            name: \"name1\", version: \"version1\", sign: Some(\"sign1\") })"
         );
         assert!(
             !builder
@@ -1177,7 +1178,7 @@ mod test {
                     git_commit: String::from("git_commit2"),
                     name: String::from("name2"),
                     version: String::from("version2"),
-                    sign: None,
+                    sign: Some(String::from("sign2")),
                     epoch: None,
                 },
             )
diff --git a/nexus/inventory/src/collector.rs b/nexus/inventory/src/collector.rs
index 36a97a26ddf..c2a8aa37d85 100644
--- a/nexus/inventory/src/collector.rs
+++ b/nexus/inventory/src/collector.rs
@@ -451,8 +451,8 @@ mod test {
         for c in &collection.cabooses {
             write!(
                 &mut s,
-                "    board {:?} name {:?} version {:?} git_commit {:?}\n",
-                c.board, c.name, c.version, c.git_commit,
+                "    board {:?} name {:?} version {:?} git_commit {:?} sign {:?}\n",
+                c.board, c.name, c.version, c.git_commit, c.sign,
             )
             .unwrap();
         }
diff --git a/nexus/inventory/src/examples.rs b/nexus/inventory/src/examples.rs
index 03006d5e6f0..f8dea3a4e0d 100644
--- a/nexus/inventory/src/examples.rs
+++ b/nexus/inventory/src/examples.rs
@@ -540,7 +540,7 @@ pub fn caboose(unique: &str) -> SpComponentCaboose {
         git_commit: format!("git_commit_{}", unique),
         name: format!("name_{}", unique),
         version: format!("version_{}", unique),
-        sign: None,
+        sign: Some(format!("sign_{}", unique)),
         epoch: None,
     }
 }
diff --git a/nexus/inventory/tests/output/collector_basic.txt b/nexus/inventory/tests/output/collector_basic.txt
index 896cdcddbc6..6550ef1f9d8 100644
--- a/nexus/inventory/tests/output/collector_basic.txt
+++ b/nexus/inventory/tests/output/collector_basic.txt
@@ -7,18 +7,18 @@ baseboards:
     part "sim-gimlet" serial "sim-9cb9b78f-5614-440c-b66d-e8e81fab69b0"
 
 cabooses:
-    board "SimGimletSp" name "SimGimlet" version "0.0.1" git_commit "fefefefe"
-    board "SimGimletSp" name "SimGimlet" version "0.0.2" git_commit "ffffffff"
-    board "SimRot" name "SimGimletRot" version "0.0.3" git_commit "edededed"
-    board "SimRot" name "SimSidecar" version "0.0.3" git_commit "edededed"
-    board "SimRot" name "SimGimletRot" version "0.0.4" git_commit "eeeeeeee"
-    board "SimRot" name "SimSidecar" version "0.0.4" git_commit "eeeeeeee"
-    board "SimRotStage0" name "SimSidecar" version "0.0.200" git_commit "dadadada"
-    board "SimRotStage0" name "SimGimletRot" version "0.0.200" git_commit "dadadadad"
-    board "SimRotStage0" name "SimSidecar" version "0.0.200" git_commit "dddddddd"
-    board "SimRotStage0" name "SimGimletRot" version "0.0.200" git_commit "ddddddddd"
-    board "SimSidecarSp" name "SimSidecar" version "0.0.1" git_commit "fefefefe"
-    board "SimSidecarSp" name "SimSidecar" version "0.0.2" git_commit "ffffffff"
+    board "SimGimletSp" name "SimGimlet" version "0.0.1" git_commit "fefefefe" sign None
+    board "SimGimletSp" name "SimGimlet" version "0.0.2" git_commit "ffffffff" sign None
+    board "SimRot" name "SimGimletRot" version "0.0.3" git_commit "edededed" sign Some("11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf")
+    board "SimRot" name "SimSidecar" version "0.0.3" git_commit "edededed" sign Some("1432cc4cfe5688c51b55546fe37837c753cfbc89e8c3c6aabcf977fdf0c41e27")
+    board "SimRot" name "SimGimletRot" version "0.0.4" git_commit "eeeeeeee" sign Some("11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf")
+    board "SimRot" name "SimSidecar" version "0.0.4" git_commit "eeeeeeee" sign Some("1432cc4cfe5688c51b55546fe37837c753cfbc89e8c3c6aabcf977fdf0c41e27")
+    board "SimRotStage0" name "SimSidecar" version "0.0.200" git_commit "dadadada" sign Some("1432cc4cfe5688c51b55546fe37837c753cfbc89e8c3c6aabcf977fdf0c41e27")
+    board "SimRotStage0" name "SimGimletRot" version "0.0.200" git_commit "dadadadad" sign Some("11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf")
+    board "SimRotStage0" name "SimSidecar" version "0.0.200" git_commit "dddddddd" sign Some("1432cc4cfe5688c51b55546fe37837c753cfbc89e8c3c6aabcf977fdf0c41e27")
+    board "SimRotStage0" name "SimGimletRot" version "0.0.200" git_commit "ddddddddd" sign Some("11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf")
+    board "SimSidecarSp" name "SimSidecar" version "0.0.1" git_commit "fefefefe" sign None
+    board "SimSidecarSp" name "SimSidecar" version "0.0.2" git_commit "ffffffff" sign None
 
 rot pages:
     data_base64 "Z2ltbGV0LWNmcGEtYWN0aXZlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
diff --git a/nexus/inventory/tests/output/collector_errors.txt b/nexus/inventory/tests/output/collector_errors.txt
index 79d61567dd1..92ccb4bedcc 100644
--- a/nexus/inventory/tests/output/collector_errors.txt
+++ b/nexus/inventory/tests/output/collector_errors.txt
@@ -5,18 +5,18 @@ baseboards:
     part "i86pc" serial "SimGimlet01"
 
 cabooses:
-    board "SimGimletSp" name "SimGimlet" version "0.0.1" git_commit "fefefefe"
-    board "SimGimletSp" name "SimGimlet" version "0.0.2" git_commit "ffffffff"
-    board "SimRot" name "SimGimletRot" version "0.0.3" git_commit "edededed"
-    board "SimRot" name "SimSidecar" version "0.0.3" git_commit "edededed"
-    board "SimRot" name "SimGimletRot" version "0.0.4" git_commit "eeeeeeee"
-    board "SimRot" name "SimSidecar" version "0.0.4" git_commit "eeeeeeee"
-    board "SimRotStage0" name "SimSidecar" version "0.0.200" git_commit "dadadada"
-    board "SimRotStage0" name "SimGimletRot" version "0.0.200" git_commit "dadadadad"
-    board "SimRotStage0" name "SimSidecar" version "0.0.200" git_commit "dddddddd"
-    board "SimRotStage0" name "SimGimletRot" version "0.0.200" git_commit "ddddddddd"
-    board "SimSidecarSp" name "SimSidecar" version "0.0.1" git_commit "fefefefe"
-    board "SimSidecarSp" name "SimSidecar" version "0.0.2" git_commit "ffffffff"
+    board "SimGimletSp" name "SimGimlet" version "0.0.1" git_commit "fefefefe" sign None
+    board "SimGimletSp" name "SimGimlet" version "0.0.2" git_commit "ffffffff" sign None
+    board "SimRot" name "SimGimletRot" version "0.0.3" git_commit "edededed" sign Some("11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf")
+    board "SimRot" name "SimSidecar" version "0.0.3" git_commit "edededed" sign Some("1432cc4cfe5688c51b55546fe37837c753cfbc89e8c3c6aabcf977fdf0c41e27")
+    board "SimRot" name "SimGimletRot" version "0.0.4" git_commit "eeeeeeee" sign Some("11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf")
+    board "SimRot" name "SimSidecar" version "0.0.4" git_commit "eeeeeeee" sign Some("1432cc4cfe5688c51b55546fe37837c753cfbc89e8c3c6aabcf977fdf0c41e27")
+    board "SimRotStage0" name "SimSidecar" version "0.0.200" git_commit "dadadada" sign Some("1432cc4cfe5688c51b55546fe37837c753cfbc89e8c3c6aabcf977fdf0c41e27")
+    board "SimRotStage0" name "SimGimletRot" version "0.0.200" git_commit "dadadadad" sign Some("11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf")
+    board "SimRotStage0" name "SimSidecar" version "0.0.200" git_commit "dddddddd" sign Some("1432cc4cfe5688c51b55546fe37837c753cfbc89e8c3c6aabcf977fdf0c41e27")
+    board "SimRotStage0" name "SimGimletRot" version "0.0.200" git_commit "ddddddddd" sign Some("11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf")
+    board "SimSidecarSp" name "SimSidecar" version "0.0.1" git_commit "fefefefe" sign None
+    board "SimSidecarSp" name "SimSidecar" version "0.0.2" git_commit "ffffffff" sign None
 
 rot pages:
     data_base64 "Z2ltbGV0LWNmcGEtYWN0aXZlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
diff --git a/nexus/inventory/tests/output/collector_sled_agent_errors.txt b/nexus/inventory/tests/output/collector_sled_agent_errors.txt
index 9e9c79aa92d..87e6e3c1589 100644
--- a/nexus/inventory/tests/output/collector_sled_agent_errors.txt
+++ b/nexus/inventory/tests/output/collector_sled_agent_errors.txt
@@ -6,18 +6,18 @@ baseboards:
     part "sim-gimlet" serial "sim-9cb9b78f-5614-440c-b66d-e8e81fab69b0"
 
 cabooses:
-    board "SimGimletSp" name "SimGimlet" version "0.0.1" git_commit "fefefefe"
-    board "SimGimletSp" name "SimGimlet" version "0.0.2" git_commit "ffffffff"
-    board "SimRot" name "SimGimletRot" version "0.0.3" git_commit "edededed"
-    board "SimRot" name "SimSidecar" version "0.0.3" git_commit "edededed"
-    board "SimRot" name "SimGimletRot" version "0.0.4" git_commit "eeeeeeee"
-    board "SimRot" name "SimSidecar" version "0.0.4" git_commit "eeeeeeee"
-    board "SimRotStage0" name "SimSidecar" version "0.0.200" git_commit "dadadada"
-    board "SimRotStage0" name "SimGimletRot" version "0.0.200" git_commit "dadadadad"
-    board "SimRotStage0" name "SimSidecar" version "0.0.200" git_commit "dddddddd"
-    board "SimRotStage0" name "SimGimletRot" version "0.0.200" git_commit "ddddddddd"
-    board "SimSidecarSp" name "SimSidecar" version "0.0.1" git_commit "fefefefe"
-    board "SimSidecarSp" name "SimSidecar" version "0.0.2" git_commit "ffffffff"
+    board "SimGimletSp" name "SimGimlet" version "0.0.1" git_commit "fefefefe" sign None
+    board "SimGimletSp" name "SimGimlet" version "0.0.2" git_commit "ffffffff" sign None
+    board "SimRot" name "SimGimletRot" version "0.0.3" git_commit "edededed" sign Some("11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf")
+    board "SimRot" name "SimSidecar" version "0.0.3" git_commit "edededed" sign Some("1432cc4cfe5688c51b55546fe37837c753cfbc89e8c3c6aabcf977fdf0c41e27")
+    board "SimRot" name "SimGimletRot" version "0.0.4" git_commit "eeeeeeee" sign Some("11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf")
+    board "SimRot" name "SimSidecar" version "0.0.4" git_commit "eeeeeeee" sign Some("1432cc4cfe5688c51b55546fe37837c753cfbc89e8c3c6aabcf977fdf0c41e27")
+    board "SimRotStage0" name "SimSidecar" version "0.0.200" git_commit "dadadada" sign Some("1432cc4cfe5688c51b55546fe37837c753cfbc89e8c3c6aabcf977fdf0c41e27")
+    board "SimRotStage0" name "SimGimletRot" version "0.0.200" git_commit "dadadadad" sign Some("11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf")
+    board "SimRotStage0" name "SimSidecar" version "0.0.200" git_commit "dddddddd" sign Some("1432cc4cfe5688c51b55546fe37837c753cfbc89e8c3c6aabcf977fdf0c41e27")
+    board "SimRotStage0" name "SimGimletRot" version "0.0.200" git_commit "ddddddddd" sign Some("11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf")
+    board "SimSidecarSp" name "SimSidecar" version "0.0.1" git_commit "fefefefe" sign None
+    board "SimSidecarSp" name "SimSidecar" version "0.0.2" git_commit "ffffffff" sign None
 
 rot pages:
     data_base64 "Z2ltbGV0LWNmcGEtYWN0aXZlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
diff --git a/nexus/types/src/inventory.rs b/nexus/types/src/inventory.rs
index 7f0bbdf1652..06d48cfa53d 100644
--- a/nexus/types/src/inventory.rs
+++ b/nexus/types/src/inventory.rs
@@ -260,6 +260,7 @@ pub struct Caboose {
     pub git_commit: String,
     pub name: String,
     pub version: String,
+    pub sign: Option<String>,
 }
 
 impl From<gateway_client::types::SpComponentCaboose> for Caboose {
@@ -269,6 +270,7 @@ impl From<gateway_client::types::SpComponentCaboose> for Caboose {
             git_commit: c.git_commit,
             name: c.name,
             version: c.version,
+            sign: c.sign,
         }
     }
 }
diff --git a/schema/crdb/caboose-sign-value/up01.sql b/schema/crdb/caboose-sign-value/up01.sql
new file mode 100644
index 00000000000..4c18df41ea2
--- /dev/null
+++ b/schema/crdb/caboose-sign-value/up01.sql
@@ -0,0 +1,2 @@
+ALTER TABLE omicron.public.sw_caboose
+    ADD COLUMN IF NOT EXISTS sign TEXT; -- nullable
diff --git a/schema/crdb/dbinit.sql b/schema/crdb/dbinit.sql
index a88d99fda9b..285f3d2c645 100644
--- a/schema/crdb/dbinit.sql
+++ b/schema/crdb/dbinit.sql
@@ -3347,7 +3347,8 @@ CREATE TABLE IF NOT EXISTS omicron.public.sw_caboose (
     board TEXT NOT NULL,
     git_commit TEXT NOT NULL,
     name TEXT NOT NULL,
-    version TEXT NOT NULL
+    version TEXT NOT NULL,
+    sign TEXT -- nullable
 );
 CREATE UNIQUE INDEX IF NOT EXISTS caboose_properties
     on omicron.public.sw_caboose (board, git_commit, name, version);
@@ -5098,7 +5099,7 @@ INSERT INTO omicron.public.db_metadata (
     version,
     target_version
 ) VALUES
-    (TRUE, NOW(), NOW(), '138.0.0', NULL)
+    (TRUE, NOW(), NOW(), '139.0.0', NULL)
 ON CONFLICT DO NOTHING;
 
 COMMIT;

From 567731ef3c40168ebd8c8356e0311115b6d38b1c Mon Sep 17 00:00:00 2001
From: karencfv <karencfv@users.noreply.github.com>
Date: Thu, 24 Apr 2025 12:01:52 +1200
Subject: [PATCH 02/11] use IS NOT DISTINCT FROM instead of EQ for nullable
 field

---
 nexus/db-queries/src/db/datastore/inventory.rs | 8 ++++----
 nexus/types/src/inventory.rs                   | 3 +++
 2 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/nexus/db-queries/src/db/datastore/inventory.rs b/nexus/db-queries/src/db/datastore/inventory.rs
index 2a9d1688710..ebfa65106c1 100644
--- a/nexus/db-queries/src/db/datastore/inventory.rs
+++ b/nexus/db-queries/src/db/datastore/inventory.rs
@@ -12,7 +12,7 @@ use async_bb8_diesel::AsyncConnection;
 use async_bb8_diesel::AsyncRunQueryDsl;
 use async_bb8_diesel::AsyncSimpleConnection;
 use clickhouse_admin_types::ClickhouseKeeperClusterMembership;
-use diesel::BoolExpressionMethods;
+use diesel::{BoolExpressionMethods, PgExpressionMethods};
 use diesel::ExpressionMethods;
 use diesel::IntoSql;
 use diesel::JoinOnDsl;
@@ -554,7 +554,7 @@ impl DataStore {
             // - `hw_baseboard` with an "id" primary key and lookup columns
             //   "part_number" and "serial_number"
             // - `sw_caboose` with an "id" primary key and lookup columns
-            //   "board", "git_commit", "name", and "version"
+            //   "board", "git_commit", "name", "version, and sign"
             // - `inv_caboose` with foreign keys "hw_baseboard_id",
             //   "sw_caboose_id", and various other columns
             //
@@ -597,7 +597,7 @@ impl DataStore {
             //         AND sw_caboose.git_commit = ...
             //         AND sw_caboose.name = ...
             //         AND sw_caboose.version = ...
-            //         AND sw_caboose.sign = ...;
+            //         AND sw_caboose.sign IS NOT DISTINCT FROM ...;
             //
             // Again, the whole point is to avoid back-and-forth between the
             // client and the database.  Those back-and-forth interactions can
@@ -644,7 +644,7 @@ impl DataStore {
                                     .and(dsl_sw_caboose::version.eq(
                                         found_caboose.caboose.version.clone(),
                                     ))
-                                    .and(dsl_sw_caboose::sign.eq(
+                                    .and(dsl_sw_caboose::sign.is_not_distinct_from(
                                         found_caboose.caboose.sign.clone(),
                                     )),
                             ),
diff --git a/nexus/types/src/inventory.rs b/nexus/types/src/inventory.rs
index 06d48cfa53d..d391bc52cf6 100644
--- a/nexus/types/src/inventory.rs
+++ b/nexus/types/src/inventory.rs
@@ -260,6 +260,9 @@ pub struct Caboose {
     pub git_commit: String,
     pub name: String,
     pub version: String,
+    // It is very unlikely that the sign hash will not be present.
+    // As far as we know, it will only be locally built BART signed images.
+    // We will be handling those edge cases by setting this field as an option.
     pub sign: Option<String>,
 }
 

From d408a44fca8a985cc0ae816981d14d63791f769c Mon Sep 17 00:00:00 2001
From: karencfv <karencfv@users.noreply.github.com>
Date: Thu, 24 Apr 2025 12:03:29 +1200
Subject: [PATCH 03/11] fmt

---
 nexus/db-queries/src/db/datastore/inventory.rs | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/nexus/db-queries/src/db/datastore/inventory.rs b/nexus/db-queries/src/db/datastore/inventory.rs
index ebfa65106c1..a4f9084405c 100644
--- a/nexus/db-queries/src/db/datastore/inventory.rs
+++ b/nexus/db-queries/src/db/datastore/inventory.rs
@@ -12,12 +12,13 @@ use async_bb8_diesel::AsyncConnection;
 use async_bb8_diesel::AsyncRunQueryDsl;
 use async_bb8_diesel::AsyncSimpleConnection;
 use clickhouse_admin_types::ClickhouseKeeperClusterMembership;
-use diesel::{BoolExpressionMethods, PgExpressionMethods};
+use diesel::BoolExpressionMethods;
 use diesel::ExpressionMethods;
 use diesel::IntoSql;
 use diesel::JoinOnDsl;
 use diesel::NullableExpressionMethods;
 use diesel::OptionalExtension;
+use diesel::PgExpressionMethods;
 use diesel::QueryDsl;
 use diesel::Table;
 use diesel::expression::SelectableHelper;

From ba10e730236a1ada4c79fc88b656db60b2aeb509 Mon Sep 17 00:00:00 2001
From: karencfv <karencfv@users.noreply.github.com>
Date: Wed, 30 Apr 2025 08:55:54 +1200
Subject: [PATCH 04/11] create unique index

---
 schema/crdb/caboose-sign-value/up02.sql | 2 ++
 schema/crdb/dbinit.sql                  | 2 +-
 2 files changed, 3 insertions(+), 1 deletion(-)
 create mode 100644 schema/crdb/caboose-sign-value/up02.sql

diff --git a/schema/crdb/caboose-sign-value/up02.sql b/schema/crdb/caboose-sign-value/up02.sql
new file mode 100644
index 00000000000..29d929beef1
--- /dev/null
+++ b/schema/crdb/caboose-sign-value/up02.sql
@@ -0,0 +1,2 @@
+CREATE UNIQUE INDEX IF NOT EXISTS caboose_properties
+    on omicron.public.sw_caboose (sign);
\ No newline at end of file
diff --git a/schema/crdb/dbinit.sql b/schema/crdb/dbinit.sql
index 285f3d2c645..c119369e570 100644
--- a/schema/crdb/dbinit.sql
+++ b/schema/crdb/dbinit.sql
@@ -3351,7 +3351,7 @@ CREATE TABLE IF NOT EXISTS omicron.public.sw_caboose (
     sign TEXT -- nullable
 );
 CREATE UNIQUE INDEX IF NOT EXISTS caboose_properties
-    on omicron.public.sw_caboose (board, git_commit, name, version);
+    on omicron.public.sw_caboose (board, git_commit, name, version, sign);
 
 /* root of trust pages: this table assigns unique ids to distinct RoT CMPA
    and CFPA page contents, each of which is a 512-byte blob */

From 8803cde4b04013a4d70869a9e8e979d89833e5a9 Mon Sep 17 00:00:00 2001
From: karencfv <karencfv@users.noreply.github.com>
Date: Wed, 30 Apr 2025 14:12:37 +1200
Subject: [PATCH 05/11] expectorate

---
 nexus/inventory/tests/output/collector_basic.txt          | 8 ++++----
 nexus/inventory/tests/output/collector_errors.txt         | 8 ++++----
 .../tests/output/collector_sled_agent_errors.txt          | 8 ++++----
 nexus/types/src/inventory.rs                              | 5 ++---
 4 files changed, 14 insertions(+), 15 deletions(-)

diff --git a/nexus/inventory/tests/output/collector_basic.txt b/nexus/inventory/tests/output/collector_basic.txt
index 6550ef1f9d8..b3b70755574 100644
--- a/nexus/inventory/tests/output/collector_basic.txt
+++ b/nexus/inventory/tests/output/collector_basic.txt
@@ -10,13 +10,13 @@ cabooses:
     board "SimGimletSp" name "SimGimlet" version "0.0.1" git_commit "fefefefe" sign None
     board "SimGimletSp" name "SimGimlet" version "0.0.2" git_commit "ffffffff" sign None
     board "SimRot" name "SimGimletRot" version "0.0.3" git_commit "edededed" sign Some("11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf")
-    board "SimRot" name "SimSidecar" version "0.0.3" git_commit "edededed" sign Some("1432cc4cfe5688c51b55546fe37837c753cfbc89e8c3c6aabcf977fdf0c41e27")
+    board "SimRot" name "SimSidecarRot" version "0.0.3" git_commit "edededed" sign Some("11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf")
     board "SimRot" name "SimGimletRot" version "0.0.4" git_commit "eeeeeeee" sign Some("11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf")
-    board "SimRot" name "SimSidecar" version "0.0.4" git_commit "eeeeeeee" sign Some("1432cc4cfe5688c51b55546fe37837c753cfbc89e8c3c6aabcf977fdf0c41e27")
-    board "SimRotStage0" name "SimSidecar" version "0.0.200" git_commit "dadadada" sign Some("1432cc4cfe5688c51b55546fe37837c753cfbc89e8c3c6aabcf977fdf0c41e27")
+    board "SimRot" name "SimSidecarRot" version "0.0.4" git_commit "eeeeeeee" sign Some("11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf")
     board "SimRotStage0" name "SimGimletRot" version "0.0.200" git_commit "dadadadad" sign Some("11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf")
-    board "SimRotStage0" name "SimSidecar" version "0.0.200" git_commit "dddddddd" sign Some("1432cc4cfe5688c51b55546fe37837c753cfbc89e8c3c6aabcf977fdf0c41e27")
+    board "SimRotStage0" name "SimSidecarRot" version "0.0.200" git_commit "dadadadad" sign Some("11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf")
     board "SimRotStage0" name "SimGimletRot" version "0.0.200" git_commit "ddddddddd" sign Some("11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf")
+    board "SimRotStage0" name "SimSidecarRot" version "0.0.200" git_commit "ddddddddd" sign Some("11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf")
     board "SimSidecarSp" name "SimSidecar" version "0.0.1" git_commit "fefefefe" sign None
     board "SimSidecarSp" name "SimSidecar" version "0.0.2" git_commit "ffffffff" sign None
 
diff --git a/nexus/inventory/tests/output/collector_errors.txt b/nexus/inventory/tests/output/collector_errors.txt
index 92ccb4bedcc..d6eba211a9c 100644
--- a/nexus/inventory/tests/output/collector_errors.txt
+++ b/nexus/inventory/tests/output/collector_errors.txt
@@ -8,13 +8,13 @@ cabooses:
     board "SimGimletSp" name "SimGimlet" version "0.0.1" git_commit "fefefefe" sign None
     board "SimGimletSp" name "SimGimlet" version "0.0.2" git_commit "ffffffff" sign None
     board "SimRot" name "SimGimletRot" version "0.0.3" git_commit "edededed" sign Some("11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf")
-    board "SimRot" name "SimSidecar" version "0.0.3" git_commit "edededed" sign Some("1432cc4cfe5688c51b55546fe37837c753cfbc89e8c3c6aabcf977fdf0c41e27")
+    board "SimRot" name "SimSidecarRot" version "0.0.3" git_commit "edededed" sign Some("11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf")
     board "SimRot" name "SimGimletRot" version "0.0.4" git_commit "eeeeeeee" sign Some("11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf")
-    board "SimRot" name "SimSidecar" version "0.0.4" git_commit "eeeeeeee" sign Some("1432cc4cfe5688c51b55546fe37837c753cfbc89e8c3c6aabcf977fdf0c41e27")
-    board "SimRotStage0" name "SimSidecar" version "0.0.200" git_commit "dadadada" sign Some("1432cc4cfe5688c51b55546fe37837c753cfbc89e8c3c6aabcf977fdf0c41e27")
+    board "SimRot" name "SimSidecarRot" version "0.0.4" git_commit "eeeeeeee" sign Some("11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf")
     board "SimRotStage0" name "SimGimletRot" version "0.0.200" git_commit "dadadadad" sign Some("11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf")
-    board "SimRotStage0" name "SimSidecar" version "0.0.200" git_commit "dddddddd" sign Some("1432cc4cfe5688c51b55546fe37837c753cfbc89e8c3c6aabcf977fdf0c41e27")
+    board "SimRotStage0" name "SimSidecarRot" version "0.0.200" git_commit "dadadadad" sign Some("11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf")
     board "SimRotStage0" name "SimGimletRot" version "0.0.200" git_commit "ddddddddd" sign Some("11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf")
+    board "SimRotStage0" name "SimSidecarRot" version "0.0.200" git_commit "ddddddddd" sign Some("11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf")
     board "SimSidecarSp" name "SimSidecar" version "0.0.1" git_commit "fefefefe" sign None
     board "SimSidecarSp" name "SimSidecar" version "0.0.2" git_commit "ffffffff" sign None
 
diff --git a/nexus/inventory/tests/output/collector_sled_agent_errors.txt b/nexus/inventory/tests/output/collector_sled_agent_errors.txt
index 87e6e3c1589..113d5c89c08 100644
--- a/nexus/inventory/tests/output/collector_sled_agent_errors.txt
+++ b/nexus/inventory/tests/output/collector_sled_agent_errors.txt
@@ -9,13 +9,13 @@ cabooses:
     board "SimGimletSp" name "SimGimlet" version "0.0.1" git_commit "fefefefe" sign None
     board "SimGimletSp" name "SimGimlet" version "0.0.2" git_commit "ffffffff" sign None
     board "SimRot" name "SimGimletRot" version "0.0.3" git_commit "edededed" sign Some("11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf")
-    board "SimRot" name "SimSidecar" version "0.0.3" git_commit "edededed" sign Some("1432cc4cfe5688c51b55546fe37837c753cfbc89e8c3c6aabcf977fdf0c41e27")
+    board "SimRot" name "SimSidecarRot" version "0.0.3" git_commit "edededed" sign Some("11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf")
     board "SimRot" name "SimGimletRot" version "0.0.4" git_commit "eeeeeeee" sign Some("11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf")
-    board "SimRot" name "SimSidecar" version "0.0.4" git_commit "eeeeeeee" sign Some("1432cc4cfe5688c51b55546fe37837c753cfbc89e8c3c6aabcf977fdf0c41e27")
-    board "SimRotStage0" name "SimSidecar" version "0.0.200" git_commit "dadadada" sign Some("1432cc4cfe5688c51b55546fe37837c753cfbc89e8c3c6aabcf977fdf0c41e27")
+    board "SimRot" name "SimSidecarRot" version "0.0.4" git_commit "eeeeeeee" sign Some("11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf")
     board "SimRotStage0" name "SimGimletRot" version "0.0.200" git_commit "dadadadad" sign Some("11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf")
-    board "SimRotStage0" name "SimSidecar" version "0.0.200" git_commit "dddddddd" sign Some("1432cc4cfe5688c51b55546fe37837c753cfbc89e8c3c6aabcf977fdf0c41e27")
+    board "SimRotStage0" name "SimSidecarRot" version "0.0.200" git_commit "dadadadad" sign Some("11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf")
     board "SimRotStage0" name "SimGimletRot" version "0.0.200" git_commit "ddddddddd" sign Some("11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf")
+    board "SimRotStage0" name "SimSidecarRot" version "0.0.200" git_commit "ddddddddd" sign Some("11594bb5548a757e918e6fe056e2ad9e084297c9555417a025d8788eacf55daf")
     board "SimSidecarSp" name "SimSidecar" version "0.0.1" git_commit "fefefefe" sign None
     board "SimSidecarSp" name "SimSidecar" version "0.0.2" git_commit "ffffffff" sign None
 
diff --git a/nexus/types/src/inventory.rs b/nexus/types/src/inventory.rs
index d391bc52cf6..2540a5fbb19 100644
--- a/nexus/types/src/inventory.rs
+++ b/nexus/types/src/inventory.rs
@@ -260,9 +260,8 @@ pub struct Caboose {
     pub git_commit: String,
     pub name: String,
     pub version: String,
-    // It is very unlikely that the sign hash will not be present.
-    // As far as we know, it will only be locally built BART signed images.
-    // We will be handling those edge cases by setting this field as an option.
+    // The sign will generally be present for production RoT and RoT bootloader images.
+    // It's currently absent from SP images and could be absent from RoT images as well.
     pub sign: Option<String>,
 }
 

From f659621459a23b8e196ae6fc45271833a4c0772a Mon Sep 17 00:00:00 2001
From: karencfv <karencfv@users.noreply.github.com>
Date: Wed, 30 Apr 2025 14:17:08 +1200
Subject: [PATCH 06/11] fmt

---
 schema/crdb/caboose-sign-value/up02.sql | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/schema/crdb/caboose-sign-value/up02.sql b/schema/crdb/caboose-sign-value/up02.sql
index 29d929beef1..1229ac4f69f 100644
--- a/schema/crdb/caboose-sign-value/up02.sql
+++ b/schema/crdb/caboose-sign-value/up02.sql
@@ -1,2 +1,2 @@
 CREATE UNIQUE INDEX IF NOT EXISTS caboose_properties
-    on omicron.public.sw_caboose (sign);
\ No newline at end of file
+    on omicron.public.sw_caboose (sign);

From 531912b3fc900900bc7bd18f5eac1ce1e22cb1cd Mon Sep 17 00:00:00 2001
From: karencfv <karencfv@users.noreply.github.com>
Date: Wed, 30 Apr 2025 17:31:30 +1200
Subject: [PATCH 07/11] drop the index first

---
 schema/crdb/caboose-sign-value/up02.sql | 3 +--
 schema/crdb/caboose-sign-value/up03.sql | 2 ++
 2 files changed, 3 insertions(+), 2 deletions(-)
 create mode 100644 schema/crdb/caboose-sign-value/up03.sql

diff --git a/schema/crdb/caboose-sign-value/up02.sql b/schema/crdb/caboose-sign-value/up02.sql
index 1229ac4f69f..cccf0aa3427 100644
--- a/schema/crdb/caboose-sign-value/up02.sql
+++ b/schema/crdb/caboose-sign-value/up02.sql
@@ -1,2 +1 @@
-CREATE UNIQUE INDEX IF NOT EXISTS caboose_properties
-    on omicron.public.sw_caboose (sign);
+DROP INDEX IF EXISTS omicron.public.sw_caboose@caboose_properties;
diff --git a/schema/crdb/caboose-sign-value/up03.sql b/schema/crdb/caboose-sign-value/up03.sql
new file mode 100644
index 00000000000..99adfdde745
--- /dev/null
+++ b/schema/crdb/caboose-sign-value/up03.sql
@@ -0,0 +1,2 @@
+CREATE UNIQUE INDEX IF NOT EXISTS caboose_properties
+    on omicron.public.sw_caboose (board, git_commit, name, version, sign);

From 19c5841693f56b76f391294dff7b6ff1b27a4bd5 Mon Sep 17 00:00:00 2001
From: karencfv <karencfv@users.noreply.github.com>
Date: Tue, 6 May 2025 13:06:31 +1200
Subject: [PATCH 08/11] use a virtual computed index instead of sign

---
 schema/crdb/caboose-sign-value/up02.sql |  3 ++-
 schema/crdb/caboose-sign-value/up03.sql |  3 +--
 schema/crdb/caboose-sign-value/up04.sql |  2 ++
 schema/crdb/dbinit.sql                  | 12 ++++++++++--
 4 files changed, 15 insertions(+), 5 deletions(-)
 create mode 100644 schema/crdb/caboose-sign-value/up04.sql

diff --git a/schema/crdb/caboose-sign-value/up02.sql b/schema/crdb/caboose-sign-value/up02.sql
index cccf0aa3427..c133efbab0b 100644
--- a/schema/crdb/caboose-sign-value/up02.sql
+++ b/schema/crdb/caboose-sign-value/up02.sql
@@ -1 +1,2 @@
-DROP INDEX IF EXISTS omicron.public.sw_caboose@caboose_properties;
+ALTER TABLE omicron.public.sw_caboose
+    ADD COLUMN IF NOT EXISTS sign_idx TEXT NOT NULL AS (IFNULL(sign, 'n/a')) VIRTUAL;
diff --git a/schema/crdb/caboose-sign-value/up03.sql b/schema/crdb/caboose-sign-value/up03.sql
index 99adfdde745..cccf0aa3427 100644
--- a/schema/crdb/caboose-sign-value/up03.sql
+++ b/schema/crdb/caboose-sign-value/up03.sql
@@ -1,2 +1 @@
-CREATE UNIQUE INDEX IF NOT EXISTS caboose_properties
-    on omicron.public.sw_caboose (board, git_commit, name, version, sign);
+DROP INDEX IF EXISTS omicron.public.sw_caboose@caboose_properties;
diff --git a/schema/crdb/caboose-sign-value/up04.sql b/schema/crdb/caboose-sign-value/up04.sql
new file mode 100644
index 00000000000..79d7109ce1c
--- /dev/null
+++ b/schema/crdb/caboose-sign-value/up04.sql
@@ -0,0 +1,2 @@
+CREATE UNIQUE INDEX IF NOT EXISTS caboose_properties
+    on omicron.public.sw_caboose (board, git_commit, name, version, sign_idx);
diff --git a/schema/crdb/dbinit.sql b/schema/crdb/dbinit.sql
index 664b69be2f2..948401d1d26 100644
--- a/schema/crdb/dbinit.sql
+++ b/schema/crdb/dbinit.sql
@@ -3348,10 +3348,18 @@ CREATE TABLE IF NOT EXISTS omicron.public.sw_caboose (
     git_commit TEXT NOT NULL,
     name TEXT NOT NULL,
     version TEXT NOT NULL,
-    sign TEXT -- nullable
+    sign TEXT, -- nullable
+    /* We cannot use `sign` as an index as it is a nullable value
+     * and nullable values are distinct by default. This means
+     * it's possible to insert rows that appear to be duplicates
+     * if one of the values is NULL. To enforce uniqueness, we
+     * create the index on a computed virtual column which assigns
+     * `n/a` as the value if `sign` is null.
+     */
+    sign_idx TEXT NOT NULL AS (IFNULL(sign, 'n/a')) VIRTUAL
 );
 CREATE UNIQUE INDEX IF NOT EXISTS caboose_properties
-    on omicron.public.sw_caboose (board, git_commit, name, version, sign);
+    on omicron.public.sw_caboose (board, git_commit, name, version, sign_idx);
 
 /* root of trust pages: this table assigns unique ids to distinct RoT CMPA
    and CFPA page contents, each of which is a 512-byte blob */

From a4e13e73f45d5edee1aed17a5f1addb07abeb4c0 Mon Sep 17 00:00:00 2001
From: karencfv <karencfv@users.noreply.github.com>
Date: Wed, 7 May 2025 13:19:43 +1200
Subject: [PATCH 09/11] Use partial indexes

---
 schema/crdb/caboose-sign-value/up02.sql |  3 +--
 schema/crdb/caboose-sign-value/up03.sql |  4 +++-
 schema/crdb/caboose-sign-value/up04.sql |  5 +++--
 schema/crdb/dbinit.sql                  | 24 ++++++++++++++----------
 4 files changed, 21 insertions(+), 15 deletions(-)

diff --git a/schema/crdb/caboose-sign-value/up02.sql b/schema/crdb/caboose-sign-value/up02.sql
index c133efbab0b..f7e4de54fbf 100644
--- a/schema/crdb/caboose-sign-value/up02.sql
+++ b/schema/crdb/caboose-sign-value/up02.sql
@@ -1,2 +1 @@
-ALTER TABLE omicron.public.sw_caboose
-    ADD COLUMN IF NOT EXISTS sign_idx TEXT NOT NULL AS (IFNULL(sign, 'n/a')) VIRTUAL;
+DROP INDEX IF EXISTS omicron.public.sw_caboose@caboose_properties;
\ No newline at end of file
diff --git a/schema/crdb/caboose-sign-value/up03.sql b/schema/crdb/caboose-sign-value/up03.sql
index cccf0aa3427..0e9df7702ef 100644
--- a/schema/crdb/caboose-sign-value/up03.sql
+++ b/schema/crdb/caboose-sign-value/up03.sql
@@ -1 +1,3 @@
-DROP INDEX IF EXISTS omicron.public.sw_caboose@caboose_properties;
+CREATE UNIQUE INDEX IF NOT EXISTS caboose_properties
+    on omicron.public.sw_caboose (board, git_commit, name, version, sign)
+    WHERE sign IS NOT NULL;
\ No newline at end of file
diff --git a/schema/crdb/caboose-sign-value/up04.sql b/schema/crdb/caboose-sign-value/up04.sql
index 79d7109ce1c..7b80fa40804 100644
--- a/schema/crdb/caboose-sign-value/up04.sql
+++ b/schema/crdb/caboose-sign-value/up04.sql
@@ -1,2 +1,3 @@
-CREATE UNIQUE INDEX IF NOT EXISTS caboose_properties
-    on omicron.public.sw_caboose (board, git_commit, name, version, sign_idx);
+CREATE UNIQUE INDEX IF NOT EXISTS caboose_properties_no_sign
+    on omicron.public.sw_caboose (board, git_commit, name, version)
+    WHERE sign IS NULL;
\ No newline at end of file
diff --git a/schema/crdb/dbinit.sql b/schema/crdb/dbinit.sql
index 94d2ee5d68e..05a59b50640 100644
--- a/schema/crdb/dbinit.sql
+++ b/schema/crdb/dbinit.sql
@@ -3376,18 +3376,22 @@ CREATE TABLE IF NOT EXISTS omicron.public.sw_caboose (
     git_commit TEXT NOT NULL,
     name TEXT NOT NULL,
     version TEXT NOT NULL,
-    sign TEXT, -- nullable
-    /* We cannot use `sign` as an index as it is a nullable value
-     * and nullable values are distinct by default. This means
-     * it's possible to insert rows that appear to be duplicates
-     * if one of the values is NULL. To enforce uniqueness, we
-     * create the index on a computed virtual column which assigns
-     * `n/a` as the value if `sign` is null.
-     */
-    sign_idx TEXT NOT NULL AS (IFNULL(sign, 'n/a')) VIRTUAL
+    sign TEXT -- nullable
 );
+
+/*
+ * We use two separate partial indexes to ensure uniqueness. 
+ * This is necessary because the sign column is NULLable, but in SQL, NULL values
+ * are considered distinct. That means that a non-partial index on all of these
+ * columns would allow duplicate rows where sign is NULL, which we don't want.
+ */
 CREATE UNIQUE INDEX IF NOT EXISTS caboose_properties
-    on omicron.public.sw_caboose (board, git_commit, name, version, sign_idx);
+    on omicron.public.sw_caboose (board, git_commit, name, version, sign)
+    WHERE sign IS NOT NULL;
+
+CREATE UNIQUE INDEX IF NOT EXISTS caboose_properties_no_sign
+    on omicron.public.sw_caboose (board, git_commit, name, version)
+    WHERE sign IS NULL;
 
 /* root of trust pages: this table assigns unique ids to distinct RoT CMPA
    and CFPA page contents, each of which is a 512-byte blob */

From 6c1202eca81f5e7f6abd47735aee423a45bc8288 Mon Sep 17 00:00:00 2001
From: karencfv <karencfv@users.noreply.github.com>
Date: Wed, 7 May 2025 16:10:24 +1200
Subject: [PATCH 10/11] Use a complete and a partial index

---
 schema/crdb/caboose-sign-value/up03.sql | 3 +--
 schema/crdb/dbinit.sql                  | 7 +++----
 2 files changed, 4 insertions(+), 6 deletions(-)

diff --git a/schema/crdb/caboose-sign-value/up03.sql b/schema/crdb/caboose-sign-value/up03.sql
index 0e9df7702ef..e323cb7f6fa 100644
--- a/schema/crdb/caboose-sign-value/up03.sql
+++ b/schema/crdb/caboose-sign-value/up03.sql
@@ -1,3 +1,2 @@
 CREATE UNIQUE INDEX IF NOT EXISTS caboose_properties
-    on omicron.public.sw_caboose (board, git_commit, name, version, sign)
-    WHERE sign IS NOT NULL;
\ No newline at end of file
+    on omicron.public.sw_caboose (board, git_commit, name, version, sign);
\ No newline at end of file
diff --git a/schema/crdb/dbinit.sql b/schema/crdb/dbinit.sql
index 05a59b50640..5cfd68577cb 100644
--- a/schema/crdb/dbinit.sql
+++ b/schema/crdb/dbinit.sql
@@ -3380,14 +3380,13 @@ CREATE TABLE IF NOT EXISTS omicron.public.sw_caboose (
 );
 
 /*
- * We use two separate partial indexes to ensure uniqueness. 
+ * We use a complete and a partial index to ensure uniqueness. 
  * This is necessary because the sign column is NULLable, but in SQL, NULL values
- * are considered distinct. That means that a non-partial index on all of these
+ * are considered distinct. That means that a single complete index on all of these
  * columns would allow duplicate rows where sign is NULL, which we don't want.
  */
 CREATE UNIQUE INDEX IF NOT EXISTS caboose_properties
-    on omicron.public.sw_caboose (board, git_commit, name, version, sign)
-    WHERE sign IS NOT NULL;
+    on omicron.public.sw_caboose (board, git_commit, name, version, sign);
 
 CREATE UNIQUE INDEX IF NOT EXISTS caboose_properties_no_sign
     on omicron.public.sw_caboose (board, git_commit, name, version)

From a581adc582ae7cd6f26604a9bcf39063f6fa6249 Mon Sep 17 00:00:00 2001
From: karencfv <karencfv@users.noreply.github.com>
Date: Thu, 8 May 2025 09:44:53 +1200
Subject: [PATCH 11/11] fix typo

---
 nexus/db-queries/src/db/datastore/inventory.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nexus/db-queries/src/db/datastore/inventory.rs b/nexus/db-queries/src/db/datastore/inventory.rs
index a4f9084405c..a64d2310f8b 100644
--- a/nexus/db-queries/src/db/datastore/inventory.rs
+++ b/nexus/db-queries/src/db/datastore/inventory.rs
@@ -555,7 +555,7 @@ impl DataStore {
             // - `hw_baseboard` with an "id" primary key and lookup columns
             //   "part_number" and "serial_number"
             // - `sw_caboose` with an "id" primary key and lookup columns
-            //   "board", "git_commit", "name", "version, and sign"
+            //   "board", "git_commit", "name", "version", and "sign"
             // - `inv_caboose` with foreign keys "hw_baseboard_id",
             //   "sw_caboose_id", and various other columns
             //