diff --git a/algo/evbuild/EventBuilder.cxx b/algo/evbuild/EventBuilder.cxx
index c901c8a7818046c6f3caacbefc26e41c96ec4e38..253cb8dbf8de431b3b11e0c47f4deaaeb7aae5c2 100644
--- a/algo/evbuild/EventBuilder.cxx
+++ b/algo/evbuild/EventBuilder.cxx
@@ -31,6 +31,7 @@ namespace cbm::algo::evbuild
     monitor.fTrd2d.fNum += ts.fData.fTrd2d.fDigis.size();
     monitor.fTof.fNum += ts.fData.fTof.fDigis.size();
     monitor.fPsd.fNum += ts.fData.fPsd.fDigis.size();
+    monitor.fFsd.fNum += ts.fData.fFsd.fDigis.size();
     monitor.fBmon.fNum += ts.fData.fT0.fDigis.size();
     return result;
   }
@@ -79,6 +80,10 @@ namespace cbm::algo::evbuild
           event.fData.fPsd.fDigis = CopyRange(ts.fData.fPsd.fDigis, tMin, tMax);
           break;
         }
+        case ECbmModuleId::kFsd: {
+          event.fData.fFsd.fDigis = CopyRange(ts.fData.fFsd.fDigis, tMin, tMax);
+          break;
+        }
         case ECbmModuleId::kT0: {
           event.fData.fT0.fDigis = CopyRange(ts.fData.fT0.fDigis, tMin, tMax);
           break;
@@ -93,6 +98,7 @@ namespace cbm::algo::evbuild
     monitor.fTrd2d.fNumInEvents += event.fData.fTrd2d.fDigis.size();
     monitor.fTof.fNumInEvents += event.fData.fTof.fDigis.size();
     monitor.fPsd.fNumInEvents += event.fData.fPsd.fDigis.size();
+    monitor.fFsd.fNumInEvents += event.fData.fFsd.fDigis.size();
     monitor.fBmon.fNumInEvents += event.fData.fT0.fDigis.size();
     return event;
   }
diff --git a/algo/evbuild/EventBuilder.h b/algo/evbuild/EventBuilder.h
index 973a13dd2e5e95277598fba23b5cf363506ed7a9..0b62b53e83ad78f06173a4961552fb359117deaa 100644
--- a/algo/evbuild/EventBuilder.h
+++ b/algo/evbuild/EventBuilder.h
@@ -44,6 +44,7 @@ namespace cbm::algo::evbuild
     EventBuilderDetectorMonitorData fTrd2d;  ///< Monitoring data for TRD2D
     EventBuilderDetectorMonitorData fRich;   ///< Monitoring data for RICH
     EventBuilderDetectorMonitorData fPsd;    ///< Monitoring data for PSD
+    EventBuilderDetectorMonitorData fFsd;    ///< Monitoring data for FSD
   };
 
 
diff --git a/algo/evbuild/EventbuildChain.cxx b/algo/evbuild/EventbuildChain.cxx
index 4674f5f39abc316b46edebb80d539351482c333d..07fed8de83532839ec20b51e9c006f8fe405373b 100644
--- a/algo/evbuild/EventbuildChain.cxx
+++ b/algo/evbuild/EventbuildChain.cxx
@@ -118,6 +118,13 @@ std::vector<double> EventbuildChain::GetDigiTimes(const CbmDigiTimeslice& timesl
       std::transform(it1, it2, result.begin(), [](const CbmPsdDigi& digi) { return digi.GetTime(); });
       break;
     }
+    case ECbmModuleId::kFsd: {
+      result.resize(timeslice.fData.fFsd.fDigis.size());
+      auto it1 = timeslice.fData.fFsd.fDigis.begin();
+      auto it2 = timeslice.fData.fFsd.fDigis.end();
+      std::transform(it1, it2, result.begin(), [](const CbmFsdDigi& digi) { return digi.GetTime(); });
+      break;
+    }
     case ECbmModuleId::kT0: {
       result.resize(timeslice.fData.fT0.fDigis.size());
       auto it1 = timeslice.fData.fT0.fDigis.begin();
diff --git a/algo/params/EventbuildConfig.yaml b/algo/params/EventbuildConfig.yaml
index a7267aff92af8a01b59b421f1825ed392dc9d488..3a37fbccd843d869198a7739a3126a147150ca4e 100644
--- a/algo/params/EventbuildConfig.yaml
+++ b/algo/params/EventbuildConfig.yaml
@@ -12,6 +12,7 @@ eventbuilder:
   TRd2D: [-100, 350]
   TOF:   [-10, 70]
   RICH:  [-20, 120]
+  FSD:   [-50, 150]
 selector:
   minDigis:
     T0: 1
diff --git a/algo/qa/DigiEventQa.cxx b/algo/qa/DigiEventQa.cxx
index f25dfaac320d09f018f521bc84ad5e93b830fd7a..7fb1e011e321cbe65f3f6741582be35bf6c2b538 100644
--- a/algo/qa/DigiEventQa.cxx
+++ b/algo/qa/DigiEventQa.cxx
@@ -51,6 +51,7 @@ namespace cbm::algo::evbuild
       case ECbmModuleId::kTrd2d: FillDeltaT<CbmTrdDigi>(event.fData.fTrd2d.fDigis, event.fTime, histo); break;
       case ECbmModuleId::kTof: FillDeltaT<CbmTofDigi>(event.fData.fTof.fDigis, event.fTime, histo); break;
       case ECbmModuleId::kPsd: FillDeltaT<CbmPsdDigi>(event.fData.fPsd.fDigis, event.fTime, histo); break;
+      case ECbmModuleId::kFsd: FillDeltaT<CbmFsdDigi>(event.fData.fFsd.fDigis, event.fTime, histo); break;
       default: throw std::runtime_error("DigiEventQa: Invalid system Id " + ::ToString(system));
     }
   }
diff --git a/algo/test/_GTestDigiEventSelector.cxx b/algo/test/_GTestDigiEventSelector.cxx
index cfc42fbd16ddcb62e0d8d136d52e494d0f394dc3..32e291a54ada8f52e5828ee8cccca5feca62aaf6 100644
--- a/algo/test/_GTestDigiEventSelector.cxx
+++ b/algo/test/_GTestDigiEventSelector.cxx
@@ -27,6 +27,7 @@ TEST(_GTestDigiEventSelector, CheckDigiEventSelectorAlgorithmSimple)
   size_t nTrd2d = 6;
   size_t nTof   = 7;
   size_t nPsd   = 8;
+  size_t nFsd   = 9;
   CbmDigiEvent event;
   for (size_t i = 0; i < nBmon; i++)
     event.fData.fT0.fDigis.push_back(CbmBmonDigi());
@@ -44,7 +45,8 @@ TEST(_GTestDigiEventSelector, CheckDigiEventSelectorAlgorithmSimple)
     event.fData.fTof.fDigis.push_back(CbmTofDigi());
   for (size_t i = 0; i < nPsd; i++)
     event.fData.fPsd.fDigis.push_back(CbmPsdDigi());
-
+  for (size_t i = 0; i < nFsd; i++)
+    event.fData.fFsd.fDigis.push_back(CbmFsdDigi());
 
   YAML::Node node;
   node["minDigis"][ToString(ECbmModuleId::kT0)]    = nBmon;
@@ -55,6 +57,7 @@ TEST(_GTestDigiEventSelector, CheckDigiEventSelectorAlgorithmSimple)
   node["minDigis"][ToString(ECbmModuleId::kTrd2d)] = nTrd2d;
   node["minDigis"][ToString(ECbmModuleId::kTof)]   = nTof;
   node["minDigis"][ToString(ECbmModuleId::kPsd)]   = nPsd;
+  node["minDigis"][ToString(ECbmModuleId::kFsd)]   = nFsd;
 
   {
     // --- Check with created numbers of digis - should pass
@@ -127,6 +130,13 @@ TEST(_GTestDigiEventSelector, CheckDigiEventSelectorAlgorithmSimple)
     EXPECT_EQ(select(event), false);
   }
 
+  {
+    // --- Increment FSD - should fail
+    node["minDigis"][ToString(ECbmModuleId::kFsd)] = nFsd + 1;
+    cbm::algo::evbuild::DigiEventSelectorConfig config(node);
+    cbm::algo::evbuild::DigiEventSelector select(config);
+    EXPECT_EQ(select(event), false);
+  }
 
   {
     // --- Test number of STS stations
diff --git a/algo/test/_GTestEventBuilder.cxx b/algo/test/_GTestEventBuilder.cxx
index 51646ebeb6e71f5a9d63bb338ec3f6dc8bd2a521..3af5eec0bccdfc5f9a6099fb45ae3016e7b67056 100644
--- a/algo/test/_GTestEventBuilder.cxx
+++ b/algo/test/_GTestEventBuilder.cxx
@@ -20,6 +20,7 @@ TEST(_GTestEventBuilder, CheckEventBuilderAlgorithmSimple)
   configNode[ToString(ECbmModuleId::kTrd)]  = std::pair<double, double> {-45., 45.};
   configNode[ToString(ECbmModuleId::kRich)] = std::pair<double, double> {-45., 45.};
   configNode[ToString(ECbmModuleId::kPsd)]  = std::pair<double, double> {-45., 45.};
+  configNode[ToString(ECbmModuleId::kFsd)]  = std::pair<double, double> {-45., 45.};
   configNode[ToString(ECbmModuleId::kT0)]   = std::pair<double, double> {-45., 45.};
   cbm::algo::evbuild::EventBuilderConfig config(configNode);
   cbm::algo::evbuild::EventBuilder evbuild(config);
@@ -37,6 +38,7 @@ TEST(_GTestEventBuilder, CheckEventBuilderAlgorithmSimple)
       CbmTrdDigi(475, 37, 150, i * inputSpacing, CbmTrdDigi::eTriggerType::kBeginTriggerTypes, 0));
     tsIn.fData.fRich.fDigis.push_back(CbmRichDigi(1111, i * inputSpacing, 1.0));
     tsIn.fData.fPsd.fDigis.push_back(CbmPsdDigi(1111, i * inputSpacing, 1.0));
