diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7368b2fdff8b5248e027b435d9900af35f23470f..dd8af6cadd5d8a38b7f70a5e12b61cdb9890bb84 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.16)
+cmake_minimum_required(VERSION 3.23)
 set(namespace "fggl")
 
 set(CMAKE_CXX_STANDARD 20)
@@ -43,12 +43,13 @@ file(GLOB_RECURSE public_headers
 	${PROJECT_SOURCE_DIR}/include/fggl/*.hpp
 )
 
+add_subdirectory( components )
+
 # engine sources, enable strict compiler warnings
 add_subdirectory( fggl )
 target_compile_options( fggl PRIVATE -Wall -Wpedantic -Wextra -Wodr -fno-strict-aliasing -fno-strict-overflow )
 set_property(TARGET fggl PROPERTY INTERPROCEDURAL_OPTIMIZATION True)
 
-add_subdirectory( components )
 
 # Unit Tests
 if (FGGL_TESTS)
@@ -126,18 +127,18 @@ configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
     INSTALL_DESTINATION "${FGGL_CONFIG_PATH}"
 )
 # install config files
-install(FILES
-    "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
-    "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
-    DESTINATION "${FGGL_CONFIG_PATH}"
-)
+#install(FILES
+#    "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
+#    "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
+#    DESTINATION "${FGGL_CONFIG_PATH}"
+#)
 # generate the export targets for the build tree
-install(TARGETS ${PROJECT_NAME}
-		EXPORT fgglTargets
-			DESTINATION "${CMAKE_INSTALL_LIBDIR}"
-		PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}
-		INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
-)
+#install(TARGETS ${PROJECT_NAME}
+#		EXPORT fgglTargets
+#			DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+#		PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}
+#		INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
+#)
 
 export(EXPORT "${PROJECT_NAME}Targets"
     FILE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Targets.cmake"
diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt
index e220ee79c6760b1dc26e5925d75960a360eec34e..119c37212b93edc23036f64c07524a425d940e10 100644
--- a/components/CMakeLists.txt
+++ b/components/CMakeLists.txt
@@ -1 +1,5 @@
+
+add_subdirectory(core)
+
+#add_subdirectory(assets)
 add_subdirectory(physics)
\ No newline at end of file
diff --git a/components/assets/CMakeLists.txt b/components/assets/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..fb6ebfc433f272d06a8edda3933aa8ccc505fee9
--- /dev/null
+++ b/components/assets/CMakeLists.txt
@@ -0,0 +1,9 @@
+add_library(fggl-assets)
+
+target_link_libraries(fggl-assets fggl-core)
+
+target_include_directories(fggl-assets PUBLIC
+        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/fggl>
+        $<INSTALL_INTERFACE:include/fggl>
+        )
+
diff --git a/components/core/CMakeLists.txt b/components/core/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..8d078db61e30a80c23a4c381729baf034eb60a78
--- /dev/null
+++ b/components/core/CMakeLists.txt
@@ -0,0 +1,28 @@
+add_library(fggl-core)
+
+target_include_directories(fggl-core PUBLIC
+        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+        $<INSTALL_INTERFACE:include/fggl>
+)
+
+target_sources(fggl-core
+        PUBLIC
+            FILE_SET core_headers
+            TYPE HEADERS
+            BASE_DIRS include
+        )
+
+target_sources(fggl-core
+        PRIVATE
+            src/filesystem/finder.cpp
+            src/filesystem/reader.cpp
+            src/filesystem/module.cpp
+            src/services/factory.cpp
+            src/services/module.cpp
+        )
+
+# platform integration tools
+add_subdirectory(src/platform)
+
+include(GNUInstallDirs)
+install(TARGETS fggl-core FILE_SET core_headers)
diff --git a/components/core/include/fggl/debug/impl/logging_fmt.hpp b/components/core/include/fggl/debug/impl/logging_fmt.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..762ab3b4bc66f28ca5e118873acb85693277a922
--- /dev/null
+++ b/components/core/include/fggl/debug/impl/logging_fmt.hpp
@@ -0,0 +1,112 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 11/06/22.
+//
+
+#ifndef FGGL_DEBUG_IMPL_LOGGING_FMT_HPP
+#define FGGL_DEBUG_IMPL_LOGGING_FMT_HPP
+
+#include <fmt/format.h>
+#include <fmt/color.h>
+
+#include <iostream>
+#include <string>
+#include <string_view>
+#include <cstdio>
+
+constexpr std::string_view CERR_FMT{"[{}] {}\n"};
+
+namespace fggl::debug {
+	using FmtType = const std::string_view;
+
+	/**
+	 * Logging levels
+	 */
+	enum class Level {
+		critical = 0,
+		error = 1,
+		warning = 2,
+		info = 3,
+		debug = 4,
+		trace = 5
+	};
+
+	constexpr std::string_view level_to_string(Level level) {
+		switch (level) {
+		case Level::critical: return "CRITICAL";
+		case Level::error: return "ERROR";
+		case Level::warning: return "WARNING";
+		case Level::info: return "INFO";
+		case Level::debug: return "DEBUG";
+		case Level::trace: return "TRACE";
+		default: return "UNKNOWN";
+		}
+	}
+
+	inline void vlog(const char *file, int line, fmt::string_view format, fmt::format_args args) {
+		fmt::print("{}: {}", file, line);
+		fmt::vprint(format, args);
+	}
+
+	template<typename S, typename... Args>
+	void logf(const char *file, int line, const S &format, Args &&... args) {
+		vlog(file, line, format, fmt::make_format_args(format, args...));
+	}
+
+	#define info_va(format, ...) \
+        logf(__FILE__, __LINE__, FMT_STRING(format), __VA_ARGS__)
+
+	template<typename ...T>
+	void log(Level level, FmtType fmt, T &&...args) {
+		auto fmtStr = fmt::format(fmt::runtime(fmt), args...);
+		fmt::print(CERR_FMT, level_to_string(level), fmtStr);
+	}
+
+	// inlined, pre-set level versions of the log function above
+
+	template<typename ...T>
+	inline void error(FmtType fmt, T &&...args) {
+		auto fmtStr = fmt::format(fmt::runtime(fmt), args...);
+		fmt::print(fg(fmt::color::red), CERR_FMT, level_to_string(Level::error), fmtStr);
+	}
+
+	template<typename ...T>
+	inline void warning(FmtType fmt, T &&...args) {
+		auto fmtStr = fmt::format(fmt::runtime(fmt), args...);
+		fmt::print(fg(fmt::color::orange), CERR_FMT, level_to_string(Level::warning), fmtStr);
+	}
+
+	template<typename ...T>
+	inline void info(FmtType fmt, T &&...args) {
+		auto fmtStr = fmt::format(fmt::runtime(fmt), args...);
+		fmt::print(CERR_FMT, level_to_string(Level::info), fmtStr);
+	}
+
+	template<typename ...T>
+	inline void debug(FmtType fmt, T &&...args) {
+		auto fmtStr = fmt::format(fmt::runtime(fmt), args...);
+		fmt::print(CERR_FMT, level_to_string(Level::debug), fmtStr);
+	}
+
+	template<typename ...T>
+	inline void trace(FmtType fmt, T &&...args) {
+		auto fmtStr = fmt::format(fmt::runtime(fmt), args...);
+		fmt::print(CERR_FMT, level_to_string(Level::trace), fmtStr);
+	}
+
+}
+
+#endif //FGGL_DEBUG_IMPL_LOGGING_FMT_HPP
diff --git a/components/core/include/fggl/debug/impl/logging_spdlog.hpp b/components/core/include/fggl/debug/impl/logging_spdlog.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..056c74f902302c253bd027a34f372e4bd25e0c0f
--- /dev/null
+++ b/components/core/include/fggl/debug/impl/logging_spdlog.hpp
@@ -0,0 +1,92 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 08/06/22.
+//
+
+#ifndef FGGL_DEBUG_IMPL_LOGGING_SPDLOG_HPP
+#define FGGL_DEBUG_IMPL_LOGGING_SPDLOG_HPP
+
+#include <string>
+#include <iostream>
+#include <memory>
+
+#include <spdlog/spdlog.h>
+#include <spdlog/sinks/stdout_color_sinks.h>
+
+namespace fggl::debug {
+
+	using FmtType = const std::string_view;
+
+	/**
+	 * Logging levels
+	 */
+	enum class Level {
+		critical = spdlog::level::critical,
+		error = spdlog::level::err,
+		warning = spdlog::level::warn,
+		info = spdlog::level::info,
+		debug = spdlog::level::debug,
+		trace = spdlog::level::trace
+	};
+
+	template<typename ...T>
+	void error(const FmtType &fmt, T &&...args) {
+		spdlog::error(fmt, args...);
+	}
+
+	template<typename ...T>
+	void warning(const FmtType &fmt, T &&...args) {
+		spdlog::warn(fmt, args...);
+	}
+
+	template<typename ...T>
+	void info(const FmtType &fmt, T &&...args) {
+		spdlog::info(fmt, args...);
+	}
+
+	template<typename ...T>
+	void debug(const FmtType &fmt, T &&...args) {
+		spdlog::debug(fmt, args...);
+	}
+
+	template<typename ...T>
+	void trace(const FmtType &fmt, T &&...args) {
+		spdlog::trace(fmt, args...);
+	}
+
+	template<typename ...T>
+	void log(const FmtType &fmt, T &&...args) {
+		spdlog::log(Level::info, fmt, args...);
+	}
+
+	template<typename ...T>
+	void log(Level level, const FmtType &fmt, T &&...args) {
+		spdlog::log(level, fmt, args...);
+	}
+
+	class Logger {
+		public:
+			Logger();
+
+			template<typename ...Args>
+			void log(Level level, const FmtType &fmt, Args &&...args) {
+				spdlog::log(level, fmt, args...);
+			}
+	};
+
+}
+
+#endif //FGGL_DEBUG_IMPL_LOGGING_SPDLOG_HPP
diff --git a/components/core/include/fggl/debug/logging.hpp b/components/core/include/fggl/debug/logging.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..51545af63edc6a070b3f54d75b363dfb65ac187a
--- /dev/null
+++ b/components/core/include/fggl/debug/logging.hpp
@@ -0,0 +1,55 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 10/07/23.
+//
+
+#ifndef FGGL_COMPONENTS_CORE_DEBUG_LOGGING_HPP
+#define FGGL_COMPONENTS_CORE_DEBUG_LOGGING_HPP
+
+#include <string_view>
+
+namespace fggl::debug {
+
+	std::string demangle(const char* name);
+
+	using FmtType = const std::string_view;
+	enum class Level;
+
+	template<typename ...T>
+	void error(FmtType fmt, T &&...args);
+
+	template<typename ...T>
+	void warning(FmtType fmt, T &&...args);
+
+	template<typename ...T>
+	void info(FmtType fmt, T &&...args);
+
+	template<typename ...T>
+	void debug(FmtType fmt, T &&...args);
+
+	template<typename ...T>
+	void trace(FmtType fmt, T &&...args);
+
+	template<typename ...T>
+	void log(FmtType fmt, T &&...args);
+
+	template<typename ...T>
+	void log(Level level, FmtType fmt, T &&...args);
+}
+
+#include "fggl/debug/impl/logging_fmt.hpp"
+
+#endif //FGGL_COMPONENTS_CORE_DEBUG_LOGGING_HPP
diff --git a/components/core/include/fggl/filesystem/Finder.hpp b/components/core/include/fggl/filesystem/Finder.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..221f173f1f279b0d0582290df48af142c78fb692
--- /dev/null
+++ b/components/core/include/fggl/filesystem/Finder.hpp
@@ -0,0 +1,91 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 10/07/23.
+//
+
+#ifndef FGGL_COMPONENTS_CORE_FILESYSTEM_FINDER_HPP
+#define FGGL_COMPONENTS_CORE_FILESYSTEM_FINDER_HPP
+
+#include <filesystem>
+#include <optional>
+#include <vector>
+
+#include "fggl/services/service.hpp"
+#include "fggl/services/module.hpp"
+#include "fggl/platform/paths.hpp"
+
+namespace fggl::filesystem {
+
+	constexpr unsigned char MASK_OWNER = 0b11110000;
+	constexpr unsigned char MASK_CONTEXT = 0b00001111;
+
+	enum class Owner : unsigned char {
+			GAME = 0b00010000,
+			DLC  = 0b00100000,
+			MOD  = 0b01000000,
+			USER = 0b10000000,
+			ANY  = 0b11110000
+	};
+
+	enum class Context : unsigned char {
+			RESOURCE = 0b00000001,
+			CONFIG = 0b00000010,
+			CACHE = 0b00000100,
+			TMP = 0b00001000
+	};
+
+	/**
+	 * Filesystem API contract.
+	 *
+	 * A module providing this contract must:
+	 *  * provide a Locator service (as defined below)
+	 */
+	FGGL_MODULE_CONTRACT(API, "fggl::filesystem::API")
+
+	class Locator {
+		public:
+			FGGL_DEFINE_SERVICE(fggl::filesystem::Locator)
+
+			using Path = std::filesystem::path;
+
+			Locator();
+
+			Path calculate(const char* name, Context context = Context::RESOURCE, Owner owner = Owner::ANY);
+			inline Path calculate(const std::string& name, Context context = Context::RESOURCE, Owner owner = Owner::ANY) {
+				return calculate(name.c_str(), context, owner);
+			}
+
+			std::optional<Path> find(const char* name, Context context = Context::RESOURCE, Owner owner = Owner::ANY);
+			inline std::optional<Path> find(const std::string& name, Context context = Context::RESOURCE, Owner owner = Owner::ANY) {
+				return find(name.c_str(), context, owner);
+			}
+
+			std::optional<Path> createUser(const char* name, Context context = Context::CONFIG);
+			inline std::optional<Path> createUser(const std::string& name, Context context = Context::CONFIG) {
+				return createUser(name.c_str(), context);
+			}
+
+		private:
+			fggl::platform::EnginePaths m_paths;
+
+	};
+
+	std::vector<Locator::Path> findAllMatching(Locator::Path root, const std::string& suffix);
+	void findAllMatching(Locator::Path root, const std::string& suffix, std::vector<Locator::Path>&);
+
+} // namespace fggl::filesystem
+
+#endif //FGGL_COMPONENTS_CORE_FILESYSTEM_FINDER_HPP
diff --git a/components/core/include/fggl/filesystem/module.hpp b/components/core/include/fggl/filesystem/module.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0a8a717284920ad3e2fc91ad29ec05aaf2af4fa8
--- /dev/null
+++ b/components/core/include/fggl/filesystem/module.hpp
@@ -0,0 +1,35 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 12/07/23.
+//
+
+#ifndef FGGL_COMPONENTS_CORE_FILESYSTEM_MODULE_HPP
+#define FGGL_COMPONENTS_CORE_FILESYSTEM_MODULE_HPP
+
+#include "fggl/services/module.hpp"
+
+namespace fggl::filesystem {
+
+	class NativeBacked : public services::Module {
+			FGGL_MODULE(fggl::filesystem::Native)
+
+			auto getProvides() const -> Module::ServiceList override;
+			void wireServices(services::ModuleBinder& binder) override;
+	};
+
+} // namespace fggl::filesystem
+
+#endif //FGGL_COMPONENTS_CORE_FILESYSTEM_MODULE_HPP
diff --git a/components/core/include/fggl/filesystem/reader.hpp b/components/core/include/fggl/filesystem/reader.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..792af42af04dc83e03ca69861c51cd4cbc08f0c2
--- /dev/null
+++ b/components/core/include/fggl/filesystem/reader.hpp
@@ -0,0 +1,60 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 12/07/23.
+//
+
+#ifndef FGGL_COMPONENTS_CORE_FILESYSTEM_READER_HPP
+#define FGGL_COMPONENTS_CORE_FILESYSTEM_READER_HPP
+
+#include <filesystem>
+#include <optional>
+#include <vector>
+
+#include "fggl/services/service.hpp"
+#include "fggl/services/module.hpp"
+#include "fggl/platform/paths.hpp"
+#include "Finder.hpp"
+
+namespace fggl::filesystem {
+
+	class Reader {
+		public:
+			FGGL_DEFINE_SERVICE(fggl::filesystem::Reader)
+
+			using Path = std::filesystem::path;
+
+			inline explicit Reader(Locator* locator) : m_locator(locator){};
+
+			std::optional<std::string> readText(const char* name, Context context = Context::RESOURCE, Owner owner = Owner::ANY);
+			char* readBytes(const char* name, Context context = Context::RESOURCE, Owner owner = Owner::ANY);
+
+			template<typename T>
+			std::optional<T> readAs(const char* name, std::function<std::optional<T>(Path&)> converter, Context context = Context::RESOURCE, Owner owner = Owner::ANY) {
+				auto path = m_locator->find(name, context, owner);
+				if ( !path.has_value() ){
+					return {};
+				}
+				return converter(path);
+			}
+
+		private:
+			Locator* m_locator;
+
+	};
+
+} // namespace fggl::filesystem
+
+#endif //FGGL_COMPONENTS_CORE_FILESYSTEM_READER_HPP
diff --git a/components/core/include/fggl/maths/common.hpp b/components/core/include/fggl/maths/common.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..75004a2800f9274e25d44d16ffc8d60a79262971
--- /dev/null
+++ b/components/core/include/fggl/maths/common.hpp
@@ -0,0 +1,80 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 10/07/23.
+//
+
+#ifndef FGGL_COMPONENTS_CORE_MATHS_COMMON_HPP
+#define FGGL_COMPONENTS_CORE_MATHS_COMMON_HPP
+
+#include <cmath>
+
+#define GLM_ENABLE_EXPERIMENTAL
+#include <glm/glm.hpp>
+
+namespace fggl::maths {
+
+	template<typename T>
+	struct Scale {
+		T start;
+		T end;
+
+		[[nodiscard]]
+		constexpr bool inRange(T value) const {
+			return start <= value && value <= end;
+		}
+
+		[[nodiscard]]
+		constexpr T lerp(T value) const {
+			assert(T(0) <= value && value <= T(1));
+			return (T(1) - value) * start + value * end;
+		}
+
+		[[nodiscard]]
+		constexpr T midpoint() const {
+			return (end + start) / T(2);
+		}
+
+		[[nodiscard]]
+		constexpr T range() const {
+			return end - start;
+		}
+
+		[[nodiscard]]
+		constexpr T wrap(float value) const {
+			value -= start;
+			return value - (range() * std::floor(value / range())) + start;
+		}
+
+		[[nodiscard]]
+		constexpr T clamp(T value) const {
+			const T valueOrMin = value < start ? start : value;
+			return valueOrMin > end ? end : valueOrMin;
+		}
+
+		[[nodiscard]]
+		constexpr T rescale(T input, Scale target) const {
+			return ((input - start) * target.range()) / range();
+		}
+
+		constexpr static Scale make(T minValue, T maxValue) {
+			assert( minValue < maxValue );
+			return {minValue, maxValue};
+		}
+	};
+
+} // namespace fggl::maths
+
+#endif //FGGL_COMPONENTS_CORE_MATHS_COMMON_HPP
diff --git a/components/core/include/fggl/maths/float.hpp b/components/core/include/fggl/maths/float.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..258ee61c8f82cc89b713f1dd20de2f7150ddd434
--- /dev/null
+++ b/components/core/include/fggl/maths/float.hpp
@@ -0,0 +1,107 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 10/07/23.
+//
+
+#ifndef FGGL_COMPONENTS_CORE_MATHS_FMATH_HPP
+#define FGGL_COMPONENTS_CORE_MATHS_FMATH_HPP
+
+#include "common.hpp"
+#include <array>
+
+namespace fggl::maths {
+
+	/**
+	 * return the remainder (modulo) of value / maximum.
+	 *
+	 * This will return a value in the range [0, maximum), the result will always be positive, even if passed a negative
+	 * input.
+	 *
+	 * @param value the value to wrap.
+	 * @param max the maximum value that it can take.
+	 * @return the wrapped value
+	 */
+	constexpr float wrap(float value, float max) {
+		return fmodf(max + fmodf(value, max), max);
+	}
+
+	/**
+	 * wrap value in range [min, max)
+	 *
+	 * If the range overflows, return the modulo of the value.
+	 *
+	 * @param value the value to be tested.
+	 * @param min the minimum allowable value
+	 * @param max the maximum allowable value
+	 */
+	constexpr float wrap(float value, float min, float max) {
+		if (min > max) {
+			std::swap(min, max);
+		};
+		value -= min;
+		float rangeSize = max - min;
+		return value - (rangeSize * std::floor(value / rangeSize)) + min;
+	}
+
+	/**
+	 * Ensure that value is wrapped in the range [min, max].
+	 *
+	 * If the value under or over flows, return the limit of the range.
+	 * if the value is larger than max, return max.
+	 * if the value is smaller than min, return min.
+	 *
+	 * @param value the value to be tested.
+	 * @param min the minimum allowable value
+	 * @param max the maximum allowable value
+	 * @return value, if it is in the range [min, max], otherwise the bound that it is outside of.
+	 */
+	constexpr float clamp(float value, float min, float max) {
+		const float valueOrMin = value < min ? min : value;
+		return valueOrMin > max ? max : valueOrMin;
+	}
+
+	/**
+	 * Free function form of linear interpolation.
+	 *
+	 * @param start the starting value for the range
+	 * @param end the ending value for the range
+	 * @param value value [0,1] along the range
+	 * @return the linearly interpolated value from the provided range
+	 */
+	constexpr float lerp(float start, float end, float value) {
+		assert( 0.0F <= value && value <= 1.0F );
+		assert( start < end );
+		return (1 - value) * start + value * end;
+	}
+
+	/**
+	 * Floating point scales
+	 */
+	using ScaleF = Scale<float>;
+
+	/**
+	 * A scale where values are between 0 and 1, midpoint 0.5
+	 */
+	constexpr ScaleF SCALE01 = ScaleF::make(0.0F, 1.0F);
+
+	/**
+	 * A scale where values are between -1 and 1, midpoint 0
+	 */
+	constexpr ScaleF SCALE11 = ScaleF::make(-1.0F, 1.0F);
+
+} // namespace fggl::maths
+
+#endif //FGGL_COMPONENTS_CORE_MATHS_FMATH_HPP
diff --git a/components/core/include/fggl/maths/int.hpp b/components/core/include/fggl/maths/int.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..f91d8a839158cf51159bc93e05ccf8594b510ddc
--- /dev/null
+++ b/components/core/include/fggl/maths/int.hpp
@@ -0,0 +1,34 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 10/07/23.
+//
+
+#ifndef FGGL_COMPONENTS_CORE_MATHS_IMATH_HPP
+#define FGGL_COMPONENTS_CORE_MATHS_IMATH_HPP
+
+#include "common.hpp"
+
+namespace fggl::maths {
+
+	/**
+	 * Integer Scales
+	 */
+	 using ScaleI = Scale<int64_t>;
+	 using ScaleUI = Scale<uint64_t>;
+
+} // namespace fggl::maths
+
+#endif //FGGL_COMPONENTS_CORE_MATHS_IMATH_HPP
diff --git a/include/fggl/platform/fallback/file.hpp b/components/core/include/fggl/platform/fallback/file.hpp
similarity index 100%
rename from include/fggl/platform/fallback/file.hpp
rename to components/core/include/fggl/platform/fallback/file.hpp
diff --git a/include/fggl/platform/fallback/paths.hpp b/components/core/include/fggl/platform/fallback/paths.hpp
similarity index 98%
rename from include/fggl/platform/fallback/paths.hpp
rename to components/core/include/fggl/platform/fallback/paths.hpp
index 85269406c1229e8535372ca03c00d47f24002534..445af8a444f2ac7458ff365cfca6cd254d127036 100644
--- a/include/fggl/platform/fallback/paths.hpp
+++ b/components/core/include/fggl/platform/fallback/paths.hpp
@@ -21,8 +21,6 @@
 #ifndef FGGL_PLATFORM_FALLBACK_PATHS_HPP
 #define FGGL_PLATFORM_FALLBACK_PATHS_HPP
 
