diff --git a/examples/boundary_example.cpp b/examples/boundary_example.cpp
index 36866057baf5b01bfe0c6df970aff52be2ce7eba..4ede912094f627f1e590996a183ba5ce4b98e5db 100644
--- a/examples/boundary_example.cpp
+++ b/examples/boundary_example.cpp
@@ -89,7 +89,8 @@ int main() {
   RNGManager<>::getInstance().registerRandomStream("cascade");
 
   // setup environment, geometry
-  using EnvType = setup::Environment;
+  using EnvironmentInterface = IMediumPropertyModel<IMagneticFieldModel<IMediumModel>>;
+  using EnvType = Environment<EnvironmentInterface>;
   EnvType env;
   auto& universe = *(env.getUniverse());
 
@@ -98,8 +99,8 @@ int main() {
   // create "world" as infinite sphere filled with protons
   auto world = EnvType::createNode<Sphere>(Point{rootCS, 0_m, 0_m, 0_m}, 100_km);
 
-  using MyHomogeneousModel = MediumPropertyModel<
-      UniformMagneticField<HomogeneousMedium<setup::EnvironmentInterface>>>;
+  using MyHomogeneousModel =
+      MediumPropertyModel<UniformMagneticField<HomogeneousMedium<EnvironmentInterface>>>;
 
   auto const props = world->setModelProperties<MyHomogeneousModel>(
       Medium::AirDry1Atm, Vector(rootCS, 0_T, 0_T, 0_T), 1_kg / (1_m * 1_m * 1_m),
@@ -128,7 +129,7 @@ int main() {
   auto sequence = make_sequence(cut, boundaryCrossing, trackWriter);
 
   // setup particle stack, and add primary particles
-  setup::Stack stack;
+  setup::Stack<EnvType> stack;
   stack.clear();
   const Code beamCode = Code::MuPlus;
   const HEPMassType mass = get_mass(beamCode);
diff --git a/examples/cascade_example.cpp b/examples/cascade_example.cpp
index ce61dd9e7d18f3c1dace87c00b45c3f0060e69ee..6d09fe1522628bc8f475b57747836fcbff44f6ef 100644
--- a/examples/cascade_example.cpp
+++ b/examples/cascade_example.cpp
@@ -68,16 +68,17 @@ int main() {
   RNGManager<>::getInstance().registerRandomStream("cascade");
 
   // setup environment, geometry
-  setup::Environment env;
+  using EnvironmentInterface = IMediumPropertyModel<IMagneticFieldModel<IMediumModel>>;
+  using EnvType = Environment<EnvironmentInterface>;
+  EnvType env;
   auto& universe = *(env.getUniverse());
 
   CoordinateSystemPtr const& rootCS = env.getCoordinateSystem();
 
-  auto world =
-      setup::Environment::createNode<Sphere>(Point{rootCS, 0_m, 0_m, 0_m}, 150_km);
+  auto world = EnvType::createNode<Sphere>(Point{rootCS, 0_m, 0_m, 0_m}, 150_km);
 
-  using MyHomogeneousModel = MediumPropertyModel<
-      UniformMagneticField<HomogeneousMedium<setup::EnvironmentInterface>>>;
+  using MyHomogeneousModel =
+      MediumPropertyModel<UniformMagneticField<HomogeneousMedium<EnvironmentInterface>>>;
 
   // fraction of oxygen
   double const fox = 0.20946;
@@ -86,15 +87,14 @@ int main() {
       1_kg / (1_m * 1_m * 1_m),
       NuclearComposition({Code::Nitrogen, Code::Oxygen}, {1. - fox, fox}));
 
-  auto innerMedium =
-      setup::Environment::createNode<Sphere>(Point{rootCS, 0_m, 0_m, 0_m}, 5000_m);
+  auto innerMedium = EnvType::createNode<Sphere>(Point{rootCS, 0_m, 0_m, 0_m}, 5000_m);
 
   innerMedium->setModelProperties(props);
   world->addChild(std::move(innerMedium));
   universe.addChild(std::move(world));
 
   // setup particle stack, and add primary particle
-  setup::Stack stack;
+  setup::Stack<EnvType> stack;
   stack.clear();
   const int nuclA = 4;
   const int nuclZ = int(nuclA / 2.15 + 0.7);
@@ -132,7 +132,7 @@ int main() {
 
   // setup processes, decays and interactions
   setup::Tracking tracking;
-  StackInspector<setup::Stack> stackInspect(100, true, E0);
+  StackInspector<setup::Stack<EnvType>> stackInspect(100, true, E0);
 
   RNGManager<>::getInstance().registerRandomStream("sibyll");
   RNGManager<>::getInstance().registerRandomStream("pythia");
diff --git a/examples/cascade_proton_example.cpp b/examples/cascade_proton_example.cpp
index 5a78452390575c9aae26d277b5776be001538d4b..bc30603fbee0af5feea6f0d08014d14743f62f5b 100644
--- a/examples/cascade_proton_example.cpp
+++ b/examples/cascade_proton_example.cpp
@@ -71,15 +71,16 @@ int main() {
   OutputManager output("cascade_proton_outputs");
 
   // setup environment, geometry
-  using EnvType = setup::Environment;
+  using EnvironmentInterface = IMediumPropertyModel<IMagneticFieldModel<IMediumModel>>;
+  using EnvType = Environment<EnvironmentInterface>;
   EnvType env;
   auto& universe = *(env.getUniverse());
   CoordinateSystemPtr const& rootCS = env.getCoordinateSystem();
 
   auto world = EnvType::createNode<Sphere>(Point{rootCS, 0_m, 0_m, 0_m}, 150_km);
 
-  using MyHomogeneousModel = MediumPropertyModel<
-      UniformMagneticField<HomogeneousMedium<setup::EnvironmentInterface>>>;
+  using MyHomogeneousModel =
+      MediumPropertyModel<UniformMagneticField<HomogeneousMedium<EnvironmentInterface>>>;
 
   world->setModelProperties<MyHomogeneousModel>(
       Medium::AirDry1Atm, MagneticFieldVector(rootCS, 0_T, 0_T, 1_mT),
@@ -88,7 +89,7 @@ int main() {
   universe.addChild(std::move(world));
 
   // setup particle stack, and add primary particle
-  setup::Stack stack;
+  setup::Stack<EnvType> stack;
   stack.clear();
   const Code beamCode = Code::Proton;
   const HEPMassType mass = Proton::mass;
@@ -119,7 +120,7 @@ int main() {
 
   // setup processes, decays and interactions
   setup::Tracking tracking;
-  StackInspector<setup::Stack> stackInspect(1000, true, E0);
+  StackInspector<setup::Stack<EnvType>> stackInspect(1000, true, E0);
 
   RNGManager<>::getInstance().registerRandomStream("sibyll");
   RNGManager<>::getInstance().registerRandomStream("pythia");
diff --git a/examples/corsika.cpp b/examples/corsika.cpp
index 94d885b216f8fa01f76baeb34111494ebde444af..5cc04e646bca810870e07b1ac640d8c475b4a827 100644
--- a/examples/corsika.cpp
+++ b/examples/corsika.cpp
@@ -81,7 +81,10 @@
 using namespace corsika;
 using namespace std;
 
-using Particle = setup::Stack::particle_type;
+using EnvironmentInterface = IMediumPropertyModel<IMagneticFieldModel<IMediumModel>>;
+using EnvType = Environment<EnvironmentInterface>;
+
+using Particle = setup::Stack<EnvType>::particle_type;
 
 void registerRandomStreams(int seed) {
   RNGManager<>::getInstance().registerRandomStream("cascade");
@@ -196,14 +199,13 @@ int main(int argc, char** argv) {
   registerRandomStreams(app["--seed"]->as<int>());
 
   /* === START: SETUP ENVIRONMENT AND ROOT COORDINATE SYSTEM === */
-  using EnvType = setup::Environment;
   EnvType env;
   CoordinateSystemPtr const& rootCS = env.getCoordinateSystem();
   Point const center{rootCS, 0_m, 0_m, 0_m};
   GeomagneticModel wmm(center, corsika_data("GeoMag/WMM.COF"));
 
   // build a Linsley US Standard atmosphere into `env`
-  create_5layer_atmosphere<setup::EnvironmentInterface, MyExtraEnv>(
+  create_5layer_atmosphere<EnvironmentInterface, MyExtraEnv>(
       env, AtmosphereId::LinsleyUSStd, center, Medium::AirDry1Atm,
       wmm.getField(2022.5, 10_km, 49, 8.4));
 
@@ -331,7 +333,7 @@ int main(int argc, char** argv) {
 
   corsika::urqmd::UrQMD urqmd;
   InteractionCounter urqmdCounted(urqmd);
-  StackInspector<setup::Stack> stackInspect(10000, false, E0);
+  StackInspector<setup::Stack<EnvType>> stackInspect(10000, false, E0);
 
   // assemble all processes into an ordered process list
   struct EnergySwitch {
@@ -360,7 +362,7 @@ int main(int argc, char** argv) {
 
   // create the cascade object using the default stack and tracking implementation
   setup::Tracking tracking;
-  setup::Stack stack;
+  setup::Stack<EnvType> stack;
   Cascade EAS(env, tracking, sequence, output, stack);
 
   // print our primary parameters all in one place
diff --git a/examples/em_shower.cpp b/examples/em_shower.cpp
index fe67c6477184f4f7d17e66b94c6ba52d8d12695a..018666a46ff209067a440cef152dafd440c334d4 100644
--- a/examples/em_shower.cpp
+++ b/examples/em_shower.cpp
@@ -91,13 +91,14 @@ int main(int argc, char** argv) {
   registerRandomStreams(seed);
 
   // setup environment, geometry
-  using EnvType = setup::Environment;
+  using EnvironmentInterface = IMediumPropertyModel<IMagneticFieldModel<IMediumModel>>;
+  using EnvType = Environment<EnvironmentInterface>;
   EnvType env;
   CoordinateSystemPtr const& rootCS = env.getCoordinateSystem();
   Point const center{rootCS, 0_m, 0_m, 0_m};
 
   // build a Linsley US Standard atmosphere into `env`
-  create_5layer_atmosphere<setup::EnvironmentInterface, MyExtraEnv>(
+  create_5layer_atmosphere<EnvironmentInterface, MyExtraEnv>(
       env, AtmosphereId::LinsleyUSStd, center, Medium::AirDry1Atm,
       MagneticFieldVector{rootCS, 0_T, 50_uT, 0_T});
 
diff --git a/examples/hybrid_MC.cpp b/examples/hybrid_MC.cpp
index 4e695063468ad88ca2159c0642f57eb54117eced..0cb65570bd3230332d416328b5115142c401ac11 100644
--- a/examples/hybrid_MC.cpp
+++ b/examples/hybrid_MC.cpp
@@ -173,18 +173,19 @@ int main(int argc, char** argv) {
   registerRandomStreams(seed);
 
   // setup environment, geometry
-  using EnvType = setup::Environment;
+  using EnvironmentInterface = IMediumPropertyModel<IMagneticFieldModel<IMediumModel>>;
+  using EnvType = Environment<EnvironmentInterface>;
   EnvType env;
   CoordinateSystemPtr const& rootCS = env.getCoordinateSystem();
   Point const center{rootCS, 0_m, 0_m, 0_m};
 
   // build a Linsley US Standard atmosphere into `env`
-  create_5layer_atmosphere<setup::EnvironmentInterface, MyExtraEnv>(
+  create_5layer_atmosphere<EnvironmentInterface, MyExtraEnv>(
       env, AtmosphereId::LinsleyUSStd, center, Medium::AirDry1Atm,
       MagneticFieldVector{rootCS, 0_T, 50_uT, 0_T});
 
   // setup particle stack, and add primary particle
-  setup::Stack stack;
+  setup::Stack<EnvType> stack;
   stack.clear();
   unsigned short const A = std::stoi(std::string(argv[1]));
   unsigned short const Z = std::stoi(std::string(argv[2]));
@@ -298,7 +299,7 @@ int main(int argc, char** argv) {
     HEPEnergyType cutE_;
     EnergySwitch(HEPEnergyType cutE)
         : cutE_(cutE) {}
-    bool operator()(const setup::Stack::particle_type& p) const {
+    bool operator()(const setup::Stack<EnvType>::particle_type& p) const {
       return (p.getEnergy() < cutE_);
     }
   };
diff --git a/examples/mars.cpp b/examples/mars.cpp
index 84cf468233b48f08cc67f6d481d627dcbda1101b..bc7cbd4cf9b0f11eb172dd6be5923c65c72c8f55 100644
--- a/examples/mars.cpp
+++ b/examples/mars.cpp
@@ -81,7 +81,10 @@
 using namespace corsika;
 using namespace std;
 
-using Particle = setup::Stack::particle_type;
+using EnvironmentInterface = IMediumPropertyModel<IMagneticFieldModel<IMediumModel>>;
+using EnvType = Environment<EnvironmentInterface>;
+
+using Particle = setup::Stack<EnvType>::particle_type;
 
 typedef decltype(1 * pascal) PressureType;
 typedef decltype(1 * degree_celsius) TemperatureType;
@@ -218,17 +221,16 @@ int main(int argc, char** argv) {
   registerRandomStreams(app["--seed"]->as<int>());
 
   /* === START: SETUP ENVIRONMENT AND ROOT COORDINATE SYSTEM === */
-  using EnvType = setup::Environment;
   EnvType env;
   CoordinateSystemPtr const& rootCS = env.getCoordinateSystem();
   Point const center{rootCS, 0_m, 0_m, 0_m};
   LengthType const radiusMars = 3389.5_km;
   auto builder =
-      make_layered_spherical_atmosphere_builder<setup::EnvironmentInterface, MyExtraEnv>::
-          create(center,
-                 radiusMars,                                   // Mars
-                 Medium::AirDry1Atm,                           // Mars, close enough
-                 MagneticFieldVector{rootCS, 0_T, 0_uT, 0_T}); // Mars
+      make_layered_spherical_atmosphere_builder<EnvironmentInterface, MyExtraEnv>::create(
+          center,
+          radiusMars,                                   // Mars
+          Medium::AirDry1Atm,                           // Mars, close enough
+          MagneticFieldVector{rootCS, 0_T, 0_uT, 0_T}); // Mars
 
   builder.setNuclearComposition(                             // Mars
       {{Code::Nitrogen, Code::Oxygen}, {1. / 3., 2. / 3.}}); // simplified
@@ -364,7 +366,7 @@ int main(int argc, char** argv) {
 
   corsika::urqmd::UrQMD urqmd;
   InteractionCounter urqmdCounted{urqmd};
-  StackInspector<setup::Stack> stackInspect(5000, false, E0);
+  StackInspector<setup::Stack<EnvType>> stackInspect(5000, false, E0);
 
   // assemble all processes into an ordered process list
   struct EnergySwitch {
@@ -395,7 +397,7 @@ int main(int argc, char** argv) {
 
   // create the cascade object using the default stack and tracking implementation
   setup::Tracking tracking;
-  setup::Stack stack;
+  setup::Stack<EnvType> stack;
   Cascade EAS(env, tracking, sequence, output, stack);
 
   // print our primary parameters all in one place
diff --git a/examples/stopping_power.cpp b/examples/stopping_power.cpp
index 8d0641960940c34e00d110780735b4d06ecdfb9a..4aa5a0f5a504aa4a6baf5ebb2bffd14f87c01c26 100644
--- a/examples/stopping_power.cpp
+++ b/examples/stopping_power.cpp
@@ -50,7 +50,7 @@ int main() {
 
   BetheBlochPDG eLoss;
 
-  setup::Stack stack;
+  setup::Stack<EnvType> stack;
 
   std::ofstream file("dEdX.dat");
   file << "# beta*gamma, dE/dX / MeV/(g/cm²)" << std::endl;
diff --git a/examples/vertical_EAS.cpp b/examples/vertical_EAS.cpp
index 6173c460319f35e2f7dedfcc5faa630a22eb88ef..b5e5f163327fec18a28242bc8bdfc27908c7a761 100644
--- a/examples/vertical_EAS.cpp
+++ b/examples/vertical_EAS.cpp
@@ -76,7 +76,10 @@
 using namespace corsika;
 using namespace std;
 
-using Particle = setup::Stack::particle_type;
+using EnvironmentInterface = IMediumPropertyModel<IMagneticFieldModel<IMediumModel>>;
+using EnvType = Environment<EnvironmentInterface>;
+
+using Particle = setup::Stack<EnvType>::particle_type;
 
 void registerRandomStreams(int seed) {
   RNGManager<>::getInstance().registerRandomStream("cascade");
@@ -122,14 +125,13 @@ int main(int argc, char** argv) {
   registerRandomStreams(seed);
 
   // setup environment, geometry
-  using EnvType = setup::Environment;
   EnvType env;
   CoordinateSystemPtr const& rootCS = env.getCoordinateSystem();
   Point const center{rootCS, 0_m, 0_m, 0_m};
   GeomagneticModel wmm(center, corsika_data("GeoMag/WMM.COF"));
 
   // build a Linsley US Standard atmosphere into `env`
-  create_5layer_atmosphere<setup::EnvironmentInterface, MyExtraEnv>(
+  create_5layer_atmosphere<EnvironmentInterface, MyExtraEnv>(
       env, AtmosphereId::LinsleyUSStd, center, Medium::AirDry1Atm,
       wmm.getField(2022.5, 10_km, 49, 8.4));
 
@@ -254,7 +256,7 @@ int main(int argc, char** argv) {
 
   corsika::urqmd::UrQMD urqmd;
   InteractionCounter urqmdCounted{urqmd};
-  StackInspector<setup::Stack> stackInspect(50000, false, E0);
+  StackInspector<setup::Stack<EnvType>> stackInspect(50000, false, E0);
 
   // assemble all processes into an ordered process list
   struct EnergySwitch {
@@ -272,7 +274,7 @@ int main(int argc, char** argv) {
   string const cMSHist_file = "inthist_cms_verticalEAS.npz";
 
   // setup particle stack, and add primary particle
-  setup::Stack stack;
+  setup::Stack<EnvType> stack;
   stack.clear();
 
   stack.addParticle(std::make_tuple(