+    tsIn.fData.fFsd.fDigis.push_back(CbmFsdDigi(1111, i * inputSpacing, 1.0));
     tsIn.fData.fT0.fDigis.push_back(CbmBmonDigi(1111, i * inputSpacing, 1.0));
   }
 
@@ -62,6 +64,7 @@ TEST(_GTestEventBuilder, CheckEventBuilderAlgorithmSimple)
     EXPECT_EQ(eventsOut[i].fData.fTrd.fDigis.size(), 9);
     EXPECT_EQ(eventsOut[i].fData.fRich.fDigis.size(), 9);
     EXPECT_EQ(eventsOut[i].fData.fPsd.fDigis.size(), 9);
+    EXPECT_EQ(eventsOut[i].fData.fFsd.fDigis.size(), 9);
     EXPECT_EQ(eventsOut[i].fData.fT0.fDigis.size(), 9);
     EXPECT_EQ(eventsOut[i].fTime, triggerIn[i]);
   }
@@ -72,6 +75,7 @@ TEST(_GTestEventBuilder, CheckEventBuilderAlgorithmSimple)
   EXPECT_EQ(monitor.fTrd.fNumInEvents, 9 * nTrigger);
   EXPECT_EQ(monitor.fRich.fNumInEvents, 9 * nTrigger);
   EXPECT_EQ(monitor.fPsd.fNumInEvents, 9 * nTrigger);
+  EXPECT_EQ(monitor.fFsd.fNumInEvents, 9 * nTrigger);
   EXPECT_EQ(monitor.fBmon.fNumInEvents, 9 * nTrigger);
 
   EXPECT_EQ(monitor.fMuch.fNum, nInput);
@@ -80,5 +84,6 @@ TEST(_GTestEventBuilder, CheckEventBuilderAlgorithmSimple)
   EXPECT_EQ(monitor.fTrd.fNum, nInput);
   EXPECT_EQ(monitor.fRich.fNum, nInput);
   EXPECT_EQ(monitor.fPsd.fNum, nInput);
+  EXPECT_EQ(monitor.fFsd.fNum, nInput);
   EXPECT_EQ(monitor.fBmon.fNum, nInput);
 }
diff --git a/core/data/fsd/CbmFsdHit.cxx b/core/data/fsd/CbmFsdHit.cxx
index dc814a2de61e3fbf68c28802a4d949bc47c7ce21..f2b63099cca783e5870997dce803fdfd0bc78bfd 100644
--- a/core/data/fsd/CbmFsdHit.cxx
+++ b/core/data/fsd/CbmFsdHit.cxx
@@ -9,6 +9,7 @@
  **/
 #include "CbmFsdHit.h"
 
+#include "CbmFsdAddress.h"
 #include "CbmHit.h"  // for kFSDHIT
 
 #include <Logger.h>  // for Logger, LOG
@@ -19,6 +20,7 @@ CbmFsdHit::CbmFsdHit() : CbmPixelHit(), fUnitId(-1), fModuleId(-1), fEdep(-1)
   SetType(kFSDHIT);
   SetTime(0.);
 }
+
 CbmFsdHit::CbmFsdHit(int32_t unit, int32_t module, double edep)
   : CbmPixelHit()
   , fUnitId(unit)
@@ -29,6 +31,15 @@ CbmFsdHit::CbmFsdHit(int32_t unit, int32_t module, double edep)
   SetTime(0.);
 }
 
+CbmFsdHit::CbmFsdHit(int32_t address, TVector3 pos, TVector3 dpos, int32_t refIndex, double time, double edep)
+  : CbmPixelHit(address, pos, dpos, 0., refIndex, time)
+  , fEdep(edep)
+{
+  SetType(kFSDHIT);
+  SetUnitId(CbmFsdAddress::GetElementId(address, static_cast<int32_t>(CbmFsdAddress::Level::Unit)));
+  SetModuleId(CbmFsdAddress::GetElementId(address, static_cast<int32_t>(CbmFsdAddress::Level::Module)));
+}
+
 
 // -----   Destructor   ----------------------------------------------------
 CbmFsdHit::~CbmFsdHit() {}
diff --git a/core/data/fsd/CbmFsdHit.h b/core/data/fsd/CbmFsdHit.h
index d833bfa8003ab8726d2be21f07cabd6d58482d53..48ef309e7ffd1d1767b5f6b922b8885bc090bcf3 100644
--- a/core/data/fsd/CbmFsdHit.h
+++ b/core/data/fsd/CbmFsdHit.h
@@ -30,6 +30,8 @@ public:
 
   CbmFsdHit(int32_t unit, int32_t module, double edep);
 
+  CbmFsdHit(int32_t address, TVector3 pos, TVector3 dpos, int32_t refIndex, double time, double edep);
+
 
   /**   Destructor   **/
   virtual ~CbmFsdHit();
@@ -52,13 +54,11 @@ public:
 
 private:
   /**   Data members  **/
-
-  int32_t fModuleId;
   int32_t fUnitId;
+  int32_t fModuleId;
   double fEdep;
 
-
-  ClassDef(CbmFsdHit, 1);
+  ClassDef(CbmFsdHit, 2);
 };
 
 
diff --git a/macro/PWG/common/production/run_reco_json_config.C b/macro/PWG/common/production/run_reco_json_config.C
index 40d17232f1ef9541ee64e26103f45cadb8370235..84e1a55c2ed648eea0606fdcbfd4a45434850c70 100644
--- a/macro/PWG/common/production/run_reco_json_config.C
+++ b/macro/PWG/common/production/run_reco_json_config.C
@@ -16,6 +16,7 @@
 #include "CbmBuildEventsQa.h"
 #include "CbmDefs.h"
 #include "CbmFindPrimaryVertex.h"
+#include "CbmFsdHitProducer.h"
 #include "CbmKF.h"
 #include "CbmL1.h"
 #include "CbmL1StsTrackFinder.h"