-#include "fggl/platform/paths.hpp"
-
 #include <filesystem>
 #include <array>
 
@@ -91,6 +89,6 @@ namespace fggl::platform {
 	std::filesystem::path locate_config(const EnginePaths &paths, const std::filesystem::path &relPath);
 	std::filesystem::path locate_cache(const EnginePaths &paths, const std::filesystem::path &relPath);
 
-}
+} // namespace fggl::platform
 
 #endif //FGGL_PLATFORM_FALLBACK_PATHS_HPP
diff --git a/include/fggl/platform/linux/paths.hpp b/components/core/include/fggl/platform/linux/paths.hpp
similarity index 94%
rename from include/fggl/platform/linux/paths.hpp
rename to components/core/include/fggl/platform/linux/paths.hpp
index 6fe48aca28acdda0e6c73347dcb47a38530786fd..ba97157053d17290eea242bc36a4a602e1256801 100644
--- a/include/fggl/platform/linux/paths.hpp
+++ b/components/core/include/fggl/platform/linux/paths.hpp
@@ -21,8 +21,6 @@
 #ifndef FGGL_PLATFORM_LINUX_PATHS_HPP
 #define FGGL_PLATFORM_LINUX_PATHS_HPP
 
-#include "fggl/platform/paths.hpp"
-
 #include <filesystem>
 #include <array>
 
@@ -60,7 +58,8 @@ namespace fggl::platform {
 	std::filesystem::path locate_data(const EnginePaths &paths, const std::filesystem::path &relPath);
 	std::filesystem::path locate_config(const EnginePaths &paths, const std::filesystem::path &relPath);
 	std::filesystem::path locate_cache(const EnginePaths &paths, const std::filesystem::path &relPath);
+	std::filesystem::path locate_temp(const EnginePaths& paths, const std::filesystem::path &relPath);
 
-}
+} // namespace fggl::platform
 
 #endif //FGGL_PLATFORM_LINUX_PATHS_HPP
diff --git a/include/fggl/platform/paths.hpp b/components/core/include/fggl/platform/paths.hpp
similarity index 75%
rename from include/fggl/platform/paths.hpp
rename to components/core/include/fggl/platform/paths.hpp
index c8443a45362ee6aebbde5b952eb3c804dcb282fb..ddfcb71fb9904b3ccf779beafadfd65ff5378f59 100644
--- a/include/fggl/platform/paths.hpp
+++ b/components/core/include/fggl/platform/paths.hpp
@@ -13,21 +13,18 @@
  */
 
 //
-// Created by webpigeon on 26/06/22.
+// Created by webpigeon on 10/07/23.
 //
 
-#ifndef FGGL_PLATFORM_PATHS_HPP
-#define FGGL_PLATFORM_PATHS_HPP
-
-#include <filesystem>
-#include <vector>
+#ifndef FGGL_COMPONENTS_CORE_PLATFORM_PATHS_HPP
+#define FGGL_COMPONENTS_CORE_PLATFORM_PATHS_HPP
 
 #ifdef __linux__
 	#define FGGL_PLATFORM_PATHS linux
-	#include "fggl/platform/linux/paths.hpp"
+	#include "linux/paths.hpp"
 #else
 	#define FGGL_PLATFORM_PATHS fallback
-	#include "fggl/platform/fallback/paths.hpp"
+	#include "fallback/paths.hpp"
 #endif
 
-#endif //FGGL_PLATFORM_PATHS_HPP
+#endif //FGGL_COMPONENTS_CORE_PLATFORM_PATHS_HPP
diff --git a/components/core/include/fggl/services/factory.hpp b/components/core/include/fggl/services/factory.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..fc2eb791601397dc46e520dc04398bb6c446630d
--- /dev/null
+++ b/components/core/include/fggl/services/factory.hpp
@@ -0,0 +1,218 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 10/07/23.
+//
+
+#ifndef FGGL_COMPONENTS_CORE_SERVICES_FACTORY_HPP
+#define FGGL_COMPONENTS_CORE_SERVICES_FACTORY_HPP
+
+#include <algorithm>
+#include <functional>
+#include <optional>
+#include <cassert>
+#include <memory>
+
+#include "registry.hpp"
+
+
+namespace fggl::services {
+
+	#ifdef NDEBUG
+		class Generator;
+		using GeneratorView = Generator;
+	#else
+		class GeneratorView;
+	#endif
+
+	class Generator;
+
+	struct ServiceBinding {
+		public:
+			virtual ~ServiceBinding() = default;
+			virtual std::shared_ptr<void> provide() = 0;
+	};
+
+	template<ServiceAPI T>
+	struct ServiceBindingT;
+
+	class Generator {
+		public:
+			Generator() = default;
+
+			// no copy
+			Generator(Generator&) = delete;
+			void operator=(Generator&) = delete;
+
+			template<ServiceAPI api>
+			Service<api> getLazy() {
+				assert( m_factories.find(api::SERVICE_ID) != m_factories.end() );
+				if ( m_registry.contains(api::SERVICE_ID) ) {
+					return m_registry.get<api>();
+				}
+
+				// does not exist, invoke factory
+				return buildAndStore<api>();
+			}
+
+			template<ServiceAPI api>
+			void bind(ServiceBinding&& binding) {
+				m_factories[ api::SERVICE_ID ] = binding;
+			}
+
+			template<ServiceAPI api>
+			ServiceBindingT<api>& createBinding() {
+				auto ptr = std::make_shared<ServiceBindingT<api>>(this);
+				m_factories[ api::SERVICE_ID ] = ptr;
+				return *(ptr.get());
+			}
+
+			template<ServiceAPI api>
+			Service<api> build() {
+				auto ptrRaw = invokeFactoryRaw(api::SERVICE_ID);
+				return Service( std::static_pointer_cast<api>(ptrRaw) );
+			}
+
+			template<ServiceAPI api>
+			std::optional<Service<api>> getFromCache() const {
+				if ( !m_registry.contains(api::SERVICE_ID) ) {
+					return {};
+				}
+				return m_registry.get<api>();
+			}
+
+			inline bool canBuild(ServiceName name) const {
+				return m_registry.contains(name) || m_factories.find(name) != m_factories.end();
+			}
+
+		private:
+			Registry m_registry;
+			std::map<ServiceName, std::shared_ptr<ServiceBinding>> m_factories;
+
+			std::shared_ptr<void> invokeFactoryRaw(ServiceName name);
+
+			template<ServiceAPI api>
+			Service<api> buildAndStore() {
+				auto ptr = invokeFactoryRaw(FGGL_SERVICE(api));
+				m_registry.provideUnsafe(FGGL_SERVICE(api), ptr);
+				return m_registry.get<api>();
+			}
+
+
+	};
+
+	template<ServiceAPI T>
+	struct ServiceBindingT : public ServiceBinding {
+			using Factory = std::function<std::shared_ptr<T>(Generator* generator)>;
+
+			inline explicit ServiceBindingT(Generator* generator) : m_generator(generator) {}
+
+			inline ServiceBindingT<T>& toFactory(Factory factory) {
+				m_instance = nullptr;
+				m_factory = factory;
+				return *this;
+			}
+
+			inline ServiceBindingT<T>& asSingleton() {
+				m_singleton = true;
+				return *this;
+			}
+
+			inline ServiceBindingT<T>& simply() {
+				m_instance = nullptr;
+				m_factory = [](const GeneratorView* /*generator*/) {
+					return std::make_shared<T>();
+				};
+				return *this;
+			}
+
+			template<ServiceAPI... Deps>
+			inline ServiceBindingT<T>& depends(){
+				m_instance = nullptr;
+				m_factory = [](Generator* generator) {
+					return std::make_shared<T>( generator->getLazy<Deps>().ptr()... );
+				};
+				return *this;
+			}
+
+			template<Subclass<T> Impl>
+			inline ServiceBindingT<T>& simplyTo(){
+				m_instance = nullptr;
+				m_factory = [](const GeneratorView* /*generator*/) {
+					return std::make_shared<Impl>();
+				};
+				return *this;
+			}
+
+			template<Subclass<T> Impl, ServiceAPI... Deps, typename... Args>
+			inline ServiceBindingT<T>& to(Args... args){
+				m_instance = nullptr;
+				m_factory = [args...](Generator* generator) {
+					return std::make_shared<Impl>( generator->getLazy<Deps>().ptr()..., args... );
+				};
+				return *this;
+			}
+
+			std::shared_ptr<void> provide() override {
+				return provideT();
+			}
+
+			inline std::shared_ptr<T> provideT() {
+				assert( m_generator != nullptr );
+				if ( m_instance != nullptr ){
+					return m_instance;
+				}
+
+				if ( m_factory != nullptr ) {
+					if ( m_singleton ) {
+						m_instance = m_factory(m_generator);
+						return m_instance;
+					}
+					return m_factory(m_generator);
+				}
+				return nullptr;
+			}
+
+		private:
+			Generator* m_generator = nullptr;
+			std::shared_ptr<T> m_instance = nullptr;
+			Factory m_factory = nullptr;
+			bool m_singleton = false;
+	};
+
+	#ifndef NDEBUG
+	struct GeneratorView {
+		public:
+			inline GeneratorView(Generator* generator, const std::vector<ServiceName>& depends) : m_generator(generator), m_declared(depends) {}
+
+			template<ServiceAPI api>
+			std::optional<Service<api>> get() const {
+				assert(std::find( m_declared.begin(), m_declared.end(), api::service ) != m_declared.end());
+				return m_generator->get<api>();
+			}
+		private:
+			Generator* m_generator;
+			std::vector<ServiceName> m_declared;
+	};
+	#endif
+
+	template<ServiceAPI impl, typename... Args>
+	std::shared_ptr<void> wire_func(GeneratorView* view) {
+		return std::make_shared<impl>( view->getLazy<Args...>() );
+	}
+
+} // namespace fggl::services
+
+#endif //FGGL_COMPONENTS_CORE_SERVICES_FACTORY_HPP
diff --git a/components/core/include/fggl/services/module.hpp b/components/core/include/fggl/services/module.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..f6d8936a5bfb5e8811dec5cacba5c5f000e86604
--- /dev/null
+++ b/components/core/include/fggl/services/module.hpp
@@ -0,0 +1,89 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef FGGL_CORE_SERVICES_MODULE_HPP
+#define FGGL_CORE_SERVICES_MODULE_HPP
+
+#include "service.hpp"
+#include "factory.hpp"
+#include <iostream>
+
+#define FGGL_MODULE(id, ...) constexpr static const char* MODULE_ID = #id; \
+						constexpr static const auto MODULE_DEPS = fggl::services::mod_extract_names<__VA_ARGS__>(); \
+                        inline fggl::services::ModuleID getName() const override { return MODULE_ID; } \
+						inline fggl::services::Module::ServiceList getDepends() const override { return fggl::services::mod_svc_list( MODULE_DEPS ); }
+
+#define FGGL_MODULE_CONTRACT(name, id) struct name { constexpr static const char* MODULE_ID = #id; };
+
+#include <functional>
+
+	namespace fggl::services {
+
+		struct ModuleBinder {
+			public:
+				inline ModuleBinder(Generator* generator) : m_generator(generator) {}
+
+				template<ServiceAPI T>
+				ServiceBindingT<T>& bind() {
+					std::cerr << "generating binding for: " << T::SERVICE_ID.get() << std::endl;
+					return m_generator->createBinding<T>();
+				}
+			private:
+				Generator *m_generator;
+		};
+
+		using ModuleID = const char*;
+		template<typename T>
+		concept ModuleAPI = requires(T* type) {
+			{ T::MODULE_ID };
+		};
+
+		class Module {
+			public:
+				using ServiceList = std::vector<ModuleID>;
+				virtual ~Module() = default;
+
+				[[nodiscard]]
+				virtual ModuleID getName() const = 0;
+				virtual void wireServices(ModuleBinder& binder) = 0;
+
+				[[nodiscard]]
+				virtual ServiceList getProvides() const;
+
+				[[nodiscard]]
+				virtual ServiceList getDepends() const = 0;
+
+				// callback methods
+				virtual void setup(Generator* generator);
+				virtual void teardown();
+		};
+
+
+		template<ModuleAPI... T>
+		constexpr auto mod_extract_names() {
+			return std::array<ModuleID, sizeof...(T)>{ T::MODULE_ID... };
+		}
+
+		template<typename T>
+		inline Module::ServiceList mod_svc_list(T items) {
+			Module::ServiceList deps{};
+			for (auto& dep : items) {
+				deps.push_back(dep);
+			}
+			return deps;
+		}
+
+	} // namespace fggl::services
+
+#endif
\ No newline at end of file
diff --git a/components/core/include/fggl/services/registry.hpp b/components/core/include/fggl/services/registry.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..825983dda95bafdf5e0f0f10bb4b436294b04a96
--- /dev/null
+++ b/components/core/include/fggl/services/registry.hpp
@@ -0,0 +1,56 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 10/07/23.
+//
+
+#ifndef FGGL_COMPONENTS_CORE_SERVICES_REGISTRY_HPP
+#define FGGL_COMPONENTS_CORE_SERVICES_REGISTRY_HPP
+
+#include "service.hpp"
+
+#include <memory>
+#include <map>
+
+namespace fggl::services {
+
+
+	template<typename T, typename U>
+	concept Subclass = std::is_base_of<U, T>::value;
+
+	class Registry {
+		public:
+			void provideUnsafe(ServiceName api, std::shared_ptr<void> service) {
+				m_services[ api ] = service;
+			}
+
+			inline bool contains(ServiceName service) const {
+				return m_services.find(service) != m_services.end();
+			}
+
+			template<ServiceAPI svc>
+			Service<svc> get() const {
+				auto wrapper = m_services.at( svc::SERVICE_ID );
+				auto ptr = std::static_pointer_cast<svc>(wrapper);
+				return Service<svc>(ptr);
+			}
+
+		private:
+			std::map<ServiceName, std::shared_ptr<void>> m_services;
+	};
+
+} // namespace fggl::services
+
+#endif //FGGL_COMPONENTS_CORE_SERVICES_REGISTRY_HPP
diff --git a/components/core/include/fggl/services/service.hpp b/components/core/include/fggl/services/service.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..389b1c857f4d3608d027844d9b27ac8d158c262c
--- /dev/null
+++ b/components/core/include/fggl/services/service.hpp
@@ -0,0 +1,76 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 10/07/23.
+//
+
+#ifndef FGGL_COMPONENTS_CORE_SERVICES_SERVICE_HPP
+#define FGGL_COMPONENTS_CORE_SERVICES_SERVICE_HPP
+
+#include "fggl/util/safety.hpp"
+
+#include <memory>
+#include <functional>
+
+#define FGGL_DEFINE_SERVICE(x) constexpr const static auto SERVICE_ID = fggl::services::make_name(#x);
+#define FGGL_SERVICE(x) x::SERVICE_ID
+
+#define FGGL_IMPLEMENT_SERVICE(x,...) constexpr static const auto SERVICE_API = FGGL_SERVICE(x); \
+                                        constexpr static const auto SERVICE_DEPS = fggl::services::svc_extract_names<__VA_ARGS__>();
+
+namespace fggl::services {
+
+	template<typename T>
+	struct Service {
+		public:
+			inline Service(std::shared_ptr<T> service) : m_service(service) {}
+
+			inline T* operator->() {
+				return m_service.operator->();
+			}
+
+			inline T* ptr() {
+				return m_service.get();
+			}
+		private:
+			std::shared_ptr<T> m_service;
+	};
+
+	using ServiceName = util::OpaqueName<std::string_view, struct ModuleServiceTag>;
+
+	template<typename T>
+	concept ServiceAPI = requires(T* type) {
+		{ T::SERVICE_ID };
+	};
+
+	template<typename T>
+	concept ServiceProvider = requires(T* type) {
+		{ T::Service::API };
+		{ T::Service::DEPS };
+	};
+
+	constexpr ServiceName make_name(const std::string_view name) {
+		return ServiceName::make(name);
+	}
+
+	template<typename... T>
+	constexpr auto svc_extract_names() {
+		return std::array<ServiceName, sizeof...(T)>{ T::SERVICE_ID... };
+	}
+
+
+} // namespace fggl::services
+
+#endif //FGGL_COMPONENTS_CORE_SERVICES_SERVICE_HPP
diff --git a/components/core/include/fggl/util/safety.hpp b/components/core/include/fggl/util/safety.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..55cfa036a2f4b577fe07cf38da45bad01eac5fa8
--- /dev/null
+++ b/components/core/include/fggl/util/safety.hpp
@@ -0,0 +1,94 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 27/06/22.
+//
+
+#ifndef FGGL_UTIL_SAFETY_HPP
+#define FGGL_UTIL_SAFETY_HPP
+
+#include <string>
+
+namespace fggl::util {
+
+	/**
+	 * A type-safe opaque handle.
+	 *
+	 * Lots of low-level libraries we use pass around handles as some primative type. It's fairly easy to accidentally
+	 * mix these up. This wrapper's job is to make sure that mixing up handle types is impossible (and results in
+	 * compiler errors).
+	 *
+	 * @tparam T the underling type of the handle.
+	 * @tparam Tag a unique tag used to identify the handle type.
+	 */
+	template<typename T, typename Tag>
+	struct OpaqueName {
+		private:
+			T m_value;
+
+		public:
+			explicit constexpr OpaqueName(T value) : m_value(value) {}
+
+			constexpr OpaqueName() : m_value() {}
+
+			constexpr T get() const {
+				return m_value;
+			}
+
+			/**
+			 * Check for equality of two handles.
+			 *
+			 * Two values are considered the same of the values contained inside them are considered equal, and both
+			 * types share the same tagging interface.
+			 *
+			 * @param other the value being compared against.
+			 * @return true iff the contained values are equal
+			 */
+			bool operator==(const OpaqueName<T, Tag> &other) const {
+				return m_value == other.m_value;
+			}
+
+			/**
+			 * Check for equality of two handles.
+			 *
+			 * Two values are considered the same of the values contained inside them are considered equal, and both
+			 * types share the same tagging interface.
+			 *
+			 * @param other the value being compared against.
+			 * @return true iff the contained values are not equal
+			 */
+			bool operator!=(const OpaqueName<T, Tag> &other) const {
+				return m_value != other.m_value;
+			}
+
+			bool operator<(const OpaqueName<T, Tag> &other) const {
+				return m_value < other.m_value;
+			}
+
+			std::strong_ordering operator<=>(const OpaqueName<T, Tag> &other) const noexcept {
+				return m_value <=> other.m_value;
+			}
+
+			/**
+			 * Generate a new tagged instance of a handle.
+			 */
+			constexpr static OpaqueName<T, Tag> make(T value) {
+				return OpaqueName<T, Tag>(value);
+			}
+	};
+
+} // namespace fggl::util
+
+#endif //FGGL_UTIL_SAFETY_HPP
diff --git a/components/core/src/filesystem/finder.cpp b/components/core/src/filesystem/finder.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0b8cff56e1a69afd8bc9b4aed405b1d714469046
--- /dev/null
+++ b/components/core/src/filesystem/finder.cpp
@@ -0,0 +1,90 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 10/07/23.
+//
+
+#include "fggl/filesystem/Finder.hpp"
+#include "fggl/platform/paths.hpp"
+
+#include <functional>
+
+namespace fggl::filesystem {
+
+	namespace {
+		void findAllMatching(Locator::Path root, const std::function<bool(std::filesystem::path)>& predicate, std::vector<Locator::Path>& paths) {
+			// path doesn't exist, can't do anything
+			if ( !std::filesystem::exists(root) ) {
+				return;
+			}
+
+			for ( const auto& entry : std::filesystem::recursive_directory_iterator(root) ) {
+				if ( entry.is_regular_file() && predicate( entry.path() )) {
+					paths.push_back(entry);
+				}
+			}
+		}
+	}
+
+	//FIXME game identifier is hard coded...
+	Locator::Locator() : m_paths(fggl::platform::calc_engine_paths("fgg-demo")) {}
+
+	Locator::Path Locator::calculate(const char *name,
+									 fggl::filesystem::Context context,
+									 fggl::filesystem::Owner owner) {
+		switch(context) {
+			case Context::RESOURCE:
+				return fggl::platform::locate_data(m_paths, name);
+			case Context::CONFIG:
+				return fggl::platform::locate_config(m_paths, name);
+			case Context::CACHE:
+				return fggl::platform::locate_cache(m_paths, name);
+			case Context::TMP:
+				return fggl::platform::locate_temp(m_paths, name);
+		}
+	}
+
+	std::optional<Locator::Path> Locator::find(const char *name,
+											   fggl::filesystem::Context context,
+											   fggl::filesystem::Owner owner) {
+		auto path = calculate(name, context, owner);
+		if ( std::filesystem::exists(path) ) {
+			return path;
+		} else {
+			return {};
+		}
+	}
+
+
+	std::optional<Locator::Path> Locator::createUser(const char *name, fggl::filesystem::Context context) {
+		auto path = calculate(name, context, fggl::filesystem::Owner::USER);
+		if ( !std::filesystem::exists(path.parent_path()) ) {
+			std::filesystem::create_directories(path.parent_path());
+		}
+		return path;
+	}
+
+	std::vector<Locator::Path> findAllMatching(Locator::Path root, const std::string& suffix) {
+		std::vector<Locator::Path> paths;
+		findAllMatching(root, [suffix](const Locator::Path& path) {return path.extension() == suffix;}, paths);
+		return paths;
+	}
+
+	void findAllMatching(Locator::Path root, const std::string& suffix, std::vector<Locator::Path>& paths) {
+		findAllMatching(root, [suffix](const Locator::Path& path) {return path.extension() == suffix;}, paths);
+	}
+
+
+}
\ No newline at end of file
diff --git a/components/core/src/filesystem/module.cpp b/components/core/src/filesystem/module.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0a630fdc86659cd49fff834bcfaf07a7f1836996
--- /dev/null
+++ b/components/core/src/filesystem/module.cpp
@@ -0,0 +1,34 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 12/07/23.
+//
+
+#include "fggl/filesystem/module.hpp"
+#include "fggl/filesystem/Finder.hpp"
+#include "fggl/filesystem/reader.hpp"
+
+namespace fggl::filesystem {
+
+	services::Module::ServiceList NativeBacked::getProvides() const {
+		return { MODULE_ID, API::MODULE_ID };
+	}
+
+	void NativeBacked::wireServices(services::ModuleBinder &binder) {
+		binder.bind<Locator>().simply().asSingleton();
+		binder.bind<Reader>().depends<Locator>();
+	}
+
+} // namespace fggl::filesystem
\ No newline at end of file
diff --git a/components/core/src/filesystem/reader.cpp b/components/core/src/filesystem/reader.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b1e5ada13a992432372fb3a88c68ecaedaf68dcb
--- /dev/null
+++ b/components/core/src/filesystem/reader.cpp
@@ -0,0 +1,58 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 12/07/23.
+//
+
+#include "fggl/filesystem/reader.hpp"
+
+#include <fstream>
+
+namespace fggl::filesystem {
+
+	std::optional<std::string> Reader::readText(const char *name, Context context, Owner owner) {
+		auto path = m_locator->find(name, context, owner);
+		if ( !path.has_value() ) {
+			return {};
+		}
+
+		std::string output;
+		std::ifstream ifs(path.value());
+		output.assign( std::istreambuf_iterator<char>(ifs), (std::istreambuf_iterator<char>()) );
+		return output;
+	}
+
+	char* Reader::readBytes(const char *name, Context context, Owner owner) {
+		auto path = m_locator->find(name, context, owner);
+		if ( !path.has_value() ) {
+			return {};
+		}
+
+		// get stream
+		std::ifstream buff(path.value(), std::ios::in | std::ios::binary );
+
+		// figure out length
+		buff.seekg(0, std::ios::end);
+		auto length = buff.tellg();
+
+		// read whole file into buffer
+		buff.seekg(0, std::ios::beg);
+		char *data = new char[length];
+		buff.read(data, length);
+
+		return data;
+	}
+
+} // namespace fggl::filesystem
\ No newline at end of file
diff --git a/fggl/platform/CMakeLists.txt b/components/core/src/platform/CMakeLists.txt
similarity index 63%
rename from fggl/platform/CMakeLists.txt
rename to components/core/src/platform/CMakeLists.txt
index 49b67c8124dd033349221c681921558525410702..27fe5929444f9462a6813773e5d7af906bff8485 100644
--- a/fggl/platform/CMakeLists.txt
+++ b/components/core/src/platform/CMakeLists.txt
@@ -1,5 +1,7 @@
+
+# Detect which operating system we want to target
 if (CMAKE_SYSTEM_NAME MATCHES "Linux")
     add_subdirectory(linux)
 else ()
     add_subdirectory(fallback)
