diff --git a/notebooks/workflow_example.ipynb b/notebooks/workflow_example.ipynb
index 229760f8..defccc32 100644
--- a/notebooks/workflow_example.ipynb
+++ b/notebooks/workflow_example.ipynb
@@ -644,8 +644,8 @@
     {
      "data": {
       "text/plain": [
-       "array([0.91077351, 0.33860412, 0.59806048, 0.66528464, 0.80125293,\n",
-       "       0.31981677, 0.54395521, 0.4926537 , 0.52626431, 0.7848854 ])"
+       "array([0.49455794, 0.6789772 , 0.48470916, 0.43574953, 0.18030331,\n",
+       "       0.6059215 , 0.65871187, 0.42205006, 0.65062977, 0.5390317 ])"
       ]
      },
      "execution_count": 22,
@@ -654,7 +654,7 @@
     },
     {
      "data": {
-      "image/png": "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",
+      "image/png": "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",
       "text/plain": [
        "<Figure size 640x480 with 1 Axes>"
       ]
@@ -1209,7 +1209,7 @@
        "</svg>\n"
       ],
       "text/plain": [
-       "<graphviz.graphs.Digraph at 0x143bdfd90>"
+       "<graphviz.graphs.Digraph at 0x146119350>"
       ]
      },
      "execution_count": 28,
