diff --git a/api/precan.go b/api/precan.go
new file mode 100644
index 0000000000000000000000000000000000000000..ac90ba42cf6ee7a46021d4dca49694c9c99f9b55
--- /dev/null
+++ b/api/precan.go
@@ -0,0 +1,96 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                          //
+//                                                                           //
+// Use of this source code is governed by a license that can be found in the //
+// LICENSE file                                                              //
+///////////////////////////////////////////////////////////////////////////////
+
+package api
+
+import (
+	"encoding/binary"
+	"math/rand"
+
+	jww "github.com/spf13/jwalterweatherman"
+	"gitlab.com/elixxir/client/storage"
+	"gitlab.com/elixxir/client/storage/user"
+	"gitlab.com/elixxir/crypto/cyclic"
+	"gitlab.com/elixxir/crypto/fastRNG"
+	"gitlab.com/xx_network/crypto/csprng"
+	"gitlab.com/xx_network/crypto/signature/rsa"
+	"gitlab.com/xx_network/primitives/id"
+)
+
+// creates a precanned user
+func createPrecannedUser(precannedID uint, rng csprng.Source, cmix,
+	e2e *cyclic.Group) user.Info {
+	// DH Keygen
+	prng := rand.New(rand.NewSource(int64(precannedID)))
+	prime := e2e.GetPBytes()
+	keyLen := len(prime)
+	e2eKeyBytes, err := csprng.GenerateInGroup(prime, keyLen, prng)
+	if err != nil {
+		jww.FATAL.Panicf(err.Error())
+	}
+
+	// Salt, UID, etc gen
+	salt := make([]byte, SaltSize)
+
+	userID := id.ID{}
+	binary.BigEndian.PutUint64(userID[:], uint64(precannedID))
+	userID.SetType(id.User)
+
+	// NOTE: not used... RSA Keygen (4096 bit defaults)
+	rsaKey, err := rsa.GenerateKey(rng, rsa.DefaultRSABitLen)
+	if err != nil {
+		jww.FATAL.Panicf(err.Error())
+	}
+
+	return user.Info{
+		TransmissionID:   &userID,
+		TransmissionSalt: salt,
+		ReceptionID:      &userID,
+		ReceptionSalt:    salt,
+		Precanned:        true,
+		E2eDhPrivateKey:  e2e.NewIntFromBytes(e2eKeyBytes),
+		TransmissionRSA:  rsaKey,
+		ReceptionRSA:     rsaKey,
+	}
+}
+
+// NewPrecannedClient creates an insecure user with predetermined keys
+// with nodes It creates client storage, generates keys, connects, and
+// registers with the network. Note that this does not register a
+// username/identity, but merely creates a new cryptographic identity
+// for adding such information at a later date.
+func NewPrecannedClient(precannedID uint, defJSON, storageDir string,
+	password []byte) error {
+	jww.INFO.Printf("NewPrecannedClient()")
+	rngStreamGen := fastRNG.NewStreamGenerator(12, 1024,
+		csprng.NewSystemRNG)
+	rngStream := rngStreamGen.GetStream()
+
+	def, err := parseNDF(defJSON)
+	if err != nil {
+		return err
+	}
+	cmixGrp, e2eGrp := decodeGroups(def)
+
+	protoUser := createPrecannedUser(precannedID, rngStream,
+		cmixGrp, e2eGrp)
+
+	store, err := checkVersionAndSetupStorage(def, storageDir, password,
+		protoUser, cmixGrp, e2eGrp, rngStreamGen, "")
+	if err != nil {
+		return err
+	}
+
+	// Mark the precanned user as finished with permissioning and registered
+	// with the network.
+	err = store.ForwardRegistrationStatus(storage.PermissioningComplete)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
diff --git a/api/user.go b/api/user.go
index 7ed520a0ffd2d19b12c1af772713273f7fbd7210..5913185b4bec08d63680a9e2e3d89d6632dca736 100644
--- a/api/user.go
+++ b/api/user.go
@@ -8,8 +8,6 @@
 package api
 
 import (
-	"encoding/binary"
-	"math/rand"
 	"regexp"
 	"runtime"
 	"strings"
@@ -39,7 +37,7 @@ func createNewUser(rng *fastRNG.StreamGenerator, cmix,
 	var e2eKeyBytes, transmissionSalt, receptionSalt []byte
 
 	e2eKeyBytes, transmissionSalt, receptionSalt,
-		transmissionRsaKey, receptionRsaKey = createDhKeys(rng, e2e)
+		transmissionRsaKey, receptionRsaKey = createKeys(rng, e2e)
 
 	// Salt, UID, etc gen
 	stream := rng.GetStream()
@@ -91,7 +89,7 @@ func createNewUser(rng *fastRNG.StreamGenerator, cmix,
 	}
 }
 
-func createDhKeys(rng *fastRNG.StreamGenerator,
+func createKeys(rng *fastRNG.StreamGenerator,
 	e2e *cyclic.Group) (e2eKeyBytes,
 	transmissionSalt, receptionSalt []byte,
 	transmissionRsaKey, receptionRsaKey *rsa.PrivateKey) {
@@ -143,44 +141,6 @@ func createDhKeys(rng *fastRNG.StreamGenerator,
 
 }
 
-// TODO: Add precanned user code structures here.
-// creates a precanned user
-func createPrecannedUser(precannedID uint, rng csprng.Source, cmix,
-	e2e *cyclic.Group) user.Info {
-	// DH Keygen
-	prng := rand.New(rand.NewSource(int64(precannedID)))
-	prime := e2e.GetPBytes()
-	keyLen := len(prime)
-	e2eKeyBytes, err := csprng.GenerateInGroup(prime, keyLen, prng)
-	if err != nil {
-		jww.FATAL.Panicf(err.Error())
-	}
-
-	// Salt, UID, etc gen
-	salt := make([]byte, SaltSize)
-
-	userID := id.ID{}
-	binary.BigEndian.PutUint64(userID[:], uint64(precannedID))
-	userID.SetType(id.User)
-
-	// NOTE: not used... RSA Keygen (4096 bit defaults)
-	rsaKey, err := rsa.GenerateKey(rng, rsa.DefaultRSABitLen)
-	if err != nil {
-		jww.FATAL.Panicf(err.Error())
-	}
-
-	return user.Info{
-		TransmissionID:   &userID,
-		TransmissionSalt: salt,
-		ReceptionID:      &userID,
-		ReceptionSalt:    salt,
-		Precanned:        true,
-		E2eDhPrivateKey:  e2e.NewIntFromBytes(e2eKeyBytes),
-		TransmissionRSA:  rsaKey,
-		ReceptionRSA:     rsaKey,
-	}
-}
-
 // createNewVanityUser generates an identity for cMix
 // The identity's ReceptionID is not random but starts with the supplied prefix
 func createNewVanityUser(rng csprng.Source, cmix,