-endif ()
\ No newline at end of file
+endif ()
diff --git a/components/core/src/platform/fallback/CMakeLists.txt b/components/core/src/platform/fallback/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a4e2eec12116d9c856138ac770e163c981849fd7
--- /dev/null
+++ b/components/core/src/platform/fallback/CMakeLists.txt
@@ -0,0 +1,4 @@
+
+target_sources(fggl-core PRIVATE
+        paths.cpp
+)
diff --git a/fggl/platform/fallback/paths.cpp b/components/core/src/platform/fallback/paths.cpp
similarity index 98%
rename from fggl/platform/fallback/paths.cpp
rename to components/core/src/platform/fallback/paths.cpp
index 30a9ff4a3880609325130babec4273328e45413c..1bc8de9316bd2a801e3bba2160fe9be68a88b3f7 100644
--- a/fggl/platform/fallback/paths.cpp
+++ b/components/core/src/platform/fallback/paths.cpp
@@ -16,8 +16,6 @@
 // Created by webpigeon on 26/06/22.
 //
 
-#define FGGL_PLATFORM_PATHS fallback
-
 #include <cstdlib>
 
 #include "fggl/platform/fallback/paths.hpp"
diff --git a/components/core/src/platform/linux/CMakeLists.txt b/components/core/src/platform/linux/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a4e2eec12116d9c856138ac770e163c981849fd7
--- /dev/null
+++ b/components/core/src/platform/linux/CMakeLists.txt
@@ -0,0 +1,4 @@
+
+target_sources(fggl-core PRIVATE
+        paths.cpp
+)
diff --git a/fggl/platform/linux/paths.cpp b/components/core/src/platform/linux/paths.cpp
similarity index 89%
rename from fggl/platform/linux/paths.cpp
rename to components/core/src/platform/linux/paths.cpp
index e2030db8c394c31faf9ef36c2ec5a2e252b93e12..1a4c99774dbbae9c601c531a89b35a00c89a8fad 100644
--- a/fggl/platform/linux/paths.cpp
+++ b/components/core/src/platform/linux/paths.cpp
@@ -18,8 +18,6 @@
 
 #include <cstdlib>
 
-#define FGGL_PLATFORM_PATHS linux
-
 #include "fggl/platform/linux/paths.hpp"
 #include "fggl/debug/logging.hpp"
 
@@ -139,4 +137,22 @@ namespace fggl::platform {
 		// if the file existed, it should exist in the user space
 		return userPath;
 	}
+
+	auto locate_temp(const EnginePaths &paths, const std::filesystem::path &relPath) -> std::filesystem::path {
+		auto userPath = paths.userCache / relPath;
+		if (std::filesystem::exists(userPath)) {
+			return userPath;
+		}
+
+		// check system paths
+		for (const auto &path : paths.configDirs) {
+			auto fullPath = path / relPath;
+			if (std::filesystem::exists(fullPath)) {
+				return fullPath;
+			}
+		}
+
+		// if the file existed, it should exist in the user space
+		return userPath;
+	}
 } // namespace fggl::platform::linux
diff --git a/components/core/src/services/factory.cpp b/components/core/src/services/factory.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..658c2fb7e9b55e89071e08ef04560a838f188f51
--- /dev/null
+++ b/components/core/src/services/factory.cpp
@@ -0,0 +1,49 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 10/07/23.
+//
+
+#include "fggl/services/factory.hpp"
+#include <iostream>
+
+namespace fggl::services {
+
+	namespace {
+		void dumpFactories( const std::map<ServiceName, std::shared_ptr<ServiceBinding>>& factories ){
+			std::cerr << "BEGIN registered factories" << std::endl;
+			for ( auto& factory : factories ) {
+				std::cerr << "\t" << factory.first.get() << std::endl;
+			}
+			std::cerr << "END registered factories" << std::endl;
+		}
+	}
+
+	std::shared_ptr<void> Generator::invokeFactoryRaw(ServiceName name) {
+		assert( m_factories.find(name) != m_factories.end() && "Missing service, either module order is wrong, or nothing provides this service" );
+
+		// invoke and build
+		auto& meta = m_factories.at(name);
+
+		#ifndef NDEBUG
+			auto provided = meta->provide();
+			assert( provided != nullptr );
+			return provided;
+		#else
+			return meta->provide();
+		#endif
+	}
+
+} // namespace fggl::services
diff --git a/components/core/src/services/module.cpp b/components/core/src/services/module.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..00a1de08e145c6f514e74ba0d9d99baad762ab40
--- /dev/null
+++ b/components/core/src/services/module.cpp
@@ -0,0 +1,62 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 12/07/23.
+//
+
+#include "fggl/services/module.hpp"
+
+namespace fggl::services {
+
+	/**
+	 * Report what contracts this module satisfies.
+	 *
+	 * By default, a module only provides its name. In other words, it only promises to provide itself. If the module
+	 * actually fulfills additional contracts (eg, providing implementations of generic filesystem services), it should
+	 * override this method.
+	 *
+	 * This method is used to determine which order setup and shutdown is called on modules.
+	 *
+	 * @return the contracts this module satisfies.
+	 */
+	Module::ServiceList Module::getProvides() const {
+		return {getName()};
+	}
+
+	/**
+	 * Perform any setup required for this Module.
+	 *
+	 * This is a stub, to avoid modules that don't do anything fancy having to implement a blank function.
+	 *
+	 * This, this should be RAII, no, I'm not going to fix it right now. Trying to fix it was causing more headaches than
+	 * it was solving...
+	 */
+	void Module::setup(Generator*) {
+
+	}
+
+	/**
+	 * Perform any teardown required for this Module.
+	 *
+	 * This is a stub, to avoid modules that don't do anything fancy having to implement a blank function.
+	 *
+	 * This, this should be RAII, no, I'm not going to fix it right now. Trying to fix it was causing more headaches than
+	 * it was solving...
+	 */
+	void Module::teardown() {
+
+	}
+
+} // namespace fggl:services
diff --git a/components/physics/CMakeLists.txt b/components/physics/CMakeLists.txt
index c9241e8632605296e916d9f1d680786182387b92..06bcde813a7c418d845295b421738a6a8deaec9d 100644
--- a/components/physics/CMakeLists.txt
+++ b/components/physics/CMakeLists.txt
@@ -1,11 +1,22 @@
 add_library(fggl-phys)
 
-target_link_libraries(fggl-phys fggl)
+target_link_libraries(fggl-phys fggl-core fggl)
 
 target_include_directories(fggl-phys PUBLIC include)
+
+target_sources(fggl-phys
+        PUBLIC
+            FILE_SET phys_headers
+            TYPE HEADERS
+            BASE_DIRS include
+        )
+
 target_sources(fggl-phys
         PRIVATE
             src/collision.cpp
             src/integration.cpp
             src/service.cpp
-        )
\ No newline at end of file
+        )
+
+include(GNUInstallDirs)
+install(TARGETS fggl-phys FILE_SET phys_headers)
\ No newline at end of file
diff --git a/components/physics/include/phys/module.hpp b/components/physics/include/phys/module.hpp
index f770494e643081b1a88256f9fb0690b2608b4fb9..ccb0c85b6574c8fdd96f04532b6fd2f4d6263280 100644
--- a/components/physics/include/phys/module.hpp
+++ b/components/physics/include/phys/module.hpp
@@ -19,8 +19,10 @@
 #ifndef FGGL_COMPONENTS_PHYSICS_INCLUDE_PHYS_MODULE_HPP
 #define FGGL_COMPONENTS_PHYSICS_INCLUDE_PHYS_MODULE_HPP
 