@@ -166,6 +167,7 @@ void run_reco_json_config(TString input = "", Int_t nTimeSlices = -1, Int_t firs
   Bool_t useTrd     = geo->IsActive(ECbmModuleId::kTrd);
   Bool_t useTof     = geo->IsActive(ECbmModuleId::kTof);
   Bool_t usePsd     = geo->IsActive(ECbmModuleId::kPsd);
+  Bool_t useFsd     = geo->IsActive(ECbmModuleId::kFsd);
   // ------------------------------------------------------------------------
   if (isL1EffQA) isL1Matching = true;
 
@@ -246,6 +248,7 @@ void run_reco_json_config(TString input = "", Int_t nTimeSlices = -1, Int_t firs
       if (!useRich) evBuildRaw->RemoveDetector(kRawEventBuilderDetRich);
       if (!useMuch) evBuildRaw->RemoveDetector(kRawEventBuilderDetMuch);
       if (!usePsd) evBuildRaw->RemoveDetector(kRawEventBuilderDetPsd);
+      if (!useFsd) evBuildRaw->RemoveDetector(kRawEventBuilderDetFsd);
       if (!useTof) evBuildRaw->RemoveDetector(kRawEventBuilderDetTof);
       if (!useTrd) evBuildRaw->RemoveDetector(kRawEventBuilderDetTrd);
       if (!useSts) {
@@ -391,6 +394,18 @@ void run_reco_json_config(TString input = "", Int_t nTimeSlices = -1, Int_t firs
     run->AddTask(psdHit);
     std::cout << "-I- " << myName << ": Added task " << psdHit->GetName() << std::endl;
   }
+  // ------------------------------------------------------------------------
+
+
+  // -----   Local reconstruction in FSD   ----------------------------------
+  if (useFsd) {
+    CbmFsdHitProducer* fsdHit = new CbmFsdHitProducer();
+    run->AddTask(fsdHit);
+    std::cout << "-I- " << myName << ": Added task " << fsdHit->GetName() << std::endl;
+  }
+  // ------------------------------------------------------------------------
+
+
   // -------------- Match MC Hits to Reco Hits--------------------------------
   if (isL1Matching) {
     CbmMatchRecoToMC* match1 = new CbmMatchRecoToMC();
diff --git a/macro/beamtime/mcbm2022/mcbm_event.C b/macro/beamtime/mcbm2022/mcbm_event.C
index 8b5a3f9976496138c8a03eb00bdee0ee452e30b4..3c4cee4161ded05cfa0699430c2f77bec6bb2c81 100644
--- a/macro/beamtime/mcbm2022/mcbm_event.C
+++ b/macro/beamtime/mcbm2022/mcbm_event.C
@@ -436,6 +436,8 @@ Bool_t mcbm_event(std::string infile,
   evBuildRaw->RemoveDetector(kRawEventBuilderDetTrd2D);
   evBuildRaw->RemoveDetector(kRawEventBuilderDetRich);
   evBuildRaw->RemoveDetector(kRawEventBuilderDetPsd);
+  evBuildRaw->RemoveDetector(kRawEventBuilderDetFsd);
+
 
   // Add all 2022 detectors in the right order
   evBuildRaw->AddDetector(kRawEventBuilderDetSts);
diff --git a/macro/reco/reco_config.yaml b/macro/reco/reco_config.yaml
index 0e0860758d53af9bc2d524d965358b6097a9f11e..8940f61e2e7a3c84ccbd65f5e1f8e21ab85de08b 100644
--- a/macro/reco/reco_config.yaml
+++ b/macro/reco/reco_config.yaml
@@ -11,6 +11,7 @@ eventbuilder:
   TRd2D: [-20, 30]
   TOF:   [-20, 30]
   RICH:  [-20, 30]
+  FSD:   [-20, 30]
 selector:
   minDigis:
     T0: 1
diff --git a/macro/run/CMakeLists.txt b/macro/run/CMakeLists.txt
index 18a5f4cd9dc125441aa1f8c9f26abbea580f13e2..699b436cf197ac7cd28b0b3424fd1c59198fe594 100644
--- a/macro/run/CMakeLists.txt
+++ b/macro/run/CMakeLists.txt
@@ -193,7 +193,7 @@ foreach(setup IN LISTS cbm_setup)
   set(binary ${CBMROOT_BINARY_DIR}/bin/cbmreco_offline.sh)
   set(inputname data/${sname}_ev.raw.root)
   set(parfilename data/${sname}_coll.par.root)
-  set(outputname data/dummy.reco.root)
+  set(outputname data/${sname}_offline_ev_ideal.reco.root)
   set(configname ${CBMROOT_SOURCE_DIR}/reco/offline/config/RecoConfig_event_ideal.yaml)
   set(cl_options -i ${inputname} -o ${outputname} -p ${parfilename} -c ${configname} -s ${setup} -w)
   add_test(NAME ${testname} COMMAND ${binary} ${cl_options})
@@ -227,7 +227,7 @@ foreach(setup IN LISTS cbm_setup)
   set(binary ${CBMROOT_BINARY_DIR}/bin/cbmreco_offline.sh)
   set(inputname data/${sname}_ev.raw.root)
   set(parfilename data/${sname}_coll.par.root)
-  set(outputname data/dummy.reco.root)
+  set(outputname data/${sname}_offline_ev_real.reco.root)
   set(configname ${CBMROOT_SOURCE_DIR}/reco/offline/config/RecoConfig_event_real.yaml)
   set(cl_options -i ${inputname} -o ${outputname} -p ${parfilename} -c ${configname} -s ${setup} -w)
   add_test(NAME ${testname} COMMAND ${binary} ${cl_options})
@@ -261,7 +261,7 @@ foreach(setup IN LISTS cbm_setup)
   set(binary ${CBMROOT_BINARY_DIR}/bin/cbmreco_offline.sh)
   set(inputname data/${sname}_ts.raw.root)
   set(parfilename data/${sname}_coll.par.root)
-  set(outputname data/dummy.reco.root)
+  set(outputname data/${sname}_offline_ts_eb_ideal.reco.root)
   set(configname ${CBMROOT_SOURCE_DIR}/reco/offline/config/RecoConfig_event_ideal.yaml)
   set(cl_options -i ${inputname} -o ${outputname} -p ${parfilename} -c ${configname} -s ${setup} -w)
   add_test(NAME ${testname} COMMAND ${binary} ${cl_options})
@@ -295,7 +295,7 @@ foreach(setup IN LISTS cbm_setup)
   set(binary ${CBMROOT_BINARY_DIR}/bin/cbmreco_offline.sh)
   set(inputname data/${sname}_ts.raw.root)
   set(parfilename data/${sname}_coll.par.root)
-  set(outputname data/dummy.reco.root)
+  set(outputname data/${sname}_offline_ts_eb_real.reco.root)
   set(configname ${CBMROOT_SOURCE_DIR}/reco/offline/config/RecoConfig_event_real.yaml)
   set(cl_options -i ${inputname} -o ${outputname} -p ${parfilename} -c ${configname} -s ${setup} -w)
   add_test(NAME ${testname} COMMAND ${binary} ${cl_options})
@@ -344,7 +344,7 @@ foreach(setup IN LISTS cbm_setup)
   set(binary ${CBMROOT_BINARY_DIR}/bin/cbmreco_offline.sh)
   set(inputname data/${sname}_ts.raw.root)
   set(parfilename data/${sname}_coll.par.root)
-  set(outputname data/dummy.reco.root)
+  set(outputname data/${sname}_offline_ts_tb.reco.root)
   set(configname ${CBMROOT_SOURCE_DIR}/reco/offline/config/RecoConfig_timeslice.yaml)
   set(cl_options -i ${inputname} -o ${outputname} -p ${parfilename} -c ${configname} -s ${setup} -w)
   add_test(NAME ${testname} COMMAND ${binary} ${cl_options})
diff --git a/macro/run/run_reco.C b/macro/run/run_reco.C
index fb82b0bcc3ce05b167e53cc4c87ee722a1b97d79..b6fd6fab56621e91252a42a8be412ec5b26f3771 100644
--- a/macro/run/run_reco.C
+++ b/macro/run/run_reco.C
@@ -16,6 +16,7 @@
 #include "CbmBuildEventsQa.h"
 #include "CbmDefs.h"
 #include "CbmFindPrimaryVertex.h"
+#include "CbmFsdHitProducer.h"
 #include "CbmKF.h"
 #include "CbmL1.h"
 #include "CbmL1StsTrackFinder.h"
@@ -146,6 +147,7 @@ void run_reco(TString input = "", Int_t nTimeSlices = -1, Int_t firstTimeSlice =
   Bool_t useTrd     = geo->IsActive(ECbmModuleId::kTrd);
   Bool_t useTof     = geo->IsActive(ECbmModuleId::kTof);
   Bool_t usePsd     = geo->IsActive(ECbmModuleId::kPsd);
+  Bool_t useFsd     = geo->IsActive(ECbmModuleId::kFsd);
   // ------------------------------------------------------------------------
 
 
@@ -225,6 +227,7 @@ void run_reco(TString input = "", Int_t nTimeSlices = -1, Int_t firstTimeSlice =
       if (!useRich) evBuildRaw->RemoveDetector(kRawEventBuilderDetRich);
       if (!useMuch) evBuildRaw->RemoveDetector(kRawEventBuilderDetMuch);
       if (!usePsd) evBuildRaw->RemoveDetector(kRawEventBuilderDetPsd);
+      if (!useFsd) evBuildRaw->RemoveDetector(kRawEventBuilderDetFsd);
       if (!useTof) evBuildRaw->RemoveDetector(kRawEventBuilderDetTof);
       if (!useTrd) evBuildRaw->RemoveDetector(kRawEventBuilderDetTrd);
       if (!useSts) {
@@ -374,6 +377,14 @@ void run_reco(TString input = "", Int_t nTimeSlices = -1, Int_t firstTimeSlice =
     std::cout << "-I- " << myName << ": Added task " << psdHit->GetName() << std::endl;
   }
   // ------------------------------------------------------------------------
+
+  // -----   Local reconstruction in FSD   ----------------------------------
+  if (useFsd) {
+    CbmFsdHitProducer* fsdHit = new CbmFsdHitProducer();
+    run->AddTask(fsdHit);
+    std::cout << "-I- " << myName << ": Added task " << fsdHit->GetName() << std::endl;
+  }
+  // ------------------------------------------------------------------------
   if (debugWithMC) {
     CbmMatchRecoToMC* match1 = new CbmMatchRecoToMC();
     run->AddTask(match1);
diff --git a/reco/detectors/CMakeLists.txt b/reco/detectors/CMakeLists.txt
index a7b2f7391d93ae7bdc6a56b738898e16e7f8bf31..3b70d1eded69c93bc705a925f5e989f8ccf731e7 100644
--- a/reco/detectors/CMakeLists.txt
+++ b/reco/detectors/CMakeLists.txt
@@ -5,6 +5,7 @@ add_subdirectory(bmon)
 add_subdirectory(much)
 add_subdirectory(mvd)
 add_subdirectory(psd)
+add_subdirectory(fsd)
 add_subdirectory(rich)
 add_subdirectory(sts)
 add_subdirectory(tof)
diff --git a/reco/detectors/fsd/CMakeLists.txt b/reco/detectors/fsd/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..bdd4d43733ad186c414b31b432469ca0785fa8ec
--- /dev/null
+++ b/reco/detectors/fsd/CMakeLists.txt
@@ -0,0 +1,31 @@
+set(INCLUDE_DIRECTORIES
+  ${CMAKE_CURRENT_SOURCE_DIR}
+  ${CMAKE_CURRENT_SOURCE_DIR}/unpack
+  )
+
+set(SRCS
+  CbmFsdHitProducer.cxx
+)
+
+
+set(LIBRARY_NAME CbmFsdReco)
+set(LINKDEF ${LIBRARY_NAME}LinkDef.h)
+set(PUBLIC_DEPENDENCIES
+  CbmBase
+  CbmData
+  CbmRecoBase
+  FairRoot::Base
+  ROOT::Core
+  )
+
+set(PRIVATE_DEPENDENCIES
+  CbmFsdBase
+  FairLogger::FairLogger
+  ROOT::Physics
+  )
+
+set(INTERFACE_DEPENDENCIES
+  external::fles_ipc
+  )
+
+generate_cbm_library()
diff --git a/reco/detectors/fsd/CbmFsdHitProducer.cxx b/reco/detectors/fsd/CbmFsdHitProducer.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..8b50cef22308cecdec580115904a10b20a4b9122
--- /dev/null
+++ b/reco/detectors/fsd/CbmFsdHitProducer.cxx
@@ -0,0 +1,263 @@
+/* Copyright (C) 2023 Physikalisches Institut, Eberhard Karls Universitaet Tuebingen, Tuebingen
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Lukas Chlad [committer] */
+
+#include "CbmFsdHitProducer.h"
+
+#include "CbmDigiManager.h"
+#include "CbmEvent.h"
+#include "CbmFsdAddress.h"
+#include "CbmFsdDetectorSpecs.h"
+#include "CbmFsdDigi.h"
+#include "CbmFsdGeoHandler.h"
+#include "CbmFsdHit.h"
+
+#include <FairRootManager.h>
+#include <Logger.h>
+
+#include <TClonesArray.h>
+#include <TStopwatch.h>
+#include <TVector3.h>
+
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+#include <map>
+#include <tuple>
+
+using std::cout;
+using std::endl;
+using std::fixed;
+using std::left;
+using std::pair;
+using std::right;
+using std::setprecision;
+using std::setw;
+using std::stringstream;
+
+
+// -----   Default constructor   -------------------------------------------
+CbmFsdHitProducer::CbmFsdHitProducer() : FairTask("FsdHitProducer") {}
+// -------------------------------------------------------------------------
+
+
+// -----   Destructor   ----------------------------------------------------
+CbmFsdHitProducer::~CbmFsdHitProducer()
+{
+  if (fHitArray) {
+    fHitArray->Delete();
+    delete fHitArray;
+  }
+}
+// -------------------------------------------------------------------------
+
+
+// -----   Public method Init   --------------------------------------------
+InitStatus CbmFsdHitProducer::Init()
+{
+  std::cout << std::endl << std::endl;
+  LOG(info) << "=======   " << GetName() << ": Init   =====================";
+
+  // --- Initialize the mapping between loaded Geometry and ModuleData
+  CbmFsdGeoHandler::GetInstance().InitMaps();  // singleton first instance initialization of geometry to maps
+
+  // --- Get RootManager
+  FairRootManager* ioman = FairRootManager::Instance();
+  if (!ioman) {
+    LOG(fatal) << "-W- CbmFsdHitProducer::Init: RootManager not instantised!";  //FLORIAN & SELIM
+    return kFATAL;
+  }
+
+  // --- Get event array, if present
+  fEvents = dynamic_cast<TClonesArray*>(ioman->GetObject("Event"));
+  if (fEvents) LOG(info) << GetName() << ": found Event branch, run event-by-event";
+  else {
+    fEvents = dynamic_cast<TClonesArray*>(ioman->GetObject("CbmEvent"));
+    if (fEvents) LOG(info) << GetName() << ": found CbmEvent branch, run event-by-event";
+  }
+  if (!fEvents) {
+    LOG(info) << GetName() << ": no event branch found; run in time-based mode";
+    LOG(warn) << "*** Note that the current FSD hit producer is not suited for time-based reconstruction!";
+    LOG(warn) << "*** Unless you have run the simulation event-by-event, it will not produce sensible results.";
+  }
+
+  // --- Initialise digi manager
+  fDigiMan = CbmDigiManager::Instance();
+  fDigiMan->Init();
+  if (!fDigiMan->IsPresent(ECbmModuleId::kFsd)) {
+    LOG(error) << GetName() << ": No FsdDigi input array present; "
+               << "task will be inactive.";
+    return kERROR;
+  }
+  LOG(info) << GetName() << ": found FsdDigi branch";
+
+  // --- Create and register output array
+  fHitArray = new TClonesArray("CbmFsdHit", 1000);
+  ioman->Register("FsdHit", "FSD", fHitArray, IsOutputBranchPersistent("FsdHit"));
+  fHitArray->Dump();
+  LOG(info) << GetName() << ": registered branch FsdHit";
+  LOG(info) << GetName() << ": intialisation successfull";
+  LOG(info) << "======================================================\n";
+
+  return kSUCCESS;
+}
+// -------------------------------------------------------------------------
+
+
+// -----   Public method Exec   --------------------------------------------
+void CbmFsdHitProducer::Exec(Option_t* /*opt*/)
+{
+
+  // --- Timer
+  TStopwatch timer;
+  timer.Start();
+
+  // --- Reset output array
+  Reset();
+
+  // --- Local variables
+  pair<Int_t, Int_t> result;
+  Int_t nEvents   = 0;
+  Int_t nDigis    = 0;
+  Int_t nHits     = 0;
+  Int_t nDigisAll = fDigiMan->GetNofDigis(ECbmModuleId::kFsd);
+  CbmEvent* event = nullptr;
+
+  // --- Time-slice mode: process entire digi array
+  if (!fEvents) {
+    result = ProcessData(nullptr);
+    nDigis = result.first;
+    nHits  = result.second;
+  }
+
+  // --- Event mode: loop over events and process digis within
+  else {
+    assert(fEvents);
+    nEvents = fEvents->GetEntriesFast();
+    for (Int_t iEvent = 0; iEvent < nEvents; iEvent++) {
+      event  = static_cast<CbmEvent*>(fEvents->At(iEvent));
+      result = ProcessData(event);
+      LOG(debug) << GetName() << ": Event " << iEvent << " / " << nEvents << ", digis " << result.first << ", hits "
+                 << result.second;
+      nDigis += result.first;
+      nHits += result.second;
+    }  //# events
+  }    //? event mode
+
+  // --- Timeslice log and statistics
+  timer.Stop();
+  stringstream logOut;
+  logOut << setw(20) << left << GetName() << " [";
+  logOut << fixed << setw(8) << setprecision(1) << right << timer.RealTime() * 1000. << " ms] ";
+  logOut << "TS " << fNofTs;
+  if (fEvents) logOut << ", events " << nEvents;
+  logOut << ", digis " << nDigis << " / " << nDigisAll;
+  logOut << ", hits " << nHits;
+  LOG(info) << logOut.str();
+  fNofTs++;
+  fNofEvents += nEvents;
+  fNofDigis += nDigis;
+  fNofHits += nHits;
+  fTimeTot += timer.RealTime();
+}
+// -------------------------------------------------------------------------
+
+
+// -----   End-of-timeslice action   ---------------------------------------
+void CbmFsdHitProducer::Finish()
+{
+
+  std::cout << std::endl;
+  LOG(info) << "=====================================";
+  LOG(info) << GetName() << ": Run summary";
+  LOG(info) << "Time slices     : " << fNofTs;
+  LOG(info) << "Digis / TS      : " << fixed << setprecision(2) << Double_t(fNofDigis) / Double_t(fNofTs);
+  LOG(info) << "Hits  / TS      : " << fixed << setprecision(2) << Double_t(fNofHits) / Double_t(fNofTs);
+  LOG(info) << "Time  / TS      : " << fixed << setprecision(2) << 1000. * fTimeTot / Double_t(fNofTs) << " ms";
+  if (fEvents) {
+    LOG(info) << "Events          : " << fNofEvents;
+    LOG(info) << "Events / TS     : " << fixed << setprecision(2) << Double_t(fNofEvents) / Double_t(fNofTs);
+    LOG(info) << "Digis  / event  : " << fixed << setprecision(2) << Double_t(fNofDigis) / Double_t(fNofEvents);
+    LOG(info) << "Hits   / event  : " << fixed << setprecision(2) << Double_t(fNofHits) / Double_t(fNofEvents);
+  }
+  LOG(info) << "=====================================\n";
+}
+// -------------------------------------------------------------------------
+
+
+// -----   Data processing   -----------------------------------------------
+pair<Int_t, Int_t> CbmFsdHitProducer::ProcessData(CbmEvent* event)
+{
+
+  // --- Local variables
+  std::map<uint32_t, std::tuple<Double_t, Double_t, Int_t>>
+    edeptimemap;  // sum of energy deposition, earliest time & digi index per address, key is digi address
+  Int_t nHits            = 0;
+  const CbmFsdDigi* digi = nullptr;
+  Int_t nDigis = (event ? event->GetNofData(ECbmDataType::kFsdDigi) : fDigiMan->GetNofDigis(ECbmModuleId::kFsd));
+
+  // --- Loop over FsdDigis. Distribute energy deposition into the modules.
+  Int_t digiIndex = -1;
+  for (Int_t iDigi = 0; iDigi < nDigis; iDigi++) {
+
+    digiIndex = (event ? event->GetIndex(ECbmDataType::kFsdDigi, iDigi) : iDigi);
+    digi      = fDigiMan->Get<const CbmFsdDigi>(digiIndex);
+    assert(digi);
+
+    uint32_t address   = digi->GetAddress();  // this contains more information than just module id
+    Double_t eDep      = (Double_t) digi->GetEdep();
+    Double_t digiTime  = (Double_t) digi->GetTime();
+    auto insert_result = edeptimemap.insert(std::make_pair(address, std::make_tuple(eDep, digiTime, digiIndex)));
+    if (!insert_result.second) {  // entry was here before
+      std::get<0>((*insert_result.first).second) = eDep + std::get<0>((*insert_result.first).second);
+      if (digiTime < std::get<1>((*insert_result.first).second)) {  // earlier digi => overwrite
+        std::get<1>((*insert_result.first).second) = digiTime;
+        std::get<2>((*insert_result.first).second) = digiIndex;
+      }
+    }  // ? exist entry
+  }    // # digis
+
+  // --- Create hits, one per activated module
+  UInt_t hitIndex = -1;
+  for (auto entry : edeptimemap) {
+    uint32_t address         = entry.first;
+    Double_t sumEdep         = std::get<0>(entry.second);
+    Double_t fastestDigiTime = std::get<1>(entry.second);
+    Int_t fastestDigiIndex   = std::get<2>(entry.second);
+
+    CbmFsdModuleSpecs* fsdModSpecs = CbmFsdGeoHandler::GetInstance().GetModuleSpecsById(
+      CbmFsdAddress::GetElementId(address, static_cast<int32_t>(CbmFsdAddress::Level::Module)));
+    if (!fsdModSpecs) {
+      LOG(warning) << "FSD module data for address: " << address << " (unitId: "
+                   << CbmFsdAddress::GetElementId(address, static_cast<int32_t>(CbmFsdAddress::Level::Unit))
+                   << " ,moduleId: "
+                   << CbmFsdAddress::GetElementId(address, static_cast<int32_t>(CbmFsdAddress::Level::Module))
+                   << " ) could not be found! => skip";
+      continue;
+    }
+    TVector3 moduleCenter;
+    moduleCenter.SetXYZ(fsdModSpecs->fX, fsdModSpecs->fY, fsdModSpecs->fZ);
+    TVector3 moduleDimensions;
+    moduleDimensions.SetXYZ(fsdModSpecs->dX, fsdModSpecs->dY, fsdModSpecs->dZ);
+
+    hitIndex = fHitArray->GetEntriesFast();
+    new ((*fHitArray)[hitIndex]) CbmFsdHit(static_cast<int32_t>(address), moduleCenter, moduleDimensions,
+                                           static_cast<int32_t>(fastestDigiIndex), fastestDigiTime, sumEdep);
+    if (event) event->AddData(ECbmDataType::kFsdHit, hitIndex);
+    nHits++;
+  }
+
+  return std::make_pair(nDigis, nHits);
+}
+// -------------------------------------------------------------------------
+
+
+// -----   Reset: clear output array   -------------------------------------
+void CbmFsdHitProducer::Reset()
+{
+  if (fHitArray) fHitArray->Delete();
+}
+// -------------------------------------------------------------------------
+
+
+ClassImp(CbmFsdHitProducer)
diff --git a/reco/detectors/fsd/CbmFsdHitProducer.h b/reco/detectors/fsd/CbmFsdHitProducer.h
new file mode 100644
index 0000000000000000000000000000000000000000..c59b21359230a297c20858a6504811d0661ed5be
--- /dev/null
+++ b/reco/detectors/fsd/CbmFsdHitProducer.h
@@ -0,0 +1,86 @@
+/* Copyright (C) 2023 Physikalisches Institut, Eberhard Karls Universitaet Tuebingen, Tuebingen
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Lukas Chlad [committer] */
+
+/**
+ * @file CbmFsdHitProducer.h
+ * @author Lukas Chlad (l.chlad@gsi.de)
+ * @brief Hit Producer for FSD
+ * @date 2023-08-09
+ * 
+ * The ideal hit producer produces hits of type CbmFsdHit as full energy deposited in 1 module
+ * 
+ */
+
+#ifndef CBMFSDHITPRODUCER_H
+#define CBMFSDHITPRODUCER_H
+
+#include "CbmDigiManager.h"
+
+#include <FairTask.h>
+
+#include <RtypesCore.h>  // for Int_t, etc.
+
+#include <utility>
+
+class TVector3;
+class TClonesArray;
+class CbmEvent;
+
+class CbmFsdHitProducer : public FairTask {
+
+public:
+  /** \brief Default constructor **/
+  CbmFsdHitProducer();
+
+
+  /** \brief Destructor **/
+  ~CbmFsdHitProducer();
+
+  /** \brief Copy constructor **/
+  CbmFsdHitProducer(const CbmFsdHitProducer&) = delete;
+
+  /** \brief Assignment operator **/
+  CbmFsdHitProducer operator=(const CbmFsdHitProducer&) = delete;
+
+
+  /** \brief Virtual method Init **/
+  virtual InitStatus Init();
+
+
+  /** \brief Virtual method Exec **/
+  virtual void Exec(Option_t* opt);
+
+  /** \brief Virtual method Finish **/
+  virtual void Finish();
+
+private:
+  Int_t fNHits = 0;
+
+  /** Output array of CbmFsdHit **/
+  TClonesArray* fHitArray = nullptr;
+
+  /** Digi Manager for input **/
+  CbmDigiManager* fDigiMan = nullptr;  //!
+
+  /** Event array **/
+  TClonesArray* fEvents = nullptr;  //!
+
+  /**
+   * Processblock of data either event-by-event or CbmEvent
+   */
+  std::pair<Int_t, Int_t> ProcessData(CbmEvent* event);
+
+  /** Counters **/
+  ULong64_t fNofTs     = 0;
+  ULong64_t fNofEvents = 0;
+  ULong64_t fNofDigis  = 0;
+  ULong64_t fNofHits   = 0;
+  Double_t fTimeTot    = 0.;
+
+  void Reset();
+
+  ClassDef(CbmFsdHitProducer, 1);
+};
+
+#endif  // CBMFSDHITPRODUCER_H
diff --git a/reco/detectors/fsd/CbmFsdRecoLinkDef.h b/reco/detectors/fsd/CbmFsdRecoLinkDef.h
new file mode 100644
index 0000000000000000000000000000000000000000..73ed8d8b76ce0adc20d46491e1b5a30314e10098
--- /dev/null
+++ b/reco/detectors/fsd/CbmFsdRecoLinkDef.h
@@ -0,0 +1,13 @@
+/* Copyright (C) 2023 Physikalisches Institut, Eberhard Karls Universitaet Tuebingen, Tuebingen
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Lukas Chlad [committer] */
+
+#ifdef __CINT__
+
+#pragma link off all globals;
+#pragma link off all classes;
+#pragma link off all functions;
+
+#pragma link C++ class CbmFsdHitProducer + ;
+
+#endif
diff --git a/reco/eventbuilder/digis/CbmAlgoBuildRawEvents.cxx b/reco/eventbuilder/digis/CbmAlgoBuildRawEvents.cxx
index 1d990db2db721b3495c5ced61841163c110a4b41..93f85a2b0f170c53313c8f46eabc161aa8f6e663 100644
--- a/reco/eventbuilder/digis/CbmAlgoBuildRawEvents.cxx
+++ b/reco/eventbuilder/digis/CbmAlgoBuildRawEvents.cxx
@@ -7,6 +7,7 @@
 /// CBM headers
 #include "CbmBmonDigi.h"
 #include "CbmEvent.h"
+#include "CbmFsdDigi.h"
 #include "CbmMuchBeamTimeDigi.h"
 #include "CbmMuchDigi.h"
 #include "CbmPsdDigi.h"
@@ -246,6 +247,10 @@ void CbmAlgoBuildRawEvents::BuildEvents()
       LoopOnSeeds<CbmPsdDigi>();
       break;
     }
+    case ECbmModuleId::kFsd: {
+      LoopOnSeeds<CbmFsdDigi>();
+      break;
+    }
     case ECbmModuleId::kT0: {
       LoopOnSeeds<CbmBmonDigi>();
       break;
@@ -465,6 +470,11 @@ const CbmPsdDigi* CbmAlgoBuildRawEvents::GetDigi(UInt_t uDigi)
   return &((*fPsdDigis)[uDigi]);
 }
 template<>
+const CbmFsdDigi* CbmAlgoBuildRawEvents::GetDigi(UInt_t uDigi)
+{
+  return &((*fFsdDigis)[uDigi]);
+}
+template<>
 const CbmBmonDigi* CbmAlgoBuildRawEvents::GetDigi(UInt_t uDigi)
 {
   return &((*fT0Digis)[uDigi]);
@@ -504,6 +514,10 @@ void CbmAlgoBuildRawEvents::SearchMatches(Double_t dSeedTime, RawEventBuilderDet
       SearchMatches<CbmPsdDigi>(dSeedTime, detMatch);
       break;
     }
+    case ECbmModuleId::kFsd: {
+      SearchMatches<CbmFsdDigi>(dSeedTime, detMatch);
+      break;
+    }
     case ECbmModuleId::kT0: {
       SearchMatches<CbmBmonDigi>(dSeedTime, detMatch);
       break;
@@ -775,6 +789,12 @@ Bool_t CbmAlgoBuildRawEvents::CheckTriggerConditions(CbmEvent* event, const RawE
         return kFALSE;
         break;
       }
+      case ECbmModuleId::kFsd: {
+        LOG(fatal) << "CbmAlgoBuildRawEvents::CheckTriggerConditions => Fired layers check not implemented yet for "
+                   << det.sName;
+        return kFALSE;
+        break;
+      }
       case ECbmModuleId::kT0: {
         LOG(fatal) << "CbmAlgoBuildRawEvents::CheckTriggerConditions => Fired layers check not implemented yet for "
                    << det.sName;
@@ -830,6 +850,9 @@ bool CbmAlgoBuildRawEvents::CheckDataAvailable(ECbmModuleId detId)
     case ECbmModuleId::kPsd: {
       return fPsdDigis != nullptr;
     }
+    case ECbmModuleId::kFsd: {
+      return fFsdDigis != nullptr;
+    }
     case ECbmModuleId::kT0: {
       return fT0Digis != nullptr;
     }
@@ -866,6 +889,9 @@ UInt_t CbmAlgoBuildRawEvents::GetNofDigis(ECbmModuleId detId)
     case ECbmModuleId::kPsd: {
       return fPsdDigis->size();
     }
+    case ECbmModuleId::kFsd: {
+      return fFsdDigis->size();
+    }
     case ECbmModuleId::kT0: {
       return fT0Digis->size();
     }
@@ -901,6 +927,9 @@ uint64_t CbmAlgoBuildRawEvents::GetSizeFromDigisNb(ECbmModuleId detId, uint64_t
     case ECbmModuleId::kPsd: {
       return ulNbDigis * sizeof(CbmPsdDigi);
     }
+    case ECbmModuleId::kFsd: {
+      return ulNbDigis * sizeof(CbmFsdDigi);
+    }
     case ECbmModuleId::kT0: {
       return ulNbDigis * sizeof(CbmBmonDigi);
     }
@@ -1289,6 +1318,12 @@ void CbmAlgoBuildRawEvents::FillHistos()
             dTimeDiff = pDigi->GetTime() - evt->GetStartTime();
             break;
           }
+          case ECbmDataType::kFsdDigi: {
+            auto pDigi = GetDigi<CbmFsdDigi>(idx);  // FIXME, need to find the proper digi template
+            if (nullptr == pDigi) continue;
+            dTimeDiff = pDigi->GetTime() - evt->GetStartTime();
+            break;
+          }
           default: LOG(error) << "Unkown dataType " << fvDets[uDetIdx].dataType;
         }
 
@@ -1360,6 +1395,12 @@ void CbmAlgoBuildRawEvents::FillHistos()
               dTimeDiff = pDigi->GetTime() - evt->GetStartTime();
               break;
             }
+            case ECbmDataType::kFsdDigi: {
+              auto pDigi = GetDigi<CbmFsdDigi>(idx);  // FIXME, need to find the proper digi template
+              if (nullptr == pDigi) continue;
+              dTimeDiff = pDigi->GetTime() - evt->GetStartTime();
+              break;
+            }
             default: LOG(error) << "Unkown dataType " << fRefDet.dataType;
           }
 
diff --git a/reco/eventbuilder/digis/CbmAlgoBuildRawEvents.h b/reco/eventbuilder/digis/CbmAlgoBuildRawEvents.h
index f17e0ee2711b6843a4fb576dcd13a5c843a2e518..ea8fbc7fa2380db48bded87f3d83f4c98d1aa908 100644
--- a/reco/eventbuilder/digis/CbmAlgoBuildRawEvents.h
+++ b/reco/eventbuilder/digis/CbmAlgoBuildRawEvents.h
@@ -27,6 +27,7 @@ class CbmEvent;
 class CbmMuchDigi;
 class CbmMuchBeamTimeDigi;
 class CbmPsdDigi;
+class CbmFsdDigi;
 class CbmRichDigi;
 class CbmStsDigi;
 class CbmTofDigi;
@@ -113,6 +114,8 @@ static const RawEventBuilderDetector kRawEventBuilderDetRich =
   RawEventBuilderDetector(ECbmModuleId::kRich, ECbmDataType::kRichDigi, "Rich");
 static const RawEventBuilderDetector kRawEventBuilderDetPsd =
   RawEventBuilderDetector(ECbmModuleId::kPsd, ECbmDataType::kPsdDigi, "Psd");
+static const RawEventBuilderDetector kRawEventBuilderDetFsd =
+  RawEventBuilderDetector(ECbmModuleId::kFsd, ECbmDataType::kFsdDigi, "Fsd");
 static const RawEventBuilderDetector kRawEventBuilderDetT0 =
   RawEventBuilderDetector(ECbmModuleId::kT0, ECbmDataType::kT0Digi, "T0");
 static const RawEventBuilderDetector kRawEventBuilderDetUndef = RawEventBuilderDetector();
@@ -207,6 +210,7 @@ public:
   void SetDigis(std::vector<CbmTofDigi>* TofDigis) { fTofDigis = TofDigis; }
   void SetDigis(std::vector<CbmRichDigi>* RichDigis) { fRichDigis = RichDigis; }
   void SetDigis(std::vector<CbmPsdDigi>* PsdDigis) { fPsdDigis = PsdDigis; }
+  void SetDigis(std::vector<CbmFsdDigi>* FsdDigis) { fFsdDigis = FsdDigis; }
   void SetDigis(std::vector<CbmMuchBeamTimeDigi>* MuchBeamTimeDigis)
   {
     fMuchBeamTimeDigis    = MuchBeamTimeDigis;
@@ -277,7 +281,7 @@ private:
   RawEventBuilderDetector fRefDet             = kRawEventBuilderDetT0;
   std::vector<RawEventBuilderDetector> fvDets = {
     kRawEventBuilderDetSts, kRawEventBuilderDetMuch, kRawEventBuilderDetTrd, kRawEventBuilderDetTrd2D,
-    kRawEventBuilderDetTof, kRawEventBuilderDetRich, kRawEventBuilderDetPsd};
+    kRawEventBuilderDetTof, kRawEventBuilderDetRich, kRawEventBuilderDetPsd, kRawEventBuilderDetFsd};
 
   Double_t fdEarliestTimeWinBeg = kdDefaultTimeWinBeg;
   Double_t fdLatestTimeWinEnd   = kdDefaultTimeWinEnd;
@@ -301,6 +305,7 @@ private:
   const std::vector<CbmTofDigi>* fTofDigis                   = nullptr;
   const std::vector<CbmRichDigi>* fRichDigis                 = nullptr;
   const std::vector<CbmPsdDigi>* fPsdDigis                   = nullptr;
+  const std::vector<CbmFsdDigi>* fFsdDigis                   = nullptr;
 
   // If explicit seed times are supplied.
   const std::vector<Double_t>* fSeedTimes = nullptr;
diff --git a/reco/eventbuilder/digis/CbmBuildEventsIdeal.cxx b/reco/eventbuilder/digis/CbmBuildEventsIdeal.cxx
index 0fb39184101ef81328331c4a4ecb5a1f7e52ba76..4299e9bd896396b71a21684db272bb43a51a7481 100644
--- a/reco/eventbuilder/digis/CbmBuildEventsIdeal.cxx
+++ b/reco/eventbuilder/digis/CbmBuildEventsIdeal.cxx
@@ -97,6 +97,7 @@ void CbmBuildEventsIdeal::Exec(Option_t*)
       case ECbmModuleId::kTrd: digiType = ECbmDataType::kTrdDigi; break;
       case ECbmModuleId::kTof: digiType = ECbmDataType::kTofDigi; break;
       case ECbmModuleId::kPsd: digiType = ECbmDataType::kPsdDigi; break;
+      case ECbmModuleId::kFsd: digiType = ECbmDataType::kFsdDigi; break;
       case ECbmModuleId::kT0: digiType = ECbmDataType::kT0Digi; break;
       default: break;
     }  //? detector
diff --git a/reco/eventbuilder/digis/CbmBuildEventsIdealNew.cxx b/reco/eventbuilder/digis/CbmBuildEventsIdealNew.cxx
index 2c0adc016c29fbf64d4c944632f9ec35793d2f42..919d6752de5652bf18ccf8b15d5e0ed8080e1a6e 100644
--- a/reco/eventbuilder/digis/CbmBuildEventsIdealNew.cxx
+++ b/reco/eventbuilder/digis/CbmBuildEventsIdealNew.cxx
@@ -12,6 +12,7 @@
 #include "CbmDigiManager.h"
 #include "CbmEvent.h"
 #include "CbmEventStore.h"
+#include "CbmFsdDigi.h"
 #include "CbmLink.h"
 #include "CbmMatch.h"
 #include "CbmModuleList.h"
@@ -125,6 +126,11 @@ void CbmBuildEventsIdealNew::Exec(Option_t*)
           event->AddDigi<CbmPsdDigi>(digi, match);
           break;
         }
+        case ECbmModuleId::kFsd: {
+          const CbmFsdDigi* digi = fDigiMan->Get<CbmFsdDigi>(iDigi);
+          event->AddDigi<CbmFsdDigi>(digi, match);
+          break;
+        }
         default: break;
       }  //? detector
 
diff --git a/reco/eventbuilder/digis/CbmBuildEventsQa.cxx b/reco/eventbuilder/digis/CbmBuildEventsQa.cxx
index 6208af5764062ac89787f7c30870ec4dda7375d6..e13675027829d9cb984e5cc0144d23df046ee9e5 100644
--- a/reco/eventbuilder/digis/CbmBuildEventsQa.cxx
+++ b/reco/eventbuilder/digis/CbmBuildEventsQa.cxx
@@ -469,6 +469,7 @@ ECbmDataType CbmBuildEventsQa::GetDigiType(ECbmModuleId system)
     case ECbmModuleId::kTrd: return ECbmDataType::kTrdDigi;
     case ECbmModuleId::kTof: return ECbmDataType::kTofDigi;
     case ECbmModuleId::kPsd: return ECbmDataType::kPsdDigi;
+    case ECbmModuleId::kFsd: return ECbmDataType::kFsdDigi;
     default: return ECbmDataType::kUnknown;
   }
 }
diff --git a/reco/eventbuilder/digis/CbmSeedFinderSlidingWindow.cxx b/reco/eventbuilder/digis/CbmSeedFinderSlidingWindow.cxx
index 065039eacc364202a5c4d76f0c1f67f40cbea69b..cb92bebb5c116bf31e4d3441d5d8771ed8a09ed7 100644
--- a/reco/eventbuilder/digis/CbmSeedFinderSlidingWindow.cxx
+++ b/reco/eventbuilder/digis/CbmSeedFinderSlidingWindow.cxx
@@ -5,6 +5,7 @@
 #include "CbmSeedFinderSlidingWindow.h"
 
 #include "CbmBmonDigi.h"
+#include "CbmFsdDigi.h"
 #include "CbmMCEventList.h"
 #include "CbmMatch.h"
 #include "CbmMuchBeamTimeDigi.h"
@@ -132,6 +133,7 @@ template void CbmSeedFinderSlidingWindow::FillSeedTimes(const std::vector<CbmMuc
                                                         const std::vector<CbmMatch>*);
 template void CbmSeedFinderSlidingWindow::FillSeedTimes(const std::vector<CbmMuchDigi>*, const std::vector<CbmMatch>*);
 template void CbmSeedFinderSlidingWindow::FillSeedTimes(const std::vector<CbmPsdDigi>*, const std::vector<CbmMatch>*);
+template void CbmSeedFinderSlidingWindow::FillSeedTimes(const std::vector<CbmFsdDigi>*, const std::vector<CbmMatch>*);
 template void CbmSeedFinderSlidingWindow::FillSeedTimes(const std::vector<CbmRichDigi>*, const std::vector<CbmMatch>*);
 template void CbmSeedFinderSlidingWindow::FillSeedTimes(const std::vector<CbmStsDigi>*, const std::vector<CbmMatch>*);
 template void CbmSeedFinderSlidingWindow::FillSeedTimes(const std::vector<CbmTofDigi>*, const std::vector<CbmMatch>*);
diff --git a/reco/eventbuilder/digis/CbmTaskBuildRawEvents.cxx b/reco/eventbuilder/digis/CbmTaskBuildRawEvents.cxx
index d05e889765958653dc35de77efb9e43e42b4427b..f3a3b05c06f7944439ceb87ea00f309e20f18873 100644
--- a/reco/eventbuilder/digis/CbmTaskBuildRawEvents.cxx
+++ b/reco/eventbuilder/digis/CbmTaskBuildRawEvents.cxx
@@ -131,6 +131,7 @@ InitStatus CbmTaskBuildRawEvents::Init()
   InitDigis(ECbmModuleId::kTof, &fTofDigis);
   InitDigis(ECbmModuleId::kRich, &fRichDigis);
   InitDigis(ECbmModuleId::kPsd, &fPsdDigis);
+  InitDigis(ECbmModuleId::kFsd, &fFsdDigis);
   InitDigis(ECbmModuleId::kT0, &fT0Digis);
 
   /// Register output (array of CbmEvent or vector of CbmDigiEvents)
@@ -253,6 +254,7 @@ void CbmTaskBuildRawEvents::BuildEvents()
   ReadDigis(ECbmModuleId::kTof, fTofDigis);
   ReadDigis(ECbmModuleId::kRich, fRichDigis);
   ReadDigis(ECbmModuleId::kPsd, fPsdDigis);
+  ReadDigis(ECbmModuleId::kFsd, fFsdDigis);
   ReadDigis(ECbmModuleId::kT0, fT0Digis);
 
   //Fill seeds
@@ -365,6 +367,7 @@ void CbmTaskBuildRawEvents::FillSeedTimesFromSlidingWindow(const RawEventBuilder
     case ECbmModuleId::kTof: fSeedFinderSlidingWindow->FillSeedTimes(fTofDigis, fvDigiMatchQa); break;
     case ECbmModuleId::kRich: fSeedFinderSlidingWindow->FillSeedTimes(fRichDigis, fvDigiMatchQa); break;
     case ECbmModuleId::kPsd: fSeedFinderSlidingWindow->FillSeedTimes(fPsdDigis, fvDigiMatchQa); break;
+    case ECbmModuleId::kFsd: fSeedFinderSlidingWindow->FillSeedTimes(fFsdDigis, fvDigiMatchQa); break;
     case ECbmModuleId::kT0: fSeedFinderSlidingWindow->FillSeedTimes(fT0Digis, fvDigiMatchQa); break;
     default: break;
   }
@@ -383,6 +386,7 @@ Double_t CbmTaskBuildRawEvents::GetDigiTime(ECbmModuleId _system, UInt_t _entry)
     case ECbmModuleId::kTof: return (fTofDigis->at(_entry)).GetTime();
     case ECbmModuleId::kRich: return (fRichDigis->at(_entry)).GetTime();
     case ECbmModuleId::kPsd: return (fPsdDigis->at(_entry)).GetTime();
+    case ECbmModuleId::kFsd: return (fFsdDigis->at(_entry)).GetTime();
     case ECbmModuleId::kT0: return (fT0Digis->at(_entry)).GetTime();
     default: break;
   }
@@ -402,6 +406,7 @@ UInt_t CbmTaskBuildRawEvents::GetNofDigis(ECbmModuleId _system)
     case ECbmModuleId::kTof: return fTofDigis->size();
     case ECbmModuleId::kRich: return fRichDigis->size();
     case ECbmModuleId::kPsd: return fPsdDigis->size();
+    case ECbmModuleId::kFsd: return fFsdDigis->size();
     case ECbmModuleId::kT0: return fT0Digis->size();
     default: break;
   }
diff --git a/reco/eventbuilder/digis/CbmTaskBuildRawEvents.h b/reco/eventbuilder/digis/CbmTaskBuildRawEvents.h
index eedb2061e24f776024f5990ef8a2d3db6bfd7f70..21a2dbb6ad750909a8904da859af58408b8f3fa1 100644
--- a/reco/eventbuilder/digis/CbmTaskBuildRawEvents.h
+++ b/reco/eventbuilder/digis/CbmTaskBuildRawEvents.h
@@ -9,6 +9,7 @@
 #include "CbmAlgoBuildRawEvents.h"
 #include "CbmBmonDigi.h"
 #include "CbmDigiEvent.h"
+#include "CbmFsdDigi.h"
 #include "CbmMuchBeamTimeDigi.h"
 #include "CbmMuchDigi.h"
 #include "CbmPsdDigi.h"
@@ -160,6 +161,7 @@ private:
   std::vector<CbmTofDigi>* fTofDigis                   = nullptr;
   std::vector<CbmRichDigi>* fRichDigis                 = nullptr;
   std::vector<CbmPsdDigi>* fPsdDigis                   = nullptr;
+  std::vector<CbmFsdDigi>* fFsdDigis                   = nullptr;
   std::vector<CbmBmonDigi>* fT0Digis                   = nullptr;
   std::vector<Double_t>* fSeedTimes                    = nullptr;
 
diff --git a/reco/mq/CbmDevBuildEvents.cxx b/reco/mq/CbmDevBuildEvents.cxx
index 577ba917f2f5acc8316635b96ad8ac943b31a160..6d4bba5fbaeb9b139f0e641f6a4790b2304d91d9 100644
--- a/reco/mq/CbmDevBuildEvents.cxx
+++ b/reco/mq/CbmDevBuildEvents.cxx
@@ -148,7 +148,8 @@ ECbmModuleId CbmDevBuildEvents::GetDetectorId(std::string detName)
                        : ("kTof"   == detName ? ECbmModuleId::kTof
                        : ("kRich"  == detName ? ECbmModuleId::kRich
                        : ("kPsd"   == detName ? ECbmModuleId::kPsd
-                                             : ECbmModuleId::kNotExist))))))));
+                       : ("kFsd"   == detName ? ECbmModuleId::kFsd
+                                             : ECbmModuleId::kNotExist)))))))));
   return detId; 
   /// FIXME: Re-enable clang formatting after formatted lines
   /* clang-format on */