@@ -1228,7 +1228,13 @@
    "source": [
     "# Example with pre-built nodes\n",
     "\n",
-    "Currently we have a handfull of pre-build nodes available for import from the `nodes` package. Let's use these to quickly put together a workflow for looking at some MD data."
+    "Currently we have a handfull of pre-build nodes available for import from the `nodes` package. Let's use these to quickly put together a workflow for looking at some MD data.\n",
+    "\n",
+    "To access prebuilt nodes we can `.create` them. This works both from the workflow class _and_ from a workflow instance. In the latter case, created nodes automatically take the creating workflow instance as their `parent`.\n",
+    "\n",
+    "There are a few of nodes that are always available under the `Workflow.create.standard` namespace, otherwise we need to register new node packages. This is done with the `register` method, which takes the domain (namespace/key/attribute/whatever you want to call it) under which you want to register the new nodes, and a string import path to a module that has a list of nodes under the name `nodes`, i.e. the module has the property `nodes: list[pyiron_workflow.nodes.Node]`. (This API is subject to change, as we work to improve usability and bring node packages more and more in line with \"FAIR\" principles.)\n",
+    "\n",
+    "You can make your own `.py` files with nodes for reuse this way, but `pyiron_workflow` also comes with a couple of packages. In this example we'll use atomistics and plotting:"
    ]
   },
   {
@@ -1240,7 +1246,7 @@
     {
      "data": {
       "application/vnd.jupyter.widget-view+json": {
-       "model_id": "a289b513c50d41989670c5b4ac9df823",
+       "model_id": "4f07c83c4a694b76847e9060c58c00d0",
        "version_major": 2,
        "version_minor": 0
       },
@@ -1249,14 +1255,6 @@
      "metadata": {},
      "output_type": "display_data"
     },
-    {
-     "name": "stderr",
-     "output_type": "stream",
-     "text": [
-      "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/channels.py:158: UserWarning: The channel run was not connected to ran, andthus could not disconnect from it.\n",
-      "  warn(\n"
-     ]
-    },
     {
      "name": "stdout",
      "output_type": "stream",
@@ -1267,7 +1265,7 @@
     {
      "data": {
       "text/plain": [
-       "<matplotlib.collections.PathCollection at 0x14f4aa190>"
+       "<matplotlib.collections.PathCollection at 0x151b0b8d0>"
       ]
      },
      "execution_count": 29,
@@ -1286,16 +1284,18 @@
     }
    ],
    "source": [
+    "wf.register(\"atomistics\", \"pyiron_workflow.node_library.atomistics\")\n",
+    "wf.register(\"plotting\", \"pyiron_workflow.node_library.plotting\")\n",
+    "\n",
     "wf = Workflow(\"with_prebuilt\")\n",
     "\n",
     "wf.structure = wf.create.atomistics.Bulk(cubic=True, name=\"Al\")\n",
     "wf.engine = wf.create.atomistics.Lammps(structure=wf.structure)\n",
     "wf.calc = wf.create.atomistics.CalcMd(job=wf.engine)\n",
-    "wf.plot = wf.create.standard.Scatter(\n",
+    "wf.plot = wf.create.plotting.Scatter(\n",
     "    x=wf.calc.outputs.steps, \n",
     "    y=wf.calc.outputs.temperature\n",
     ")\n",
-    "wf.structure > wf.engine > wf.calc > wf.plot\n",
     "\n",
     "out = wf.run()\n",
     "out.plot__fig"
@@ -1330,27 +1330,27 @@
        "<title>clusterwith_prebuilt</title>\n",
        "<polygon fill=\"white\" stroke=\"none\" points=\"-4,4 -4,-818.25 487.64,-818.25 487.64,4 -4,4\"/>\n",
        "<text text-anchor=\"middle\" x=\"241.82\" y=\"-4.95\" font-family=\"Times,serif\" font-size=\"14.00\">with_prebuilt: Workflow</text>\n",
-       "<g id=\"clust1\" class=\"cluster\">\n",
-       "<title>clusterwith_prebuiltInputs</title>\n",
+       "<g id=\"clust2\" class=\"cluster\">\n",
+       "<title>clusterwith_prebuiltOutputs</title>\n",
        "<defs>\n",
-       "<linearGradient id=\"clust1_l_0\" gradientUnits=\"userSpaceOnUse\" x1=\"8\" y1=\"-445.25\" x2=\"227.53\" y2=\"-445.25\" >\n",
+       "<linearGradient id=\"clust2_l_0\" gradientUnits=\"userSpaceOnUse\" x1=\"475.64\" y1=\"-418.25\" x2=\"247.53\" y2=\"-418.25\" >\n",
        "<stop offset=\"0\" style=\"stop-color:#7f7f7f;stop-opacity:1.;\"/>\n",
        "<stop offset=\"1\" style=\"stop-color:#d9d9d9;stop-opacity:1.;\"/>\n",
        "</linearGradient>\n",
        "</defs>\n",
-       "<polygon fill=\"url(#clust1_l_0)\" stroke=\"black\" points=\"8,-84.25 8,-806.25 227.53,-806.25 227.53,-84.25 8,-84.25\"/>\n",
-       "<text text-anchor=\"middle\" x=\"117.76\" y=\"-788.95\" font-family=\"Times,serif\" font-size=\"14.00\">Inputs</text>\n",
+       "<polygon fill=\"url(#clust2_l_0)\" stroke=\"black\" points=\"247.53,-30.25 247.53,-806.25 475.64,-806.25 475.64,-30.25 247.53,-30.25\"/>\n",
+       "<text text-anchor=\"middle\" x=\"361.58\" y=\"-788.95\" font-family=\"Times,serif\" font-size=\"14.00\">Outputs</text>\n",
        "</g>\n",
-       "<g id=\"clust2\" class=\"cluster\">\n",
-       "<title>clusterwith_prebuiltOutputs</title>\n",
+       "<g id=\"clust1\" class=\"cluster\">\n",
+       "<title>clusterwith_prebuiltInputs</title>\n",
        "<defs>\n",
-       "<linearGradient id=\"clust2_l_1\" gradientUnits=\"userSpaceOnUse\" x1=\"475.64\" y1=\"-418.25\" x2=\"247.53\" y2=\"-418.25\" >\n",
+       "<linearGradient id=\"clust1_l_1\" gradientUnits=\"userSpaceOnUse\" x1=\"8\" y1=\"-445.25\" x2=\"227.53\" y2=\"-445.25\" >\n",
        "<stop offset=\"0\" style=\"stop-color:#7f7f7f;stop-opacity:1.;\"/>\n",
        "<stop offset=\"1\" style=\"stop-color:#d9d9d9;stop-opacity:1.;\"/>\n",
        "</linearGradient>\n",
        "</defs>\n",
-       "<polygon fill=\"url(#clust2_l_1)\" stroke=\"black\" points=\"247.53,-30.25 247.53,-806.25 475.64,-806.25 475.64,-30.25 247.53,-30.25\"/>\n",
-       "<text text-anchor=\"middle\" x=\"361.58\" y=\"-788.95\" font-family=\"Times,serif\" font-size=\"14.00\">Outputs</text>\n",
+       "<polygon fill=\"url(#clust1_l_1)\" stroke=\"black\" points=\"8,-84.25 8,-806.25 227.53,-806.25 227.53,-84.25 8,-84.25\"/>\n",
+       "<text text-anchor=\"middle\" x=\"117.76\" y=\"-788.95\" font-family=\"Times,serif\" font-size=\"14.00\">Inputs</text>\n",
        "</g>\n",
        "<!-- clusterwith_prebuiltInputsrun -->\n",
        "<g id=\"node1\" class=\"node\">\n",
@@ -1519,7 +1519,7 @@
        "</svg>\n"
       ],
       "text/plain": [
-       "<graphviz.graphs.Digraph at 0x14f4a1cd0>"
+       "<graphviz.graphs.Digraph at 0x14621b010>"
       ]
      },
      "execution_count": 30,
@@ -1531,6 +1531,14 @@
     "wf.draw(depth=0)"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "id": "b2990bbf-28fb-43e2-a01d-82377d12879c",
+   "metadata": {},
+   "source": [
+    "Note: the `draw` call returns a `graphviz.graphs.Digraphs` object; these get natively rendered alright in jupyter notebooks, as seen above, but you can also snag the object in a variable and do everything else graphviz allows, e.g. using the `render` method on the object to save it to file. Cf. the graphviz docs for details."
+   ]
+  },
   {
    "cell_type": "markdown",
    "id": "d1f3b308-28b2-466b-8cf5-6bfd806c08ca",
@@ -2939,7 +2947,7 @@
        "</svg>\n"
       ],
       "text/plain": [
-       "<graphviz.graphs.Digraph at 0x14efd7650>"
+       "<graphviz.graphs.Digraph at 0x151b7d790>"
       ]
      },
      "execution_count": 36,
