diff --git a/algo/CMakeLists.txt b/algo/CMakeLists.txt
index 612c11a1a5f537f078177f538ff91be607a8aa03..c4100eca53beeea0bac737e1372b5c29686a54f1 100644
--- a/algo/CMakeLists.txt
+++ b/algo/CMakeLists.txt
@@ -50,15 +50,14 @@ if (CBM_ONLINE_STANDALONE)
   include(CbmMacros) # for 'download_project_if_needed', 'Gen_Exe_Script' macro
 
   add_subdirectory(../external external)
+  add_subdirectory(../core/utility utility)
 endif()
 
-add_subdirectory(kfp)
 add_subdirectory(log)
-add_subdirectory(data)
-add_subdirectory(kf)
 add_subdirectory(ca)
-add_subdirectory(base)
-#add_subdirectory(kfp) # For KFParticleOnline
+add_subdirectory(online)
+add_subdirectory(kf)
+add_subdirectory(kfp)
 
 # exclude unittests from being build inside the container
 if (NOT CBM_ONLINE_STANDALONE)
@@ -82,7 +81,6 @@ set(SRCS
   base/compat/Algorithm.cxx
   base/util/MemoryLogger.cxx
   base/util/StlUtils.cxx
-  base/util/EnumDict.cxx
   base/util/TimingsFormat.cxx
   data/sts/HitfinderPars.cxx
   data/sts/LandauTable.cxx
@@ -217,6 +215,9 @@ target_include_directories(Algo
 
 target_link_libraries(Algo
   PUBLIC    OnlineData
+            CbmUtility
+            CbmYamlInterface
+            OnlineContainers
             KfCore
             CaCore
             ROOT::GenVector
@@ -234,7 +235,6 @@ target_link_libraries(Algo
             cppzmq
             poolstl
   PRIVATE   CbmKFParticleOnlineInterface
-  INTERFACE CbmYamlInterface
 )
 target_compile_definitions(Algo PUBLIC NO_ROOT)
 xpu_attach(Algo ${DEVICE_SRCS})
@@ -294,6 +294,9 @@ if (NOT CBM_ONLINE_STANDALONE)
 
   target_link_libraries(AlgoOffline
     PUBLIC    CbmData
+              CbmYamlInterface
+              CbmUtility
+              CbmContainers
               KfCoreOffline
               CaCoreOffline
               ROOT::GenVector
@@ -311,7 +314,6 @@ if (NOT CBM_ONLINE_STANDALONE)
               cppzmq
               poolstl
     PRIVATE   CbmKFParticleOnlineInterface
-    INTERFACE CbmYamlInterface
   )
   xpu_attach(AlgoOffline ${DEVICE_SRCS})
 
@@ -368,10 +370,7 @@ install(
     base/RecoParams.h
     base/SubChain.h
     base/System.h
-    base/PartitionedVector.h
-    base/PartitionedSpan.h
     base/DigiData.h
-    base/PODVector.h
     base/AlgoTraits.h
     base/AuxDigiData.h
     base/BuildInfo.h
@@ -401,6 +400,9 @@ install(
     # NOTE: SZh 20.11.2023:
     #       The ca/qa directory depends on the online qa classes, so for now it has to be a part of the Algo library.
     ca/qa/CaQa.h
+  
   DESTINATION
     include/
 )
+
+
diff --git a/algo/base/CMakeLists.txt b/algo/base/CMakeLists.txt
deleted file mode 100644
index 93b295fe0a83615d30852967c9754863bd3cd2d8..0000000000000000000000000000000000000000
--- a/algo/base/CMakeLists.txt
+++ /dev/null
@@ -1 +0,0 @@
-add_subdirectory(yaml)
diff --git a/algo/base/Definitions.h b/algo/base/Definitions.h
index c9cac7a917126b96499a3c8ede8824f1ee9bb5ca..73cc85bf7769c911f885ab59b8340a0ac352f721 100644
--- a/algo/base/Definitions.h
+++ b/algo/base/Definitions.h
@@ -4,8 +4,8 @@
 #ifndef CBM_BASE_TYPES_H
 #define CBM_BASE_TYPES_H
 
+#include "CbmEnumDict.h"
 #include "MicrosliceDescriptor.hpp"  // For fles::Subsystem
-#include "util/EnumDict.h"
 
 #include <cstdint>
 
@@ -108,18 +108,18 @@ CBM_ENUM_DICT(fles::Subsystem,
 );
 
 CBM_ENUM_DICT(cbm::algo::Step,
-  {"Unpack", Step::Unpack},
-  {"DigiTrigger", Step::DigiTrigger},
-  {"LocalReco", Step::LocalReco},
-  {"Tracking", Step::Tracking}
+  {"Unpack", cbm::algo::Step::Unpack},
+  {"DigiTrigger", cbm::algo::Step::DigiTrigger},
+  {"LocalReco", cbm::algo::Step::LocalReco},
+  {"Tracking", cbm::algo::Step::Tracking}
 );
 
 CBM_ENUM_DICT(cbm::algo::RecoData,
-  {"DigiTimeslice", RecoData::DigiTimeslice},
-  {"DigiEvent", RecoData::DigiEvent},
-  {"Cluster", RecoData::Cluster},
-  {"Hit", RecoData::Hit},
-  {"Track", RecoData::Track}
+  {"DigiTimeslice", cbm::algo::RecoData::DigiTimeslice},
+  {"DigiEvent", cbm::algo::RecoData::DigiEvent},
+  {"Cluster", cbm::algo::RecoData::Cluster},
+  {"Hit", cbm::algo::RecoData::Hit},
+  {"Track", cbm::algo::RecoData::Track}
 );
 
 CBM_ENUM_DICT(cbm::algo::Setup,
diff --git a/algo/base/RecoParams.h b/algo/base/RecoParams.h
index db747f189c96263f8dd7bc9fa1be3466c408c47e..c933c63de14bf7e90579b777b89e13a832d7a7ba 100644
--- a/algo/base/RecoParams.h
+++ b/algo/base/RecoParams.h
@@ -5,7 +5,6 @@
 #define CBM_ALGO_BASE_RECOPARAMS_H
 
 #include "Definitions.h"
-#include "util/EnumDict.h"
 #include "yaml/Property.h"
 #include "yaml/Yaml.h"
 
@@ -106,14 +105,14 @@ namespace cbm::algo
 CBM_YAML_EXTERN_DECL(cbm::algo::RecoParams);
 
 CBM_ENUM_DICT(cbm::algo::RecoParams::SortMode,
-  {"BlockSort", RecoParams::SortMode::BlockSort},
-  {"CUBSegmentedSort", RecoParams::SortMode::CUBSegmentedSort}
+  {"BlockSort", cbm::algo::RecoParams::SortMode::BlockSort},
+  {"CUBSegmentedSort", cbm::algo::RecoParams::SortMode::CUBSegmentedSort}
 );
 
 CBM_ENUM_DICT(cbm::algo::RecoParams::AllocationMode,
-  {"Auto", RecoParams::AllocationMode::Auto},
-  {"Static", RecoParams::AllocationMode::Static},
-  {"Dynamic", RecoParams::AllocationMode::Dynamic}
+  {"Auto", cbm::algo::RecoParams::AllocationMode::Auto},
+  {"Static", cbm::algo::RecoParams::AllocationMode::Static},
+  {"Dynamic", cbm::algo::RecoParams::AllocationMode::Dynamic}
 );
 
 #endif  // CBM_ALGO_BASE_RECOPARAMS_H
diff --git a/algo/base/util/ProfilingLevel.h b/algo/base/util/ProfilingLevel.h
index d358bd36c9c6f5c3575981271ba59fa14c44c8d8..5d9469359ed8d41c3038bb40b7a4a84c1e85def0 100644
--- a/algo/base/util/ProfilingLevel.h
+++ b/algo/base/util/ProfilingLevel.h
@@ -4,7 +4,7 @@
 
 #pragma once
 
-#include "EnumDict.h"
+#include "CbmEnumDict.h"
 
 namespace cbm::algo
 {
diff --git a/algo/detectors/bmon/Clusterizer.h b/algo/detectors/bmon/Clusterizer.h
index 86b3c76e3ba74d1595e7ae0282ac1f10e0e22a09..276f28d1badd3f7df270888b049a5ebba5051a6c 100644
--- a/algo/detectors/bmon/Clusterizer.h
+++ b/algo/detectors/bmon/Clusterizer.h
@@ -9,7 +9,7 @@
 
 #pragma once
 
-#include "base/PODVector.h"
+#include "PODVector.h"
 #include "bmon/ClusterizerPars.h"
 #include "bmon/Hit.h"
 
diff --git a/algo/detectors/sts/HitfinderChain.cxx b/algo/detectors/sts/HitfinderChain.cxx
index 6f65f4621226d13ed49c230d14bb5fe4958712ea..5796cfa54bf292f88d54d9ca5d30bda9441ce812 100644
--- a/algo/detectors/sts/HitfinderChain.cxx
+++ b/algo/detectors/sts/HitfinderChain.cxx
@@ -12,6 +12,8 @@
 #include <numeric>
 
 using namespace cbm::algo;
+using cbm::PartitionedSpan;
+using cbm::PartitionedVector;
 
 void sts::HitfinderChain::SetParameters(const HitfinderChainPars& parameters)
 {
@@ -30,7 +32,7 @@ void sts::HitfinderChain::SetParameters(const HitfinderChainPars& parameters)
 
   if (!memoryPars.IsDynamic() && !memoryPars.IsStatic()) {
     throw std::runtime_error(
-      fmt::format("STS Hitfinder Chain: Unknown allocation mode: {}", ToString(memoryPars.allocationMode)));
+      fmt::format("STS Hitfinder Chain: Unknown allocation mode: {}", util::ToString(memoryPars.allocationMode)));
   }
 
   if (memoryPars.IsStatic()) {
diff --git a/algo/global/ParFiles.cxx b/algo/global/ParFiles.cxx
index 9d4e8ee2cde9139b0cc765ea92b5f97d62025d66..85134bd4bba52da79806ef16d94a2da24f612e3b 100644
--- a/algo/global/ParFiles.cxx
+++ b/algo/global/ParFiles.cxx
@@ -124,6 +124,6 @@ ParFiles::ParFiles(uint32_t runId)
       kfp.V0FinderConfig = "mcbm2025_02/kfp_lambda_v25a.yaml";
       break;
 
-    default: throw FatalError("Unknown setup: {}", ToString(setup));
+    default: throw FatalError("Unknown setup: {}", util::ToString(setup));
   }
 }
diff --git a/algo/global/Reco.cxx b/algo/global/Reco.cxx
index 1554da152a372b06c12fad0c227dcff9878905c9..3736872a029491ffb09c5f879da2c03f81229f6f 100644
--- a/algo/global/Reco.cxx
+++ b/algo/global/Reco.cxx
@@ -646,7 +646,7 @@ void Reco::QueueUnpackerMetricsDet(const UnpackMonitor<MSMonitor>& monitor)
     return;
   }
 
-  std::string_view det = ToString(monitor.system);
+  std::string_view det = util::ToString(monitor.system);
 
   auto MkKey = [&](std::string_view key) { return fmt::format("{}{}", key, Capitalize(det)); };
 
diff --git a/algo/online/CMakeLists.txt b/algo/online/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..f72fc4858c376d7bae6a815147cf317ab296ed7f
--- /dev/null
+++ b/algo/online/CMakeLists.txt
@@ -0,0 +1,6 @@
+# A rule to build the online versions of CbmRoot core libraries
+#
+add_subdirectory(containers)
+add_subdirectory(data)
+
+
diff --git a/algo/online/containers/CMakeLists.txt b/algo/online/containers/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..34a95ef9d3a4b22d20b6b78c8d2c02f7e46d562d
--- /dev/null
+++ b/algo/online/containers/CMakeLists.txt
@@ -0,0 +1,17 @@
+# Online version of libCbmContainers
+
+set(OFFLINE_CONTAINERS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../core/containers)
+
+add_library(OnlineContainers INTERFACE)
+
+target_include_directories(OnlineContainers
+  INTERFACE ${OFFLINE_CONTAINERS_DIR}
+)
+
+target_compile_definitions(OnlineContainers INTERFACE NO_ROOT)
+
+target_link_libraries(OnlineContainers 
+  INTERFACE GSL
+)
+
+install(TARGETS OnlineContainers DESTINATION lib)
diff --git a/algo/data/CMakeLists.txt b/algo/online/data/CMakeLists.txt
similarity index 96%
rename from algo/data/CMakeLists.txt
rename to algo/online/data/CMakeLists.txt
index 717ba53896c880649c4c07c1ff010fad8c14551b..0d99f1d5fa5b50adc9eae20140cc895849eb720a 100644
--- a/algo/data/CMakeLists.txt
+++ b/algo/online/data/CMakeLists.txt
@@ -2,7 +2,7 @@
 # the array .
 # The extension is already found.  Any number of sources could be listed here.
 
-set(OFFLINE_DATA_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../core/data)
+set(OFFLINE_DATA_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../core/data)
 
 set(SRCS
   ${OFFLINE_DATA_DIR}/CbmDefs.cxx
diff --git a/algo/test/_GTestPartitionedSpan.cxx b/algo/test/_GTestPartitionedSpan.cxx
index ccc3823250d773de15a4be28b68c6db5cae2797c..08bd0009396500fcf49ee81bee2ead976d52e451 100644
--- a/algo/test/_GTestPartitionedSpan.cxx
+++ b/algo/test/_GTestPartitionedSpan.cxx
@@ -1,6 +1,8 @@
 /* Copyright (C) 2023 FIAS Frankfurt Institute for Advanced Studies, Frankfurt / Main
    SPDX-License-Identifier: GPL-3.0-only
    Authors: Felix Weiglhofer [committer] */
+
+#include "Definitions.h"
 #include "PartitionedSpan.h"
 #include "PartitionedVector.h"
 
@@ -8,6 +10,9 @@
 
 using namespace cbm::algo;
 
+using cbm::PartitionedSpan;
+using cbm::PartitionedVector;
+
 template<typename T>
 void EXPECT_CONTAINER_EQ(gsl::span<T> a, std::vector<i32> b)
 {
diff --git a/algo/test/_GTestPartitionedVector.cxx b/algo/test/_GTestPartitionedVector.cxx
index 49ab5b17609f616092e1501fa36ac30fe47ea71a..5eda3d2f971eb38790b73adee628b3dfe5cbd363 100644
--- a/algo/test/_GTestPartitionedVector.cxx
+++ b/algo/test/_GTestPartitionedVector.cxx
@@ -2,12 +2,17 @@
    SPDX-License-Identifier: GPL-3.0-only
    Authors: Felix Weiglhofer [committer] */
 
+#include "Definitions.h"
 #include "PODVector.h"
 #include "PartitionedVector.h"
 #include "gtest/gtest.h"
 
 using namespace cbm::algo;
 
+using cbm::PartitionedPODVector;
+using cbm::PartitionedVector;
+using cbm::PODAllocator;
+
 template<typename T>
 void EXPECT_CONTAINER_EQ(gsl::span<const T> a, std::vector<T> b)
 {
diff --git a/algo/unpack/CommonUnpacker.cxx b/algo/unpack/CommonUnpacker.cxx
index d7cc52db073c3d64534202dec590e6314147f08a..ba737260e98a02a3a6ee151bbc1e06b939ea30da 100644
--- a/algo/unpack/CommonUnpacker.cxx
+++ b/algo/unpack/CommonUnpacker.cxx
@@ -23,7 +23,7 @@ detail::MSData::MSData(const fles::Timeslice& ts, fles::Subsystem subsystem, gsl
 
     if (std::find(legalEqIds.begin(), legalEqIds.end(), componentId) == legalEqIds.end()) {
       L_(error) << "Invalid equipment id 0x" << std::hex << std::setw(4) << componentId << std::dec << " for subsystem "
-                << ToString(subsystem);
+                << util::ToString(subsystem);
       monitor.errInvalidEqId++;
       continue;
     }
@@ -36,5 +36,5 @@ detail::MSData::MSData(const fles::Timeslice& ts, fles::Subsystem subsystem, gsl
       msContent.push_back(ts.content(comp, mslice));
     }
   }
-  L_(debug) << "Found " << monitor.numMs << " microslices for subsystem " << ToString(subsystem);
+  L_(debug) << "Found " << monitor.numMs << " microslices for subsystem " << util::ToString(subsystem);
 }
diff --git a/algo/unpack/CommonUnpacker.h b/algo/unpack/CommonUnpacker.h
index 3db3952e782feee53bcbc62c353b867f8287eb01..3d644c1c9b2491129f24c34cf60f1641959bdd21 100644
--- a/algo/unpack/CommonUnpacker.h
+++ b/algo/unpack/CommonUnpacker.h
@@ -118,12 +118,12 @@ namespace cbm::algo
         if (algo == fAlgos.end()) {
           if (!Contains(legalEqIds, msDesc.eq_id)) {
             L_(error) << "Invalid equip id " << std::hex << int{msDesc.eq_id} << std::dec << " for subsystem "
-                      << ToString(subsystem);
+                      << util::ToString(subsystem);
             monitorOut.errInvalidEqId++;
           }
           else {
             L_(error) << "Invalid system version " << std::hex << int{msDesc.sys_ver} << std::dec << " for subsystem "
-                      << ToString(subsystem);
+                      << util::ToString(subsystem);
             monitorOut.errInvalidSysVer++;
           }
           continue;
diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt
index 7fa667426b023d1307b541fb9ed0f8921059f9b8..c574819054e656a01509755ecf2436da389f6bd2 100644
--- a/core/CMakeLists.txt
+++ b/core/CMakeLists.txt
@@ -2,10 +2,12 @@
 set(CBMDATA_DIR ${CMAKE_CURRENT_SOURCE_DIR}/data)
 set(CBMBASE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/base)
 
+add_subdirectory(containers)
 add_subdirectory(data)
 add_subdirectory(field)
 add_subdirectory(base)
 add_subdirectory(qa)
+add_subdirectory(utility)
 
 add_subdirectory(detectors/trd)
 add_subdirectory(detectors/rich)
diff --git a/core/containers/CMakeLists.txt b/core/containers/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..6de870b63ac7f8c6b7a935edbd8360f2c56eb920
--- /dev/null
+++ b/core/containers/CMakeLists.txt
@@ -0,0 +1,39 @@
+# Library: CbmContainers
+# 
+
+message("!!!! BUILDING CbmContainers")
+
+set(INCLUDE_DIRECTORIES
+  ${CMAKE_CURRENT_SOURCE_DIR}
+)
+
+set(HEADERS 
+  PartitionedVector.h
+  PartitionedSpan.h
+  PODVector.h
+  PODAllocator.h
+)
+
+set(LIBRARY_NAME CbmContainers)
+
+add_library(${LIBRARY_NAME} INTERFACE)
+
+target_include_directories(${LIBRARY_NAME}
+  INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
+)
+
+target_link_libraries(${LIBRARY_NAME}
+  INTERFACE GSL
+)
+
+install(TARGETS CbmContainers DESTINATION lib)
+
+install(
+  FILES 
+    PartitionedVector.h
+    PartitionedSpan.h
+    PODAllocator.h
+    PODVector.h
+  DESTINATION
+    include/
+)
diff --git a/algo/base/util/PODAllocator.h b/core/containers/PODAllocator.h
similarity index 89%
rename from algo/base/util/PODAllocator.h
rename to core/containers/PODAllocator.h
index 45f1ea89f30f5604f255bc36842ab99d30c800f0..c276655e068911a28590dbe48129fa0125fbc252 100644
--- a/algo/base/util/PODAllocator.h
+++ b/core/containers/PODAllocator.h
@@ -8,7 +8,7 @@
 #include <type_traits>
 #include <utility>
 
-namespace cbm::algo
+namespace cbm
 {
   /**
   * @brief Allocator for plain old data types.
@@ -22,7 +22,7 @@ namespace cbm::algo
     // static_assert(std::is_trivially_constructible_v<T>, "PODAllocator only works for POD types");
 
     // Ensure T has no vtable
-    static_assert(std::is_standard_layout_v<T>, "PODAllocator only works with types with standard layout");
+    static_assert(!std::is_polymorphic_v<T>, "PODAllocator only works with types with non-polymorphic types");
 
     using value_type = T;
 
@@ -39,6 +39,6 @@ namespace cbm::algo
     bool operator==(const PODAllocator&) const { return true; }
     bool operator!=(const PODAllocator&) const { return false; }
   };
-}  // namespace cbm::algo
+}  // namespace cbm
 
 #endif  // CBM_ALGO_BASE_POD_ALLOCATOR_H
diff --git a/algo/base/PODVector.h b/core/containers/PODVector.h
similarity index 90%
rename from algo/base/PODVector.h
rename to core/containers/PODVector.h
index 16f1ceed518bea5af781370502a13aa90798016b..d63f042c93103b753c0f12cb714f421df21e5161 100644
--- a/algo/base/PODVector.h
+++ b/core/containers/PODVector.h
@@ -4,11 +4,11 @@
 #ifndef CBM_ALGO_BASE_POD_VECTOR_H
 #define CBM_ALGO_BASE_POD_VECTOR_H
 
-#include "util/PODAllocator.h"
+#include "PODAllocator.h"
 
 #include <vector>
 
-namespace cbm::algo
+namespace cbm
 {
   /**
   * @brief PODVector is a std::vector that doesn't initialize its elements.
@@ -28,6 +28,6 @@ namespace cbm::algo
     return PODVector<T>(vec.begin(), vec.end());
   }
 
-}  // namespace cbm::algo
+}  // namespace cbm
 
 #endif  // CBM_ALGO_BASE_POD_VECTOR_H
diff --git a/algo/base/PartitionedSpan.h b/core/containers/PartitionedSpan.h
similarity index 87%
rename from algo/base/PartitionedSpan.h
rename to core/containers/PartitionedSpan.h
index c626b75552aa7632a3edbf05160513d33f2d2f93..20cb76f12b7097d5a93b0ec2e40b9f7a565ff670 100644
--- a/algo/base/PartitionedSpan.h
+++ b/core/containers/PartitionedSpan.h
@@ -4,14 +4,13 @@
 #ifndef CBM_ALGO_BASE_PARTITIONED_SPAN_H
 #define CBM_ALGO_BASE_PARTITIONED_SPAN_H
 
-#include "Definitions.h"
-
 #include <array>
+#include <cstdint>
 #include <gsl/span>
 #include <stdexcept>
 #include <vector>
 
-namespace cbm::algo
+namespace cbm
 {
 
   template<typename T, typename Allocator>
@@ -36,7 +35,7 @@ namespace cbm::algo
     // #if  defined(__INTELLISENSE__) || defined(__clang__)
     template<typename Allocator>
     PartitionedSpan(std::vector<T, Allocator>& container, gsl::span<const size_t> offsets,
-                    gsl::span<const u32> addresses)
+                    gsl::span<const uint32_t> addresses)
       : fData(container)
       , fOffsets(offsets)
       , fAdresses(addresses)
@@ -47,7 +46,7 @@ namespace cbm::algo
     // FIXME disable if T is non-const via SFINAE, otherwise get misleading compiler errors
     template<typename Allocator>
     PartitionedSpan(const std::vector<T, Allocator>& container, gsl::span<const size_t> offsets,
-                    gsl::span<const u32> addresses)
+                    gsl::span<const uint32_t> addresses)
       : fData(container)
       , fOffsets(offsets)
       , fAdresses(addresses)
@@ -56,7 +55,7 @@ namespace cbm::algo
     }
 
     template<size_t N>
-    PartitionedSpan(std::array<T, N>& container, gsl::span<const size_t> offsets, gsl::span<const u32> addresses)
+    PartitionedSpan(std::array<T, N>& container, gsl::span<const size_t> offsets, gsl::span<const uint32_t> addresses)
       : fData(container)
       , fOffsets(offsets)
       , fAdresses(addresses)
@@ -66,7 +65,8 @@ namespace cbm::algo
 
     // FIXME disable if T is non-const via SFINAE
     template<size_t N>
-    PartitionedSpan(const std::array<T, N>& container, gsl::span<const size_t> offsets, gsl::span<const u32> addresses)
+    PartitionedSpan(const std::array<T, N>& container, gsl::span<const size_t> offsets,
+                    gsl::span<const uint32_t> addresses)
       : fData(container)
       , fOffsets(offsets)
       , fAdresses(addresses)
@@ -75,7 +75,7 @@ namespace cbm::algo
     }
     // #endif
 
-    PartitionedSpan(gsl::span<T> data, gsl::span<const size_t> offsets, gsl::span<const u32> addresses)
+    PartitionedSpan(gsl::span<T> data, gsl::span<const size_t> offsets, gsl::span<const uint32_t> addresses)
       : fData(data)
       , fOffsets(offsets)
       , fAdresses(addresses)
@@ -115,16 +115,16 @@ namespace cbm::algo
       return UnsafePartitionSpan(i);
     }
 
-    u32 Address(size_t i) const
+    uint32_t Address(size_t i) const
     {
       EnsureBounds(i);
       return fAdresses[i];
     }
 
-    std::pair<gsl::span<T>, u32> Partition(size_t i) const
+    std::pair<gsl::span<T>, uint32_t> Partition(size_t i) const
     {
       EnsureBounds(i);
-      return std::pair<gsl::span<T>, u32>(UnsafePartitionSpan(i), fAdresses[i]);
+      return std::pair<gsl::span<T>, uint32_t>(UnsafePartitionSpan(i), fAdresses[i]);
     }
 
     size_t NPartitions() const { return fAdresses.size(); }
@@ -139,7 +139,7 @@ namespace cbm::algo
 
     gsl::span<T> Data() const { return fData; }
 
-    gsl::span<const u32> Addresses() const { return fAdresses; }
+    gsl::span<const uint32_t> Addresses() const { return fAdresses; }
 
     gsl::span<const size_t> Offsets() const { return fOffsets; }
 
@@ -149,7 +149,7 @@ namespace cbm::algo
 
     gsl::span<T> fData;
     gsl::span<const size_t> fOffsets;
-    gsl::span<const u32> fAdresses;
+    gsl::span<const uint32_t> fAdresses;
 
     // FIXME code duplication with PartitionedVector
 
@@ -176,10 +176,10 @@ namespace cbm::algo
 
   // template auto deduction
   template<typename T, template<typename> class Container>
-  PartitionedSpan(Container<T>&, gsl::span<const size_t>, gsl::span<const u32>) -> PartitionedSpan<T>;
+  PartitionedSpan(Container<T>&, gsl::span<const size_t>, gsl::span<const uint32_t>) -> PartitionedSpan<T>;
 
   template<typename T, template<typename> class Container>
-  PartitionedSpan(const Container<T>&, gsl::span<const size_t>, gsl::span<const u32>) -> PartitionedSpan<const T>;
+  PartitionedSpan(const Container<T>&, gsl::span<const size_t>, gsl::span<const uint32_t>) -> PartitionedSpan<const T>;
 
   template<typename T, typename Allocator>
   PartitionedSpan(PartitionedVector<T, Allocator>&) -> PartitionedSpan<T>;
@@ -187,6 +187,6 @@ namespace cbm::algo
   template<typename T, typename Allocator>
   PartitionedSpan(const PartitionedVector<T, Allocator>&) -> PartitionedSpan<const T>;
 
-}  // namespace cbm::algo
+}  // namespace cbm
 
 #endif
diff --git a/algo/base/PartitionedVector.h b/core/containers/PartitionedVector.h
similarity index 90%
rename from algo/base/PartitionedVector.h
rename to core/containers/PartitionedVector.h
index 6aa91585c03692b7913bb0c8600a86a36eaa6691..c0599860946cc658675df2ae3ffa14be7f4c3693 100644
--- a/algo/base/PartitionedVector.h
+++ b/core/containers/PartitionedVector.h
@@ -4,8 +4,7 @@
 #ifndef CBM_ALGO_BASE_PARTITIONED_VECTOR_H
 #define CBM_ALGO_BASE_PARTITIONED_VECTOR_H
 
-#include "Definitions.h"
-#include "util/PODAllocator.h"
+#include "PODAllocator.h"
 
 #include <boost/serialization/access.hpp>
 #include <boost/serialization/vector.hpp>
@@ -13,7 +12,7 @@
 #include <gsl/span>
 #include <vector>
 
-namespace cbm::algo
+namespace cbm
 {
   template<typename T>
   class PartitionedSpan;
@@ -46,7 +45,7 @@ namespace cbm::algo
      *
      * @note Requires sizes.size() == addresses.size()
      */
-    PartitionedVector(Container_t&& data, gsl::span<const size_t> sizes, gsl::span<const u32> addresses)
+    PartitionedVector(Container_t&& data, gsl::span<const size_t> sizes, gsl::span<const uint32_t> addresses)
       : fData(std::move(data))
       , fOffsets()
       , fAddresses(addresses.begin(), addresses.end())
@@ -149,7 +148,7 @@ namespace cbm::algo
     /**
      * @brief Get the hardware address of partition i.
      */
-    u32 Address(size_t i) const
+    uint32_t Address(size_t i) const
     {
       EnsureBounds(i);
       return fAddresses[i];
@@ -169,19 +168,19 @@ namespace cbm::algo
     /**
      * @brief Get a pair of the data and the hardware address of partition i.
      */
-    std::pair<gsl::span<T>, u32> Partition(size_t i)
+    std::pair<gsl::span<T>, uint32_t> Partition(size_t i)
     {
       EnsureBounds(i);
-      return std::pair<gsl::span<T>, u32>(UnsafePartitionSpan(i), fAddresses[i]);
+      return std::pair<gsl::span<T>, uint32_t>(UnsafePartitionSpan(i), fAddresses[i]);
     }
 
     /**
      * @brief Get a pair of the data and the hardware address of partition i.
      */
-    std::pair<gsl::span<const T>, u32> Partition(size_t i) const
+    std::pair<gsl::span<const T>, uint32_t> Partition(size_t i) const
     {
       EnsureBounds(i);
-      return std::pair<gsl::span<const T>, u32>(UnsafePartitionSpan(i), fAddresses[i]);
+      return std::pair<gsl::span<const T>, uint32_t>(UnsafePartitionSpan(i), fAddresses[i]);
     }
 
     /**
@@ -221,7 +220,7 @@ namespace cbm::algo
     /**
      * @brief Get the addresses.
      */
-    const std::vector<u32>& Addresses() const { return fAddresses; }
+    const std::vector<uint32_t>& Addresses() const { return fAddresses; }
 
     /**
      * @brief Get the underlying offsets.
@@ -230,9 +229,9 @@ namespace cbm::algo
 
 
    private:
-    Container_t fData;             //< Data
-    std::vector<size_t> fOffsets;  // < Offsets of the partitions in fData
-    std::vector<u32> fAddresses;   //< Hardware addresses of the partitions
+    Container_t fData;                 ///< Data
+    std::vector<size_t> fOffsets;      ///< Offsets of the partitions in fData
+    std::vector<uint32_t> fAddresses;  ///< Addresses of the partitions
 
     void EnsureDimensions() const
     {
@@ -285,6 +284,6 @@ namespace cbm::algo
   template<typename T>
   using PartitionedPODVector = PartitionedVector<T, PODAllocator<T>>;
 
-}  // namespace cbm::algo
+}  // namespace cbm
 
 #endif
diff --git a/core/qa/CMakeLists.txt b/core/qa/CMakeLists.txt
index 9b022d7bb8905345543b90516b6693cfa36a4ec4..18f04ce3ab0afe05d253f99ee659d53a997b7afe 100644
--- a/core/qa/CMakeLists.txt
+++ b/core/qa/CMakeLists.txt
@@ -2,7 +2,6 @@ set(INCLUDE_DIRECTORIES
   ${CMAKE_CURRENT_SOURCE_DIR}/checker
   ${CMAKE_CURRENT_SOURCE_DIR}/report
   ${CMAKE_CURRENT_SOURCE_DIR}
-  ${CMAKE_SOURCE_DIR}/algo/base  # For "algo/base/yaml/*.h" included as relative "yaml/?????.h" to fit install tree
   )
 
 set(SRCS
diff --git a/core/utility/CMakeLists.txt b/core/utility/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..f4e2b1368389a25a1b14d069e756920da0dd4b46
--- /dev/null
+++ b/core/utility/CMakeLists.txt
@@ -0,0 +1,31 @@
+# A library for CBM common utilities. The utilities include generic functions and classes
+# to extend the STL and BOOST C++. The library is not a place for a detector- or analysis-specific
+# software.
+#
+add_subdirectory(yaml)
+
+set(SRCS 
+  CbmEnumDict.cxx
+)
+
+add_library(CbmUtility SHARED ${SRCS})
+
+target_include_directories(CbmUtility
+  PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
+)
+
+target_link_libraries(CbmUtility
+  PUBLIC GSL
+         fmt::fmt
+         xpu
+)
+
+install(TARGETS CbmUtility DESTINATION lib)
+
+install(
+  FILES 
+    CbmEnumDict.h
+  DESTINATION
+    include/
+)
+
diff --git a/algo/base/util/EnumDict.cxx b/core/utility/CbmEnumDict.cxx
similarity index 85%
rename from algo/base/util/EnumDict.cxx
rename to core/utility/CbmEnumDict.cxx
index a0ffd40f360a6bea9e7f3976e08ef7b2c8f44cee..960d54df601821377009fda2e01ec38cea7dc615 100644
--- a/algo/base/util/EnumDict.cxx
+++ b/core/utility/CbmEnumDict.cxx
@@ -1,11 +1,11 @@
 /* Copyright (C) 2023 FIAS Frankfurt Institute for Advanced Studies, Frankfurt / Main
    SPDX-License-Identifier: GPL-3.0-only
    Authors: Felix Weiglhofer [committer] */
-#include "EnumDict.h"
+#include "CbmEnumDict.h"
 
 #include <sstream>
 
-void cbm::algo::detail::RaiseUnknownEntry(std::string_view str, const std::vector<std::string_view>& validEntries)
+void cbm::util::detail::RaiseUnknownEntry(std::string_view str, const std::vector<std::string_view>& validEntries)
 {
   std::ostringstream oss;
   oss << "Could not parse '" << str << "'. Valid entries are: ";
diff --git a/algo/base/util/EnumDict.h b/core/utility/CbmEnumDict.h
similarity index 87%
rename from algo/base/util/EnumDict.h
rename to core/utility/CbmEnumDict.h
index fde2d6d7850590a8c5f089ccad49762dba3c2174..c60ab8a5a0aaaf33186d938424c25c7fa87c6cf3 100644
--- a/algo/base/util/EnumDict.h
+++ b/core/utility/CbmEnumDict.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2023 FIAS Frankfurt Institute for Advanced Studies, Frankfurt / Main
+/* Copyright (C) 2023-2025 FIAS Frankfurt Institute for Advanced Studies, Frankfurt / Main
    SPDX-License-Identifier: GPL-3.0-only
    Authors: Felix Weiglhofer [committer] */
 #ifndef CBM_ALGO_BASE_UTIL_SERIALIZABLEENUM_H
@@ -16,7 +16,7 @@
 #include <fmt/format.h>
 #include <xpu/defines.h>
 
-namespace cbm::algo
+namespace cbm::util
 {
   namespace detail
   {
@@ -68,7 +68,7 @@ namespace cbm::algo
     if (it == dict.end()) throw std::runtime_error(fmt::format("Entry {} for enum missing!", static_cast<int>(t)));
     return it->first;
   }
-}  // namespace cbm::algo
+}  // namespace cbm::util
 
 #if XPU_IS_CPU
 /**
@@ -97,9 +97,9 @@ namespace cbm::algo
    */
 #define CBM_ENUM_DICT(type, ...)                                                                                       \
   template<>                                                                                                           \
-  inline const cbm::algo::detail::EnumDict_t<type> cbm::algo::detail::EnumDict<type> = {__VA_ARGS__};                  \
+  inline const cbm::util::detail::EnumDict_t<type> cbm::util::detail::EnumDict<type> = {__VA_ARGS__};                  \
   template<>                                                                                                           \
-  struct cbm::algo::detail::EnumHasDict<type> : std::true_type {                                                       \
+  struct cbm::util::detail::EnumHasDict<type> : std::true_type {                                                       \
   }
 #else  // XPU_IS_CPU
 // Disable macro in GPU code, causes some issues with nvcc
@@ -110,22 +110,22 @@ namespace cbm::algo
 // Placed in global namespace to be found by ADL e.g. for std::ostream_iterator
 namespace std
 {
-  template<typename T, typename = std::enable_if_t<cbm::algo::detail::EnumHasDict_v<T>>>
+  template<typename T, typename = std::enable_if_t<cbm::util::detail::EnumHasDict_v<T>>>
   std::ostream& operator<<(std::ostream& os, T t)
   {
-    os << cbm::algo::ToString(t);
+    os << cbm::util::ToString(t);
     return os;
   }
 
-  template<typename T, typename = std::enable_if_t<cbm::algo::detail::EnumHasDict_v<T>>>
+  template<typename T, typename = std::enable_if_t<cbm::util::detail::EnumHasDict_v<T>>>
   std::istream& operator>>(std::istream& is, T& t)
   {
     std::string str;
     is >> str;
-    auto maybet = cbm::algo::FromString<T>(str);
+    auto maybet = cbm::util::FromString<T>(str);
 
     if (!maybet) {
-      cbm::algo::detail::RaiseUnknownEntry(str, cbm::algo::detail::ValidEntries<T>());
+      cbm::util::detail::RaiseUnknownEntry(str, cbm::util::detail::ValidEntries<T>());
     }
     t = *maybet;
 
diff --git a/algo/base/yaml/BaseTypes.h b/core/utility/yaml/BaseTypes.h
similarity index 100%
rename from algo/base/yaml/BaseTypes.h
rename to core/utility/yaml/BaseTypes.h
diff --git a/algo/base/yaml/CMakeLists.txt b/core/utility/yaml/CMakeLists.txt
similarity index 68%
rename from algo/base/yaml/CMakeLists.txt
rename to core/utility/yaml/CMakeLists.txt
index 756401d832e849767e03cf1f9b33188c770022b7..992fcfc5cce116f1489160277ab9bde82df1b333 100644
--- a/algo/base/yaml/CMakeLists.txt
+++ b/core/utility/yaml/CMakeLists.txt
@@ -1,15 +1,16 @@
 set(INCLUDE_DIRECTORIES
-  ${CMAKE_SOURCE_DIR}/algo/base  # For "algo/base/yaml/*.h" included as relative "yaml/?????.h" to fit install tree
+  ${CMAKE_CURRENT_SOURCE_DIR}/..  # For inclusions like "yaml/Yaml.h"
 )
 
 add_library(CbmYamlInterface INTERFACE)
 
 target_include_directories(CbmYamlInterface
-  INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
+  INTERFACE ${INCLUDE_DIRECTORIES}
 )
 
 target_link_libraries(CbmYamlInterface
   INTERFACE GSL
+            CbmUtility
             xpu
             fmt::fmt
             external::yaml-cpp
diff --git a/algo/base/yaml/Property.h b/core/utility/yaml/Property.h
similarity index 99%
rename from algo/base/yaml/Property.h
rename to core/utility/yaml/Property.h
index 223a978542779a9499bb7f95d5b273f5238f4d5c..95ef674408e0dc2d3763a9038831dc53f5113daa 100644
--- a/algo/base/yaml/Property.h
+++ b/core/utility/yaml/Property.h
@@ -5,8 +5,6 @@
 #define CBM_YAML_PROPERTY_H
 #pragma once
 
-#include "Definitions.h"
-
 #include <optional>
 #include <string_view>
 #include <tuple>
diff --git a/algo/base/yaml/Yaml.h b/core/utility/yaml/Yaml.h
similarity index 93%
rename from algo/base/yaml/Yaml.h
rename to core/utility/yaml/Yaml.h
index e5dfa3d33adbe63720b129d52f762028b4f00840..13cbbf58d45738675aab0a93e686fdf90a859ee4 100644
--- a/algo/base/yaml/Yaml.h
+++ b/core/utility/yaml/Yaml.h
@@ -5,12 +5,12 @@
 #define CBM_YAML_YAML_H
 #pragma once
 
-#include "Definitions.h"
-#include "compat/Filesystem.h"
-#include "util/EnumDict.h"
+#include "CbmEnumDict.h"
 #include "yaml/BaseTypes.h"
 #include "yaml/Property.h"
 
+#include <boost/filesystem.hpp>
+
 #include <sstream>
 #include <string_view>
 
@@ -19,6 +19,19 @@
 
 namespace cbm::algo::yaml
 {
+  namespace fs = boost::filesystem;
+
+  // typealias for Rust-like fixed size integer types
+  using i8  = std::int8_t;
+  using u8  = std::uint8_t;
+  using i16 = std::int16_t;
+  using u16 = std::uint16_t;
+  using i32 = std::int32_t;
+  using u32 = std::uint32_t;
+  using i64 = std::int64_t;
+  using u64 = std::uint64_t;
+  using f32 = float;
+  using f64 = double;
 
   template<typename T>
   T Read(const YAML::Node& node);
@@ -71,14 +84,14 @@ namespace cbm::algo::yaml
 #endif
     using Type = std::remove_cv_t<std::remove_reference_t<T>>;
 
-    static_assert(!IsEnum<T> || detail::EnumHasDict_v<T>, "Enum must have a dictionary to be deserializable");
+    static_assert(!IsEnum<T> || util::detail::EnumHasDict_v<T>, "Enum must have a dictionary to be deserializable");
 
     // TODO: error handling
     if constexpr (IsFundamental<Type>) {
       return node.as<Type>();
     }
     else if constexpr (IsEnum<Type>) {
-      std::optional<T> maybet = FromString<T>(node.as<std::string>());
+      std::optional<T> maybet = util::FromString<T>(node.as<std::string>());
       if (!maybet) {
         throw std::runtime_error(fmt::format("Invalid enum value: {}", node.as<std::string>()));
       }
@@ -207,7 +220,7 @@ namespace cbm::algo::yaml
     template<typename T>
     void DoDump(const T& object, YAML::Emitter& ss, std::optional<YAML::EMITTER_MANIP> formatEntries = {})
     {
-      static_assert(!IsEnum<T> || detail::EnumHasDict_v<T>, "Enum must have a dictionary");
+      static_assert(!IsEnum<T> || util::detail::EnumHasDict_v<T>, "Enum must have a dictionary");
 
       if constexpr (IsFundamental<T>) {
         // Take care that i8 and u8 are printed as integers not as characters
@@ -217,7 +230,7 @@ namespace cbm::algo::yaml
           ss << object;
       }
       else if constexpr (IsEnum<T>) {
-        ss << std::string{ToString<T>(object)};
+        ss << std::string{util::ToString<T>(object)};
       }
       else if constexpr (IsVector<T> || IsArray<T> || IsSet<T>) {
         ss << YAML::BeginSeq;
diff --git a/reco/steer/CbmSourceRecoTimeslice.cxx b/reco/steer/CbmSourceRecoTimeslice.cxx
index a94565dfcb89715b35a14933601ac87994429c43..f4299055092de891be0f82fdf37f617ace78af89 100644
--- a/reco/steer/CbmSourceRecoTimeslice.cxx
+++ b/reco/steer/CbmSourceRecoTimeslice.cxx
@@ -79,22 +79,22 @@ Bool_t CbmSourceRecoTimeslice::Init()
   };
 
   // TODO: replace individual vectors with reco-timeslice objects
-  fBmonHits = new PartitionedVector<bmon::Hit>();
+  fBmonHits = new cbm::PartitionedVector<bmon::Hit>();
   if (!RegisterVector(fBmonHits, "OnlineBmonHit")) {
     return kFALSE;
   }
 
-  fStsHits = new PartitionedVector<sts::Hit>();
+  fStsHits = new cbm::PartitionedVector<sts::Hit>();
   if (!RegisterVector(fStsHits, "OnlineStsHit")) {
     return kFALSE;
   }
 
-  fTrdHits = new PartitionedVector<trd::Hit>();
+  fTrdHits = new cbm::PartitionedVector<trd::Hit>();
   if (!RegisterVector(fTrdHits, "OnlineTrdHit")) {
     return kFALSE;
   }
 
-  fTofHits = new PartitionedVector<tof::Hit>();
+  fTofHits = new cbm::PartitionedVector<tof::Hit>();
   if (!RegisterVector(fTofHits, "OnlineTofHit")) {
     return kFALSE;
   }
diff --git a/reco/steer/CbmSourceRecoTimeslice.h b/reco/steer/CbmSourceRecoTimeslice.h
index 1d0b907a6e508e3cc4c9d01d105291eef01e9375..1b2763ab2aeec88fa6e824995daf73fdaf99e847 100644
--- a/reco/steer/CbmSourceRecoTimeslice.h
+++ b/reco/steer/CbmSourceRecoTimeslice.h
@@ -81,10 +81,10 @@ class CbmSourceRecoTimeslice : public FairSource {
   void ClearOutputVectors();
 
   //* Data containers
-  cbm::algo::PartitionedVector<cbm::algo::bmon::Hit>* fBmonHits{nullptr};
-  cbm::algo::PartitionedVector<cbm::algo::sts::Hit>* fStsHits{nullptr};
-  cbm::algo::PartitionedVector<cbm::algo::trd::Hit>* fTrdHits{nullptr};
-  cbm::algo::PartitionedVector<cbm::algo::tof::Hit>* fTofHits{nullptr};
+  cbm::PartitionedVector<cbm::algo::bmon::Hit>* fBmonHits{nullptr};
+  cbm::PartitionedVector<cbm::algo::sts::Hit>* fStsHits{nullptr};
+  cbm::PartitionedVector<cbm::algo::trd::Hit>* fTrdHits{nullptr};
+  cbm::PartitionedVector<cbm::algo::tof::Hit>* fTofHits{nullptr};
   cbm::algo::ca::Vector<cbm::algo::ca::Track>* fTracks{nullptr};
   cbm::algo::StorableRecoResults::TrackHitIndexContainer_t* fTrackStsHitIndices{nullptr};
   cbm::algo::StorableRecoResults::TrackHitIndexContainer_t* fTrackTrdHitIndices{nullptr};
diff --git a/reco/tasks/CbmTaskInspectRecoTimeslice.h b/reco/tasks/CbmTaskInspectRecoTimeslice.h
index 16365d5f418975bfbb7a45d22cfb00652f880ff5..d1722a851b26044ae84334c65dc48f46a246a23a 100644
--- a/reco/tasks/CbmTaskInspectRecoTimeslice.h
+++ b/reco/tasks/CbmTaskInspectRecoTimeslice.h
@@ -53,10 +53,10 @@ class CbmTaskInspectRecoTimeslice : public FairTask {
   virtual InitStatus Init();
 
   //* Data containers
-  const cbm::algo::PartitionedVector<cbm::algo::bmon::Hit>* fBmonHits{nullptr};
-  const cbm::algo::PartitionedVector<cbm::algo::sts::Hit>* fStsHits{nullptr};
-  const cbm::algo::PartitionedVector<cbm::algo::trd::Hit>* fTrdHits{nullptr};
-  const cbm::algo::PartitionedVector<cbm::algo::tof::Hit>* fTofHits{nullptr};
+  const cbm::PartitionedVector<cbm::algo::bmon::Hit>* fBmonHits{nullptr};
+  const cbm::PartitionedVector<cbm::algo::sts::Hit>* fStsHits{nullptr};
+  const cbm::PartitionedVector<cbm::algo::trd::Hit>* fTrdHits{nullptr};
+  const cbm::PartitionedVector<cbm::algo::tof::Hit>* fTofHits{nullptr};
   const cbm::algo::ca::Vector<cbm::algo::ca::Track>* fTracks{nullptr};
   const cbm::algo::StorableRecoResults::TrackHitIndexContainer_t* fTrackStsHitIndices{nullptr};
   const cbm::algo::StorableRecoResults::TrackHitIndexContainer_t* fTrackTrdHitIndices{nullptr};