@@ -217,6 +218,7 @@ bool CbmDevBuildEvents::HandleData(FairMQParts& parts, int /*index*/)
   LOG(debug) << "TOF Vector size: " << ts.fData.fTof.fDigis.size();
   LOG(debug) << "RICH Vector size: " << ts.fData.fRich.fDigis.size();
   LOG(debug) << "PSD Vector size: " << ts.fData.fPsd.fDigis.size();
+  LOG(debug) << "FSD Vector size: " << ts.fData.fFsd.fDigis.size();
   LOG(debug) << "triggers: " << triggers.size();
 
   /// Create events
diff --git a/reco/mq/CbmDevTrigger.cxx b/reco/mq/CbmDevTrigger.cxx
index f24e6a797dd41798632ddc485a06d23fe9652762..9cda6161ea050f93f9a9cf0c8af867a0fefb1a37 100644
--- a/reco/mq/CbmDevTrigger.cxx
+++ b/reco/mq/CbmDevTrigger.cxx
@@ -73,7 +73,8 @@ ECbmModuleId CbmDevTrigger::GetDetectorId(std::string detName)
                        : ("Tof"  == detName ? ECbmModuleId::kTof
                        : ("Rich" == detName ? ECbmModuleId::kRich
                        : ("Psd"  == detName ? ECbmModuleId::kPsd
-                                             : ECbmModuleId::kNotExist)))))));
+                       : ("Fsd"  == detName ? ECbmModuleId::kFsd
+                                             : ECbmModuleId::kNotExist))))))));
   return detId; 
   /// FIXME: Re-enable clang formatting after formatted lines
   /* clang-format on */