@@ -2982,7 +2990,7 @@
      "name": "stderr",
      "output_type": "stream",
      "text": [
-      "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/channels.py:158: UserWarning: The channel run was not connected to ran, andthus could not disconnect from it.\n",
+      "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/channels.py:158: UserWarning: The channel ran was not connected to run, andthus could not disconnect from it.\n",
       "  warn(\n"
      ]
     },
@@ -3057,6 +3065,14 @@
    "id": "ed4a3a22-fc3a-44c9-9d4f-c65bc1288889",
    "metadata": {},
    "outputs": [
+    {
+     "name": "stderr",
+     "output_type": "stream",
+     "text": [
+      "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/channels.py:158: UserWarning: The channel ran was not connected to run, andthus could not disconnect from it.\n",
+      "  warn(\n"
+     ]
+    },
     {
      "name": "stdout",
      "output_type": "stream",
@@ -3083,7 +3099,7 @@
      "name": "stderr",
      "output_type": "stream",
      "text": [
-      "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/channels.py:158: UserWarning: The channel run was not connected to ran, andthus could not disconnect from it.\n",
+      "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/channels.py:158: UserWarning: The channel ran was not connected to run, andthus could not disconnect from it.\n",
       "  warn(\n"
      ]
     },
@@ -3137,7 +3153,7 @@
     "\n",
     "Serialization doesn't exist yet.\n",
     "\n",
-    "What you _can_ do is `register` new lists of nodes (including macros) with the workflow, so feel free to build up your own `.py` files containing nodes you like to use for easy re-use.\n",
+    "What you _can_ do is `register` new lists of nodes (including macros) with the workflow, so feel free to build up your own `.py` files containing nodes you like to use for easy re-use. Registration is now discussed in the main body of the notebook, but the API may change significantly going forward.\n",
     "\n",
     "Serialization of workflows is still forthcoming, while for node registration flexibility and documentation is forthcoming but the basics are here already."
    ]
@@ -3307,8 +3323,8 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "0.064 <= 0.2\n",
-      "Finally 0.064\n"
+      "0.012 <= 0.2\n",
+      "Finally 0.012\n"
      ]
     }
    ],
diff --git a/pyiron_workflow/_tests.py b/pyiron_workflow/_tests.py
new file mode 100644
index 00000000..bbebb826
--- /dev/null
+++ b/pyiron_workflow/_tests.py
@@ -0,0 +1,15 @@
+"""
+Tools specifically for the test suite, not intended for general use.
+"""
+
+from pathlib import Path
+import sys
+
+
+def ensure_tests_in_python_path():
+    """So that you can import from the static module"""
+    path_to_tests = Path(__file__).parent.parent / "tests"
+    as_string = str(path_to_tests.resolve())
+
+    if as_string not in sys.path:
+        sys.path.append(as_string)
diff --git a/pyiron_workflow/channels.py b/pyiron_workflow/channels.py
index 4cfee805..cb3958ca 100644
--- a/pyiron_workflow/channels.py
+++ b/pyiron_workflow/channels.py
@@ -398,6 +398,7 @@ def to_dict(self) -> dict:
         d = super().to_dict()
         d["value"] = repr(self.value)
         d["ready"] = self.ready
+        d["type_hint"] = str(self.type_hint)
         return d
 
 
diff --git a/pyiron_workflow/composite.py b/pyiron_workflow/composite.py
index 270992e0..3b1fce86 100644
--- a/pyiron_workflow/composite.py
+++ b/pyiron_workflow/composite.py
@@ -6,7 +6,7 @@
 from __future__ import annotations
 
 from abc import ABC, abstractmethod
-from functools import partial
+from functools import partial, wraps
 from typing import Literal, Optional, TYPE_CHECKING
 
 from bidict import bidict
