diff --git a/applications/gui/src/config_window.cpp b/applications/gui/src/config_window.cpp
index fcba8ff7ad2e033ab790790b03aed4a3af25af15..d75772f09e84e391e8d6eedd342fe819c1349c68 100644
--- a/applications/gui/src/config_window.cpp
+++ b/applications/gui/src/config_window.cpp
@@ -15,23 +15,15 @@ using std::string;
 using std::vector;
 using ftl::config::json_t;
 
-ConfigWindow::ConfigWindow(nanogui::Widget *parent, ftl::ctrl::Master *ctrl, const ftl::UUID &peer) : ConfigWindow(parent, ctrl, std::optional<ftl::UUID>(peer)) {
-
-}
-
-ConfigWindow::ConfigWindow(nanogui::Widget *parent, ftl::ctrl::Master *ctrl, const std::optional<ftl::UUID> &peer)
-		: nanogui::Window(parent, "Settings"), ctrl_(ctrl), peer_(peer) {
+ConfigWindow::ConfigWindow(nanogui::Widget *parent, ftl::ctrl::Master *ctrl)
+		: nanogui::Window(parent, "Settings"), ctrl_(ctrl) {
 	using namespace nanogui;
 
 	setLayout(new GroupLayout());
 	setPosition(Vector2i(parent->width()/2.0f - 100.0f, parent->height()/2.0f - 100.0f));
 	//setModal(true);
 
-	if (peer) {
-		configurables_ = ctrl->getConfigurables(peer.value());
-	} else {
-		configurables_ = ftl::config::list();
-	}
+	configurables_ = ftl::config::list();
 
 	new Label(this, "Select Configurable","sans-bold");
 
@@ -52,67 +44,50 @@ ConfigWindow::~ConfigWindow() {
 
 }
 
-class ConfigWindow::References {
-	public:
-	References(ftl::NetConfigurable* nc, ftl::config::json_t* config, const std::string* suri) : nc(nc), config(config), suri(suri) {
-	}
-
-	~References() {
-		delete nc;
-		delete config;
-		delete suri;
-	}
-
-	private:
-	ftl::NetConfigurable* nc;
-	ftl::config::json_t* config;
-	const std::string* suri;
-};
-
-std::vector<ftl::gui::ConfigWindow::References *> ConfigWindow::_addElements(nanogui::FormHelper *form, ftl::Configurable &nc, const std::string &suri, std::function<ftl::Configurable*(const std::string*, std::vector<References *>&)> construct) {
+void ConfigWindow::_addElements(nanogui::FormHelper *form, const std::string &suri) {
 	using namespace nanogui;
 
-	std::vector<References *> references;
-
-	auto data = nc.getConfig();
+	Configurable *configurable = ftl::config::find(suri);
+	ftl::config::json_t data;
+	if (configurable) {
+		data = configurable->getConfig();
+	}
 
 	for (auto i=data.begin(); i!=data.end(); ++i) {
 		if (i.key() == "$id") continue;
 
 		if (i.key() == "$ref" && i.value().is_string()) {
 			LOG(INFO) << "Follow $ref: " << i.value();
-			const std::string* suri = new std::string(i.value().get<string>());
-			ftl::Configurable* rc = construct(suri, references);
-			auto new_references = _addElements(form, *rc, *suri, construct);
-			references.insert(references.end(), new_references.begin(), new_references.end());
+			const std::string suri = std::string(i.value().get<string>());
+			_addElements(form, suri);
 			continue;
 		}
 
 		if (i.value().is_boolean()) {
 			string key = i.key();
-			form->addVariable<bool>(i.key(), [this,data,key,&nc](const bool &b){
-				nc.set(key, b);
+			form->addVariable<bool>(i.key(), [this,data,key,suri](const bool &b){
+				ftl::config::update(suri+"/"+key, b);
 			}, [data,key]() -> bool {
 				return data[key].get<bool>();
 			});
 		} else if (i.value().is_number_integer()) {
 			string key = i.key();
-			form->addVariable<int>(i.key(), [this,data,key,&nc](const int &f){
-				nc.set(key, f);
+			form->addVariable<int>(i.key(), [this,data,key,suri](const int &f){
+				ftl::config::update(suri+"/"+key, f);
 			}, [data,key]() -> int {
 				return data[key].get<int>();
 			});
 		} else if (i.value().is_number_float()) {
 			string key = i.key();
-			form->addVariable<float>(i.key(), [this,data,key,&nc](const float &f){
-				nc.set(key, f);
+			form->addVariable<float>(i.key(), [this,data,key,suri](const float &f){
+				ftl::config::update(suri+"/"+key, f);
 			}, [data,key]() -> float {
 				return data[key].get<float>();
 			});
 		} else if (i.value().is_string()) {
 			string key = i.key();
-			form->addVariable<string>(i.key(), [this,data,key,&nc](const string &f){
-				nc.set(key, f);
+			form->addVariable<string>(i.key(), [this,data,key,suri](const string &f){
+				ftl::config::update(suri+"/"+key, f);
 			}, [data,key]() -> string {
 				return data[key].get<string>();
 			});
@@ -131,8 +106,6 @@ std::vector<ftl::gui::ConfigWindow::References *> ConfigWindow::_addElements(nan
 			}
 		}
 	}
-
-	return references;
 }
 
 void ConfigWindow::_buildForm(const std::string &suri) {
@@ -145,50 +118,15 @@ void ConfigWindow::_buildForm(const std::string &suri) {
 	form->addWindow(Vector2i(100,50), uri.getFragment());
 	form->window()->setTheme(theme());
 
-	ftl::config::json_t* config;
-	config = new ftl::config::json_t;
-	const std::string* allocated_suri = new std::string(suri);
-	std::vector<ftl::gui::ConfigWindow::References *> references;
-
-	ftl::Configurable* nc;
-
-	if (peer_) {
-		*config = ctrl_->get(peer_.value(), suri);
-		nc = new ftl::NetConfigurable(peer_.value(), *allocated_suri, *ctrl_, *config);
+	_addElements(form, suri);
 
-		references = _addElements(form, *nc, *allocated_suri, [this](auto suri, auto &references) {
-			ftl::config::json_t* config = new ftl::config::json_t;
-			*config = ctrl_->get(peer_.value(), *suri);
-			auto nc = new ftl::NetConfigurable(peer_.value(), *suri, *ctrl_, *config);
-			auto r = new References(nc, config, suri);
-			references.push_back(r);
-			return nc;
-		});
-	} else {
-		nc = ftl::config::find(suri);
-		if (nc) {
-			references = _addElements(form, *nc, *allocated_suri, [this](auto suri, auto &references) {
-				return ftl::config::find(*suri);
-			});
-		}
-	}
-
-	auto closebutton = form->addButton("Close", [this,form,config,allocated_suri,nc,references]() {
+	auto closebutton = form->addButton("Close", [this,form]() {
 		form->window()->setVisible(false);
-		for(auto r : references) {
-			delete r;
-		}
-		if (peer_) {
-			delete nc;
-		}
-		delete config;
-		delete allocated_suri;
 		delete form;
 	});
 	closebutton->setIcon(ENTYPO_ICON_CROSS);
 }
 
 bool ConfigWindow::exists(const std::string &uri) {
-	// If the Configurable is a NetConfigurable, the URI is not checked.
-	return peer_ || ftl::config::find(uri);
-}
\ No newline at end of file
+	return ftl::config::find(uri) != nullptr;
+}
diff --git a/applications/gui/src/config_window.hpp b/applications/gui/src/config_window.hpp
index 23279d93cee1d601e53e0147b4edee8d5309a483..a0fe74155fe5320f983871d8c9237c7571625670 100644
--- a/applications/gui/src/config_window.hpp
+++ b/applications/gui/src/config_window.hpp
@@ -16,23 +16,15 @@ namespace gui {
  */
 class ConfigWindow : public nanogui::Window {
 	public:
-	ConfigWindow(nanogui::Widget *parent, ftl::ctrl::Master *ctrl, const ftl::UUID &peer);
-	ConfigWindow(nanogui::Widget *parent, ftl::ctrl::Master *ctrl, const std::optional<ftl::UUID> &peer = std::nullopt);
+	ConfigWindow(nanogui::Widget *parent, ftl::ctrl::Master *ctrl);
 	~ConfigWindow();
 
 	private:
-	/*
-	References holds the pointers to a NetConfigurable and all its members so that
-	they can all be returned from _addElements() and then simultaneously deleted
-	as the form is closed.
-	*/
-	class References;
 	ftl::ctrl::Master *ctrl_;
-	std::optional<ftl::UUID> peer_;
 	std::vector<std::string> configurables_;
 	
 	void _buildForm(const std::string &uri);
-	std::vector<References *> _addElements(nanogui::FormHelper *form, ftl::Configurable &nc, const std::string &suri, std::function<ftl::Configurable*(const std::string*, std::vector<References *>&)> construct);
+	void _addElements(nanogui::FormHelper *form, const std::string &suri);
 	bool exists(const std::string &uri);
 };
 
diff --git a/applications/gui/src/ctrl_window.cpp b/applications/gui/src/ctrl_window.cpp
index 67a3682bd2fcfcb55a68fb9c71b6dd55aad36491..fc82c45838a568efc8b4e8d456cdb3fbbef2c8b4 100644
--- a/applications/gui/src/ctrl_window.cpp
+++ b/applications/gui/src/ctrl_window.cpp
@@ -69,7 +69,7 @@ ControlWindow::ControlWindow(nanogui::Widget *parent, ftl::ctrl::Master *ctrl)
 
 	button = new Button(tools, "", ENTYPO_ICON_COG);
 	button->setCallback([this,parent] {
-		auto cfgwin = new ConfigWindow(parent, ctrl_, _getActiveID());
+		auto cfgwin = new ConfigWindow(parent, ctrl_);
 		cfgwin->setTheme(theme());
 	});
 	button->setTooltip("Edit node configuration");
diff --git a/applications/gui/src/main.cpp b/applications/gui/src/main.cpp
index 50bc3638c2e26197fbcf5b74dadacd4b6a49d0ad..7875e52bb04afc59953ac1a728dafe64008b6d6c 100644
--- a/applications/gui/src/main.cpp
+++ b/applications/gui/src/main.cpp
@@ -2,6 +2,7 @@
 #include <ftl/net/universe.hpp>
 #include <ftl/rgbd.hpp>
 #include <ftl/master.hpp>
+#include <ftl/net_configurable.hpp>
 
 #include <loguru.hpp>
 
@@ -12,9 +13,6 @@ int main(int argc, char **argv) {
 	auto root = ftl::configure(argc, argv, "gui_default");
 	ftl::net::Universe *net = ftl::create<ftl::net::Universe>(root, "net");
 
-	net->start();
-	net->waitConnections();
-
 	ftl::ctrl::Master *controller = new ftl::ctrl::Master(root, net);
 	controller->onLog([](const ftl::ctrl::LogEvent &e){
 		const int v = e.verbosity;
@@ -25,6 +23,33 @@ int main(int argc, char **argv) {
 		}
 	});
 
+	std::map<ftl::UUID, std::vector<ftl::NetConfigurable*>> peerConfigurables;
+
+	net->onConnect([&controller, &peerConfigurables](ftl::net::Peer *p) {
+		ftl::UUID peer = p->id();
+		auto cs = controller->getConfigurables(peer);
+		for (auto c : cs) {
+			ftl::config::json_t *configuration = new ftl::config::json_t;
+			*configuration = controller->get(peer, c);
+			if (!configuration->empty()) {
+				ftl::NetConfigurable *nc = new ftl::NetConfigurable(peer, c, *controller, *configuration);
+				peerConfigurables[peer].push_back(nc);
+			}
+		}
+	});
+
+	net->onDisconnect([&peerConfigurables](ftl::net::Peer *p) {
+		ftl::UUID peer = p->id();
+		for (ftl::NetConfigurable *nc : peerConfigurables[peer]) {
+			ftl::config::json_t *configuration = &(nc->getConfig());
+			delete nc;
+			delete configuration;
+		}
+	});
+
+	net->start();
+	net->waitConnections();
+
 	/*auto available = net.findAll<string>("list_streams");
 	for (auto &a : available) {
 		std::cout << " -- " << a << std::endl;
@@ -57,4 +82,4 @@ int main(int argc, char **argv) {
 	delete root;
 
 	return 0;
-}
\ No newline at end of file
+}
diff --git a/applications/gui/src/media_panel.cpp b/applications/gui/src/media_panel.cpp
index b3ea1a6afbbc8f2a31bf294e85a85564fdffe1cb..407fb917222c078c76d34a58d00ac93f2deb7228 100644
--- a/applications/gui/src/media_panel.cpp
+++ b/applications/gui/src/media_panel.cpp
@@ -18,7 +18,6 @@ MediaPanel::MediaPanel(ftl::gui::Screen *screen) : nanogui::Window(screen, ""),
 	using namespace nanogui;
 
 	paused_ = false;
-	writer_ = nullptr;
 	disable_switch_channels_ = false;
 
 	setLayout(new BoxLayout(Orientation::Horizontal,
@@ -38,26 +37,21 @@ MediaPanel::MediaPanel(ftl::gui::Screen *screen) : nanogui::Window(screen, ""),
 	button = new Button(this, "", ENTYPO_ICON_CONTROLLER_RECORD);
 	button->setFlags(Button::ToggleButton);
 	button->setChangeCallback([this,button](bool state) {
-		if (state){
-			auto *cam = screen_->activeCamera();
-
-			button->setTextColor(nanogui::Color(1.0f,0.1f,0.1f,1.0f));
-			char timestamp[18];
-			std::time_t t=std::time(NULL);
-			std::strftime(timestamp, sizeof(timestamp), "%F-%H%M%S", std::localtime(&t));
-			writer_ = new ftl::rgbd::SnapshotStreamWriter(std::string(timestamp) + ".tar.gz", 1000 / 25);
-			writer_->addSource(cam->source());
-			writer_->start();
-		} else {
-			button->setTextColor(nanogui::Color(1.0f,1.0f,1.0f,1.0f));
-			if (writer_) {
-				writer_->stop();
-				delete writer_;
-				writer_ = nullptr;
+		auto tag = screen_->activeCamera()->source()->get<std::string>("uri");
+		if (tag) {
+			auto tagvalue = tag.value();
+			auto configurables = ftl::config::findByTag(tagvalue);
+			if (configurables.size() > 0) {
+				ftl::Configurable *configurable = configurables[0];
+				if (state){
+					configurable->set("record", true);
+					button->setTextColor(nanogui::Color(1.0f,0.1f,0.1f,1.0f));
+				} else {
+					button->setTextColor(nanogui::Color(1.0f,1.0f,1.0f,1.0f));
+					configurable->set("record", false);
+				}
 			}
 		}
-		//if (state) ... start
-		//else ... stop
 	});
 
 	button = new Button(this, "", ENTYPO_ICON_CONTROLLER_STOP);
diff --git a/applications/gui/src/screen.cpp b/applications/gui/src/screen.cpp
index a9f4cc023265ba427a0be451ee3337fe834528bc..474952f0c8e5df041962a3333d5a1c0ea0a677be 100644
--- a/applications/gui/src/screen.cpp
+++ b/applications/gui/src/screen.cpp
@@ -229,14 +229,13 @@ ftl::gui::Screen::Screen(ftl::Configurable *proot, ftl::net::Universe *pnet, ftl
 	{
 		LOG(INFO) << "NET CONNECT";
 		auto node_details = ctrl_->getSlaves();
-		std::vector<std::string> node_titles;
 
 		for (auto &d : node_details) {
 			LOG(INFO) << "ADDING TITLE: " << d.dump();
 			auto peer = ftl::UUID(d["id"].get<std::string>());
 			auto itembutton = new Button(popup, d["title"].get<std::string>());
 			itembutton->setCallback([this,popup,peer]() {
-				auto config_window = new ConfigWindow(this, ctrl_, peer);
+				auto config_window = new ConfigWindow(this, ctrl_);
 				config_window->setTheme(windowtheme);
 			});
 		}
diff --git a/applications/gui/src/src_window.cpp b/applications/gui/src/src_window.cpp
index 8e087d1dd42e25b00afd0efaf7b5dd22d5139a0d..b5eb141f72278ed986aac47b6bb3bd2feb7a7345 100644
--- a/applications/gui/src/src_window.cpp
+++ b/applications/gui/src/src_window.cpp
@@ -63,7 +63,7 @@ SourceWindow::SourceWindow(ftl::gui::Screen *screen)
 		UNIQUE_LOCK(mutex_, lk);
 		_updateCameras(screen_->net()->findAll<string>("list_streams"));
 	});
-	
+
 	UNIQUE_LOCK(mutex_, lk);
 
 	std::vector<ftl::rgbd::Source*> srcs = ftl::createArray<ftl::rgbd::Source>(screen_->root(), "sources", screen_->net());
diff --git a/applications/reconstruct/src/main.cpp b/applications/reconstruct/src/main.cpp
index 83dd19f84dabd37d345c0ac1294970fa8fd8c4cf..02ee515a1a02bf6128a051a7c67d860ed538b075 100644
--- a/applications/reconstruct/src/main.cpp
+++ b/applications/reconstruct/src/main.cpp
@@ -176,6 +176,7 @@ static void run(ftl::Configurable *root) {
 	//ftl::voxhash::SceneRep *scene = ftl::create<ftl::voxhash::SceneRep>(root, "voxelhash");
 	ftl::rgbd::Streamer *stream = ftl::create<ftl::rgbd::Streamer>(root, "stream", net);
 	ftl::rgbd::VirtualSource *virt = ftl::create<ftl::rgbd::VirtualSource>(root, "virtual");
+	root->set("tags", nlohmann::json::array({ root->getID()+"/virtual" }));
 	ftl::render::Triangular *splat = ftl::create<ftl::render::Triangular>(root, "renderer", &scene_B);
 	ftl::rgbd::Group *group = new ftl::rgbd::Group;
 	ftl::ILW *align = ftl::create<ftl::ILW>(root, "merge");
diff --git a/components/common/cpp/include/ftl/configuration.hpp b/components/common/cpp/include/ftl/configuration.hpp
index 94fcc7dfa6b57e9bfd59fbcf36c5be80e0b82638..9e4ba8aed4bcf8b5f2c8cec2c1079edb945359c3 100644
--- a/components/common/cpp/include/ftl/configuration.hpp
+++ b/components/common/cpp/include/ftl/configuration.hpp
@@ -72,6 +72,13 @@ json_t &resolveWait(const std::string &);
  */
 Configurable *find(const std::string &uri);
 
+/**
+ * Get all configurables that contain a specified tag. Tags are given under the
+ * "tags" property as an array of strings, but only during configurable
+ * construction.
+ */
+const std::vector<Configurable *> &findByTag(const std::string &tag);
+
 std::vector<std::string> list();
 
 /**
diff --git a/components/common/cpp/src/configuration.cpp b/components/common/cpp/src/configuration.cpp
index bd9d9feec20538cebc210cafba72a98f651b243c..082285f4826b88fb0a7589d76e9ec27cc7c9c1ee 100644
--- a/components/common/cpp/src/configuration.cpp
+++ b/components/common/cpp/src/configuration.cpp
@@ -165,6 +165,7 @@ static bool mergeConfig(const string path) {
 
 static std::map<std::string, json_t*> config_index;
 static std::map<std::string, ftl::Configurable*> config_instance;
+static std::map<std::string, std::vector<ftl::Configurable*>> tag_index;
 
 /*
  * Recursively URI index the JSON structure.
@@ -198,6 +199,10 @@ ftl::Configurable *ftl::config::find(const std::string &uri) {
 	else return (*ix).second;
 }
 
+const std::vector<Configurable*> &ftl::config::findByTag(const std::string &tag) {
+	return tag_index[tag];
+}
+
 std::vector<std::string> ftl::config::list() {
 	vector<string> r;
 	for (auto i : config_instance) {
@@ -219,6 +224,13 @@ void ftl::config::registerConfigurable(ftl::Configurable *cfg) {
 	} else {
 		config_instance[*uri] = cfg;
 		LOG(INFO) << "Registering instance: " << *uri;
+
+		auto tags = cfg->get<vector<string>>("tags");
+		if (tags) {
+			for (auto &t : *tags) {
+				tag_index[t].push_back(cfg);
+			}
+		}
 	}
 }
 
diff --git a/components/net/cpp/include/ftl/net_configurable.hpp b/components/net/cpp/include/ftl/net_configurable.hpp
index 3a31ecc27675f2e37b72bc42ee83da0dee5b7e4c..a9deaf94b0a75c121d6fcd367715da1b465a8ef2 100644
--- a/components/net/cpp/include/ftl/net_configurable.hpp
+++ b/components/net/cpp/include/ftl/net_configurable.hpp
@@ -17,7 +17,7 @@ namespace ftl {
 
     private:
 	ftl::UUID peer;
-	const std::string &suri;
+	const std::string suri;
 	ftl::ctrl::Master &ctrl;
     };