diff --git a/Framework/Cascade/Cascade.h b/Framework/Cascade/Cascade.h
index 584ab04e7738b84025d519c92c3900a11ba1d5ab..e89409e918d36ba6867ac5694ac3a0b398633acc 100644
--- a/Framework/Cascade/Cascade.h
+++ b/Framework/Cascade/Cascade.h
@@ -242,6 +242,7 @@ namespace corsika::cascade {
       } else { // boundary crossing
         std::cout << "boundary crossing! next node = " << nextVol << std::endl;
         particle.SetNode(nextVol);
+        fProcessSequence.DoBoundaryCrossing(particle, *currentLogicalNode, *nextVol);
       }
     }
 
diff --git a/Framework/ProcessSequence/BoundaryCrossingProcess.h b/Framework/ProcessSequence/BoundaryCrossingProcess.h
new file mode 100644
index 0000000000000000000000000000000000000000..1e90e9ae10878c8b4ac544bb8c8f06084f0ee0f7
--- /dev/null
+++ b/Framework/ProcessSequence/BoundaryCrossingProcess.h
@@ -0,0 +1,35 @@
+/*
+ * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
+ *
+ * See file AUTHORS for a list of contributors.
+ *
+ * This software is distributed under the terms of the GNU General Public
+ * Licence version 3 (GPL Version 3). See file LICENSE for a full version of
+ * the license.
+ */
+
+#ifndef _include_corsika_processes_BoundaryCrossingProcess_h_
+#define _include_corsika_processes_BoundaryCrossingProcess_h_
+
+#include <corsika/environment/Environment.h>
+
+namespace corsika::process {
+  template <typename TDerived>
+  struct BoundaryCrossingProcess {
+    auto& GetRef() { return static_cast<TDerived&>(*this); }
+    auto const& GetRef() const { return static_cast<const TDerived&>(*this); }
+
+    /**
+     * This method is called when a particle crosses the boundary between the nodes
+     * \arg from and \arg to.
+     */
+    template <typename Particle>
+    EProcessReturn DoBoundaryCrossing(Particle&, environment::BaseNodeType const& from,
+                                      environment::BaseNodeType const& to);
+  };
+
+  template <class T>
+  std::true_type is_process_impl(BoundaryCrossingProcess<T> const* impl);
+} // namespace corsika::process
+
+#endif
diff --git a/Framework/ProcessSequence/CMakeLists.txt b/Framework/ProcessSequence/CMakeLists.txt
index d45d28d6c208ef80302130bbe3e2e1c837b1dfcf..db0b3dbf3f22d046da1bc7ec0050bc2a0c74a08e 100644
--- a/Framework/ProcessSequence/CMakeLists.txt
+++ b/Framework/ProcessSequence/CMakeLists.txt
@@ -11,6 +11,7 @@ set (
 set (
   CORSIKAprocesssequence_HEADERS
   BaseProcess.h
+  BoundaryCrossingProcess.h
   ContinuousProcess.h
   InteractionProcess.h
   DecayProcess.h
diff --git a/Framework/ProcessSequence/ProcessSequence.h b/Framework/ProcessSequence/ProcessSequence.h
index 1056ccd9e4cdb4ec80fca29b7c08857de1e28fc9..f055b826c39cbeab1b23be4b2585e6d01236eeee 100644
--- a/Framework/ProcessSequence/ProcessSequence.h
+++ b/Framework/ProcessSequence/ProcessSequence.h
@@ -13,6 +13,7 @@
 #define _include_ProcessSequence_h_
 
 #include <corsika/process/BaseProcess.h>
+#include <corsika/process/BoundaryCrossingProcess.h>
 #include <corsika/process/ContinuousProcess.h>
 #include <corsika/process/DecayProcess.h>
 #include <corsika/process/InteractionProcess.h>
@@ -70,6 +71,24 @@ namespace corsika::process {
     // example for a trait-based call:
     // void Hello() const  { detail::CallHello<T1,T2>::Call(A, B); }
 
+    template <typename Particle>
+    EProcessReturn DoBoundaryCrossing(Particle& p, environment::BaseNodeType const& from,
+                                      environment::BaseNodeType const& to) {
+      EProcessReturn ret = EProcessReturn::eOk;
+
+      if constexpr (std::is_base_of<BoundaryCrossingProcess<T1type>, T1type>::value ||
+                    is_process_sequence<T1>::value) {
+        ret |= A.DoBoundaryCrossing(p, from, to);
+      }
+
+      if constexpr (std::is_base_of<BoundaryCrossingProcess<T2type>, T2type>::value ||
+                    is_process_sequence<T2>::value) {
+        ret |= B.DoBoundaryCrossing(p, from, to);
+      }
+
+      return ret;
+    }
+
     template <typename Particle, typename Track, typename Stack>
     EProcessReturn DoContinuous(Particle& p, Track& t, Stack& s) {
       EProcessReturn ret = EProcessReturn::eOk;