@@ -422,6 +422,11 @@ def replace(self, owned_node: Node | str, replacement: Node | type[Node]) -> Nod
             self.starting_nodes.append(replacement)
         return owned_node
 
+    @classmethod
+    @wraps(Creator.register)
+    def register(cls, domain: str, package_identifier: str) -> None:
+        cls.create.register(domain=domain, package_identifier=package_identifier)
+
     def __setattr__(self, key: str, node: Node):
         if isinstance(node, Node) and key != "parent":
             self.add(node, label=key)
diff --git a/pyiron_workflow/function.py b/pyiron_workflow/function.py
index aaea64b7..058f79e7 100644
--- a/pyiron_workflow/function.py
+++ b/pyiron_workflow/function.py
@@ -322,6 +322,7 @@ def __init__(
         else:
             # If a callable node function is received, use it
             self.node_function = node_function
+            self._type_hints = get_type_hints(node_function)
 
         super().__init__(
             label=label if label is not None else self.node_function.__name__,
@@ -382,7 +383,7 @@ def outputs(self) -> Outputs:
 
     def _build_input_channels(self):
         channels = []
-        type_hints = get_type_hints(self.node_function)
+        type_hints = self._type_hints
 
         for ii, (label, value) in enumerate(self._input_args.items()):
             is_self = False
@@ -435,7 +436,7 @@ def _init_keywords(self):
 
     def _build_output_channels(self, *return_labels: str):
         try:
-            type_hints = get_type_hints(self.node_function)["return"]
+            type_hints = self._type_hints["return"]
             if len(return_labels) > 1:
                 type_hints = get_args(type_hints)
                 if not isinstance(type_hints, tuple):
@@ -607,7 +608,13 @@ def __getitem__(self, item):
         return self.single_value.__getitem__(item)
 
     def __getattr__(self, item):
-        return getattr(self.single_value, item)
+        try:
+            return getattr(self.single_value, item)
+        except Exception as e:
+            raise AttributeError(
+                f"Could not find {item} as an attribute of the single value "
+                f"{self.single_value}"
+            ) from e
 
     def __repr__(self):
         return self.single_value.__repr__()
@@ -618,35 +625,61 @@ def __str__(self):
         )
 
 
-def function_node(output_labels=None):
+def _wrapper_factory(
+    parent_class: type[Function], output_labels: Optional[list[str]]
+) -> callable:
     """
-    A decorator for dynamically creating node classes from functions.
-
-    Decorates a function.
-    Returns a `Function` subclass whose name is the camel-case version of the function
-    node, and whose signature is modified to exclude the node function and output labels
-    (which are explicitly defined in the process of using the decorator).
-
-    Optionally takes any keyword arguments of `Function`.
+    An abstract base for making decorators that wrap a function as `Function` or its
+    children.
     """
 
+    # One really subtle thing is that we manually parse the function type hints right
+    # here and include these as a class-level attribute.
+    # This is because on (de)(cloud)pickling a function node, somehow the node function
+    # method attached to it gets its `__globals__` attribute changed; it retains stuff
+    # _inside_ the function, but loses imports it used from the _outside_ -- i.e. type
+    # hints! I (@liamhuber) don't deeply understand _why_ (de)pickling is modifying the
+    # __globals__ in this way, but the result is that type hints cannot be parsed after
+    # the change.
+    # The final piece of the puzzle here is that because the node function is a _class_
+    # level attribute, if you (de)pickle a node, _new_ instances of that node wind up
+    # having their node function's `__globals__` trimmed down in this way!
+    # So to keep the type hint parsing working, we snag and interpret all the type hints
+    # at wrapping time, when we are guaranteed to have all the globals available, and
+    # also slap them on as a class-level attribute. These get safely packed and returned
+    # when (de)pickling so we can keep processing type hints without trouble.
     def as_node(node_function: callable):
         return type(
             node_function.__name__.title().replace("_", ""),  # fnc_name to CamelCase
-            (Function,),  # Define parentage
+            (parent_class,),  # Define parentage
             {
                 "__init__": partialmethod(
-                    Function.__init__,
+                    parent_class.__init__,
                     None,
                     output_labels=output_labels,
                 ),
                 "node_function": staticmethod(node_function),
+                "_type_hints": get_type_hints(node_function),
             },
         )
 
     return as_node
 
 
+def function_node(output_labels=None):
+    """
+    A decorator for dynamically creating node classes from functions.
+
+    Decorates a function.
+    Returns a `Function` subclass whose name is the camel-case version of the function
+    node, and whose signature is modified to exclude the node function and output labels
+    (which are explicitly defined in the process of using the decorator).
+
+    Optionally takes any keyword arguments of `Function`.
+    """
+    return _wrapper_factory(parent_class=Function, output_labels=output_labels)
+
+
 def single_value_node(output_labels=None):
     """
     A decorator for dynamically creating fast node classes from functions.
@@ -655,19 +688,4 @@ def single_value_node(output_labels=None):
 
     Optionally takes any keyword arguments of `SingleValueNode`.
     """
-
-    def as_single_value_node(node_function: callable):
-        return type(
-            node_function.__name__.title().replace("_", ""),  # fnc_name to CamelCase
-            (SingleValue,),  # Define parentage
-            {
-                "__init__": partialmethod(
-                    SingleValue.__init__,
-                    None,
-                    output_labels=output_labels,
-                ),
-                "node_function": staticmethod(node_function),
-            },
-        )
-
-    return as_single_value_node
+    return _wrapper_factory(parent_class=SingleValue, output_labels=output_labels)
diff --git a/pyiron_workflow/interfaces.py b/pyiron_workflow/interfaces.py
index 02d826de..d766c1e5 100644
--- a/pyiron_workflow/interfaces.py
+++ b/pyiron_workflow/interfaces.py
@@ -4,7 +4,8 @@
 
 from __future__ import annotations
 
-from typing import TYPE_CHECKING
+from importlib import import_module
+from sys import version_info
 
 from pyiron_base.interfaces.singleton import Singleton
 
@@ -18,9 +19,6 @@
     single_value_node,
 )
 
