Merge "Add AutoNav Bounce Splines"
diff --git a/aos/BUILD b/aos/BUILD
index b87f7bc..535c0b2 100644
--- a/aos/BUILD
+++ b/aos/BUILD
@@ -595,3 +595,26 @@
     srcs = ["flatbuffers_static.py"],
     visibility = ["//visibility:public"],
 )
+
+cc_library(
+    name = "uuid",
+    srcs = ["uuid.cc"],
+    hdrs = ["uuid.h"],
+    target_compatible_with = ["@platforms//os:linux"],
+    visibility = ["//visibility:public"],
+    deps = [
+        "@com_github_google_flatbuffers//:flatbuffers",
+        "@com_github_google_glog//:glog",
+        "@com_google_absl//absl/types:span",
+    ],
+)
+
+cc_test(
+    name = "uuid_test",
+    srcs = ["uuid_test.cc"],
+    target_compatible_with = ["@platforms//os:linux"],
+    deps = [
+        ":uuid",
+        "//aos/testing:googletest",
+    ],
+)
diff --git a/aos/events/BUILD b/aos/events/BUILD
index a1a4c6e..1a72db9 100644
--- a/aos/events/BUILD
+++ b/aos/events/BUILD
@@ -83,7 +83,7 @@
         "//aos:configuration_fbs",
         "//aos:flatbuffers",
         "//aos:ftrace",
-        "//aos/events/logging:uuid",
+        "//aos:uuid",
         "//aos/ipc_lib:data_alignment",
         "//aos/logging:implementations",
         "//aos/time",
diff --git a/aos/events/event_loop.h b/aos/events/event_loop.h
index 5320e1e..22f7c6f 100644
--- a/aos/events/event_loop.h
+++ b/aos/events/event_loop.h
@@ -12,7 +12,6 @@
 #include "aos/events/channel_preallocated_allocator.h"
 #include "aos/events/event_loop_event.h"
 #include "aos/events/event_loop_generated.h"
-#include "aos/events/logging/uuid.h"
 #include "aos/events/timing_statistics.h"
 #include "aos/flatbuffers.h"
 #include "aos/ftrace.h"
@@ -20,6 +19,7 @@
 #include "aos/json_to_flatbuffer.h"
 #include "aos/time/time.h"
 #include "aos/util/phased_loop.h"
+#include "aos/uuid.h"
 
 #include "absl/container/btree_set.h"
 #include "flatbuffers/flatbuffers.h"
@@ -147,21 +147,18 @@
   // get the sent times instead.
   virtual void *data() = 0;
   virtual size_t size() = 0;
-  bool Send(size_t size,
-            aos::monotonic_clock::time_point monotonic_remote_time =
-                aos::monotonic_clock::min_time,
-            aos::realtime_clock::time_point realtime_remote_time =
-                aos::realtime_clock::min_time,
-            uint32_t remote_queue_index = 0xffffffffu);
+  bool Send(size_t size);
+  bool Send(size_t size, monotonic_clock::time_point monotonic_remote_time,
+            realtime_clock::time_point realtime_remote_time,
+            uint32_t remote_queue_index);
 
   // Sends a single block of data by copying it.
   // The remote arguments have the same meaning as in Send above.
+  bool Send(const void *data, size_t size);
   bool Send(const void *data, size_t size,
-            aos::monotonic_clock::time_point monotonic_remote_time =
-                aos::monotonic_clock::min_time,
-            aos::realtime_clock::time_point realtime_remote_time =
-                aos::realtime_clock::min_time,
-            uint32_t remote_queue_index = 0xffffffffu);
+            monotonic_clock::time_point monotonic_remote_time,
+            realtime_clock::time_point realtime_remote_time,
+            uint32_t remote_queue_index);
 
   const Channel *channel() const { return channel_; }
 
@@ -190,10 +187,8 @@
  protected:
   EventLoop *event_loop() { return event_loop_; }
 
-  aos::monotonic_clock::time_point monotonic_sent_time_ =
-      aos::monotonic_clock::min_time;
-  aos::realtime_clock::time_point realtime_sent_time_ =
-      aos::realtime_clock::min_time;
+  monotonic_clock::time_point monotonic_sent_time_ = monotonic_clock::min_time;
+  realtime_clock::time_point realtime_sent_time_ = realtime_clock::min_time;
   uint32_t sent_queue_index_ = 0xffffffff;
 
  private:
diff --git a/aos/events/event_loop_param_test.cc b/aos/events/event_loop_param_test.cc
index 79f326d..e0c6024 100644
--- a/aos/events/event_loop_param_test.cc
+++ b/aos/events/event_loop_param_test.cc
@@ -1848,6 +1848,7 @@
       aos::monotonic_clock::time_point(chrono::seconds(1501));
   const aos::realtime_clock::time_point realtime_remote_time =
       aos::realtime_clock::time_point(chrono::seconds(3132));
