diff --git a/Documentation/Examples/cascade_example.cc b/Documentation/Examples/cascade_example.cc
index f18ff72efcf8db5c95cc370b8624229d65fccdc0..b4529f1e2dc3da14e714983ebe8403fd3c8c51ae 100644
--- a/Documentation/Examples/cascade_example.cc
+++ b/Documentation/Examples/cascade_example.cc
@@ -25,6 +25,7 @@
 
 #include <corsika/process/sibyll/Decay.h>
 #include <corsika/process/sibyll/Interaction.h>
+#include <corsika/process/sibyll/NuclearInteraction.h>
 
 #include <corsika/process/track_writer/TrackWriter.h>
 
@@ -233,7 +234,8 @@ int main() {
   stack_inspector::StackInspector<setup::Stack> p0(true);
 
   corsika::random::RNGManager::GetInstance().RegisterRandomStream("s_rndm");
-  corsika::process::sibyll::Interaction sibyll(env);
+  //  corsika::process::sibyll::Interaction sibyll(env);
+  corsika::process::sibyll::NuclearInteraction sibyll(env);
   corsika::process::sibyll::Decay decay;
   ProcessCut cut(8_GeV);
 
@@ -257,8 +259,8 @@ int main() {
   double phi = 0.;
   {
     auto particle = stack.NewParticle();
-    particle.SetPID(Code::Proton);
-    HEPMomentumType P0 = sqrt(E0 * E0 - Proton::GetMass() * Proton::GetMass());
+    particle.SetPID(Code::Helium);
+    HEPMomentumType P0 = sqrt(E0 * E0 - Helium::GetMass() * Helium::GetMass());
     auto momentumComponents = [](double theta, double phi, HEPMomentumType ptot) {
       return std::make_tuple(ptot * sin(theta) * cos(phi), ptot * sin(theta) * sin(phi),
                              -ptot * cos(theta));
diff --git a/Processes/Sibyll/Interaction.h b/Processes/Sibyll/Interaction.h
index 288e724fe086c05adaa280a4fe385be3fdb3e27f..2a4c02d063242ae247f2a3e473f9c179ca5a81cb 100644
--- a/Processes/Sibyll/Interaction.h
+++ b/Processes/Sibyll/Interaction.h
@@ -185,6 +185,11 @@ namespace corsika::process::sibyll {
       cout << "ProcessSibyll: "
            << "DoInteraction: " << corsikaBeamId << " interaction? "
            << process::sibyll::CanInteract(corsikaBeamId) << endl;
+      if(corsika::particles::IsNucleus(corsikaBeamId)){
+	// nuclei handled by different process, skip
+	return process::EProcessReturn::eOk;
+      }
+      
       if (process::sibyll::CanInteract(corsikaBeamId)) {
         const CoordinateSystem& rootCS =
             RootCoordinateSystem::GetInstance().GetRootCoordinateSystem();
@@ -291,6 +296,7 @@ namespace corsika::process::sibyll {
           std::cout << "Interaction: "
                     << " DoInteraction: should have dropped particle.. "
                     << "THIS IS AN ERROR" << std::endl;
+	  throw std::runtime_error("energy too low for SIBYLL");
           // p.Delete(); delete later... different process
         } else {
           fCount++;
diff --git a/Processes/Sibyll/NuclearInteraction.h b/Processes/Sibyll/NuclearInteraction.h
index 5f0d29781e1b5260f61cb65ef3a9e25f209f279b..90a194e85911c8f459f0baa170671655f557392c 100644
--- a/Processes/Sibyll/NuclearInteraction.h
+++ b/Processes/Sibyll/NuclearInteraction.h
@@ -58,16 +58,17 @@ namespace corsika::process::sibyll {
       using namespace corsika::units::si;
       double sigProd, dummy, dum1, dum2, dum3, dum4;
       double dumdif[3];
-      
-      if(!corsika::particles::IsNucleus(BeamId)){
-	sigProd = std::numeric_limits<double>::infinity();
-	return std::make_tuple(sigProd * 1_mbarn, 1);
-      }
 
-      // TODO: use nuclib to calc. nuclear cross sections
-      // FOR NOW: use proton cross section for nuclei
-      auto const BeamIdToUse = corsika::particles::Proton::GetCode();
-      std::cout << "WARNING: replacing beam nucleus with proton!" << std::endl;
+      corsika::particles::Code BeamIdToUse;
+      if(corsika::particles::IsNucleus(BeamId)){
+
+	// TODO: use nuclib to calc. nuclear cross sections
+	// FOR NOW: use proton cross section for nuclei
+	BeamIdToUse = corsika::particles::Proton::GetCode();
+	std::cout << "WARNING: replacing beam nucleus with proton!" << std::endl;
+      } else {
+	BeamIdToUse = BeamId;
+      }
       
       const int iBeam = process::sibyll::GetSibyllXSCode(BeamIdToUse);
       const double dEcm = CoMenergy / 1_GeV;
@@ -104,13 +105,13 @@ namespace corsika::process::sibyll {
 
       const particles::Code corsikaBeamId = p.GetPID();
 
-      if(!corsika::particles::IsNucleus(corsikaBeamId))
+      if(!corsika::particles::IsNucleus(corsikaBeamId)){
 	// no nuclear interaction
 	return std::numeric_limits<double>::infinity() * 1_g / (1_cm * 1_cm);
-
+      }
+      
       // beam particles for sibyll : 1, 2, 3 for p, pi, k
       // read from cross section code table
-
       const HEPMassType nucleon_mass = 0.5 * (corsika::particles::Proton::GetMass() +
 					      corsika::particles::Neutron::GetMass());
 
@@ -192,11 +193,143 @@ namespace corsika::process::sibyll {
       using std::endl;
 
       const auto corsikaBeamId = p.GetPID();
-      const auto kNucleus = IsNucleus(corsikaBeamId);
-      if(!kNucleus)
+      cout << "NuclearInteraction: DoInteraction: called with:" << corsikaBeamId
+	   << endl;
+	      
+      if(!IsNucleus(corsikaBeamId))
 	return process::EProcessReturn::eOk;
+
+      const CoordinateSystem& rootCS =
+	RootCoordinateSystem::GetInstance().GetRootCoordinateSystem();
+      
+      // position and time of interaction, not used in Sibyll
+      Point pOrig = p.GetPosition();
+      TimeType tOrig = p.GetTime();
+
+      // beam nucleon number
+      const int kABeam = GetNucleusA(corsikaBeamId);
+      // TODO: verify this number !!!
+      if(kABeam>56)
+	throw std::runtime_error("Beam nucleus too large for SIBYLL!");
       
+      // kinematics
+      // define projectile NUCLEON
+      HEPEnergyType const eProjectileLab = p.GetEnergy() / kABeam;
+      auto const pProjectileLab = p.GetMomentum() / kABeam;
+      const FourVector PprojLab(eProjectileLab, pProjectileLab);
+
+      cout << "NuclearInteraction: ebeam lab: " << eProjectileLab / 1_GeV << endl
+	   << "NuclearInteraction: pbeam lab: " << pProjectileLab.GetComponents() / 1_GeV
+	   << endl;
+
+      // define target
+      // always a nucleon
+      // for Sibyll is always a single nucleon
+      auto constexpr nucleon_mass = 0.5 * (corsika::particles::Proton::GetMass() +
+					   corsika::particles::Neutron::GetMass());
+      // target is always at rest
+      const auto eTargetLab = 0_GeV + nucleon_mass;
+      const auto pTargetLab = MomentumVector(rootCS, 0_GeV, 0_GeV, 0_GeV);
+      const FourVector PtargLab(eTargetLab, pTargetLab);
+      
+      cout << "NuclearInteraction: etarget lab: " << eTargetLab / 1_GeV << endl
+	   << "NuclearInteraction: ptarget lab: " << pTargetLab.GetComponents() / 1_GeV
+	   << endl;
+
+      // center-of-mass energy in nucleon-nucleon frame
+      auto const Ptot4 = PtargLab + PprojLab;
+      HEPEnergyType Ecm = Ptot4.GetNorm();
+      cout << "NuclearInteraction: nuc-nuc cm energy: " << Ecm / 1_GeV << endl;
+
+      const auto beamId = corsika::particles::Proton::GetCode();
+      // sample target nucleon number
+      const auto currentNode = fEnvironment.GetUniverse()->GetContainingNode(pOrig);
+      const auto& mediumComposition =
+	currentNode->GetModelProperties().GetNuclearComposition();
+      // get cross sections for target materials
+      /*
+	Here we read the cross section from the interaction model again,
+	should be passed from GetInteractionLength if possible
+      */
+      auto const& compVec = mediumComposition.GetComponents();
+      std::vector<si::CrossSectionType> cross_section_of_components(compVec.size());
+
+      for (size_t i = 0; i < compVec.size(); ++i) {
+	auto const targetId = compVec[i];
+	const auto [sigProd, nNuc] = GetCrossSection(beamId,targetId,Ecm);
+	cross_section_of_components[i] = sigProd;
+      }
+
+      const auto targetCode = currentNode->GetModelProperties().SampleTarget(
+									     cross_section_of_components, fRNG);
+      cout << "Interaction: target selected: " << targetCode << endl;
+      /*
+	FOR NOW: allow nuclei with A<18 or protons only.
+	when medium composition becomes more complex, approximations will have to be
+	allowed air in atmosphere also contains some Argon.
+      */
+      int kATarget = -1;
+      if (IsNucleus(targetCode)) kATarget = GetNucleusA(targetCode);
+      if (targetCode == corsika::particles::Proton::GetCode()) kATarget = 1;
+      cout << "NuclearInteraction: sibyll target code: " << kATarget << endl;
+      if (kATarget > 18 || kATarget < 1)
+	throw std::runtime_error(
+				 "Sibyll target outside range. Only nuclei with A<18 or protons are "
+				 "allowed.");
+      // end of target sampling
+
+      // superposition 
+      cout << "NuclearInteraction: sampling nuc. multiple interaction structure.. "<< endl;
+      // get nucleon-nucleon cross section
+      // (needed to determine number of scatterings)
+      double sigProd, sigEla, dum1, dum2, dum3, dum4;
+      double dumdif[3];
+      const double sqsnuc = Ecm / 1_GeV;
+      const int iBeam = 1;
+      sib_sigma_hp_(iBeam, sqsnuc, dum1, sigEla, sigProd, dumdif, dum3, dum4);	
+      
+      // sample number of interactions (output in common cnucms)
+      // nuclear multiple scattering according to glauber (r.i.p.)
+      int_nuc_( kATarget, kABeam, sigProd, sigEla);
+
+      cout << "number of wounded nucleons in target   : " << cnucms_.na << endl
+	   << "number of wounded nucleons in project. : " << cnucms_.nb << endl
+	   << "number of inel. nuc.-nuc. interactions : " << cnucms_.ni << endl
+	   << "number of elastic nucleons in target   : " << cnucms_.nael << endl
+	   << "number of elastic nucleons in project. : " << cnucms_.nbel << endl;
+      
+      throw std::runtime_error(" end now");      
+
+      // calculate fragmentation
+      // call FRAGM (IAT,IAP, NW,B, NF, IAF)
+      // fragm_(kATarget, kABeam, NBT, B, NF, IAF)
+
+      /*
+C.  INPUT: IAP = mass of incident nucleus
+C.         IAT = mass of target   nucleus
+C.         NW = number of wounded nucleons in the beam nucleus
+C.         B  = impact parameter in the interaction
+C.     
+C.  OUTPUT : NF = number of fragments  of the spectator nucleus
+C.           IAF(1:NF) = mass number of each fragment
+C.           PF(3,60) in common block /FRAGMENTS/ contains
+C.           the three momentum components (MeV/c) of each
+C.           fragment in the projectile frame
+C..............................................................
+       */
+      
+      // put spectators on intermediate stack
+      //Stack nucs;
+      
+      // add elastic nucleons to stack
+
+      // add inelastic interactions
+      
+
+      // move particles to corsika stack
+      // boost
 
+      
       // add proton instead
       auto pnew = s.NewParticle();
       pnew.SetPID( corsika::particles::Proton::GetCode() );
diff --git a/Processes/Sibyll/sibyll2.3c.h b/Processes/Sibyll/sibyll2.3c.h
index cd13ebb67bc9642310f6cf95f8194d6fc6b551f5..e7411d4abbbd1144ddb33006f789f115688dc714 100644
--- a/Processes/Sibyll/sibyll2.3c.h
+++ b/Processes/Sibyll/sibyll2.3c.h
@@ -70,6 +70,22 @@ extern struct {
   int lun;
 } s_debug_;
 
+  // nuclib common, NUClear Multiple Scattering
+  /*
+      COMMON /CNUCMS/ B, BMAX, NTRY, NA, NB, NI, NAEL, NBEL
+     +         ,JJA(IAMAX), JJB(IAMAX), JJINT(IAMAX,IAMAX)
+     +         ,JJAEL(IAMAX), JJBEL(IAMAX)
+   */
+
+  extern struct {
+    double b, bmax;
+    int ntry, na, nb, ni, nael, nbel;
+    int jja[56], jjb[56], jjint[56][56], jjael[56], jjbel[56];
+  } cnucms_;
+
+
+
+  
 // lund random generator setup
 // extern struct {int mrlu[6]; float rrlu[100]; }ludatr_;
 
@@ -81,6 +97,12 @@ void sibyll_ini_();
 
 // subroutine to initiate nuclib  
 void nuc_nuc_ini_();
+
+// subroutine to sample nuclear interaction structure
+  void int_nuc_( const int&, const int&, const double&, const double&);
+
+// subroutine to sample nuclear fragments  
+  void fragm_(const int&, const int&, int&, double&, int&, int&);
   
 // subroutine to SET DECAYS
 void dec_ini_();
diff --git a/Processes/Sibyll/testSibyll.cc b/Processes/Sibyll/testSibyll.cc
index 16449b87c96f57f9eba63c92f4aa30973b597978..79316ba8fee006bfba759a62fd297f6be560098c 100644
--- a/Processes/Sibyll/testSibyll.cc
+++ b/Processes/Sibyll/testSibyll.cc
@@ -133,7 +133,7 @@ TEST_CASE("SibyllInterface", "[processes]") {
     setup::Stack stack;
     auto particle = stack.NewParticle();
 
-    Interaction model(env);
+    NuclearInteraction model(env);
 
     model.Init();
     [[maybe_unused]] const process::EProcessReturn ret =