-if TYPE_CHECKING:
-    from pyiron_workflow.node import Node
-
 
 class Creator(metaclass=Singleton):
     """
@@ -30,6 +28,8 @@ class Creator(metaclass=Singleton):
     """
 
     def __init__(self):
+        self._node_packages = {}
+
         self.Executor = Executor
 
         self.Function = Function
@@ -40,6 +40,13 @@ def __init__(self):
         self._workflow = None
         self._meta = None
 
+        if version_info[0] == 3 and version_info[1] >= 10:
+            # These modules use syntactic sugar for type hinting that is only supported
+            # in python >=3.10
+            # If the CI skips testing on 3.9 gets dropped, we can think about removing
+            # this if-clause and just letting users of python <3.10 hit an error.
+            self.register("standard", "pyiron_workflow.node_library.standard")
+
     @property
     def Macro(self):
         if self._macro is None:
@@ -56,20 +63,6 @@ def Workflow(self):
             self._workflow = Workflow
         return self._workflow
 
-    @property
-    def standard(self):
-        from pyiron_workflow.node_package import NodePackage
-        from pyiron_workflow.node_library.standard import nodes
-
-        return NodePackage(*nodes)
-
-    @property
-    def atomistics(self):
-        from pyiron_workflow.node_package import NodePackage
-        from pyiron_workflow.node_library.atomistics import nodes
-
-        return NodePackage(*nodes)
-
     @property
     def meta(self):
         if self._meta is None:
@@ -78,16 +71,113 @@ def meta(self):
             self._meta = meta_nodes
         return self._meta
 
