diff --git a/cli/http.go b/cli/http.go
index 746c4a45146a882d827ef4b58526a5e06e4ef9a3..a90be457ba4e72d9c1a9005fb81c6b9860410039 100644
--- a/cli/http.go
+++ b/cli/http.go
@@ -58,6 +58,8 @@ func HTTPGet(apiEndpoint, f string, args ...string) error {
 		uuid := string(bytes[19:55])
 		viper.Set("LAST_DEVICE_UUID", uuid)
 		fmt.Println(string(bytes))
+	case http.StatusAccepted:
+
 	default:
 		log.WithFields(log.Fields{
 			"status code": resp.StatusCode,
diff --git a/cli/set.go b/cli/set.go
index 4d01d1477fd281446c3666faaf69e9a9899f7347..c5499d6f3a085db76d03fcef3227645662e829fc 100644
--- a/cli/set.go
+++ b/cli/set.go
@@ -3,10 +3,7 @@ package cli
 import (
 	"code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi"
 	"code.fbi.h-da.de/cocsn/gosdn/nucleus"
-	"code.fbi.h-da.de/cocsn/gosdn/nucleus/util/proto"
 	"context"
-	pb "google.golang.org/protobuf/proto"
-	"os"
 )
 
 // Set sends a gNMI Set request to the specified target. Only one
@@ -23,28 +20,5 @@ func Set(a, u, p, typ string, args ...string) error {
 	if err != nil {
 		return err
 	}
-
-	path := gnmi.SplitPath(args[0])
-	req := []interface{}{
-		&gnmi.Operation{
-			Type:   typ,
-			Origin: "",
-			Target: "",
-			Path:   path,
-			Val:    args[1],
-		},
-	}
-
-	resp, err := t.Set(context.Background(), req...)
-	if err != nil {
-		return err
-	}
-
-	_, tap := os.LookupEnv("GOSDN_TAP")
-	if tap {
-		if err := proto.Write(resp.(pb.Message), "resp-set-system-config-hostname"); err != nil {
-			return err
-		}
-	}
-	return nil
+	return t.Set(context.Background(), args)
 }
diff --git a/cmd/change.go b/cmd/change.go
new file mode 100644
index 0000000000000000000000000000000000000000..fc10bdb4fb746317c2450b4548cfae680115769f
--- /dev/null
+++ b/cmd/change.go
@@ -0,0 +1,49 @@
+/*
+Copyright © 2021 da/net research group <danet.fbi.h-da.de>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package cmd
+
+import (
+	"github.com/spf13/cobra"
+)
+
+// changeCmd represents the change command
+var changeCmd = &cobra.Command{
+	Use:   "change",
+	Short: "manage changes of the specified pnd",
+	Long: `use "change list" or "change list-pending" to list changes
+
+use "change commit" or "change confirm" respectively`,
+}
+
+func init() {
+	cliCmd.AddCommand(changeCmd)
+}
diff --git a/cmd/cliSet.go b/cmd/cliSet.go
index 9a79c630bed30d80c0d8438224edc42f084ff14d..e3f3a0743fdea2fa9c5c2ebd5686ef2e6675dc6b 100644
--- a/cmd/cliSet.go
+++ b/cmd/cliSet.go
@@ -41,12 +41,14 @@ var cliSetCmd = &cobra.Command{
 	Use:   "set",
 	Args:  cobra.ExactArgs(2),
 	Short: "set a value on a device",
-	Long: `Set a path value for a given device. Only one path and
-only one value supported for now`,
+	Long: `Update a path value for a given device. Only one path and
+only one value supported for now.
+
+Use "set replace" or "set delete" respectively`,
 	RunE: func(cmd *cobra.Command, args []string) error {
 		return cli.HTTPGet(
 			apiEndpoint,
-			"set",
+			"update",
 			"uuid="+uuid,
 			"cliSbi="+cliSbi,
 			"cliPnd="+cliPnd,
@@ -60,5 +62,5 @@ only one value supported for now`,
 func init() {
 	cliCmd.AddCommand(cliSetCmd)
 
-	cliSetCmd.Flags().StringVar(&uuid, "uuid", "", "uuid of the requested device")
+	cliSetCmd.PersistentFlags().StringVar(&uuid, "uuid", "", "uuid of the target device")
 }
diff --git a/cmd/commit.go b/cmd/commit.go
new file mode 100644
index 0000000000000000000000000000000000000000..3e3937c3dd8ed918ce4cbe57719a0ef4deeff8e6
--- /dev/null
+++ b/cmd/commit.go
@@ -0,0 +1,57 @@
+/*
+Copyright © 2021 da/net research group <danet.fbi.h-da.de>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package cmd
+
+import (
+	"code.fbi.h-da.de/cocsn/gosdn/cli"
+	"github.com/spf13/cobra"
+)
+
+// commitCmd represents the commit command
+var commitCmd = &cobra.Command{
+	Use:   "commit",
+	Args:  cobra.ExactArgs(1),
+	Short: "Commit the given change for the active PND",
+	Long: ``,
+	RunE: func(cmd *cobra.Command, args []string) error {
+		return cli.HTTPGet(
+			apiEndpoint,
+			"change-commit",
+			"pnd="+cliPnd,
+			"cuid="+args[0],
+		)
+	},
+}
+
+func init() {
+	changeCmd.AddCommand(commitCmd)
+}
diff --git a/cmd/confirm.go b/cmd/confirm.go
new file mode 100644
index 0000000000000000000000000000000000000000..4707a8e5e7460802a54e5ec9f8ae121591206a61
--- /dev/null
+++ b/cmd/confirm.go
@@ -0,0 +1,57 @@
+/*
+Copyright © 2021 da/net research group <danet.fbi.h-da.de>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package cmd
+
+import (
+	"code.fbi.h-da.de/cocsn/gosdn/cli"
+	"github.com/spf13/cobra"
+)
+
+// confirmCmd represents the confirm command
+var confirmCmd = &cobra.Command{
+	Use:   "confirm",
+	Args:  cobra.ExactArgs(1),
+	Short: "Confirms the given change for the active PND",
+	Long: ``,
+	RunE: func(cmd *cobra.Command, args []string) error {
+		return cli.HTTPGet(
+			apiEndpoint,
+			"change-confirm",
+			"pnd="+cliPnd,
+			"cuid="+args[0],
+		)
+	},
+}
+
+func init() {
+	changeCmd.AddCommand(confirmCmd)
+}
diff --git a/cmd/delete.go b/cmd/delete.go
new file mode 100644
index 0000000000000000000000000000000000000000..3c4e4e7603c32be00c4bba428bd22b09cd735eea
--- /dev/null
+++ b/cmd/delete.go
@@ -0,0 +1,71 @@
+/*
+Copyright © 2021 da/net research group <danet.fbi.h-da.de>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package cmd
+
+import (
+	"code.fbi.h-da.de/cocsn/gosdn/cli"
+	"github.com/spf13/cobra"
+)
+
+// deleteCmd represents the delete command
+var deleteCmd = &cobra.Command{
+	Use:   "delete",
+	Args:  cobra.ExactArgs(1),
+	Short: "set a value on a device",
+	Long: `Set a path value for a given device. Only one path and
+only one value supported for now`,
+	RunE: func(cmd *cobra.Command, args []string) error {
+		return cli.HTTPGet(
+			apiEndpoint,
+			"delete",
+			"uuid="+uuid,
+			"cliSbi="+cliSbi,
+			"cliPnd="+cliPnd,
+			"path="+args[0],
+			"address="+address,
+		)
+	},
+}
+
+func init() {
+	cliSetCmd.AddCommand(deleteCmd)
+
+	// Here you will define your flags and configuration settings.
+
+	// Cobra supports Persistent Flags which will work for this command
+	// and all subcommands, e.g.:
+	// deleteCmd.PersistentFlags().String("foo", "", "A help for foo")
+
+	// Cobra supports local flags which will only run when this command
+	// is called directly, e.g.:
+	// deleteCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
+}
diff --git a/cmd/list.go b/cmd/list.go
new file mode 100644
index 0000000000000000000000000000000000000000..2e3a0ce52e18d3b457de393cda1869addc3715d5
--- /dev/null
+++ b/cmd/list.go
@@ -0,0 +1,55 @@
+/*
+Copyright © 2021 da/net research group <danet.fbi.h-da.de>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package cmd
+
+import (
+	"code.fbi.h-da.de/cocsn/gosdn/cli"
+	"github.com/spf13/cobra"
+)
+
+// listCmd represents the list command
+var listCmd = &cobra.Command{
+	Use:   "list",
+	Short: "Lists all committed changes",
+	Long: ``,
+	RunE: func(cmd *cobra.Command, args []string) error {
+		return cli.HTTPGet(
+			apiEndpoint,
+			"change-list",
+			"pnd="+cliPnd,
+		)
+	},
+}
+
+func init() {
+	changeCmd.AddCommand(listCmd)
+}
diff --git a/cmd/listPending.go b/cmd/listPending.go
new file mode 100644
index 0000000000000000000000000000000000000000..4d5ece8fee0055c1b2d4439ace77680927789a4f
--- /dev/null
+++ b/cmd/listPending.go
@@ -0,0 +1,55 @@
+/*
+Copyright © 2021 da/net research group <danet.fbi.h-da.de>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package cmd
+
+import (
+	"code.fbi.h-da.de/cocsn/gosdn/cli"
+	"github.com/spf13/cobra"
+)
+
+// listPendingCmd represents the listPending command
+var listPendingCmd = &cobra.Command{
+	Use:   "listPending",
+	Short: "Lists all committed changes",
+	Long: ``,
+	RunE: func(cmd *cobra.Command, args []string) error {
+		return cli.HTTPGet(
+			apiEndpoint,
+			"change-list-pending",
+			"pnd="+cliPnd,
+		)
+	},
+}
+
+func init() {
+	changeCmd.AddCommand(listPendingCmd)
+}
diff --git a/cmd/replace.go b/cmd/replace.go
new file mode 100644
index 0000000000000000000000000000000000000000..33529c6d0e647aa9494b0dec9eee4ea2b2253818
--- /dev/null
+++ b/cmd/replace.go
@@ -0,0 +1,62 @@
+/*
+Copyright © 2021 da/net research group <danet.fbi.h-da.de>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package cmd
+
+import (
+	"code.fbi.h-da.de/cocsn/gosdn/cli"
+	"github.com/spf13/cobra"
+)
+
+// replaceCmd represents the replace command
+var replaceCmd = &cobra.Command{
+	Use:   "replace",
+	Args:  cobra.ExactArgs(2),
+	Short: "set a value on a device",
+	Long: `Set a path value for a given device. Only one path and
+only one value supported for now`,
+	RunE: func(cmd *cobra.Command, args []string) error {
+		return cli.HTTPGet(
+			apiEndpoint,
+			"replace",
+			"uuid="+uuid,
+			"cliSbi="+cliSbi,
+			"cliPnd="+cliPnd,
+			"path="+args[0],
+			"address="+address,
+			"value="+args[1],
+		)
+	},
+}
+
+func init() {
+	cliSetCmd.AddCommand(replaceCmd)
+}
diff --git a/cmd/update.go b/cmd/update.go
new file mode 100644
index 0000000000000000000000000000000000000000..93db8e374adf4553675bd14db98c29b09ee88052
--- /dev/null
+++ b/cmd/update.go
@@ -0,0 +1,62 @@
+/*
+Copyright © 2021 da/net research group <danet.fbi.h-da.de>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package cmd
+
+import (
+	"code.fbi.h-da.de/cocsn/gosdn/cli"
+	"github.com/spf13/cobra"
+)
+
+// updateCmd represents the update command
+var updateCmd = &cobra.Command{
+	Use:   "update",
+	Args:  cobra.ExactArgs(2),
+	Short: "update a value on a device",
+	Long: `Update a path value for a given device. Only one path and
+only one value supported for now`,
+	RunE: func(cmd *cobra.Command, args []string) error {
+		return cli.HTTPGet(
+			apiEndpoint,
+			"update",
+			"uuid="+uuid,
+			"cliSbi="+cliSbi,
+			"cliPnd="+cliPnd,
+			"path="+args[0],
+			"address="+address,
+			"value="+args[1],
+		)
+	},
+}
+
+func init() {
+	cliSetCmd.AddCommand(updateCmd)
+}
diff --git a/go.mod b/go.mod
index e34ad1f7839c9f774d5f1319c3ad05b654dc876c..c3595c3e3c9bf7cfe659b8fa65dff53fcf79a714 100644
--- a/go.mod
+++ b/go.mod
@@ -11,7 +11,7 @@ require (
 	github.com/neo4j/neo4j-go-driver v1.8.3
 	github.com/openconfig/gnmi v0.0.0-20200617225440-d2b4e6a45802
 	github.com/openconfig/goyang v0.2.3
-	github.com/openconfig/ygot v0.10.0
+	github.com/openconfig/ygot v0.10.4
 	github.com/sirupsen/logrus v1.8.0
 	github.com/spf13/cobra v1.1.1
 	github.com/spf13/viper v1.7.1
diff --git a/go.sum b/go.sum
index ccec3caa45e621417689f472330ef8a6c83794fd..dfc84d46a711f66bbcaac6e485f4bdbc96f34d2b 100644
--- a/go.sum
+++ b/go.sum
@@ -441,6 +441,8 @@ github.com/openconfig/ygot v0.6.0/go.mod h1:o30svNf7O0xK+R35tlx95odkDmZWS9JyWWQS
 github.com/openconfig/ygot v0.9.0/go.mod h1:oCQNdXnv7dWc8scTDgoFkauv1wwplJn5HspHcjlxSAQ=
 github.com/openconfig/ygot v0.10.0 h1:EmgwLXbFiCBmEUlSI4/1fPuRzgf4EsD0sThmAmRqbYM=
 github.com/openconfig/ygot v0.10.0/go.mod h1:oCQNdXnv7dWc8scTDgoFkauv1wwplJn5HspHcjlxSAQ=
+github.com/openconfig/ygot v0.10.4 h1:7rfvHEWFBYxFeVVK9UbMq6BJMAD7zeedidEUuxhcfaU=
+github.com/openconfig/ygot v0.10.4/go.mod h1:oCQNdXnv7dWc8scTDgoFkauv1wwplJn5HspHcjlxSAQ=
 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
 github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
 github.com/pborman/getopt v0.0.0-20190409184431-ee0cd42419d3/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=
diff --git a/mocks/PrincipalNetworkDomain.go b/mocks/PrincipalNetworkDomain.go
index 38233e76794d9913cf722aa848b2409fe4e1e499..3645a6215e5e79a44adbae955facaa66fcb639f4 100644
--- a/mocks/PrincipalNetworkDomain.go
+++ b/mocks/PrincipalNetworkDomain.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.6.0. DO NOT EDIT.
+// Code generated by mockery 2.7.4. DO NOT EDIT.
 
 package mocks
 
@@ -6,6 +6,8 @@ import (
 	mock "github.com/stretchr/testify/mock"
 
 	uuid "github.com/google/uuid"
+
+	ygot "github.com/openconfig/ygot/ygot"
 )
 
 // PrincipalNetworkDomain is an autogenerated mock type for the PrincipalNetworkDomain type
@@ -41,6 +43,50 @@ func (_m *PrincipalNetworkDomain) AddSbi(_a0 interface{}) error {
 	return r0
 }
 
+// ChangeOND provides a mock function with given fields: _a0, operation, path, value
+func (_m *PrincipalNetworkDomain) ChangeOND(_a0 uuid.UUID, operation interface{}, path string, value ...string) error {
+	_va := make([]interface{}, len(value))
+	for _i := range value {
+		_va[_i] = value[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _a0, operation, path)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(uuid.UUID, interface{}, string, ...string) error); ok {
+		r0 = rf(_a0, operation, path, value...)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// Committed provides a mock function with given fields: _a0
+func (_m *PrincipalNetworkDomain) Committed(_a0 uuid.UUID) (interface{}, error) {
+	ret := _m.Called(_a0)
+
+	var r0 interface{}
+	if rf, ok := ret.Get(0).(func(uuid.UUID) interface{}); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(interface{})
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(uuid.UUID) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
 // ContainsDevice provides a mock function with given fields: _a0
 func (_m *PrincipalNetworkDomain) ContainsDevice(_a0 uuid.UUID) bool {
 	ret := _m.Called(_a0)
@@ -83,6 +129,29 @@ func (_m *PrincipalNetworkDomain) GetDescription() string {
 	return r0
 }
 
+// GetDevice provides a mock function with given fields: _a0
+func (_m *PrincipalNetworkDomain) GetDevice(_a0 uuid.UUID) (ygot.GoStruct, error) {
+	ret := _m.Called(_a0)
+
+	var r0 ygot.GoStruct
+	if rf, ok := ret.Get(0).(func(uuid.UUID) ygot.GoStruct); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(ygot.GoStruct)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(uuid.UUID) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
 // GetName provides a mock function with given fields:
 func (_m *PrincipalNetworkDomain) GetName() string {
 	ret := _m.Called()
@@ -113,7 +182,7 @@ func (_m *PrincipalNetworkDomain) GetSBIs() interface{} {
 	return r0
 }
 
-// Id provides a mock function with given fields:
+// ID provides a mock function with given fields:
 func (_m *PrincipalNetworkDomain) ID() uuid.UUID {
 	ret := _m.Called()
 
@@ -129,6 +198,38 @@ func (_m *PrincipalNetworkDomain) ID() uuid.UUID {
 	return r0
 }
 
+// ListCommitted provides a mock function with given fields:
+func (_m *PrincipalNetworkDomain) ListCommitted() []uuid.UUID {
+	ret := _m.Called()
+
+	var r0 []uuid.UUID
+	if rf, ok := ret.Get(0).(func() []uuid.UUID); ok {
+		r0 = rf()
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).([]uuid.UUID)
+		}
+	}
+
+	return r0
+}
+
+// ListPending provides a mock function with given fields:
+func (_m *PrincipalNetworkDomain) ListPending() []uuid.UUID {
+	ret := _m.Called()
+
+	var r0 []uuid.UUID
+	if rf, ok := ret.Get(0).(func() []uuid.UUID); ok {
+		r0 = rf()
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).([]uuid.UUID)
+		}
+	}
+
+	return r0
+}
+
 // MarshalDevice provides a mock function with given fields: _a0
 func (_m *PrincipalNetworkDomain) MarshalDevice(_a0 uuid.UUID) (string, error) {
 	ret := _m.Called(_a0)
@@ -150,6 +251,29 @@ func (_m *PrincipalNetworkDomain) MarshalDevice(_a0 uuid.UUID) (string, error) {
 	return r0, r1
 }
 
+// Pending provides a mock function with given fields: _a0
+func (_m *PrincipalNetworkDomain) Pending(_a0 uuid.UUID) (interface{}, error) {
+	ret := _m.Called(_a0)
+
+	var r0 interface{}
+	if rf, ok := ret.Get(0).(func(uuid.UUID) interface{}); ok {
+		r0 = rf(_a0)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(interface{})
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(uuid.UUID) error); ok {
+		r1 = rf(_a0)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
 // RemoveDevice provides a mock function with given fields: _a0
 func (_m *PrincipalNetworkDomain) RemoveDevice(_a0 uuid.UUID) error {
 	ret := _m.Called(_a0)
diff --git a/mocks/SouthboundInterface.go b/mocks/SouthboundInterface.go
index 6217f5a7674db004f64254bfe5d5c8348ac733a7..38ffb81ff021989bb8153c33169ce40ad8863a33 100644
--- a/mocks/SouthboundInterface.go
+++ b/mocks/SouthboundInterface.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.6.0. DO NOT EDIT.
+// Code generated by mockery 2.7.4. DO NOT EDIT.
 
 package mocks
 
@@ -18,7 +18,7 @@ type SouthboundInterface struct {
 	mock.Mock
 }
 
-// Id provides a mock function with given fields:
+// ID provides a mock function with given fields:
 func (_m *SouthboundInterface) ID() uuid.UUID {
 	ret := _m.Called()
 
diff --git a/mocks/Storable.go b/mocks/Storable.go
index 630f70145d05ec4878461e243d238fe59958a483..e55a23eb41a51d2715f2ee94dfbb9e7a285a0145 100644
--- a/mocks/Storable.go
+++ b/mocks/Storable.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.6.0. DO NOT EDIT.
+// Code generated by mockery 2.7.4. DO NOT EDIT.
 
 package mocks
 
@@ -13,7 +13,7 @@ type Storable struct {
 	mock.Mock
 }
 
-// Id provides a mock function with given fields:
+// ID provides a mock function with given fields:
 func (_m *Storable) ID() uuid.UUID {
 	ret := _m.Called()
 
diff --git a/mocks/Transport.go b/mocks/Transport.go
index 6dc256acae1ee730623be6ab1fd5508f9f4ed9f5..7d8531e57334fd330a698165132428273d618f39 100644
--- a/mocks/Transport.go
+++ b/mocks/Transport.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.6.0. DO NOT EDIT.
+// Code generated by mockery 2.7.4. DO NOT EDIT.
 
 package mocks
 
@@ -76,29 +76,20 @@ func (_m *Transport) ProcessResponse(resp interface{}, root interface{}, models
 }
 
 // Set provides a mock function with given fields: ctx, params
-func (_m *Transport) Set(ctx context.Context, params ...interface{}) (interface{}, error) {
+func (_m *Transport) Set(ctx context.Context, params ...interface{}) error {
 	var _ca []interface{}
 	_ca = append(_ca, ctx)
 	_ca = append(_ca, params...)
 	ret := _m.Called(_ca...)
 
-	var r0 interface{}
-	if rf, ok := ret.Get(0).(func(context.Context, ...interface{}) interface{}); ok {
+	var r0 error
+	if rf, ok := ret.Get(0).(func(context.Context, ...interface{}) error); ok {
 		r0 = rf(ctx, params...)
 	} else {
-		if ret.Get(0) != nil {
-			r0 = ret.Get(0).(interface{})
-		}
-	}
-
-	var r1 error
-	if rf, ok := ret.Get(1).(func(context.Context, ...interface{}) error); ok {
-		r1 = rf(ctx, params...)
-	} else {
-		r1 = ret.Error(1)
+		r0 = ret.Error(0)
 	}
 
-	return r0, r1
+	return r0
 }
 
 // Subscribe provides a mock function with given fields: ctx, params
diff --git a/mocks/TransportOptions.go b/mocks/TransportOptions.go
index 4893655f8a5bc0fef06e4898e116fc3df7171421..b969fdb60e490658017a191dfb2bae818408b83a 100644
--- a/mocks/TransportOptions.go
+++ b/mocks/TransportOptions.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.6.0. DO NOT EDIT.
+// Code generated by mockery 2.7.4. DO NOT EDIT.
 
 package mocks
 
diff --git a/nucleus/gnmi_transport.go b/nucleus/gnmi_transport.go
index 3566855d7c2f81195e8adeb0b01ef12e3df140bc..80441b0c6322b55d53569e1b15c3f35c28a7a9ba 100644
--- a/nucleus/gnmi_transport.go
+++ b/nucleus/gnmi_transport.go
@@ -2,8 +2,7 @@ package nucleus
 
 import (
 	"code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi"
-	"code.fbi.h-da.de/cocsn/gosdn/nucleus/pnd"
-	pathutils"code.fbi.h-da.de/cocsn/gosdn/nucleus/util/path"
+	pathutils "code.fbi.h-da.de/cocsn/gosdn/nucleus/util/path"
 	"context"
 	gpb "github.com/openconfig/gnmi/proto/gnmi"
 	"github.com/openconfig/gnmi/proto/gnmi_ext"
@@ -242,10 +241,6 @@ func (g *Gnmi) ProcessResponse(resp interface{}, root interface{}, s *ytypes.Sch
 	return nil
 }
 
-func (g *Gnmi) TranslateChange(i ...interface{}) (*pnd.Change, error) {
-	panic("implement me")
-}
-
 // Capabilities calls GNMI capabilities
 func (g *Gnmi) Capabilities(ctx context.Context) (interface{}, error) {
 	log.WithFields(log.Fields{
diff --git a/nucleus/gnmi_transport_test.go b/nucleus/gnmi_transport_test.go
index 38270c8da5fe3f31f030c5010de2b980a8d8d127..18627bd9349ab7d740a80bd5472c7eda045632bf 100644
--- a/nucleus/gnmi_transport_test.go
+++ b/nucleus/gnmi_transport_test.go
@@ -276,7 +276,6 @@ func TestGnmi_Set(t *testing.T) {
 		name    string
 		fields  fields
 		args    args
-		want    interface{}
 		wantErr bool
 	}{
 		{
@@ -285,20 +284,15 @@ func TestGnmi_Set(t *testing.T) {
 			args: args{
 				params: nil,
 			},
-			want:    nil,
 			wantErr: true,
 		},
 		// TODO: Positive test cases
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got, err := tt.fields.transport.Set(context.Background(), tt.args.params...)
+			err := tt.fields.transport.Set(context.Background(), tt.args.params...)
 			if (err != nil) != tt.wantErr {
 				t.Errorf("Set() error = %v, wantErr %v", err, tt.wantErr)
-				return
-			}
-			if !reflect.DeepEqual(got, tt.want) {
-				t.Errorf("Set() got = %v, want %v", got, tt.want)
 			}
 		})
 	}
diff --git a/nucleus/http.go b/nucleus/http.go
index 56ad4cb0af707662e72656e1a7b6a4fa0a88f046..621d0d30ca7071a675f67d7b8ee62a4bf1340dd9 100644
--- a/nucleus/http.go
+++ b/nucleus/http.go
@@ -2,16 +2,24 @@ package nucleus
 
 import (
 	"code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi"
+	. "code.fbi.h-da.de/cocsn/gosdn/nucleus/pnd"
 	"context"
 	"fmt"
 	"github.com/google/uuid"
 	gpb "github.com/openconfig/gnmi/proto/gnmi"
 	log "github.com/sirupsen/logrus"
+	"io"
 	"net/http"
 	"net/url"
 	"time"
 )
 
+var apiOpmap = map[string]Operation{
+	"update":  TRANSPORT_UPDATE,
+	"replace": TRANSPORT_REPLACE,
+	"delete":  TRANSPORT_DELETE,
+}
+
 func stopHttpServer() error {
 	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
 	defer cancel()
@@ -61,7 +69,6 @@ func httpHandler(writer http.ResponseWriter, request *http.Request) {
 		return
 	}
 
-
 	id, err := uuid.Parse(query.Get("uuid"))
 	if err != nil {
 		if err.Error() != "invalid UUID length: 0" {
@@ -88,23 +95,18 @@ func httpHandler(writer http.ResponseWriter, request *http.Request) {
 	if query.Get("q") != "init" && query.Get("q") != "getIDs" {
 		pnd, err = c.pndc.get(pid)
 		if err != nil {
-			log.Error(err)
-			writer.WriteHeader(http.StatusInternalServerError)
+			handleServerError(writer, err)
 			return
 		}
 		sbic := pnd.GetSBIs()
 		sbi, err = sbic.(*sbiStore).get(sid)
 		if err != nil {
-			log.WithFields(log.Fields{
-				"requested uuid":  sid,
-				"available uuids": sbic.(*sbiStore).UUIDs(),
-			}).Error(err)
-			writer.WriteHeader(http.StatusInternalServerError)
+			handleServerError(writer, err)
 			return
 		}
 	}
 
-	switch query.Get("q") {
+	switch q := query.Get("q"); q {
 	case "addDevice":
 		d, err := NewDevice(sbi, &GnmiTransportOptions{
 			Config: gnmi.Config{
@@ -167,42 +169,81 @@ func httpHandler(writer http.ResponseWriter, request *http.Request) {
 		writer.Header().Set("Content-Type", "application/json")
 		fmt.Fprintf(writer, "%v", device)
 	case "getIDs":
-		writeIDs := func(typ string, ids []uuid.UUID) {
-			fmt.Fprintf(writer, "%v:\n", typ)
-			for i, id := range ids {
-				fmt.Fprintf(writer, "%v: %v\n", i+1, id)
-			}
-		}
+
 		pnds := c.pndc.UUIDs()
-		writeIDs("PNDs", pnds)
-		writeIDs("SBIs", c.sbic.UUIDs())
-		for _,id := range pnds{
+		writeIDs(writer, "PNDs", pnds)
+		writeIDs(writer, "SBIs", c.sbic.UUIDs())
+		for _, id := range pnds {
 			p, err := c.pndc.get(id)
 			if err != nil {
-				writer.WriteHeader(http.StatusInternalServerError)
-				log.Error(err)
+				handleServerError(writer, err)
 				return
 			}
-			writeIDs("Devices", p.(*pndImplementation).devices.UUIDs())
+			writeIDs(writer, "Devices", p.(*pndImplementation).devices.UUIDs())
 		}
 	case "init":
-		writeIDs := func(typ string, ids []uuid.UUID) {
-			for _, id := range ids {
-				fmt.Fprintf(writer, "%v", id)
-			}
+		writeIDs(writer, "PNDs", c.pndc.UUIDs())
+		writeIDs(writer, "SBIs", c.sbic.UUIDs())
+	case "update", "replace":
+		if err := pnd.ChangeOND(id, apiOpmap[q], query.Get("path"), query.Get("value")); err != nil {
+			handleServerError(writer, err)
+			return
 		}
-		writeIDs("PNDs", c.pndc.UUIDs())
-		writeIDs("SBIs", c.sbic.UUIDs())
-	case "set":
-		resp, err := pnd.(*pndImplementation).Set(id, query.Get("path"), query.Get("value"))
-		if err != nil {
-			writer.WriteHeader(http.StatusInternalServerError)
-			log.Error(err)
+		writer.WriteHeader(http.StatusOK)
+	case "delete":
+		if err := pnd.ChangeOND(id, TRANSPORT_DELETE, query.Get("path")); err != nil {
+			handleServerError(writer, err)
 			return
 		}
 		writer.WriteHeader(http.StatusOK)
-		fmt.Fprintln(writer, resp)
+	case "change-list":
+		changes := pnd.ListCommitted()
+		writeIDs(writer, "Tentative changes", changes)
+		writer.WriteHeader(http.StatusOK)
+	case "change-list-pending":
+		changes := pnd.ListPending()
+		writeIDs(writer, "Pending changes", changes)
+		writer.WriteHeader(http.StatusOK)
+	case "change-commit":
+		cuid, err := uuid.Parse(query.Get("cuid"))
+		if err != nil {
+			handleServerError(writer, err)
+			return
+		}
+		change, err := pnd.Pending(cuid)
+		if err != nil {
+			handleServerError(writer, err)
+			return
+		}
+		change.(*Change).Commit()
+		writer.WriteHeader(http.StatusAccepted)
+	case "change-confirm":
+		cuid, err := uuid.Parse(query.Get("cuid"))
+		if err != nil {
+			handleServerError(writer, err)
+			return
+		}
+		change, err := pnd.Committed(cuid)
+		if err != nil {
+			handleServerError(writer, err)
+			return
+		}
+		change.(*Change).Confirm()
+		writer.WriteHeader(http.StatusAccepted)
 	default:
 		writer.WriteHeader(http.StatusBadRequest)
 	}
 }
+
+func writeIDs(w io.Writer, typ string, ids []uuid.UUID) {
+	fmt.Fprintf(w, "%v:\n", typ)
+	for i, id := range ids {
+		fmt.Fprintf(w, "%v: %v\n", i+1, id)
+	}
+}
+
+func handleServerError(w http.ResponseWriter, err error) {
+	w.WriteHeader(http.StatusInternalServerError)
+	fmt.Fprintf(w, "error: %v", err)
+	log.Error(err)
+}
diff --git a/nucleus/http_test.go b/nucleus/http_test.go
index 9efa72110a1fe89cf1507424d147e7faac6fd71b..b7728d9ff87b8359a26623a56fe881ef41b6a3b3 100644
--- a/nucleus/http_test.go
+++ b/nucleus/http_test.go
@@ -123,7 +123,19 @@ func Test_httpApi(t *testing.T) {
 		},
 		{
 			name:    "set",
-			request: apiEndpoint + "/api?q=set" + args + "&path=/system/config/hostname&value=ceos3000",
+			request: apiEndpoint + "/api?q=update" + args + "&path=/system/config/hostname&value=ceos3000",
+			want:    &http.Response{StatusCode: http.StatusOK},
+			wantErr: false,
+		},
+		{
+			name:    "replace",
+			request: apiEndpoint + "/api?q=replace" + args + "&path=/system/config/hostname&value=ceos3000",
+			want:    &http.Response{StatusCode: http.StatusOK},
+			wantErr: false,
+		},
+		{
+			name:    "delete",
+			request: apiEndpoint + "/api?q=delete" + args + "&path=/system/config/hostname",
 			want:    &http.Response{StatusCode: http.StatusOK},
 			wantErr: false,
 		},
diff --git a/nucleus/initialise_test.go b/nucleus/initialise_test.go
index 6c90b054e44d009e03542821ced09f46cd65bb49..932adb147fc0033dbd66557923f27ec965f9c2a4 100644
--- a/nucleus/initialise_test.go
+++ b/nucleus/initialise_test.go
@@ -139,20 +139,24 @@ func readTestUUIDs() {
 }
 
 func mockDevice() Device {
+	sbi := &OpenConfig{}
 	return Device{
 		UUID:      mdid,
-		GoStruct:  nil,
-		SBI:       &OpenConfig{},
+		GoStruct:  sbi.Schema().Root,
+		SBI:       sbi,
 		Transport: &mocks.Transport{},
 	}
 }
 
 func newPnd() pndImplementation {
 	return pndImplementation{
-		name:        "default",
-		description: "default test pnd",
-		sbic:        sbiStore{store{}},
-		devices:     deviceStore{store{}},
-		id:          defaultPndID,
+		name:             "default",
+		description:      "default test pnd",
+		sbic:             sbiStore{store{}},
+		devices:          deviceStore{store{}},
+		pendingChanges:   changeStore{store{}},
+		committedChanges: changeStore{store{}},
+		confirmedChanges: changeStore{store{}},
+		id:               defaultPndID,
 	}
 }
diff --git a/nucleus/principalNetworkDomain.go b/nucleus/principalNetworkDomain.go
index e9dba35b2885669dd7d963710e3fffd6fa307773..df77cfa866b8c2a6afeadd47ced27d348edbd242 100644
--- a/nucleus/principalNetworkDomain.go
+++ b/nucleus/principalNetworkDomain.go
@@ -1,6 +1,7 @@
 package nucleus
 
 import (
+	"code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi"
 	. "code.fbi.h-da.de/cocsn/gosdn/nucleus/pnd"
 	"context"
 	"github.com/openconfig/ygot/ygot"
@@ -19,7 +20,7 @@ type PrincipalNetworkDomain interface {
 	RemoveSbi(uuid.UUID) error
 	AddDevice(interface{}) error
 	GetDevice(uuid uuid.UUID) (ygot.GoStruct, error)
-	ChangeOND(uuid uuid.UUID, operation Operation, path string, value string) error
+	ChangeOND(uuid uuid.UUID, operation interface{}, path string, value ...string) error
 	RemoveDevice(uuid.UUID) error
 	Request(uuid.UUID, string) error
 	RequestAll(string) error
@@ -29,6 +30,10 @@ type PrincipalNetworkDomain interface {
 	ContainsDevice(uuid.UUID) bool
 	GetSBIs() interface{}
 	ID() uuid.UUID
+	ListPending() []uuid.UUID
+	Pending(uuid.UUID) (interface{}, error)
+	ListCommitted() []uuid.UUID
+	Committed(uuid.UUID) (interface{}, error)
 }
 
 type pndImplementation struct {
@@ -36,9 +41,9 @@ type pndImplementation struct {
 	description      string
 	sbic             sbiStore
 	devices          deviceStore
-	pendingChanges   store
-	committedChanges store
-	confirmedChanges store
+	pendingChanges   changeStore
+	committedChanges changeStore
+	confirmedChanges changeStore
 	id               uuid.UUID
 }
 
@@ -49,9 +54,9 @@ func NewPND(name, description string, id uuid.UUID, sbi SouthboundInterface) (Pr
 		description:      description,
 		sbic:             sbiStore{store{}},
 		devices:          deviceStore{store{}},
-		pendingChanges:   store{},
-		committedChanges: store{},
-		confirmedChanges: store{},
+		pendingChanges:   changeStore{store{}},
+		committedChanges: changeStore{store{}},
+		confirmedChanges: changeStore{store{}},
 		id:               id,
 	}
 	if err := pnd.sbic.add(sbi); err != nil {
@@ -60,6 +65,22 @@ func NewPND(name, description string, id uuid.UUID, sbi SouthboundInterface) (Pr
 	return pnd, nil
 }
 
+func (pnd *pndImplementation) ListPending() []uuid.UUID {
+	return pnd.pendingChanges.UUIDs()
+}
+
+func (pnd *pndImplementation) ListCommitted() []uuid.UUID {
+	return pnd.committedChanges.UUIDs()
+}
+
+func (pnd *pndImplementation)Pending(id uuid.UUID) (interface{}, error) {
+	return pnd.pendingChanges.get(id)
+}
+
+func (pnd *pndImplementation)Committed(id uuid.UUID) (interface{}, error) {
+	return pnd.committedChanges.get(id)
+}
+
 func (pnd *pndImplementation) ID() uuid.UUID {
 	return pnd.id
 }
@@ -126,7 +147,7 @@ func (pnd *pndImplementation) GetDevice(uuid uuid.UUID) (ygot.GoStruct, error) {
 	if err != nil {
 		return nil, err
 	}
-	return ygot.DeepCopy(d)
+	return ygot.DeepCopy(d.GoStruct)
 }
 
 // RemoveDevice removes a device from the PND
@@ -209,13 +230,13 @@ func (pnd *pndImplementation) RequestAll(path string) error {
 }
 
 // ChangeOND creates a change from the provided Operation, path and value. The Change is pending and
-func (pnd *pndImplementation) ChangeOND(uuid uuid.UUID, operation Operation, path string, value string) error {
+func (pnd *pndImplementation) ChangeOND(uuid uuid.UUID, operation interface{}, path string, value ...string) error {
 	d, err := pnd.getDevice(uuid)
 	if err != nil {
 		return err
 	}
-
 	cpy, err := ygot.DeepCopy(d.GoStruct)
+	ygot.BuildEmptyTree(cpy)
 
 	p, err := ygot.StringToStructuredPath(path)
 	if err != nil {
@@ -223,12 +244,9 @@ func (pnd *pndImplementation) ChangeOND(uuid uuid.UUID, operation Operation, pat
 	}
 
 	switch operation {
-	case TRANSPORT_UPDATE:
-		if err := ytypes.SetNode(d.SBI.Schema().RootSchema(), cpy, p, value); err != nil {
-			return err
-		}
-	case TRANSPORT_REPLACE:
-		if err := ytypes.SetNode(d.SBI.Schema().RootSchema(), cpy, p, value); err != nil {
+	case TRANSPORT_UPDATE, TRANSPORT_REPLACE:
+		typedValue := gnmi.TypedValue(value[0])
+		if err := ytypes.SetNode(d.SBI.Schema().RootSchema(), cpy, p, typedValue); err != nil {
 			return err
 		}
 	case TRANSPORT_DELETE:
diff --git a/nucleus/principalNetworkDomain_test.go b/nucleus/principalNetworkDomain_test.go
index f02f840bc948d4887983a4beb532808d9e8dc2c7..4b885cdf550139ee2a0410afca2c38d1025202d0 100644
--- a/nucleus/principalNetworkDomain_test.go
+++ b/nucleus/principalNetworkDomain_test.go
@@ -522,3 +522,47 @@ func Test_pndImplementation_RequestAll(t *testing.T) {
 		})
 	}
 }
+
+func Test_pndImplementation_ChangeOND(t *testing.T) {
+	type fields struct {
+		name             string
+		description      string
+		sbic             sbiStore
+		devices          deviceStore
+		pendingChanges   changeStore
+		committedChanges changeStore
+		confirmedChanges changeStore
+		id               uuid.UUID
+	}
+	type args struct {
+		uuid      uuid.UUID
+		operation interface{}
+		path      string
+		value     []string
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		args    args
+		wantErr bool
+	}{
+		// TODO: Add test cases.
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			pnd := &pndImplementation{
+				name:             tt.fields.name,
+				description:      tt.fields.description,
+				sbic:             tt.fields.sbic,
+				devices:          tt.fields.devices,
+				pendingChanges:   tt.fields.pendingChanges,
+				committedChanges: tt.fields.committedChanges,
+				confirmedChanges: tt.fields.confirmedChanges,
+				id:               tt.fields.id,
+			}
+			if err := pnd.ChangeOND(tt.args.uuid, tt.args.operation, tt.args.path, tt.args.value...); (err != nil) != tt.wantErr {
+				t.Errorf("ChangeOND() error = %v, wantErr %v", err, tt.wantErr)
+			}
+		})
+	}
+}
\ No newline at end of file
diff --git a/nucleus/restconf_transport.go b/nucleus/restconf_transport.go
index 7afdd8868d71f499463325532318f5f5874960a5..e1ae9fad723e70552e86562f759ee76fc3854f83 100644
--- a/nucleus/restconf_transport.go
+++ b/nucleus/restconf_transport.go
@@ -1,7 +1,6 @@
 package nucleus
 
 import (
-	"code.fbi.h-da.de/cocsn/gosdn/nucleus/pnd"
 	"context"
 	"github.com/openconfig/ygot/ytypes"
 )
@@ -38,9 +37,4 @@ func (r Restconf) GetOptions() interface{} {
 // ProcessResponse not yet implemented
 func (r Restconf) ProcessResponse(resp interface{}, root interface{}, models *ytypes.Schema) error {
 	return &ErrNotYetImplemented{}
-}
-
-// TranslateChange not yet implemented
-func (r Restconf) TranslateChange(i ...interface{}) (*pnd.Change, error) {
-	return nil, &ErrNotYetImplemented{}
-}
+}
\ No newline at end of file
diff --git a/nucleus/restconf_transport_test.go b/nucleus/restconf_transport_test.go
index 5f1eb1e352f2cb8214686c5215eea419c465dcd4..b31df33d665563cb12c64aac28f98788454e7272 100644
--- a/nucleus/restconf_transport_test.go
+++ b/nucleus/restconf_transport_test.go
@@ -61,26 +61,21 @@ func TestRestconf_ProcessResponse(t *testing.T) {
 func TestRestconf_Set(t *testing.T) {
 	type args struct {
 		ctx    context.Context
-		params []string
+		params []interface{}
 	}
 	tests := []struct {
 		name    string
 		args    args
-		want    interface{}
 		wantErr bool
 	}{
-		{name: "not implemented", args: args{}, want: nil, wantErr: true},
+		{name: "not implemented", args: args{}, wantErr: true},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 			r := Restconf{}
-			got, err := r.Set(tt.args.ctx, tt.args.params...)
+			err := r.Set(tt.args.ctx, tt.args.params...)
 			if (err != nil) != tt.wantErr {
 				t.Errorf("Set() error = %v, wantErr %v", err, tt.wantErr)
-				return
-			}
-			if !reflect.DeepEqual(got, tt.want) {
-				t.Errorf("Set() got = %v, want %v", got, tt.want)
 			}
 		})
 	}
diff --git a/nucleus/store.go b/nucleus/store.go
index 0563a5a30533467809380ac25d949bfc4b578e68..aeed8cb8f11e176d75878c1657e3f1c83f9f388a 100644
--- a/nucleus/store.go
+++ b/nucleus/store.go
@@ -1,6 +1,7 @@
 package nucleus
 
 import (
+	. "code.fbi.h-da.de/cocsn/gosdn/nucleus/pnd"
 	"github.com/google/uuid"
 	log "github.com/sirupsen/logrus"
 	"reflect"
@@ -131,7 +132,7 @@ func (s deviceStore) get(id uuid.UUID) (*Device, error) {
 	if !ok {
 		return nil, &ErrInvalidTypeAssertion{
 			v: device,
-			t: "Device",
+			t: reflect.TypeOf(&Device{}),
 		}
 	}
 	log.WithFields(log.Fields{
@@ -139,3 +140,25 @@ func (s deviceStore) get(id uuid.UUID) (*Device, error) {
 	}).Debug("device was accessed")
 	return device, nil
 }
+
+type changeStore struct {
+	store
+}
+
+func (s changeStore) get(id uuid.UUID) (*Change, error) {
+	item, err := s.store.get(id)
+	if err != nil {
+		return nil, err
+	}
+	change, ok := item.(*Change)
+	if !ok {
+		return nil, &ErrInvalidTypeAssertion{
+			v: change,
+			t: reflect.TypeOf(&Change{}),
+		}
+	}
+	log.WithFields(log.Fields{
+		"uuid": id,
+	}).Debug("change was accessed")
+	return change, nil
+}
diff --git a/nucleus/transport.go b/nucleus/transport.go
index 17ab0f82af2011c342b772c08df1e997fc2fe308..2d9313d591acb6e66667d20f1abf81deca3daeaf 100644
--- a/nucleus/transport.go
+++ b/nucleus/transport.go
@@ -2,7 +2,6 @@ package nucleus
 
 import (
 	"bytes"
-	"code.fbi.h-da.de/cocsn/gosdn/nucleus/pnd"
 	"context"
 	"github.com/openconfig/ygot/ytypes"
 	"io"
@@ -26,7 +25,6 @@ type Transport interface {
 	Type() string
 	GetOptions() interface{}
 	ProcessResponse(resp interface{}, root interface{}, models *ytypes.Schema) error
-	TranslateChange(...interface{}) (*pnd.Change, error)
 }
 
 // YANGConsumer is a auxillary type to redirect the response
diff --git a/test/integration/nucleusIntegration_test.go b/test/integration/nucleusIntegration_test.go
index 816888d6f77f857225f34d22b794017efe641629..960623c1de8b373cf0f4790220e876abb21fc49e 100644
--- a/test/integration/nucleusIntegration_test.go
+++ b/test/integration/nucleusIntegration_test.go
@@ -23,13 +23,12 @@ func TestGnmi_SetIntegration(t *testing.T) {
 	}
 	type args struct {
 		ctx    context.Context
-		params []interface{}
+		params []string
 	}
 	tests := []struct {
 		name    string
 		fields  fields
 		args    args
-		want    interface{}
 		wantErr bool
 	}{
 		{
@@ -42,9 +41,8 @@ func TestGnmi_SetIntegration(t *testing.T) {
 			},
 			args: args{
 				ctx:    context.Background(),
-				params: []interface{}{&gnmi.Operation{}},
+				params: []string{},
 			},
-			want:    nil,
 			wantErr: true,
 		},
 		{
@@ -52,21 +50,8 @@ func TestGnmi_SetIntegration(t *testing.T) {
 			fields: fields{opt: opt},
 			args: args{
 				ctx: context.Background(),
-				params: []interface{}{
-					&gnmi.Operation{
-						Type:   "update",
-						Origin: "",
-						Target: "",
-						Path: []string{
-							"system",
-							"config",
-							"hostname",
-						},
-						Val: "ceos3000",
-					},
+				params: []string{"/system/config/hostname", "ceos3000"},
 				},
-			},
-			want:    gnmiMessages["../proto/resp-set-system-config-hostname"],
 			wantErr: false,
 		},
 	}
@@ -77,21 +62,11 @@ func TestGnmi_SetIntegration(t *testing.T) {
 				t.Errorf("NewGnmiTransport() error = %v, wantErr %v", err, tt.wantErr)
 				return
 			}
-			resp, err := g.Set(tt.args.ctx, tt.args.params...)
+			err = g.Set(tt.args.ctx, tt.args.params)
 			if (err != nil) != tt.wantErr {
 				t.Errorf("Set() error = %v, wantErr %v", err, tt.wantErr)
 				return
 			}
-			got, ok := resp.(*gpb.SetResponse)
-			if !ok {
-				t.Errorf("want: %v, got %v, error: %v", reflect.TypeOf(&gpb.SetResponse{}), reflect.TypeOf(resp), &nucleus.ErrInvalidTypeAssertion{})
-			}
-			if err != nil && tt.wantErr {
-				return
-			} else if got.Prefix.Target != testAddress ||
-				got.Response[0].Op != gpb.UpdateResult_UPDATE {
-				t.Errorf("Set() got = %v, want %v", got, tt.want)
-			}
 		})
 	}
 }