-#include <fggl/phys/types.hpp>
-#include <fggl/phys/service.hpp>
+	#include "fggl/services/module.hpp"
+	#include <fggl/phys/types.hpp>
+	#include <fggl/phys/service.hpp>
+
 
 namespace fggl::phys {
 
@@ -52,15 +54,10 @@ namespace fggl::phys {
 			InternalPhysicsEngine* create(entity::EntityManager*, entity::EntityFactory*) override;
 	};
 
-	struct InternalPhysics {
-		constexpr static const char *name = "fggl::phys::internal";
-		constexpr static const std::array<modules::ServiceName, 1> provides {
-			phys::PhysicsProvider::service
-		};
-		constexpr static const std::array<modules::ServiceName, 1> depends {
-			entity::EntityFactory::service
-		};
-		static bool factory(modules::ServiceName name, modules::Services& services);
+	struct InternalPhysics : public services::Module {
+		FGGL_MODULE(fggl::phys::Internal, fggl::entity::ECS)
+
+		void wireServices(services::ModuleBinder& binder) override;
 	};
 
 } // namespace fggl::phys
diff --git a/components/physics/include/phys/types.hpp b/components/physics/include/phys/types.hpp
index 70a48533e6c52d1366c089595065a01ff7fbd05e..504a10fba0e67825a565038c112fe3e75641793d 100644
--- a/components/physics/include/phys/types.hpp
+++ b/components/physics/include/phys/types.hpp
@@ -19,7 +19,7 @@
 #ifndef FGGL_COMPONENTS_PHYSICS_INCLUDE_PHYS_TYPES_HPP
 #define FGGL_COMPONENTS_PHYSICS_INCLUDE_PHYS_TYPES_HPP
 
-#include <fggl/math/fmath.hpp>
+#include <fggl/maths/float.hpp>
 #include <fggl/entity/module.hpp>
 
 namespace fggl::phys {
diff --git a/components/physics/src/service.cpp b/components/physics/src/service.cpp
index 4cc48131bb6935bd71eeab4882f49bcac50fd9cf..fe78355aef1dddc75b645bce792e937496b5bc33 100644
--- a/components/physics/src/service.cpp
+++ b/components/physics/src/service.cpp
@@ -25,7 +25,7 @@ namespace fggl::phys {
 		void build_internal(const entity::ComponentSpec & /*config*/,
 							   entity::EntityManager &manager,
 							   const entity::EntityID &entity,
-							   modules::Services &/*services*/) {
+							   services::Generator */*services*/) {
 			manager.add<fggl::phys::RigidBody>(entity);
 			manager.add<fggl::phys::Dynamics>(entity);
 			manager.add<fggl::phys::Motion>(entity);
@@ -40,12 +40,8 @@ namespace fggl::phys {
 		return new InternalPhysicsEngine(manager);
 	}
 
-	bool InternalPhysics::factory(modules::ServiceName serviceName, modules::Services &services) {
-		if ( serviceName == phys::PhysicsProvider::service ) {
-			services.bind<phys::PhysicsProvider, InternalPhysicsProvider>();
-			return true;
-		}
-		return false;
+	void InternalPhysics::wireServices(services::ModuleBinder &binder) {
+		binder.bind<phys::PhysicsProvider>().simplyTo<InternalPhysicsProvider>();
 	}
 
 	void checkCollisions(entity::EntityManager& manager) {
diff --git a/demo/demo/main.cpp b/demo/demo/main.cpp
index d06c75fe95faaee9cba94d00415df0f1298a962c..e9c9d5fce8ebbebb38f3c561e159b8d038cf2936 100644
--- a/demo/demo/main.cpp
+++ b/demo/demo/main.cpp
@@ -37,6 +37,7 @@
 #include "fggl/scenes/menu.hpp"
 #include "fggl/modules/manager.hpp"
 
+#include "fggl/filesystem/module.hpp"
 #include "fggl/data/assimp/module.hpp"
 #include "fggl/assets/module.hpp"
 #include "fggl/assets/packed/module.hpp"
@@ -60,7 +61,7 @@ static void setup_menu(fggl::App& app) {
 	const std::array scenes = {"game", "rollball", "topdown", "gridworld", "viewer", "hexboard"};
 
 	for (std::size_t i = 0; i < labels.size(); ++i) {
-		std::string sceneName = scenes.at(i);
+		std::string const sceneName = scenes.at(i);
 
 		menu->add(labels.at(i), [&app, sceneName]() {
 			auto* audio = app.service<fggl::audio::AudioService>();
@@ -80,8 +81,10 @@ int main(int argc, const char* argv[]) {
 
 	// DAG initialisation test
 	fggl::modules::Manager moduleManager;
+
 	moduleManager.use<fggl::input::Generic>();
-	moduleManager.use<fggl::data::LocalStorage>();
+	moduleManager.use<fggl::filesystem::NativeBacked>();
+
 	moduleManager.use<fggl::gui::FreeType>();
 	moduleManager.use<fggl::audio::OpenAL>();
 	moduleManager.use<fggl::gfx::OpenGL4>();
@@ -102,7 +105,14 @@ int main(int argc, const char* argv[]) {
 	#else
 		moduleManager.use<fggl::phys::InternalPhysics>();
 	#endif
-	moduleManager.resolve();
+
+	auto resolved = moduleManager.resolve();
+	if ( !resolved ) {
+		std::cerr << "could not build module dependency graph." << std::endl;
+		std::cerr << "This means that not all required modules could be loaded." << std::endl;
+		std::cerr << "check what modules you have asked for!" << std::endl;
+		return 1;
+	}
 
 	// create the application
 	fggl::App app( &moduleManager, "fggl-demo" );
diff --git a/fggl/CMakeLists.txt b/fggl/CMakeLists.txt
index 28461506a0a3726505142e16739aa3799fbc2445..30e4ee531ab81ee9365778bda408a8b461cbe545 100644
--- a/fggl/CMakeLists.txt
+++ b/fggl/CMakeLists.txt
@@ -44,7 +44,9 @@ add_subdirectory(phys)
 target_sources(${PROJECT_NAME}
         PRIVATE
         app.cpp
+        modules/manager.cpp
 
+        display/glfw/module.cpp
         data/model.cpp
         data/procedural.cpp
         data/heightmap.cpp
@@ -54,6 +56,7 @@ target_sources(${PROJECT_NAME}
         scenes/game.cpp
 
         input/input.cpp
+        input/module.cpp
         input/mouse.cpp
         input/camera_input.cpp
 )
@@ -64,6 +67,7 @@ add_subdirectory(gui)
 # yaml-cpp for configs and storage
 find_package(yaml-cpp)
 target_link_libraries(fggl PUBLIC yaml-cpp)
+target_link_libraries(fggl PUBLIC fggl-core)
 
 # model loading
 add_subdirectory(data/assimp)
@@ -78,7 +82,6 @@ add_subdirectory(audio)
 add_subdirectory(debug)
 
 # platform integrations
-add_subdirectory(platform)
 add_subdirectory(entity)
 
 ##
diff --git a/fggl/assets/module.cpp b/fggl/assets/module.cpp
index 670029946bd9f4ec7795f107821ba75f6a57a1c3..6f1718300d13ff01cc9253a3c4a9b167a96f47bb 100644
--- a/fggl/assets/module.cpp
+++ b/fggl/assets/module.cpp
@@ -20,18 +20,9 @@
 
 namespace fggl::assets {
 
-	auto AssetFolders::factory(modules::ServiceName service, modules::Services &services) -> bool {
-		if (service == Loader::service) {
-			auto *storage = services.get<data::Storage>();
-			auto *checkin = services.get<CheckinAdapted>();
-			services.create<Loader>(storage, checkin);
-			return true;
-		}
-		if (service == AssetManager::service) {
-			services.create<AssetManager>();
-			return true;
-		}
-		return false;
+	void AssetFolders::wireServices(services::ModuleBinder &binder) {
+		binder.bind<Loader>().depends<filesystem::Locator, CheckinAdapted>().asSingleton();
+		binder.bind<AssetManager>().simply().asSingleton();
 	}
 
 } // namespace fggl::assets
\ No newline at end of file
diff --git a/fggl/assets/packed/module.cpp b/fggl/assets/packed/module.cpp
index 4d9dc9e115b0affc414af23ed686a263bbb482a5..657350ee6a9bfb20deb0811bf31ce0148fb8c69b 100644
--- a/fggl/assets/packed/module.cpp
+++ b/fggl/assets/packed/module.cpp
@@ -20,18 +20,9 @@
 
 namespace fggl::assets {
 
-	auto PackedAssets::factory(modules::ServiceName service, modules::Services &services) -> bool {
-		if (service == RawCheckin::service) {
-			services.create<RawCheckin>();
-			return true;
-		}
-		if (service == CheckinAdapted::service) {
-			auto* storage = services.get<data::Storage>();
-			auto* rawCheckin = services.get<RawCheckin>();
-			services.create<CheckinAdapted>(storage, rawCheckin);
-			return true;
-		}
-		return false;
+	void PackedAssets::wireServices(services::ModuleBinder &binder) {
+		binder.bind<RawCheckin>().simply().asSingleton();
+		binder.bind<CheckinAdapted>().depends<filesystem::Locator, RawCheckin>().asSingleton();
 	}
 
 } // namespace fggl::assets
\ No newline at end of file
diff --git a/fggl/audio/fallback/audio.cpp b/fggl/audio/fallback/audio.cpp
index 32dd285023317d05527973b70c573f96256e0f59..d40116bb5d9b9b907de105684b7ab83226c64b8c 100644
--- a/fggl/audio/fallback/audio.cpp
+++ b/fggl/audio/fallback/audio.cpp
@@ -26,12 +26,8 @@ namespace fggl::audio {
 	void NullAudioService::play(const fggl::audio::AudioClipShort &, bool) {
 	}
 
-	auto NullAudio::factory(modules::ServiceName service, modules::Services &services) -> bool{
-		if (service == SERVICE_AUDIO_PLAYBACK) {
-			services.bind<audio::AudioService, audio::NullAudioService>();
-			return true;
-		}
-		return false;
+	void NullAudio::wireServices(services::ModuleBinder &binder) {
+		binder.bind<audio::AudioService>().simplyTo<audio::NullAudioService>();
 	}
 
 } // namespace fggl::audio
\ No newline at end of file
diff --git a/fggl/audio/openal/module.cpp b/fggl/audio/openal/module.cpp
index 843cd4218817dbe8dee5354eca9dd80c72373a66..c6b6259f29d9f442bc15b06d6554359406e1951b 100644
--- a/fggl/audio/openal/module.cpp
+++ b/fggl/audio/openal/module.cpp
@@ -20,25 +20,19 @@
 
 namespace fggl::audio {
 
-	auto OpenAL::factory(modules::ServiceName service, modules::Services &services) -> bool {
-		if (service == SERVICE_AUDIO_PLAYBACK) {
-			auto* assets = services.get<assets::AssetManager>();
-
-			{
-				auto *assetLoader = services.get<assets::Loader>();
-				assetLoader->setFactory( ASSET_CLIP_SHORT, openal::load_vorbis, assets::LoadType::PATH);
-			}
+	void OpenAL::wireServices(services::ModuleBinder& binder) {
+		binder.bind<audio::AudioService>().to<openal::AudioServiceOAL, assets::AssetManager>();
+	}
 
-			{
-				auto *checkin = services.get<assets::CheckinAdapted>();
-				checkin->setLoader( RES_OGG_VORBIS, openal::load_vorbis_short, openal::check_vorbis );
-			}
+	void OpenAL::setup(services::Generator *generator) {
+		Module::setup(generator);
 
-			services.bind<audio::AudioService, openal::AudioServiceOAL>(assets);
-			return true;
-		}
+		// register asset loaders
+		auto loader = generator->getLazy<assets::Loader>();
+		loader->setFactory( ASSET_CLIP_SHORT, openal::load_vorbis, assets::LoadType::PATH );
 
-		return false;
+		auto checkin = generator->getLazy<assets::CheckinAdapted>();
+		checkin->setLoader( RES_OGG_VORBIS, openal::load_vorbis_short, openal::check_vorbis);
 	}
 
 }
\ No newline at end of file
diff --git a/fggl/data/assimp/module.cpp b/fggl/data/assimp/module.cpp
index 64d10508b2b84dab9aaf2c76cb3029497abc58f4..0afee1844b3d5f566a42f7acf409041437921146 100644
--- a/fggl/data/assimp/module.cpp
+++ b/fggl/data/assimp/module.cpp
@@ -313,24 +313,30 @@ namespace fggl::data::models {
 		return true;
 	}
 
-	auto AssimpModule::factory(modules::ServiceName service, modules::Services &serviceManager) -> bool {
-		if ( service == MODEL_PROVIDER ) {
-			auto* assetLoader = serviceManager.get<assets::Loader>();
-			assetLoader->setFactory( MODEL_MULTI3D, load_assimp_model, assets::LoadType::PATH );
-			assetLoader->setFactory( TEXTURE_RGBA, load_assimp_texture, assets::LoadType::PATH );
-
-			// new loading system
-			auto* checkin = serviceManager.get<assets::CheckinAdapted>();
-			checkin->setLoader( MIME_JPG, load_tex_stb, is_tex_stb );
-			checkin->setLoader( MIME_PNG, load_tex_stb, is_tex_stb );
-
-			checkin->setLoader( MIME_OBJ, assets::NEEDS_CHECKIN, is_model_assimp );
-			checkin->setLoader( MIME_FBX, assets::NEEDS_CHECKIN, is_model_assimp );
-			checkin->setProcessor( MIME_OBJ, extract_requirements);
-			checkin->setProcessor( MIME_FBX, extract_requirements );
-			return false;
-		}
-		return false;
+	void AssimpModule::wireServices(services::ModuleBinder &binder) {
+		// assimp provides no direct services, it's just plugs into the asset loading pipelines...
+	}
+
+	void AssimpModule::setup(services::Generator *generator) {
+		Module::setup(generator);
+
+		// wire up the asset detection stuff
+		auto* assetLoader = generator->getLazy<assets::Loader>().ptr();
+		assetLoader->setFactory( MODEL_MULTI3D, load_assimp_model, assets::LoadType::PATH );
+		assetLoader->setFactory( TEXTURE_RGBA, load_assimp_texture, assets::LoadType::PATH );
+
+		// define what textures we support
+		auto* checkin = generator->getLazy<assets::CheckinAdapted>().ptr();
+		checkin->setLoader(MIME_JPG, load_tex_stb, is_tex_stb);
+		checkin->setLoader(MIME_PNG, load_tex_stb, is_tex_stb);
+
+		// define what model formats we support
+		checkin->setLoader( MIME_OBJ, assets::NEEDS_CHECKIN, is_model_assimp );
+		checkin->setLoader( MIME_FBX, assets::NEEDS_CHECKIN, is_model_assimp );
+
+		// models have dependencies on other files, extract them for the chain loaders
+		checkin->setProcessor( MIME_OBJ, extract_requirements);
+		checkin->setProcessor( MIME_FBX, extract_requirements );
 	}
 
 } // namespace fggl::data
diff --git a/fggl/data/module.cpp b/fggl/data/module.cpp
index cbf91275b990fafacce76cac5e75cc768534a545..27941b1d6632f2177cc57af20b5d12c875caced1 100644
--- a/fggl/data/module.cpp
+++ b/fggl/data/module.cpp
@@ -20,6 +20,7 @@
 
 namespace fggl::data {
 
+	/*
 	auto LocalStorage::factory(modules::ServiceName service, modules::Services &data) -> bool {
 		if (service == SERVICE_STORAGE) {
 			// FIXME: no easy way to set the application name
@@ -28,6 +29,6 @@ namespace fggl::data {
 			return true;
 		}
 		return false;
-	}
+	}*/
 
 } // namespace fggl::data
\ No newline at end of file
diff --git a/fggl/display/glfw/module.cpp b/fggl/display/glfw/module.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8b613e0773d2ddf012013b6fb5103390b1230241
--- /dev/null
+++ b/fggl/display/glfw/module.cpp
@@ -0,0 +1,39 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 12/07/23.
+//
+
+#include "fggl/display/glfw/module.hpp"
+
+namespace fggl::display {
+
+	void GLFW::wireServices(services::ModuleBinder& binder)  {
+		// FIXME creating a service requires context to exist, which requires starting glfw too early
+		binder.bind<WindowService>().to<glfw::WindowService, gfx::WindowGraphics>(m_context);
+	}
+
+	void GLFW::setup(services::Generator* generator) {
+		// this is safe, because we know that input has already been set-up before us
+		auto* input = generator->getLazy<input::Input>().ptr();
+		m_context = std::make_shared<glfw::GlfwContext>(input);
+	}
+
+	void GLFW::teardown() {
+		// window services no longer available
+		m_context = nullptr;
+	}
+
+} // namespace glfw
diff --git a/fggl/entity/loader/loader.cpp b/fggl/entity/loader/loader.cpp
index 90e22b5e11185b08fa641e246e1b3bca1d87bbeb..3b36c439343c4e69d8fb63f2188d902687bb2851 100644
--- a/fggl/entity/loader/loader.cpp
+++ b/fggl/entity/loader/loader.cpp
@@ -52,7 +52,7 @@ namespace fggl::entity {
 						spec.config = compConfig.second;
 
 						// re-run the factory with the modified arguments (might need to support a 'patch' function...)
-						compInfo.factory( spec, manager, eid, gamePtr->owner().services());
+						compInfo.factory( spec, manager, eid, gamePtr->owner().services() );
 					}
 				};
 
diff --git a/fggl/entity/module.cpp b/fggl/entity/module.cpp
index d967b72298a62408cbfc61b1972bec4b02992ada..9860c38f78f1f14c45b88e119e51c8dce3afe708 100644
--- a/fggl/entity/module.cpp
+++ b/fggl/entity/module.cpp
@@ -16,13 +16,14 @@
 // stateless component factories that are probably safe
 //
 
+#include "fggl/services/factory.hpp"
 #include "fggl/entity/module.hpp"
 #include "fggl/math/types.hpp"
 #include "fggl/scenes/game.hpp"
 
 namespace fggl::entity {
 
-	void make_transform(const entity::ComponentSpec &spec, EntityManager &manager, const entity::EntityID entity, modules::Services &/*svc*/) {
+	void make_transform(const entity::ComponentSpec &spec, EntityManager &manager, const entity::EntityID entity, services::Generator */*svc*/) {
 		auto &transform = manager.add<math::Transform>(entity);
 
 		//FIXME won't work for patching!
@@ -66,7 +67,7 @@ namespace fggl::entity {
 
 
 	auto ECS::factory(modules::ServiceName service, modules::Services &services) -> bool {
-		if (service == EntityFactory::service) {
+		/*if (service == EntityFactory::service) {
 			auto *factory = services.create<EntityFactory>(services);
 			install_component_factories(factory);
 
@@ -87,7 +88,40 @@ namespace fggl::entity {
 			checkin->setProcessor(MIME_SCENE, get_scene_deps);
 
 			return true;
-		}
+		}*/
 		return false;
 	}
+
+	void ECS::setup(services::Generator *generator) {
+		auto loader = generator->getLazy<assets::Loader>();
+
+		// describe how scenes can be loaded
+		loader->setFactory(ENTITY_SCENE, load_scene, assets::LoadType::PATH);
+
+		// tell the check-in service what a scene is, and what dependencies it has
+		auto checkin = generator->getLazy<assets::CheckinAdapted>();
+		checkin->setLoader(MIME_SCENE, assets::NEEDS_CHECKIN, is_scene);
+		checkin->setProcessor(MIME_SCENE, get_scene_deps);
+	}
+
+
+	void ECS::wireServices(services::ModuleBinder &binder) {
+		binder.bind<EntityFactory>().toFactory([](fggl::services::Generator* builder){
+			auto factory = std::make_shared<EntityFactory>(builder);
+			install_component_factories(factory.get());
+
+			auto loader = builder->getLazy<assets::Loader>();
+			loader->setFactory(ENTITY_PROTOTYPE, [factory](assets::Loader* loader, const assets::AssetID& a, assets::LoaderContext b, void* ptr){
+				EntityFactory* facPtr = factory.get();
+
+				// if we were passed an entity factory, it might be different/better, use that instead.
+				if ( ptr != nullptr ){
+					facPtr = (EntityFactory*)ptr;
+				}
+				return load_prototype(loader, a, b, facPtr);
+			}, assets::LoadType::PATH);
+
+			return factory;
+		}).asSingleton();
+	}
 }
\ No newline at end of file
diff --git a/fggl/gfx/ogl/renderer.cpp b/fggl/gfx/ogl/renderer.cpp
index 600f1460e827683edf0ce20a1645fcedc9d8ac93..6b3b60ecf18f4878f24cebcbce0dd1cf5ac71e6f 100644
--- a/fggl/gfx/ogl/renderer.cpp
+++ b/fggl/gfx/ogl/renderer.cpp
@@ -131,6 +131,9 @@ namespace fggl::gfx {
 
 	static void setup_shaders(ShaderCache* cache) {
 		// FIXME this should not be hard-coded, it should be part of the scene loading process
+		cache->initFallbackPipelines();
+
+		// force loading phong shaders
 		cache->load(ShaderConfig::named("phong"));
 		cache->load(ShaderConfig::named("redbook/lighting"));
 		cache->load(ShaderConfig::named("redbook/debug"));
@@ -188,8 +191,8 @@ namespace fggl::gfx {
 
 	}
 
-	OpenGL4Backend::OpenGL4Backend(data::Storage *storage, gui::FontLibrary *fonts, assets::AssetManager *assets, GlFunctionLoader loader)
-		: fggl::gfx::Graphics(), m_canvasPipeline(nullptr), m_storage(storage) {
+	OpenGL4Backend::OpenGL4Backend(gui::FontLibrary *fonts, assets::AssetManager *assets, ShaderCache* cache, GlFunctionLoader loader)
+		: fggl::gfx::Graphics(), m_canvasPipeline(nullptr), m_cache(cache) {
 
 		// load OpenGL context, or fail.
 		int version = gladLoadGLLoader(loader);
@@ -214,8 +217,7 @@ namespace fggl::gfx {
 		//glViewport(0, 0, fbSize.x, fbSize.y);
 
 		// setup the shader cache
-		m_cache = std::make_unique<ShaderCache>(m_storage);
-		setup_shaders(m_cache.get());
+		setup_shaders(m_cache);
 
 		// setup 2D rendering system
 		ShaderConfig shader2DConfig = ShaderConfig::named("canvas");
@@ -230,7 +232,7 @@ namespace fggl::gfx {
 
 		// rendering helpers
 		m_canvasRenderer = std::make_unique<ogl4::CanvasRenderer>(fonts);
-		m_modelRenderer = std::make_unique<ogl4::StaticModelRenderer>(m_cache.get(), assets);
+		m_modelRenderer = std::make_unique<ogl4::StaticModelRenderer>(m_cache, assets);
 
 		m_debugRenderer = std::make_unique<ogl4::DebugRenderer>(m_cache->getOrLoad(ShaderConfig::named("debug")));
 		if (m_debugRenderer) {
diff --git a/fggl/gfx/ogl/shader.cpp b/fggl/gfx/ogl/shader.cpp
index 31d96b6a91ededf341c89dcdcd406703b7a3571b..1a2c5172046132570f6f1bf765fca7b0c6d397e4 100644
--- a/fggl/gfx/ogl/shader.cpp
+++ b/fggl/gfx/ogl/shader.cpp
@@ -48,17 +48,15 @@ namespace fggl::gfx {
 	}
 
 	auto ShaderCache::readAndCompileShader(const std::string &filename, GLuint shader) -> bool {
-		std::string source;
-		bool result = m_storage->load(fggl::data::Data, filename, &source);
-		if (!result) {
-			spdlog::warn("could not load shader source from disk: {}", filename.c_str());
+		auto source = m_reader->readText(filename.c_str());
+		if ( !source.has_value() ){
+			spdlog::warn("failed to find shader sources for {}", filename);
 			return false;
 		}
-
-		return compileShaderFromSource(source, shader);
+		return compileShaderFromSource(source.value(), shader);
 	}
 
-	ShaderCache::ShaderCache(fggl::data::Storage *storage) : m_storage(storage), m_shaders(), m_binary(true) {
+	ShaderCache::ShaderCache(filesystem::Locator *storage, filesystem::Reader *reader) : m_storage(storage), m_reader(reader), m_shaders(), m_binary(true) {
 
 		if (!GLAD_GL_ARB_get_program_binary) {
 			spdlog::warn("the graphics card doesn support shader caching, disabling");
@@ -71,12 +69,12 @@ namespace fggl::gfx {
 		if (GLAD_GL_ARB_shading_language_include) {
 			setupIncludes();
 		}
-		initFallbackPipelines();
+		//initFallbackPipelines();
 	}
 
 	void ShaderCache::setupIncludes() {
-		auto root = m_storage->resolvePath(data::StorageType::Data, "include");
-		auto paths = m_storage->findResources(root, ".glsl");
+		/*auto root = m_storage->calculate("include");
+		auto paths = fggl::filesystem::findAllMatching(root, ".glsl");
 
 		for (auto &path : paths) {
 			std::string source;
@@ -85,13 +83,13 @@ namespace fggl::gfx {
 			auto relPath = std::filesystem::relative(path, root);
 			const auto relPathStr = "/" + relPath.string();
 			glNamedStringARB(GL_SHADER_INCLUDE_ARB, -1, relPathStr.c_str(), -1, source.c_str());
-		}
+		}*/
 
 	}
 
 	auto ShaderCache::loadFromDisk(GLuint pid, const std::string &pipelineName) -> bool {
 
-		BinaryCache cache;
+		/*BinaryCache cache;
 		auto fname = "shader_" + pipelineName + ".bin";
 		bool status = m_storage->load(fggl::data::Cache, fname, &cache);
 
@@ -102,15 +100,17 @@ namespace fggl::gfx {
 
 		bool result = cacheLoad(pid, &cache);
 		std::free(cache.data);
-		return result;
+		return result;*/
+
+		return false;
 	}
 
 	void ShaderCache::saveToDisk(GLuint pid, const std::string &pipelineName) {
-		BinaryCache cache;
+		/*BinaryCache cache;
 		cacheSave(pid, &cache);
 
 		auto fname = "shader_" + pipelineName + ".bin";
-		m_storage->save(fggl::data::Cache, fname, &cache);
+		m_storage->save(fggl::data::Cache, fname, &cache);*/
 	}
 
 	auto ShaderCache::getOrLoad(const ShaderConfig &config) -> ShaderCache::ShaderPtr {
@@ -148,17 +148,26 @@ namespace fggl::gfx {
 
 		// TODO actual shader loading
 		GLuint vertShader = glCreateShader(GL_VERTEX_SHADER);
-		readAndCompileShader(config.vertex, vertShader);
+		if( !readAndCompileShader(config.vertex, vertShader) ) {
+			spdlog::warn("compiling vertex shader program '{}' failed.", config.name);
+			return nullptr;
+		}
 		glAttachShader(pid, vertShader);
 
 		GLuint fragShader = glCreateShader(GL_FRAGMENT_SHADER);
-		readAndCompileShader(config.fragment, fragShader);
+		if ( !readAndCompileShader(config.fragment, fragShader) ) {
+			spdlog::warn("compiling fragment shader program '{}' failed.", config.name);
+			return nullptr;
+		}
 		glAttachShader(pid, fragShader);
 
 		GLuint geomShader = 0;
 		if (config.hasGeom) {
 			geomShader = glCreateShader(GL_GEOMETRY_SHADER);
-			readAndCompileShader(config.geometry, geomShader);
+			if (!readAndCompileShader(config.geometry, geomShader)) {
+				spdlog::warn("compiling geometry shader program '{}' failed.", config.name);
+				return nullptr;
+			}
 			glAttachShader(pid, geomShader);
 		}
 
@@ -220,17 +229,26 @@ namespace fggl::gfx {
 
 		// TODO actual shader loading
 		GLuint vertShader = glCreateShader(GL_VERTEX_SHADER);
-		compileShaderFromSource(sources.vertexSource, vertShader);
+		if (!compileShaderFromSource(sources.vertexSource, vertShader)){
+			spdlog::warn("Compiling vertex shader for {} failed", sources.name);
+			return nullptr;
+		}
 		glAttachShader(pid, vertShader);
 
 		GLuint fragShader = glCreateShader(GL_FRAGMENT_SHADER);
-		compileShaderFromSource(sources.fragmentSource, fragShader);
+		if (!compileShaderFromSource(sources.fragmentSource, fragShader)) {
+			spdlog::warn("Compiling fragment shader for {} failed", sources.name);
+			return nullptr;
+		}
 		glAttachShader(pid, fragShader);
 
 		GLuint geomShader = INVALID_SHADER_ID;
 		if (!sources.geometrySource.empty()) {
 			geomShader = glCreateShader(GL_GEOMETRY_SHADER);
-			compileShaderFromSource(sources.geometrySource, geomShader);
+			if (!compileShaderFromSource(sources.geometrySource, geomShader)) {
+				spdlog::warn("Compiling geometry shader for {} failed", sources.name);
+				return nullptr;
+			}
 			glAttachShader(pid, geomShader);
 		}
 
@@ -305,6 +323,7 @@ namespace fggl::gfx {
 
 }
 
+/*
 	template<>
 	auto fggl::data::fggl_deserialize(std::filesystem::path &data, fggl::gfx::BinaryCache *out) -> bool {
 		auto* f =
@@ -347,8 +366,9 @@ namespace fggl::gfx {
 		std::fclose(f);
 		return result;
 	}
+*/
 
-
+/*
 template<>
 auto fggl::data::fggl_deserialize(std::filesystem::path &data, std::string *out) -> bool {
 	std::ifstream ifs(data);
@@ -380,4 +400,4 @@ auto fggl::data::fggl_serialize(std::filesystem::path &data, const fggl::gfx::Bi
 	std::free(out->data);
 
 	return true;
-}
+}*/
diff --git a/fggl/gfx/ogl4/module.cpp b/fggl/gfx/ogl4/module.cpp
index 2a912e96e398a66ae949b8e9e8de2d4b7e894c14..5a488071ae733f60baa7e60a8aa35cbe3f1c41ad 100644
--- a/fggl/gfx/ogl4/module.cpp
+++ b/fggl/gfx/ogl4/module.cpp
@@ -57,15 +57,11 @@ namespace fggl::gfx {
 	}
 
 
-	void attach_mesh(const entity::ComponentSpec &spec, entity::EntityManager &manager, const entity::EntityID &id, modules::Services &services) {
+	void attach_mesh(const entity::ComponentSpec &spec, entity::EntityManager &manager, const entity::EntityID &id, services::Generator *services) {
 
 		// check for the asset service
-		auto* assetService = services.get<assets::AssetManager>();
-		auto* assetLoader = services.get<assets::Loader>();
-		if ( assetService == nullptr || assetLoader == nullptr ) {
-			// no asset service, give up
-			return;
-		}
+		auto assetService = services->getLazy<assets::AssetManager>();
+		auto assetLoader = services->getLazy<assets::Loader>();
 
 		// asset is a procedural mesh description
 		if (spec.has("shape")) {
@@ -147,7 +143,7 @@ namespace fggl::gfx {
 	void attach_material(const entity::ComponentSpec &spec,
 						 entity::EntityManager &manager,
 						 const entity::EntityID &id,
-						 modules::Services& /*services*/ ) {
+						 modules::Services* /*services*/ ) {
 		auto &mat = manager.add<gfx::PhongMaterial>(id);
 		mat.ambient = spec.get<math::vec3>("ambient", gfx::DEFAULT_AMBIENT);
 		mat.diffuse = spec.get<math::vec3>("diffuse", gfx::DEFAULT_DIFFUSE);
@@ -155,7 +151,7 @@ namespace fggl::gfx {
 		mat.shininess = spec.get<float>("ambient", gfx::DEFAULT_SHININESS);
 	}
 
-	void attach_light_directional(const entity::ComponentSpec &spec, entity::EntityManager &manager, const entity::EntityID &id, modules::Services& /*services*/) {
+	void attach_light_directional(const entity::ComponentSpec &spec, entity::EntityManager &manager, const entity::EntityID &id, services::Generator* /*services*/) {
 		auto &light = manager.add<gfx::DirectionalLight>(id);
 		light.position = spec.get<math::vec3>("direction", -math::UP);
 		light.ambient = spec.get<math::vec3>("ambient", gfx::colours::WHITE);
@@ -163,7 +159,7 @@ namespace fggl::gfx {
 		light.diffuse = spec.get<math::vec3>("diffuse", gfx::colours::WHITE);
 	}
 
-	void attach_light_point(const entity::ComponentSpec &spec, entity::EntityManager &manager, const entity::EntityID &id, modules::Services& /*services*/) {
+	void attach_light_point(const entity::ComponentSpec &spec, entity::EntityManager &manager, const entity::EntityID &id, services::Generator* /*services*/) {
 		auto &light = manager.add<gfx::PointLight>(id);
 		light.position = spec.get<math::vec3>("position", math::VEC3_ZERO);
 
@@ -172,27 +168,27 @@ namespace fggl::gfx {
 		light.quadratic = spec.get<float>("quadratic", 0.000007F);
 	}
 
-	auto OpenGL4::factory(modules::ServiceName service, modules::Services &services) -> bool {
-		if (service == WindowGraphics::service) {
-			// setup the thing responsible for graphics
-			auto *storage = services.get<data::Storage>();
-			auto *fontLibrary = services.get<gui::FontLibrary>();
-			auto *assets = services.get<assets::AssetManager>();
-			services.bind<WindowGraphics, ogl4::WindowGraphics>(storage, fontLibrary, assets);
-
-			// register as responsible for creating rendering components
-			auto *entityFactory = services.get<entity::EntityFactory>();
-			entityFactory->bind(data::StaticMesh::guid, attach_mesh);
-			entityFactory->bind(mesh::StaticMesh3D::guid, attach_mesh);
-			entityFactory->bind(mesh::StaticMultiMesh3D::guid, attach_mesh);
-			entityFactory->bind(gfx::PhongMaterial::guid, attach_material);
-
-			entityFactory->bind(gfx::DirectionalLight::guid, attach_light_directional);
-			entityFactory->bind(gfx::PointLight::guid, attach_light_point);
-
-			return true;
-		}
-		return false;
+
+	void OpenGL4::wireServices(fggl::services::ModuleBinder &binder) {
+		binder.bind<WindowGraphics>().to<ogl4::WindowGraphics, filesystem::Locator, filesystem::Reader, gui::FontLibrary, assets::AssetManager>();
+	}
+
+	services::Module::ServiceList OpenGL4::getProvides() const {
+		return { MODULE_ID, API::MODULE_ID };
+	}
+
+	void OpenGL4::setup(services::Generator *generator) {
+		auto entityFac = generator->getLazy<entity::EntityFactory>();
+		entityFac->bind(data::StaticMesh::guid, attach_mesh);
+		entityFac->bind(mesh::StaticMesh3D::guid, attach_mesh);
+		entityFac->bind(mesh::StaticMultiMesh3D::guid, attach_mesh);
+
+		// material
+		entityFac->bind(gfx::PhongMaterial::guid, attach_material);
+
+		// lighting
+		entityFac->bind(gfx::DirectionalLight::guid, attach_light_directional);
+		entityFac->bind(gfx::PointLight::guid, attach_light_point);
 	}
 
 } // namespace fggl::gfX::ogl4
\ No newline at end of file
diff --git a/fggl/gfx/ogl4/setup.cpp b/fggl/gfx/ogl4/setup.cpp
index 7668b21e753106b1f51e74f389985091664db60c..1b51992ac82460b3ac251568e505fd39d17e455e 100644
--- a/fggl/gfx/ogl4/setup.cpp
+++ b/fggl/gfx/ogl4/setup.cpp
@@ -21,7 +21,8 @@
 namespace fggl::gfx::ogl4 {
 
 	auto WindowGraphics::create(display::Window &window) -> Graphics * {
-		return new OpenGL4Backend(m_storage, m_fonts, m_assets, (GlFunctionLoader)glfwGetProcAddress);
+		ShaderCache* cache = new ShaderCache(m_storage, m_reader);
+		return new OpenGL4Backend(m_fonts, m_assets, cache, (GlFunctionLoader)glfwGetProcAddress);
 	}
 
 }
\ No newline at end of file
diff --git a/fggl/gfx/window.cpp b/fggl/gfx/window.cpp
index cac8e496a30f5af1f9471ef5e9fcb2545935c05e..7612d8d339409d763454213b0d3ee2e817f25491 100644
--- a/fggl/gfx/window.cpp
+++ b/fggl/gfx/window.cpp
@@ -156,6 +156,8 @@ namespace fggl::display::glfw {
 
 	Window::Window(std::shared_ptr<GlfwContext> context, gfx::WindowGraphics *graphics)
 		: m_context(std::move(context)), m_window(nullptr), m_framesize() {
+		assert( context != nullptr );
+		assert( graphics != nullptr );
 
 		// don't iconify when focus is lost.
 		glfwWindowHint( GLFW_AUTO_ICONIFY, GLFW_FALSE );
diff --git a/fggl/gui/fonts.cpp b/fggl/gui/fonts.cpp
index 634543f346edb0e7908f80bd98a2ece0ddd79f45..74d689f45afa4ee4268f905882f1e3e50f82ec16 100644
--- a/fggl/gui/fonts.cpp
+++ b/fggl/gui/fonts.cpp
@@ -23,7 +23,7 @@ namespace fggl::gui {
 		FT_Done_Face(m_face);
 	}
 
-	FontLibrary::FontLibrary(data::Storage *storage) : m_context(nullptr), m_storage(storage) {
+	FontLibrary::FontLibrary(filesystem::Locator *storage) : m_context(nullptr), m_storage(storage) {
 		FT_Init_FreeType(&m_context);
 		m_defaultFont = getFont(DEFAULT_FONT_NAME);
 	}
diff --git a/fggl/input/input.cpp b/fggl/input/input.cpp
index 093ccf0a5c5f9c331d8d11a7747c5b18ac1d0bd6..f88f4a90009eb9766bc60c8b60761992b7bf1f72 100644
--- a/fggl/input/input.cpp
+++ b/fggl/input/input.cpp
@@ -22,4 +22,6 @@ namespace fggl::input {
 		gamepads.frame(dt);
 	}
 
+
+
 } // namespace fggl::input
diff --git a/fggl/platform/linux/fonts.cpp b/fggl/input/module.cpp
similarity index 59%
rename from fggl/platform/linux/fonts.cpp
rename to fggl/input/module.cpp
index 1362cf58e6353c2bee5d8084358d0e49701846cb..0cba23b05ccbb4d87f3f544d01a83e1ff622ed1a 100644
--- a/fggl/platform/linux/fonts.cpp
+++ b/fggl/input/module.cpp
@@ -13,22 +13,15 @@
  */
 
 //
-// Created by webpigeon on 23/06/22.
+// Created by webpigeon on 12/07/23.
 //
 
-#include <fontconfig/fontconfig.h>
+#include "fggl/input/module.hpp"
 
-namespace fggl::platform::Linux {
+namespace fggl::input {
 
-	void get_font() {
-		/*	FcConfig *config = FcInitLoadConfigAndFonts();
-			FcPattern* pat = FcPatternCreate();
-			FcObjectSet* os = FcObjectSetBuild(FC_FAMILY, FC_STYLE, FC_WEIGHT, FC_SLANT, FC_PIXEL_SIZE, FC_SIZE, nullptr);
-			FcFontSet* fs = FcFontList(config, pat, os);
-
-			if ( fs ) {
-				FcFontSetDestroy(fs);
-			}*/
+	void Generic::wireServices(services::ModuleBinder &binder) {
+		binder.bind<input::Input>().simply().asSingleton();
 	}
 
-} // namespace fggl::platform::linux
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/fggl/modules/manager.cpp b/fggl/modules/manager.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..391167f46050066954cdaf7f19140606832f82dd
--- /dev/null
+++ b/fggl/modules/manager.cpp
@@ -0,0 +1,76 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 12/07/23.
+//
+
+#include "fggl/modules/manager.hpp"
+
+namespace fggl::modules {
+
+	bool Manager::buildGraph() {
+		// resolve links between modules
+		for (auto &moduleItr : m_modules) {
+			debug::trace("Generating vertex for {}", moduleItr.first);
+
+			// add us to the module graph
+			m_dependencies.addVertex( moduleItr.first );
+
+			// wire us to the contracts and modules we depend on
+			for (auto &service : moduleItr.second->getDepends()) {
+
+				auto provider = m_serviceProviders.find(service);
+				if (provider == m_serviceProviders.end()) {
+					debug::log(debug::Level::warning,
+							   "{} depends on {}, but nothing provides it",
+							   moduleItr.first,
+							   service);
+					// nothing can provide the service requested, setup is invalid.
+					return false;
+				}
+
+				m_dependencies.addEdge(moduleItr.first, provider->second);
+			}
+		}
+		return true;
+	}
+
+	bool Manager::resolve() {
+		assert( !m_locked );
+		if (!buildGraph()) {
+			fggl::debug::error("Failed to build module dependency graph, your configuration is broken!");
+			return false;
+		}
+
+		std::queue<ModuleIdentifier> stack;
+		m_dependencies.getOrderRev(stack);
+
+		services::ModuleBinder binder(&m_services);
+		while (!stack.empty()) {
+			auto nextToInit = stack.front();
+			debug::log(debug::Level::info, "Initializing {}", nextToInit);
+
+			auto &module = m_modules.at(nextToInit);
+			module->setup( &m_services );
+			module->wireServices( binder );
+			stack.pop();
+		}
+
+		debug::log(debug::Level::info, "engine boot complete");
+		m_locked = true;
+		return true;
+	}
+
+} // namespace fggl::modules
\ No newline at end of file
diff --git a/fggl/phys/null.cpp b/fggl/phys/null.cpp
index 9df68b27de9787c0e779880a3e36e26cc5092e26..eeb2ca41a2bed9bce7d86bf94295bde074068486 100644
--- a/fggl/phys/null.cpp
+++ b/fggl/phys/null.cpp
@@ -21,10 +21,10 @@
 namespace fggl::phys {
 
 	auto NullPhysics::factory(modules::ServiceName serviceName, modules::Services &serviceManager) -> bool {
-		if (serviceName == phys::PhysicsProvider::service) {
+		/*if (serviceName == phys::PhysicsProvider::service) {
 			serviceManager.bind<phys::PhysicsProvider, NullPhysicsProvider>();
 			return true;
-		}
+		}*/
 		return false;
 	}
 }
\ No newline at end of file
diff --git a/fggl/platform/linux/CMakeLists.txt b/fggl/platform/linux/CMakeLists.txt
deleted file mode 100644
index a177d99b15013ab1b85c4da967f297438a7ba867..0000000000000000000000000000000000000000
--- a/fggl/platform/linux/CMakeLists.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-#find_package(Fontconfig)
-#target_link_libraries(fggl PRIVATE Fontconfig::Fontconfig)
-
-target_sources(fggl
-        PRIVATE
-        #       fonts.cpp
-        paths.cpp
-        )
\ No newline at end of file
diff --git a/include/fggl/app.hpp b/include/fggl/app.hpp
index 0fffefb57abf3b9a6fb42e4f6f28744c59603040..be791a74ab82a14a9d32bc4c9358fe68816a5518 100644
--- a/include/fggl/app.hpp
+++ b/include/fggl/app.hpp
@@ -23,6 +23,8 @@
 #include <unordered_map>
 
 #include "fggl/display/glfw/window.hpp"
+#include "fggl/services/service.hpp"
+
 #include <fggl/gfx/paint.hpp>
 #include <fggl/util/states.hpp>
 #include "fggl/modules/manager.hpp"
@@ -191,20 +193,16 @@ namespace fggl {
              *
              * returns nullptr if the service does not exist, or cannot be provided.
              */
-			template<typename T>
+			template<services::ServiceAPI T>
 			inline auto service() -> T * {
 				try {
 					return m_subsystems->template get<T>();
 				} catch (std::out_of_range &e) {
-					debug::log(debug::Level::error, "Service not found: {}", T::service.get());
-					return nullptr;
+					debug::log(debug::Level::error, "Service not found: {}", FGGL_SERVICE(T).get() );
+					throw std::runtime_error("could not load required service, abort!");
 				}
 			}
 
-			inline auto services() -> modules::Services& {
-				return m_subsystems->services();
-			}
-
 			inline auto running() const -> bool {
 				return m_running;
 			}
@@ -213,6 +211,10 @@ namespace fggl {
 				m_running = state;
 			}
 
+			inline auto* services() {
+				return m_subsystems->services();
+			}
+
 		private:
 			bool m_running;
 			display::Window *m_window;
diff --git a/include/fggl/assets/loader.hpp b/include/fggl/assets/loader.hpp
index 6c104bd64c5ffe7d8a22a3a4777cec4812db250f..6778e730d6b7bcefcc581d9d9d6998438083f5fd 100644
--- a/include/fggl/assets/loader.hpp
+++ b/include/fggl/assets/loader.hpp
@@ -46,11 +46,12 @@ namespace fggl::assets {
 
 	class Loader {
 		public:
-			constexpr const static auto service = modules::make_service("fggl::assets::Loader");
+			FGGL_DEFINE_SERVICE(fggl::assets::Loader)
+			FGGL_IMPLEMENT_SERVICE(Loader, Loader, filesystem::Locator, CheckinAdapted)
 
-			explicit inline Loader(data::Storage *storage, CheckinAdapted *checkin) : m_storage(storage), m_checkin(checkin) {}
+			explicit inline Loader(filesystem::Locator *storage, CheckinAdapted *checkin) : m_storage(storage), m_checkin(checkin) {}
 
-			explicit Loader(Loader *parent, data::Storage *storage) : m_parent(parent), m_storage(storage) {};
+			explicit Loader(Loader *parent, filesystem::Locator *storage) : m_parent(parent), m_storage(storage) {};
 
 			// no move, no copy.
 			Loader(const Loader &) = delete;
@@ -109,8 +110,10 @@ namespace fggl::assets {
 				if ( !checkinLoad ) {
 					debug::info("could not perform checkin load for {} - missing loaders?", guid);
 
-					auto path = m_storage->resolvePath(data::StorageType::Data, guid);
-					loadDirect(path, assetID, type, pack, userPtr);
+					auto path = m_storage->find(guid);
+					if ( path.has_value() ) {
+						loadDirect(path.value(), assetID, type, pack, userPtr);
+					}
 				}
 			}
 
@@ -180,7 +183,7 @@ namespace fggl::assets {
 		private:
 			Loader *m_parent = nullptr;
 			using Config = std::pair<Checkin, LoadType>;
-			data::Storage *m_storage;
+			filesystem::Locator *m_storage;
 			CheckinAdapted *m_checkin;
 			std::queue<ResourceRequest> m_requests;
 			std::map<AssetTypeID, Config> m_factories;
diff --git a/include/fggl/assets/manager.hpp b/include/fggl/assets/manager.hpp
index 86dd5c1cfea4495ddee24ee365cce37a88f8cd09..3a570f0f73f1ac7f744d5127a26e584408966f42 100644
--- a/include/fggl/assets/manager.hpp
+++ b/include/fggl/assets/manager.hpp
@@ -66,7 +66,7 @@ namespace fggl::assets {
 
 	class AssetManager {
 		public:
-			constexpr const static modules::ServiceName service = modules::make_service("fggl::assets::Manager");
+			FGGL_DEFINE_SERVICE(fggl::assets::Manager)
 
 			AssetManager() = default;
 			virtual ~AssetManager() = default;
diff --git a/include/fggl/assets/module.hpp b/include/fggl/assets/module.hpp
index 81e09abb65fe1bcbdfd93aa7acf84735763f86ce..7e150ac631ae51d0cacd8e444169c70d113f6f68 100644
--- a/include/fggl/assets/module.hpp
+++ b/include/fggl/assets/module.hpp
@@ -20,6 +20,7 @@
 #define FGGL_ASSETS_MODULE_HPP
 
 #include "fggl/modules/module.hpp"
+	#include "fggl/services/module.hpp"
 
 #include "fggl/data/module.hpp"
 
@@ -29,17 +30,10 @@
 
 namespace fggl::assets {
 
-	struct AssetFolders {
-		constexpr static const char *name = "fggl::assets::Folders";
-		constexpr static const std::array<modules::ServiceName, 2> provides = {
-			Loader::service,
-			AssetManager::service
-		};
-		constexpr static const std::array<modules::ServiceName, 2> depends = {
-			data::Storage::service,
-			CheckinAdapted::service
-		};
-		static bool factory(modules::ServiceName name, modules::Services &serviceManager);
+	struct AssetFolders : public services::Module {
+		FGGL_MODULE(fggl::assets::Folders, fggl::filesystem::API)
+
+		void wireServices(services::ModuleBinder& binder) override;
 	};
 
 } // namespace fggl::assets
diff --git a/include/fggl/assets/packed/adapter.hpp b/include/fggl/assets/packed/adapter.hpp
index a5cbab2871b75fac516edaebe46c0c381edd9b37..ec5a841ed2351f1f0191787b44a87a9e9e066168 100644
--- a/include/fggl/assets/packed/adapter.hpp
+++ b/include/fggl/assets/packed/adapter.hpp
@@ -23,6 +23,8 @@
 #include <ranges>
 #include <algorithm>
 
+#include "fggl/util/safety.hpp"
+#include "fggl/filesystem/Finder.hpp"
 #include "fggl/assets/packed/direct.hpp"
 #include "fggl/data/storage.hpp"
 #include "fggl/ds/graph.hpp"
@@ -65,13 +67,14 @@ namespace fggl::assets {
 	 */
 	class CheckinAdapted {
 		public:
-			constexpr const static auto service = modules::make_service("fggl::assets::checkin::debug");
+			FGGL_DEFINE_SERVICE(fggl::assets::checkin::debug)
+			FGGL_IMPLEMENT_SERVICE(CheckinAdapted, CheckinAdapted, filesystem::Locator, RawCheckin)
 
 			using FilePredicate = std::function<AssetTypeID(const std::filesystem::path&)>;
 			using FileLoader = std::function<bool(const std::filesystem::path&, MemoryBlock& block)>;
 			using AssetMetadata = std::function<bool(const std::string& pack, const std::filesystem::path& packRoot, ResourceRecord&)>;
 
-			CheckinAdapted(data::Storage* storage, RawCheckin* checkSvc) : m_storage(storage), m_checkin(checkSvc) {};
+			CheckinAdapted(filesystem::Locator* storage, RawCheckin* checkSvc) : m_storage(storage), m_checkin(checkSvc) {};
 
 			// asset loading
 			void load(AssetID asset) {
@@ -105,8 +108,8 @@ namespace fggl::assets {
 					return;
 				}
 
-				std::string packRoot = "packs/";
-				auto packDir = m_storage->resolvePath( data::StorageType::Data, packRoot + packName );
+				const std::string packRoot = "packs/";
+				auto packDir = m_storage->calculate( packRoot + packName );
 				discover(packDir, useCache, updateCache);
 			}
 
@@ -116,7 +119,7 @@ namespace fggl::assets {
 				auto packName = packDir.filename();
 				m_packs[packName].rootDir = packDir;
 
-				if ( useCache && has_manifest(packDir)) {
+				if ( useCache && has_manifest(packDir) ) {
 					// check if we've cached the search
 					load_manifest(packName, packDir);
 					return;
@@ -169,7 +172,7 @@ namespace fggl::assets {
 			}
 
 		private:
-			data::Storage* m_storage;
+			filesystem::Locator* m_storage;
 			RawCheckin* m_checkin;
 			std::map<AssetID, ResourceRecord> m_files;
 			ds::DirectedGraph<AssetID> m_requires;
@@ -184,7 +187,7 @@ namespace fggl::assets {
 			std::map<ResourceType, AssetMetadata> m_metadata;
 			std::map<std::string, PackInfo> m_packs;
 
-			void process_file(std::filesystem::path path, std::filesystem::path packDir, std::vector<AssetID> packFiles) {
+			void process_file(const std::filesystem::path& path, const std::filesystem::path& packDir, std::vector<AssetID> packFiles) {
 				for( auto& [rType, pred] : m_predicates ) {
 					// check the predicate, is this valid?
 					auto aType = pred(path);
@@ -219,8 +222,7 @@ namespace fggl::assets {
 			}
 
 			inline bool has_manifest(const std::string& packName) {
-				auto packManifest = m_storage->resolvePath( data::StorageType::Cache, packName + "_manifest.bin" );
-				return std::filesystem::exists(packManifest);
+				return m_storage->find( packName + "_manifest.bin", filesystem::Context::CACHE ).has_value();
 			}
 
 			void load_manifest_entry(FILE* file, const std::string& packName, const std::filesystem::path& packRoot) {
@@ -269,15 +271,16 @@ namespace fggl::assets {
 			}
 
 			void load_manifest(const std::string& packName, const std::filesystem::path& packRoot) {
-				auto packManifest = m_storage->resolvePath( data::StorageType::Cache, packName + "_manifest.bin" );
-				if ( !std::filesystem::exists(packManifest) ) {
+				auto packManifest = m_storage->find( packName + "_manifest.bin", filesystem::Context::CACHE );
+				if ( !packManifest.has_value() ) {
+					debug::warning("no asset pack manifest to load");
 					return;
 				}
 
 				// open the manifest file and start extracting entries
-				FILE* file = std::fopen(packManifest.c_str(), "r");
+				FILE* file = std::fopen(packManifest->c_str(), "r");
 				if ( file == nullptr ) {
-					debug::warning("error opening manifest: {}", packManifest.c_str());
+					debug::warning("error opening manifest: {}", packManifest->c_str());
 					return;
 				}
 
@@ -292,11 +295,14 @@ namespace fggl::assets {
 			}
 
 			void save_manifest(const std::string& packName, const std::filesystem::path& packRoot, const std::vector<AssetID>& assets) {
-				auto packManifest = m_storage->resolvePath( data::StorageType::Cache, packName + "_manifest.bin", true);
+				auto packManifest = m_storage->createUser( packName + "_manifest.bin", filesystem::Context::CACHE );
+				if ( !packManifest.has_value() ){
+					return;
+				}
 
-				FILE* file = std::fopen(packManifest.c_str(), "w");
+				FILE* file = std::fopen(packManifest->c_str(), "w");
 				if ( file == nullptr) {
-					debug::warning("error saving manifest {}, missing dir?", packManifest.c_str());
+					debug::warning("error saving manifest {}, missing dir?", packManifest->c_str());
 					return;
 				}
 
diff --git a/include/fggl/assets/packed/direct.hpp b/include/fggl/assets/packed/direct.hpp
index e184ab48210b8c6c5de152115adb88e02da6e79d..66174e0dff20f32f7213629fdb509688521f1517 100644
--- a/include/fggl/assets/packed/direct.hpp
+++ b/include/fggl/assets/packed/direct.hpp
@@ -37,7 +37,8 @@ namespace fggl::assets {
 
 	class RawCheckin {
 		public:
-			constexpr const static auto service = modules::make_service("fggl::assets::checkin");
+			FGGL_DEFINE_SERVICE(fggl::assets::Checkin)
+
 			using DecodeAndCheckFunc = std::function<void(AssetGUID, MemoryBlock& block)>;
 
 			void check(AssetID, AssetTypeID, MemoryBlock& block) const;
diff --git a/include/fggl/assets/packed/module.hpp b/include/fggl/assets/packed/module.hpp
index c99b99d738f560ce667c729085ec8ddf9c2e6c21..fb25fe0819a6a68d0ae75926eabed70275f1c050 100644
--- a/include/fggl/assets/packed/module.hpp
+++ b/include/fggl/assets/packed/module.hpp
@@ -20,6 +20,8 @@
 #define FGGL_ASSETS_PACKED_MODULE_HPP
 
 #include "fggl/modules/module.hpp"
+#include "fggl/services/module.hpp"
+
 #include "fggl/data/module.hpp"
 #include "fggl/assets/loader.hpp"
 
@@ -28,16 +30,10 @@
 
 namespace fggl::assets {
 
-	struct PackedAssets {
-		constexpr static const char *name = "fggl::assets::packed";
-		constexpr static const std::array<modules::ServiceName, 2> provides = {
-			RawCheckin::service,
-			CheckinAdapted::service
-		};
-		constexpr static const std::array<modules::ServiceName, 1> depends = {
-			data::Storage::service
-		};
-		static bool factory(modules::ServiceName name, modules::Services &serviceManager);
+	struct PackedAssets : services::Module {
+		FGGL_MODULE(fggl::assets::PackedAssets, fggl::filesystem::API)
+
+		void wireServices(services::ModuleBinder& binder) override;
 	};
 
 } // namespace fggl::assets
diff --git a/include/fggl/audio/audio.hpp b/include/fggl/audio/audio.hpp
index 3c021a4fbf6681e357aa66f9c90d9fe247543e15..7ac7b39963a4f46d31793ea5c5d20133320e8560 100644
--- a/include/fggl/audio/audio.hpp
+++ b/include/fggl/audio/audio.hpp
@@ -19,6 +19,7 @@
 
 #include "fggl/data/storage.hpp"
 #include "fggl/modules/module.hpp"
+	#include "fggl/services/module.hpp"
 #include "fggl/assets/module.hpp"
 #include "fggl/assets/packed/module.hpp"
 
@@ -57,7 +58,20 @@ namespace fggl::audio {
 	 constexpr auto ASSET_CLIP_SHORT = assets::make_asset_type("Audio:Clip:Short");
 	 constexpr auto ASSET_CLIP_BYTE = assets::make_asset_type("Audio:Clip:Byte");
 
-	constexpr auto SERVICE_AUDIO_PLAYBACK = modules::make_service("fggl::audio::AudioService");
+	 /**
+	  * Audio Playback Contract.
+	  *
+	  * A module depending on this contract my assume that its lifetime is bounded by the existance of a low-level
+	  * audio device. In other words, its setup method will be called after the audio device has been created and its
+	  * teardown method will be called before the audio device is unavailable.
+	  *
+	  * A module providing this contract must:
+	  *  * Ensure the low-level audio APIs are available once its setup method has been invoked
+	  *  * Ensure the low-level audio APIs are cleaned up one its teardown method has been invoked
+	  *  * Provide an instance of of an AudioService
+	  *
+	  */
+	 FGGL_MODULE_CONTRACT(AUDIO_PROVIDER, fggl::audio::Provider);
 
 	/**
 	 *
@@ -65,7 +79,7 @@ namespace fggl::audio {
 	 */
 	class AudioService {
 		public:
-			constexpr static const modules::ServiceName service = SERVICE_AUDIO_PLAYBACK;
+			FGGL_DEFINE_SERVICE(fggl::audio::AudioService)
 
 			virtual void play(const assets::AssetGUID &asset, bool looping = false) = 0;
 			virtual void play(const AudioClipShort &clip, bool looping = false) = 0;
diff --git a/include/fggl/audio/null_audio.hpp b/include/fggl/audio/null_audio.hpp
index 4d14b2746da7dc9cb85bb0b1505c9c9d990e587d..7035e058911a680e342fb7d350bbcf57c1678237 100644
--- a/include/fggl/audio/null_audio.hpp
+++ b/include/fggl/audio/null_audio.hpp
@@ -20,6 +20,7 @@
 #define FGGL_AUDIO_NULL_AUDIO_HPP
 
 #include "fggl/audio/audio.hpp"
+#include "fggl/services/module.hpp"
 
 namespace fggl::audio {
 
@@ -38,13 +39,10 @@ namespace fggl::audio {
 	};
 
     //! A dummy audio module that does nothing
-	struct NullAudio {
-		constexpr static const char *name = "fggl::audio::NULL";
-		constexpr static const std::array<modules::ServiceName, 1> provides = {
-			SERVICE_AUDIO_PLAYBACK
-		};
-		constexpr static const std::array<modules::ServiceName, 0> depends = {};
-		bool factory(modules::ServiceName, modules::Services&);
+	struct NullAudio : public services::Module {
+		FGGL_MODULE(fggl::audio::NullAudio)
+
+		void wireServices(services::ModuleBinder& binder) override;
 	};
 
 
diff --git a/include/fggl/audio/openal/module.hpp b/include/fggl/audio/openal/module.hpp
index d6236ed95596b1050b4b7a982ac9652a1f9ead28..6eea7a6b9a4dbd50c907ef42852b3aab40a605d9 100644
--- a/include/fggl/audio/openal/module.hpp
+++ b/include/fggl/audio/openal/module.hpp
@@ -34,16 +34,11 @@ namespace fggl::audio {
 	constexpr auto RES_OGG_VORBIS = assets::from_mime("audio/vorbis");
 
     //! an audio module which uses openal(-soft) as a backend.
-	struct OpenAL {
-		constexpr static const char *name = "fggl::audio::OpenAL";
-		constexpr static const std::array<modules::ServiceName, 1> provides = {
-			SERVICE_AUDIO_PLAYBACK
-		};
-		constexpr static const std::array<modules::ServiceName, 2> depends = {
-			assets::AssetManager::service,
-			assets::CheckinAdapted::service
-		};
-		static bool factory(modules::ServiceName name, modules::Services &serviceManager);
+	struct OpenAL : public services::Module {
+		FGGL_MODULE( fggl::audio::OpenAL, fggl::assets::AssetFolders );
+
+		void setup(services::Generator* generator) override;
+		void wireServices(services::ModuleBinder& binder) override;
 	};
 
 
diff --git a/include/fggl/data/assimp/module.hpp b/include/fggl/data/assimp/module.hpp
index 98e7a50e30075afc111860f128a530d442df788b..9901cc6e1e9ced16d42eac936f121982a623509c 100644
--- a/include/fggl/data/assimp/module.hpp
+++ b/include/fggl/data/assimp/module.hpp
@@ -19,14 +19,16 @@
 #ifndef FGGL_DATA_ASSIMP_MODULE_HPP
 #define FGGL_DATA_ASSIMP_MODULE_HPP
 
-#include "fggl/modules/module.hpp"
-#include "fggl/assets/loader.hpp"
-#include "fggl/assets/packed/module.hpp"
-#include "fggl/data/texture.hpp"
+	#include "fggl/modules/module.hpp"
+	#include "fggl/services/module.hpp"
+
+	#include "fggl/assets/loader.hpp"
+	#include "fggl/assets/packed/module.hpp"
+	#include "fggl/data/texture.hpp"
 
 namespace fggl::data::models {
 
-	constexpr auto MODEL_PROVIDER = modules::make_service("fggl::data::Model");
+	//constexpr auto MODEL_PROVIDER = modules::make_service("fggl::data::Model");
 	constexpr auto ASSIMP_MODEL = assets::AssetType::make("model::assimp");
 
 	constexpr auto MIME_JPG = assets::from_mime("image/jpeg");
@@ -49,16 +51,11 @@ namespace fggl::data::models {
 	assets::AssetTypeID is_model_assimp(const std::filesystem::path& filePath);
 	bool extract_requirements(const std::string& packName, const std::filesystem::path& packRoot, assets::ResourceRecord& rr);
 
-	struct AssimpModule {
-		constexpr static const char *name = "fggl::data::Assimp";
-		constexpr static const std::array<modules::ServiceName, 1> provides = {
-			MODEL_PROVIDER
-		};
-		constexpr static const std::array<modules::ServiceName, 2> depends = {
-			assets::Loader::service,
-			assets::CheckinAdapted::service
-		};
-		static bool factory(modules::ServiceName service, modules::Services &serviceManager);
+	struct AssimpModule : public services::Module {
+		FGGL_MODULE(fggl::data::Assimp, fggl::assets::PackedAssets)
+
+		void setup(services::Generator* generator) override;
+		void wireServices(services::ModuleBinder& binder) override;
 	};
 
 }
diff --git a/include/fggl/data/module.hpp b/include/fggl/data/module.hpp
index dc9cd5b9dd371d9566e4a4f422b49097703a23d1..5c72d9933c71e56f1c6a12eff93db26c8267109f 100644
--- a/include/fggl/data/module.hpp
+++ b/include/fggl/data/module.hpp
@@ -20,11 +20,14 @@
 #define FGGL_DATA_MODULE_HPP
 
 #include "fggl/modules/module.hpp"
+#include "fggl/filesystem/Finder.hpp"
 #include "fggl/data/storage.hpp"
-#include "fggl/platform/paths.hpp"
 
 namespace fggl::data {
 
+	using Storage = fggl::filesystem::Locator;
+
+	/*
 	struct LocalStorage {
 		constexpr static const char *name = "fggl::data::Storage";
 		constexpr static const std::array<modules::ServiceName, 1> provides = {
@@ -32,7 +35,7 @@ namespace fggl::data {
 		};
 		constexpr static const std::array<modules::ServiceName, 0> depends = {};
 		static bool factory(modules::ServiceName service, modules::Services &serviceManager);
-	};
+	};*/
 
 } // namespace fggl::data
 
diff --git a/include/fggl/data/storage.hpp b/include/fggl/data/storage.hpp
index 79fb3a16562b3680737b7eea6b056fa7cdca8190..ae4d717643fec2e0f0d5db886a86e65c2e4be214 100644
--- a/include/fggl/data/storage.hpp
+++ b/include/fggl/data/storage.hpp
@@ -15,90 +15,4 @@
 #ifndef FGGL_DATA_STORAGE_HPP
 #define FGGL_DATA_STORAGE_HPP
 
-#include <iostream>
-#include <string>
-#include <filesystem>
-#include <utility>
-#include <vector>
-
-#include "fggl/debug/logging.hpp"
-#include "fggl/modules/module.hpp"
-#include "fggl/platform/paths.hpp"
-
-namespace fggl::data {
-
-	template<typename T>
-	bool fggl_serialize(std::filesystem::path &data, const T *out);
-
-	template<typename T>
-	bool fggl_deserialize(std::filesystem::path &data, T *out);
-
-	enum StorageType { Data, Config, Cache };
-
-	constexpr const auto SERVICE_STORAGE = modules::make_service("fggl::data::Storage");
-
-	class Storage {
-		public:
-			constexpr static auto service = SERVICE_STORAGE;
-
-			Storage(fggl::platform::EnginePaths paths) : m_paths(std::move(paths)) {}
-
-			template<typename T>
-			bool load(StorageType pool, const std::string &name, T *out) {
-				auto path = resolvePath(pool, name);
-				if (!std::filesystem::exists(path)) {
-					debug::log(debug::Level::warning, "Attempted to load '{}', but it did not exist", path.string());
-					return false;
-				}
-				return fggl_deserialize<T>(path, out);
-			}
-
-			std::vector<std::filesystem::path> findResources(std::filesystem::path root, const std::string ext) {
-				if (!std::filesystem::exists(root)) {
-					return {};
-				}
-
-				std::vector<std::filesystem::path> paths;
-				for (const auto &entry : std::filesystem::recursive_directory_iterator(root)) {
-					if (entry.is_regular_file() && entry.path().extension() == ext) {
-						paths.push_back(entry);
-					}
-				}
-				return paths;
-			}
-
-			template<typename T>
-			void save(StorageType pool, const std::string &name, const T *out) {
-				auto path = resolvePath(pool, name, true);
-				fggl_serialize<T>(path, out);
-			}
-
-			inline std::filesystem::path resolvePath(StorageType pool,
-													 const std::string &name,
-													 bool createParents = false) {
-				std::filesystem::path path;
-				switch (pool) {
-				case Data: path = fggl::platform::locate_data(m_paths, name);
-					break;
-				case Config: path = fggl::platform::locate_config(m_paths, name);
-					break;
-				case Cache: path = fggl::platform::locate_cache(m_paths, name);
-					break;
-				}
-
-				if (createParents) {
-					if (!std::filesystem::exists(path.parent_path())) {
-						std::filesystem::create_directories(path.parent_path());
-					}
-				}
-
-				return path;
-			}
-
-		private:
-			fggl::platform::EnginePaths m_paths;
-	};
-
-}
-
 #endif
diff --git a/include/fggl/debug/logging.hpp b/include/fggl/debug/logging.hpp
index f3ab6bfd565e8bc3fe246f780b111c808c7d0e47..f3b61486fd994c7dc7cf65d982d7f18ce20c299b 100644
--- a/include/fggl/debug/logging.hpp
+++ b/include/fggl/debug/logging.hpp
@@ -21,8 +21,6 @@
 
 #include <string_view>
 
-
-
 namespace fggl::debug {
 
 	std::string demangle(const char* name);
diff --git a/include/fggl/display/glfw/module.hpp b/include/fggl/display/glfw/module.hpp
index b844fa330aa10af040c8e8f8451fa600bddbe4fc..e2d5e6294876946b15f44dacec5217e89e5931b8 100644
--- a/include/fggl/display/glfw/module.hpp
+++ b/include/fggl/display/glfw/module.hpp
@@ -22,35 +22,24 @@
 #include "fggl/display/window.hpp"
 #include "fggl/input/module.hpp"
 
+#include "fggl/services/module.hpp"
 #include "fggl/display/glfw/window.hpp"
 #include "fggl/display/glfw/services.hpp"
 
 namespace fggl::display {
 
-	struct GLFW {
-		constexpr static const char *name = "fggl::display::glfw";
-		constexpr static const std::array<modules::ServiceName, 1> provides = {
-			WindowService::service
-		};
-		constexpr static const std::array<modules::ServiceName, 2> depends = {
-			fggl::input::Input::service,
-			fggl::gfx::WindowGraphics::service
-		};
-
-		static bool factory(modules::ServiceName name, modules::Services &serviceManager);
+	struct GLFW : public services::Module {
+		FGGL_MODULE(fggl::display::GLFW, fggl::gfx::API, fggl::input::Generic)
+
+		void setup(services::Generator*) override;
+		void teardown() override;
+
+		void wireServices(services::ModuleBinder& binder) override;
+
+		private:
+			std::shared_ptr<glfw::GlfwContext> m_context;
 	};
 
-	bool GLFW::factory(modules::ServiceName service, modules::Services &services) {
-		if (service == WindowService::service) {
-			auto input = services.get<input::Input>();
-			auto graphics = services.get<gfx::WindowGraphics>();
-
-			auto context = std::make_shared<glfw::GlfwContext>(input);
-			services.bind<WindowService, glfw::WindowService>(context, graphics);
-			return true;
-		}
-		return false;
-	}
 
 } // namespace fggl::display
 
diff --git a/include/fggl/display/glfw/services.hpp b/include/fggl/display/glfw/services.hpp
index 71ca9f5813b98257b84b65323887df1624147d4d..2f15dcbc8fcf7900c2db35560f254124697de31a 100644
--- a/include/fggl/display/glfw/services.hpp
+++ b/include/fggl/display/glfw/services.hpp
@@ -29,12 +29,15 @@ namespace fggl::display::glfw {
 
 	class WindowService : public display::WindowService {
 		public:
-			explicit WindowService(std::shared_ptr<GlfwContext> context, gfx::WindowGraphics *gfx)
-				: m_context(std::move(context)), m_gfx(gfx), m_windows() {}
+			inline explicit WindowService(gfx::WindowGraphics *gfx, std::shared_ptr<GlfwContext> context)
+				: m_gfx(gfx), m_context(std::move(context)) {}
 
 			virtual ~WindowService() = default;
 
 			display::Window *create() override {
+				assert( m_context != nullptr );
+				assert( m_gfx != nullptr );
+
 				m_windows.push_back(std::make_unique<Window>(m_context, m_gfx));
 				return m_windows.back().get();
 			}
@@ -44,8 +47,8 @@ namespace fggl::display::glfw {
 			}
 
 		private:
+			gfx::WindowGraphics *m_gfx = nullptr;
 			std::shared_ptr<GlfwContext> m_context;
-			gfx::WindowGraphics *m_gfx;
 			std::vector<std::unique_ptr<Window>> m_windows;
 	};
 
diff --git a/include/fggl/display/window.hpp b/include/fggl/display/window.hpp
index 8842387bcc5cb1d9a1c28d5ca12ecbef07413311..472bef8781b165df0be44e77b390a1468d6f03c6 100644
--- a/include/fggl/display/window.hpp
+++ b/include/fggl/display/window.hpp
@@ -60,8 +60,7 @@ namespace fggl::display {
 
 	class WindowService {
 		public:
-			constexpr static const auto
-				service = modules::make_service("fggl::display::WindowService");
+			FGGL_DEFINE_SERVICE(fggl::display::WindowService)
 
 			virtual Window *create() = 0;
 			virtual void pollEvents() = 0;
diff --git a/include/fggl/ds/graph.hpp b/include/fggl/ds/graph.hpp
index 8675c08564ca7afafff015bdf702f1bb5e77edc6..7c4bdc6a6716d5dacbd9bea01ed08720d848dd36 100644
--- a/include/fggl/ds/graph.hpp
+++ b/include/fggl/ds/graph.hpp
@@ -41,7 +41,7 @@ namespace fggl::ds {
 			 * @param end the thing it depends on
 			 */
 			inline void addEdge(const T start, const T end) {
-				m_edges[start].push_back(end);
+				m_edges.at(start).push_back(end);
 				m_edges[end];
 			}
 
@@ -140,9 +140,11 @@ namespace fggl::ds {
 			void sortUtilRev(T idx, std::set<T> &visited, std::queue<T> &stack) {
 				visited.emplace(idx);
 
+				assert( m_edges.contains(idx) );
 				for (auto dep : m_edges.at(idx)) {
-					if (!visited.contains(dep))
+					if (!visited.contains(dep)) {
 						sortUtilRev(dep, visited, stack);
+					}
 				}
 
 				stack.push(idx);
diff --git a/include/fggl/entity/loader/loader.hpp b/include/fggl/entity/loader/loader.hpp
index 5ae0442025821aafaf3f6874132f58188e72266f..5fbc316ee3fce3445378f9e1677cd289c933cae4 100644
--- a/include/fggl/entity/loader/loader.hpp
+++ b/include/fggl/entity/loader/loader.hpp
@@ -34,7 +34,7 @@ namespace fggl::entity {
 	constexpr auto ENTITY_PROTOTYPE = assets::make_asset_type("entity/prototype");
 	constexpr auto ENTITY_SCENE = assets::make_asset_type("entity/scene");
 
-	using FactoryFunc = std::function<void(const ComponentSpec &config, EntityManager &, const EntityID &, modules::Services &svc)>;
+	using FactoryFunc = std::function<void(const ComponentSpec &config, EntityManager &, const EntityID &, services::Generator *svc)>;
 	using CustomiseFunc = std::function<void(EntityManager &, const EntityID &)>;
 
 	struct FactoryInfo {
@@ -44,9 +44,9 @@ namespace fggl::entity {
 
 	class EntityFactory {
 		public:
-			constexpr static const modules::ServiceName service = modules::make_service("fggl::entity:Factory");
+			FGGL_DEFINE_SERVICE(fggl::entity::Factory)
 
-			inline EntityFactory(modules::Services &services) : m_services(services) {}
+			inline EntityFactory(services::Generator *services) : m_services(services) {}
 
 			EntityID create(const EntityType &spec, EntityManager &manager, const CustomiseFunc &customise = nullptr) {
 				std::vector<CustomiseFunc> finishers;
@@ -107,7 +107,7 @@ namespace fggl::entity {
 			}
 
 		private:
-			modules::Services m_services;
+			services::Generator *m_services;
 			std::map<ComponentID, FactoryInfo> m_factories;
 			std::map<EntityType, EntitySpec> m_prototypes;
 
diff --git a/include/fggl/entity/module.hpp b/include/fggl/entity/module.hpp
index e3cd5d541a196ebdc37d0dc18650a065b2d46b6d..9a15218d23855cab71a35e7072211a5b64abe8a0 100644
--- a/include/fggl/entity/module.hpp
+++ b/include/fggl/entity/module.hpp
@@ -20,7 +20,10 @@
 #define FGGL_ENTITY_MODULE_HPP
 
 #include "fggl/modules/module.hpp"
-#include "fggl/assets/loader.hpp"
+#include "fggl/services/module.hpp"
+
+#include "fggl/assets/module.hpp"
+#include "fggl/assets/packed/module.hpp"
 
 #include "fggl/assets/packed/adapter.hpp"
 #include "fggl/entity/loader/loader.hpp"
@@ -29,16 +32,20 @@ namespace fggl::entity {
 
 	constexpr auto MIME_SCENE = assets::from_mime("x-fggl/scene");
 
-	struct ECS {
-		constexpr static const char *name = "fggl::entity::ECS";
+	struct ECS : public services::Module {
+		FGGL_MODULE(fggl::entity::ECS, assets::PackedAssets, assets::AssetFolders)
+
 		constexpr static const std::array<modules::ServiceName, 1> provides = {
-			EntityFactory::service
+			FGGL_SERVICE(EntityFactory)
 		};
 		constexpr static const std::array<modules::ServiceName, 2> depends = {
-			assets::Loader::service,
-			assets::CheckinAdapted::service
+			FGGL_SERVICE(assets::Loader),
+			FGGL_SERVICE(assets::CheckinAdapted)
 		};
 		static bool factory(modules::ServiceName name, modules::Services &serviceManager);
+
+		void setup(services::Generator* generator) override;
+		void wireServices(services::ModuleBinder& binder) override;
 	};
 
 	void install_component_factories(EntityFactory *factory);
diff --git a/include/fggl/gfx/interfaces.hpp b/include/fggl/gfx/interfaces.hpp
index ee18edfcd493543f82e923e8a7b71b47e2203261..76f8fed867ab5bd6a6af288bd36e6a29cfc6c903 100644
--- a/include/fggl/gfx/interfaces.hpp
+++ b/include/fggl/gfx/interfaces.hpp
@@ -35,7 +35,7 @@ namespace fggl::gfx {
 
 	class Graphics {
 		public:
-			constexpr static const auto service = modules::make_service("fggl::gfx::Graphics");
+			FGGL_DEFINE_SERVICE(fggl::gfx::Graphics)
 
 			virtual ~Graphics() = default;
 			virtual void clear() = 0;
diff --git a/include/fggl/gfx/ogl/renderer.hpp b/include/fggl/gfx/ogl/renderer.hpp
index 20ec8e60eef14ab8ab625212161ec0401b67af4c..4ff76310c2f693c27ad545e81b8ede3b87b79c3e 100644
--- a/include/fggl/gfx/ogl/renderer.hpp
+++ b/include/fggl/gfx/ogl/renderer.hpp
@@ -41,7 +41,7 @@ namespace fggl::gfx {
 	 */
 	class OpenGL4Backend : public Graphics {
 		public:
-			explicit OpenGL4Backend(data::Storage *storage, gui::FontLibrary *fonts, assets::AssetManager *assets, GlFunctionLoader loader);
+			explicit OpenGL4Backend(gui::FontLibrary *fonts, assets::AssetManager *assets, ShaderCache* cache, GlFunctionLoader loader);
 			~OpenGL4Backend() override = default;
 
 			// copy bad
@@ -92,9 +92,9 @@ namespace fggl::gfx {
 			std::unique_ptr<ogl4::StaticModelRenderer> m_modelRenderer;
 			std::unique_ptr<ogl4::CanvasRenderer> m_canvasRenderer;
 			std::unique_ptr<ogl4::DebugRenderer> m_debugRenderer;
-			std::unique_ptr<ShaderCache> m_cache;
 			std::shared_ptr<ogl::Shader> m_canvasPipeline;
-			data::Storage *m_storage;
+			ShaderCache* m_cache;
+			//filesystem::Locator *m_storage;
 			gui::FontLibrary *m_fontLibrary;
 	};
 
diff --git a/include/fggl/gfx/ogl/shader.hpp b/include/fggl/gfx/ogl/shader.hpp
index 28d690f44a10dafa8d73b33d319a00f0eaabb7e5..77d503cd36a7e00c7486499cda3daae4814a60cf 100644
--- a/include/fggl/gfx/ogl/shader.hpp
+++ b/include/fggl/gfx/ogl/shader.hpp
@@ -20,6 +20,7 @@
 #include "fggl/gfx/ogl/types.hpp"
 
 #include <fggl/data/storage.hpp>
+#include "fggl/filesystem/reader.hpp"
 
 #include <filesystem>
 #include <stdexcept>
@@ -67,7 +68,7 @@ namespace fggl::gfx {
 		public:
 			using ShaderPtr = std::shared_ptr<ogl::Shader>;
 
-			ShaderCache(fggl::data::Storage *storage);
+			ShaderCache(filesystem::Locator *storage, filesystem::Reader *reader);
 			~ShaderCache() = default;
 
 			ShaderPtr load(const ShaderConfig &config);
@@ -86,7 +87,9 @@ namespace fggl::gfx {
 			void initFallbackPipelines();
 
 		private:
-			fggl::data::Storage* m_storage;
+			filesystem::Locator* m_storage;
+			filesystem::Reader* m_reader;
+
 			std::unordered_map<std::string, ShaderPtr> m_shaders;
 
 			// extensions
diff --git a/include/fggl/gfx/ogl4/module.hpp b/include/fggl/gfx/ogl4/module.hpp
index 8cdb6ad797775722d31dd4b5e1c08a7d6d9a38a7..37c20bc093c95b09b1dece0165756636473b90ef 100644
--- a/include/fggl/gfx/ogl4/module.hpp
+++ b/include/fggl/gfx/ogl4/module.hpp
@@ -25,8 +25,7 @@
 
 #include "fggl/assets/manager.hpp"
 #include "fggl/assets/packed/module.hpp"
-
-#include "fggl/entity/loader/loader.hpp"
+#include "fggl/entity/module.hpp"
 
 #include "fggl/gfx/interfaces.hpp"
 #include "fggl/gfx/setup.hpp"
@@ -36,20 +35,13 @@
 
 namespace fggl::gfx {
 
-	struct OpenGL4 {
-		constexpr static const char *name = "fggl::gfx::OpenGL4";
-		constexpr static const std::array<modules::ServiceName, 1> provides = {
-			WindowGraphics::service
-		};
-		constexpr static const std::array<modules::ServiceName, 5> depends = {
-			data::Storage::service,
-			assets::AssetManager::service,
-			assets::CheckinAdapted::service,
-			gui::FontLibrary::service,
-			entity::EntityFactory::service
-		};
-
-		static bool factory(modules::ServiceName name, modules::Services &serviceManager);
+	struct OpenGL4 : public services::Module {
+		FGGL_MODULE(fggl::gfx::OpenGL4, filesystem::API, assets::PackedAssets, entity::ECS)
+
+		Module::ServiceList getProvides() const override;
+
+		void setup(services::Generator*) override;
+		void wireServices(fggl::services::ModuleBinder &binder) override;
 	};
 
 } //namespace fggl::gfx
diff --git a/include/fggl/gfx/ogl4/setup.hpp b/include/fggl/gfx/ogl4/setup.hpp
index 781279cceb5517b4e31aa075b12dbff393c67a91..39b49b876d7ad6e2cdcc74a735c7c321fb38519e 100644
--- a/include/fggl/gfx/ogl4/setup.hpp
+++ b/include/fggl/gfx/ogl4/setup.hpp
@@ -29,7 +29,7 @@ namespace fggl::gfx::ogl4 {
 
 	class WindowGraphics : public gfx::WindowGraphics {
 		public:
-			WindowGraphics(data::Storage *storage, gui::FontLibrary *fonts, assets::AssetManager* assets) : m_storage(storage), m_fonts(fonts), m_assets(assets) {};
+			WindowGraphics(filesystem::Locator *storage, filesystem::Reader *reader, gui::FontLibrary *fonts, assets::AssetManager* assets) : m_storage(storage), m_reader(reader), m_fonts(fonts), m_assets(assets) {};
 			~WindowGraphics() override = default;
 
 			// no copy
@@ -52,7 +52,8 @@ namespace fggl::gfx::ogl4 {
 			}
 
 		private:
-			data::Storage *m_storage;
+			filesystem::Locator *m_storage;
+			filesystem::Reader *m_reader;
 			gui::FontLibrary *m_fonts;
 			assets::AssetManager *m_assets;
 	};
diff --git a/include/fggl/gfx/setup.hpp b/include/fggl/gfx/setup.hpp
index 804bc00e771221912403fc4d3f994551c5e84d34..288f7f0a1c6270b713d50e24afa7db8e5639b6b6 100644
--- a/include/fggl/gfx/setup.hpp
+++ b/include/fggl/gfx/setup.hpp
@@ -21,6 +21,7 @@
 
 #include "fggl/gfx/interfaces.hpp"
 #include "fggl/display/window.hpp"
+#include "fggl/services/module.hpp"
 
 namespace fggl::gfx {
 
@@ -35,9 +36,25 @@ namespace fggl::gfx {
 		bool debug;
 	};
 
+	/**
+	 * Graphics API Contract.
+	 *
+	 * This defines the lifetime of any suitable low-level graphics APIs used by the program. A module depending on this
+	 * contract may assume that its lifetime will be bounded by that of the graphics API (ie, its setup method will
+	 * only be called once the graphics API has been initialised and its teardown method will be called before the
+	 * graphics API is shut down).
+	 *
+	 * A module providing this contract must:
+	 *  * Provide services for rendering
+	 *  * Initialise required graphics APIs (eg, OpenGL) during its setup method
+	 *  * Clean up required graphics APIs (eg, OpenGL) its teardown method
+	 *
+	 */
+	FGGL_MODULE_CONTRACT(API, fggl::gfx::API)
+
 	class WindowGraphics {
 		public:
-			constexpr const static auto service = modules::make_service("fggl::gfx::WindowGraphics");
+			FGGL_DEFINE_SERVICE(fggl::gfx::WindowGraphics)
 
 			WindowGraphics() = default;
 			virtual ~WindowGraphics() = default;
diff --git a/include/fggl/gui/fonts.hpp b/include/fggl/gui/fonts.hpp
index 4983a0a8ac3398464c28135d0e5e5bc359349a6c..0ec7cbafcd61a6f4620b86b4435a820baab710e1 100644
--- a/include/fggl/gui/fonts.hpp
+++ b/include/fggl/gui/fonts.hpp
@@ -26,6 +26,7 @@
 #include "fggl/math/types.hpp"
 #include "fggl/data/storage.hpp"
 #include "fggl/modules/module.hpp"
+#include "fggl/filesystem/Finder.hpp"
 
 #include <ft2build.h>
 #include FT_FREETYPE_H
@@ -71,9 +72,9 @@ namespace fggl::gui {
 
 	class FontLibrary {
 		public:
-			constexpr static const auto service = modules::make_service("fggl::gui::font");
+			FGGL_DEFINE_SERVICE(fggl::gui::font)
 
-			explicit FontLibrary(data::Storage *storage);
+			explicit FontLibrary(filesystem::Locator *storage);
 			~FontLibrary();
 
 			// copy and moving not needed
@@ -89,10 +90,13 @@ namespace fggl::gui {
 				}
 
 				// need to load the font...
-				auto path = m_storage->resolvePath(data::StorageType::Data, name);
+				auto path = m_storage->find(name);
+				if ( !path.has_value() ){
+					return nullptr;
+				}
 
 				FT_Face face;
-				if (FT_New_Face(m_context, path.string().c_str(), 0, &face)) {
+				if (FT_New_Face(m_context, path->string().c_str(), 0, &face)) {
 					return nullptr;
 				}
 				FT_Set_Pixel_Sizes(face, 0, 18);
@@ -112,7 +116,7 @@ namespace fggl::gui {
 			}
 		private:
 			FT_Library m_context;
-			data::Storage *m_storage;
+			filesystem::Locator *m_storage;
 			std::map<const std::string, std::shared_ptr<FontFace>> m_cache;
 			std::shared_ptr<FontFace> m_defaultFont;
 	};
diff --git a/include/fggl/gui/model/parser.hpp b/include/fggl/gui/model/parser.hpp
index c87ede4166418d5aca1ec3e79c64d309828741cc..ad4d3a0d8e1cb80a1934a3f64fdcbc59e3acd2f8 100644
--- a/include/fggl/gui/model/parser.hpp
+++ b/include/fggl/gui/model/parser.hpp
@@ -25,11 +25,11 @@
 
 namespace fggl::gui::model {
 
-	constexpr auto WIDGET_FACTORY_SERVICE = modules::make_service("gui::WidgetService");
-
 	class WidgetFactory {
 		public:
-			constexpr static auto service = WIDGET_FACTORY_SERVICE;
+			FGGL_DEFINE_SERVICE(fggl::gui::WidgetService)
+			FGGL_IMPLEMENT_SERVICE(WidgetFactory, WidgetFactory, FontLibrary)
+
 			inline WidgetFactory(FontLibrary* lib) : m_fontLibrary(lib) {}
 
 			inline Widget* build(std::string templateName) {
diff --git a/include/fggl/gui/module.hpp b/include/fggl/gui/module.hpp
index 14723b4867715dd5a0685551a1b74449252e33af..ad2b3634752c444c4dd8c15beb0cc7b4f71af8d9 100644
--- a/include/fggl/gui/module.hpp
+++ b/include/fggl/gui/module.hpp
@@ -37,37 +37,18 @@ namespace fggl::gui {
 		return assets::INVALID_ASSET_TYPE;
 	}
 
-	struct FreeType {
-		constexpr static const char *name = "fggl::gui::FreeType";
-		constexpr static const std::array<modules::ServiceName, 2> provides = {
-			FontLibrary::service,
-			model::WidgetFactory::service
-		};
-		constexpr static const std::array<modules::ServiceName, 2> depends = {
-			data::Storage::service,
-			assets::CheckinAdapted::service
-		};
-		static bool factory(modules::ServiceName name, modules::Services &serviceManager);
+	struct FreeType : services::Module {
+		FGGL_MODULE( fggl::gui::FreeType, fggl::filesystem::API, fggl::assets::PackedAssets );
+		void wireServices(services::ModuleBinder& binder) override;
 	};
 
-	bool FreeType::factory(modules::ServiceName service, modules::Services &services) {
-		if (service == FontLibrary::service) {
-			auto storage = services.get<data::Storage>();
-			services.create<FontLibrary>(storage);
+	void FreeType::wireServices(services::ModuleBinder &binder) {
+		binder.bind<FontLibrary>().depends<filesystem::Locator>();
+		binder.bind<model::WidgetFactory>().depends<FontLibrary>();
 
-			auto* checkin = services.get<assets::CheckinAdapted>();
-			checkin->setLoader( MIME_TTF, assets::NEEDS_CHECKIN, is_font );
-
-			return true;
-		}
-
-		if ( service == model::WidgetFactory::service ) {
-			auto fonts = services.get<FontLibrary>();
-			services.create<model::WidgetFactory>(fonts);
-			return true;
-		}
-
-		return false;
+		// TODO
+		//auto* checkin = services.get<assets::CheckinAdapted>();
+		//checkin->setLoader( MIME_TTF, assets::NEEDS_CHECKIN, is_font );
 	}
 }
 
diff --git a/include/fggl/input/input.hpp b/include/fggl/input/input.hpp
index dbdd0a27b37d5e55d4d76e94e29adf7ea7344991..d9cad0ee550de501a7f0ebed8b8a8745214018ce 100644
--- a/include/fggl/input/input.hpp
+++ b/include/fggl/input/input.hpp
@@ -24,11 +24,10 @@
 
 namespace fggl::input {
 
-	constexpr const auto SERVICE_INPUT = modules::make_service("fggl::input::Input");
-
 	class Input {
 		public:
-			constexpr static const auto service = SERVICE_INPUT;
+			FGGL_DEFINE_SERVICE(fggl::input::Input)
+			FGGL_IMPLEMENT_SERVICE(fggl::input::Input)
 
 			Input() = default;
 			void frame(float dt);
diff --git a/include/fggl/input/module.hpp b/include/fggl/input/module.hpp
index b550ab016b12a5a90ef5016c9a0027fc0bbe0d36..705defe5cb87c290512015801e548388b5feb627 100644
--- a/include/fggl/input/module.hpp
+++ b/include/fggl/input/module.hpp
@@ -21,26 +21,16 @@
 
 #include <array>
 #include "fggl/modules/module.hpp"
+#include "fggl/services/module.hpp"
 #include "fggl/input/input.hpp"
 
 namespace fggl::input {
 
-	struct Generic {
-		constexpr static const char *name = "fggl::input::Generic";
-		constexpr static const std::array<modules::ServiceName, 1> provides = {
-			SERVICE_INPUT
-		};
-		constexpr static const std::array<modules::ServiceName, 0> depends = {};
-		static bool factory(modules::ServiceName service, modules::Services &services);
+	struct Generic : public services::Module {
+		FGGL_MODULE(fggl::input::Generic)
+		virtual void wireServices(services::ModuleBinder& binder) override;
 	};
 
-	bool Generic::factory(modules::ServiceName service, modules::Services &services) {
-		if (service == SERVICE_INPUT) {
-			services.create<input::Input>();
-			return true;
-		}
-		return false;
-	}
 }
 
 #endif //FGGL_INPUT_MODULE_HPP
diff --git a/include/fggl/modules/manager.hpp b/include/fggl/modules/manager.hpp
index b29490ef55160ec5e164394744abaca3fef77045..346d0c226c045f498197a5cbf30259cd58c20ac8 100644
--- a/include/fggl/modules/manager.hpp
+++ b/include/fggl/modules/manager.hpp
@@ -23,6 +23,11 @@
 #include "fggl/debug/logging.hpp"
 #include "fggl/ds/graph.hpp"
 
+#include "fggl/services/service.hpp"
+#include "fggl/services/factory.hpp"
+#include "fggl/services/module.hpp"
+
+#include <format>
 #include <cassert>
 #include <queue>
 #include <vector>
@@ -44,134 +49,52 @@ namespace fggl::modules {
 		public:
 			Manager() = default;
 
-			template<ServiceType T>
-			class Service {
-				public:
-					inline Service(Manager* manager) : m_manager(manager) {}
-
-					inline T* operator->() {
-						if ( m_ptr == nullptr ) {
-							m_ptr = m_manager->get<T>();
-						}
-						return m_ptr;
-					}
-
-				private:
-					Manager* m_manager;
-					std::shared_ptr<T> m_ptr;
-			};
-
-			inline void addVirtual(const Config &config) {
-				assert(!m_locked);
-
-				m_modules[config.name] = config;
-				for (const auto &service : config.provides) {
-					m_serviceProviders[service] = config.name;
-				}
-			}
+			Manager(Manager&) = delete;
+			void operator=(Manager&) = delete;
 
-			template<ModuleType T>
+			template<services::Subclass<services::Module> T>
 			void use() {
+				static_assert( !std::is_abstract_v<T>, "Module provided is abstract, check you have implemented all required methods." );
 				assert(!m_locked);
-
-				Config config{.name = T::name, .provides = {}, .depends = {}};
-				for (auto service : T::provides) {
-					config.provides.push_back(service);
-				}
-
-				for (auto service : T::depends) {
-					config.depends.push_back(service);
-				}
-				config.factory = T::factory;
-
-				addVirtual(config);
+				auto modulePtr = std::make_shared<T>();
+				depend(modulePtr);
 			}
 
-            // FIXME this should be in cpp file
-			bool buildGraph() {
-				// resolve links between modules
-				for (auto &moduleItr : m_modules) {
-					if (moduleItr.second.depends.empty()) {
-						m_dependencies.addVertex(moduleItr.first);
-						continue;
-					}
-
-					for (auto &service : moduleItr.second.depends) {
-
-						auto provider = m_serviceProviders.find(service);
-						if (provider == m_serviceProviders.end()) {
-							debug::log(debug::Level::warning,
-									   "{} depends on {}, but nothing provides it",
-									   moduleItr.first,
-									   service.get());
-							// nothing can provide the service requested, setup is invalid.
-							return false;
-						}
-
-						m_dependencies.addEdge(moduleItr.first, provider->second);
-					}
+			inline void depend(const std::shared_ptr<services::Module>& modulePtr) {
+				assert(!m_locked);
+				m_modules[modulePtr->getName()] = modulePtr;
+				for ( auto& provided : modulePtr->getProvides() ) {
+					m_serviceProviders[provided] = modulePtr->getName();
 				}
-				return true;
 			}
 
-			template<ServiceType T>
-			T *get() const {
+			template<services::ServiceAPI T>
+			auto get() -> T * {
 				assert(m_locked);
-				return m_services.template get<T>();
+				return m_services.template getLazy<T>().ptr();
 			}
 
-			template<ServiceType T>
-			Service<T> getLazy() const {
+			template<services::ServiceAPI T>
+			auto get() const -> std::optional<T> * {
 				assert(m_locked);
-				return { this };
+				return m_services.template getFromCache<T>();
 			}
 
-            //FIXME this should be in cpp file!
-			void resolve() {
-				assert( !m_locked );
-				if (!buildGraph()) {
-					return;
-				}
-
-				std::queue<ModuleIdentifier> stack;
-				m_dependencies.getOrderRev(stack);
-
-				while (!stack.empty()) {
-					auto nextToInit = stack.front();
-					debug::log(debug::Level::info, "Initializing {}", nextToInit);
-
-					auto &module = m_modules.at(nextToInit);
-					if (module.factory != nullptr) {
-						for (auto &service : module.provides) {
-							bool result = module.factory(service, m_services);
-							if (!result) {
-								debug::log(debug::Level::warning,
-										   "{} could not create service {}",
-										   nextToInit,
-										   service.get());
-							}
-						}
-					} else {
-						debug::log(debug::Level::warning, "{} has no factory - skipping", nextToInit);
-					}
-					stack.pop();
-				}
+			auto resolve() -> bool;
 
-				debug::log(debug::Level::info, "engine boot complete");
-				m_locked = true;
-			}
-
-			inline Services& services() {
-				return m_services;
+			inline auto* services() {
+				return &m_services;
 			}
 
 		private:
 			bool m_locked = false;
-			Services m_services;
-			std::map<ModuleIdentifier, Config> m_modules;
+			services::Generator m_services;
+			std::map<services::ModuleID, std::shared_ptr<services::Module> > m_modules;
+
 			ds::DirectedGraph<ModuleIdentifier> m_dependencies;
-			std::map<ServiceName, ModuleIdentifier> m_serviceProviders;
+			std::map<services::ModuleID, services::ModuleID> m_serviceProviders;
 
+			bool buildGraph();
 	};
 
 } // namespace fggl::modules
diff --git a/include/fggl/modules/module.hpp b/include/fggl/modules/module.hpp
index 4d0944ef96ac47cd3247a862740b903eed03716e..0f602adc2ecd04775567d388a99ea2fc5a0f6422 100644
--- a/include/fggl/modules/module.hpp
+++ b/include/fggl/modules/module.hpp
@@ -27,16 +27,19 @@
 
 #include "fggl/util/safety.hpp"
 #include "service.hpp"
+#include "fggl/services/module.hpp"
 
 namespace fggl::modules {
 
 	template<typename T>
 	concept ModuleType = requires(T type) {
+		{ T::services };
 		{ T::provides };
 		{ T::depends };
 	};
 
-	using ModuleIdentifier = std::string;
+	using ModuleIdentifier = services::ModuleID;
+	using ServiceName = services::ServiceName;
 
 	using ServiceFactory = std::function<bool(ServiceName , Services &)>;
 	struct Config {
diff --git a/include/fggl/modules/service.hpp b/include/fggl/modules/service.hpp
index 75a42606de64c5b0e350a8c362ada2898e8b2659..31720a016dbc700404cf1a742b5b73d404df7ac1 100644
--- a/include/fggl/modules/service.hpp
+++ b/include/fggl/modules/service.hpp
@@ -19,58 +19,13 @@
 #ifndef FGGL_MODULES_SERVICE_HPP
 #define FGGL_MODULES_SERVICE_HPP
 
-#include <map>
-#include <memory>
-
-#include "fggl/util/safety.hpp"
+#include "fggl/services/service.hpp"
+#include "fggl/services/factory.hpp"
 
 namespace fggl::modules {
 
-	using ServiceName = util::OpaqueName<std::string_view, struct ModuleServiceTag>;
-	constexpr ServiceName make_service(const std::string_view name) {
-		return ServiceName::make(name);
-	}
-
-	template<typename T>
-	concept ServiceType = requires(T* type) {
-		{ T::service };
-	};
-
-	template<typename T, typename U>
-	concept Derived = std::is_base_of<U, T>::value;
-
-	class Services {
-		public:
-			template<ServiceType Svc, Derived<Svc> Impl, typename ...Args>
-			void bind(Args... args) {
-				m_services[Svc::service] = std::make_shared<Impl>(args...);
-			}
-
-			template<ServiceType Svc, typename ...Args>
-			Svc *create(Args... args) {
-				auto svc = std::make_shared<Svc>(args...);
-				m_services[Svc::service] = svc;
-				return svc.get();
-			}
-
-			template<ServiceType Svc>
-			void provide(std::shared_ptr<Svc> service) {
-				m_services[Svc::service] = service;
-			}
-
-			template<ServiceType S>
-			S *get() const {
-				auto serviceWrapper = m_services.at(S::service);
-				auto ptr = std::static_pointer_cast<S>(serviceWrapper);
-				return ptr.get();
-			}
-
-		private:
-			std::map<ServiceName, std::shared_ptr<void>> m_services;
-	};
+	using Services = services::Generator;
 
 } // namespace fggl::modules
 
-
-
 #endif //FGGL_MODULES_SERVICE_HPP
diff --git a/include/fggl/phys/null.hpp b/include/fggl/phys/null.hpp
index d39324dea52b3a203d3a88070d0c5fef316c96ac..6d670cb8cf0afcb96fa6cd683373e2aa2355f7c8 100644
--- a/include/fggl/phys/null.hpp
+++ b/include/fggl/phys/null.hpp
@@ -27,7 +27,7 @@ namespace fggl::phys {
 	inline void build_noop(const entity::ComponentSpec & /*config*/,
 						   entity::EntityManager &manager,
 						   const entity::EntityID &entity,
-						   modules::Services &/*services*/) {
+						   services::Generator */*services*/) {
 		manager.add<fggl::phys::RigidBody>(entity);
 		manager.add<fggl::phys::Dynamics>(entity);
 	}
@@ -71,10 +71,10 @@ namespace fggl::phys {
 	struct NullPhysics {
 		constexpr static const char *name = "fggl::phys::null";
 		constexpr static const std::array<modules::ServiceName, 1> provides = {
-			phys::PhysicsProvider::service
+			FGGL_SERVICE(phys::PhysicsProvider)
 		};
 		constexpr static const std::array<modules::ServiceName, 1> depends = {
-			entity::EntityFactory::service
+			FGGL_SERVICE(entity::EntityFactory)
 		};
 		static bool factory(modules::ServiceName serviceName, modules::Services &serviceManager);
 	};
diff --git a/include/fggl/phys/service.hpp b/include/fggl/phys/service.hpp
index c637ca2f19aaa44df12c00234a9be0b3834cc5a6..541a55bf7f0ec3dbb76722049410a7731b50c477 100644
--- a/include/fggl/phys/service.hpp
+++ b/include/fggl/phys/service.hpp
@@ -27,7 +27,7 @@ namespace fggl::phys {
 
 	class PhysicsProvider {
 		public:
-			constexpr static const auto service = modules::make_service("fggl::phys::service");
+			FGGL_DEFINE_SERVICE(fggl::phys::PhysicsProvider)
 			virtual ~PhysicsProvider() = default;
 			virtual PhysicsEngine *create(entity::EntityManager *entityManager, entity::EntityFactory *factory) = 0;
 	};
diff --git a/include/fggl/script/engine.hpp b/include/fggl/script/engine.hpp
index 07ae3b37cb6c3e70affae0a5e23ff919c78d2110..3567349abc1a7ad4439dbca9ff782acad2e1c097 100644
--- a/include/fggl/script/engine.hpp
+++ b/include/fggl/script/engine.hpp
@@ -46,7 +46,7 @@ namespace fggl::script {
 
 	class ScriptProvider {
 		public:
-			constexpr static const modules::ServiceName service = modules::make_service("fggl::script::service");
+			FGGL_DEFINE_SERVICE(fggl::script::service)
 			virtual ScriptEngine* create() = 0;
 	};
 
diff --git a/integrations/lua/include/fggl/script/lua/engine.hpp b/integrations/lua/include/fggl/script/lua/engine.hpp
index 1c8c85f3bf5b742e2ae075d771e5288a48e5a8a8..055fa7013ee9e9ab23449c19e7fa649c80ddc16f 100644
--- a/integrations/lua/include/fggl/script/lua/engine.hpp
+++ b/integrations/lua/include/fggl/script/lua/engine.hpp
@@ -20,7 +20,7 @@
 #define FGGL_INTEGRATIONS_LUA_SCRIPT_LUA_ENGINE_HPP
 
 #include "fggl/script/engine.hpp"
-#include "fggl/data/storage.hpp"
+#include "fggl/filesystem/Finder.hpp"
 
 extern "C" {
 	#include "lua.h"
@@ -32,7 +32,7 @@ namespace fggl::script::lua {
 
 	class LuaScriptEngine : public ScriptEngine {
 		public:
-			LuaScriptEngine(data::Storage* storage);
+			LuaScriptEngine(filesystem::Locator* locator );
 			virtual ~LuaScriptEngine();
 
 			void onActivate() override;
@@ -49,20 +49,20 @@ namespace fggl::script::lua {
 			void setGlobal(const char* name, void* ptr) override;
 
 		private:
-			data::Storage* m_storage;
+			filesystem::Locator* m_storage;
 			lua_State* m_state;
 			void release();
 	};
 
 	class LuaScriptProvider : public ScriptProvider {
 		public:
-			LuaScriptProvider(data::Storage* storage);
+			LuaScriptProvider(filesystem::Locator* storage);
 
 			virtual ~LuaScriptProvider() = default;
 			LuaScriptEngine* create() override;
 
 		private:
-			data::Storage* m_storage;
+			filesystem::Locator* m_storage;
 	};
 
 }
diff --git a/integrations/lua/include/fggl/script/lua/module.hpp b/integrations/lua/include/fggl/script/lua/module.hpp
index 6eda012719cf05b8ef4d7b3aa9ff316d94931bf0..0137152584010d1f96925a755dab98e2e6c85d9b 100644
--- a/integrations/lua/include/fggl/script/lua/module.hpp
+++ b/integrations/lua/include/fggl/script/lua/module.hpp
@@ -19,30 +19,25 @@
 #ifndef FGGL_INTEGRATIONS_LUA_SCRIPT_LUA_MODULE_HPP
 #define FGGL_INTEGRATIONS_LUA_SCRIPT_LUA_MODULE_HPP
 
-#define FGGL_HAS_LUA
+	#define FGGL_HAS_LUA
 
-#include "fggl/modules/module.hpp"
-#include "fggl/entity/module.hpp"
-#include "fggl/script/engine.hpp"
-#include "fggl/data/module.hpp"
-#include "fggl/assets/packed/adapter.hpp"
+	#include "fggl/modules/module.hpp"
+	#include "fggl/services/module.hpp"
 
+	#include "fggl/entity/module.hpp"
+	#include "fggl/script/engine.hpp"
+	#include "fggl/data/module.hpp"
+	#include "fggl/assets/packed/adapter.hpp"
 
 namespace fggl::script::lua {
 
 	constexpr auto MIME_LUA = assets::from_mime("text/lua");
 	constexpr auto SCRIPT_LUA = assets::make_asset_type("script/lua");
 
-	struct Lua {
-		constexpr static const char* name = "fggl::script::lua";
-		constexpr static const std::array<modules::ServiceName, 1> provides = {
-			script::ScriptProvider::service
-		};
-		constexpr static const std::array<modules::ServiceName, 2> depends = {
-			data::SERVICE_STORAGE,
-			assets::CheckinAdapted::service
-		};
-		static bool factory(modules::ServiceName name, modules::Services& serviceManager);
+	struct Lua : public services::Module {
+		FGGL_MODULE(fggl::script::Lua, fggl::filesystem::API)
+
+		void wireServices(services::ModuleBinder& binder) override;
 	};
 
 } // namespace fggl::script::lua
diff --git a/integrations/lua/src/engine.cpp b/integrations/lua/src/engine.cpp
index cd5d98deb4da7872b08b86b7162d9cb3c1c568cd..54995155f2c29eaa2a14d715c389d05e607b1a52 100644
--- a/integrations/lua/src/engine.cpp
+++ b/integrations/lua/src/engine.cpp
@@ -51,7 +51,7 @@ extern "C" {
 
 namespace fggl::script::lua {
 
-	LuaScriptProvider::LuaScriptProvider(data::Storage *storage) : m_storage(storage) {
+	LuaScriptProvider::LuaScriptProvider(filesystem::Locator *storage) : m_storage(storage) {
 
 	}
 
@@ -59,7 +59,7 @@ namespace fggl::script::lua {
 		return new LuaScriptEngine(m_storage);
 	}
 
-	LuaScriptEngine::LuaScriptEngine(data::Storage* storage) : m_state(luaL_newstate()), m_storage(storage) {
+	LuaScriptEngine::LuaScriptEngine(filesystem::Locator* storage) : m_state(luaL_newstate()), m_storage(storage) {
 		luaL_openlibs(m_state);
 		open_lua_fggl(m_state);
 	}
@@ -109,15 +109,15 @@ namespace fggl::script::lua {
 
 	bool LuaScriptEngine::load(const char *filename) {
 		assert( filename != nullptr);
-		auto path = m_storage->resolvePath(data::StorageType::Data, filename);
 
-		if ( !std::filesystem::exists(path) ) {
-			fggl::debug::warning("lua error: file does not exist: {}", path.c_str());
+		auto path = m_storage->find(filename);
+		if ( !path.has_value() ) {
+			fggl::debug::warning("lua error: file does not exist: {}", filename);
 			return false;
 		}
 
 		// load the file ( OK = 0 = false because reasons...)
-		auto result = !luaL_dofile(m_state, path.c_str());
+		auto result = !luaL_dofile(m_state, path->c_str());
 		if ( !result ) {
 			fggl::debug::warning("lua error: {}", lua_tostring(m_state, -1));
 			return false;
diff --git a/integrations/lua/src/module.cpp b/integrations/lua/src/module.cpp
index bfe91a2e96d12a4bff7daeeb8692c0f7bcab7429..dd1ba35e240f687d996e46e6ddd1187ab43fab2c 100644
--- a/integrations/lua/src/module.cpp
+++ b/integrations/lua/src/module.cpp
@@ -28,19 +28,12 @@ namespace fggl::script::lua {
 		return assets::INVALID_ASSET_TYPE;
 	}
 
-	bool Lua::factory(modules::ServiceName service, modules::Services &serviceManager) {
+	void Lua::wireServices(services::ModuleBinder &binder) {
+		binder.bind<ScriptProvider>().to<LuaScriptProvider, fggl::filesystem::Locator>();
 
-		if ( service == ScriptProvider::service ) {
-			auto *storageService = serviceManager.get<data::Storage>();
-			serviceManager.bind<ScriptProvider,LuaScriptProvider>(storageService);
-
-			auto *assetPacker = serviceManager.get<assets::CheckinAdapted>();
-			assetPacker->setLoader(MIME_LUA, assets::NEEDS_CHECKIN, is_lua);
-
-			return true;
-		}
-
-		return false;
+		//TODO
+		//auto *assetPacker = serviceManager.get<assets::CheckinAdapted>();
+		//assetPacker->setLoader(MIME_LUA, assets::NEEDS_CHECKIN, is_lua);
 	}
 
 }
\ No newline at end of file