-    def register(self, domain: str, *nodes: list[type[Node]]):
-        raise NotImplementedError(
-            "Registering new node packages is currently not playing well with "
-            "executors. We hope to return this feature soon."
-        )
-        # if domain in self.__dir__():
-        #     raise AttributeError(f"{domain} is already an attribute of {self}")
-        # from pyiron_workflow.node_package import NodePackage
-        #
-        # setattr(self, domain, NodePackage(*nodes))
+    def __getattr__(self, item):
+        try:
+            module = import_module(self._node_packages[item])
+            from pyiron_workflow.node_package import NodePackage
+
+            return NodePackage(*module.nodes)
+        except KeyError as e:
+            raise AttributeError(
+                f"{self.__class__.__name__} could not find attribute {item} -- did you "
+                f"forget to register node package to this key?"
+            ) from e
+
+    def __getstate__(self):
+        return self.__dict__
+
+    def __setstate__(self, state):
+        self.__dict__ = state
+
+    def register(self, domain: str, package_identifier: str) -> None:
+        """
+        Add a new package of nodes under the provided attribute, e.g. after adding
+        nodes to the domain `"my_nodes"`, and instance of creator can call things like
+        `creator.my_nodes.some_node_that_is_there()`.
+
+        Note: If a macro is going to use a creator, the node registration should be
+            _inside_ the macro definition to make sure the node actually has access to
+            those nodes! It also needs to be _able_ to register those nodes, i.e. have
+            import access to that location, but we don't for that check that.
+
+        Args:
+            domain (str): The attribute name at which to register the new package.
+                (Note: no sanitizing is done here, so if you provide a string that
+                won't work as an attribute name, that's your problem.)
+            package_identifier (str): An identifier for the node package. (Right now
+                that's just a string version of the path to the module, e.g.
+                `pyiron_workflow.node_library.standard`.)
+
+        Raises:
+            KeyError: If the domain already exists, but the identifier doesn't match
+                with the stored identifier.
+            AttributeError: If you try to register at a domain that is already another
+                method or attribute of the creator.
+            ValueError: If the identifier can't be parsed.
+        """
+
+        if self._package_conflicts_with_existing(domain, package_identifier):
+            raise KeyError(
+                f"{domain} is already a registered node package, please choose a "
+                f"different domain to store these nodes under"
+            )
+        elif domain in self.__dir__():
+            raise AttributeError(f"{domain} is already an attribute of {self}")
+
+        self._verify_identifier(package_identifier)
+
+        self._node_packages[domain] = package_identifier
+
+    def _package_conflicts_with_existing(
+        self, domain: str, package_identifier: str
+    ) -> bool:
+        """
+        Check if the new package conflict with an existing package at the requested
+        domain; if there isn't one, or if the new and old packages are identical then
+        there is no conflict!
+
+        Args:
+            domain (str): The domain at which the new package is attempting to register.
+            package_identifier (str): The identifier for the new package.
+
+        Returns:
+            (bool): True iff there is a package already at that domain and it is not
+                the same as the new one.
+        """
+        if domain in self._node_packages.keys():
+            # If it's already here, it had better be the same package
+            return package_identifier != self._node_packages[domain]
+            # We can make "sameness" logic more complex as we allow more sophisticated
+            # identifiers
+        else:
+            # If it's not here already, it can't conflict!
+            return False
+
+    @staticmethod
+    def _verify_identifier(package_identifier: str):
+        """
+        Logic for verifying whether new package identifiers will actually be usable for
+        creating node packages when their domain is called. Lets us fail early in
+        registration.
+
+        Right now, we just make sure it's a string from which we can import a list of
+        nodes.
+        """
+        from pyiron_workflow.node import Node
+
+        try:
+            module = import_module(package_identifier)
+            nodes = module.nodes
+            if not all(issubclass(node, Node) for node in nodes):
+                raise TypeError(
+                    f"At least one node in {nodes} was not of the type {Node.__name__}"
+                )
+        except Exception as e:
+            raise ValueError(
+                f"The package identifier is {package_identifier} is not valid. Please "
+                f"ensure it is an importable module with a list of {Node.__name__} "
+                f"objects stored in the variable `nodes`."
+            ) from e
 
 
 class Wrappers(metaclass=Singleton):
diff --git a/pyiron_workflow/node_library/plotting.py b/pyiron_workflow/node_library/plotting.py
new file mode 100644
index 00000000..fd4e4355
--- /dev/null
+++ b/pyiron_workflow/node_library/plotting.py
@@ -0,0 +1,25 @@
+"""
+For graphical representations of data.
+"""
+
+from __future__ import annotations
+
+from typing import Optional
+
+import numpy as np
+
+from pyiron_workflow.function import single_value_node
+
+
+@single_value_node(output_labels="fig")
+def scatter(
+    x: Optional[list | np.ndarray] = None, y: Optional[list | np.ndarray] = None
+):
+    from matplotlib import pyplot as plt
+
+    return plt.scatter(x, y)
+
+
+nodes = [
+    scatter,
+]
diff --git a/pyiron_workflow/node_library/standard.py b/pyiron_workflow/node_library/standard.py
index bf24fab5..caf3d5ad 100644
--- a/pyiron_workflow/node_library/standard.py
+++ b/pyiron_workflow/node_library/standard.py
@@ -1,22 +1,15 @@
+"""
+Common-use nodes relying only on the standard library
+"""
+
 from __future__ import annotations
 
 from inspect import isclass
-from typing import Optional
-
-import numpy as np
-from matplotlib import pyplot as plt
 
 from pyiron_workflow.channels import NotData, OutputSignal
 from pyiron_workflow.function import SingleValue, single_value_node
 
 
-@single_value_node(output_labels="fig")
-def scatter(
-    x: Optional[list | np.ndarray] = None, y: Optional[list | np.ndarray] = None
-):
-    return plt.scatter(x, y)
-
-
 @single_value_node()
 def user_input(user_input):
     return user_input
@@ -52,7 +45,6 @@ def process_run_result(self, function_output):
 
 
 nodes = [
-    scatter,
     user_input,
     If,
 ]
diff --git a/tests/integration/test_workflow.py b/tests/integration/test_workflow.py
index 834f66bf..0dd6c06a 100644
--- a/tests/integration/test_workflow.py
+++ b/tests/integration/test_workflow.py
@@ -2,6 +2,7 @@
 
 import numpy as np
 