@@ -103,6 +104,7 @@ bool CbmDevTrigger::HandleData(FairMQParts& parts, int /*index*/)
   LOG(debug) << "TOF Vector size: " << ts.fData.fTof.fDigis.size();
   LOG(debug) << "RICH Vector size: " << ts.fData.fRich.fDigis.size();
   LOG(debug) << "PSD Vector size: " << ts.fData.fPsd.fDigis.size();
+  LOG(debug) << "FSD Vector size: " << ts.fData.fFsd.fDigis.size();
 
   const std::vector<double> triggers = GetTriggerTimes(ts);
   LOG(debug) << "triggers: " << triggers.size();
@@ -141,6 +143,10 @@ std::vector<double> CbmDevTrigger::GetTriggerTimes(const CbmDigiTimeslice& ts)
       vDigiTimes = GetDigiTimes(ts.fData.fPsd.fDigis);
       break;
     }
+    case ECbmModuleId::kFsd: {
+      vDigiTimes = GetDigiTimes(ts.fData.fFsd.fDigis);
+      break;
+    }
     case ECbmModuleId::kT0: {
       vDigiTimes = GetDigiTimes(ts.fData.fT0.fDigis);
       break;
diff --git a/reco/offline/steer/CMakeLists.txt b/reco/offline/steer/CMakeLists.txt
index 044afdd09e4371f64a65c0bd2e1db1fe2d27f83e..5ce81a76f2f29731cc0749aa4b4433278e9e3e54 100644
--- a/reco/offline/steer/CMakeLists.txt
+++ b/reco/offline/steer/CMakeLists.txt
@@ -35,12 +35,14 @@ set(PRIVATE_DEPENDENCIES
   CbmGlobal
   CbmRichBase
   CbmTofBase
+  CbmFsdBase
   CbmMuchReco
   CbmMvdReco
   CbmRichReco
   CbmRecoSts
   CbmTofReco
   CbmTrdReco
+  CbmFsdReco
   CbmSimSteer
   L1
   KF
diff --git a/reco/offline/steer/Run.cxx b/reco/offline/steer/Run.cxx
index c11b30b037fa69bac728e3fcd5acdafec53ed1b6..5addfc19681e7c7494a3f8b407aa3dc93b8d505e 100644
--- a/reco/offline/steer/Run.cxx
+++ b/reco/offline/steer/Run.cxx
@@ -106,6 +106,7 @@ namespace cbm::reco::offline
       fact.RegisterMuchReco();           // Local reconstruction in MUCH
       fact.RegisterTrdReco();            // Local reconstruction in TRD
       fact.RegisterTofReco();            // Local reconstruction in TOF
+      fact.RegisterFsdReco();            // Local reconstruction in FSD
       fact.RegisterCaTracking();         // CA track finder in STS and MVD
       fact.RegisterTrackEventBuilder();  // Event building from STS tracks
     }
@@ -119,6 +120,7 @@ namespace cbm::reco::offline
       fact.RegisterMuchReco();          // Local reconstruction in MUCH
       fact.RegisterTrdReco();           // Local reconstruction in TRD
       fact.RegisterTofReco();           // Local reconstruction in TOF
+      fact.RegisterFsdReco();           // Local reconstruction in FSD
       fact.RegisterCaTracking();        // CA track finder in STS and MVD
       fact.RegisterPvFinder();          // Primary vertex finding
       fact.RegisterGlobalTracking();    // Global tracking
diff --git a/reco/offline/steer/TaskFactory.cxx b/reco/offline/steer/TaskFactory.cxx
index adae01ca78ed5a1c4d4fc0017faad4e6bab93d08..310ba8914be68ef3dcae22687e303c01f7712302 100644
--- a/reco/offline/steer/TaskFactory.cxx
+++ b/reco/offline/steer/TaskFactory.cxx
@@ -12,6 +12,7 @@
 #include "CbmBuildEventsFromTracksReal.h"
 #include "CbmBuildEventsIdeal.h"
 #include "CbmFindPrimaryVertex.h"
+#include "CbmFsdHitProducer.h"
 #include "CbmKF.h"
 #include "CbmL1.h"
 #include "CbmL1StsTrackFinder.h"
@@ -115,6 +116,7 @@ namespace cbm::reco::offline
         case ECbmModuleId::kTof: evBuildRaw->SetReferenceDetector(kRawEventBuilderDetTof); break;
         case ECbmModuleId::kRich: evBuildRaw->SetReferenceDetector(kRawEventBuilderDetRich); break;
         case ECbmModuleId::kPsd: evBuildRaw->SetReferenceDetector(kRawEventBuilderDetPsd); break;
+        case ECbmModuleId::kFsd: evBuildRaw->SetReferenceDetector(kRawEventBuilderDetFsd); break;
         case ECbmModuleId::kT0: evBuildRaw->SetReferenceDetector(kRawEventBuilderDetT0); break;
         default: throw std::out_of_range("Event builder: Undefined trigger detector"); break;
       }