+  const uint32_t remote_queue_index = 0x254971;
 
   std::unique_ptr<aos::RawSender> sender =
       loop1->MakeRawSender(configuration::GetChannel(
@@ -1859,18 +1860,20 @@
 
   loop2->OnRun([&]() {
     EXPECT_TRUE(sender->Send(kData.data(), kData.size(), monotonic_remote_time,
-                             realtime_remote_time));
+                             realtime_remote_time, remote_queue_index));
   });
 
   bool happened = false;
   loop2->MakeRawWatcher(
       configuration::GetChannel(loop2->configuration(), "/test",
                                 "aos.TestMessage", "", nullptr),
-      [this, monotonic_remote_time, realtime_remote_time, &fetcher, &happened](
-          const Context &context, const void * /*message*/) {
+      [this, monotonic_remote_time, realtime_remote_time,
+       remote_queue_index, &fetcher,
+       &happened](const Context &context, const void * /*message*/) {
         happened = true;
         EXPECT_EQ(monotonic_remote_time, context.monotonic_remote_time);
         EXPECT_EQ(realtime_remote_time, context.realtime_remote_time);
+        EXPECT_EQ(remote_queue_index, context.remote_queue_index);
 
         ASSERT_TRUE(fetcher->Fetch());
         EXPECT_EQ(monotonic_remote_time,
diff --git a/aos/events/event_loop_tmpl.h b/aos/events/event_loop_tmpl.h
index 06b1643..74d3493 100644
--- a/aos/events/event_loop_tmpl.h
+++ b/aos/events/event_loop_tmpl.h
@@ -129,6 +129,11 @@
   return false;
 }
 
+inline bool RawSender::Send(size_t size) {
+  return Send(size, monotonic_clock::min_time, realtime_clock::min_time,
+              0xffffffffu);
+}
+
 inline bool RawSender::Send(
     size_t size, aos::monotonic_clock::time_point monotonic_remote_time,
     aos::realtime_clock::time_point realtime_remote_time,
@@ -147,6 +152,11 @@
   return false;
 }
 
+inline bool RawSender::Send(const void *data, size_t size) {
+  return Send(data, size, monotonic_clock::min_time, realtime_clock::min_time,
+              0xffffffffu);
+}
+
 inline bool RawSender::Send(
     const void *data, size_t size,
     aos::monotonic_clock::time_point monotonic_remote_time,
diff --git a/aos/events/logging/BUILD b/aos/events/logging/BUILD
index 586e932..0b6adeb 100644
--- a/aos/events/logging/BUILD
+++ b/aos/events/logging/BUILD
@@ -27,7 +27,7 @@
     deps = [
         ":buffer_encoder",
         ":logger_fbs",
-        ":uuid",
+        "//aos:uuid",
         "//aos:configuration",
         "//aos:flatbuffer_merge",
         "//aos:flatbuffers",
@@ -176,7 +176,7 @@
     deps = [
         ":logfile_utils",
         ":logger_fbs",
-        ":uuid",
+        "//aos:uuid",
         "@com_github_google_flatbuffers//:flatbuffers",
     ],
 )
@@ -214,7 +214,7 @@
         ":log_writer",
         ":logfile_utils",
         ":logger_fbs",
-        ":uuid",
+        "//aos:uuid",
         "//aos/events:event_loop",
         "//aos/events:simulated_event_loop",
         "//aos/network:message_bridge_server_fbs",
@@ -377,27 +377,6 @@
     ],
 )
 
-cc_library(
-    name = "uuid",
-    srcs = ["uuid.cc"],
-    hdrs = ["uuid.h"],
-    target_compatible_with = ["@platforms//os:linux"],
-    visibility = ["//visibility:public"],
-    deps = [
-        "@com_github_google_glog//:glog",
-    ],
-)
-
-cc_test(
-    name = "uuid_test",
-    srcs = ["uuid_test.cc"],
-    target_compatible_with = ["@platforms//os:linux"],
-    deps = [
-        ":uuid",
-        "//aos/testing:googletest",
-    ],
-)
-
 cc_test(
     name = "logfile_utils_test",
     srcs = ["logfile_utils_test.cc"],
diff --git a/aos/events/logging/log_namer.cc b/aos/events/logging/log_namer.cc
index 44e8f5a..a0625d0 100644
--- a/aos/events/logging/log_namer.cc
+++ b/aos/events/logging/log_namer.cc
@@ -9,7 +9,7 @@
 #include "absl/strings/str_cat.h"
 #include "aos/events/logging/logfile_utils.h"
 #include "aos/events/logging/logger_generated.h"
-#include "aos/events/logging/uuid.h"
+#include "aos/uuid.h"
 #include "flatbuffers/flatbuffers.h"
 #include "glog/logging.h"
 
@@ -20,11 +20,10 @@
     aos::SizePrefixedFlatbufferDetachedBuffer<LogFileHeader> *header,
     const UUID &uuid, int parts_index) const {
   header->mutable_message()->mutate_parts_index(parts_index);
-  CHECK_EQ(uuid.string_view().size(),
+  CHECK_EQ(UUID::kStringSize,
            header->mutable_message()->mutable_parts_uuid()->size());
-  std::copy(uuid.string_view().begin(), uuid.string_view().end(),
-            reinterpret_cast<char *>(
-                header->mutable_message()->mutable_parts_uuid()->Data()));
+  uuid.CopyTo(reinterpret_cast<char *>(
+      header->mutable_message()->mutable_parts_uuid()->Data()));
 }
 
 void LocalLogNamer::WriteHeader(
diff --git a/aos/events/logging/log_namer.h b/aos/events/logging/log_namer.h
index 2d4c23e..8f3f712 100644
--- a/aos/events/logging/log_namer.h
+++ b/aos/events/logging/log_namer.h
@@ -9,7 +9,7 @@
 
 #include "aos/events/logging/logfile_utils.h"
 #include "aos/events/logging/logger_generated.h"
-#include "aos/events/logging/uuid.h"
+#include "aos/uuid.h"
 #include "flatbuffers/flatbuffers.h"
 
 namespace aos {
diff --git a/aos/events/logging/log_reader.cc b/aos/events/logging/log_reader.cc
index 6e92568..07b423f 100644
--- a/aos/events/logging/log_reader.cc
+++ b/aos/events/logging/log_reader.cc
@@ -13,7 +13,6 @@
 #include "aos/events/event_loop.h"
 #include "aos/events/logging/logfile_sorting.h"
 #include "aos/events/logging/logger_generated.h"
-#include "aos/events/logging/uuid.h"
 #include "aos/flatbuffer_merge.h"
 #include "aos/network/multinode_timestamp_filter.h"
 #include "aos/network/remote_message_generated.h"
@@ -22,6 +21,7 @@
 #include "aos/network/timestamp_channel.h"
 #include "aos/time/time.h"
 #include "aos/util/file.h"
+#include "aos/uuid.h"
 #include "flatbuffers/flatbuffers.h"
 #include "openssl/sha.h"
 
@@ -1172,7 +1172,7 @@
     flatbuffers::FlatBufferBuilder fbb;
     fbb.ForceDefaults(true);
     flatbuffers::Offset<flatbuffers::String> boot_uuid_offset =
-        fbb.CreateString(event_loop_->boot_uuid().string_view());
+        event_loop_->boot_uuid().PackString(&fbb);
 
     RemoteMessage::Builder message_header_builder(fbb);
 
diff --git a/aos/events/logging/log_reader.h b/aos/events/logging/log_reader.h
index 24ac5c9..37163c1 100644
--- a/aos/events/logging/log_reader.h
+++ b/aos/events/logging/log_reader.h
@@ -11,13 +11,13 @@
 #include "aos/events/logging/logfile_sorting.h"
 #include "aos/events/logging/logfile_utils.h"
 #include "aos/events/logging/logger_generated.h"
-#include "aos/events/logging/uuid.h"
 #include "aos/events/simulated_event_loop.h"
 #include "aos/network/message_bridge_server_generated.h"
 #include "aos/network/multinode_timestamp_filter.h"
 #include "aos/network/remote_message_generated.h"
 #include "aos/network/timestamp_filter.h"
 #include "aos/time/time.h"
+#include "aos/uuid.h"
 #include "flatbuffers/flatbuffers.h"
 
 namespace aos {
diff --git a/aos/events/logging/log_writer.cc b/aos/events/logging/log_writer.cc
index 96995e9..400ad8d 100644
--- a/aos/events/logging/log_writer.cc
+++ b/aos/events/logging/log_writer.cc
@@ -432,7 +432,7 @@
     // There are no offsets to compute for ourself, so always succeed.
     SetStartTime(node_index, monotonic_start_time, realtime_start_time,
                  monotonic_start_time, realtime_start_time);
-    node_state_[node_index].SetBootUUID(event_loop_->boot_uuid().string_view());
+    node_state_[node_index].SetBootUUID(event_loop_->boot_uuid());
     return true;
   } else if (server_statistics_fetcher_.get() != nullptr) {
     // We must be a remote node now.  Look for the connection and see if it is
@@ -500,10 +500,10 @@
 
   CHECK(log_event_uuid_ != UUID::Zero());
   const flatbuffers::Offset<flatbuffers::String> log_event_uuid_offset =
-      fbb.CreateString(log_event_uuid_.string_view());
+      log_event_uuid_.PackString(&fbb);
 
   const flatbuffers::Offset<flatbuffers::String> logger_instance_uuid_offset =
-      fbb.CreateString(logger_instance_uuid_.string_view());
+      logger_instance_uuid_.PackString(&fbb);
 
   flatbuffers::Offset<flatbuffers::String> log_start_uuid_offset;
   if (!log_start_uuid_.empty()) {
@@ -516,10 +516,10 @@
   }
 
   const flatbuffers::Offset<flatbuffers::String> logger_node_boot_uuid_offset =
-      fbb.CreateString(event_loop_->boot_uuid().string_view());
+      event_loop_->boot_uuid().PackString(&fbb);
 
   const flatbuffers::Offset<flatbuffers::String> source_node_boot_uuid_offset =
-      fbb.CreateString(event_loop_->boot_uuid().string_view());
+      event_loop_->boot_uuid().PackString(&fbb);
 
   const flatbuffers::Offset<flatbuffers::String> parts_uuid_offset =
       fbb.CreateString("00000000-0000-4000-8000-000000000000");
diff --git a/aos/events/logging/log_writer.h b/aos/events/logging/log_writer.h
index 4c7d39f..0e88b4f 100644
--- a/aos/events/logging/log_writer.h
+++ b/aos/events/logging/log_writer.h
@@ -9,11 +9,11 @@
 #include "aos/events/logging/log_namer.h"
 #include "aos/events/logging/logfile_utils.h"
 #include "aos/events/logging/logger_generated.h"
-#include "aos/events/logging/uuid.h"
 #include "aos/events/simulated_event_loop.h"
 #include "aos/network/message_bridge_server_generated.h"
 #include "aos/network/remote_message_generated.h"
 #include "aos/time/time.h"
+#include "aos/uuid.h"
 #include "flatbuffers/flatbuffers.h"
 
 namespace aos {
@@ -209,6 +209,18 @@
     bool header_valid = false;
 
     // Sets the source_node_boot_uuid, properly updating everything.
+    void SetBootUUID(const UUID &new_source_node_boot_uuid) {
+      new_source_node_boot_uuid.CopyTo(source_node_boot_uuid.data());
+      header_valid = false;
+      has_source_node_boot_uuid = true;
+
+      flatbuffers::String *source_node_boot_uuid_string =
+          log_file_header.mutable_message()->mutable_source_node_boot_uuid();
+      CHECK_EQ(source_node_boot_uuid.size(),
+               source_node_boot_uuid_string->size());
+      memcpy(source_node_boot_uuid_string->data(), source_node_boot_uuid.data(),
+             source_node_boot_uuid.size());
+    }
     void SetBootUUID(std::string_view new_source_node_boot_uuid) {
       source_node_boot_uuid = new_source_node_boot_uuid;
       header_valid = false;
diff --git a/aos/events/logging/logfile_sorting.h b/aos/events/logging/logfile_sorting.h
index f479d62..95a6431 100644
--- a/aos/events/logging/logfile_sorting.h
+++ b/aos/events/logging/logfile_sorting.h
@@ -6,8 +6,8 @@
 #include <vector>
 
 #include "aos/configuration.h"
-#include "aos/events/logging/uuid.h"
 #include "aos/time/time.h"
+#include "aos/uuid.h"
 
 namespace aos {
 namespace logger {
diff --git a/aos/events/logging/logger_test.cc b/aos/events/logging/logger_test.cc
index cf8a373..6ffa16c 100644
--- a/aos/events/logging/logger_test.cc
+++ b/aos/events/logging/logger_test.cc
@@ -1901,7 +1901,7 @@
 
           ASSERT_TRUE(header.has_boot_uuid());
           EXPECT_EQ(header.boot_uuid()->string_view(),
-                    pi2_event_loop->boot_uuid().string_view());
+                    pi2_event_loop->boot_uuid().ToString());
 
           EXPECT_EQ(pi1_context->queue_index, header.remote_queue_index());
           EXPECT_EQ(pi2_context->remote_queue_index,
@@ -1981,7 +1981,7 @@
 
           ASSERT_TRUE(header.has_boot_uuid());
           EXPECT_EQ(header.boot_uuid()->string_view(),
-                    pi1_event_loop->boot_uuid().string_view());
+                    pi1_event_loop->boot_uuid().ToString());
 
           EXPECT_EQ(pi2_context->queue_index, header.remote_queue_index());
           EXPECT_EQ(pi1_context->remote_queue_index,
@@ -2090,7 +2090,7 @@
   {
     pi2_boot1 = event_loop_factory_.GetNodeEventLoopFactory(pi2_)
                     ->boot_uuid()
-                    .string_view();
+                    .ToString();
     LoggerState pi1_logger = MakeLogger(pi1_);
 
     event_loop_factory_.RunFor(chrono::milliseconds(95));
@@ -2103,7 +2103,7 @@
 
     pi2_boot2 = event_loop_factory_.GetNodeEventLoopFactory(pi2_)
                     ->boot_uuid()
-                    .string_view();
+                    .ToString();
 
     event_loop_factory_.RunFor(chrono::milliseconds(20000));
   }
diff --git a/aos/events/logging/uuid.cc b/aos/events/logging/uuid.cc
deleted file mode 100644
index 3914740..0000000
--- a/aos/events/logging/uuid.cc
+++ /dev/null
@@ -1,86 +0,0 @@
-#include "aos/events/logging/uuid.h"
-
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <array>
-#include <random>
-#include <string_view>
-
-#include "glog/logging.h"
-
-namespace aos {
-namespace {
-char ToHex(int val) {
-  if (val < 10) {
-    return val + '0';
-  } else {
-    return val - 10 + 'a';
-  }
-}
-}  // namespace
-
-UUID UUID::Random() {
-  std::random_device rd;
-  std::mt19937 gen(rd());
-
-  std::uniform_int_distribution<> dis(0, 15);
-  std::uniform_int_distribution<> dis2(8, 11);
-
-  UUID result;
-
-  // UUID4 is implemented per https://www.cryptosys.net/pki/uuid-rfc4122.html
-  int i;
-  for (i = 0; i < 8; i++) {
-    result.data_[i] = ToHex(dis(gen));
-  }
-  result.data_[i] = '-';
-  ++i;
-  for (; i < 13; i++) {
-    result.data_[i] = ToHex(dis(gen));
-  }
-  result.data_[i] = '-';
-  ++i;
-  result.data_[i] = '4';
-  ++i;
-  for (; i < 18; i++) {
-    result.data_[i] = ToHex(dis(gen));
-  }
-  result.data_[i] = '-';
-  ++i;
-  result.data_[i] = ToHex(dis2(gen));
-  ++i;
-  for (; i < 23; i++) {
-    result.data_[i] = ToHex(dis(gen));
-  }
-  result.data_[i] = '-';
-  ++i;
-  for (; i < 36; i++) {
-    result.data_[i] = ToHex(dis(gen));
-  }
-
-  return result;
-}
-
-UUID UUID::Zero() { return FromString("00000000-0000-0000-0000-000000000000"); }
-
-UUID UUID::FromString(std::string_view str) {
-  UUID result;
-  CHECK_EQ(str.size(), kSize);
-
-  std::copy(str.begin(), str.end(), result.data_.begin());
-  return result;
-}
-
-UUID UUID::BootUUID() {
-  int fd = open("/proc/sys/kernel/random/boot_id", O_RDONLY);
-  PCHECK(fd != -1);
-
-  UUID result;
-  CHECK_EQ(static_cast<ssize_t>(kSize), read(fd, result.data_.begin(), kSize));
-  close(fd);
-
-  return result;
-}
-
-}  // namespace aos
diff --git a/aos/events/logging/uuid.h b/aos/events/logging/uuid.h
deleted file mode 100644
index d7ec33a..0000000
--- a/aos/events/logging/uuid.h
+++ /dev/null
@@ -1,46 +0,0 @@
-#ifndef AOS_EVENTS_LOGGING_UUID_H_
-#define AOS_EVENTS_LOGGING_UUID_H_
-
-#include <array>
-#include <random>
-#include <string_view>
-
-namespace aos {
-
-// Class to generate and hold a UUID.
-class UUID {
- public:
-  // Returns a randomly generated UUID.  This is known as a UUID4.
-  static UUID Random();
-
-  // Returns a uuid with all '0' characters.
-  static UUID Zero();
-
-  static UUID FromString(std::string_view);
-
-  static UUID BootUUID();
-
-  // Size of a UUID.
-  static constexpr size_t kSize = 36;
-
-  std::string_view string_view() const {
-    return std::string_view(data_.data(), data_.size());
-  }
-
-  bool operator==(const UUID &other) const {
-    return other.string_view() == string_view();
-  }
-  bool operator!=(const UUID &other) const {
-    return other.string_view() != string_view();
-  }
-
- private:
-  UUID() {}
-
-  // Fixed size storage for the data.  Non-null terminated.
-  std::array<char, kSize> data_;
-};
-
-}  // namespace aos
-
-#endif  // AOS_EVENTS_LOGGING_UUID_H_
diff --git a/aos/events/logging/uuid_test.cc b/aos/events/logging/uuid_test.cc
deleted file mode 100644
index 4ea351c..0000000
--- a/aos/events/logging/uuid_test.cc
+++ /dev/null
@@ -1,20 +0,0 @@
-#include "aos/events/logging/uuid.h"
-
-#include "glog/logging.h"
-#include "gtest/gtest.h"
-
-namespace aos {
-namespace testing {
-
-// Tests that random UUIDs are actually random, and we can convert them to a
-// string.  Not very exhaustive, but it is a good smoke test.
-TEST(UUIDTest, GetOne) {
-  LOG(INFO) << UUID::Random().string_view();
-
-  EXPECT_NE(UUID::Random(), UUID::Random());
-  EXPECT_NE(UUID::Random(), UUID::Zero());
-  EXPECT_EQ(UUID::Zero(), UUID::Zero());
-}
-
-}  // namespace testing
-}  // namespace aos
diff --git a/aos/events/simulated_event_loop.h b/aos/events/simulated_event_loop.h
index 3ef7c96..348e7dd 100644
--- a/aos/events/simulated_event_loop.h
+++ b/aos/events/simulated_event_loop.h
@@ -13,11 +13,11 @@
 #include "absl/container/btree_map.h"
 #include "aos/events/event_loop.h"
 #include "aos/events/event_scheduler.h"
-#include "aos/events/logging/uuid.h"
 #include "aos/events/simple_channel.h"
 #include "aos/flatbuffer_merge.h"
 #include "aos/flatbuffers.h"
 #include "aos/ipc_lib/index.h"
+#include "aos/uuid.h"
 #include "glog/logging.h"
 
 namespace aos {
diff --git a/aos/events/simulated_event_loop_test.cc b/aos/events/simulated_event_loop_test.cc
index 43833fd..60731c1 100644
--- a/aos/events/simulated_event_loop_test.cc
+++ b/aos/events/simulated_event_loop_test.cc
@@ -653,7 +653,7 @@
           EXPECT_EQ(header.boot_uuid()->string_view(),
                     simulated_event_loop_factory.GetNodeEventLoopFactory(pi2)
                         ->boot_uuid()
-                        .string_view());
+                        .ToString());
 
           const aos::monotonic_clock::time_point header_monotonic_sent_time(
               chrono::nanoseconds(header.monotonic_sent_time()));
@@ -1433,7 +1433,7 @@
         EXPECT_EQ(header.boot_uuid()->string_view(),
                   simulated_event_loop_factory.GetNodeEventLoopFactory(pi2)
                       ->boot_uuid()
-                      .string_view());
+                      .ToString());
         VLOG(1) << aos::FlatbufferToJson(&header);
         if (header.channel_index() == reliable_channel_index) {
           ++reliable_timestamp_count;
@@ -1488,7 +1488,7 @@
   std::string expected_boot_uuid(
       simulated_event_loop_factory.GetNodeEventLoopFactory(pi2)
           ->boot_uuid()
-          .string_view());
+          .ToString());
 
   int timestamp_count = 0;
   pi1_remote_timestamp->MakeWatcher(
@@ -1530,12 +1530,12 @@
   EXPECT_NE(expected_boot_uuid,
             simulated_event_loop_factory.GetNodeEventLoopFactory(pi2)
                 ->boot_uuid()
-                .string_view());
+                .ToString());
 
   expected_boot_uuid =
       simulated_event_loop_factory.GetNodeEventLoopFactory(pi2)
           ->boot_uuid()
-          .string_view();
+          .ToString();
   timestamp_count = 0;
   pi1_server_statistics_count = 0;
 
diff --git a/aos/events/simulated_network_bridge.cc b/aos/events/simulated_network_bridge.cc
index 8b29b23..e5eb4cb 100644
--- a/aos/events/simulated_network_bridge.cc
+++ b/aos/events/simulated_network_bridge.cc
@@ -148,20 +148,18 @@
 
     if (timestamp_logger_) {
       flatbuffers::FlatBufferBuilder fbb;
-        fbb.ForceDefaults(true);
+      fbb.ForceDefaults(true);
       // Reset the filter every time the UUID changes.  There's probably a more
       // clever way to do this, but that means a better concept of rebooting.
       if (server_status_->BootUUID(destination_node_index_) !=
-          send_node_factory_->boot_uuid().string_view()) {
+          send_node_factory_->boot_uuid()) {
         server_status_->ResetFilter(destination_node_index_);
-        server_status_->SetBootUUID(
-            destination_node_index_,
-            send_node_factory_->boot_uuid().string_view());
+        server_status_->SetBootUUID(destination_node_index_,
+                                    send_node_factory_->boot_uuid());
       }
 
       flatbuffers::Offset<flatbuffers::String> boot_uuid_offset =
-          fbb.CreateString(
-              send_node_factory_->boot_uuid().string_view());
+          send_node_factory_->boot_uuid().PackString(&fbb);
 
       RemoteMessage::Builder message_header_builder(fbb);
 
@@ -343,8 +341,7 @@
         auto client_event_loop = event_loop_map_.find(client_node);
         it->second.server_status.ResetFilter(node_index);
         it->second.server_status.SetBootUUID(
-            node_index,
-            client_event_loop->second.event_loop->boot_uuid().string_view());
+            node_index, client_event_loop->second.event_loop->boot_uuid());
       }
       ++node_index;
     }
diff --git a/aos/ipc_lib/lockless_queue.cc b/aos/ipc_lib/lockless_queue.cc
index 2b704ef..a0c68cb 100644
--- a/aos/ipc_lib/lockless_queue.cc
+++ b/aos/ipc_lib/lockless_queue.cc
@@ -906,29 +906,27 @@
 
 bool LocklessQueueSender::Send(
     const char *data, size_t length,
-    aos::monotonic_clock::time_point monotonic_remote_time,
-    aos::realtime_clock::time_point realtime_remote_time,
+    monotonic_clock::time_point monotonic_remote_time,
+    realtime_clock::time_point realtime_remote_time,
     uint32_t remote_queue_index,
-    aos::monotonic_clock::time_point *monotonic_sent_time,
-    aos::realtime_clock::time_point *realtime_sent_time,
-    uint32_t *queue_index) {
+    monotonic_clock::time_point *monotonic_sent_time,
+    realtime_clock::time_point *realtime_sent_time, uint32_t *queue_index) {
   CHECK_LE(length, size());
   // Flatbuffers write from the back of the buffer to the front.  If we are
   // going to write an explicit chunk of memory into the buffer, we need to
   // adhere to this convention and place it at the end.
   memcpy((reinterpret_cast<char *>(Data()) + size() - length), data, length);
   return Send(length, monotonic_remote_time, realtime_remote_time,
-              remote_queue_index, monotonic_sent_time, realtime_sent_time,
-              queue_index);
+              remote_queue_index, monotonic_sent_time,
+              realtime_sent_time, queue_index);
 }
 
 bool LocklessQueueSender::Send(
-    size_t length, aos::monotonic_clock::time_point monotonic_remote_time,
-    aos::realtime_clock::time_point realtime_remote_time,
+    size_t length, monotonic_clock::time_point monotonic_remote_time,
+    realtime_clock::time_point realtime_remote_time,
     uint32_t remote_queue_index,
-    aos::monotonic_clock::time_point *monotonic_sent_time,
-    aos::realtime_clock::time_point *realtime_sent_time,
-    uint32_t *queue_index) {
+    monotonic_clock::time_point *monotonic_sent_time,
+    realtime_clock::time_point *realtime_sent_time, uint32_t *queue_index) {
   const size_t queue_size = memory_->queue_size();
   CHECK_LE(length, size());
 
@@ -1207,11 +1205,12 @@
 
 LocklessQueueReader::Result LocklessQueueReader::Read(
     uint32_t uint32_queue_index,
-    ::aos::monotonic_clock::time_point *monotonic_sent_time,
-    ::aos::realtime_clock::time_point *realtime_sent_time,
-    ::aos::monotonic_clock::time_point *monotonic_remote_time,
-    ::aos::realtime_clock::time_point *realtime_remote_time,
-    uint32_t *remote_queue_index, size_t *length, char *data) const {
+    monotonic_clock::time_point *monotonic_sent_time,
+    realtime_clock::time_point *realtime_sent_time,
+    monotonic_clock::time_point *monotonic_remote_time,
+    realtime_clock::time_point *realtime_remote_time,
+    uint32_t *remote_queue_index, size_t *length,
+    char *data) const {
   const size_t queue_size = memory_->queue_size();
 
   // Build up the QueueIndex.
diff --git a/aos/ipc_lib/lockless_queue.h b/aos/ipc_lib/lockless_queue.h
index a5de6cc..69207f4 100644
--- a/aos/ipc_lib/lockless_queue.h
+++ b/aos/ipc_lib/lockless_queue.h
@@ -82,12 +82,12 @@
     // Timestamp of the message.  Needs to be monotonically incrementing in the
     // queue, which means that time needs to be re-sampled every time a write
     // fails.
-    ::aos::monotonic_clock::time_point monotonic_sent_time;
-    ::aos::realtime_clock::time_point realtime_sent_time;
+    monotonic_clock::time_point monotonic_sent_time;
+    realtime_clock::time_point realtime_sent_time;
     // Timestamps of the message from the remote node.  These are transparently
     // passed through.
-    ::aos::monotonic_clock::time_point monotonic_remote_time;
-    ::aos::realtime_clock::time_point realtime_remote_time;
+    monotonic_clock::time_point monotonic_remote_time;
+    realtime_clock::time_point realtime_remote_time;
 
     // Queue index from the remote node.
     uint32_t remote_queue_index;
@@ -303,26 +303,21 @@
   // Note: calls to Data() are expensive enough that you should cache it.
   size_t size() const;
   void *Data();
-  bool Send(size_t length,
-            aos::monotonic_clock::time_point monotonic_remote_time =
-                aos::monotonic_clock::min_time,
-            aos::realtime_clock::time_point realtime_remote_time =
-                aos::realtime_clock::min_time,
-            uint32_t remote_queue_index = 0xffffffff,
-            aos::monotonic_clock::time_point *monotonic_sent_time = nullptr,
-            aos::realtime_clock::time_point *realtime_sent_time = nullptr,
+  bool Send(size_t length, monotonic_clock::time_point monotonic_remote_time,
+            realtime_clock::time_point realtime_remote_time,
+            uint32_t remote_queue_index,
+            monotonic_clock::time_point *monotonic_sent_time = nullptr,
+            realtime_clock::time_point *realtime_sent_time = nullptr,
             uint32_t *queue_index = nullptr);
 
   // Sends up to length data.  Does not wakeup the target.
   bool Send(const char *data, size_t length,
-            aos::monotonic_clock::time_point monotonic_remote_time =
-                aos::monotonic_clock::min_time,
-            aos::realtime_clock::time_point realtime_remote_time =
-                aos::realtime_clock::min_time,
-            uint32_t remote_queue_index = 0xffffffff,
-            aos::monotonic_clock::time_point *monotonic_sent_time = nullptr,
-            aos::realtime_clock::time_point *realtime_sent_time = nullptr,
-            uint32_t *queue_index = nullptr);
+            monotonic_clock::time_point monotonic_remote_time,
+            realtime_clock::time_point realtime_remote_time,
+            uint32_t remote_queue_index,
+            monotonic_clock::time_point *monotonic_sent_time,
+            realtime_clock::time_point *realtime_sent_time,
+            uint32_t *queue_index);
 
   int buffer_index() const;
 
@@ -401,11 +396,12 @@
   //
   // data may be nullptr to indicate the data should not be copied.
   Result Read(uint32_t queue_index,
-              ::aos::monotonic_clock::time_point *monotonic_sent_time,
-              ::aos::realtime_clock::time_point *realtime_sent_time,
-              ::aos::monotonic_clock::time_point *monotonic_remote_time,
-              ::aos::realtime_clock::time_point *realtime_remote_time,
-              uint32_t *remote_queue_index, size_t *length, char *data) const;
+              monotonic_clock::time_point *monotonic_sent_time,
+              realtime_clock::time_point *realtime_sent_time,
+              monotonic_clock::time_point *monotonic_remote_time,
+              realtime_clock::time_point *realtime_remote_time,
+              uint32_t *remote_queue_index,
+              size_t *length, char *data) const;
 
   // Returns the index to the latest queue message.  Returns empty_queue_index()
   // if there are no messages in the queue.  Do note that this index wraps if
diff --git a/aos/ipc_lib/lockless_queue_death_test.cc b/aos/ipc_lib/lockless_queue_death_test.cc
index b4bb66a..346f88e 100644
--- a/aos/ipc_lib/lockless_queue_death_test.cc
+++ b/aos/ipc_lib/lockless_queue_death_test.cc
@@ -533,7 +533,9 @@
         for (int i = 0; i < 5; ++i) {
           char data[100];
           size_t s = snprintf(data, sizeof(data), "foobar%d", i + 1);
-          sender.Send(data, s + 1);
+          sender.Send(data, s + 1, monotonic_clock::min_time,
+                      realtime_clock::min_time, 0xffffffffl,
+                      nullptr, nullptr, nullptr);
           // Pin a message, so when we keep writing we will exercise the pinning
           // logic.
           if (i == 1) {
@@ -605,7 +607,9 @@
           // Send a message to make sure that the queue still works.
           char data[100];
           size_t s = snprintf(data, sizeof(data), "foobar%d", 971);
-          sender.Send(data, s + 1);
+          sender.Send(data, s + 1, monotonic_clock::min_time,
+                      realtime_clock::min_time, 0xffffffffl,
+                      nullptr, nullptr, nullptr);
         }
 
         // Now loop through the queue and make sure the number in the snprintf
@@ -613,10 +617,10 @@
         char last_data = '0';
         int i = 0;
         while (true) {
-          ::aos::monotonic_clock::time_point monotonic_sent_time;
-          ::aos::realtime_clock::time_point realtime_sent_time;
-          ::aos::monotonic_clock::time_point monotonic_remote_time;
-          ::aos::realtime_clock::time_point realtime_remote_time;
+          monotonic_clock::time_point monotonic_sent_time;
+          realtime_clock::time_point realtime_sent_time;
+          monotonic_clock::time_point monotonic_remote_time;
+          realtime_clock::time_point realtime_remote_time;
           uint32_t remote_queue_index;
           char read_data[1024];
           size_t length;
diff --git a/aos/ipc_lib/lockless_queue_test.cc b/aos/ipc_lib/lockless_queue_test.cc
index a90b976..c8f3d23 100644
--- a/aos/ipc_lib/lockless_queue_test.cc
+++ b/aos/ipc_lib/lockless_queue_test.cc
@@ -238,17 +238,18 @@
     // Send a trivial piece of data.
     char data[100];
     size_t s = snprintf(data, sizeof(data), "foobar%d", i);
-    sender.Send(data, s);
+    sender.Send(data, s, monotonic_clock::min_time, realtime_clock::min_time,
+                0xffffffffu, nullptr, nullptr, nullptr);
 
     // Confirm that the queue index still makes sense.  This is easier since the
     // empty case has been handled.
     EXPECT_EQ(reader.LatestIndex().index(), i);
 
     // Read a result from 5 in the past.
-    ::aos::monotonic_clock::time_point monotonic_sent_time;
-    ::aos::realtime_clock::time_point realtime_sent_time;
-    ::aos::monotonic_clock::time_point monotonic_remote_time;
-    ::aos::realtime_clock::time_point realtime_remote_time;
+    monotonic_clock::time_point monotonic_sent_time;
+    realtime_clock::time_point realtime_sent_time;
+    monotonic_clock::time_point monotonic_remote_time;
+    realtime_clock::time_point realtime_remote_time;
     uint32_t remote_queue_index;
     char read_data[1024];
     size_t length;
diff --git a/aos/ipc_lib/queue_racer.cc b/aos/ipc_lib/queue_racer.cc
index 5b3d88b..afd4135 100644
--- a/aos/ipc_lib/queue_racer.cc
+++ b/aos/ipc_lib/queue_racer.cc
@@ -177,7 +177,9 @@
         }
 
         ++started_writes_;
-        sender.Send(sizeof(ThreadPlusCount));
+        sender.Send(sizeof(ThreadPlusCount), aos::monotonic_clock::min_time,
+                    aos::realtime_clock::min_time, 0xffffffff,
+                    nullptr, nullptr, nullptr);
         // Blank out the new scratch buffer, to catch other people using it.
         {
           char *const new_data = static_cast<char *>(sender.Data()) +
@@ -261,10 +263,10 @@
 
   for (uint64_t i = initial_i;
        i < (1 + write_wrap_count) * num_messages_ * num_threads_; ++i) {
-    ::aos::monotonic_clock::time_point monotonic_sent_time;
-    ::aos::realtime_clock::time_point realtime_sent_time;
-    ::aos::monotonic_clock::time_point monotonic_remote_time;
-    ::aos::realtime_clock::time_point realtime_remote_time;
+    monotonic_clock::time_point monotonic_sent_time;
+    realtime_clock::time_point realtime_sent_time;
+    monotonic_clock::time_point monotonic_remote_time;
+    realtime_clock::time_point realtime_remote_time;
     uint32_t remote_queue_index;
     size_t length;
     char read_data[1024];
diff --git a/aos/network/BUILD b/aos/network/BUILD
index 1d4b634..42e115b 100644
--- a/aos/network/BUILD
+++ b/aos/network/BUILD
@@ -37,6 +37,13 @@
 )
 
 flatbuffer_cc_library(
+    name = "remote_data_fbs",
+    srcs = ["remote_data.fbs"],
+    gen_reflections = 1,
+    target_compatible_with = ["@platforms//os:linux"],
+)
+
+flatbuffer_cc_library(
     name = "timestamp_fbs",
     srcs = ["timestamp.fbs"],
     gen_reflections = 1,
@@ -148,6 +155,7 @@
         "//aos:configuration",
         "//aos:flatbuffer_merge",
         "//aos:flatbuffers",
+        "//aos:uuid",
         "@com_github_google_flatbuffers//:flatbuffers",
     ],
 )
@@ -205,6 +213,7 @@
         ":message_bridge_protocol",
         ":message_bridge_server_fbs",
         ":message_bridge_server_status",
+        ":remote_data_fbs",
         ":remote_message_fbs",
         ":sctp_lib",
         ":sctp_server",
@@ -286,6 +295,7 @@
         ":message_bridge_client_status",
         ":message_bridge_protocol",
         ":message_bridge_server_fbs",
+        ":remote_data_fbs",
         ":remote_message_fbs",
         ":sctp_client",
         ":timestamp_fbs",
diff --git a/aos/network/message_bridge_client_lib.cc b/aos/network/message_bridge_client_lib.cc
index 8a0ec34..bde8480 100644
--- a/aos/network/message_bridge_client_lib.cc
+++ b/aos/network/message_bridge_client_lib.cc
@@ -8,6 +8,7 @@
 #include "aos/network/connect_generated.h"
 #include "aos/network/message_bridge_client_generated.h"
 #include "aos/network/message_bridge_protocol.h"
+#include "aos/network/remote_data_generated.h"
 #include "aos/network/sctp_client.h"
 #include "aos/network/timestamp_generated.h"
 #include "aos/unique_malloc_ptr.h"
@@ -98,9 +99,9 @@
     std::vector<SctpClientChannelState> *channels, int client_index,
     MessageBridgeClientStatus *client_status)
     : event_loop_(event_loop),
-      connect_message_(
-          MakeConnectMessage(event_loop->configuration(), my_node, remote_name,
-                             event_loop->boot_uuid().string_view())),
+      connect_message_(MakeConnectMessage(event_loop->configuration(), my_node,
+                                          remote_name,
+                                          event_loop->boot_uuid())),
       message_reception_reply_(MakeMessageHeaderReply()),
       remote_node_(CHECK_NOTNULL(
           configuration::GetNode(event_loop->configuration(), remote_name))),
@@ -218,8 +219,8 @@
 }
 
 void SctpClientConnection::HandleData(const Message *message) {
-  const logger::MessageHeader *message_header =
-      flatbuffers::GetSizePrefixedRoot<logger::MessageHeader>(message->data());
+  const RemoteData *remote_data =
+      flatbuffers::GetSizePrefixedRoot<RemoteData>(message->data());
 
   VLOG(1) << "Got a message of size " << message->size;
   CHECK_EQ(message->size, flatbuffers::GetPrefixedSize(message->data()) +
@@ -228,9 +229,9 @@
   const int stream = message->header.rcvinfo.rcv_sid - kControlStreams();
   SctpClientChannelState *channel_state = &((*channels_)[stream_to_channel_[stream]]);
 
-  if (message_header->queue_index() == channel_state->last_queue_index &&
-      aos::monotonic_clock::time_point(
-          chrono::nanoseconds(message_header->monotonic_sent_time())) ==
+  if (remote_data->queue_index() == channel_state->last_queue_index &&
+      monotonic_clock::time_point(
+          chrono::nanoseconds(remote_data->monotonic_sent_time())) ==
           channel_state->last_timestamp) {
     LOG(INFO) << "Duplicate message from " << message->PeerAddress();
     connection_->mutate_duplicate_packets(connection_->duplicate_packets() + 1);
@@ -238,23 +239,23 @@
   } else {
     connection_->mutate_received_packets(connection_->received_packets() + 1);
 
-    channel_state->last_queue_index = message_header->queue_index();
-    channel_state->last_timestamp = aos::monotonic_clock::time_point(
-        chrono::nanoseconds(message_header->monotonic_sent_time()));
+    channel_state->last_queue_index = remote_data->queue_index();
+    channel_state->last_timestamp = monotonic_clock::time_point(
+        chrono::nanoseconds(remote_data->monotonic_sent_time()));
 
     // Publish the message.
     RawSender *sender = channel_state->sender.get();
-    sender->Send(message_header->data()->data(), message_header->data()->size(),
-                 aos::monotonic_clock::time_point(chrono::nanoseconds(
-                     message_header->monotonic_sent_time())),
-                 aos::realtime_clock::time_point(
-                     chrono::nanoseconds(message_header->realtime_sent_time())),
-                 message_header->queue_index());
+    sender->Send(remote_data->data()->data(), remote_data->data()->size(),
+                 monotonic_clock::time_point(
+                     chrono::nanoseconds(remote_data->monotonic_sent_time())),
+                 realtime_clock::time_point(
+                     chrono::nanoseconds(remote_data->realtime_sent_time())),
+                 remote_data->queue_index());
 
     client_status_->SampleFilter(
         client_index_,
-        aos::monotonic_clock::time_point(
-            chrono::nanoseconds(message_header->monotonic_sent_time())),
+        monotonic_clock::time_point(
+            chrono::nanoseconds(remote_data->monotonic_sent_time())),
         sender->monotonic_sent_time());
 
     if (stream_reply_with_timestamp_[stream]) {
@@ -264,13 +265,13 @@
       // Now fill out the message received reply.  This uses a MessageHeader
       // container so it can be directly logged.
       message_reception_reply_.mutable_message()->mutate_channel_index(
-          message_header->channel_index());
+          remote_data->channel_index());
       message_reception_reply_.mutable_message()->mutate_monotonic_sent_time(
-          message_header->monotonic_sent_time());
+          remote_data->monotonic_sent_time());
       message_reception_reply_.mutable_message()->mutate_realtime_sent_time(
-          message_header->realtime_sent_time());
+          remote_data->realtime_sent_time());
       message_reception_reply_.mutable_message()->mutate_queue_index(
-          message_header->queue_index());
+          remote_data->queue_index());
 
       // And capture the relevant data needed to generate the forwarding
       // MessageHeader.
diff --git a/aos/network/message_bridge_protocol.cc b/aos/network/message_bridge_protocol.cc
index 53102c2..87114ed 100644
--- a/aos/network/message_bridge_protocol.cc
+++ b/aos/network/message_bridge_protocol.cc
@@ -13,14 +13,14 @@
 
 aos::FlatbufferDetachedBuffer<aos::message_bridge::Connect> MakeConnectMessage(
     const Configuration *config, const Node *my_node,
-    std::string_view remote_name, std::string_view boot_uuid) {
+    std::string_view remote_name, const UUID &boot_uuid) {
   CHECK(config->has_nodes()) << ": Config must have nodes to transfer.";
 
   flatbuffers::FlatBufferBuilder fbb;
   fbb.ForceDefaults(true);
 
   flatbuffers::Offset<flatbuffers::String> boot_uuid_offset =
-      fbb.CreateString(boot_uuid);
+      boot_uuid.PackString(&fbb);
 
   flatbuffers::Offset<Node> node_offset =
       RecursiveCopyFlatBuffer<Node>(my_node, &fbb);
diff --git a/aos/network/message_bridge_protocol.h b/aos/network/message_bridge_protocol.h
index 5759a29..d0a28a1 100644
--- a/aos/network/message_bridge_protocol.h
+++ b/aos/network/message_bridge_protocol.h
@@ -5,6 +5,7 @@
 
 #include "aos/configuration.h"
 #include "aos/network/connect_generated.h"
+#include "aos/uuid.h"
 
 namespace aos {
 namespace message_bridge {
@@ -33,7 +34,7 @@
 // Builds up a subscription request for my_node to remote_name.
 aos::FlatbufferDetachedBuffer<aos::message_bridge::Connect> MakeConnectMessage(
     const Configuration *config, const Node *my_node,
-    std::string_view remote_name, std::string_view boot_uuid);
+    std::string_view remote_name, const UUID &boot_uuid);
 
 }  // namespace message_bridge
 }  // namespace aos
diff --git a/aos/network/message_bridge_server_lib.cc b/aos/network/message_bridge_server_lib.cc
index a96d08e..7347607 100644
--- a/aos/network/message_bridge_server_lib.cc
+++ b/aos/network/message_bridge_server_lib.cc
@@ -8,6 +8,7 @@
 #include "aos/network/connect_generated.h"
 #include "aos/network/message_bridge_protocol.h"
 #include "aos/network/message_bridge_server_generated.h"
+#include "aos/network/remote_data_generated.h"
 #include "aos/network/remote_message_generated.h"
 #include "aos/network/sctp_server.h"
 #include "aos/network/timestamp_channel.h"
@@ -34,10 +35,22 @@
           << channel_->name()->string_view() << " "
           << channel_->type()->string_view() << " size " << context.size;
 
+  flatbuffers::Offset<flatbuffers::Vector<uint8_t>> data_offset =
+      fbb.CreateVector(static_cast<const uint8_t *>(context.data),
+                       context.size);
+
+  RemoteData::Builder remote_data_builder(fbb);
+  remote_data_builder.add_channel_index(channel_index_);
+  remote_data_builder.add_queue_index(context.queue_index);
+  remote_data_builder.add_monotonic_sent_time(
+      context.monotonic_event_time.time_since_epoch().count());
+  remote_data_builder.add_realtime_sent_time(
+      context.realtime_event_time.time_since_epoch().count());
+  remote_data_builder.add_data(data_offset);
+
   // TODO(austin): Use an iovec to build it up in 3 parts to avoid the copy?
   // Only useful when not logging.
-  fbb.FinishSizePrefixed(logger::PackMessage(&fbb, context, channel_index_,
-                                             logger::LogType::kLogMessage));
+  fbb.FinishSizePrefixed(remote_data_builder.Finish());
 
   return fbb;
 }
@@ -113,33 +126,32 @@
             peer.timestamp_logger->MakeBuilder();
 
         flatbuffers::Offset<flatbuffers::String> boot_uuid_offset =
-            builder.fbb()->CreateString(
-                server_status.BootUUID(peer.node_index));
+            server_status.BootUUID(peer.node_index).PackString(builder.fbb());
 
-        RemoteMessage::Builder message_header_builder =
+        RemoteMessage::Builder remote_message_builder =
             builder.MakeBuilder<RemoteMessage>();
 
-        message_header_builder.add_channel_index(
+        remote_message_builder.add_channel_index(
             message_header->channel_index());
 
-        message_header_builder.add_queue_index(
+        remote_message_builder.add_queue_index(
             message_header->remote_queue_index());
-        message_header_builder.add_monotonic_sent_time(
+        remote_message_builder.add_monotonic_sent_time(
             message_header->monotonic_remote_time());
-        message_header_builder.add_realtime_sent_time(
+        remote_message_builder.add_realtime_sent_time(
             message_header->realtime_remote_time());
 
         // Swap the remote and sent metrics.  They are from the sender's
         // perspective, not the receiver's perspective.
-        message_header_builder.add_monotonic_remote_time(
+        remote_message_builder.add_monotonic_remote_time(
             message_header->monotonic_sent_time());
-        message_header_builder.add_realtime_remote_time(
+        remote_message_builder.add_realtime_remote_time(
             message_header->realtime_sent_time());
-        message_header_builder.add_remote_queue_index(
+        remote_message_builder.add_remote_queue_index(
             message_header->queue_index());
-        message_header_builder.add_boot_uuid(boot_uuid_offset);
+        remote_message_builder.add_boot_uuid(boot_uuid_offset);
 
-        builder.Send(message_header_builder.Finish());
+        builder.Send(remote_message_builder.Finish());
       }
       break;
     }
@@ -167,7 +179,7 @@
 }
 
 void ChannelState::HandleFailure(
-    SizePrefixedFlatbufferDetachedBuffer<logger::MessageHeader> &&message) {
+    SizePrefixedFlatbufferDetachedBuffer<RemoteData> &&message) {
   // TODO(austin): Put it in the log queue.
   if (VLOG_IS_ON(2)) {
     LOG(INFO) << "Failed to send " << FlatbufferToJson(message);
@@ -279,7 +291,7 @@
                            configuration::GetNode(event_loop->configuration(),
                                                   destination_node_name),
                            event_loop->node()->name()->string_view(),
-                           UUID::Zero().string_view())
+                           UUID::Zero())
             .span()
             .size());
     VLOG(1) << "Connection to " << destination_node_name << " has size "
@@ -405,7 +417,7 @@
                    ->name()
                    ->string_view();
     server_status_.ResetFilter(node_index);
-    server_status_.SetBootUUID(node_index, "");
+    server_status_.ClearBootUUID(node_index);
   }
 }
 
@@ -480,7 +492,8 @@
       }
     }
     server_status_.ResetFilter(node_index);
-    server_status_.SetBootUUID(node_index, connect->boot_uuid()->string_view());
+    server_status_.SetBootUUID(
+        node_index, UUID::FromString(connect->boot_uuid()->string_view()));
     VLOG(1) << "Resetting filters for " << node_index << " "
             << event_loop_->configuration()
                    ->nodes()
diff --git a/aos/network/message_bridge_server_lib.h b/aos/network/message_bridge_server_lib.h
index 88433d1..be1801c 100644
--- a/aos/network/message_bridge_server_lib.h
+++ b/aos/network/message_bridge_server_lib.h
@@ -11,6 +11,7 @@
 #include "aos/network/message_bridge_client_generated.h"
 #include "aos/network/message_bridge_server_generated.h"
 #include "aos/network/message_bridge_server_status.h"
+#include "aos/network/remote_data_generated.h"
 #include "aos/network/remote_message_generated.h"
 #include "aos/network/sctp_server.h"
 #include "aos/network/timestamp_channel.h"
@@ -88,7 +89,7 @@
 
   // Handles (by consuming) failure to deliver a message.
   void HandleFailure(
-      SizePrefixedFlatbufferDetachedBuffer<logger::MessageHeader> &&message);
+      SizePrefixedFlatbufferDetachedBuffer<RemoteData> &&message);
 
  private:
   const int channel_index_;
@@ -96,8 +97,7 @@
 
   std::vector<Peer> peers_;
 
-  std::deque<SizePrefixedFlatbufferDetachedBuffer<logger::MessageHeader>>
-      sent_messages_;
+  std::deque<SizePrefixedFlatbufferDetachedBuffer<RemoteData>> sent_messages_;
 
   // A fetcher to use to send the last message when a node connects and is
   // reliable.
diff --git a/aos/network/message_bridge_server_status.cc b/aos/network/message_bridge_server_status.cc
index 6c58dd1..7788f4a 100644
--- a/aos/network/message_bridge_server_status.cc
+++ b/aos/network/message_bridge_server_status.cc
@@ -85,11 +85,8 @@
       statistics_.message().connections()->size());
 
   filters_.resize(event_loop->configuration()->nodes()->size());
-  boot_uuids_.resize(event_loop->configuration()->nodes()->size());
-  for (std::string &boot_uuid : boot_uuids_) {
-    // Make sure the memory gets allocated.
-    boot_uuid.reserve(UUID::kSize);
-  }
+  boot_uuids_.resize(event_loop->configuration()->nodes()->size(), UUID::Zero());
+  has_boot_uuids_.resize(event_loop->configuration()->nodes()->size(), false);
   timestamp_fetchers_.resize(event_loop->configuration()->nodes()->size());
   server_connection_.resize(event_loop->configuration()->nodes()->size());
 
@@ -144,12 +141,19 @@
 }
 
 void MessageBridgeServerStatus::SetBootUUID(int node_index,
-                                            std::string_view boot_uuid) {
+                                            const UUID &boot_uuid) {
+  has_boot_uuids_[node_index] = true;
   boot_uuids_[node_index] = boot_uuid;
   SendStatistics();
   last_statistics_send_time_ = event_loop_->monotonic_now();
 }
 
+void MessageBridgeServerStatus::ClearBootUUID(int node_index) {
+  has_boot_uuids_[node_index] = false;
+  SendStatistics();
+  last_statistics_send_time_ = event_loop_->monotonic_now();
+}
+
 void MessageBridgeServerStatus::ResetFilter(int node_index) {
   filters_[node_index].Reset();
   server_connection_[node_index]->mutate_monotonic_offset(0);
@@ -175,9 +179,9 @@
     flatbuffers::Offset<Node> node_offset = node_builder.Finish();
 
     flatbuffers::Offset<flatbuffers::String> boot_uuid_offset;
-    if (!boot_uuids_[node_index].empty() &&
-        connection->state() == State::CONNECTED) {
-      boot_uuid_offset = builder.fbb()->CreateString(boot_uuids_[node_index]);
+    if (connection->state() == State::CONNECTED &&
+        has_boot_uuids_[node_index]) {
+      boot_uuid_offset = boot_uuids_[node_index].PackString(builder.fbb());
     }
 
     ServerConnection::Builder server_connection_builder =
diff --git a/aos/network/message_bridge_server_status.h b/aos/network/message_bridge_server_status.h
index 680c189..52fc174 100644
--- a/aos/network/message_bridge_server_status.h
+++ b/aos/network/message_bridge_server_status.h
@@ -41,13 +41,13 @@
   // Resets the filter and clears the entry from the server statistics.
   void ResetFilter(int node_index);
   // Sets the boot UUID for the provided node.
-  void SetBootUUID(int node_index, std::string_view boot_uuid);
+  void SetBootUUID(int node_index, const UUID &boot_uuid);
+  // Clears the boot UUID for the provided node.
+  void ClearBootUUID(int node_index);
 
   // Returns the boot UUID for a node, or an empty string_view if there isn't
   // one.
-  std::string_view BootUUID(int node_index) const {
-    return boot_uuids_[node_index];
-  }
+  const UUID &BootUUID(int node_index) const { return boot_uuids_[node_index]; }
 
   // Returns the ServerConnection message which is updated by the server.
   ServerConnection *FindServerConnection(std::string_view node_name);
@@ -93,7 +93,8 @@
   std::vector<ClippedAverageFilter> filters_;
 
   // List of UUIDs for each node.
-  std::vector<std::string> boot_uuids_;
+  std::vector<UUID> boot_uuids_;
+  std::vector<bool> has_boot_uuids_;
 
   // Sender for the timestamps that we are forwarding over the network.
   aos::Sender<Timestamp> timestamp_sender_;
diff --git a/aos/network/remote_data.fbs b/aos/network/remote_data.fbs
new file mode 100644
index 0000000..0813e57
--- /dev/null
+++ b/aos/network/remote_data.fbs
@@ -0,0 +1,25 @@
+namespace aos.message_bridge;
+
+table RemoteData {
+  // Index into the channel datastructure in the log file header.  This
+  // provides the data type.
+  channel_index:uint (id: 0);
+  // Time this message was sent on the monotonic clock in nanoseconds on this
+  // node.
+  monotonic_sent_time:long (id: 1);
+  // Time this message was sent on the realtime clock in nanoseconds on this
+  // node.
+  realtime_sent_time:long (id: 2);
+  // Index into the ipc queue of this message.  This should start with 0 and
+  // always monotonically increment if no messages were ever lost.  It will
+  // wrap at a multiple of the queue size.
+  queue_index:uint (id: 3);
+
+  // The nested flatbuffer.
+  data:[ubyte] (id: 4);
+
+  // UUID for this boot.  This is 16 bytes long.
+  boot_uuid:[uint8] (id: 5);
+}
+
+root_type RemoteData;
diff --git a/aos/uuid.cc b/aos/uuid.cc
new file mode 100644
index 0000000..71e688c
--- /dev/null
+++ b/aos/uuid.cc
@@ -0,0 +1,164 @@
+#include "aos/uuid.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <array>
+#include <random>
+#include <string_view>
+
+#include "glog/logging.h"
+
+namespace aos {
+namespace {
+void ToHex(const uint8_t *val, char *result, size_t count) {
+  while (count > 0) {
+    int upper = ((*val) >> 4) & 0xf;
+    if (upper < 10) {
+      result[0] = upper + '0';
+    } else {
+      result[0] = upper - 10 + 'a';
+    }
+
+    int lower = (*val) & 0xf;
+    if (lower < 10) {
+      result[1] = lower + '0';
+    } else {
+      result[1] = lower - 10 + 'a';
+    }
+
+    ++val;
+    result += 2;
+    --count;
+  }
+}
+
+void FromHex(const char *val, uint8_t *result, size_t count) {
+  while (count > 0) {
+    CHECK((val[0] >= '0' && val[0] <= '9') || (val[0] >= 'a' && val[0] <= 'f'))
+        << ": Invalid hex '" << val[0] << "'";
+    CHECK((val[1] >= '0' && val[1] <= '9') || (val[1] >= 'a' && val[1] <= 'f'))
+        << ": Invalid hex '" << val[1] << "'";
+
+    uint8_t converted = 0;
+    if (val[0] < 'a') {
+      converted |= static_cast<uint8_t>(val[0] - '0') << 4;
+    } else {
+      converted |= (static_cast<uint8_t>(val[0] - 'a') + 0xa) << 4;
+    }
+    if (val[1] < 'a') {
+      converted |= static_cast<uint8_t>(val[1] - '0');
+    } else {
+      converted |= (static_cast<uint8_t>(val[1] - 'a') + 0xa);
+    }
+    *result = converted;
+
+    val += 2;
+    ++result;
+    --count;
+  }
+}
+
+}  // namespace
+
+UUID UUID::Random() {
+  std::random_device rd;
+  std::mt19937 gen(rd());
+
+  std::uniform_int_distribution<> dis(0, 255);
+  std::uniform_int_distribution<> dis2(8, 11);
+  UUID result;
+  for (size_t i = 0; i < kDataSize; ++i) {
+    result.data_[i] = dis(gen);
+  }
+
+  // Mark the reserved bits in the data that this is a uuid4, a random UUID.
+  result.data_[6] = (result.data_[6] & 0x0f) | 0x40;
+  result.data_[8] = (result.data_[6] & 0x3f) | 0x80;
+
+  return result;
+}
+
+std::string UUID::ToString() const {
+  std::string out;
+  out.resize(UUID::kStringSize);
+  CopyTo(out.data());
+  return out;
+}
+
+std::ostream &operator<<(std::ostream &os, const UUID &uuid) {
+  return os << uuid.ToString();
+}
+
+flatbuffers::Offset<flatbuffers::String> UUID::PackString(
+    flatbuffers::FlatBufferBuilder *fbb) const {
+  std::array<char, kStringSize> data;
+  CopyTo(data.data());
+
+  return fbb->CreateString(data.data(), data.size());
+}
+
+flatbuffers::Offset<flatbuffers::Vector<uint8_t>> UUID::PackVector(
+    flatbuffers::FlatBufferBuilder *fbb) const {
+  return fbb->CreateVector(data_.data(), data_.size());
+}
+
+void UUID::CopyTo(char *result) const {
+  ToHex(&data_[0], result, 4);
+  result[8] = '-';
+  ToHex(&data_[4], result + 9, 2);
+  result[13] = '-';
+  ToHex(&data_[6], result + 14, 2);
+  result[18] = '-';
+  ToHex(&data_[8], result + 19, 2);
+  result[23] = '-';
+  ToHex(&data_[10], result + 24, 6);
+}
+
+UUID UUID::Zero() {
+  UUID result;
+  std::memset(result.data_.data(), 0, result.data_.size());
+  return result;
+}
+
+UUID UUID::FromString(const flatbuffers::String *str) {
+  return FromString(str->string_view());
+}
+
+UUID UUID::FromVector(const flatbuffers::Vector<uint8_t> *data) {
+  CHECK(data != nullptr);
+  CHECK_EQ(data->size(), kDataSize);
+
+  UUID result;
+  std::memcpy(result.data_.data(), data->Data(), kDataSize);
+  return result;
+}
+
+UUID UUID::FromString(std::string_view str) {
+  CHECK_EQ(str.size(), kStringSize);
+
+  UUID result;
+  FromHex(str.data(), result.data_.data(), 4);
+  CHECK(str.data()[8] == '-' && str.data()[13] == '-' &&
+        str.data()[18] == '-' && str.data()[23] == '-')
+      << ": Invalid uuid.";
+  FromHex(str.data() + 9, result.data_.data() + 4, 2);
+  FromHex(str.data() + 14, result.data_.data() + 6, 2);
+  FromHex(str.data() + 19, result.data_.data() + 8, 2);
+  FromHex(str.data() + 24, result.data_.data() + 10, 6);
+  return result;
+}
+
+UUID UUID::BootUUID() {
+  int fd = open("/proc/sys/kernel/random/boot_id", O_RDONLY);
+  PCHECK(fd != -1);
+
+  std::array<char, kStringSize> data;
+  CHECK_EQ(static_cast<ssize_t>(kStringSize),
+           read(fd, data.begin(), kStringSize));
+  close(fd);
+
+  return UUID::FromString(std::string_view(data.data(), data.size()));
+}
+
+}  // namespace aos
diff --git a/aos/uuid.h b/aos/uuid.h
new file mode 100644
index 0000000..d12bedf
--- /dev/null
+++ b/aos/uuid.h
@@ -0,0 +1,74 @@
+#ifndef AOS_EVENTS_LOGGING_UUID_H_
+#define AOS_EVENTS_LOGGING_UUID_H_
+
+#include <array>
+#include <ostream>
+#include <string>
+
+#include "absl/types/span.h"
+#include "flatbuffers/flatbuffers.h"
+
+namespace aos {
+
+// Class to generate and hold a UUID.
+class UUID {
+ public:
+  // Size of a UUID both as a string and the raw data.
+  static constexpr size_t kStringSize = 36;
+  static constexpr size_t kDataSize = 16;
+
+  // Returns a randomly generated UUID.  This is known as a UUID4.
+  static UUID Random();
+
+  // Returns a uuid with all '0's.
+  static UUID Zero();
+
+  // Converts a string UUID of the form 00000000-0000-0000-0000-000000000000 to
+  // a UUID.
+  static UUID FromString(std::string_view string);
+  static UUID FromString(const flatbuffers::String *string);
+
+  // Converts a 16 byte vector (128 bits) to a UUID.  This requires no
+  // transformation.
+  static UUID FromVector(const flatbuffers::Vector<uint8_t> *data);
+
+  // Returns the boot UUID for the current linux computer.
+  static UUID BootUUID();
+
+  // Default constructor which builds an uninitialized UUID.  Use one of the
+  // static methods if you want something more useful.
+  UUID() {}
+
+  // Packs this UUID into a flatbuffer as a string.
+  flatbuffers::Offset<flatbuffers::String> PackString(
+      flatbuffers::FlatBufferBuilder *fbb) const;
+  // Copies this UUID as a string into the memory pointed by result.  Result
+  // must be at least kStringSize long.
+  void CopyTo(char *result) const;
+  // Returns this UUID as a string.
+  std::string ToString() const;
+
+  // Packs the UUID bytes directly into a vector.
+  flatbuffers::Offset<flatbuffers::Vector<uint8_t>> PackVector(
+      flatbuffers::FlatBufferBuilder *fbb) const;
+
+  // Returns the underlying UUID data.
+  absl::Span<const uint8_t> span() const {
+    return absl::Span<const uint8_t>(data_.data(), data_.size());
+  }
+
+  bool operator==(const UUID &other) const { return other.span() == span(); }
+  bool operator!=(const UUID &other) const { return other.span() != span(); }
+
+ private:
+  friend std::ostream &operator<<(std::ostream &os, const UUID &uuid);
+
+  // Encoded storage for the data.
+  std::array<uint8_t, kDataSize> data_;
+};
+
+std::ostream &operator<<(std::ostream &os, const UUID &uuid);
+
+}  // namespace aos
+
+#endif  // AOS_EVENTS_LOGGING_UUID_H_
diff --git a/aos/uuid_test.cc b/aos/uuid_test.cc
new file mode 100644
index 0000000..33ec6ef
--- /dev/null
+++ b/aos/uuid_test.cc
@@ -0,0 +1,52 @@
+#include "aos/uuid.h"
+
+#include "glog/logging.h"
+#include "gtest/gtest.h"
+
+namespace aos {
+namespace testing {
+
+// Tests that random UUIDs are actually random, and we can convert them to a
+// string.  Not very exhaustive, but it is a good smoke test.
+TEST(UUIDTest, GetOne) {
+  LOG(INFO) << UUID::Random();
+
+  UUID r = UUID::Random();
+
+  std::stringstream ss;
+  ss << r;
+
+  UUID r2 = UUID::FromString(ss.str());
+  EXPECT_EQ(r2, r);
+
+  EXPECT_NE(UUID::Random(), UUID::Random());
+  EXPECT_NE(UUID::Random(), UUID::Zero());
+  EXPECT_EQ(UUID::Zero(), UUID::Zero());
+}
+
+// Tests that converting to and from various formats produces the same UUID.
+TEST(UUIDTest, FromString) {
+  std::string_view str = "4b88ab00-556a-455b-a395-17d1a0c6f906";
+  std::array<uint8_t, UUID::kDataSize> data = {
+      0x4b, 0x88, 0xab, 0x00, 0x55, 0x6a, 0x45, 0x5b,
+      0xa3, 0x95, 0x17, 0xd1, 0xa0, 0xc6, 0xf9, 0x06};
+
+  const UUID u = UUID::FromString(str);
+
+  EXPECT_EQ(u.span(), absl::Span<uint8_t>(data.data(), data.size()));
+  EXPECT_EQ(u.ToString(), str);
+
+  flatbuffers::FlatBufferBuilder fbb;
+  flatbuffers::Offset<flatbuffers::Vector<uint8_t>> data_offset =
+      fbb.CreateVector(data.data(), data.size());
+
+  const flatbuffers::Vector<uint8_t> *data_vector =
+      flatbuffers::GetTemporaryPointer(fbb, data_offset);
+
+  const UUID u2 = UUID::FromVector(data_vector);
+
+  EXPECT_EQ(u, u2);
+}
+
+}  // namespace testing
+}  // namespace aos