diff --git a/components/structures/include/ftl/data/new_frame.hpp b/components/structures/include/ftl/data/new_frame.hpp
index 48c18ed8b6c4c672ff994f3ad4d4714b31c42a06..963e91b5fdf39f097ce3de20631671cf7a47d859 100644
--- a/components/structures/include/ftl/data/new_frame.hpp
+++ b/components/structures/include/ftl/data/new_frame.hpp
@@ -13,6 +13,32 @@
 namespace ftl {
 namespace data {
 
+class Session;
+
+/** Kind of channel in terms of data persistence */
+enum class ChannelMode {
+	PERSISTENT,		// Most recent value, even from previous fram
+	IMMEDIATE,		// Only most recent value since last frame
+	SEQUENCE		// All changes since last frame
+};
+
+struct ChannelConfig {
+	std::string name;
+	ChannelMode mode;
+	size_t type_id;
+};
+
+template <typename T>
+ChannelConfig make_channel(const std::string &name, ChannelMode mode) {
+	// TODO: Generate packer + unpacker?
+	return {name, mode, typeid(T).hash_code()};
+}
+
+//template <>
+//ChannelConfig make_channel<void>(const std::string &name, ChannelMode mode) {
+//	return {name, mode, 0};
+//}
+
 class Frame {
 	public:
 	uint32_t id=0;
@@ -20,7 +46,7 @@ class Frame {
 
 	public:
 	Frame() : parent_(nullptr) {};
-	explicit Frame(Frame *parent) : parent_(parent) {};
+	explicit Frame(Session *parent) : parent_(parent) {};
 	~Frame() { flush(); };
 
 	Frame(Frame &&f) {
@@ -38,13 +64,9 @@ class Frame {
 	Frame(const Frame &)=delete;
 	Frame &operator=(const Frame &)=delete;
 
-	inline bool has(ftl::codecs::Channel c) {
-		return data_.find(c) != data_.end() || (parent_ && parent_->has(c));
-	}
+	inline bool has(ftl::codecs::Channel c);
 
-	inline bool changed(ftl::codecs::Channel c) {
-		return changed_.find(c) != changed_.end();
-	}
+	inline bool changed(ftl::codecs::Channel c);
 
 	inline const std::unordered_set<ftl::codecs::Channel> &changed() const { return changed_; }
 
@@ -83,14 +105,9 @@ class Frame {
 	template <typename T>
 	void set(ftl::codecs::Channel c, const T &v);
 
-	inline void on(ftl::codecs::Channel c, const std::function<bool(Frame&,ftl::codecs::Channel)> &cb) {
-		if (parent_) parent_->on(c, cb);
-		else triggers_[c].push_back(cb);  // TODO: Find better way to enable removal
-	}
+	inline void on(ftl::codecs::Channel c, const std::function<bool(Frame&,ftl::codecs::Channel)> &cb);
 
-	inline void on(const std::function<bool(Frame&,ftl::codecs::Channel)> &cb) {
-		any_triggers_.push_back(cb);
-	}
+	inline void on(const std::function<bool(Frame&,ftl::codecs::Channel)> &cb);
 
 	void merge(Frame &);
 
@@ -114,19 +131,46 @@ class Frame {
 	// onEndFlush
 	// onError
 
+	static void registerChannel(ftl::codecs::Channel, const ChannelConfig &config);
+	static void clearRegistry();
+
+	static bool isPersistent(ftl::codecs::Channel);
+	static size_t getChannelType(ftl::codecs::Channel);
+	static std::string getChannelName(ftl::codecs::Channel);
+	static ftl::codecs::Channel getChannelByName(const std::string &name);
+
 	private:
 	std::map<ftl::codecs::Channel, std::any> data_;
 	std::unordered_set<ftl::codecs::Channel> changed_;
 	std::unordered_map<ftl::codecs::Channel, std::list<std::function<bool(Frame&,ftl::codecs::Channel)>>> triggers_;
 	std::list<std::function<bool(Frame&,ftl::codecs::Channel)>> any_triggers_;
-	Frame *parent_;
+	Session *parent_;
 };
 
+class Session : public Frame {};
+
 }
 }
 
 // ==== Implementations ========================================================
 
+bool ftl::data::Frame::has(ftl::codecs::Channel c) {
+	return data_.find(c) != data_.end() || (parent_ && parent_->has(c));
+}
+
+bool ftl::data::Frame::changed(ftl::codecs::Channel c) {
+	return changed_.find(c) != changed_.end();
+}
+
+void ftl::data::Frame::on(ftl::codecs::Channel c, const std::function<bool(Frame&,ftl::codecs::Channel)> &cb) {
+	if (parent_) parent_->on(c, cb);
+	else triggers_[c].push_back(cb);  // TODO: Find better way to enable removal
+}
+
+void ftl::data::Frame::on(const std::function<bool(Frame&,ftl::codecs::Channel)> &cb) {
+	any_triggers_.push_back(cb);
+}
+
 template <typename T>
 bool ftl::data::Frame::isType(ftl::codecs::Channel c) {
 	auto i = data_.find(c);
@@ -160,6 +204,8 @@ const T &ftl::data::Frame::get(ftl::codecs::Channel c) const {
 template <typename T>
 T &ftl::data::Frame::create(ftl::codecs::Channel c, const T &value) {
 	touch(c);
+	size_t t = getChannelType(c);
+	if (t > 0 && t != typeid(T).hash_code()) throw FTL_Error("Incorrect type for channel " << static_cast<unsigned int>(c));
 	auto &d = data_[c];
 	d = value;
 	return *std::any_cast<T>(&d);
@@ -168,6 +214,8 @@ T &ftl::data::Frame::create(ftl::codecs::Channel c, const T &value) {
 template <typename T>
 T &ftl::data::Frame::create(ftl::codecs::Channel c) {
 	touch(c);
+	size_t t = getChannelType(c);
+	if (t > 0 && t != typeid(T).hash_code()) throw FTL_Error("Incorrect type for channel " << static_cast<unsigned int>(c));
 	if (!isType<T>(c)) return data_[c].emplace<T>();
 	else return *std::any_cast<T>(&data_[c]);
 }
diff --git a/components/structures/src/new_frame.cpp b/components/structures/src/new_frame.cpp
index 9d030ed8c259549d14b43b23d73db472b2612a74..36a8b0c64e5f9a46bc2dc42afbcccedae27ecf04 100644
--- a/components/structures/src/new_frame.cpp
+++ b/components/structures/src/new_frame.cpp
@@ -1,10 +1,47 @@
 #include <ftl/data/new_frame.hpp>
 
 using ftl::data::Frame;
+using ftl::data::ChannelConfig;
+using ftl::data::ChannelMode;
 
 #define LOGURU_REPLACE_GLOG 1
 #include <loguru.hpp>
 
+static std::unordered_map<ftl::codecs::Channel, ChannelConfig> reg_channels;
+
+void Frame::registerChannel(ftl::codecs::Channel c, const ChannelConfig &config) {
+	auto i = reg_channels.find(c);
+	if (i != reg_channels.end()) {
+		throw FTL_Error("Channel " << static_cast<unsigned int>(c) << " already registered");
+	}
+
+	reg_channels[c] = config;
+}
+
+void Frame::clearRegistry() {
+	reg_channels.clear();
+}
+
+bool Frame::isPersistent(ftl::codecs::Channel c) {
+	auto i = reg_channels.find(c);
+	return (i != reg_channels.end()) ? i->second.mode == ChannelMode::PERSISTENT : true;
+}
+
+size_t Frame::getChannelType(ftl::codecs::Channel c) {
+	auto i = reg_channels.find(c);
+	return (i != reg_channels.end()) ? i->second.type_id : 0;
+}
+
+std::string Frame::getChannelName(ftl::codecs::Channel c) {
+	auto i = reg_channels.find(c);
+	return (i != reg_channels.end()) ? i->second.name : "";
+}
+
+ftl::codecs::Channel Frame::getChannelByName(const std::string &name) {
+	return ftl::codecs::Channel::Colour;
+}
+
+
 bool Frame::flush() {
 	if (parent_) {
 		for (auto c : changed_) {
diff --git a/components/structures/test/frame_unit.cpp b/components/structures/test/frame_unit.cpp
index da8ffca5905337bc68c2716c9f4cb1f37112ebfa..7cf289868ff22f1053997da25ee22fb3695583ba 100644
--- a/components/structures/test/frame_unit.cpp
+++ b/components/structures/test/frame_unit.cpp
@@ -2,7 +2,7 @@
 
 #include <ftl/data/new_frame.hpp>
 
-
+using ftl::data::Session;
 using ftl::data::Frame;
 using ftl::codecs::Channel;
 
@@ -133,7 +133,7 @@ TEST_CASE("ftl::data::Frame create", "[Frame]") {
 
 TEST_CASE("ftl::data::Frame use of parent", "[Frame]") {
 	SECTION("get from parent") {
-		Frame p;
+		Session p;
 		Frame f(&p);
 
 		p.create<int>(Channel::Pose, 55);
@@ -147,7 +147,7 @@ TEST_CASE("ftl::data::Frame use of parent", "[Frame]") {
 	}
 
 	SECTION("has from parent") {
-		Frame p;
+		Session p;
 		Frame f(&p);
 
 		p.create<int>(Channel::Pose, 55);
@@ -155,7 +155,7 @@ TEST_CASE("ftl::data::Frame use of parent", "[Frame]") {
 	}
 
 	SECTION("no change in parent") {
-		Frame p;
+		Session p;
 		Frame f(&p);
 
 		p.create<int>(Channel::Pose, 55);
@@ -197,7 +197,7 @@ TEST_CASE("ftl::data::Frame flush", "[Frame]") {
 	}
 
 	SECTION("parent event on flush") {
-		Frame p;
+		Session p;
 		Frame f(&p);
 
 		int event = 0;
@@ -214,7 +214,7 @@ TEST_CASE("ftl::data::Frame flush", "[Frame]") {
 	}
 
 	SECTION("parent change on flush") {
-		Frame p;
+		Session p;
 		Frame f(&p);
 
 		p.create<int>(Channel::Pose, 55);
@@ -242,6 +242,33 @@ TEST_CASE("ftl::data::Frame flush", "[Frame]") {
 	}
 }
 
+TEST_CASE("ftl::data::Frame register", "[Frame]") {
+	SECTION("register typed channel and valid create") {
+		Frame f;
+
+		Frame::registerChannel(Channel::Colour, ftl::data::make_channel<float>("colour", ftl::data::ChannelMode::PERSISTENT));
+		REQUIRE( f.create<float>(Channel::Colour, 5.0f) == 5.0f );
+
+		Frame::clearRegistry();
+	}
+
+	SECTION("register typed channel and invalid create") {
+		Frame f;
+
+		Frame::registerChannel(Channel::Colour, ftl::data::make_channel<float>("colour", ftl::data::ChannelMode::PERSISTENT));
+
+		bool err = false;
+		try {
+			f.create<int>(Channel::Colour, 5);
+		} catch(const std::exception &e) {
+			err = true;
+		}
+		REQUIRE( err );
+
+		Frame::clearRegistry();
+	}
+}
+
 // ==== Complex type overload test =============================================
 
 struct TestA {