diff --git a/applications/rtdt-manager/app/app.go b/applications/rtdt-manager/app/app.go
index f4dec1cf8714e9fa354067cd1906ed32e3050f23..43572373a2d1ee83559c9ce2a6212fc61b7bbe1f 100644
--- a/applications/rtdt-manager/app/app.go
+++ b/applications/rtdt-manager/app/app.go
@@ -7,22 +7,30 @@ import (
 	"syscall"
 
 	"code.fbi.h-da.de/danet/gosdn/application-framework/event"
+	"code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/clab-config"
 	"code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/rtdt-auth"
 	"code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/rtdt-manager"
+	"code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/util"
 	"google.golang.org/grpc"
 )
 
 // An App manages one realnet instance of gosdn and one (or potentially more) twin
 type App struct {
-	conn     *grpc.ClientConn // connection to realnet gosdn
-	stopChan chan os.Signal
-	managers []*rtdtmanager.RtdtManager // future consideration: launch multiple siblings
+	gosdnPath string           // Absolute root of gosdn repo on system
+	conn      *grpc.ClientConn // connection to realnet gosdn
+	stopChan  chan os.Signal
+	managers  []*rtdtmanager.RtdtManager // future consideration: launch multiple siblings
 }
 
 func NewApp(conn *grpc.ClientConn, auth *rtdt_auth.RtdtAuth) *App {
+	gosdnPath, err := util.GenerateGosdnPath()
+	if err != nil {
+		return nil
+	}
 	return &App{
-		conn:     conn,
-		stopChan: make(chan os.Signal), // TODO: Should this be shared globally?
+		gosdnPath: gosdnPath,
+		conn:      conn,
+		stopChan:  make(chan os.Signal), // TODO: Should this be shared globally?
 	}
 }
 
@@ -30,6 +38,10 @@ func (app *App) AddManager(rMan *rtdtmanager.RtdtManager) {
 	app.managers = append(app.managers, rMan)
 }
 