@@ -128,6 +130,7 @@ namespace cbm::reco::offline
       if (!fRun->IsDataPresent(ECbmModuleId::kPsd)) evBuildRaw->RemoveDetector(kRawEventBuilderDetPsd);
       if (!fRun->IsDataPresent(ECbmModuleId::kTof)) evBuildRaw->RemoveDetector(kRawEventBuilderDetTof);
       if (!fRun->IsDataPresent(ECbmModuleId::kTrd)) evBuildRaw->RemoveDetector(kRawEventBuilderDetTrd);
+      if (!fRun->IsDataPresent(ECbmModuleId::kFsd)) evBuildRaw->RemoveDetector(kRawEventBuilderDetFsd);
 
       // --- Timeslice parameters
       evBuildRaw->SetTsParameters(0.0, 1.e7, 0.0);
@@ -261,6 +264,18 @@ namespace cbm::reco::offline
   // --------------------------------------------------------------------------
 
 
+  // -----   FSD reconstruction   ---------------------------------------------
+  void TaskFactory::RegisterFsdReco()
+  {
+    assert(fRun);
+    if (!fRun->IsDataPresent(ECbmModuleId::kFsd)) return;
+
+    CbmFsdHitProducer* fsdHit = new CbmFsdHitProducer();
+    fRun->AddTask(fsdHit);
+  }
+  // --------------------------------------------------------------------------
+
+
   // -----   Event building from tracks   -------------------------------------
   void TaskFactory::RegisterTrackEventBuilder()
   {
diff --git a/reco/offline/steer/TaskFactory.h b/reco/offline/steer/TaskFactory.h
index 5fd7bf11952008a61017131f66ad3581bda0cf01..6621e4301cc6ea3d9cf65064c50909309bf22912 100644
--- a/reco/offline/steer/TaskFactory.h
+++ b/reco/offline/steer/TaskFactory.h
@@ -38,6 +38,7 @@ namespace cbm::reco::offline
     void RegisterRichReco();           /// Local reconstruction for RICH
     void RegisterStsReco();            /// Local reconstruction for STS
     void RegisterTofReco();            /// Local reconstruction for TOF
+    void RegisterFsdReco();            /// Local reconstruction for FSD
     void RegisterTrackEventBuilder();  /// Event building from tracks
     void RegisterTrdReco();            /// Local reconstruction for TRD
     void RegisterTrdPid();             /// PID with TRD