diff --git a/CMakeLists.txt b/CMakeLists.txt
index 39e38fd1d14476b6003a83f6a287dfa8ebc85708..5e65099568c9177102745a986b43643537ba4832 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -73,12 +73,17 @@ ELSE()
 
     MESSAGE( "All dependencies were found.  Run 'make' to build SLIC and 'make install' to install to '${CMAKE_INSTALL_PREFIX}'." )
 
+    # build user plugin library
+    FILE( GLOB_RECURSE plugin_sources ${PROJECT_SOURCE_DIR}/plugins/*.cc )
+    ADD_LIBRARY( slicPlugins SHARED ${plugin_sources} )
+    INSTALL( TARGETS slicPlugins DESTINATION ${CMAKE_INSTALL_PREFIX}/slic/lib )
+
     # set executable target
     ADD_EXECUTABLE( slic ${library_sources} slic.cc )
 
-    # make slic depend on its external deps (is this necessary???)
-    ADD_DEPENDENCIES( slic extdeps )
-
+    # make slic depend on its external deps
+    ADD_DEPENDENCIES( slic extdeps slicPlugins )
+    
     # local includes
     SET( SLIC_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/include )
     INCLUDE_DIRECTORIES( ${SLIC_INCLUDE_DIR} )
@@ -93,7 +98,7 @@ ELSE()
     INCLUDE_DIRECTORIES( ${XERCES_INCLUDE_DIR} ${LCIO_INCLUDE_DIRS} ${Geant4_INCLUDE_DIRS} ${GDML_INCLUDE_DIR} ${LCDD_INCLUDE_DIR} ${HEPPDT_INCLUDE_DIR} )
 
     # libraries
-    TARGET_LINK_LIBRARIES( slic ${XERCES_LIBRARY} ${Geant4_LIBRARIES} ${GDML_LIBRARY} ${LCDD_LIBRARY} ${HEPPDT_LIBRARIES} ${LCIO_LIBRARIES} )
+    TARGET_LINK_LIBRARIES( slic ${XERCES_LIBRARY} ${Geant4_LIBRARIES} ${GDML_LIBRARY} ${LCDD_LIBRARY} ${HEPPDT_LIBRARIES} ${LCIO_LIBRARIES} slicPlugins )
 
     # link directories
     LINK_DIRECTORIES( ${GDML_LIBRARY_DIR} ${LCDD_LIBRARY_DIR} ${LCIO_LIBRARY_DIRS} )
diff --git a/include/EventAction.hh b/include/EventAction.hh
index 092994b1dfab96f502710cfb4a341607542047c1..5690791408cac3a786161344deff8c82e2b362aa 100644
--- a/include/EventAction.hh
+++ b/include/EventAction.hh
@@ -3,6 +3,7 @@
 
 // SLIC
 #include "Module.hh"
+#include "PluginManagerAccessor.hh"
 
 // LCDD
 #include "lcdd/hits/CalorimeterHit.hh"
@@ -21,7 +22,7 @@ namespace slic {
  * @brief Implementation of G4UserEventAction.
  * @note  Calls actions of TrajectoryManager and LcioManager.
  */
-class EventAction: public G4UserEventAction, public Module {
+class EventAction: public G4UserEventAction, public Module, public PluginManagerAccessor {
 
 public:
 
diff --git a/include/PluginLoader.hh b/include/PluginLoader.hh
new file mode 100644
index 0000000000000000000000000000000000000000..02bc4f0f719c2e36222b3889f73237b7d899f6e5
--- /dev/null
+++ b/include/PluginLoader.hh
@@ -0,0 +1,55 @@
+/**
+ * @file PluginLoader.h
+ * @brief Class which loads user simulation plugins from external shared libraries
+ * @author Jeremy McCormick, SLAC National Accelerator Laboratory
+ */
+
+#ifndef SLIC_PLUGINLOADER_HH_
+#define SLIC_PLUGINLOADER_HH_
+
+// LDMX
+#include "UserActionPlugin.hh"
+
+namespace slic {
+
+/**
+ * @class PluginLoader
+ * @brief Loads user sim plugin classes from external shared libraries
+ */
+class PluginLoader {
+
+    public:
+
+        /**
+         * Create a user sim plugin with the given name from the supplied
+         * library name.
+         * @param pluginName The name of the plugin.
+         * @param libName The name of the shared library.
+         */
+        UserActionPlugin* create(std::string pluginName, std::string libName);
+
+        /**
+         * Destroy an existing user sim plugin.
+         * @param plugin The sim plugin to destroy.
+         */
+        void destroy(UserActionPlugin* plugin);
+
+    private:
+
+        /**
+         * Get a handle for a plugin.
+         * @param pluginName The name of the plugin.
+         */
+        void* getHandle(std::string pluginName);
+
+    private:
+
+        /**
+         * Map of plugins to their handles.
+         */
+        std::map<UserActionPlugin*, void*> pluginHandles_;
+};
+
+}
+
+#endif
diff --git a/include/PluginManager.hh b/include/PluginManager.hh
new file mode 100644
index 0000000000000000000000000000000000000000..c11877d9cf2cd1886a8b9ed945c612a9f01f9edf
--- /dev/null
+++ b/include/PluginManager.hh
@@ -0,0 +1,194 @@
+/**
+ * @file PluginManager.h
+ * @brief Class for managing the loading, activation and destruction of UserSimPlugin objects
+ * @author Jeremy McCormick, SLAC National Accelerator Laboratory
+ */
+
+#ifndef SLIC_PLUGINMANAGER_HH_
+#define SLIC_PLUGINMANAGER_HH_
+
+// LDMX
+#include "PluginLoader.hh"
+#include "UserActionPlugin.hh"
+
+// STL
+#include <algorithm>
+#include <ostream>
+
+// Geant4
+#include "G4ClassificationOfNewTrack.hh"
+
+namespace slic {
+
+/**
+ * @class PluginManager
+ * @brief Manages user sim plugins
+ *
+ * @note
+ * This class loads UserSimPlugin objects from dynamic libraries using a PluginLoader.
+ * It is also responsible for activating the user action hooks for all registered plugins.
+ * Only one instance of a given plugin can be loaded at a time.
+ *
+ * @see UserActionPlugin
+ * @see PluginLoader
+ */
+class PluginManager {
+
+    public:
+
+        /**
+         * Vector of plugins.
+         */
+        typedef std::vector<UserActionPlugin*> PluginVec;
+
+        /**
+         * Class constructor.
+         */
+        PluginManager() {
+        }
+
+        /**
+         * Class destructor.
+         * This will destroy all the currently registered plugins.
+         */
+        virtual ~PluginManager();
+
+        /**
+         * Activate the begin run hook for registered plugins.
+         * @param aRun The Geant4 run that is beginning.
+         */
+        void beginRun(const G4Run* aRun);
+
+        /**
+         * Activate the end run hook of registered plugins.
+         * @param aRun The Geant4 run that is ending.
+         */
+        void endRun(const G4Run* aRun);
+
+        /**
+         * Activate the stepping hook of registered plugins.
+         * @param aStep The Geant4 step.
+         */
+        void stepping(const G4Step* aStep);
+
+        /**
+         * Activate the pre-tracking hook of registered plugins.
+         * @param aTrack The Geant4 track.
+         */
+        void preTracking(const G4Track* aTrack);
+
+        /**
+         * Activate the post-tracking hook of registered plugins.
+         * @param aTrack The Geant4 track.
+         */
+        void postTracking(const G4Track* aTrack);
+
+        /**
+         * Activate the begin event hook of registered plugins.
+         * @param anEvent The Geant4 event.
+         */
+        void beginEvent(const G4Event* anEvent);
+
+        /**
+         * Activate the end event hook of registered plugins.
+         * @param anEvent The Geant4 event.
+         */
+        void endEvent(const G4Event* anEvent);
+
+        /**
+         * Activate the generate primary hook of registered plugins.
+         * @param anEvent The Geant4 event.
+         */
+        void generatePrimary(G4Event* anEvent);
+
+        /**
+         *
+         * Activate the track classification hook of registered plugins.
+         * @param aTrack The Geant4 track.
+         *
+         * @brief Return a track classification from the user plugins that have stacking actions.
+         *
+         * @note
+         * The current classification will only be updated if a plugin actually provides a different classification
+         * than the current one.
+         */
+        G4ClassificationOfNewTrack stackingClassifyNewTrack(const G4Track* aTrack);
+
+        /**
+         * Activate the stacking new stage hook of registered plugins.
+         */
+        void stackingNewStage();
+
+        /**
+         * Activate the prepare new event stacking hook of registered plugins.
+         */
+        void stackingPrepareNewEvent();
+
+        /**
+         * Find a plugin by name.
+         * @param pluginName The name of the plugin.
+         * @return The plugin with the name or <i>nullptr</i> if does not exist.
+         */
+        UserActionPlugin* findPlugin(const std::string& pluginName);
+
+        /**
+         * Create a new plugin by name from the given library.
+         * @param pluginName The name of the plugin.
+         * @param libName The name of the shared library.
+         */
+        void create(const std::string& pluginName, const std::string& libName);
+
+        /**
+         * Destroy a plugin by name.
+         * @param pluginName The name of the plugin.
+         */
+        void destroy(const std::string& pluginName);
+
+        /**
+         * Print out information about registered plugins.
+         * @param os The output stream.
+         * @return The same output stream.
+         */
+        std::ostream& print(std::ostream& os);
+
+    private:
+
+        /**
+         * Destroy a plugin.
+         * @param plugin Pointer to plugin that should be destroyed.
+         */
+        void destroy(UserActionPlugin* plugin);
+
+        /**
+         * Register a plugin.
+         * @param plugin Pointer to plugin that should be registered.
+         */
+        void registerPlugin(UserActionPlugin* plugin);
+
+        /**
+         * De-register a plugin.
+         * @param plugin Pointer to plugin that should be de-registered.
+         */
+        void deregisterPlugin(UserActionPlugin* plugin);
+
+        /**
+         * Destroy all registered plugins.
+         */
+        void destroyPlugins();
+
+    private:
+
+        /**
+         * The plugin loader for loading plugins from shared libraries.
+         */
+        PluginLoader pluginLoader_;
+
+        /**
+         * The list of registered plugins.
+         */
+        PluginVec plugins_;
+};
+
+}
+
+#endif
diff --git a/include/PluginManagerAccessor.hh b/include/PluginManagerAccessor.hh
new file mode 100644
index 0000000000000000000000000000000000000000..3361e88951b33b6953a18c5e0d2d9c70897c92a1
--- /dev/null
+++ b/include/PluginManagerAccessor.hh
@@ -0,0 +1,50 @@
+/**
+ * @file PluginManagerAccessor.h
+ * @brief Mixin class for accessing the plugin manager
+ * @author Jeremy McCormick, SLAC National Accelerator Laboratory
+ */
+
+#ifndef SLIC_PLUGINMANAGERACCESSOR_HH_
+#define SLIC_PLUGINMANAGERACCESSOR_HH_
+
+// LDMX
+#include "PluginManager.hh"
+
+namespace slic {
+
+/**
+ * @class PluginManagerAccessor
+ * @brief Mixin for accessing the PluginManager
+ *
+ * @note
+ * This class is used to assign a plugin manager to each of the user action classes.
+ */
+class PluginManagerAccessor {
+
+    public:
+
+        /**
+         * Set the plugin manager pointer.
+         * @param pluginManager Pointer to the plugin manager.
+         */
+        void setPluginManager(PluginManager* pluginManager) {
+            m_pluginManager = pluginManager;
+        }
+
+        /**
+         * Get the plugin manager.
+         * @return The plugin manager.
+         */
+        PluginManager* getPluginManager() {
+            return m_pluginManager;
+        }
+
+    protected:
+
+        /* The plugin manager pointer; allow protected access for convenience of sub-classes. */
+        PluginManager* m_pluginManager;
+};
+
+}
+
+#endif
diff --git a/include/PluginMessenger.hh b/include/PluginMessenger.hh
new file mode 100644
index 0000000000000000000000000000000000000000..7723dcfc3af01fb48424a54191f21f6e37bcb413
--- /dev/null
+++ b/include/PluginMessenger.hh
@@ -0,0 +1,74 @@
+/**
+ * @file PluginMessenger.h
+ * @brief Class defining a messenger for simulation plugins
+ * @author Jeremy McCormick, SLAC National Accelerator Laboratory
+ */
+
+#ifndef SLIC_PLUGINMESSENGER_HH_
+#define SLIC_PLUGINMESSENGER_HH_
+
+// Geant4
+#include "G4UImessenger.hh"
+
+// LDMX
+#include "PluginManager.hh"
+
+namespace slic {
+
+/**
+ * @class PluginMessenger
+ * @brief Messenger class for loading and destroying user sim plugins using macro commands
+ */
+class PluginMessenger : public G4UImessenger {
+
+    public:
+
+        /**
+         * Class constructor.
+         * @param mgr The plugin manager.
+         */
+        PluginMessenger(PluginManager* mgr);
+
+        /**
+         * Class destructor.
+         */
+        virtual ~PluginMessenger();
+
+        /**
+         * Process the macro command.
+         * @param[in] command The macro command.
+         * @param[in] newValues The argument values.
+         */
+        void SetNewValue(G4UIcommand* command, G4String newValues);
+
+    private:
+
+        /**
+         * The plugin manager.
+         */
+        PluginManager* pluginManager_;
+
+        /**
+         * Directory for plugin commands.
+         */
+        G4UIdirectory* pluginDir_;
+
+        /**
+         * Command for loading a plugin by name.
+         */
+        G4UIcommand* loadCmd_;
+
+        /**
+         * Command for destroying a plugin by name.
+         */
+        G4UIcommand* destroyCmd_;
+
+        /**
+         * Command for listing currently registered plugins.
+         */
+        G4UIcommand* listCmd_;
+};
+
+}
+
+#endif
diff --git a/include/PrimaryGeneratorAction.hh b/include/PrimaryGeneratorAction.hh
index 8cda9f1294f9805ea9477686dad1868074f110c3..37ccdbc3558221823e75d5324ebe064492e09103 100644
--- a/include/PrimaryGeneratorAction.hh
+++ b/include/PrimaryGeneratorAction.hh
@@ -4,6 +4,7 @@
 // SLIC
 #include "EventSourceManager.hh"
 #include "Module.hh"
+#include "PluginManagerAccessor.hh"
 
 // Geant4
 #include "G4VUserPrimaryGeneratorAction.hh"
@@ -17,7 +18,7 @@ namespace slic {
  * @class PrimaryGeneratorAction
  * @brief Implementation of G4VUserPrimaryGeneratorAction.
  */
-class PrimaryGeneratorAction: public G4VUserPrimaryGeneratorAction, public Module {
+class PrimaryGeneratorAction: public G4VUserPrimaryGeneratorAction, public Module, public PluginManagerAccessor {
 
 public:
 
diff --git a/include/RunAction.hh b/include/RunAction.hh
index d04d0d11bf0fd2a95455d17fe6d374d4799e6fc5..22c7c2c3535a769071202c415337ca96651307a3 100644
--- a/include/RunAction.hh
+++ b/include/RunAction.hh
@@ -3,6 +3,7 @@
 
 // slic
 #include "Module.hh"
+#include "PluginManagerAccessor.hh"
 
 // geant4
 #include "G4UserRunAction.hh"
@@ -14,7 +15,7 @@ namespace slic {
  * @class RunAction
  * @brief Implementation of G4UserRunAction.
  */
-class RunAction: public G4UserRunAction, public Module {
+class RunAction: public G4UserRunAction, public Module, public PluginManagerAccessor {
 
 public:
 
diff --git a/include/RunManager.hh b/include/RunManager.hh
index 30441c80f9c4f49c57a6398729b2c2779dcbe33c..15bed1133b455ad2dc3542efad128e84b209f19b 100644
--- a/include/RunManager.hh
+++ b/include/RunManager.hh
@@ -6,6 +6,7 @@
 
 // slic
 #include "Module.hh"
+#include "PluginManager.hh"
 #include "SlicApplication.hh"
 
 namespace slic {
@@ -105,6 +106,7 @@ private:
 
     int m_numberOfEventsToRun;
     bool m_abortRun;
+    PluginManager* m_pluginManager;
 };
 }
 
diff --git a/include/StackingAction.hh b/include/StackingAction.hh
index 46824aa33df67d29ef76d5823d6bb9b542de77d6..ef95e93a8129993399da68457703c479c74ce1a6 100644
--- a/include/StackingAction.hh
+++ b/include/StackingAction.hh
@@ -1,9 +1,13 @@
 #ifndef SLIC_STACKINGACTION_HH_
 #define SLIC_STACKINGACTION_HH_ 1
 
+#include "PluginManagerAccessor.hh"
+
 #include "globals.hh"
 #include "G4UserStackingAction.hh"
 
+namespace slic {
+
 /**
  * @class StackingAction
  *
@@ -15,7 +19,7 @@
  * suspends backscattering tracks until the end
  * of shower development.
  */
-class StackingAction : public G4UserStackingAction {
+class StackingAction : public G4UserStackingAction, public PluginManagerAccessor {
 
 public:
     StackingAction();
@@ -24,7 +28,13 @@ public:
 public:
 
     G4ClassificationOfNewTrack ClassifyNewTrack(const G4Track* aTrack);
+
+    void NewStage();
+
+    void PrepareNewEvent();
 };
 
+}
+
 #endif
 
diff --git a/include/SteppingAction.hh b/include/SteppingAction.hh
index a7bcbe98e9d5081d123c3660a5004e7208b33fea..8bc8366d2daea001e913568b59b2a5faf603a63e 100644
--- a/include/SteppingAction.hh
+++ b/include/SteppingAction.hh
@@ -3,6 +3,7 @@
 
 // SLIC
 #include "Module.hh"
+#include "PluginManagerAccessor.hh"
 
 // Geant4
 #include "G4UserSteppingAction.hh"
@@ -18,7 +19,7 @@ namespace slic {
  * @class SteppingAction
  * @brief Implementation of G4UserSteppingAction.
  */
-class SteppingAction: public G4UserSteppingAction, Module {
+class SteppingAction: public G4UserSteppingAction, public Module, public PluginManagerAccessor {
 
 public:
 
diff --git a/include/TrackingAction.hh b/include/TrackingAction.hh
index 57601489ed01aaf75177e28d4154c5d437a9f30b..1a57cbcaae035dac83e8d9cdf9839ffc274010a9 100644
--- a/include/TrackingAction.hh
+++ b/include/TrackingAction.hh
@@ -1,6 +1,8 @@
 #ifndef SLIC_TRACKINGACTION_HH_
 #define SLIC_TRACKINGACTION_HH_ 1
 
+#include "PluginManagerAccessor.hh"
+
 // Geant4
 #include "G4UserTrackingAction.hh"
 #include "G4TrackingManager.hh"
@@ -13,7 +15,7 @@ class TrajectoryManager;
  * @class TrackingAction
  * @brief Implementation of G4UserTrackingAction
  */
-class TrackingAction: public G4UserTrackingAction {
+class TrackingAction: public G4UserTrackingAction, public PluginManagerAccessor {
 
 public:
 
diff --git a/include/UserActionPlugin.hh b/include/UserActionPlugin.hh
new file mode 100644
index 0000000000000000000000000000000000000000..9324ecc1106a3ba94ad8f3489b841176f4b8edfd
--- /dev/null
+++ b/include/UserActionPlugin.hh
@@ -0,0 +1,215 @@
+/**
+ * @file UserActionPlugin.h
+ * @brief Class defining an interface for a user simulation plugin
+ * @author Jeremy McCormick, SLAC National Accelerator Laboratory
+ */
+
+#ifndef SLIC_USERACTIONPLUGIN_HH_
+#define SLIC_USERACTIONPLUGIN_HH_
+
+// Geant4
+#include "G4Run.hh"
+#include "G4Event.hh"
+#include "G4Track.hh"
+#include "G4Step.hh"
+#include "G4ClassificationOfNewTrack.hh"
+
+namespace slic {
+
+/**
+ * @class UserActionPlugin
+ * @brief User simulation plugin
+ *
+ * @note
+ * This class defines a plugin interface to the Geant4 simulation engine
+ * which is activated in the "user action" hooks.  An implementation
+ * class must define "create" and "destroy" functions as entry points for
+ * the dynamic library loading.  By default, the plugin does not activate
+ * any of the user actions, and the user should override the methods
+ * which return boolean flags to activate these hooks.
+ *
+ * @par
+ * Here are examples of the static functions that should be defined for
+ * creating and destroying the plugin in the class's source file, assuming that
+ * the plugin is called %DummySimPlugin.
+ *
+ * @par
+ * Example create function:
+ * @snippet extern "C" sim::DummySimPlugin* createDummySimPlugin() { return new sim::DummySimPlugin; }
+ *
+ * @par
+ * Example destroy function:
+ * @snippet extern "C" void destroyDummySimPlugin(sim::DummySimPlugin* object) { delete object; }
+ */
+class UserActionPlugin {
+
+    public:
+
+        /**
+         * Class destructor.
+         */
+        virtual ~UserActionPlugin() {
+        }
+
+        /**
+         * Get the name of the plugin.
+         * The user must override this function.
+         * @return The name of the plugin.
+         */
+        virtual std::string getName() = 0;
+
+        /**
+         * Set the verbose level of the plugin (1-4).
+         * @param verbose The verbose level of the plugin.
+         */
+        void setVerboseLevel(int verbose) {
+            verbose_ = verbose;
+            if (verbose_ < 1) {
+                verbose = 1;
+            } else if (verbose_ > 4) {
+                verbose = 4;
+            }
+        }
+
+        /**
+         * Get the current verbose level.
+         * @return The current verbose level.
+         */
+        int getVerboseLevel() {
+            return verbose_;
+        }
+
+        /**
+         * Get whether this plugin implements the run action.
+         * @return True if the plugin implements the run action.
+         */
+        virtual bool hasRunAction() {
+            return false;
+        }
+
+        /**
+         * Get whether this plugin implements the stepping action.
+         * @return True if the plugin implements the stepping action.
+         */
+        virtual bool hasSteppingAction() {
+            return false;
+        }
+
+        /**
+         * Get whether this plugin implements the tracking action.
+         * @return True if the plugin implements the tracking action.
+         */
+        virtual bool hasTrackingAction() {
+            return false;
+        }
+
+        /**
+         * Get whether this plugin implements the event action.
+         * @return True if the plugin implements the event action.
+         */
+        virtual bool hasEventAction() {
+            return false;
+        }
+
+        /**
+         * Get whether this plugin implements the stacking action.
+         * @return True if the plugin implements the stacking action.
+         */
+        virtual bool hasStackingAction() {
+            return false;
+        }
+
+        /**
+         * Get whether this plugin implements the primary generator action.
+         * @return True if the plugin implements the primary generator action.
+         */
+        virtual bool hasPrimaryGeneratorAction() {
+            return false;
+        }
+
+        /**
+         * Begin of run action.
+         */
+        virtual void beginRun(const G4Run*) {
+        }
+
+        /**
+         * End of run action.
+         */
+        virtual void endRun(const G4Run*) {
+        }
+
+        /**
+         * Stepping action.
+         */
+        virtual void stepping(const G4Step*) {
+        }
+
+        /**
+         * Pre-tracking action.
+         */
+        virtual void preTracking(const G4Track*) {
+        }
+
+        /**
+         * Post-tracking action.
+         */
+        virtual void postTracking(const G4Track*) {
+        }
+
+        /**
+         * Begin of event action.
+         */
+        virtual void beginEvent(const G4Event*) {
+        }
+
+        /**
+         * End of event action.
+         */
+        virtual void endEvent(const G4Event*) {
+        }
+
+        /**
+         * Generate primary action.
+         */
+        virtual void generatePrimary(G4Event*) {
+        }
+
+        /**
+         * Classify a new track.
+         * @param currentTrackClass The current track classification.
+         * @return The current track classification returned by default.
+         */
+        virtual G4ClassificationOfNewTrack stackingClassifyNewTrack(const G4Track*, const G4ClassificationOfNewTrack& currentTrackClass) {
+            return currentTrackClass;
+        }
+
+        /**
+         * New stacking stage action.
+         */
+        virtual void stackingNewStage() {
+        }
+
+        /**
+         * New event stacking action.
+         */
+        virtual void stackingPrepareNewEvent() {
+        }
+
+    protected:
+
+        /** Protected access to verbose level for convenience of sub-classes. */
+        int verbose_ {1};
+};
+
+}
+
+#define DECLARE_PLUGIN(NS, NAME) \
+extern "C" NS::NAME* create ## NAME() { \
+    return new NS::NAME; \
+} \
+extern "C" void destroy ## NAME(NS::NAME* object) { \
+    delete object; \
+}
+
+#endif
diff --git a/include/UserActionPluginMessenger.hh b/include/UserActionPluginMessenger.hh
new file mode 100644
index 0000000000000000000000000000000000000000..a6f089afaad3644f05ef9fbdbb027408993ceb14
--- /dev/null
+++ b/include/UserActionPluginMessenger.hh
@@ -0,0 +1,90 @@
+/**
+ * @file UserActionPluginMessenger.h
+ * @brief Class defining a macro messenger for a UserActionPlugin
+ * @author Jeremy McCormick, SLAC National Accelerator Laboratory
+ */
+
+#ifndef SLIC_USERACTIONPLUGINMESSENGER_HH_
+#define SLIC_USERACTIONPLUGINMESSENGER_HH_
+
+// LDMX
+#include "UserActionPlugin.hh"
+
+// Geant4
+#include "G4UImessenger.hh"
+#include "G4UIdirectory.hh"
+#include "G4UIcommand.hh"
+
+namespace slic {
+
+/**
+ * @class UserActionPluginMessenger
+ * @brief Messenger for sending macro commands to a UserActionPlugin
+ *
+ * @note
+ * By default, this class creates a directory for the plugin and provides
+ * a command for setting of the verbose level.  Users can override this
+ * class to provide additional commands for their specific plugins.
+ */
+class UserActionPluginMessenger : public G4UImessenger {
+
+    public:
+
+        /**
+         * Class constructor.
+         * @param userPlugin The associated UserActionPlugin.
+         */
+        UserActionPluginMessenger(UserActionPlugin* userPlugin);
+
+        /**
+         * Class destructor.
+         */
+        virtual ~UserActionPluginMessenger() {
+            delete verboseCmd_;
+            delete pluginDir_;
+        }
+
+        /**
+         * Process the macro command.
+         * @param[in] command The macro command.
+         * @param[in] newValue The argument values.
+         */
+        void SetNewValue(G4UIcommand *command, G4String newValue);
+
+        /**
+         * Get the command path (plugin's macro directory).
+         * @return The command path.
+         */
+        const std::string& getPath() {
+            return pluginDir_->GetCommandPath();
+        }
+
+        /**
+         * Get the associated UserActionPlugin.
+         * @return The associated UserActionPlugin.
+         */
+        UserActionPlugin* getPlugin() {
+            return userPlugin_;
+        }
+
+    private:
+
+        /**
+         * The associated UserActionPligin.
+         */
+        UserActionPlugin* userPlugin_;
+
+        /**
+         * The plugin's command directory.
+         */
+        G4UIdirectory* pluginDir_;
+
+        /**
+         * The command for setting verbose level.
+         */
+        G4UIcommand* verboseCmd_;
+};
+
+}
+
+#endif
diff --git a/plugins/DummySimPlugin.cc b/plugins/DummySimPlugin.cc
new file mode 100644
index 0000000000000000000000000000000000000000..4b154e6e2d25cd9f98b9412c767da4d912f836d3
--- /dev/null
+++ b/plugins/DummySimPlugin.cc
@@ -0,0 +1,115 @@
+/**
+ * @file DummySimPlugin.h
+ * @brief Class that defines a dummy simulation plugin
+ * @author Jeremy McCormick, SLAC National Accelerator Laboratory
+ */
+
+#ifndef SIMPLUGINS_DUMMYSIMPLUGIN_H_
+#define SIMPLUGINS_DUMMYSIMPLUGIN_H_
+
+// LDMX
+#include "UserActionPlugin.hh"
+
+namespace slic {
+
+    /**
+     * @class DummySimPlugin
+     * @brief Dummy implementation of UserActionPlugin
+     */
+    class DummySimPlugin : public UserActionPlugin {
+
+        public:
+
+            DummySimPlugin() {
+                std::cout << "DummySimPlugin::DummySimPlugin - Hello!" << std::endl;
+            }
+
+            virtual ~DummySimPlugin() {
+                std::cout << "DummySimPlugin::~DummySimPlugin - Goodbye!" << std::endl;
+            }
+
+            virtual std::string getName() {
+                return "DummySimPlugin";
+            }
+
+            bool hasRunAction() {
+                return true;
+            }
+
+            bool hasSteppingAction() {
+                return true;
+            }
+
+            bool hasTrackingAction() {
+                return true;
+            }
+
+            bool hasEventAction() {
+                return true;
+            }
+
+            bool hasStackingAction() {
+                return true;
+            }
+
+            bool hasPrimaryGeneratorAction() {
+                return true;
+            }
+
+            void beginRun(const G4Run* run) {
+                std::cout << "DummySimPlugin::beginRun - run " << run->GetRunID() << std::endl;
+            }
+
+            void endRun(const G4Run* run) {
+                std::cout << "DummySimPlugin::endRun - run " << run->GetRunID() << std::endl;
+            }
+
+            void stepping(const G4Step* step) {
+                std::cout << "DummySimPlugin::stepping - pre-point: " << step->GetPreStepPoint()->GetPosition() << ", post-point: ";
+                if (step->GetPostStepPoint()) {
+                    std::cout << step->GetPostStepPoint()->GetPosition();
+                } else {
+                    std::cout << "NONE";
+                }
+                std::cout << std::endl;
+            }
+
+            void preTracking(const G4Track* track) {
+                std::cout << "DummySimPlugin::preTracking - track ID " << track->GetTrackID() << std::endl;
+            }
+
+            void postTracking(const G4Track* track) {
+                std::cout << "DummySimPlugin::postTracking - track ID " << track->GetTrackID() << std::endl;
+            }
+
+            void beginEvent(const G4Event* event) {
+                std::cout << "DummySimPlugin::beginEvent - event " << event->GetEventID() << std::endl;
+            }
+
+            void endEvent(const G4Event* event) {
+                std::cout << "DummySimPlugin::endEvent - event " << event->GetEventID() << std::endl;
+            }
+
+            G4ClassificationOfNewTrack stackingClassifyNewTrack(const G4Track* aTrack, const G4ClassificationOfNewTrack& currentTrackClass) {
+                std::cout << "DummySimPlugin::stackingClassifyNewTrack - track ID " << aTrack->GetTrackID() << " classified as " << currentTrackClass << std::endl;
+                return currentTrackClass;
+            }
+
+            void stackingNewStage() {
+                std::cout << "DummySimPlugin::stackingNewStage" << std::endl;
+            }
+
+            void stackingPrepareNewEvent() {
+                std::cout << "DummySimPlugin::stackingPrepareNewEvent" << std::endl;
+            }
+
+            void generatePrimary(G4Event* event) {
+                std::cout << "DummySimPlugin::generatorPrimary - event " << event->GetEventID() << std::endl;
+            }
+    };
+
+}
+
+DECLARE_PLUGIN(slic, DummySimPlugin)
+
+#endif
diff --git a/scripts/slic-env.sh.in b/scripts/slic-env.sh.in
index a5cb228e6d394847c53f1694737ff61a22bf0a0e..d2e03850185010dbf309dd37ded83b6ab71cc03b 100755
--- a/scripts/slic-env.sh.in
+++ b/scripts/slic-env.sh.in
@@ -1,13 +1,13 @@
 #!/bin/sh
 
-export LD_LIBRARY_PATH=@HEPPDT_DIR@/lib/:@GDML_DIR@/lib:@LCDD_DIR@/lib:@LCIO_DIR@/lib:@XERCES_DIR@/lib
+export LD_LIBRARY_PATH=@HEPPDT_DIR@/lib/:@GDML_DIR@/lib:@LCDD_DIR@/lib:@LCIO_DIR@/lib:@XERCES_DIR@/lib:@CMAKE_INSTALL_PREFIX@/slic/lib
 
 if [ "@CMAKE_CXX_COMPILER@" != "/usr/bin/g++" ]
 then
     export LD_LIBRARY_PATH=$(dirname @CMAKE_CXX_COMPILER@)/../lib64:$LD_LIBRARY_PATH
 fi
 
-export GDML_SCHEMA_DIR=@GDML_DIR@/schemas
+#export GDML_SCHEMA_DIR=@GDML_DIR@/schemas
 
 . @Geant4_DIR@/../../bin/geant4.sh
 
diff --git a/src/EventAction.cc b/src/EventAction.cc
index f2b5ff7ab54796e18d188d33b2c2816644a00db6..9be2cd4b8cf67c26dc714f7ebf034aad8e3bc1f4 100644
--- a/src/EventAction.cc
+++ b/src/EventAction.cc
@@ -27,9 +27,11 @@ EventAction::EventAction() :
 EventAction::~EventAction() {
 }
 
-void EventAction::BeginOfEventAction(const G4Event*) {
+void EventAction::BeginOfEventAction(const G4Event* anEvent) {
     // Reset current track state.
     CurrentTrackState::setCurrentTrackID(-1);
+
+    m_pluginManager->beginEvent(anEvent);
 }
 
 void EventAction::EndOfEventAction(const G4Event *anEvent) {
@@ -50,6 +52,8 @@ void EventAction::EndOfEventAction(const G4Event *anEvent) {
 		LcioManager::instance()->endEvent(anEvent);
 	}
 
+    m_pluginManager->endEvent(anEvent);
+
 	/* Stop the event timer. */
 	stopEventTimer();
 
diff --git a/src/PluginLoader.cc b/src/PluginLoader.cc
new file mode 100644
index 0000000000000000000000000000000000000000..1a44c7aa739a88a59f8b839ccf3de12b948b280a
--- /dev/null
+++ b/src/PluginLoader.cc
@@ -0,0 +1,86 @@
+#include "PluginLoader.hh"
+
+#include <dlfcn.h>
+
+namespace slic {
+
+UserActionPlugin* PluginLoader::create(std::string pluginName, std::string libName) {
+
+    // Open a handle to the specific dynamic lib.
+    void* handle = dlopen(libName.c_str(), RTLD_LAZY);
+
+    // Plugin to be created.
+    UserActionPlugin* plugin = nullptr;
+
+    // Is there a proper handle to the lib?
+    if (handle) {
+
+        // Get a function pointer from the library to create the plugin object.
+        UserActionPlugin* (*createIt)();
+        createIt = (UserActionPlugin* (*)())dlsym(handle, std::string("create" + pluginName).c_str());
+
+        // Did we get back a good function pointer from the lib?
+        if(createIt) {
+
+            // Create the plugin object from the function pointer.
+            plugin = (UserActionPlugin*)createIt();
+
+            // Was the plugin created successfully?
+            if (plugin) {
+                // Register the plugin handle for when it needs to be destroyed.
+                this->pluginHandles_[plugin] = handle;
+            } else {
+                // For some reason, the plugin could not be created by the lib function!
+                std::cerr << "[ PluginLoader ] : Failed to create plugin " << pluginName << " from lib "
+                << libName << "!!!" << std::endl;
+                throw std::runtime_error("Failed to create the plugin.");
+            }
+        } else {
+            // The create function was not found in the lib.
+            std::cerr << "[ PluginLoader ] : Failed to find create function for plugin " << pluginName << " in lib "
+            << libName << "!!!" << std::endl;
+            throw std::runtime_error("Failed to find library function for creating plugin.");
+        }
+    } else {
+        // The plugin lib could not be opened.  Probably it doesn't exist!
+        std::cerr << "[ PluginLoader ] : Could not open " << libName << " plugin lib." << std::endl;
+        throw std::runtime_error("Plugin lib not found.");
+    }
+
+    return plugin;
+}
+
+void PluginLoader::destroy(UserActionPlugin* plugin) {
+
+    // Is the plugin argument non-null?
+    if (plugin) {
+
+        // Get the lib handle for the plugin.
+        void* handle = this->pluginHandles_[plugin];
+
+        // Do we have a lib handle for this plugin?
+        if (handle) {
+
+            // Destroy the plugin by calling its destroy method from the lib.
+            void (*destroyIt)(UserActionPlugin*);
+            destroyIt = (void (*)(UserActionPlugin*))dlsym(handle, std::string("destroy" + plugin->getName()).c_str());destroyIt
+            (plugin);
+
+            // Delete from plugin handles.
+            std::map<UserActionPlugin*, void*>::iterator it;
+            it = this->pluginHandles_.find(plugin);
+            if (it != pluginHandles_.end()) {
+                this->pluginHandles_.erase(it);
+            }
+        } else {
+            // No handle exists for this plugin.  Maybe it wasn't registered with this PluginLoader?
+            std::cerr << "[ PluginLoader ] : Failed to find handle for " << plugin->getName() << " plugin!!!" << std::endl;
+            throw std::runtime_error("Failed to find handle for plugin.");
+        }
+    } else {
+        // Null argument is just ignored with a warning.
+        std::cerr << "[ PluginLoader ] : Ignoring null plugin argument. " << std::endl;
+    }
+}
+
+}
diff --git a/src/PluginManager.cc b/src/PluginManager.cc
new file mode 100644
index 0000000000000000000000000000000000000000..64ef4288e21fb1ede77ecda9c77f64d32d93f94d
--- /dev/null
+++ b/src/PluginManager.cc
@@ -0,0 +1,173 @@
+#include "PluginManager.hh"
+
+namespace slic {
+
+PluginManager::~PluginManager() {
+    destroyPlugins();
+}
+
+void PluginManager::beginRun(const G4Run* run) {
+    for (PluginVec::iterator it = plugins_.begin(); it != plugins_.end(); it++) {
+        if ((*it)->hasRunAction()) {
+            (*it)->beginRun(run);
+        }
+    }
+}
+
+void PluginManager::endRun(const G4Run* run) {
+    for (PluginVec::iterator it = plugins_.begin(); it != plugins_.end(); it++) {
+        if ((*it)->hasRunAction()) {
+            (*it)->endRun(run);
+        }
+    }
+}
+
+void PluginManager::stepping(const G4Step* step) {
+    for (PluginVec::iterator it = plugins_.begin(); it != plugins_.end(); it++) {
+        if ((*it)->hasSteppingAction()) {
+            (*it)->stepping(step);
+        }
+    }
+}
+
+void PluginManager::preTracking(const G4Track* track) {
+    for (PluginVec::iterator it = plugins_.begin(); it != plugins_.end(); it++) {
+        if ((*it)->hasTrackingAction()) {
+            (*it)->preTracking(track);
+        }
+    }
+}
+
+void PluginManager::postTracking(const G4Track* track) {
+    for (PluginVec::iterator it = plugins_.begin(); it != plugins_.end(); it++) {
+        if ((*it)->hasTrackingAction()) {
+            (*it)->postTracking(track);
+        }
+    }
+}
+
+void PluginManager::beginEvent(const G4Event* event) {
+    for (PluginVec::iterator it = plugins_.begin(); it != plugins_.end(); it++) {
+        if ((*it)->hasEventAction()) {
+            (*it)->beginEvent(event);
+        }
+    }
+}
+
+void PluginManager::endEvent(const G4Event* event) {
+    for (PluginVec::iterator it = plugins_.begin(); it != plugins_.end(); it++) {
+        if ((*it)->hasEventAction()) {
+            (*it)->endEvent(event);
+        }
+    }
+}
+
+void PluginManager::generatePrimary(G4Event* event) {
+    for (PluginVec::iterator it = plugins_.begin(); it != plugins_.end(); it++) {
+        if ((*it)->hasPrimaryGeneratorAction()) {
+            (*it)->generatePrimary(event);
+        }
+    }
+}
+
+G4ClassificationOfNewTrack PluginManager::stackingClassifyNewTrack(const G4Track* track) {
+
+    // Default value of a track is fUrgent.
+    G4ClassificationOfNewTrack currentTrackClass = G4ClassificationOfNewTrack::fUrgent;
+
+    for (PluginVec::iterator it = plugins_.begin(); it != plugins_.end(); it++) {
+        if ((*it)->hasStackingAction()) {
+
+            // Get proposed new track classification from this plugin.
+            G4ClassificationOfNewTrack newTrackClass = (*it)->stackingClassifyNewTrack(track, currentTrackClass);
+
+            // Only set the current classification if the plugin changed it.
+            if (newTrackClass != currentTrackClass) {
+
+                // Set the track classification from this plugin.
+                currentTrackClass = newTrackClass;
+            }
+        }
+    }
+
+    // Return the current track classification.
+    return currentTrackClass;
+}
+
+void PluginManager::stackingNewStage() {
+    for (PluginVec::iterator it = plugins_.begin(); it != plugins_.end(); it++) {
+        if ((*it)->hasEventAction()) {
+            (*it)->stackingNewStage();
+        }
+    }
+}
+
+void PluginManager::stackingPrepareNewEvent() {
+    for (PluginVec::iterator it = plugins_.begin(); it != plugins_.end(); it++) {
+        if ((*it)->hasStackingAction()) {
+            (*it)->stackingPrepareNewEvent();
+        }
+    }
+}
+
+UserActionPlugin* PluginManager::findPlugin(const std::string& pluginName) {
+    UserActionPlugin* foundPlugin = nullptr;
+    for (PluginVec::iterator iPlugin = plugins_.begin(); iPlugin != plugins_.end(); iPlugin++) {
+        if ((*iPlugin)->getName() == pluginName) {
+            foundPlugin = *iPlugin;
+            break;
+        }
+    }
+    return foundPlugin;
+}
+
+void PluginManager::create(const std::string& pluginName, const std::string& libName) {
+    if (findPlugin(pluginName) == nullptr) {
+        UserActionPlugin* plugin = pluginLoader_.create(pluginName, libName);
+        registerPlugin(plugin);
+    } else {
+        std::cerr << "[ PluginManager ] - Plugin " << pluginName << " already exists." << std::endl;
+        throw new std::runtime_error("Plugin already loaded.");
+    }
+}
+
+void PluginManager::destroy(const std::string& pluginName) {
+    UserActionPlugin* plugin = findPlugin(pluginName);
+    if (plugin != nullptr) {
+        destroy(plugin);
+    }
+}
+
+void PluginManager::destroy(UserActionPlugin* plugin) {
+    if (plugin != nullptr) {
+        deregisterPlugin(plugin);
+        pluginLoader_.destroy(plugin);
+    }
+}
+
+std::ostream& PluginManager::print(std::ostream& os) {
+    for (PluginVec::iterator iPlugin = plugins_.begin(); iPlugin != plugins_.end(); iPlugin++) {
+        os << (*iPlugin)->getName() << std::endl;
+    }
+    return os;
+}
+
+void PluginManager::registerPlugin(UserActionPlugin* plugin) {
+    plugins_.push_back(plugin);
+}
+
+void PluginManager::deregisterPlugin(UserActionPlugin* plugin) {
+    std::vector<UserActionPlugin*>::iterator pos = std::find(plugins_.begin(), plugins_.end(), plugin);
+    if (pos != plugins_.end()) {
+        plugins_.erase(pos);
+    }
+}
+
+void PluginManager::destroyPlugins() {
+    for (unsigned iPlugin = 0; iPlugin < plugins_.size(); iPlugin++) {
+        destroy(plugins_[iPlugin]);
+    }
+    plugins_.clear();
+}
+
+} // namespace sim
diff --git a/src/PluginMessenger.cc b/src/PluginMessenger.cc
new file mode 100644
index 0000000000000000000000000000000000000000..dda18ea10e4219058e33a10de36080ae78ba6efa
--- /dev/null
+++ b/src/PluginMessenger.cc
@@ -0,0 +1,60 @@
+#include "PluginMessenger.hh"
+
+namespace slic {
+
+PluginMessenger::PluginMessenger(PluginManager* thePluginManager) :
+        pluginManager_(thePluginManager) {
+
+    pluginDir_ = new G4UIdirectory("/slic/plugins/");
+    pluginDir_->SetGuidance("Commands for controlling LDMX sim plugins");
+
+    loadCmd_ = new G4UIcommand("/slic/plugins/load", this);
+    loadCmd_->AvailableForStates(G4ApplicationState::G4State_PreInit, G4ApplicationState::G4State_Idle);
+    loadCmd_->SetGuidance("Load a sim plugin from a shared library.  A plugin of a specific type may only be loaded once.");
+    G4UIparameter* pluginName = new G4UIparameter("pluginName", 's', false);
+    pluginName->SetGuidance("Name of plugin to load.");
+    loadCmd_->SetParameter(pluginName);
+    G4UIparameter* libName = new G4UIparameter("libName", 's', true);
+    libName->SetGuidance("Name of plugin library; if omitted will use the 'libSimPlugins.so' library.");
+    loadCmd_->SetParameter(libName);
+
+    destroyCmd_ = new G4UIcommand("/slic/plugins/destroy", this);
+    destroyCmd_->SetGuidance("Destroy a loaded plugin by name.");
+    destroyCmd_->AvailableForStates(G4ApplicationState::G4State_Idle);
+    pluginName = new G4UIparameter("pluginName", 's', false);
+    pluginName->SetGuidance("Name of plugin to destroy.");
+    destroyCmd_->SetParameter(pluginName);
+
+    listCmd_ = new G4UIcommand("/slic/plugins/list", this);
+    listCmd_->SetGuidance("List currently loaded plugins.");
+    listCmd_->AvailableForStates(G4ApplicationState::G4State_Idle, G4ApplicationState::G4State_PreInit);
+}
+
+PluginMessenger::~PluginMessenger() {
+    delete pluginDir_;
+    delete loadCmd_;
+    delete destroyCmd_;
+    delete listCmd_;
+}
+
+void PluginMessenger::SetNewValue(G4UIcommand* command, G4String newValues) {
+
+    std::istringstream is((const char*) newValues);
+    std::string pluginName, libName;
+
+    is >> pluginName >> libName;
+
+    if (libName.size() == 0 || libName == "") {
+        libName = "libslicPlugins.so";
+    }
+
+    if (command == loadCmd_) {
+        pluginManager_->create(pluginName, libName);
+    } else if (command == destroyCmd_) {
+        pluginManager_->destroy(pluginName);
+    } else if (command == listCmd_) {
+        pluginManager_->print(std::cout);
+    }
+}
+
+}
diff --git a/src/PrimaryGeneratorAction.cc b/src/PrimaryGeneratorAction.cc
index ce7eade8a514b1e265dd0ca0b39c0bdcb53994e9..58b9941d8df086d78757aee87df918df26cf8718 100644
--- a/src/PrimaryGeneratorAction.cc
+++ b/src/PrimaryGeneratorAction.cc
@@ -51,6 +51,8 @@ void PrimaryGeneratorAction::GeneratePrimaries(G4Event *anEvent) {
 	if (_manager->isEOF()) {
 		SlicApplication::instance()->setAborting(true);
 	}
+
+	m_pluginManager->generatePrimary(anEvent);
 }
 
 void PrimaryGeneratorAction::printBeginEventMessage(G4Event* anEvent) {
diff --git a/src/RunAction.cc b/src/RunAction.cc
index e1d4a8bfdcf4a810ae2a492d22cf746e1b09a6e7..6eb59b25a8dc35605ec32ad5efbebe340aa6a34e 100644
--- a/src/RunAction.cc
+++ b/src/RunAction.cc
@@ -37,6 +37,8 @@ void RunAction::BeginOfRunAction(const G4Run *aRun) {
 
 	// Execute GeometryManager's beginRun action.
 	GeometryManager::instance()->beginRun(aRun);
+
+	 m_pluginManager->beginRun(aRun);
 }
 
 void RunAction::EndOfRunAction(const G4Run *run) {
@@ -47,6 +49,8 @@ void RunAction::EndOfRunAction(const G4Run *run) {
 	// event source mgr
 	EventSourceManager::instance()->endRun(run);
 
+	m_pluginManager->endRun(run);
+
 	// stop run clock
 	stopRunTimer();
 
diff --git a/src/RunManager.cc b/src/RunManager.cc
index 327d9b75e000f45d71f5ac0b77e9dd91ef3f68e8..35de48a9b0f9ba994061e0cd1de3dbf981b533c1 100644
--- a/src/RunManager.cc
+++ b/src/RunManager.cc
@@ -10,6 +10,7 @@
 #include "StackingAction.hh"
 #include "SteppingAction.hh"
 #include "TrackingAction.hh"
+#include "PluginMessenger.hh"
 
 // LCDD
 #include "lcdd/core/LCDDParser.hh"
@@ -26,6 +27,7 @@ RunManager::RunManager() :
 }
 
 RunManager::~RunManager() {
+    delete m_pluginManager;
 }
 
 void RunManager::Initialize() {
@@ -37,12 +39,29 @@ void RunManager::Initialize() {
 	// This makes sure that physics initialization occurs before other user actions.
 	G4RunManager::Initialize();
 
-    SetUserAction(new PrimaryGeneratorAction());
-    SetUserAction(new RunAction());
-    SetUserAction(new EventAction());
-    SetUserAction(new TrackingAction());
-    SetUserAction(new SteppingAction());
-    SetUserAction(new StackingAction());
+	m_pluginManager = new PluginManager();
+	new PluginMessenger(m_pluginManager);
+
+	PrimaryGeneratorAction* genAction = new PrimaryGeneratorAction;
+	RunAction* runAction = new RunAction;
+	EventAction* eventAction = new EventAction;
+	TrackingAction* trackingAction = new TrackingAction;
+	SteppingAction* steppingAction = new SteppingAction;
+	StackingAction* stackingAction = new StackingAction;
+
+	genAction->setPluginManager(m_pluginManager);
+	runAction->setPluginManager(m_pluginManager);
+	eventAction->setPluginManager(m_pluginManager);
+	trackingAction->setPluginManager(m_pluginManager);
+	steppingAction->setPluginManager(m_pluginManager);
+	stackingAction->setPluginManager(m_pluginManager);
+
+    SetUserAction(genAction);
+    SetUserAction(runAction);
+    SetUserAction(eventAction);
+    SetUserAction(trackingAction);
+    SetUserAction(steppingAction);
+    SetUserAction(stackingAction);
 
 	// Initialize the event generation manager.
 	EventSourceManager::instance();
@@ -141,3 +160,4 @@ bool RunManager::isRunAborted() {
 	return m_abortRun;
 }
 } // namespace slic
+
diff --git a/src/StackingAction.cc b/src/StackingAction.cc
index c3e6057fd98d3f227144620a0f1535103a602e46..9b2381e373cbbb868c03bf7a54910aedeb9fbadf 100644
--- a/src/StackingAction.cc
+++ b/src/StackingAction.cc
@@ -5,6 +5,8 @@
 #include "G4DecayProducts.hh"
 #include "G4ios.hh"
 
+namespace slic {
+
 StackingAction::StackingAction() {
 }
 
@@ -12,8 +14,17 @@ StackingAction::~StackingAction() {
 }
 
 G4ClassificationOfNewTrack StackingAction::ClassifyNewTrack(const G4Track* aTrack) {
-    G4ClassificationOfNewTrack classification = fUrgent;
-    if(aTrack->GetTrackStatus()==fSuspend) classification = fWaiting;
-    return classification;
+    // FIXME: Need to determine how these are supposed to work together!
+    G4ClassificationOfNewTrack classification = m_pluginManager->stackingClassifyNewTrack(aTrack);
+    if(aTrack->GetTrackStatus() == fSuspend) classification = fWaiting;
+}
+
+void StackingAction::NewStage() {
+    m_pluginManager->stackingNewStage();
 }
 
+void StackingAction::PrepareNewEvent() {
+    m_pluginManager->stackingPrepareNewEvent();
+}
+
+}
diff --git a/src/SteppingAction.cc b/src/SteppingAction.cc
index afaf279675395e73941f044ed4eeac92a0819c38..5fdda8ae8f21f64dc6cf7b3cfd709a8ed8805513 100644
--- a/src/SteppingAction.cc
+++ b/src/SteppingAction.cc
@@ -48,6 +48,8 @@ void SteppingAction::UserSteppingAction(const G4Step* step) {
 
 	/* Check if the track should be killed. */
 	checkKillTrack(step);
+
+	m_pluginManager->stepping(step);
 }
 
 void SteppingAction::checkKillTrack(const G4Step* aStep) {
diff --git a/src/TrackingAction.cc b/src/TrackingAction.cc
index 09cffd7803fcddec1269cebd1138632422c3f898..a18a70f218e5151481d84dbf7505e695c7f1eda2 100644
--- a/src/TrackingAction.cc
+++ b/src/TrackingAction.cc
@@ -64,6 +64,8 @@ void TrackingAction::PreUserTrackingAction(const G4Track* track) {
             fpTrackingManager->SetStoreTrajectory(false);
         }
     }
+
+    m_pluginManager->preTracking(track);
 }
 
 void TrackingAction::PostUserTrackingAction(const G4Track* track) {
@@ -85,6 +87,8 @@ void TrackingAction::PostUserTrackingAction(const G4Track* track) {
         // Update the track summary.
         trackSummary->update(track);
     }
+
+    m_pluginManager->postTracking(track);
 }
 
 } // namespace slic
diff --git a/src/UserActionPluginMessenger.cc b/src/UserActionPluginMessenger.cc
new file mode 100644
index 0000000000000000000000000000000000000000..cc39851ac3a3e3524190145b4f1a0a5092f93b31
--- /dev/null
+++ b/src/UserActionPluginMessenger.cc
@@ -0,0 +1,24 @@
+#include "UserActionPluginMessenger.hh"
+
+namespace slic {
+
+UserActionPluginMessenger::UserActionPluginMessenger(UserActionPlugin* userPlugin) :
+        userPlugin_(userPlugin) {
+
+    pluginDir_ = new G4UIdirectory(std::string("/slic/plugins/" + userPlugin->getName() + "/").c_str());
+    pluginDir_->SetGuidance(std::string("Commands for the sim plugin " + userPlugin->getName()).c_str());
+
+    verboseCmd_ = new G4UIcommand(std::string(getPath() + "verbose").c_str(), this);
+    G4UIparameter* verbose = new G4UIparameter("verboseLevel", 'i', false);
+    verboseCmd_->SetParameter(verbose);
+    verboseCmd_->SetGuidance("Set the verbosity level of the sim plugin (1-4).");
+    verboseCmd_->AvailableForStates(G4ApplicationState::G4State_PreInit, G4ApplicationState::G4State_Idle);
+}
+
+void UserActionPluginMessenger::SetNewValue(G4UIcommand *command, G4String newValue) {
+    if (command == verboseCmd_) {
+        userPlugin_->setVerboseLevel(std::atoi(newValue));
+        std::cout << userPlugin_->getName() << " verbose set to " << userPlugin_->getVerboseLevel() << std::endl;
+    }
+}
+}