+func (app App) GetGosdnPath() string {
+	return app.gosdnPath
+}
+
 // Run the app with manager, catch events and react to them
 func (app *App) Run() error {
 	if len(app.managers) == 0 {
@@ -39,15 +51,21 @@ func (app *App) Run() error {
 	signal.Notify(app.stopChan, os.Interrupt, syscall.SIGSTOP)
 
 	// Run until SIGINT, SIGTERM is received
-    // Based on "code.fbi.h-da.de/danet/gosdn/applications/hostname-checker/app.go"
-	var forever chan struct{}
+	// Based on "code.fbi.h-da.de/danet/gosdn/applications/hostname-checker/app.go"
+	forever := make(chan struct{})
 	go func() {
 		for {
 			select {
 			// idea: make channels for different events?
 			case stop := <-app.stopChan:
+				// this takes full path now
+				err := clabconfig.ClabDestroy(app.managers[0].GetClabTwinFilename())
+				if err != nil {
+					fmt.Printf("Couldn't clean up twin: %v\n", err)
+				}
+
 				close(forever)
-				fmt.Print("Received SIGINT/SIGSTOP, quitting..")
+				fmt.Print("Received SIGINT/SIGSTOP, quitting..\n")
 				_ = stop
 				return
 			}
diff --git a/applications/rtdt-manager/clab-config/clab-config.go b/applications/rtdt-manager/clab-config/clab-config.go
index 97ff714c7cd55045abe44fe40961d713b778f20d..04b694c576b46b3657a47a7a3045c10e00746147 100644
--- a/applications/rtdt-manager/clab-config/clab-config.go
+++ b/applications/rtdt-manager/clab-config/clab-config.go
@@ -4,11 +4,16 @@ import (
 	"fmt"
 	"os"
 	"os/exec"
+	"os/signal"
 	"path/filepath"
 	"strconv"
 	"strings"
+	"syscall"
+	"time"
 
 	"gopkg.in/yaml.v3"
+
+	util "code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/util"
 )
 
 // 1st level (Root entry)
@@ -50,6 +55,17 @@ type Link struct {
 	Endpoints []string `yaml:"endpoints"`
 }
 
+// return absolute clab config path based on gosdn root
+// should return /home/user/path/to/gosdn/dev_env_data/clab
+func ClabConfigPath() (string, error) {
+
+	gosdnPath, err := util.GenerateGosdnPath()
+	if err != nil {
+		return "", fmt.Errorf("Couldn't get Gosdn Path: %w\n", err)
+	}
+	return filepath.Join(gosdnPath, "/dev_env_data/clab"), nil
+}
+
 // Read file and parse into ClabConfig struct
 func LoadConfig(filename string) (*ClabConfig, error) {
 	absFilepath, err := filepath.Abs(filename)
@@ -142,14 +158,14 @@ func DeriveConfig(clabconfig *ClabConfig, newIPv4Subnet, newIPv6Subnet string, p
 }
 
 // WriteConfig writes the Config struct to a YAML file
-func WriteConfig(filename string, dirname string, config *ClabConfig) error {
+// Takes config file as absolute path
+func WriteConfig(filename string, config *ClabConfig) error {
 	data, err := yaml.Marshal(config)
 	if err != nil {
 		return fmt.Errorf("failed to marshal YAML: %w", err)
 	}
 
-	fullPath := filepath.Join(dirname, filename)
-	err = os.WriteFile(fullPath, data, 0644)
+	err = os.WriteFile(filename, data, 0644)
 	if err != nil {
 		return fmt.Errorf("failed to write file: %w", err)
 	}
@@ -157,12 +173,27 @@ func WriteConfig(filename string, dirname string, config *ClabConfig) error {
 	return nil
 }
 
-func ClabDeploy(filename string, dirname string, stopchan chan os.Signal) error {
-	fullPath := filepath.Join(dirname, filename)
+func ClabDestroy(fullPath string) error {
+	fmt.Println("Trying to destroy twin first")
+	cmd := exec.Command("sudo", "containerlab", "destroy", "-t", fullPath)
+	output, err := cmd.CombinedOutput()
+	if err != nil {
+		return fmt.Errorf("Couldn't destroy clab environment: %w", err)
+	}
+	fmt.Println("Output of containerlab: ", string(output))
+	time.Sleep(20 * time.Second)
+	return err
+
+}
+
+// Launch a containerlab environment, pass in absolute path of clab yaml
+func ClabDeploy(fullPath string) error {
 	cmd := exec.Command("sudo", "containerlab", "deploy", "-t", fullPath, "--reconfigure")
 
 	// Run the command in a Goroutine
 	done := make(chan error, 1)
+	stopdeploy := make(chan os.Signal, 1)
+	signal.Notify(stopdeploy, os.Interrupt, syscall.SIGTERM)
 	go func() {
 		output, err := cmd.CombinedOutput() // Use CombinedOutput to capture stdout and stderr
 		if err != nil {
@@ -177,11 +208,17 @@ func ClabDeploy(filename string, dirname string, stopchan chan os.Signal) error
 	// Wait for the deployment to finish or a signal to stop
 	select {
 	case err := <-done: // Command finished
+		fmt.Println("Successfully deployed the twin")
 		return err
-	case <-stopchan: // Signal received to interrupt
+	case <-stopdeploy: // Signal received to interrupt
 		if err := cmd.Process.Kill(); err != nil {
 			fmt.Printf("Failed to kill process: %v\n", err)
 		}
-		return fmt.Errorf("deployment interrupted by signal")
+		err := ClabDestroy(fullPath)
+		if err != nil {
+			return fmt.Errorf("Deploying containerlab environment was interrupted and couldn't be cleaned up: %v", err)
+		}
+
+		return fmt.Errorf("Deployment interrupted by signal")
 	}
 }
diff --git a/applications/rtdt-manager/data/.gosdn_slim.clab.yaml.bak b/applications/rtdt-manager/data/.gosdn_slim.clab.yaml.bak
new file mode 100644
index 0000000000000000000000000000000000000000..860448226b9ca3368bbfb7148525170278fca5bc
--- /dev/null
+++ b/applications/rtdt-manager/data/.gosdn_slim.clab.yaml.bak
@@ -0,0 +1,101 @@
+name: gosdn_csbi_arista_base
+
+mgmt:
+  network: gosdn-csbi-arista-base-net
+  ipv4-subnet: 172.100.0.0/16
+  ipv6-subnet: 2001:db8::/64
+  mtu: 1500
+
+topology:
+  nodes:
+    plugin-registry:
+      kind: linux
+      image: plugin-registry
+      mgmt-ipv4: 172.100.0.16
+
+    gosdn:
+      kind: linux
+      image: gosdn
+      ports:
+        - 55055:55055
+        - 8080:8080
+        - 40000:40000
+      cmd: --config /app/configs/containerlab-gosdn.toml
+      mgmt-ipv4: 172.100.0.5
+      env:
+        GOSDN_ADMIN_PASSWORD: TestPassword
+      binds:
+        - ../../../artifacts/ssl/gosdn:/app/ssl
+
+    gnmi-target-switch0:
+      kind: linux
+      image: registry.code.fbi.h-da.de/danet/gnmi-target/debian:master
+      #only for local use
+      #image: gnmi-target:latest
+      binds:
+        - ../../../artifacts/ssl/gnmi-target:/etc/gnmi-target/ssl
+      ports:
+        - 7030:7030
+      cmd: start --ca_file /etc/gnmi-target/ssl/ca.crt --cert /etc/gnmi-target/ssl/certs/gnmi-target-selfsigned.crt --key /etc/gnmi-target/ssl/private/gnmi-target-selfsigned.key
+      mgmt-ipv4: 172.100.0.11
+      startup-delay: 5
+
+    gnmi-target-switch1:
+      kind: linux
+      image: registry.code.fbi.h-da.de/danet/gnmi-target/debian:master
+      #only for local use
+      #image: gnmi-target:latest
+      binds:
+        - ../../../artifacts/ssl/gnmi-target:/etc/gnmi-target/ssl
+      ports:
+        - 7031:7030
+      cmd: start --ca_file /etc/gnmi-target/ssl/ca.crt --cert /etc/gnmi-target/ssl/certs/gnmi-target-selfsigned.crt --key /etc/gnmi-target/ssl/private/gnmi-target-selfsigned.key
+      mgmt-ipv4: 172.100.0.12
+      startup-delay: 5
+
+    centos0:
+      kind: linux
+      image: centos:8
+      mgmt-ipv4: 172.100.0.3
+      group: server
+
+    centos1:
+      kind: linux
+      image: centos:8
+      mgmt-ipv4: 172.100.0.4
+      group: server
+
+    mongodb:
+      kind: linux
+      image: mongo:7
+      ports:
+        - 27017:27017
+      env:
+        MONGO_INITDB_ROOT_USERNAME: root
+        MONGO_INITDB_ROOT_PASSWORD: example
+      mgmt-ipv4: 172.100.0.13
+
+    mongodb-express:
+      kind: linux
+      image: mongo-express:1.0.2
+      ports:
+        - 8081:8081
+      env:
+        ME_CONFIG_MONGODB_AUTH_USERNAME: root
+        ME_CONFIG_MONGODB_AUTH_PASSWORD: example
+        ME_CONFIG_MONGODB_SERVER: mongodb
+        ME_CONFIG_BASICAUTH: "false"
+      mgmt-ipv4: 172.100.0.14
+
+    rabbitmq:
+      kind: linux
+      image: rabbitmq:3-management
+      ports:
+        - 127.0.0.1:5672:5672
+        - 127.0.0.1:15672:15672
+      mgmt-ipv4: 172.100.0.15
+
+  links:
+    - endpoints: ["gnmi-target-switch0:eth1", "gnmi-target-switch1:eth1"]
+    - endpoints: ["gnmi-target-switch0:eth2", "centos0:eth1"]
+    - endpoints: ["gnmi-target-switch1:eth2", "centos1:eth1"]
diff --git a/applications/rtdt-manager/data/gosdn_slim.clab.yaml b/applications/rtdt-manager/data/gosdn_slim.clab.yaml
index f4d76846cddf22b054f8cee3c4e39d70589cb7ea..860448226b9ca3368bbfb7148525170278fca5bc 100644
--- a/applications/rtdt-manager/data/gosdn_slim.clab.yaml
+++ b/applications/rtdt-manager/data/gosdn_slim.clab.yaml
@@ -25,7 +25,7 @@ topology:
       env:
         GOSDN_ADMIN_PASSWORD: TestPassword
       binds:
-        - ../../artifacts/ssl/gosdn:/app/ssl
+        - ../../../artifacts/ssl/gosdn:/app/ssl
 
     gnmi-target-switch0:
       kind: linux
@@ -33,7 +33,7 @@ topology:
       #only for local use
       #image: gnmi-target:latest
       binds:
-        - ../../artifacts/ssl/gnmi-target:/etc/gnmi-target/ssl
+        - ../../../artifacts/ssl/gnmi-target:/etc/gnmi-target/ssl
       ports:
         - 7030:7030
       cmd: start --ca_file /etc/gnmi-target/ssl/ca.crt --cert /etc/gnmi-target/ssl/certs/gnmi-target-selfsigned.crt --key /etc/gnmi-target/ssl/private/gnmi-target-selfsigned.key
@@ -46,7 +46,7 @@ topology:
       #only for local use
       #image: gnmi-target:latest
       binds:
-        - ../../artifacts/ssl/gnmi-target:/etc/gnmi-target/ssl
+        - ../../../artifacts/ssl/gnmi-target:/etc/gnmi-target/ssl
       ports:
         - 7031:7030
       cmd: start --ca_file /etc/gnmi-target/ssl/ca.crt --cert /etc/gnmi-target/ssl/certs/gnmi-target-selfsigned.crt --key /etc/gnmi-target/ssl/private/gnmi-target-selfsigned.key
diff --git a/applications/rtdt-manager/main.go b/applications/rtdt-manager/main.go
index 5c58ca8f4a22f5f7eeec0db08c079cf05a09969d..f78258b0b22c16404d9f4a9362a9cd6dc93b20b1 100644
--- a/applications/rtdt-manager/main.go
+++ b/applications/rtdt-manager/main.go
@@ -35,14 +35,21 @@ func main() {
 	defer conn.Close()
 
 	auth := rtdt_auth.NewRtdtAuth(user, url, pass, conn) // logs in and stores token
+	if auth == nil {
+		fmt.Println("Couldn't log in to gosdn, quitting!")
+        return
+	}
 	rtdt_app := app.NewApp(conn, auth)
 	rtdtMan := RtdtMan.NewRtdtManager(conn, auth, topology_file)
 	if rtdtMan == nil {
 		fmt.Println("Couldn't initialize rtdt-manager, quitting!")
 		return
 	}
+	// Register manager with app and run
+
 	rtdt_app.AddManager(rtdtMan)
-	_ = rtdtMan
+	// If twin should be launched, uncomment:
+	//rtdtMan.LaunchTwin("172.101.0.0/16", "2001:db9::/64", "twin")
 
 	err = rtdt_app.Run()
 	if err != nil {
diff --git a/applications/rtdt-manager/rtdt-manager/rtdt-manager.go b/applications/rtdt-manager/rtdt-manager/rtdt-manager.go
index bdd440074b754efc92138eccd45734ee16f52235..7e13e3036da31716f36bcaad36ee8785a9a9d4ef 100644
--- a/applications/rtdt-manager/rtdt-manager/rtdt-manager.go
+++ b/applications/rtdt-manager/rtdt-manager/rtdt-manager.go
@@ -3,11 +3,8 @@ package rtdtmanager
 import (
 	"fmt"
 	"os"
-	"os/exec"
 	"os/signal"
-	"path"
 	"path/filepath"
-	"strings"
 	"syscall"
 	"time"
 
@@ -19,53 +16,27 @@ import (
 	confManPb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/configurationmanagement"
 	pnd "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/pnd"
 	topoPb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/topology"
-	"code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/clab-config"
+	clabconfig "code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/clab-config"
 	"code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/rtdt-auth"
-	"code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/rtdt-topology"
-	"code.fbi.h-da.de/danet/gosdn/applications/venv-manager/containerlab"
+	util "code.fbi.h-da.de/danet/gosdn/applications/rtdt-manager/util"
 	"google.golang.org/grpc"
-	"gopkg.in/yaml.v3"
 )
 
-func now() int64 {
-	return int64(time.Now().Nanosecond())
-}
-
-func getGosdnPath() (string, error) {
-	var execPath string
-	var absExecPath string
-	var err error
-	if execPath, err = os.Executable(); err != nil {
-		return "", nil
-	}
-	if absExecPath, err = filepath.Abs(execPath); err != nil {
-		return "", nil
-	}
-	executableDir := filepath.Dir(absExecPath)
-	projectRoot := filepath.Dir(executableDir)
-
-	fmt.Println("Project Root Path:", projectRoot)
-	return projectRoot, nil
-
-}
-
 type RtdtManager struct {
-	auth         *rtdt_auth.RtdtAuth         // auth struct for realnet gosdn
-	conn         *grpc.ClientConn            // connection to twin's gosdn instance
-	Pnd          *pnd.PrincipalNetworkDomain // PND for realnet gosdn
-	topo         *topoPb.Topology            // Topology with which to create clab.yaml
-	sdnConfig    string
-	clabFilename string // Clab file that exemplifies the topology
-	topoData     *containerlab.YamlStruct
-	clabData     *clabconfig.ClabConfig
-	eventService event.ServiceInterface // Receive events from realnet gosdn
-	stopChan     chan os.Signal         // Global stop channel TODO Can I use that like this?
+	auth             *rtdt_auth.RtdtAuth         // auth struct for realnet gosdn
+	conn             *grpc.ClientConn            // connection to twin's gosdn instance
+	Pnd              *pnd.PrincipalNetworkDomain // PND for realnet gosdn
+	topo             *topoPb.Topology            // Topology with which to create clab.yaml
+	sdnConfig        string                      // TODO
+	clabFilename     string                      // Clab file that exemplifies the topology of realnet
+	clabTwinFilename string
+	clabData         *clabconfig.ClabConfig // Represents the yaml config file as data structure
+	eventService     event.ServiceInterface // Receive events from realnet gosdn (not used yet)
+	stopChan         chan os.Signal         // Global stop channel TODO Can I use that like this?
 	// TODO auth and conn for virtual net?
 }
 
 func NewRtdtManager(conn *grpc.ClientConn, auth *rtdt_auth.RtdtAuth, clabFilename string) *RtdtManager {
-	projectRoot, _ := getGosdnPath()
-
 	rMan := RtdtManager{
 		conn:         conn,
 		auth:         auth,
@@ -78,55 +49,30 @@ func NewRtdtManager(conn *grpc.ClientConn, auth *rtdt_auth.RtdtAuth, clabFilenam
 		fmt.Println(err)
 		return nil
 	}
+	// Event system for realnet gosdn
+	if err := rMan.initEventSystem(); err != nil {
+		fmt.Println("Failed to initialize event system: ", err)
+		return nil
+	}
+
+	// Set up the twin here
 	// The topology of the currently running realnet needs to be parsed. We get this
 	// from the clab.yaml file that was used to create it, that means that --topolog needs to point
 	// to it
+	// TODO Implement
 	var err error
 	if rMan.clabData, err = clabconfig.LoadConfig(rMan.clabFilename); err != nil {
 		fmt.Println(err)
 		return nil
 	}
-	var derivedConfig *clabconfig.ClabConfig
-	if derivedConfig, err = clabconfig.DeriveConfig(rMan.clabData, "172.101.0.0/16", "2001:db9::/64", "twin"); err != nil {
-		fmt.Println(err)
-		return nil
-	}
-
-	clabConfigDir := filepath.Join(projectRoot, "dev_env_data/clab")
-	if err := clabconfig.WriteConfig("clab-derived.yaml", clabConfigDir, derivedConfig); err != nil {
-		return nil
-	}
-	if err := clabconfig.ClabDeploy("clab-derived.yaml", clabConfigDir, rMan.stopChan); err != nil {
-		return nil
-	}
 	fmt.Println("Success: RtdtManager created")
 	return &rMan
-	// Now we need to apply the topology to gosdn because it is not done automatically when creating
-	// devices
-	if err := rtdt_topology.ApplyTopology(rMan.topoData); err != nil {
-		fmt.Println(err)
-		return nil
-	}
-	// Write the topology to a file to check
-	if err := rMan.writeModifiedTopologyToFile(rMan.topoData); err != nil {
-		fmt.Println(err)
-		return nil
-	}
-	if err := rMan.initEventSystem(); err != nil {
-		fmt.Println(err)
-		return nil
-	}
-	// if err := rMan.manageConfig("import"); err != nil {
-	// 	fmt.Println(err)
-	// 	return nil
-	// }
-	return nil
 }
 
 func (rMan *RtdtManager) fetchPndUUID() error {
 	pndService := pnd.NewPndServiceClient(rMan.conn)
 	ctx := rMan.auth.CreateContextWithAuthorization()
-	pndResponse, err := pndService.GetPndList(ctx, &pnd.GetPndListRequest{Timestamp: now()})
+	pndResponse, err := pndService.GetPndList(ctx, &pnd.GetPndListRequest{Timestamp: util.Now()})
 	if err != nil {
 		return fmt.Errorf("Failed to retrieve PND information: %w", err)
 	}
@@ -142,44 +88,45 @@ func (rMan *RtdtManager) fetchPndUUID() error {
 func (rMan *RtdtManager) applyTopology() error {
 	topoService := topoPb.NewTopologyServiceClient(rMan.conn)
 	ctx := rMan.auth.CreateContextWithAuthorization()
-	topoService.AddLink(ctx, &topoPb.AddLinkRequest{Timestamp: now()})
+	topoService.AddLink(ctx, &topoPb.AddLinkRequest{Timestamp: util.Now()})
 
 	return nil
 }
 
 // \cite venv-manager
+// Keep for later
 // Write a clab.yaml file to launch a different
-func (rMan *RtdtManager) writeModifiedTopologyToFile(clabStruct *containerlab.YamlStruct) error {
-	rMan.topoData.Mgmt.Network = "gosdn-csbi-arist-twin-net"
-
-	splitMainNetwork := strings.Split(rMan.topoData.Mgmt.Ipv4Subnet, ".")
-	splitMainNetwork[1] = "101"
-	rMan.topoData.Mgmt.Ipv4Subnet = strings.Join(splitMainNetwork, ".")
-
-	splitMainNetworkIPv6 := strings.Split(rMan.topoData.Mgmt.Ipv6Subnet, ":")
-	splitMainNetworkIPv6[1] = "db9"
-	rMan.topoData.Mgmt.Ipv6Subnet = strings.Join(splitMainNetworkIPv6, ":")
-
-	// Different network for our twin
-	for i, node := range rMan.topoData.Topology.Nodes {
-		splitIPv4 := strings.Split(node.MgmtIpv4, ".")
-		splitIPv4[1] = "101"
-		node.MgmtIpv4 = strings.Join(splitIPv4, ".")
-		rMan.topoData.Topology.Nodes[i] = node
-	}
-	yaml, err := yaml.Marshal(clabStruct)
-	if err != nil {
-		return err
-	}
-
-	fname := "./topo.clab.tmp.yaml"
-	err = os.WriteFile(fname, yaml, 0600)
-	if err != nil {
-		return err
-	}
-
-	return nil
-}
+// func (rMan *RtdtManager) writeModifiedTopologyToFile(clabStruct *containerlab.YamlStruct) error {
+// 	rMan.topoData.Mgmt.Network = "gosdn-csbi-arist-twin-net"
+//
+// 	splitMainNetwork := strings.Split(rMan.topoData.Mgmt.Ipv4Subnet, ".")
+// 	splitMainNetwork[1] = "101"
+// 	rMan.topoData.Mgmt.Ipv4Subnet = strings.Join(splitMainNetwork, ".")
+//
+// 	splitMainNetworkIPv6 := strings.Split(rMan.topoData.Mgmt.Ipv6Subnet, ":")
+// 	splitMainNetworkIPv6[1] = "db9"
+// 	rMan.topoData.Mgmt.Ipv6Subnet = strings.Join(splitMainNetworkIPv6, ":")
+//
+// 	// Different network for our twin
+// 	for i, node := range rMan.topoData.Topology.Nodes {
+// 		splitIPv4 := strings.Split(node.MgmtIpv4, ".")
+// 		splitIPv4[1] = "101"
+// 		node.MgmtIpv4 = strings.Join(splitIPv4, ".")
+// 		rMan.topoData.Topology.Nodes[i] = node
+// 	}
+// 	yaml, err := yaml.Marshal(clabStruct)
+// 	if err != nil {
+// 		return err
+// 	}
+//
+// 	fname := "./topo.clab.tmp.yaml"
+// 	err = os.WriteFile(fname, yaml, 0600)
+// 	if err != nil {
+// 		return err
+// 	}
+//
+// 	return nil
+// }
 
 // This retrieves the topology from the running realnet gosdn instance
 // This is needed to generate the clab file to be used with the virtual net
@@ -187,7 +134,7 @@ func (rMan *RtdtManager) writeModifiedTopologyToFile(clabStruct *containerlab.Ya
 func (rMan *RtdtManager) fetchTopology() error {
 	topoService := topoPb.NewTopologyServiceClient(rMan.conn)
 	ctx := rMan.auth.CreateContextWithAuthorization()
-	topoResponse, err := topoService.GetTopology(ctx, &topoPb.GetTopologyRequest{Timestamp: int64(time.Now().Nanosecond())})
+	topoResponse, err := topoService.GetTopology(ctx, &topoPb.GetTopologyRequest{Timestamp: util.Now()})
 	if err != nil {
 		return fmt.Errorf("Failed to retrieve Topology: %w", err)
 	}
@@ -201,27 +148,32 @@ func (rMan *RtdtManager) fetchTopology() error {
 	return nil
 }
 
+// To launch a new twin, this runs through the following steps:
+// - Load the clab config for current realnet gosdn (passed on cli)
+// - Derive the config we need for twin
+// - Write the config to disk
+// - Use that config to call "containerlab deploy"
+// Takes
 // Launch a second gosdn instance which will manage the virtual network
-func (rMan *RtdtManager) LaunchTwin() error {
-	// First create the clab environment
-	// cmdClab := exec.Command("sudo", "containerlab", "deploy")
-	// output, err := cmdClab.Output()
-	// if err != nil {
-	// 	return fmt.Errorf("Failed to execute containerlab: %w", err)
-	// }
-	// Now launch gosdn and then use the api to register the clab environment with it
-	exePath, err := os.Executable()
-	if err != nil {
-		return fmt.Errorf("Failed to retrieve folder of executable: %w", err)
-	}
-	fmt.Printf("The current executable's path is %s\n", exePath)
-	exeDir := path.Dir(exePath)
-	_ = exeDir
-	fmt.Println("Launching gosdn..")
-	cmdGosdn := exec.Command(filepath.Join(exeDir, "gosdn"))
-	outputGosdn, err := cmdGosdn.CombinedOutput()
-	fmt.Printf("Output of gosdn: %s\n", outputGosdn)
+func (r *RtdtManager) LaunchTwin(twinSubnetIPv4, twinSubnetIPv6, twinName string) error {
+	var derivedConfig *clabconfig.ClabConfig
+	var clabConfigPath string
+	var err error
 
+	if r.clabData == nil {
+		err = fmt.Errorf("Can't launch a twin without loading a clab yaml file for physical network first")
+		return err
+	}
+	if derivedConfig, err = clabconfig.DeriveConfig(r.clabData, twinSubnetIPv4, twinSubnetIPv6, twinName); err != nil {
+		return fmt.Errorf("Failed to derive config for twin: %w", err)
+	}
+	clabConfigPath, err = clabconfig.ClabConfigPath()
+	clabConfigFullPath := filepath.Join(clabConfigPath, "twin-clab.yaml")
+	if err = clabconfig.WriteConfig(clabConfigFullPath, derivedConfig); err != nil {
+		return fmt.Errorf("Failed to write modified twin clab config to disk: %w", err)
+	}
+	// Now run deploy with new config file
+	clabconfig.ClabDeploy(clabConfigFullPath)
 	return nil
 }
 
@@ -242,7 +194,7 @@ func (rMan *RtdtManager) manageConfig(op string) error {
 	// Export from rtdt-manager into gosdn controller
 	case "export":
 		fmt.Println("Now exporting sdnConfig into gosdn controller")
-		// Implement me some day
+		//TODO Implement me some day
 		return nil
 	default:
 		return fmt.Errorf("Unknown config operation")
@@ -254,34 +206,53 @@ func (rMan *RtdtManager) manageConfig(op string) error {
 func (rMan *RtdtManager) initEventSystem() error {
 	ctx := rMan.auth.CreateContextWithAuthorization()
 	queueCredentials, err := registration.Register(ctx, rMan.auth.GetURL(), "basic-interface-monitoring", "SecurePresharedToken")
+    fmt.Println("Acquired queueCredentials: ", queueCredentials)
 	// TODO: Find out how I can receive the ip address here (it returns rabbitmq)
 	queueCredentials = "amqp://guest:guest@172.100.0.15:5672/" // fix by setting manually
 	if err != nil {
 		return fmt.Errorf("Encountered error while trying to register event system: %w", err)
 	}
 	fmt.Println("Trying to register with amqp with following queueCredentials: ", queueCredentials)
-	eventService, err := event.NewEventService(queueCredentials, []event.Topic{event.ManagedNetworkElement})
+	// You have to have one event service for a topic
+	eventServiceMNE, err := event.NewEventService(queueCredentials, []event.Topic{event.ManagedNetworkElement})
 	if err != nil {
 		return fmt.Errorf("Failed to attach to event system: %w", err)
 	}
-	// Subscribe to a topic and call "callback()" when event happens
-	// eventService is of type Service and contains a channel
-	// Callback gets passed the event
-	// Possible events: Update, Delete, Subscribe
-	// Use this to map desired callbacks to different events
-	eventService.SubscribeToEventType([]event.TypeToCallbackTuple{
-		{Type: event.Update, Callback: rMan.callback},
+	eventServiceUser, err := event.NewEventService(queueCredentials, []event.Topic{event.User})
+	if err != nil {
+		return fmt.Errorf("Failed to attach to event system: %w", err)
+	}
+
+	// Can have different callback per type per topic (e.g. adding mne or updating mne)
+	eventServiceMNE.SubscribeToEventType([]event.TypeToCallbackTuple{
+		{Type: event.Type(event.Update), Callback: rMan.updateMNECallback},
+	})
+	eventServiceUser.SubscribeToEventType([]event.TypeToCallbackTuple{
+		{Type: event.Type(event.User), Callback: rMan.userEventCallback},
 	})
 	// Now iterate over all topics of service and create goRoutines
 	// that consumes queue
 	// This function is supposed to be removed in the future?
-	eventService.SetupEventReciever(make(chan os.Signal, 1)) // doesn't seem to use channel internally..
+	eventServiceMNE.SetupEventReciever(make(chan os.Signal, 1)) // doesn't seem to use stop channel internally..
 	return nil
 }
 
-// Based on currently running network managed by gosdn, launch a new gosdn instance that mirrors it
-func (rMan *RtdtManager) callback(event *event.Event) {
-	fmt.Println("Event has happened, hurray!")
+func (rMan *RtdtManager) updateMNECallback(event *event.Event) {
+	fmt.Println("MNE Event has happened (type: update), hurray!")
+	fmt.Println("EventID: ", event.ID.ID())
+	fmt.Println("Event Type: ", event.Type)
+}
+
+func (rMan *RtdtManager) userEventCallback(event *event.Event) {
+	fmt.Println("User Event has happened (type: update), hurray!")
 	fmt.Println("EventID: ", event.ID.ID())
 	fmt.Println("Event Type: ", event.Type)
 }
+
+// {Get,Set}ers
+func (r RtdtManager) GetClabFilename() string {
+	return r.clabFilename
+}
+func (r RtdtManager) GetClabTwinFilename() string {
+	return r.clabTwinFilename
+}
diff --git a/applications/rtdt-manager/util/util.go b/applications/rtdt-manager/util/util.go
new file mode 100644
index 0000000000000000000000000000000000000000..ec1e048dc672de7798d2f029c137b1a6bffa2b1c
--- /dev/null
+++ b/applications/rtdt-manager/util/util.go
@@ -0,0 +1,50 @@
+package util
+
+import (
+	"fmt"
+	"os"
+	"path/filepath"
+	"strings"
+	"time"
+)
+
+func Now() int64 {
+	return int64(time.Now().Nanosecond())
+}
+
+func GenerateGosdnPath() (string, error) {
+	var execPath string
+	var absExecPath string
+	var err error
+	if execPath, err = os.Executable(); err != nil {
+		return "", err
+	}
+	if absExecPath, err = filepath.Abs(execPath); err != nil {
+		return "", err
+	}
+	executableDir := filepath.Dir(absExecPath)
+	projectRoot := filepath.Dir(executableDir)
+
+	return projectRoot, nil
+}
+
+// Get the path of a file, if path is a path, just return absolute path
+func GetAbsPath(path string) (string, error) {
+	absPath, err := filepath.Abs(path)
+	if err != nil {
+		return "", fmt.Errorf("Failed to resolve absolut path of %s: %w", path, err)
+	}
+	if strings.HasSuffix(path, "/") {
+		return absPath, nil
+	}
+	info, err := os.Stat(absPath)
+	if err != nil {
+		return "", fmt.Errorf("Failed to stat path: %w", err)
+	}
+	// If path is not a directory, make it one
+	if !info.IsDir() {
+		absPath = filepath.Dir(absPath)
+	}
+
+	return absPath, nil
+}