+from pyiron_workflow._tests import ensure_tests_in_python_path
 from pyiron_workflow.channels import OutputSignal
 from pyiron_workflow.function import Function
 from pyiron_workflow.workflow import Workflow
@@ -77,30 +78,27 @@ def numpy_sqrt(value=0):
         )
 
     def test_for_loop(self):
+        ensure_tests_in_python_path()
+        Workflow.register("demo", "static.demo_nodes")
+
         n = 5
 
         bulk_loop = Workflow.create.meta.for_loop(
-            Workflow.create.atomistics.Bulk,
+            Workflow.create.demo.OptionallyAdd,
             n,
-            iterate_on=("a",),
+            iterate_on=("y",),
         )()
 
+        base = 42
+        to_add = np.arange(n, dtype=int)
         out = bulk_loop(
-            name="Al",  # Sent equally to each body node
-            A=np.linspace(3.9, 4.1, n).tolist(),  # Distributed across body nodes
+            x=base,  # Sent equally to each body node
+            Y=to_add.tolist(),  # Distributed across body nodes
         )
 
         self.assertTrue(
-            np.allclose(
-                [struct.cell.volume for struct in out.STRUCTURE],
-                [
-                    14.829749999999995,
-                    15.407468749999998,
-                    15.999999999999998,
-                    16.60753125,
-                    17.230249999999995
-                ]
-            )
+            np.allclose([added for added in out.SUM], to_add + base),
+            msg="Output should be list result of each individiual result"
         )
 
     def test_while_loop(self):
@@ -171,3 +169,27 @@ def less_than_ten(value):
 
             out = wf(a=1, b=2)
             self.assertEqual(out.total, 11)
+
+    def test_executor_and_creator_interaction(self):
+        """
+        Make sure that submitting stuff to a parallel processor doesn't stop us from
+        using the same stuff on the main process. This can happen because the
+        (de)(cloud)pickle process messes with the `__globals__` attribute of the node
+        function, and since the node function is a class attribute the original node
+        gets updated on de-pickling.
+        We code around this, but lets make sure it stays working by adding a test!
+        Critical in this test is that the node used has complex type hints.
+
+        C.f. `pyiron_workflow.function._wrapper_factory` for more detail.
+        """
+
+        ensure_tests_in_python_path()
+        wf = Workflow("depickle")
+        wf.create.register("demo", "static.demo_nodes")
+        wf.before_pickling = wf.create.demo.OptionallyAdd(1)
+        wf.before_pickling.executor = True
+        wf()
+        wf.before_pickling.future.result()  # Wait for it to finish
+        wf.before_pickling.executor = False
+        wf.after_pickling = wf.create.demo.OptionallyAdd(2, y=3)
+        wf()
diff --git a/tests/static/__init__.py b/tests/static/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/static/demo_nodes.py b/tests/static/demo_nodes.py
new file mode 100644
index 00000000..f5c2fde9
--- /dev/null
+++ b/tests/static/demo_nodes.py
@@ -0,0 +1,16 @@
+"""
+A demo node package for the purpose of testing node package registration.
+"""
+
+from typing import Optional
+
+from pyiron_workflow import Workflow
+
+
+@Workflow.wrap_as.single_value_node("sum")
+def optionally_add(x: int, y: Optional[int] = None) -> int:
+    y = 0 if y is None else y
+    return x + y
+
+
+nodes = [optionally_add]
diff --git a/tests/static/faulty_node_package.py b/tests/static/faulty_node_package.py
new file mode 100644
index 00000000..2679fce8
--- /dev/null
+++ b/tests/static/faulty_node_package.py
@@ -0,0 +1,13 @@
+"""
+An incorrect node package for the purpose of testing node package registration.
+"""
+
+from pyiron_workflow import Workflow
+
+
+@Workflow.wrap_as.single_value_node("sum")
+def add(x: int, y: int) -> int:
+    return x + y
+
+
+nodes = [add, 42]  # Not everything here is a node!
diff --git a/tests/static/forgetful_node_package.py b/tests/static/forgetful_node_package.py
new file mode 100644
index 00000000..8e471704
--- /dev/null
+++ b/tests/static/forgetful_node_package.py
@@ -0,0 +1,13 @@
+"""
+An incorrect node package for the purpose of testing node package registration.
+"""
+
+from pyiron_workflow import Workflow
+
+
+@Workflow.wrap_as.single_value_node("sum")
+def add(x: int, y: int) -> int:
+    return x + y
+
+
+# nodes = [add]  # Oops, we "forgot" to populate a `nodes` list
diff --git a/tests/static/not_a_node_package.py b/tests/static/not_a_node_package.py
new file mode 100644
index 00000000..27002ed5
--- /dev/null
+++ b/tests/static/not_a_node_package.py
@@ -0,0 +1,6 @@
+"""
+A module that is not a node package at all for the purpose of testing node package
+registration.
+"""
+
+this_variable = "is not a list of Node objects called `nodes`"
diff --git a/tests/unit/executors/test_cloudprocesspool.py b/tests/unit/executors/test_cloudprocesspool.py
index eb49333e..018bc418 100644
--- a/tests/unit/executors/test_cloudprocesspool.py
+++ b/tests/unit/executors/test_cloudprocesspool.py
@@ -155,7 +155,7 @@ def slow():
         executor = CloudpickleProcessPoolExecutor()
         fs = executor.submit(f.run)
         self.assertEqual(
-            fs.result(timeout=30),
+            fs.result(timeout=60),
             fortytwo,
             msg="waiting long enough should get the result"
         )
