diff --git a/main.go b/main.go
index 8ee45ddf9f458caa6f888d05887d4f5268ea1e64..41508da84b647d752c619765ed30f00f23cc6ee5 100644
--- a/main.go
+++ b/main.go
@@ -11,10 +11,11 @@ package main
 
 import (
 	"fmt"
-	"github.com/spf13/cobra"
 	"os"
 	"syscall/js"
 
+	"github.com/spf13/cobra"
+
 	jww "github.com/spf13/jwalterweatherman"
 
 	"gitlab.com/elixxir/xxdk-wasm/logging"
@@ -147,6 +148,8 @@ func setGlobals() {
 	// wasm/cmix.go
 	js.Global().Set("NewCmix", js.FuncOf(wasm.NewCmix))
 	js.Global().Set("LoadCmix", js.FuncOf(wasm.LoadCmix))
+	js.Global().Set("LoadSynchronizedCmix",
+		js.FuncOf(wasm.LoadSyncrhonizedCmix))
 
 	// wasm/delivery.go
 	js.Global().Set("SetDashboardURL", js.FuncOf(wasm.SetDashboardURL))
@@ -231,10 +234,6 @@ func setGlobals() {
 	js.Global().Set("Listen", js.FuncOf(wasm.Listen))
 
 	// wasm/sync.go
-	js.Global().Set("NewFileSystemRemoteStorage",
-		js.FuncOf(wasm.NewFileSystemRemoteStorage))
-	js.Global().Set("NewOrLoadSyncRemoteKV",
-		js.FuncOf(wasm.NewOrLoadSyncRemoteKV))
 
 	// wasm/timeNow.go
 	js.Global().Set("SetTimeSource", js.FuncOf(wasm.SetTimeSource))
@@ -258,7 +257,7 @@ func setGlobals() {
 
 var (
 	logLevel, fileLogLevel      jww.Threshold
-	maxLogFileSizeMB              int
+	maxLogFileSizeMB            int
 	workerScriptURL, workerName string
 )
 
diff --git a/wasm/cmix.go b/wasm/cmix.go
index e3430f4cf4b489ad7480ab53522fac2631c7cbce..83a76767c84878f1fd8c6df9e4faccb657510caa 100644
--- a/wasm/cmix.go
+++ b/wasm/cmix.go
@@ -10,9 +10,10 @@
 package wasm
 
 import (
+	"syscall/js"
+
 	"gitlab.com/elixxir/client/v4/bindings"
 	"gitlab.com/elixxir/xxdk-wasm/utils"
-	"syscall/js"
 )
 
 // Cmix wraps the [bindings.Cmix] object so its methods can be wrapped to be
@@ -29,6 +30,7 @@ func newCmixJS(api *bindings.Cmix) map[string]any {
 		// cmix.go
 		"GetID":          js.FuncOf(c.GetID),
 		"GetReceptionID": js.FuncOf(c.GetReceptionID),
+		"GetRemoteKV":    js.FuncOf(c.GetRemoteKV),
 		"EKVGet":         js.FuncOf(c.EKVGet),
 		"EKVSet":         js.FuncOf(c.EKVSet),
 
@@ -142,6 +144,37 @@ func LoadCmix(_ js.Value, args []js.Value) any {
 	return utils.CreatePromise(promiseFn)
 }
 
+// LoadSyncrhonizedCmix will [LoadCmix] using a RemoteStore to establish
+// a synchronized RemoteKV.
+//
+// Parameters:
+//   - args[0] - Storage directory path (string).
+//   - args[1] - Password used for storage (Uint8Array).
+//   - args[2] - Javascript [RemoteStore] implementation.
+//   - args[3] - JSON of [xxdk.CMIXParams] (Uint8Array).
+//
+// Returns a promise:
+//   - Resolves to a Javascript representation of the [Cmix] object.
+//   - Rejected with an error if loading [Cmix] fails.
+func LoadSyncrhonizedCmix(_ js.Value, args []js.Value) any {
+	storageDir := args[0].String()
+	password := utils.CopyBytesToGo(args[1])
+	rs := newRemoteStore(args[2])
+	cmixParamsJSON := utils.CopyBytesToGo(args[3])
+
+	promiseFn := func(resolve, reject func(args ...any) js.Value) {
+		net, err := bindings.LoadSynchronizedCmix(storageDir, password,
+			rs, cmixParamsJSON)
+		if err != nil {
+			reject(utils.JsTrace(err))
+		} else {
+			resolve(newCmixJS(net))
+		}
+	}
+
+	return utils.CreatePromise(promiseFn)
+}
+
 // GetID returns the ID for this [bindings.Cmix] in the cmixTracker.
 //
 // Returns:
@@ -158,6 +191,20 @@ func (c *Cmix) GetReceptionID(js.Value, []js.Value) any {
 	return utils.CopyBytesToJS(c.api.GetReceptionID())
 }
 
+// GetRemoteKV returns the cMix RemoteKV
+//
+// Returns a promise:
+//   - Resolves with the RemoteKV object.
+func (c *Cmix) GetRemoteKV(_ js.Value, args []js.Value) any {
+
+	promiseFn := func(resolve, reject func(args ...any) js.Value) {
+		kv := c.api.GetRemoteKV()
+		resolve(newRemoteKvJS(kv))
+	}
+
+	return utils.CreatePromise(promiseFn)
+}
+
 // EKVGet allows access to a value inside the secure encrypted key value store.
 //
 // Parameters:
diff --git a/wasm/collective.go b/wasm/collective.go
new file mode 100644
index 0000000000000000000000000000000000000000..3b1f62fd105045627c23b3f9a8cf593d34be5949
--- /dev/null
+++ b/wasm/collective.go
@@ -0,0 +1,598 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2022 xx foundation                                             //
+//                                                                            //
+// Use of this source code is governed by a license that can be found in the  //
+// LICENSE file.                                                              //
+////////////////////////////////////////////////////////////////////////////////
+
+//go:build js && wasm
+
+package wasm
+
+import (
+	"syscall/js"
+
+	"gitlab.com/elixxir/client/v4/bindings"
+	"gitlab.com/elixxir/xxdk-wasm/utils"
+)
+
+////////////////////////////////////////////////////////////////////////////////
+// RemoteKV Methods                                                           //
+////////////////////////////////////////////////////////////////////////////////
+
+// RemoteKV wraps the [bindings.RemoteKV] object so its methods can be wrapped
+// to be Javascript compatible.
+type RemoteKV struct {
+	api *bindings.RemoteKV
+}
+
+// newRemoteKvJS creates a new Javascript compatible object (map[string]any)
+// that matches the [RemoteKV] structure.
+func newRemoteKvJS(api *bindings.RemoteKV) map[string]any {
+	rkv := RemoteKV{api}
+	rkvMap := map[string]any{
+		"Get":               js.FuncOf(rkv.Get),
+		"Delete":            js.FuncOf(rkv.Delete),
+		"Set":               js.FuncOf(rkv.Set),
+		"GetPrefix":         js.FuncOf(rkv.GetPrefix),
+		"HasPrefx":          js.FuncOf(rkv.HasPrefix),
+		"Prefix":            js.FuncOf(rkv.Prefix),
+		"Root":              js.FuncOf(rkv.Root),
+		"IsMemStore":        js.FuncOf(rkv.IsMemStore),
+		"GetFullKey":        js.FuncOf(rkv.GetFullKey),
+		"Transaction":       js.FuncOf(rkv.Transaction),
+		"StoreMapElement":   js.FuncOf(rkv.StoreMapElement),
+		"StoreMap":          js.FuncOf(rkv.StoreMap),
+		"DeleteMapElement":  js.FuncOf(rkv.DeleteMapElement),
+		"GetMap":            js.FuncOf(rkv.GetMap),
+		"GetMapElement":     js.FuncOf(rkv.GetMapElement),
+		"ListenOnRemoteKey": js.FuncOf(rkv.ListenOnRemoteKey),
+		"ListenOnRemoteMap": js.FuncOf(rkv.ListenOnRemoteMap),
+	}
+
+	return rkvMap
+}
+
+// Get returns the object stored at the specified version.
+// returns a json of [versioned.Object].
+//
+// Parameters:
+//   - args[0] - key to access, a string
+//   - args[1] - version, an integer
+//
+// Returns a promise:
+//   - Resolves to JSON of a [versioned.Object], e.g.:
+//     {"Version":1,"Timestamp":"2023-05-13T00:50:03.889192694Z","Data":"bm90IHVwZ3JhZGVk"}
+//   - Rejected with an access error. Note: File does not exist errors
+//     are returned whent key is not set.
+func (r *RemoteKV) Get(_ js.Value, args []js.Value) any {
+	key := args[0].String()
+	version := int64(args[1].Int())
+
+	promiseFn := func(resolve, reject func(args ...any) js.Value) {
+		value, err := r.api.Get(key, version)
+		if err != nil {
+			reject(utils.JsTrace(err))
+		} else {
+			resolve(utils.CopyBytesToJS(value))
+		}
+	}
+
+	return utils.CreatePromise(promiseFn)
+}
+
+// Delete removes a given key from the data store.
+//
+// Parameters:
+//   - args[0] - key to access, a string
+//   - args[1] - version, an integer
+//
+// Returns a promise:
+//   - Rejected with an access error. Note: File does not exist errors
+//     are returned whent key is not set.
+func (r *RemoteKV) Delete(_ js.Value, args []js.Value) any {
+	key := args[0].String()
+	version := int64(args[1].Int())
+
+	promiseFn := func(resolve, reject func(args ...any) js.Value) {
+		err := r.api.Delete(key, version)
+		if err != nil {
+			reject(utils.JsTrace(err))
+		} else {
+			resolve()
+		}
+	}
+
+	return utils.CreatePromise(promiseFn)
+}
+
+// Set upserts new data into the storage
+// When calling this, you are responsible for prefixing the
+// key with the correct type optionally unique id! Call
+// GetFullKey() to do so.
+// The [Object] should contain the versioning if you are
+// maintaining such a functionality.
+//
+// Parameters:
+//   - args[0] - the key string
+//   - args[1] - the [versioned.Object] JSON value, e.g.:
+//     {"Version":1,"Timestamp":"2023-05-13T00:50:03.889192694Z",
+//     "Data":"bm90IHVwZ3JhZGVk"}
+//
+// Returns a promise:
+//   - Rejected with an access error.
+func (r *RemoteKV) Set(_ js.Value, args []js.Value) any {
+	key := args[0].String()
+	value := utils.CopyBytesToGo(args[1])
+
+	promiseFn := func(resolve, reject func(args ...any) js.Value) {
+		err := r.api.Set(key, value)
+		if err != nil {
+			reject(utils.JsTrace(err))
+		} else {
+			resolve()
+		}
+	}
+
+	return utils.CreatePromise(promiseFn)
+}
+
+// GetPrefix returns the full Prefix of the KV
+// Returns a string via a Promise
+func (r *RemoteKV) GetPrefix(_ js.Value, args []js.Value) any {
+	promiseFn := func(resolve, reject func(args ...any) js.Value) {
+		prefix := r.api.GetPrefix()
+		resolve(prefix)
+	}
+
+	return utils.CreatePromise(promiseFn)
+}
+
+// HasPrefix returns whether this prefix exists in the KV
+//
+// Parameters:
+//   - args[0] - the prefix string to check for.
+//
+// Returns a bool via a promise.
+func (r *RemoteKV) HasPrefix(_ js.Value, args []js.Value) any {
+	prefix := args[0].String()
+	promiseFn := func(resolve, reject func(args ...any) js.Value) {
+		resolve(r.api.HasPrefix(prefix))
+	}
+
+	return utils.CreatePromise(promiseFn)
+}
+
+// Prefix returns a new KV with the new prefix appending
+//
+// Parameters:
+//   - args[0] - the prefix to append to the list of prefixes
+//
+// Returns a promise:
+//   - Resolves to a new RemoteKV
+//   - Rejected with an error.
+func (r *RemoteKV) Prefix(_ js.Value, args []js.Value) any {
+	prefix := args[0].String()
+
+	promiseFn := func(resolve, reject func(args ...any) js.Value) {
+		newAPI, err := r.api.Prefix(prefix)
+
+		if err != nil {
+			reject(utils.JsTrace(err))
+		} else {
+			resolve(newRemoteKvJS(newAPI))
+		}
+	}
+
+	return utils.CreatePromise(promiseFn)
+}
+
+// Root returns the KV with no prefixes
+func (r *RemoteKV) Root(_ js.Value, args []js.Value) any {
+	promiseFn := func(resolve, reject func(args ...any) js.Value) {
+		newAPI, err := r.api.Root()
+
+		if err != nil {
+			reject(utils.JsTrace(err))
+		} else {
+			resolve(newRemoteKvJS(newAPI))
+		}
+	}
+
+	return utils.CreatePromise(promiseFn)
+}
+
+// IsMemStore returns true if the underlying KV is memory based
+func (r *RemoteKV) IsMemStore(_ js.Value, args []js.Value) any {
+	promiseFn := func(resolve, reject func(args ...any) js.Value) {
+		resolve(r.api.IsMemStore())
+	}
+
+	return utils.CreatePromise(promiseFn)
+}
+
+// GetFullKey returns the key with all prefixes appended
+func (r *RemoteKV) GetFullKey(_ js.Value, args []js.Value) any {
+	key := args[0].String()
+	version := int64(args[1].Int())
+
+	promiseFn := func(resolve, reject func(args ...any) js.Value) {
+		fullKey := r.api.GetFullKey(key, version)
+		resolve(fullKey)
+	}
+
+	return utils.CreatePromise(promiseFn)
+}
+
+// Transaction locks a key while it is being mutated then stores the result
+// and returns the old value and if it existed in a JSON object.
+// Transactions cannot be remote operations
+// If the op returns an error, the operation will be aborted.
+func (r *RemoteKV) Transaction(_ js.Value, args []js.Value) any {
+	promiseFn := func(resolve, reject func(args ...any) js.Value) {
+		reject("unimplemented")
+	}
+
+	return utils.CreatePromise(promiseFn)
+}
+
+// StoreMapElement stores a versioned map element into the KV. This relies
+// on the underlying remote [KV.StoreMapElement] function to lock and control
+// updates, but it uses [versioned.Object] values.
+// All Map storage functions update the remote.
+// valueJSON is a json of a versioned.Object
+//
+// Parameters:
+//   - args[0] - the mapName string
+//   - args[1] - the elementKey string
+//   - args[2] - the [versioned.Object] JSON value, e.g.:
+//     {"Version":1,"Timestamp":"2023-05-13T00:50:03.889192694Z",
+//     "Data":"bm90IHVwZ3JhZGVk"}
+//   - args[3] - the version int
+//
+// Returns a promise with an error if any
+func (r *RemoteKV) StoreMapElement(_ js.Value, args []js.Value) any {
+	mapName := args[0].String()
+	elementKey := args[1].String()
+	val := utils.CopyBytesToGo(args[2])
+	version := int64(args[3].Int())
+
+	promiseFn := func(resolve, reject func(args ...any) js.Value) {
+		err := r.api.StoreMapElement(mapName, elementKey, val, version)
+		if err != nil {
+			reject(utils.JsTrace(err))
+		} else {
+			resolve()
+		}
+	}
+
+	return utils.CreatePromise(promiseFn)
+}
+
+// StoreMap saves a versioned map element into the KV. This relies
+// on the underlying remote [KV.StoreMap] function to lock and control
+// updates, but it uses [versioned.Object] values.
+// All Map storage functions update the remote.
+// valueJSON is a json of map[string]*versioned.Object
+//
+// Parameters:
+//   - args[0] - the mapName string
+//   - args[1] - the [map[string]versioned.Object] JSON value, e.g.:
+//     {"elementKey": {"Version":1,"Timestamp":"2023-05-13T00:50:03.889192694Z",
+//     "Data":"bm90IHVwZ3JhZGVk"}}
+//   - args[2] - the version int
+//
+// Returns a promise with an error if any
+func (r *RemoteKV) StoreMap(_ js.Value, args []js.Value) any {
+	mapName := args[0].String()
+	val := utils.CopyBytesToGo(args[1])
+	version := int64(args[2].Int())
+
+	promiseFn := func(resolve, reject func(args ...any) js.Value) {
+		err := r.api.StoreMap(mapName, val, version)
+		if err != nil {
+			reject(utils.JsTrace(err))
+		} else {
+			resolve()
+		}
+	}
+
+	return utils.CreatePromise(promiseFn)
+}
+
+// DeleteMapElement removes a versioned map element from the KV.
+//
+// Parameters:
+//   - args[0] - the mapName string
+//   - args[1] - the elementKey string
+//   - args[2] - the version int
+//
+// Returns a promise with an error if any or the json of the deleted
+// [versioned.Object], e.g.:
+//
+//	{"Version":1,"Timestamp":"2023-05-13T00:50:03.889192694Z",
+//	"Data":"bm90IHVwZ3JhZGVk"}
+func (r *RemoteKV) DeleteMapElement(_ js.Value, args []js.Value) any {
+	mapName := args[0].String()
+	elementKey := args[1].String()
+	version := int64(args[2].Int())
+
+	promiseFn := func(resolve, reject func(args ...any) js.Value) {
+		deleted, err := r.api.DeleteMapElement(mapName, elementKey,
+			version)
+		if err != nil {
+			reject(utils.JsTrace(err))
+		} else {
+			resolve(utils.CopyBytesToJS(deleted))
+		}
+	}
+
+	return utils.CreatePromise(promiseFn)
+}
+
+// GetMap loads a versioned map from the KV. This relies
+// on the underlying remote [KV.GetMap] function to lock and control
+// updates, but it uses [versioned.Object] values.
+//
+// Parameters:
+//   - args[0] - the mapName string
+//   - args[1] - the version int
+//
+// Returns a promise with an error if any or the
+// the [map[string]versioned.Object] JSON value, e.g.:
+//
+//	{"elementKey": {"Version":1,"Timestamp":"2023-05-13T00:50:03.889192694Z",
+//	"Data":"bm90IHVwZ3JhZGVk"}}
+func (r *RemoteKV) GetMap(_ js.Value, args []js.Value) any {
+	mapName := args[0].String()
+	version := int64(args[1].Int())
+
+	promiseFn := func(resolve, reject func(args ...any) js.Value) {
+		mapJSON, err := r.api.GetMap(mapName, version)
+		if err != nil {
+			reject(utils.JsTrace(err))
+		} else {
+			resolve(utils.CopyBytesToJS(mapJSON))
+		}
+	}
+
+	return utils.CreatePromise(promiseFn)
+}
+
+// GetMapElement loads a versioned map element from the KV. This relies
+// on the underlying remote [KV.GetMapElement] function to lock and control
+// updates, but it uses [versioned.Object] values.
+// Parameters:
+//   - args[0] - the mapName string
+//   - args[1] - the elementKey string
+//   - args[2] - the version int
+//
+// Returns a promise with an error if any or the json of the
+// [versioned.Object], e.g.:
+//
+//	{"Version":1,"Timestamp":"2023-05-13T00:50:03.889192694Z",
+//	"Data":"bm90IHVwZ3JhZGVk"}
+func (r *RemoteKV) GetMapElement(_ js.Value, args []js.Value) any {
+	mapName := args[0].String()
+	elementKey := args[1].String()
+	version := int64(args[2].Int())
+
+	promiseFn := func(resolve, reject func(args ...any) js.Value) {
+		deleted, err := r.api.GetMapElement(mapName, elementKey,
+			version)
+		if err != nil {
+			reject(utils.JsTrace(err))
+		} else {
+			resolve(utils.CopyBytesToJS(deleted))
+		}
+	}
+
+	return utils.CreatePromise(promiseFn)
+}
+
+// ListenOnRemoteKey sets up a callback listener for the object specified
+// by the key and version. It returns the current [versioned.Object] JSON
+// of the value.
+// Parameters:
+//   - args[0] - the key string
+//   - args[1] - the version int
+//   - args[2] - the [KeyChangedByRemoteCallback] javascript callback
+//
+// Returns a promise with an error if any or the json of the existing
+// [versioned.Object], e.g.:
+//
+//	{"Version":1,"Timestamp":"2023-05-13T00:50:03.889192694Z",
+//	"Data":"bm90IHVwZ3JhZGVk"}
+func (r *RemoteKV) ListenOnRemoteKey(_ js.Value, args []js.Value) any {
+	key := args[0].String()
+	version := int64(args[1].Int())
+	cb := newKeyChangedByRemoteCallback(args[2])
+
+	promiseFn := func(resolve, reject func(args ...any) js.Value) {
+		deleted, err := r.api.ListenOnRemoteKey(key, version, cb)
+		if err != nil {
+			reject(utils.JsTrace(err))
+		} else {
+			resolve(utils.CopyBytesToJS(deleted))
+		}
+	}
+
+	return utils.CreatePromise(promiseFn)
+}
+
+// ListenOnRemoteMap allows the caller to receive updates when
+// the map or map elements are updated. Returns a JSON of
+// map[string]versioned.Object of the current map value.
+// Parameters:
+//   - args[0] - the mapName string
+//   - args[1] - the version int
+//   - args[2] - the [MapChangedByRemoteCallback] javascript callback
+//
+// Returns a promise with an error if any or the json of the existing
+// the [map[string]versioned.Object] JSON value, e.g.:
+//
+//	{"elementKey": {"Version":1,"Timestamp":"2023-05-13T00:50:03.889192694Z",
+//	"Data":"bm90IHVwZ3JhZGVk"}}
+func (r *RemoteKV) ListenOnRemoteMap(_ js.Value, args []js.Value) any {
+	mapName := args[0].String()
+	version := int64(args[1].Int())
+	cb := newMapChangedByRemoteCallback(args[2])
+
+	promiseFn := func(resolve, reject func(args ...any) js.Value) {
+		deleted, err := r.api.ListenOnRemoteMap(mapName, version, cb)
+		if err != nil {
+			reject(utils.JsTrace(err))
+		} else {
+			resolve(utils.CopyBytesToJS(deleted))
+		}
+	}
+
+	return utils.CreatePromise(promiseFn)
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// RemoteStore                                                                //
+////////////////////////////////////////////////////////////////////////////////
+
+// RemoteStore wraps Javascript callbacks to adhere to the
+// [bindings.RemoteStore] interface.
+type RemoteStore struct {
+	read            func(args ...any) js.Value
+	write           func(args ...any) js.Value
+	getLastModified func(args ...any) js.Value
+	getLastWrite    func(args ...any) js.Value
+	readDir         func(args ...any) js.Value
+}
+
+// newRemoteStoreCallbacks maps the functions of the Javascript object matching
+// [bindings.RemoteStore] to a RemoteStoreCallbacks.
+func newRemoteStore(arg js.Value) *RemoteStore {
+	return &RemoteStore{
+		read:            utils.WrapCB(arg, "Read"),
+		write:           utils.WrapCB(arg, "Write"),
+		getLastModified: utils.WrapCB(arg, "GetLastModified"),
+		getLastWrite:    utils.WrapCB(arg, "GetLastWrite"),
+		readDir:         utils.WrapCB(arg, "ReadDir"),
+	}
+}
+
+// Read impelements [bindings.RemoteStore.Read]
+//
+// Parameters:
+//   - path - The file path to read from (string).
+//
+// Returns:
+//   - The file data (Uint8Array).
+//   - Catches any thrown errors (of type Error) and returns it as an error.
+func (rsCB *RemoteStore) Read(path string) ([]byte, error) {
+
+	fn := func() js.Value { return rsCB.read(path) }
+	v, err := utils.RunAndCatch(fn)
+	if err != nil {
+		return nil, err
+	}
+	return utils.CopyBytesToGo(v), err
+}
+
+// Write implements [bindings.RemoteStore.Write]
+//
+// Parameters:
+//   - path - The file path to write to (string).
+//   - data - The file data to write (Uint8Array).
+//
+// Returns:
+//   - Catches any thrown errors (of type Error) and returns it as an error.
+func (rsCB *RemoteStore) Write(path string, data []byte) error {
+	fn := func() js.Value { return rsCB.write(path, utils.CopyBytesToJS(data)) }
+	_, err := utils.RunAndCatch(fn)
+	return err
+}
+
+// GetLastModified implements [bindings.RemoteStore.GetLastModified]
+//
+// Parameters:
+//   - path - The file path (string).
+//
+// Returns:
+//   - JSON of [bindings.RemoteStoreReport] (Uint8Array).
+//   - Catches any thrown errors (of type Error) and returns it as an error.
+func (rsCB *RemoteStore) GetLastModified(path string) ([]byte, error) {
+	fn := func() js.Value { return rsCB.getLastModified(path) }
+	v, err := utils.RunAndCatch(fn)
+	if err != nil {
+		return nil, err
+	}
+	return utils.CopyBytesToGo(v), err
+}
+
+// GetLastWrite implements [bindings.RemoteStore.GetLastWrite()
+//
+// Returns:
+//   - JSON of [bindings.RemoteStoreReport] (Uint8Array).
+//   - Catches any thrown errors (of type Error) and returns it as an error.
+func (rsCB *RemoteStore) GetLastWrite() ([]byte, error) {
+	fn := func() js.Value { return rsCB.getLastWrite() }
+	v, err := utils.RunAndCatch(fn)
+	if err != nil {
+		return nil, err
+	}
+	return utils.CopyBytesToGo(v), err
+}
+
+// ReadDir implements [bindings.RemoteStore.ReadDir]
+//
+// Parameters:
+//   - path - The file path (string).
+//
+// Returns:
+//   - JSON of []string (Uint8Array).
+//   - Catches any thrown errors (of type Error) and returns it as an error.
+func (rsCB *RemoteStore) ReadDir(path string) ([]byte, error) {
+	fn := func() js.Value { return rsCB.readDir(path) }
+	v, err := utils.RunAndCatch(fn)
+	if err != nil {
+		return nil, err
+	}
+	return utils.CopyBytesToGo(v), err
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Callbacks                                                                  //
+////////////////////////////////////////////////////////////////////////////////
+
+// KeyChangedByRemoteCallback wraps the passed javascript function and
+// implements [bindings.KeyChangedByRemoteCallback]
+type KeyChangedByRemoteCallback struct {
+	callback func(args ...any) js.Value
+}
+
+func (k *KeyChangedByRemoteCallback) Callback(key string, old, new []byte,
+	opType int8) {
+	k.callback(key, utils.CopyBytesToJS(old), utils.CopyBytesToJS(new),
+		opType)
+}
+
+func newKeyChangedByRemoteCallback(
+	jsFunc js.Value) *KeyChangedByRemoteCallback {
+	return &KeyChangedByRemoteCallback{
+		callback: utils.WrapCB(jsFunc, "Callback"),
+	}
+}
+
+// MapChangedByRemoteCallback wraps the passed javascript function and
+// implements [bindings.KeyChangedByRemoteCallback]
+type MapChangedByRemoteCallback struct {
+	callback func(args ...any) js.Value
+}
+
+func (m *MapChangedByRemoteCallback) Callback(mapName string,
+	editsJSON []byte) {
+	m.callback(mapName, utils.CopyBytesToJS(editsJSON))
+}
+
+func newMapChangedByRemoteCallback(
+	jsFunc js.Value) *MapChangedByRemoteCallback {
+	return &MapChangedByRemoteCallback{
+		callback: utils.WrapCB(jsFunc, "Callback"),
+	}
+}
diff --git a/wasm/sync_test.go b/wasm/collective_test.go
similarity index 56%
rename from wasm/sync_test.go
rename to wasm/collective_test.go
index 4ab3b8abb818f63f6085a41905354f8b5617be73..8c512adce32127a2a448882167799e6268454fea 100644
--- a/wasm/sync_test.go
+++ b/wasm/collective_test.go
@@ -16,48 +16,6 @@ import (
 	"gitlab.com/elixxir/client/v4/bindings"
 )
 
-// Tests that the map representing RemoteStoreFileSystem returned by
-// newRemoteStoreFileSystemJS contains all of the methods on
-// RemoteStoreFileSystem.
-func Test_newRemoteStoreFileSystemJS(t *testing.T) {
-	rsfType := reflect.TypeOf(&RemoteStoreFileSystem{})
-
-	rsf := newRemoteStoreFileSystemJS(&bindings.RemoteStoreFileSystem{})
-	if len(rsf) != rsfType.NumMethod() {
-		t.Errorf("RemoteStoreFileSystem JS object does not have all methods."+
-			"\nexpected: %d\nreceived: %d", rsfType.NumMethod(), len(rsf))
-	}
-
-	for i := 0; i < rsfType.NumMethod(); i++ {
-		method := rsfType.Method(i)
-
-		if _, exists := rsf[method.Name]; !exists {
-			t.Errorf("Method %s does not exist.", method.Name)
-		}
-	}
-}
-
-// Tests that RemoteStoreFileSystem has all the methods that
-// [bindings.RemoteStoreFileSystem] has.
-func Test_RemoteStoreFileSystemMethods(t *testing.T) {
-	rsfType := reflect.TypeOf(&RemoteStoreFileSystem{})
-	binRsfType := reflect.TypeOf(&bindings.RemoteStoreFileSystem{})
-
-	if binRsfType.NumMethod() != rsfType.NumMethod() {
-		t.Errorf("WASM RemoteStoreFileSystem object does not have all methods "+
-			"from bindings.\nexpected: %d\nreceived: %d",
-			binRsfType.NumMethod(), rsfType.NumMethod())
-	}
-
-	for i := 0; i < binRsfType.NumMethod(); i++ {
-		method := binRsfType.Method(i)
-
-		if _, exists := rsfType.MethodByName(method.Name); !exists {
-			t.Errorf("Method %s does not exist.", method.Name)
-		}
-	}
-}
-
 // Tests that the map representing RemoteKV returned by newRemoteKvJS contains
 // all of the methods on RemoteKV.
 func Test_newRemoteKvJS(t *testing.T) {
diff --git a/wasm/sync.go b/wasm/sync.go
deleted file mode 100644
index c01d28e93c0404c027c4f7155ed8411aff9dcc39..0000000000000000000000000000000000000000
--- a/wasm/sync.go
+++ /dev/null
@@ -1,423 +0,0 @@
-////////////////////////////////////////////////////////////////////////////////
-// Copyright © 2022 xx foundation                                             //
-//                                                                            //
-// Use of this source code is governed by a license that can be found in the  //
-// LICENSE file.                                                              //
-////////////////////////////////////////////////////////////////////////////////
-
-//go:build js && wasm
-
-package wasm
-
-import (
-	"syscall/js"
-
-	"gitlab.com/elixxir/client/v4/bindings"
-	"gitlab.com/elixxir/xxdk-wasm/utils"
-)
-
-// TODO: add tests
-
-////////////////////////////////////////////////////////////////////////////////
-// Remote Storage Interface and Implementation(s)                             //
-////////////////////////////////////////////////////////////////////////////////
-
-// RemoteStoreFileSystem wraps the [bindings.RemoteStoreFileSystem] object so
-// its methods can be wrapped to be Javascript compatible.
-type RemoteStoreFileSystem struct {
-	api *bindings.RemoteStoreFileSystem
-}
-
-// newRemoteStoreFileSystemJS creates a new Javascript compatible object
-// (map[string]any) that matches the [RemoteStoreFileSystem] structure.
-func newRemoteStoreFileSystemJS(api *bindings.RemoteStoreFileSystem) map[string]any {
-	rsf := RemoteStoreFileSystem{api}
-	rsfMap := map[string]any{
-		"Read":            js.FuncOf(rsf.Read),
-		"Write":           js.FuncOf(rsf.Write),
-		"GetLastModified": js.FuncOf(rsf.GetLastModified),
-		"GetLastWrite":    js.FuncOf(rsf.GetLastWrite),
-	}
-
-	return rsfMap
-}
-
-// NewFileSystemRemoteStorage is a constructor for [RemoteStoreFileSystem].
-//
-// Parameters:
-//   - args[0] - The base directory that all file operations will be performed.
-//     It must contain a file delimiter (i.e., `/`) (string).
-//
-// Returns:
-//   - A Javascript representation of the [RemoteStoreFileSystem] object.
-func NewFileSystemRemoteStorage(_ js.Value, args []js.Value) any {
-	baseDir := args[0].String()
-	api := bindings.NewFileSystemRemoteStorage(baseDir)
-
-	return newRemoteStoreFileSystemJS(api)
-}
-
-// Read reads from the provided file path and returns the data at that path.
-// An error is returned if it failed to read the file.
-//
-// Parameters:
-//   - args[0] - The file path to read from (string).
-//
-// Returns a promise:
-//   - Resolves to the file data (Uint8Array)
-//   - Rejected with an error if reading from the file fails.
-func (rsf *RemoteStoreFileSystem) Read(_ js.Value, args []js.Value) any {
-	path := args[0].String()
-
-	promiseFn := func(resolve, reject func(args ...any) js.Value) {
-		data, err := rsf.api.Read(path)
-		if err != nil {
-			reject(utils.JsTrace(err))
-		} else {
-			resolve(utils.CopyBytesToJS(data))
-		}
-	}
-
-	return utils.CreatePromise(promiseFn)
-}
-
-// Write writes to the file path the provided data. An error is returned if it
-// fails to write to file.
-//
-// Parameters:
-//   - args[0] - The file path to write to (string).
-//   - args[1] - The file data to write (Uint8Array).
-//
-// Returns a promise:
-//   - Resolves on success (void).
-//   - Rejected with an error if writing to the file fails.
-func (rsf *RemoteStoreFileSystem) Write(_ js.Value, args []js.Value) any {
-	path := args[0].String()
-	data := utils.CopyBytesToGo(args[1])
-
-	promiseFn := func(resolve, reject func(args ...any) js.Value) {
-		err := rsf.api.Write(path, data)
-		if err != nil {
-			reject(utils.JsTrace(err))
-		} else {
-			resolve()
-		}
-	}
-
-	return utils.CreatePromise(promiseFn)
-}
-
-// GetLastModified returns when the file at the given file path was last
-// modified. If the implementation that adheres to this interface does not
-// support this, [Write] or [Read] should be implemented to either write a
-// separate timestamp file or add a prefix.
-//
-// Parameters:
-//   - args[0] - The file path (string).
-//
-// Returns a promise:
-//   - Resolves to the JSON of [bindings.RemoteStoreReport] (Uint8Array).
-//   - Rejected with an error on failure.
-func (rsf *RemoteStoreFileSystem) GetLastModified(_ js.Value, args []js.Value) any {
-	path := args[0].String()
-
-	promiseFn := func(resolve, reject func(args ...any) js.Value) {
-		report, err := rsf.api.GetLastModified(path)
-		if err != nil {
-			reject(utils.JsTrace(err))
-		} else {
-			resolve(utils.CopyBytesToJS(report))
-		}
-	}
-
-	return utils.CreatePromise(promiseFn)
-}
-
-// GetLastWrite retrieves the most recent successful write operation that was
-// received by [RemoteStoreFileSystem].
-//
-// Returns a promise:
-//   - Resolves to the JSON of [bindings.RemoteStoreReport] (Uint8Array).
-//   - Rejected with an error on failure.
-func (rsf *RemoteStoreFileSystem) GetLastWrite(js.Value, []js.Value) any {
-	promiseFn := func(resolve, reject func(args ...any) js.Value) {
-		report, err := rsf.api.GetLastWrite()
-		if err != nil {
-			reject(utils.JsTrace(err))
-		} else {
-			resolve(utils.CopyBytesToJS(report))
-		}
-	}
-
-	return utils.CreatePromise(promiseFn)
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// RemoteKV Methods                                                           //
-////////////////////////////////////////////////////////////////////////////////
-
-// RemoteKV wraps the [bindings.RemoteKV] object so its methods can be wrapped
-// to be Javascript compatible.
-type RemoteKV struct {
-	api *bindings.RemoteKV
-}
-
-// newRemoteKvJS creates a new Javascript compatible object (map[string]any)
-// that matches the [RemoteKV] structure.
-func newRemoteKvJS(api *bindings.RemoteKV) map[string]any {
-	rkv := RemoteKV{api}
-	rkvMap := map[string]any{
-		"Write":   js.FuncOf(rkv.Write),
-		"Read":    js.FuncOf(rkv.Read),
-		"GetList": js.FuncOf(rkv.GetList),
-	}
-
-	return rkvMap
-}
-
-// NewOrLoadSyncRemoteKV constructs a [RemoteKV].
-//
-// Parameters:
-//   - args[0] - ID of [E2e] object in tracker (int).
-//   - args[1] - A Javascript object that implements the functions on
-//     [RemoteKVCallbacks]. These will be the callbacks that are called for
-//     [bindings.RemoteStore] operations.
-//   - args[2] - A [RemoteStoreCallbacks]. This will be a structure the consumer
-//     implements. This acts as a wrapper around the remote storage API
-//     (e.g., Google Drive's API, DropBox's API, etc.).
-//
-// Returns a promise:
-//   - Resolves to a Javascript representation of the [RemoteKV] object.
-//   - Rejected with an error if initialising the remote KV fails.
-func NewOrLoadSyncRemoteKV(_ js.Value, args []js.Value) any {
-	e2eID := args[0].Int()
-	remoteKvCallbacks := newRemoteKVCallbacks(args[1])
-	remote := newRemoteStoreCallbacks(args[2])
-
-	promiseFn := func(resolve, reject func(args ...any) js.Value) {
-		api, err :=
-			bindings.NewOrLoadSyncRemoteKV(e2eID, remoteKvCallbacks, remote)
-		if err != nil {
-			reject(utils.JsTrace(err))
-		} else {
-			resolve(newRemoteKvJS(api))
-		}
-	}
-
-	return utils.CreatePromise(promiseFn)
-}
-
-// Write writes a transaction to the remote and local store.
-//
-// Parameters:
-//   - args[0] - The key that this data will be written to (i.e., the device
-//     name, the channel name, etc.). Certain keys should follow a pattern and
-//     contain special characters (see [RemoteKV.GetList] for details) (string).
-//   - args[1] - The data that will be stored (i.e., state data) (Uint8Array).
-//   - args[2] - A Javascript object that implements the functions on
-//     [RemoteKVCallbacks]. This may be nil if you do not care about the network
-//     report.
-//
-// Returns a promise:
-//   - Resolves on success (void).
-//   - Rejected with an error if writing to the file fails.
-func (rkv *RemoteKV) Write(_ js.Value, args []js.Value) any {
-	path := args[0].String()
-	data := utils.CopyBytesToGo(args[1])
-	cb := newRemoteKVCallbacks(args[1])
-
-	promiseFn := func(resolve, reject func(args ...any) js.Value) {
-		err := rkv.api.Write(path, data, cb)
-		if err != nil {
-			reject(utils.JsTrace(err))
-		} else {
-			resolve()
-		}
-	}
-
-	return utils.CreatePromise(promiseFn)
-}
-
-// Read retrieves the data stored in the underlying KV. Returns an error if the
-// data at this key cannot be retrieved.
-//
-// Parameters:
-//   - args[0] - The path that this data will be written to (i.e., the device
-//     name) (string).
-//
-// Returns a promise:
-//   - Resolves to the file data (Uint8Array)
-//   - Rejected with an error if reading from the file fails.
-func (rkv *RemoteKV) Read(_ js.Value, args []js.Value) any {
-	path := args[0].String()
-
-	promiseFn := func(resolve, reject func(args ...any) js.Value) {
-		data, err := rkv.api.Read(path)
-		if err != nil {
-			reject(utils.JsTrace(err))
-		} else {
-			resolve(utils.CopyBytesToJS(data))
-		}
-	}
-
-	return utils.CreatePromise(promiseFn)
-}
-
-// GetList returns all entries for a path (or key) that contain the name
-// parameter from the local store.
-//
-// For example, assuming the usage of the [sync.LocalStoreKeyDelimiter], if both
-// "channels-123" and "channels-abc" are written to [RemoteKV], then
-// GetList("channels") will retrieve the data for both channels. All data that
-// contains no [sync.LocalStoreKeyDelimiter] can be retrieved using GetList("").
-//
-// Parameters:
-//   - args[0] - Some prefix to a Write operation. If no prefix applies, simply
-//     use the empty string. (string).
-//
-// Returns:
-//   - The file data (Uint8Array)
-//   - Throws a TypeError if getting the list fails.
-func (rkv *RemoteKV) GetList(_ js.Value, args []js.Value) any {
-	name := args[0].String()
-
-	data, err := rkv.api.GetList(name)
-	if err != nil {
-		utils.Throw(utils.TypeError, err)
-		return nil
-	}
-
-	return utils.CopyBytesToJS(data)
-}
-
-// RemoteStoreCallbacks wraps Javascript callbacks to adhere to the
-// [bindings.RemoteStore] interface.
-type RemoteStoreCallbacks struct {
-	read            func(args ...any) js.Value
-	write           func(args ...any) js.Value
-	getLastModified func(args ...any) js.Value
-	getLastWrite    func(args ...any) js.Value
-}
-
-// newRemoteStoreCallbacks maps the functions of the Javascript object matching
-// [bindings.RemoteStore] to a RemoteStoreCallbacks.
-func newRemoteStoreCallbacks(arg js.Value) *RemoteStoreCallbacks {
-	return &RemoteStoreCallbacks{
-		read:            utils.WrapCB(arg, "Read"),
-		write:           utils.WrapCB(arg, "Write"),
-		getLastModified: utils.WrapCB(arg, "GetLastModified"),
-		getLastWrite:    utils.WrapCB(arg, "GetLastWrite"),
-	}
-}
-
-// Read reads from the provided file path and returns the data at that path.
-// An error is returned if it failed to read the file.
-//
-// Parameters:
-//   - path - The file path to read from (string).
-//
-// Returns:
-//   - The file data (Uint8Array).
-//   - Catches any thrown errors (of type Error) and returns it as an error.
-func (rsCB *RemoteStoreCallbacks) Read(path string) ([]byte, error) {
-
-	fn := func() js.Value { return rsCB.read(path) }
-	v, err := utils.RunAndCatch(fn)
-	if err != nil {
-		return nil, err
-	}
-	return utils.CopyBytesToGo(v), err
-}
-
-// Write writes to the file path the provided data. An error is returned if it
-// fails to write to file.
-//
-// Parameters:
-//   - path - The file path to write to (string).
-//   - data - The file data to write (Uint8Array).
-//
-// Returns:
-//   - Catches any thrown errors (of type Error) and returns it as an error.
-func (rsCB *RemoteStoreCallbacks) Write(path string, data []byte) error {
-	fn := func() js.Value { return rsCB.write(path, utils.CopyBytesToJS(data)) }
-	_, err := utils.RunAndCatch(fn)
-	return err
-}
-
-// GetLastModified returns when the file at the given file path was last
-// modified. If the implementation that adheres to this interface does not
-// support this, [Write] or [Read] should be implemented to either write a
-// separate timestamp file or add a prefix.
-//
-// Parameters:
-//   - path - The file path (string).
-//
-// Returns:
-//   - JSON of [bindings.RemoteStoreReport] (Uint8Array).
-//   - Catches any thrown errors (of type Error) and returns it as an error.
-func (rsCB *RemoteStoreCallbacks) GetLastModified(path string) ([]byte, error) {
-	fn := func() js.Value { return rsCB.getLastModified(path) }
-	v, err := utils.RunAndCatch(fn)
-	if err != nil {
-		return nil, err
-	}
-	return utils.CopyBytesToGo(v), err
-}
-
-// GetLastWrite retrieves the most recent successful write operation that was
-// received by [RemoteStoreFileSystem].
-//
-// Returns:
-//   - JSON of [bindings.RemoteStoreReport] (Uint8Array).
-//   - Catches any thrown errors (of type Error) and returns it as an error.
-func (rsCB *RemoteStoreCallbacks) GetLastWrite() ([]byte, error) {
-	fn := func() js.Value { return rsCB.getLastWrite() }
-	v, err := utils.RunAndCatch(fn)
-	if err != nil {
-		return nil, err
-	}
-	return utils.CopyBytesToGo(v), err
-}
-
-// RemoteKVCallbacks wraps Javascript callbacks to adhere to the
-// [bindings.RemoteKVCallbacks] interface.
-type RemoteKVCallbacks struct {
-	keyUpdated        func(args ...any) js.Value
-	remoteStoreResult func(args ...any) js.Value
-}
-
-// newRemoteKVCallbacks maps the functions of the Javascript object matching
-// [bindings.RemoteKVCallbacks] to a RemoteKVCallbacks.
-func newRemoteKVCallbacks(arg js.Value) *RemoteKVCallbacks {
-	return &RemoteKVCallbacks{
-		keyUpdated:        utils.WrapCB(arg, "KeyUpdated"),
-		remoteStoreResult: utils.WrapCB(arg, "RemoteStoreResult"),
-	}
-}
-
-// KeyUpdated is the callback to be called any time a key is updated by another
-// device tracked by the [RemoteKV] store.
-//
-// Parameters:
-//   - key - (string).
-//   - oldVal - (Uint8Array).
-//   - newVal - (Uint8Array).
-//   - updated - (Boolean)
-func (rkvCB *RemoteKVCallbacks) KeyUpdated(
-	key string, oldVal, newVal []byte, updated bool) {
-	rkvCB.keyUpdated(
-		key, utils.CopyBytesToJS(oldVal), utils.CopyBytesToJS(newVal), updated)
-}
-
-// RemoteStoreResult is called to report network save results after the key has
-// been updated locally.
-//
-// NOTE: Errors originate from the authentication and writing code in regard to
-// remote which is handled by the user of this API. As a result, this callback
-// provides no information in simple implementations.
-//
-// Parameters:
-//   - remoteStoreReport - JSON of [bindings.RemoteStoreReport] (Uint8Array).
-func (rkvCB *RemoteKVCallbacks) RemoteStoreResult(remoteStoreReport []byte) {
-	rkvCB.remoteStoreResult(utils.CopyBytesToJS(remoteStoreReport))
-}