diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..8551215fa5b651bb93a247cde5ab89d4b8f5f7e6
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+Copyright (c) 2022, xx network SEZC
+
+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.
+
+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 OWNER 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.
diff --git a/go.mod b/go.mod
index f01654c7e9c5fabf4c1f488a6cebc2c8ca4b06bb..78bf2b9a85d6e53a78738b3bfc657ac984cda9fc 100644
--- a/go.mod
+++ b/go.mod
@@ -3,59 +3,46 @@ module gitlab.com/elixxir/xxdk-wasm
 go 1.17
 
 require (
+	github.com/hack-pad/go-indexeddb v0.2.0
 	github.com/pkg/errors v0.9.1
 	github.com/spf13/jwalterweatherman v1.1.0
-	gitlab.com/elixxir/client v1.5.1-0.20220908221453-a8b65cb24385
+	gitlab.com/elixxir/client v1.5.1-0.20220908211108-28a1e0f44f0b
+	gitlab.com/elixxir/crypto v0.0.7-0.20220902165412-5c5e3e990e84
+	gitlab.com/xx_network/primitives v0.0.4-0.20220809193445-9fc0a5209548
 )
 
 require (
-	git.xx.network/elixxir/grpc-web-go-client v0.0.0-20220908162406-67a330f8f67a // indirect
+	git.xx.network/elixxir/grpc-web-go-client v0.0.0-20220829220442-4f51c27ab822 // indirect
 	github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect
 	github.com/badoux/checkmail v1.2.1 // indirect
 	github.com/cenkalti/backoff/v4 v4.1.3 // indirect
 	github.com/cloudflare/circl v1.2.0 // indirect
 	github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect
 	github.com/elliotchance/orderedmap v1.4.0 // indirect
-	github.com/fsnotify/fsnotify v1.5.4 // indirect
 	github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 // indirect
 	github.com/golang/protobuf v1.5.2 // indirect
 	github.com/gorilla/websocket v1.5.0 // indirect
-	github.com/hashicorp/hcl v1.0.0 // indirect
 	github.com/improbable-eng/grpc-web v0.15.0 // indirect
-	github.com/inconshreveable/mousetrap v1.0.0 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
 	github.com/klauspost/compress v1.11.7 // indirect
 	github.com/klauspost/cpuid/v2 v2.1.0 // indirect
 	github.com/ktr0731/grpc-web-go-client v0.2.8 // indirect
-	github.com/magiconair/properties v1.8.6 // indirect
 	github.com/mattn/go-isatty v0.0.14 // indirect
 	github.com/mitchellh/go-homedir v1.1.0 // indirect
-	github.com/mitchellh/mapstructure v1.5.0 // indirect
 	github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
-	github.com/pelletier/go-toml v1.9.5 // indirect
-	github.com/pelletier/go-toml/v2 v2.0.2 // indirect
-	github.com/pkg/profile v1.6.0 // indirect
 	github.com/rs/cors v1.8.2 // indirect
 	github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
 	github.com/soheilhy/cmux v0.1.5 // indirect
-	github.com/spf13/afero v1.9.2 // indirect
-	github.com/spf13/cast v1.5.0 // indirect
-	github.com/spf13/cobra v1.5.0 // indirect
-	github.com/spf13/pflag v1.0.5 // indirect
-	github.com/spf13/viper v1.12.0 // indirect
-	github.com/subosito/gotenv v1.4.0 // indirect
 	github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2 // indirect
 	github.com/ttacon/libphonenumber v1.2.1 // indirect
 	github.com/tyler-smith/go-bip39 v1.1.0 // indirect
 	github.com/zeebo/blake3 v0.2.3 // indirect
 	gitlab.com/elixxir/bloomfilter v0.0.0-20211222005329-7d931ceead6f // indirect
-	gitlab.com/elixxir/comms v0.0.4-0.20220908220232-4755ca5e8bcc // indirect
-	gitlab.com/elixxir/crypto v0.0.7-0.20220902165412-5c5e3e990e84 // indirect
+	gitlab.com/elixxir/comms v0.0.4-0.20220907184530-d8eec143a1e8 // indirect
 	gitlab.com/elixxir/ekv v0.2.1-0.20220901224437-ab4cbf94bf8b // indirect
 	gitlab.com/elixxir/primitives v0.0.3-0.20220901220638-1acc75fabdc6 // indirect
-	gitlab.com/xx_network/comms v0.0.4-0.20220908215521-17222b8efc87 // indirect
+	gitlab.com/xx_network/comms v0.0.4-0.20220902164216-e3272eb0efac // indirect
 	gitlab.com/xx_network/crypto v0.0.5-0.20220902182733-69aad094b487 // indirect
-	gitlab.com/xx_network/primitives v0.0.4-0.20220809193445-9fc0a5209548 // indirect
 	gitlab.com/xx_network/ring v0.0.3-0.20220222211904-da613960ad93 // indirect
 	go.uber.org/atomic v1.10.0 // indirect
 	go.uber.org/ratelimit v0.2.0 // indirect
@@ -66,8 +53,5 @@ require (
 	google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc // indirect
 	google.golang.org/grpc v1.49.0 // indirect
 	google.golang.org/protobuf v1.28.1 // indirect
-	gopkg.in/ini.v1 v1.66.6 // indirect
-	gopkg.in/yaml.v2 v2.4.0 // indirect
-	gopkg.in/yaml.v3 v3.0.1 // indirect
 	nhooyr.io/websocket v1.8.7 // indirect
 )
diff --git a/go.sum b/go.sum
index 59d50fece8bdf87e632ed0ffd16d0f9412b20e3b..a89e721bb66ef9ec416bf570cc0812e1d35dbd1c 100644
--- a/go.sum
+++ b/go.sum
@@ -56,8 +56,6 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 git.xx.network/elixxir/grpc-web-go-client v0.0.0-20220829220442-4f51c27ab822 h1:s2Lxh7KcUbACkGKI2SqaGTeaKdO6GCtfU6/F12Q3Hp4=
 git.xx.network/elixxir/grpc-web-go-client v0.0.0-20220829220442-4f51c27ab822/go.mod h1:GrZ4Fy3YfaNe7RLnai+H+jE+fwqFA90tVmYOpKK90Yg=
-git.xx.network/elixxir/grpc-web-go-client v0.0.0-20220908162406-67a330f8f67a h1:qR2z2D4h8S7ks3Mn2y+V/WltXnYhaZH+DZlkn3IuY8A=
-git.xx.network/elixxir/grpc-web-go-client v0.0.0-20220908162406-67a330f8f67a/go.mod h1:D5gtpA4jngKUZQjEyua+HLIFteqYTC9b1IJe4QXPmv4=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
@@ -166,7 +164,6 @@ github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2
 github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
 github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
@@ -302,6 +299,8 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaD
 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
 github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
+github.com/hack-pad/go-indexeddb v0.2.0 h1:QHDM6gLrtCJvHdHUK8UdibJu4xWQlIDs4+l8L65AUdA=
+github.com/hack-pad/go-indexeddb v0.2.0/go.mod h1:NH8CaojufPNcKYDhy5JkjfyBXE/72oJPeiywlabN/lM=
 github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
 github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0=
 github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
@@ -330,7 +329,6 @@ github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA
 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
-github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
 github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
 github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
@@ -348,7 +346,6 @@ github.com/improbable-eng/grpc-web v0.12.0/go.mod h1:6hRR09jOEG81ADP5wCQju1z71g6
 github.com/improbable-eng/grpc-web v0.14.1/go.mod h1:zEjGHa8DAlkoOXmswrNvhUGEYQA9UI7DhrGeHR1DMGU=
 github.com/improbable-eng/grpc-web v0.15.0 h1:BN+7z6uNXZ1tQGcNAuaU1YjsLTApzkjt2tzCixLaUPQ=
 github.com/improbable-eng/grpc-web v0.15.0/go.mod h1:1sy9HKV4Jt9aEs9JSnkWlRJPuPtwNr0l57L4f878wP8=
-github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
 github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
@@ -404,7 +401,6 @@ github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0U
 github.com/liyue201/goqr v0.0.0-20200803022322-df443203d4ea h1:uyJ13zfy6l79CM3HnVhDalIyZ4RJAyVfDrbnfFeJoC4=
 github.com/liyue201/goqr v0.0.0-20200803022322-df443203d4ea/go.mod h1:w4pGU9PkiX2hAWyF0yuHEHmYTQFAd6WHzp6+IY7JVjE=
 github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
-github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
 github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
 github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
 github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
@@ -437,7 +433,6 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu
 github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
-github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
 github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
 github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -483,10 +478,8 @@ github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIw
 github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
 github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
 github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
-github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
 github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
 github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
-github.com/pelletier/go-toml/v2 v2.0.2 h1:+jQXlF3scKIcSEKkdHzXhCTDLPFi5r1wnK6yPS+49Gw=
 github.com/pelletier/go-toml/v2 v2.0.2/go.mod h1:MovirKjgVRESsAvNZlAjtFwV867yGuwRkXbG66OzopI=
 github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
 github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
@@ -496,7 +489,6 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
-github.com/pkg/profile v1.6.0 h1:hUDfIISABYI59DyeB3OTay/HxSRwTQ8rB/H83k6r5dM=
 github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18=
 github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -561,19 +553,14 @@ github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJ
 github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo=
-github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw=
 github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
-github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
 github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
 github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
-github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
 github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
 github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
 github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
 github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
-github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ=
 github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI=
 github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
 github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
@@ -592,7 +579,6 @@ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1F
 github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs=
-github.com/subosito/gotenv v1.4.0 h1:yAzM1+SmVcz5R4tXGsNMu1jUl2aOJXoiWUCEwwnGrvs=
 github.com/subosito/gotenv v1.4.0/go.mod h1:mZd6rFysKEcUhUHXJk0C/08wAgyDBFuwEYL7vWWGaGo=
 github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
 github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2 h1:5u+EJUQiosu3JFX0XS0qTf5FznsMOzTjGqavBGuCbo0=
@@ -627,14 +613,10 @@ github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
 github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
 gitlab.com/elixxir/bloomfilter v0.0.0-20211222005329-7d931ceead6f h1:yXGvNBqzZwAhDYlSnxPRbgor6JWoOt1Z7s3z1O9JR40=
 gitlab.com/elixxir/bloomfilter v0.0.0-20211222005329-7d931ceead6f/go.mod h1:H6jztdm0k+wEV2QGK/KYA+MY9nj9Zzatux/qIvDDv3k=
-gitlab.com/elixxir/client v1.5.1-0.20220907184817-0409e48f068a h1:l7cNBeWqxN4x6sNKZmG7nPYaSqTcalRPFF1n+V/UGlE=
-gitlab.com/elixxir/client v1.5.1-0.20220907184817-0409e48f068a/go.mod h1:HvISFfNXL/XCJ6ynSrEwG9kPEp14i87G5y4wyL1H8R0=
-gitlab.com/elixxir/client v1.5.1-0.20220908221453-a8b65cb24385 h1:fTpXiV6XOEzAZNiYEU8GJDNxMcjqZrGMRYyKWK9bfec=
-gitlab.com/elixxir/client v1.5.1-0.20220908221453-a8b65cb24385/go.mod h1:NNHMHtOHRAOgd6QinkRZoJTNN5j6lI8fDvvnuUEOcv0=
+gitlab.com/elixxir/client v1.5.1-0.20220908211108-28a1e0f44f0b h1:nc3bQH2AWGPyy2Vhy4Z4Nt/wyMZ3tM7TZkiotM6cqm8=
+gitlab.com/elixxir/client v1.5.1-0.20220908211108-28a1e0f44f0b/go.mod h1:HvISFfNXL/XCJ6ynSrEwG9kPEp14i87G5y4wyL1H8R0=
 gitlab.com/elixxir/comms v0.0.4-0.20220907184530-d8eec143a1e8 h1:xbJBQdMdB+mMsKIVXqx7eAWzZmulA3KdRhlbyAk0NIc=
 gitlab.com/elixxir/comms v0.0.4-0.20220907184530-d8eec143a1e8/go.mod h1:xE9NKMAxzvTXmyJ5BMjBZFWMPfvQpw3rB7be2VYvU8E=
-gitlab.com/elixxir/comms v0.0.4-0.20220908220232-4755ca5e8bcc h1:sD4o87jVG3zX47QDViHim3SR7NyvX/pJL6gqC7IVMrQ=
-gitlab.com/elixxir/comms v0.0.4-0.20220908220232-4755ca5e8bcc/go.mod h1:hCC0OOdJI/PQRs9jMCrxKcRmY0fL8Mu5iJGBhsmjHyU=
 gitlab.com/elixxir/crypto v0.0.0-20200804182833-984246dea2c4/go.mod h1:ucm9SFKJo+K0N2GwRRpaNr+tKXMIOVWzmyUD0SbOu2c=
 gitlab.com/elixxir/crypto v0.0.3/go.mod h1:ZNgBOblhYToR4m8tj4cMvJ9UsJAUKq+p0gCp07WQmhA=
 gitlab.com/elixxir/crypto v0.0.7-0.20220902165412-5c5e3e990e84 h1:sQCoZ+w09X/8LjQo9m7bg51IoX4AXSrXBtU8eblM/II=
@@ -652,8 +634,6 @@ gitlab.com/elixxir/primitives v0.0.3-0.20220901220638-1acc75fabdc6/go.mod h1:9Bb
 gitlab.com/xx_network/comms v0.0.0-20200805174823-841427dd5023/go.mod h1:owEcxTRl7gsoM8c3RQ5KAm5GstxrJp5tn+6JfQ4z5Hw=
 gitlab.com/xx_network/comms v0.0.4-0.20220902164216-e3272eb0efac h1:LitOitWXN/4VNXe4dfR7Kp0reqoYmduVaExrmT1pylg=
 gitlab.com/xx_network/comms v0.0.4-0.20220902164216-e3272eb0efac/go.mod h1:S5p4aZTz1rpN27E36U+nCQbqw6ZqQfnJNeFS54DnnJ0=
-gitlab.com/xx_network/comms v0.0.4-0.20220908215521-17222b8efc87 h1:6MuZhbmRkz3dQ+qLAIBKsVcOFk2KMOSCnBNYNKANrLE=
-gitlab.com/xx_network/comms v0.0.4-0.20220908215521-17222b8efc87/go.mod h1:lY9eVavkU2+nMP/eP2zgLrwO3w1gKZjSr7+cvWuJPmA=
 gitlab.com/xx_network/crypto v0.0.3/go.mod h1:DF2HYvvCw9wkBybXcXAgQMzX+MiGbFPjwt3t17VRqRE=
 gitlab.com/xx_network/crypto v0.0.4/go.mod h1:+lcQEy+Th4eswFgQDwT0EXKp4AXrlubxalwQFH5O0Mk=
 gitlab.com/xx_network/crypto v0.0.5-0.20220729193517-1e5e96f39f6e/go.mod h1:/SJf+R75E+QepdTLh0H1/udsovxx2Q5ru34q1v0umKk=
@@ -1228,7 +1208,6 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
 gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
-gopkg.in/ini.v1 v1.66.6 h1:LATuAqN/shcYAOkv3wl2L4rkaKqkcgTBQjOyYDvcPKI=
 gopkg.in/ini.v1 v1.66.6/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
diff --git a/indexedDb/implementation.go b/indexedDb/implementation.go
new file mode 100644
index 0000000000000000000000000000000000000000..632bd1317f09a9ebfb17163ae8f690f9c9986350
--- /dev/null
+++ b/indexedDb/implementation.go
@@ -0,0 +1,360 @@
+////////////////////////////////////////////////////////////////////////////////
+// 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
+// +build js,wasm
+
+package indexedDb
+
+import (
+	"context"
+	"encoding/base64"
+	"encoding/json"
+	"github.com/hack-pad/go-indexeddb/idb"
+	"github.com/pkg/errors"
+	jww "github.com/spf13/jwalterweatherman"
+	"gitlab.com/elixxir/xxdk-wasm/utils"
+	"syscall/js"
+	"time"
+
+	"gitlab.com/elixxir/client/channels"
+	"gitlab.com/elixxir/client/cmix/rounds"
+	cryptoBroadcast "gitlab.com/elixxir/crypto/broadcast"
+	cryptoChannel "gitlab.com/elixxir/crypto/channel"
+	"gitlab.com/xx_network/primitives/id"
+)
+
+// dbTimeout is the global timeout for operations with the storage context.Contact
+const dbTimeout = time.Second
+
+// wasmModel implements [channels.EventModel] interface which uses the channels
+// system passed an object which adheres to in order to get events on the channel.
+type wasmModel struct {
+	db *idb.Database
+}
+
+// newContext builds a context for database operations
+func newContext() (context.Context, context.CancelFunc) {
+	return context.WithTimeout(context.Background(), dbTimeout)
+}
+
+// JoinChannel is called whenever a channel is joined locally.
+func (w *wasmModel) JoinChannel(channel *cryptoBroadcast.Channel) {
+	parentErr := errors.New("failed to JoinChannel")
+
+	// Build object
+	newChannel := Channel{
+		Id:          channel.ReceptionID.Marshal(),
+		Name:        channel.Name,
+		Description: channel.Description,
+	}
+
+	// Convert to jsObject
+	newChannelJson, err := json.Marshal(&newChannel)
+	if err != nil {
+		jww.ERROR.Printf("%+v", errors.WithMessagef(parentErr,
+			"Unable to marshal Channel: %+v", err))
+		return
+	}
+	channelObj, err := utils.JsonToJS(newChannelJson)
+	if err != nil {
+		jww.ERROR.Printf("%+v", errors.WithMessagef(parentErr,
+			"Unable to marshal Channel: %+v", err))
+		return
+	}
+
+	// Prepare the Transaction
+	txn, err := w.db.Transaction(idb.TransactionReadWrite, channelsStoreName)
+	if err != nil {
+		jww.ERROR.Printf("%+v", errors.WithMessagef(parentErr,
+			"Unable to create Transaction: %+v", err))
+		return
+	}
+	store, err := txn.ObjectStore(channelsStoreName)
+	if err != nil {
+		jww.ERROR.Printf("%+v", errors.WithMessagef(parentErr,
+			"Unable to get ObjectStore: %+v", err))
+		return
+	}
+
+	// Perform the operation
+	_, err = store.Add(channelObj)
+	if err != nil {
+		jww.ERROR.Printf("%+v", errors.WithMessagef(parentErr,
+			"Unable to Add Channel: %+v", err))
+		return
+	}
+
+	// Wait for the operation to return
+	ctx, cancel := newContext()
+	err = txn.Await(ctx)
+	cancel()
+	if err != nil {
+		jww.ERROR.Printf("%+v", errors.WithMessagef(parentErr,
+			"Adding Channel failed: %+v", err))
+		return
+	}
+	jww.DEBUG.Printf("Successfully added channel: %s",
+		channel.ReceptionID.String())
+}
+
+// LeaveChannel is called whenever a channel is left locally.
+func (w *wasmModel) LeaveChannel(channelID *id.ID) {
+	parentErr := errors.New("failed to LeaveChannel")
+
+	// Prepare the Transaction
+	txn, err := w.db.Transaction(idb.TransactionReadWrite, channelsStoreName)
+	if err != nil {
+		jww.ERROR.Printf("%+v", errors.WithMessagef(parentErr,
+			"Unable to create Transaction: %+v", err))
+		return
+	}
+	store, err := txn.ObjectStore(channelsStoreName)
+	if err != nil {
+		jww.ERROR.Printf("%+v", errors.WithMessagef(parentErr,
+			"Unable to get ObjectStore: %+v", err))
+		return
+	}
+
+	// Perform the operation
+	_, err = store.Delete(js.ValueOf(channelID.String()))
+	if err != nil {
+		jww.ERROR.Printf("%+v", errors.WithMessagef(parentErr,
+			"Unable to Delete Channel: %+v", err))
+		return
+	}
+
+	// Wait for the operation to return
+	ctx, cancel := newContext()
+	err = txn.Await(ctx)
+	cancel()
+	if err != nil {
+		jww.ERROR.Printf("%+v", errors.WithMessagef(parentErr,
+			"Deleting Channel failed: %+v", err))
+		return
+	}
+	jww.DEBUG.Printf("Successfully deleted channel: %s", channelID.String())
+}
+
+// ReceiveMessage is called whenever a message is received on a given channel
+// It may be called multiple times on the same message, it is incumbent on
+// the user of the API to filter such called by message ID.
+func (w *wasmModel) ReceiveMessage(channelID *id.ID, messageID cryptoChannel.MessageID,
+	senderUsername string, text string, timestamp time.Time, lease time.Duration,
+	_ rounds.Round, status channels.SentStatus) {
+	parentErr := errors.New("failed to ReceiveMessage")
+
+	err := w.receiveHelper(buildMessage(channelID.Marshal(), messageID.Bytes(),
+		nil, senderUsername, text, timestamp, lease, status))
+	if err != nil {
+		jww.ERROR.Printf("%+v", errors.Wrap(parentErr, err.Error()))
+	}
+}
+
+// ReceiveReply is called whenever a message is received which is a reply
+// on a given channel. It may be called multiple times on the same message,
+// it is incumbent on the user of the API to filter such called by message ID
+// Messages may arrive our of order, so a reply in theory can arrive before
+// the initial message, as a result it may be important to buffer replies.
+func (w *wasmModel) ReceiveReply(channelID *id.ID, messageID cryptoChannel.MessageID,
+	replyTo cryptoChannel.MessageID, senderUsername string, text string,
+	timestamp time.Time, lease time.Duration, _ rounds.Round, status channels.SentStatus) {
+	parentErr := errors.New("failed to ReceiveReply")
+
+	err := w.receiveHelper(buildMessage(channelID.Marshal(), messageID.Bytes(),
+		replyTo.Bytes(), senderUsername, text, timestamp, lease, status))
+	if err != nil {
+		jww.ERROR.Printf("%+v", errors.Wrap(parentErr, err.Error()))
+	}
+}
+
+// ReceiveReaction is called whenever a reaction to a message is received
+// on a given channel. It may be called multiple times on the same reaction,
+// it is incumbent on the user of the API to filter such called by message ID
+// Messages may arrive our of order, so a reply in theory can arrive before
+// the initial message, as a result it may be important to buffer reactions.
+func (w *wasmModel) ReceiveReaction(channelID *id.ID, messageID cryptoChannel.MessageID,
+	reactionTo cryptoChannel.MessageID, senderUsername string, reaction string,
+	timestamp time.Time, lease time.Duration, _ rounds.Round, status channels.SentStatus) {
+	parentErr := errors.New("failed to ReceiveReaction")
+
+	err := w.receiveHelper(buildMessage(channelID.Marshal(), messageID.Bytes(),
+		reactionTo.Bytes(), senderUsername, reaction, timestamp, lease, status))
+	if err != nil {
+		jww.ERROR.Printf("%+v", errors.Wrap(parentErr, err.Error()))
+	}
+}
+
+// UpdateSentStatus is called whenever the SentStatus of a message
+// has changed
+// TODO: Potential race condition due to separate get/update operations
+func (w *wasmModel) UpdateSentStatus(messageID cryptoChannel.MessageID,
+	status channels.SentStatus) {
+	parentErr := errors.New("failed to UpdateSentStatus")
+
+	// Convert messageID to the key generated by json.Marshal
+	key := js.ValueOf(base64.StdEncoding.EncodeToString(messageID[:]))
+
+	// Use the key to get the existing Message
+	currentMsg, err := w.get(messageStoreName, key)
+	if err != nil {
+		return
+	}
+
+	// Extract the existing Message and update the Status
+	newMessage := &Message{}
+	err = json.Unmarshal([]byte(currentMsg), newMessage)
+	if err != nil {
+		return
+	}
+	newMessage.Status = uint8(status)
+
+	// Store the updated Message
+	err = w.receiveHelper(newMessage)
+	if err != nil {
+		jww.ERROR.Printf("%+v", errors.Wrap(parentErr, err.Error()))
+	}
+}
+
+// buildMessage is a private helper that converts typical [channels.EventModel]
+// inputs into a basic Message structure for insertion into storage
+func buildMessage(channelID []byte, messageID []byte,
+	parentId []byte, senderUsername string, text string,
+	timestamp time.Time, lease time.Duration, status channels.SentStatus) *Message {
+	return &Message{
+		Id:              messageID,
+		SenderUsername:  senderUsername,
+		ChannelId:       channelID,
+		ParentMessageId: parentId,
+		Timestamp:       timestamp,
+		Lease:           lease,
+		Status:          uint8(status),
+		Hidden:          false,
+		Pinned:          false,
+		Text:            text,
+	}
+}
+
+// receiveHelper is a private helper for receiving any sort of message
+func (w *wasmModel) receiveHelper(newMessage *Message) error {
+	// Convert to jsObject
+	newMessageJson, err := json.Marshal(newMessage)
+	if err != nil {
+		return errors.Errorf("Unable to marshal Message: %+v", err)
+	}
+	messageObj, err := utils.JsonToJS(newMessageJson)
+	if err != nil {
+		return errors.Errorf("Unable to marshal Message: %+v", err)
+	}
+
+	// Prepare the Transaction
+	txn, err := w.db.Transaction(idb.TransactionReadWrite, messageStoreName)
+	if err != nil {
+		return errors.Errorf("Unable to create Transaction: %+v", err)
+	}
+	store, err := txn.ObjectStore(messageStoreName)
+	if err != nil {
+		return errors.Errorf("Unable to get ObjectStore: %+v", err)
+	}
+
+	// Perform the upsert (put) operation
+	_, err = store.Put(messageObj)
+	if err != nil {
+		return errors.Errorf("Unable to upsert Message: %+v", err)
+	}
+
+	// Wait for the operation to return
+	ctx, cancel := newContext()
+	err = txn.Await(ctx)
+	cancel()
+	if err != nil {
+		return errors.Errorf("Upserting Message failed: %+v", err)
+	}
+	jww.DEBUG.Printf("Successfully stored message from %s",
+		newMessage.SenderUsername)
+	return nil
+}
+
+// get is a generic private helper for getting values from the given [idb.ObjectStore].
+func (w *wasmModel) get(objectStoreName string, key js.Value) (string, error) {
+	parentErr := errors.Errorf("failed to get %s/%s", objectStoreName, key)
+
+	// Prepare the Transaction
+	txn, err := w.db.Transaction(idb.TransactionReadOnly, objectStoreName)
+	if err != nil {
+		return "", errors.WithMessagef(parentErr,
+			"Unable to create Transaction: %+v", err)
+	}
+	store, err := txn.ObjectStore(objectStoreName)
+	if err != nil {
+		return "", errors.WithMessagef(parentErr,
+			"Unable to get ObjectStore: %+v", err)
+	}
+
+	// Perform the operation
+	getRequest, err := store.Get(key)
+	if err != nil {
+		return "", errors.WithMessagef(parentErr,
+			"Unable to Get from ObjectStore: %+v", err)
+	}
+
+	// Wait for the operation to return
+	ctx, cancel := newContext()
+	resultObj, err := getRequest.Await(ctx)
+	cancel()
+	if err != nil {
+		return "", errors.WithMessagef(parentErr,
+			"Unable to get from ObjectStore: %+v", err)
+	}
+
+	// Process result into string
+	resultStr := utils.JsToJson(resultObj)
+	jww.DEBUG.Printf("Got from %s/%s: %s", objectStoreName, key, resultStr)
+	return resultStr, nil
+}
+
+// dump given [idb.ObjectStore] contents to string slice for debugging purposes
+func (w *wasmModel) dump(objectStoreName string) ([]string, error) {
+	parentErr := errors.Errorf("failed to dump %s", objectStoreName)
+
+	txn, err := w.db.Transaction(idb.TransactionReadOnly, objectStoreName)
+	if err != nil {
+		return nil, errors.WithMessagef(parentErr,
+			"Unable to create Transaction: %+v", err)
+	}
+	store, err := txn.ObjectStore(objectStoreName)
+	if err != nil {
+		return nil, errors.WithMessagef(parentErr,
+			"Unable to get ObjectStore: %+v", err)
+	}
+	cursorRequest, err := store.OpenCursor(idb.CursorNext)
+	if err != nil {
+		return nil, errors.WithMessagef(parentErr,
+			"Unable to open Cursor: %+v", err)
+	}
+
+	// Run the query
+	jww.DEBUG.Printf("%s values:", objectStoreName)
+	results := make([]string, 0)
+	ctx, cancel := newContext()
+	err = cursorRequest.Iter(ctx, func(cursor *idb.CursorWithValue) error {
+		value, err := cursor.Value()
+		if err != nil {
+			return err
+		}
+		valueStr := utils.JsToJson(value)
+		results = append(results, valueStr)
+		jww.DEBUG.Printf("- %v", valueStr)
+		return nil
+	})
+	cancel()
+	if err != nil {
+		return nil, errors.WithMessagef(parentErr,
+			"Unable to dump ObjectStore: %+v", err)
+	}
+	return results, nil
+}
diff --git a/indexedDb/implementation_test.go b/indexedDb/implementation_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..a7b005abb782c0151bb0045d0eb9239c611ac947
--- /dev/null
+++ b/indexedDb/implementation_test.go
@@ -0,0 +1,118 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2022 xx foundation                                             //
+//                                                                            //
+// Use of this source code is governed by a license that can be found in the  //
+// LICENSE file                                                               //
+////////////////////////////////////////////////////////////////////////////////
+
+package indexedDb
+
+import (
+	"encoding/json"
+	jww "github.com/spf13/jwalterweatherman"
+	"gitlab.com/elixxir/client/channels"
+	cryptoBroadcast "gitlab.com/elixxir/crypto/broadcast"
+	"gitlab.com/elixxir/crypto/channel"
+	"gitlab.com/xx_network/primitives/id"
+	"os"
+	"testing"
+	"time"
+)
+
+func TestMain(m *testing.M) {
+	jww.SetStdoutThreshold(jww.LevelDebug)
+	os.Exit(m.Run())
+}
+
+// Test UpdateSentStatus happy path and ensure fields don't change
+func TestWasmModel_UpdateSentStatus(t *testing.T) {
+	testString := "test"
+	testMsgId := channel.MakeMessageID([]byte(testString))
+	eventModel, err := newWasmModel(testString)
+	if err != nil {
+		t.Fatalf("%+v", err)
+	}
+
+	// Store a test message
+	testMsg := buildMessage([]byte(testString), testMsgId.Bytes(),
+		nil, testString, testString, time.Now(), time.Second, channels.Sent)
+	err = eventModel.receiveHelper(testMsg)
+	if err != nil {
+		t.Fatalf("%+v", err)
+	}
+
+	// Ensure one message is stored
+	results, err := eventModel.dump(messageStoreName)
+	if err != nil {
+		t.Fatalf("%+v", err)
+	}
+	if len(results) != 1 {
+		t.Fatalf("Expected 1 message to exist")
+	}
+
+	// Update the sentStatus
+	expectedStatus := channels.Failed
+	eventModel.UpdateSentStatus(testMsgId, expectedStatus)
+
+	// Check the resulting status
+	results, err = eventModel.dump(messageStoreName)
+	if err != nil {
+		t.Fatalf("%+v", err)
+	}
+	if len(results) != 1 {
+		t.Fatalf("Expected 1 message to exist")
+	}
+	resultMsg := &Message{}
+	err = json.Unmarshal([]byte(results[0]), resultMsg)
+	if err != nil {
+		t.Fatalf("%+v", err)
+	}
+	if resultMsg.Status != uint8(expectedStatus) {
+		t.Fatalf("Unexpected Status: %v", resultMsg.Status)
+	}
+
+	// Make sure other fields didn't change
+	if resultMsg.SenderUsername != testString {
+		t.Fatalf("Unexpected SenderUsername: %v", resultMsg.SenderUsername)
+	}
+}
+
+// Smoke test JoinChannel/LeaveChannel happy paths
+func TestWasmModel_JoinChannel_LeaveChannel(t *testing.T) {
+	eventModel, err := newWasmModel("test")
+	if err != nil {
+		t.Fatalf("%+v", err)
+	}
+
+	testChannel := &cryptoBroadcast.Channel{
+		ReceptionID: id.NewIdFromString("test", id.Generic, t),
+		Name:        "test",
+		Description: "test",
+		Salt:        nil,
+		RsaPubKey:   nil,
+	}
+	testChannel2 := &cryptoBroadcast.Channel{
+		ReceptionID: id.NewIdFromString("test2", id.Generic, t),
+		Name:        "test2",
+		Description: "test2",
+		Salt:        nil,
+		RsaPubKey:   nil,
+	}
+	eventModel.JoinChannel(testChannel)
+	eventModel.JoinChannel(testChannel2)
+	results, err := eventModel.dump(channelsStoreName)
+	if err != nil {
+		t.Fatalf("%+v", err)
+	}
+	if len(results) != 2 {
+		t.Fatalf("Expected 2 channels to exist")
+	}
+	eventModel.LeaveChannel(testChannel.ReceptionID)
+	results, err = eventModel.dump(channelsStoreName)
+	if err != nil {
+		t.Fatalf("%+v", err)
+	}
+	if len(results) != 1 {
+		t.Fatalf("Expected 1 channels to exist")
+	}
+}
diff --git a/indexedDb/init.go b/indexedDb/init.go
new file mode 100644
index 0000000000000000000000000000000000000000..f256b935111a330c30779eb697a1f6da4195dc84
--- /dev/null
+++ b/indexedDb/init.go
@@ -0,0 +1,110 @@
+////////////////////////////////////////////////////////////////////////////////
+// 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
+// +build js,wasm
+
+package indexedDb
+
+import (
+	"github.com/hack-pad/go-indexeddb/idb"
+	"github.com/pkg/errors"
+	jww "github.com/spf13/jwalterweatherman"
+	"syscall/js"
+
+	"gitlab.com/elixxir/client/channels"
+)
+
+const (
+	// databaseSuffix to be appended to the name of the database
+	databaseSuffix = "_messenger"
+	// currentVersion of the IndexDb runtime. Used for migration purposes.
+	currentVersion uint = 1
+)
+
+// NewWasmEventModel returns a [channels.EventModel] backed by a wasmModel
+func NewWasmEventModel(username string) (channels.EventModel, error) {
+	databaseName := username + databaseSuffix
+	return newWasmModel(databaseName)
+}
+
+// newWasmModel creates the given [idb.Database] and returns a wasmModel
+func newWasmModel(databaseName string) (*wasmModel, error) {
+	// Attempt to open database object
+	ctx, cancel := newContext()
+	defer cancel()
+	openRequest, _ := idb.Global().Open(ctx, databaseName, currentVersion,
+		func(db *idb.Database, oldVersion, newVersion uint) error {
+			if oldVersion == newVersion {
+				jww.INFO.Printf("IndexDb version is current: v%d",
+					newVersion)
+				return nil
+			}
+
+			jww.INFO.Printf("IndexDb upgrade required: v%d -> v%d",
+				oldVersion, newVersion)
+
+			if oldVersion == 0 && newVersion == 1 {
+				return v1Upgrade(db)
+			}
+
+			return errors.Errorf("Invalid version upgrade path: v%d -> v%d",
+				oldVersion, newVersion)
+		})
+
+	// Wait for database open to finish
+	db, err := openRequest.Await(ctx)
+
+	return &wasmModel{db: db}, err
+}
+
+// v1Upgrade performs the v0 -> v1 database upgrade.
+// This can never be changed without permanently breaking backwards compatibility.
+func v1Upgrade(db *idb.Database) error {
+	storeOpts := idb.ObjectStoreOptions{
+		KeyPath:       js.ValueOf(pkeyName),
+		AutoIncrement: false,
+	}
+	indexOpts := idb.IndexOptions{
+		Unique:     false,
+		MultiEntry: false,
+	}
+
+	// Build Message ObjectStore and Indexes
+	messageStore, err := db.CreateObjectStore(messageStoreName, storeOpts)
+	if err != nil {
+		return err
+	}
+	_, err = messageStore.CreateIndex(messageStoreChannelIndex,
+		js.ValueOf(messageStoreChannel), indexOpts)
+	if err != nil {
+		return err
+	}
+	_, err = messageStore.CreateIndex(messageStoreParentIndex,
+		js.ValueOf(messageStoreParent), indexOpts)
+	if err != nil {
+		return err
+	}
+	_, err = messageStore.CreateIndex(messageStoreTimestampIndex,
+		js.ValueOf(messageStoreTimestamp), indexOpts)
+	if err != nil {
+		return err
+	}
+	_, err = messageStore.CreateIndex(messageStorePinnedIndex,
+		js.ValueOf(messageStorePinned), indexOpts)
+	if err != nil {
+		return err
+	}
+
+	// Build Channel ObjectStore
+	_, err = db.CreateObjectStore(channelsStoreName, storeOpts)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
diff --git a/indexedDb/model.go b/indexedDb/model.go
new file mode 100644
index 0000000000000000000000000000000000000000..9354e67a869c87f70824dc52fecc67a1058671d2
--- /dev/null
+++ b/indexedDb/model.go
@@ -0,0 +1,60 @@
+////////////////////////////////////////////////////////////////////////////////
+// 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
+// +build js,wasm
+
+package indexedDb
+
+import (
+	"time"
+)
+
+const (
+	// Text representation of primary key value (keyPath).
+	pkeyName = "id"
+
+	// Text representation of the names of the various [idb.ObjectStore].
+	messageStoreName  = "messages"
+	channelsStoreName = "channels"
+
+	// Message index names.
+	messageStoreChannelIndex   = "channel_id_index"
+	messageStoreParentIndex    = "parent_message_id_index"
+	messageStoreTimestampIndex = "timestamp_index"
+	messageStorePinnedIndex    = "pinned_index"
+
+	// Message keyPath names (must match json struct tags).
+	messageStoreChannel   = "channel_id"
+	messageStoreParent    = "parent_message_id"
+	messageStoreTimestamp = "timestamp"
+	messageStorePinned    = "pinned"
+)
+
+// Message defines the IndexedDb representation of a single Message.
+// A Message belongs to one Channel.
+// A Message may belong to one Message (Parent).
+type Message struct {
+	Id              []byte        `json:"id"` // Matches pkeyName
+	SenderUsername  string        `json:"sender_username"`
+	ChannelId       []byte        `json:"channel_id"`        // Index
+	ParentMessageId []byte        `json:"parent_message_id"` // Index
+	Timestamp       time.Time     `json:"timestamp"`         // Index
+	Lease           time.Duration `json:"lease"`
+	Status          uint8         `json:"status"`
+	Hidden          bool          `json:"hidden"`
+	Pinned          bool          `json:"pinned"` // Index
+	Text            string        `json:"text"`
+}
+
+// Channel defines the IndexedDb representation of a single Channel
+// A Channel has many Message.
+type Channel struct {
+	Id          []byte `json:"id"` // Matches pkeyName
+	Name        string `json:"name"`
+	Description string `json:"description"`
+}
diff --git a/utils/convert.go b/utils/convert.go
index 293e5f3703b92028747781120ac29290be5faf2e..aad9d8a153845f18f8b6590559e3870cfbbf14ff 100644
--- a/utils/convert.go
+++ b/utils/convert.go
@@ -28,16 +28,15 @@ func CopyBytesToJS(src []byte) js.Value {
 	return dst
 }
 
-// JsonToJS converts a marshalled JSON bytes to a Javascript object.
-func JsonToJS(src []byte) (js.Value, error) {
-	var inInterface map[string]interface{}
-	err := json.Unmarshal(src, &inInterface)
+// JsonToJS is a helper that converts JSON bytes input
+// to a [js.Value] of the object subtype.
+func JsonToJS(inputJson []byte) (js.Value, error) {
+	jsObj := make(map[string]interface{})
+	err := json.Unmarshal(inputJson, &jsObj)
 	if err != nil {
-		Throw(TypeError, err)
-		return js.ValueOf(nil), err
+		return js.Value{}, err
 	}
-
-	return js.ValueOf(inInterface), nil
+	return js.ValueOf(jsObj), nil
 }
 
 // JsToJson converts the Javascript value to JSON.