diff --git a/tests/unit/test_interfaces.py b/tests/unit/test_interfaces.py
new file mode 100644
index 00000000..e53eb696
--- /dev/null
+++ b/tests/unit/test_interfaces.py
@@ -0,0 +1,80 @@
+import sys
+from unittest import TestCase, skipUnless
+
+from pyiron_workflow._tests import ensure_tests_in_python_path
+from pyiron_workflow.interfaces import Creator
+
+
+@skipUnless(
+    sys.version_info[0] == 3 and sys.version_info[1] >= 10, "Only supported for 3.10+"
+)
+class TestCreator(TestCase):
+    @classmethod
+    def setUpClass(cls) -> None:
+        cls.creator = Creator()
+        ensure_tests_in_python_path()
+
+    def test_registration(self):
+
+        with self.assertRaises(
+            AttributeError,
+            msg="Sanity check that the package isn't there yet and the test setup is "
+                "what we want"
+        ):
+            self.creator.demo_nodes
+
+        self.creator.register("demo", "static.demo_nodes")
+
+        node = self.creator.demo.OptionallyAdd(1, 2)
+        self.assertEqual(
+            3,
+            node(),
+            msg="Node should get instantiated from creator and be operable"
+        )
+
+        with self.subTest("Test re-registration"):
+            self.creator.register("demo", "static.demo_nodes")
+            # Same thing to the same location should be fine
+
+            self.creator.register("a_key_other_than_demo", "static.demo_nodes")
+            # The same thing to another key is usually dumb, but totally permissible
+
+            with self.assertRaises(
+                KeyError,
+                msg="Should not be able to register a new package to an existing domain"
+            ):
+                self.creator.register("demo", "pyiron_workflow.node_library.standard")
+
+            with self.assertRaises(
+                AttributeError,
+                msg="Should not be able to register to existing fields"
+            ):
+                some_field = self.creator.dir()[0]
+                self.creator.register(some_field, "static.demo_nodes")
+
+        with self.subTest("Test failure cases"):
+            n_initial_packages = len(self.creator._node_packages)
+
+            with self.assertRaises(
+                ValueError,
+                msg="Mustn't allow importing from things that are not node packages"
+            ):
+                self.creator.register("not_even", "static.not_a_node_package")
+
+            with self.assertRaises(
+                ValueError,
+                msg="Must require a `nodes` property in the module"
+            ):
+                self.creator.register("forgetful", "static.forgetful_node_package")
+
+            with self.assertRaises(
+                ValueError,
+                msg="Must have only nodes in the iterable `nodes` property"
+            ):
+                self.creator.register("faulty", "static.faulty_node_package")
+
+            self.assertEqual(
+                n_initial_packages,
+                len(self.creator._node_packages),
+                msg="Packages should not be getting added if exceptions are raised"
+            )
diff --git a/tests/unit/test_workflow.py b/tests/unit/test_workflow.py
index 0c0e09ae..fc7b8775 100644
--- a/tests/unit/test_workflow.py
+++ b/tests/unit/test_workflow.py
@@ -86,18 +86,18 @@ def test_node_removal(self):
             msg="Removal should also remove from starting nodes"
         )
 
-
     def test_node_packages(self):
         wf = Workflow("my_workflow")
+        wf.register("demo", "static.demo_nodes")
 
         # Test invocation
-        wf.create.atomistics.Bulk(cubic=True, element="Al")
+        wf.create.demo.OptionallyAdd(label="by_add")
         # Test invocation with attribute assignment
-        wf.engine = wf.create.atomistics.Lammps(structure=wf.bulk)
+        wf.by_assignment = wf.create.demo.OptionallyAdd()
 
         self.assertSetEqual(
             set(wf.nodes.keys()),
-            set(["bulk", "engine"]),
+            set(["by_add", "by_assignment"]),
             msg=f"Expected one node label generated automatically from the class and "
                 f"the other from the attribute assignment, but got {wf.nodes.keys()}"
         )