diff --git a/browserid/compress.sh b/browserid/compress.sh
index 9ee33236fb0d16057fbec0826c73f910a296ae22..f0c3be699ee27b81172abc73cca33eceb8bb36e1 100755
--- a/browserid/compress.sh
+++ b/browserid/compress.sh
@@ -48,7 +48,7 @@ echo ''
 
 cd ../js
 # re-minimize everything together
-cat jquery-1.6.2.min.js json2.js browserid.js ../dialog/resources/underscore-min.js ../dialog/resources/browserid-extensions.js ../dialog/resources/storage.js ../dialog/resources/browserid-network.js ../dialog/resources/browserid-identities.js ../dialog/resources/tooltip.js ../dialog/resources/validation.js pages/index.js pages/add_email_address.js pages/verify_email_address.js pages/manage_account.js pages/signin.js pages/signup.js pages/forgot.js > lib.js
+cat jquery-1.6.2.min.js json2.js browserid.js ../dialog/resources/underscore-min.js ../dialog/resources/browserid-extensions.js ../dialog/resources/storage.js ../dialog/resources/network.js ../dialog/resources/user.js ../dialog/resources/tooltip.js ../dialog/resources/validation.js pages/index.js pages/add_email_address.js pages/verify_email_address.js pages/manage_account.js pages/signin.js pages/signup.js pages/forgot.js > lib.js
 $UGLIFY < lib.js > lib.min.js
 
 cd ../css
diff --git a/browserid/static/dialog/controllers/authenticate_controller.js b/browserid/static/dialog/controllers/authenticate_controller.js
index 2ebe697ef56e72c50abcf5fab6aaed1cf8c968b1..ee62a1988df743ea40559db159129223ed2b1bd1 100644
--- a/browserid/static/dialog/controllers/authenticate_controller.js
+++ b/browserid/static/dialog/controllers/authenticate_controller.js
@@ -39,7 +39,7 @@
 
   var ANIMATION_TIME = 250,
       bid = BrowserID,
-      identities = bid.Identities,
+      user = bid.User,
       validation = bid.Validation;
 
   function checkEmail(el, event) {
@@ -52,7 +52,7 @@
       return;
     }
 
-    identities.emailRegistered(email, function onComplete(registered) {
+    user.isEmailRegistered(email, function onComplete(registered) {
       if(registered) {
         enterPasswordState.call(self);
       }
@@ -72,7 +72,7 @@
       return;
     }
 
-    identities.createUser(email, function(keypair) {
+    user.createUser(email, function(keypair) {
       if(keypair) {
         self.close("user_staged", {
           email: email,
@@ -96,7 +96,7 @@
       return;
     }
 
-    identities.authenticateAndSync(email, pass, 
+    user.authenticateAndSync(email, pass, 
       function onAuthenticate(authenticated) {
         if (authenticated) {
           self.doWait(bid.Wait.authentication);
@@ -122,7 +122,7 @@
 
     cancelEvent(event);
 
-    identities.requestPasswordReset(email, function() {
+    user.requestPasswordReset(email, function() {
       self.close("reset_password", {
         email: email
       });
@@ -196,7 +196,7 @@
       this._super({
         bodyTemplate: "authenticate.ejs",
         bodyVars: {
-          sitename: identities.getOrigin(),
+          sitename: user.getOrigin(),
           siteicon: "/i/times.gif",
           email: options.email || ""
         }
diff --git a/browserid/static/dialog/controllers/checkregistration_controller.js b/browserid/static/dialog/controllers/checkregistration_controller.js
index e7ec8093816d9e7846c6f4107a52c88b2c8fe0fa..9988c00a4e70b9620cb00187c26a775eac899874 100644
--- a/browserid/static/dialog/controllers/checkregistration_controller.js
+++ b/browserid/static/dialog/controllers/checkregistration_controller.js
@@ -37,7 +37,7 @@
 (function() {
   "use strict";
 
-  var identities = BrowserID.Identities;
+  var user = BrowserID.User;
 
   PageController.extend("Checkregistration", {}, {
     init: function(el, options) {
@@ -56,9 +56,9 @@
 
     setupRegCheck: function() {
       var me=this;
-      identities[me.verifier](me.email, function(status) {
+      user[me.verifier](me.email, function(status) {
         if (status === "complete") {
-          identities.syncEmails(function() {
+          user.syncEmails(function() {
             me.close(me.verificationMessage);
           });
         }
diff --git a/browserid/static/dialog/controllers/dialog_controller.js b/browserid/static/dialog/controllers/dialog_controller.js
index aaa56166b272aadde5cad2ba869ba3069beb22aa..1d8224aecde9d56e9017f40344da48ac0a83cb3b 100644
--- a/browserid/static/dialog/controllers/dialog_controller.js
+++ b/browserid/static/dialog/controllers/dialog_controller.js
@@ -40,173 +40,175 @@
 //
 
 (function() {
-"use strict";
-
-PageController.extend("Dialog", {}, {
-    init: function(el) {
-      var self=this;
-      //this.element.show();
-
-      // keep track of where we are and what we do on success and error
-      self.onsuccess = null;
-      self.onerror = null;
-      setupChannel(self);
-      self.stateMachine();
-    },
-      
-    getVerifiedEmail: function(origin_url, onsuccess, onerror) {
-      this.onsuccess = onsuccess;
-      this.onerror = onerror;
-
-      BrowserID.Identities.setOrigin(origin_url);
-
-      this.doCheckAuth();
-
-      var self=this;
-      $(window).bind("unload", function() {
-        self.doCancel();
-      });
-    },
-
-
-    stateMachine: function() {
-      var self=this, 
-          hub = OpenAjax.hub, 
-          el = this.element;
-     
-
-      hub.subscribe("user_staged", function(msg, info) {
-        self.doConfirmUser(info.email);
-      });
-
-      hub.subscribe("user_confirmed", function() {
-        self.doEmailConfirmed();
-      });
-
-      hub.subscribe("authenticated", function(msg, info) {
-        //self.doEmailSelected(info.email);
-        // XXX benadida, lloyd - swap these two if you want to experiment with 
-        // generating assertions directly from signin.
-        self.syncEmails();
-      });
-
-      hub.subscribe("reset_password", function(msg, info) {
-        self.doConfirmUser(info.email);
-      });
-
-      hub.subscribe("assertion_generated", function(msg, info) {
-        self.doAssertionGenerated(info.assertion);
-      });
-
-      hub.subscribe("email_staged", function(msg, info) {
-        self.doConfirmEmail(info.email);
-      });
-
-      hub.subscribe("email_confirmed", function() {
-        self.doEmailConfirmed();
-      });
-
-      hub.subscribe("notme", function() {
-        self.doNotMe();
-      });
-
-      hub.subscribe("auth", function(msg, info) {
-        info = info || {};
-
-        self.doAuthenticate({
-          email: info.email
+  "use strict";
+
+  var user = BrowserID.User;
+
+  PageController.extend("Dialog", {}, {
+      init: function(el) {
+        var self=this;
+        //this.element.show();
+
+        // keep track of where we are and what we do on success and error
+        self.onsuccess = null;
+        self.onerror = null;
+        setupChannel(self);
+        self.stateMachine();
+      },
+        
+      getVerifiedEmail: function(origin_url, onsuccess, onerror) {
+        this.onsuccess = onsuccess;
+        this.onerror = onerror;
+
+        user.setOrigin(origin_url);
+
+        this.doCheckAuth();
+
+        var self=this;
+        $(window).bind("unload", function() {
+          self.doCancel();
+        });
+      },
+
+
+      stateMachine: function() {
+        var self=this, 
+            hub = OpenAjax.hub, 
+            el = this.element;
+       
+
+        hub.subscribe("user_staged", function(msg, info) {
+          self.doConfirmUser(info.email);
+        });
+
+        hub.subscribe("user_confirmed", function() {
+          self.doEmailConfirmed();
+        });
+
+        hub.subscribe("authenticated", function(msg, info) {
+          //self.doEmailSelected(info.email);
+          // XXX benadida, lloyd - swap these two if you want to experiment with 
+          // generating assertions directly from signin.
+          self.syncEmails();
+        });
+
+        hub.subscribe("reset_password", function(msg, info) {
+          self.doConfirmUser(info.email);
+        });
+
+        hub.subscribe("assertion_generated", function(msg, info) {
+          self.doAssertionGenerated(info.assertion);
+        });
+
+        hub.subscribe("email_staged", function(msg, info) {
+          self.doConfirmEmail(info.email);
+        });
+
+        hub.subscribe("email_confirmed", function() {
+          self.doEmailConfirmed();
+        });
+
+        hub.subscribe("notme", function() {
+          self.doNotMe();
+        });
+
+        hub.subscribe("auth", function(msg, info) {
+          info = info || {};
+
+          self.doAuthenticate({
+            email: info.email
+          });
+        });
+
+        hub.subscribe("start", function() {
+          self.doCheckAuth();
+        });
+
+        hub.subscribe("cancel", function() {
+          self.doCancel();
+        });
+
+      },
+
+      doConfirmUser: function(email) {
+        this.confirmEmail = email;
+
+        this.element.checkregistration({
+          email: email,
+          verifier: "waitForUserValidation",
+          verificationMessage: "user_confirmed"
+        });
+      },
+
+      doCancel: function() {
+        var self=this;
+        if(self.onsuccess) {
+          self.onsuccess(null);
+        }
+      },
+
+      doSignIn: function() {
+        this.element.pickemail();
+      },
+
+      doAuthenticate: function(info) {
+        this.element.authenticate(info);
+      },
+
+      doForgotPassword: function(email) {
+        this.element.forgotpassword({
+          email: email  
+        });
+      },
+
+      doConfirmEmail: function(email) {
+        this.confirmEmail = email;
+
+        this.element.checkregistration({
+          email: email,
+          verifier: "waitForEmailValidation",
+          verificationMessage: "email_confirmed"
         });
-      });
-
-      hub.subscribe("start", function() {
-        self.doCheckAuth();
-      });
-
-      hub.subscribe("cancel", function() {
-        self.doCancel();
-      });
-
-    },
-
-    doConfirmUser: function(email) {
-      this.confirmEmail = email;
-
-      this.element.checkregistration({
-        email: email,
-        verifier: "waitForUserRegistration",
-        verificationMessage: "user_confirmed"
-      });
-    },
-
-    doCancel: function() {
-      var self=this;
-      if(self.onsuccess) {
-        self.onsuccess(null);
-      }
-    },
-
-    doSignIn: function() {
-      this.element.pickemail();
-    },
-
-    doAuthenticate: function(info) {
-      this.element.authenticate(info);
-    },
-
-    doForgotPassword: function(email) {
-      this.element.forgotpassword({
-        email: email  
-      });
-    },
-
-    doConfirmEmail: function(email) {
-      this.confirmEmail = email;
-
-      this.element.checkregistration({
-        email: email,
-        verifier: "waitForEmailRegistration",
-        verificationMessage: "email_confirmed"
-      });
-    },
-
-    doEmailConfirmed: function() {
-      var self=this;
-      // yay!  now we need to produce an assertion.
-      BrowserID.Identities.getAssertion(this.confirmEmail, self.doAssertionGenerated.bind(self));
-    },
-
-    doAssertionGenerated: function(assertion) {
-      var self=this;
-      // Clear onerror before the call to onsuccess - the code to onsuccess 
-      // calls window.close, which would trigger the onerror callback if we 
-      // tried this afterwards.
-      self.onerror = null;
-      self.onsuccess(assertion);
-    },
-
-    doNotMe: function() {
-      BrowserID.Identities.logoutUser(this.doAuthenticate.bind(this));
-    },
-
-    syncEmails: function() {
-      var self = this;
-      BrowserID.Identities.syncEmails(self.doSignIn.bind(self), 
-        self.getErrorDialog(BrowserID.Errors.signIn));
-    },
-
-
-    doCheckAuth: function() {
-      var self=this;
-      BrowserID.Identities.checkAuthenticationAndSync(function onSuccess() {}, 
-        function onComplete(authenticated) {
-          if (authenticated) {
-              self.doSignIn();
-          } else {
-            self.doAuthenticate();
-          }
-        }, 
-        self.getErrorDialog(BrowserID.Errors.checkAuthentication));
-  }
+      },
+
+      doEmailConfirmed: function() {
+        var self=this;
+        // yay!  now we need to produce an assertion.
+        user.getAssertion(this.confirmEmail, self.doAssertionGenerated.bind(self));
+      },
+
+      doAssertionGenerated: function(assertion) {
+        var self=this;
+        // Clear onerror before the call to onsuccess - the code to onsuccess 
+        // calls window.close, which would trigger the onerror callback if we 
+        // tried this afterwards.
+        self.onerror = null;
+        self.onsuccess(assertion);
+      },
+
+      doNotMe: function() {
+        user.logoutUser(this.doAuthenticate.bind(this));
+      },
+
+      syncEmails: function() {
+        var self = this;
+        user.syncEmails(self.doSignIn.bind(self), 
+          self.getErrorDialog(BrowserID.Errors.signIn));
+      },
+
+
+      doCheckAuth: function() {
+        var self=this;
+        user.checkAuthenticationAndSync(function onSuccess() {}, 
+          function onComplete(authenticated) {
+            if (authenticated) {
+                self.doSignIn();
+            } else {
+              self.doAuthenticate();
+            }
+          }, 
+          self.getErrorDialog(BrowserID.Errors.checkAuthentication));
+    }
 
   });
 
diff --git a/browserid/static/dialog/controllers/page_controller.js b/browserid/static/dialog/controllers/page_controller.js
index 724f37e5c240a091d137c9ac2f142a42effc4356..40a2ec79b4a6c3acbc73011336b57440d0429887 100644
--- a/browserid/static/dialog/controllers/page_controller.js
+++ b/browserid/static/dialog/controllers/page_controller.js
@@ -37,9 +37,7 @@
 (function() {
 "use strict";
 
-  var bid = BrowserID,  
-      ANIMATION_TIME = 250,
-      identities = bid.Identities;
+  var ANIMATION_TIME = 250;
 
 
   $.Controller.extend("PageController", {
diff --git a/browserid/static/dialog/controllers/pickemail_controller.js b/browserid/static/dialog/controllers/pickemail_controller.js
index fc4cb138e1c654a0cf42aebc51aa2d96806cf4e4..49e6a018c5268c041dcafbedc97d7c123dc9e52c 100644
--- a/browserid/static/dialog/controllers/pickemail_controller.js
+++ b/browserid/static/dialog/controllers/pickemail_controller.js
@@ -39,7 +39,7 @@
 
   var ANIMATION_TIME = 250,
       bid = BrowserID,
-      identities = bid.Identities;
+      user = bid.User;
 
   function animateSwap(fadeOutSelector, fadeInSelector, callback) {
     // XXX instead of using jQuery here, think about using CSS animations.
@@ -100,7 +100,7 @@
 
     // Kick of the assertion fetching/keypair generating while we are showing 
     // the animation, hopefully this minimizes the delay the user notices.
-    identities.getAssertion(email, function(assert) {
+    user.getAssertion(email, function(assert) {
       assertion = assert;
       onComplete();
     });
@@ -127,12 +127,12 @@
       return;
     }
 
-    identities.emailRegistered(email, function onComplete(registered) {
+    user.isEmailRegistered(email, function onComplete(registered) {
       if(registered) {
         bid.Tooltip.showTooltip("#already_taken");
       }
       else {
-        identities.addEmail(email, function(added) {
+        user.addEmail(email, function(added) {
           if (added) {
             self.close("email_staged", {
               email: email
@@ -154,9 +154,9 @@
       this._super({
         bodyTemplate: "pickemail.ejs",
         bodyVars: {
-          sitename: identities.getOrigin(),
+          sitename: user.getOrigin(),
           siteicon: '/i/times.gif',
-          identities: identities.getStoredEmailKeypairs(),
+          identities: user.getStoredEmailKeypairs(),
         }
       });
 
diff --git a/browserid/static/dialog/dialog.js b/browserid/static/dialog/dialog.js
index 0284e86006de853eba596ea254bb3a1ca7a15607..d3070184d61ce0bc8855bb5e552a55f8dbeae6f9 100644
--- a/browserid/static/dialog/dialog.js
+++ b/browserid/static/dialog/dialog.js
@@ -54,10 +54,10 @@ steal.plugins(
                'tooltip',
                'validation',
                'browserid-extensions',
-               'browserid-network',
-               'browserid-identities',
-               'browserid-errors',
-               'browserid-wait')					// 3rd party script's (like jQueryUI), in resources folder
+               'network',
+               'user',
+               'error-messages',
+               'wait-messages')					// 3rd party script's (like jQueryUI), in resources folder
 
 	.controllers('page',
                'dialog',
diff --git a/browserid/static/dialog/resources/browserid-errors.js b/browserid/static/dialog/resources/error-messages.js
similarity index 100%
rename from browserid/static/dialog/resources/browserid-errors.js
rename to browserid/static/dialog/resources/error-messages.js
diff --git a/browserid/static/dialog/resources/browserid-network.js b/browserid/static/dialog/resources/network.js
similarity index 100%
rename from browserid/static/dialog/resources/browserid-network.js
rename to browserid/static/dialog/resources/network.js
diff --git a/browserid/static/dialog/resources/storage.js b/browserid/static/dialog/resources/storage.js
index 1a42ffa1749bbce52e72bc4ad43ffa90a0f74702..809f912a404b44e6590fd4c0d8f73552cae6247f 100644
--- a/browserid/static/dialog/resources/storage.js
+++ b/browserid/static/dialog/resources/storage.js
@@ -47,8 +47,12 @@ BrowserID.Storage = (function() {
     window.localStorage.emails = JSON.stringify(emails);
   }
 
-  function clearEmails() {
+  function clear() {
     storeEmails({});
+    var localStorage = window.localStorage;
+    localStorage.removeItem("tempKeypair");
+    localStorage.removeItem("stagedOnBehalfOf");
+    localStorage.removeItem("sitesToEmail");
   }
 
   function getEmails() {
@@ -60,10 +64,16 @@ BrowserID.Storage = (function() {
     }
     
     // if we had a problem parsing or the emails are null
-    clearEmails();
+    clear();
     return {};
   }
 
+  function getEmail(email) {
+    var ids = getEmails();
+
+    return ids && ids[email];
+  }
+
   function addEmail(email, obj) {
     var emails = getEmails();
     emails[email] = obj;
@@ -72,18 +82,35 @@ BrowserID.Storage = (function() {
 
   function removeEmail(email) {
     var emails = getEmails();
-    delete emails[email];
-    storeEmails(emails);
+    if(emails[email]) {
+      delete emails[email];
+      storeEmails(emails);
+
+      // remove any sites associated with this email address.
+      var sitesToEmail = JSON.parse(localStorage.sitesToEmail || "{}");
+      for(var site in sitesToEmail) {
+        if(sitesToEmail[site] === email) {
+          delete sitesToEmail[site];
+        }
+      }
+      localStorage.sitesToEmail = JSON.stringify(sitesToEmail);
+    }
+    else {
+      throw "unknown email address";
+    }
   }
 
   function invalidateEmail(email) {
-    var id = getEmails()[email];
+    var id = getEmail(email);
     if (id) {
       delete id.priv;
       delete id.pub;
       delete id.cert;
       addEmail(email, id);
     }
+    else {
+      throw "unknown email address";
+    }
   }
 
   function storeTemporaryKeypair(keypair) {
@@ -133,12 +160,73 @@ BrowserID.Storage = (function() {
     return origin;
   }
 
+  function setSiteEmail(site, email) {
+    if(getEmail(email)) {
+      var localStorage = window.localStorage;
+
+      var sitesToEmail = JSON.parse(localStorage.sitesToEmail || "{}");
+      sitesToEmail[site] = email;
+
+      localStorage.sitesToEmail = JSON.stringify(sitesToEmail);
+    }
+    else {
+      throw "unknown email address";
+    }
+  }
+
+  function getSiteEmail(site) {
+    var sitesToEmail = JSON.parse(localStorage.sitesToEmail || "{}");
+    return sitesToEmail[site];
+  }
+
   return {
-    getEmails: getEmails,
+    /**
+     * Add an email address and optional key pair.
+     * @method addEmail
+     */
     addEmail: addEmail,
+    /**
+     * Get all email addresses and their associated key pairs
+     * @method getEmails
+     */
+    getEmails: getEmails,
+    /**
+     * Get one email address and its key pair, if found.  Returns undefined if 
+     * not found.
+     * @method getEmail
+     */
+    getEmail: getEmail,
+    /**
+     * Remove an email address, its key pairs, and any sites associated with 
+     * email address.
+     * @throws "unknown email address" if email address is not known.
+     * @method removeEmail
+     */
     removeEmail: removeEmail,
+    /**
+     * Remove the key information for an email address.
+     * @throws "unknown email address" if email address is not known.
+     * @method invalidateEmail
+     */
     invalidateEmail: invalidateEmail,
-    clearEmails: clearEmails,
+    /**
+     * Set the associated email address for a site
+     * @throws "uknown email address" if the email address is not known.
+     * @method setSiteEmail
+     */
+    setSiteEmail: setSiteEmail,
+    /**
+     * Get the associated email address for a site, if known.  If not known, 
+     * return undefined.
+     * @method getSiteEmail
+     */
+    getSiteEmail: getSiteEmail,
+    /**
+     * Clear all stored data - email addresses, key pairs, temporary key pairs, 
+     * site/email associations.
+     * @method clear
+     */
+    clear: clear,
     storeTemporaryKeypair: storeTemporaryKeypair,
     retrieveTemporaryKeypair: retrieveTemporaryKeypair,
     setStagedOnBehalfOf: setStagedOnBehalfOf,
diff --git a/browserid/static/dialog/resources/browserid-identities.js b/browserid/static/dialog/resources/user.js
similarity index 81%
rename from browserid/static/dialog/resources/browserid-identities.js
rename to browserid/static/dialog/resources/user.js
index f902a72dd2939b33c5680d8b4f4a01566b3da72c..53e4a69fc96f7eeaec5cfc8388911de7271b7661 100644
--- a/browserid/static/dialog/resources/browserid-identities.js
+++ b/browserid/static/dialog/resources/user.js
@@ -35,12 +35,13 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
-BrowserID.Identities = (function() {
+BrowserID.User = (function() {
   "use strict";
 
   var jwk, jwt, vep, jwcert, origin,
       network = BrowserID.Network,
-      storage = BrowserID.Storage;
+      storage = BrowserID.Storage,
+      User;
 
   function prepareDeps() {
     if (!jwk) {
@@ -93,7 +94,7 @@ BrowserID.Identities = (function() {
     $('body')[func]('authenticated');
 
     if (!authenticated) {
-      storage.clearEmails();
+      storage.clear();
     }
   }
 
@@ -127,7 +128,64 @@ BrowserID.Identities = (function() {
     poll();
   }
 
-  var Identities = {
+
+  /**
+   * Certify an identity with the server, persist it to storage if the server 
+   * says the identity is good
+   * @method certifyEmailKeypair
+   */
+  function certifyEmailKeypair(email, keypair, onSuccess, onFailure) {
+    network.certKey(email, keypair.publicKey, function(cert) {
+      persistEmailKeypair(email, keypair, cert, onSuccess, onFailure);
+    }, onFailure);      
+  }
+    
+  /**
+   * Persist an email address without a keypair
+   * @method persistEmail
+   * @param {string} email - Email address to persist.
+   * @param {function} [onSuccess] - Called on successful completion. 
+   * @param {function} [onFailure] - Called on error.
+   */
+  function persistEmail(email, onSuccess, onFailure) {
+    storage.addEmail(email, {
+      created: new Date() 
+    });
+
+    if (onSuccess) {
+      onSuccess();
+    }
+  }
+
+  /** 
+   * Persist an address and key pair locally.
+   * @method persistEmailKeypair
+   * @param {string} email - Email address to persist.
+   * @param {object} keypair - Key pair to save
+   * @param {function} [onSuccess] - Called on successful completion. 
+   * @param {function} [onFailure] - Called on error.
+   */
+  function persistEmailKeypair(email, keypair, cert, onSuccess, onFailure) {
+    var now = new Date();
+    var email_obj = storage.getEmails()[email] || {
+      created: now
+    };
+
+    _.extend(email_obj, {
+      updated: now,
+      pub: keypair.publicKey.toSimpleObject(),
+      priv: keypair.secretKey.toSimpleObject(),
+      cert: cert
+    });
+
+    storage.addEmail(email, email_obj);
+
+    if (onSuccess) {
+      onSuccess();
+    }
+  }
+
+  User = {
     /**
      * Set the interface to use for networking.  Used for unit testing.
      * @method setNetwork
@@ -169,13 +227,8 @@ BrowserID.Identities = (function() {
       // remember this for later
       storage.setStagedOnBehalfOf(origin);
       
-      // FIXME: keysize
       network.createUser(email, origin, function(created) {
         if (onSuccess) {
-          if(created) {
-            self.stagedEmail = email;
-          }
-
           onSuccess(created);
         }
       }, onFailure);
@@ -183,12 +236,12 @@ BrowserID.Identities = (function() {
 
     /**
      * Poll the server until user registration is complete.
-     * @method waitForUserRegistration
+     * @method waitForUserValidation
      * @param {string} email - email address to check.
      * @param {function} [onSuccess] - Called to give status updates.
      * @param {function} [onFailure] - Called on error.
      */
-    waitForUserRegistration: function(email, onSuccess, onFailure) {
+    waitForUserValidation: function(email, onSuccess, onFailure) {
       registrationPoll(network.checkUserRegistration, email, onSuccess, onFailure);
     },
 
@@ -255,7 +308,7 @@ BrowserID.Identities = (function() {
      */
     syncEmails: function(onSuccess, onFailure) {
       cleanupIdentities();
-      var issued_identities = Identities.getStoredEmailKeypairs();
+      var issued_identities = this.getStoredEmailKeypairs();
 
       // FIXME for certs
 
@@ -294,36 +347,13 @@ BrowserID.Identities = (function() {
 
           var email = emails_to_add.shift();
 
-          self.persistEmail(email, addNextEmail, onFailure);
+          persistEmail(email, addNextEmail, onFailure);
         }
 
         addNextEmail();
       });
     },
 
-    /**
-     * Signifies that an identity has been confirmed.
-     * @method confirmEmail
-     * @param {string} email - Email address.
-     * @param {function} [onSuccess] - Called on successful completion. 
-     * @param {function} [onFailure] - Called on error.
-     */
-    confirmEmail: function(email, onSuccess, onFailure) {
-      var self = this;
-      if (email === self.stagedEmail) {
-        self.stagedEmail = null;
-
-        // certify
-        Identities.persistEmail(email, function() {
-          self.syncEmails(onSuccess, onFailure);
-        });
-
-      }
-      else if (onFailure) {
-        onFailure();
-      }
-    },
-
     /**
      * Check whether the current user is authenticated.
      * @method checkAuthentication
@@ -414,7 +444,7 @@ BrowserID.Identities = (function() {
      * otw.
      * @param {function} [onFailure] - Called on XHR failure.
      */
-    emailRegistered: function(email, onSuccess, onFailure) {
+    isEmailRegistered: function(email, onSuccess, onFailure) {
       network.emailRegistered(email, onSuccess, onFailure);
     },
 
@@ -432,8 +462,6 @@ BrowserID.Identities = (function() {
       var self = this;
       network.addEmail(email, origin, function(added) {
         if (added) {
-          self.stagedEmail = email;
-
           // we no longer send the keypair, since we will certify it later.
           if (onSuccess) {
             onSuccess(added);
@@ -442,7 +470,14 @@ BrowserID.Identities = (function() {
       }, onFailure);
     },
 
-    waitForEmailRegistration: function(email, onSuccess, onFailure) {
+    /**
+     * Wait for the email registration to complete
+     * @method waitForEmailValidation
+     * @param {string} email - email address to check.
+     * @param {function} [onSuccess] - Called to give status updates.
+     * @param {function} [onFailure] - Called on error.
+     */
+    waitForEmailValidation: function(email, onSuccess, onFailure) {
       registrationPoll(network.checkEmailRegistration, email, onSuccess, onFailure);
     },
 
@@ -454,12 +489,16 @@ BrowserID.Identities = (function() {
      * @param {function} [onFailure] - Called on failure.
      */
     removeEmail: function(email, onSuccess, onFailure) {
-      network.removeEmail(email, function() {
-        storage.removeEmail(email);
-        if (onSuccess) {
-          onSuccess();
-        }
-      }, onFailure);
+      if(storage.getEmail(email)) {
+        network.removeEmail(email, function() {
+          storage.removeEmail(email);
+          if (onSuccess) {
+            onSuccess();
+          }
+        }, onFailure);
+      } else if(onSuccess) {
+        onSuccess();
+      }
     },
 
     /**
@@ -475,63 +514,9 @@ BrowserID.Identities = (function() {
       // FIXME use true key sizes
       prepareDeps();
       var keypair = jwk.KeyPair.generate(vep.params.algorithm, 64);
-      Identities.certifyEmailKeypair(email, keypair, onSuccess, onFailure);
+      certifyEmailKeypair(email, keypair, onSuccess, onFailure);
     },
 
-    /**
-     * Certify an identity.
-     * @method certifyEmailKeypair
-     */
-    certifyEmailKeypair: function(email, keypair, onSuccess, onFailure) {
-      network.certKey(email, keypair.publicKey, function(cert) {
-        Identities.persistEmailKeypair(email, keypair, cert, onSuccess, onFailure);
-      }, onFailure);      
-    },
-    
-    /**
-     * Persist an email address without a keypair
-     * @method persistEmail
-     * @param {string} email - Email address to persist.
-     * @param {function} [onSuccess] - Called on successful completion. 
-     * @param {function} [onFailure] - Called on error.
-     */
-    persistEmail: function(email, onSuccess, onFailure) {
-      storage.addEmail(email, {
-        created: new Date() 
-      });
-
-      if (onSuccess) {
-        onSuccess();
-      }
-    },
-
-    /** 
-     * Persist an address and key pair locally.
-     * @method persistEmailKeypair
-     * @param {string} email - Email address to persist.
-     * @param {object} keypair - Key pair to save
-     * @param {function} [onSuccess] - Called on successful completion. 
-     * @param {function} [onFailure] - Called on error.
-     */
-    persistEmailKeypair: function(email, keypair, cert, onSuccess, onFailure) {
-      var now = new Date();
-      var email_obj = storage.getEmails()[email] || {
-        created: now
-      };
-
-      _.extend(email_obj, {
-        updated: now,
-        pub: keypair.publicKey.toSimpleObject(),
-        priv: keypair.secretKey.toSimpleObject(),
-        cert: cert
-      });
-
-      storage.addEmail(email, email_obj);
-
-      if (onSuccess) {
-        onSuccess();
-      }
-    },
 
     /**
      * Get an assertion for an identity
@@ -544,17 +529,18 @@ BrowserID.Identities = (function() {
       // we use the current time from the browserid servers
       // to avoid issues with clock drift on user's machine.
       // (issue #329)
-      network.serverTime(function(serverTime) {
-        var storedID = Identities.getStoredEmailKeypairs()[email],
-        assertion;
+        var storedID = storage.getEmail(email),
+            assertion;
 
         function createAssertion(idInfo) {
-          var sk = jwk.SecretKey.fromSimpleObject(idInfo.priv);
-          var tok = new jwt.JWT(null, serverTime, origin);
-          assertion = vep.bundleCertsAndAssertion([idInfo.cert], tok.sign(sk));
-          if (onSuccess) {
-            onSuccess(assertion);
-          }
+          network.serverTime(function(serverTime) {
+            var sk = jwk.SecretKey.fromSimpleObject(idInfo.priv);
+            var tok = new jwt.JWT(null, serverTime, origin);
+            assertion = vep.bundleCertsAndAssertion([idInfo.cert], tok.sign(sk));
+            if (onSuccess) {
+              onSuccess(assertion);
+            }
+          });
         }
 
         if (storedID) {
@@ -566,15 +552,14 @@ BrowserID.Identities = (function() {
           else {
             // we have no key for this identity, go generate the key, 
             // sync it and then get the assertion recursively.
-            Identities.syncEmailKeypair(email, function() {
-              Identities.getAssertion(email, onSuccess, onFailure);
+            User.syncEmailKeypair(email, function() {
+              User.getAssertion(email, onSuccess, onFailure);
             }, onFailure);
           }
         }
         else if (onSuccess) {
           onSuccess();
         }
-      });
     },
 
     /**
@@ -591,13 +576,13 @@ BrowserID.Identities = (function() {
      * @method clearStoredEmailKeypairs
      */
     clearStoredEmailKeypairs: function() {
-      storage.clearEmails();
+      storage.clear();
     },
 
 
   };
 
-  Identities.setOrigin(document.location.host);
+  User.setOrigin(document.location.host);
 
-  return Identities;
+  return User;
 }());
diff --git a/browserid/static/dialog/resources/browserid-wait.js b/browserid/static/dialog/resources/wait-messages.js
similarity index 100%
rename from browserid/static/dialog/resources/browserid-wait.js
rename to browserid/static/dialog/resources/wait-messages.js
diff --git a/browserid/static/dialog/test/qunit/browserid-identities_functional_test.js b/browserid/static/dialog/test/qunit/browserid-identities_functional_test.js
deleted file mode 100644
index ee268426cb1cc99a1117bdad3b7e103da9092e86..0000000000000000000000000000000000000000
--- a/browserid/static/dialog/test/qunit/browserid-identities_functional_test.js
+++ /dev/null
@@ -1,260 +0,0 @@
-/*jshint browsers:true, forin: true, laxbreak: true */
-/*global steal: true, test: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID: true */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Mozilla BrowserID.
- *
- * The Initial Developer of the Original Code is Mozilla.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-/**
- * This test assumes for authentication that there is a user named 
- * "testuser@testuser.com" with the password "testuser"
- */
-steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/browserid-identities", function() {
-  module("browserid-identities");
-
-  function failure(message) {
-    return function() {
-      ok(false, message);
-      start();
-    };
-  }
-
-  test("getStoredIdentities", function() {
-    var identities = BrowserID.Identities.getStoredIdentities();
-    equal("object", typeof identities, "we have some identities");
-  });
-
-  test("clearStoredIdentities", function() {
-    BrowserID.Identities.clearStoredIdentities();
-    var identities = BrowserID.Identities.getStoredIdentities();
-    var count = 0;
-    for(var key in identities) { 
-      if(identities.hasOwnProperty(key)) {
-        count++; 
-      }
-    }
-
-    equal(0, count, "after clearing, there are no identities");
-  });
-
-  test("stageIdentity", function() {
-    BrowserID.Identities.stageIdentity("testuser@testuser.com", "testuser", function(keypair) {
-      equal("object", typeof keypair, "We have a key pair");
-      start();
-    }, failure("stageIdentity failure"));
-
-    stop();
-  });
-
-  test("confirmIdentity", function() {
-  /*  BrowserID.Identities.confirmIdentity("testuser@testuser.com", function() {
-      start();
-    });
-
-    stop();
-    */
-  });
-
-  test("authenticateAndSync", function() {
-    BrowserID.Storage.clearEmails();
-    BrowserID.Identities.authenticateAndSync("testuser@testuser.com", "testuser", function() {
-    }, function() {
-      var identities = BrowserID.Identities.getStoredIdentities();
-      ok("testuser@testuser.com" in identities, "authenticateAndSync syncs email addresses");
-      start();
-    }, failure("Authentication failure"));
-
-    stop();
-
-  });
-
-  test("checkAuthenticationAndSync", function() {
-    BrowserID.Network.authenticate("testuser@testuser.com", "testuser", function() {
-      BrowserID.Storage.clearEmails();
-      BrowserID.Identities.checkAuthenticationAndSync(function() {
-        var identities = BrowserID.Identities.getStoredIdentities();
-        ok("testuser@testuser.com" in identities, "checkAuthenticationAndSync syncs email addresses");
-        start();
-      });
-    }, failure("Authentication failure"));
-
-    stop();
-  });
-
-  test("addIdentity", function() {
-    BrowserID.Network.authenticate("testuser@testuser.com", "testuser", function() {
-      BrowserID.Identities.removeIdentity("testemail@testemail.com", function() {
-        BrowserID.Identities.addIdentity("testemail@testemail.com", function(keypair) {
-          equal("object", typeof keypair, "we have a keypair");
-
-          var identities = BrowserID.Identities.getStoredIdentities();
-          equal(false, "testemail@testemail.com" in identities, "Our new email is not added until confirmation.");
-
-          start();
-        }, failure("addIdentity failure"));
-      }, failure("removeIdentity failure"));
-    }, failure("Authentication failure"));
-
-    stop();
-  });
-
-  /*
-  test("syncIdentity on confirmed email address", function() {
-    BrowserID.Network.authenticate("testuser@testuser.com", "testuser", function() {
-      BrowserID.Identities.removeIdentity("testemail@testemail.com", "issuer", function() {
-        // XXX verify the identity here 
-        BrowserID.Identities.syncIdentity("testemail@testemail.com", "issuer", function(keypair) {
-          ok(false, "Syncing a non-verified identity should fail");
-
-          start();
-        }, failure("syncIdentity failure"));
-      }, failure("removeIdentity failure"));
-    }, failure("Authentication failure"));
-
-    stop();
-  });
-*/
-
-  test("persistIdentity", function() {
-    BrowserID.Identities.persistIdentity("testemail2@testemail.com", { pub: "pub", priv: "priv" });
-    var identities = BrowserID.Identities.getStoredIdentities();
-    ok("testemail2@testemail.com" in identities, "Our new email is added");
-  });
-
-  /*
-  test("removeIdentity that we add", function() {
-    BrowserID.Network.authenticate("testuser@testuser.com", "testuser", function() {
-      BrowserID.Identities.syncIdentity("testemail@testemail.com", "issuer", function(keypair) {
-        BrowserID.Identities.removeIdentity("testemail@testemail.com", function() {
-          var identities = BrowserID.Identities.getStoredIdentities();
-          equal(false, "testemail@testemail.com" in identities, "Our new email is removed");
-          start();
-        }, failure("removeIdentity failure"));
-      }, failure("syncIdentity failure"));
-    }, failure("Authentication failure"));
-
-    stop();
-  });
-  */
-  test("syncIdentities with no identities", function() {
-    BrowserID.Storage.clearEmails();
-    BrowserID.Network.authenticate("testuser@testuser.com", "testuser", function() {
-      BrowserID.Identities.syncIdentities(function onSuccess() {
-        ok(true, "we have synced identities");
-        start();
-      }, failure("identity sync failure"));
-    }, failure("Authentication failure"));
-
-    stop();
-  });
-
-  test("syncIdentities with identities preloaded", function() {
-    BrowserID.Network.authenticate("testuser@testuser.com", "testuser", function() {
-      BrowserID.Identities.syncIdentities(function onSuccess() {
-        ok(true, "we have synced identities");
-        start();
-      }, failure("identity sync failure"));
-    }, failure("Authentication failure"));
-
-    stop();
-  });
-
-  test("getIdentityAssertion", function() {
-    BrowserID.Network.authenticate("testuser@testuser.com", "testuser", function() {
-      BrowserID.Identities.getIdentityAssertion("testuser@testuser.com", function(assertion) {
-        equal("string", typeof assertion, "we have an assertion!");
-        start();
-      });
-    }, failure("Authentication failure"));
-
-    stop();
-  });
-
-  /*
-  test("syncIdentity on non-confirmed email address", function() {
-    BrowserID.Storage.clearEmails();
-    BrowserID.Network.authenticate("testuser@testuser.com", "testuser", function() {
-      BrowserID.Identities.removeIdentity("testemail@testemail.com", function() {
-        BrowserID.Identities.syncIdentity("testemail@testemail.com", "issuer", function(keypair) {
-          ok(false, "Syncing a non-verified identity should fail");
-
-          start();
-        }, function() {
-          ok(true, "trying to sync an identity that is not yet verified should fail");
-
-          var identities = BrowserID.Identities.getStoredIdentities();
-          equal("testemail@testemail.com" in identities, false, "Our new email is added");
-
-          start();
-        });
-      }, failure("removeIdentity failure"));
-    }, failure("Authentication failure"));
-
-    stop();
-  });
-
-  test("syncIdentity without first validating email", function() {
-    BrowserID.Network.authenticate("testuser@testuser.com", "testuser", function() {
-      // First, force removal that way we know it is not part of our list.
-      BrowserID.Identities.removeIdentity("unvalidated@unvalidated.com", function() {
-
-        BrowserID.Storage.clearEmails();
-        BrowserID.Identities.syncIdentities(function onSuccess() {
-
-          var identities = BrowserID.Identities.getStoredIdentities();
-          // Make sure the server has forgotten about this email address.
-          equal("unvalidated@unvalidated.com" in identities, false, "The removed email should not be on the list.");
-
-          // This next call will call /wsapi/set_key on a 
-          // key that has not been validated.
-          BrowserID.Identities.syncIdentity("unvalidated@unvalidated.com", "issuer", function(keypair) {
-            // Clear all the local emails, then refetch the list from the server
-            // just to be sure we are seeing what the server sees.
-            BrowserID.Storage.clearEmails();
-            BrowserID.Identities.syncIdentities(function onSuccess() {
-
-              var identities = BrowserID.Identities.getStoredIdentities();
-              // woah.  Things just went wrong.
-              equal("unvalidated@unvalidated.com" in identities, false, "The unvalidated email should not be added just through calling sync_key");
-              start();
-            }, failure("syncIdentities failure"));
-          }, function() {
-            ok(true, "We expect syncIdentity to fail on an address we cannot confirm");
-          });
-        }, failure("syncIdentities failure"));
-      }, failure("removeEmail failure"));
-    }, failure("Authentication failure"));
-
-    stop();
-  });
-*/
-});
diff --git a/browserid/static/dialog/test/qunit/browserid-network_test.js b/browserid/static/dialog/test/qunit/network_unit_test.js
similarity index 99%
rename from browserid/static/dialog/test/qunit/browserid-network_test.js
rename to browserid/static/dialog/test/qunit/network_unit_test.js
index 46560a6f23e3d81fd59145b000e36b10eb17f102..4da6a9f1856d7535a5c80c26014a76b9b9e55bec 100644
--- a/browserid/static/dialog/test/qunit/browserid-network_test.js
+++ b/browserid/static/dialog/test/qunit/network_unit_test.js
@@ -34,7 +34,7 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
-steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/browserid-network", function() {
+steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", function() {
   "use strict";
 
   var testName;
@@ -130,7 +130,7 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/browserid-netw
   }
 
 
-  module("browserid-network", {
+  module("network", {
     setup: function() {
       network.setXHR(xhr);
       xhr.useResult("valid");
diff --git a/browserid/static/dialog/test/qunit/qunit.js b/browserid/static/dialog/test/qunit/qunit.js
index b66d55a29bc6c2832dcd2775d20bffdabe59586e..9c0289f75ab429893ca827cdbbc14277f93c65ac 100644
--- a/browserid/static/dialog/test/qunit/qunit.js
+++ b/browserid/static/dialog/test/qunit/qunit.js
@@ -7,5 +7,5 @@ steal("/dialog/resources/browserid.js",
   .then("browserid_unit_test")
   .then("validation_unit_test")
   .then("storage_unit_test")
-  .then("browserid-network_test")
-  .then("browserid-identities_unit_test")
+  .then("network_unit_test")
+  .then("user_unit_test")
diff --git a/browserid/static/dialog/test/qunit/storage_unit_test.js b/browserid/static/dialog/test/qunit/storage_unit_test.js
index 2c2f3c2d63564ec0bc83c5a10a122b4fc73d3aa2..352d24113f763ba09c57072714d3c8a228723278 100644
--- a/browserid/static/dialog/test/qunit/storage_unit_test.js
+++ b/browserid/static/dialog/test/qunit/storage_unit_test.js
@@ -37,13 +37,13 @@
 steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/storage", function() {
   var storage = BrowserID.Storage;
 
-  module("BrowserID.Storage", {
+  module("storage", {
     setup: function() {
-      storage.clearEmails();
+      storage.clear();
     },
 
     teardown: function() {
-      storage.clearEmails();
+      storage.clear();
     }
   });
 
@@ -54,14 +54,16 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/storage", func
     equal(_.size(emails), 0, "object should be empty");
   });
 
-  test("addEmail, getEmails", function() {
+  test("addEmail, getEmails, getEmail", function() {
     storage.addEmail("testuser@testuser.com", {priv: "key"});
 
     var emails = storage.getEmails();
     equal(_.size(emails), 1, "object should have one item");
     ok("testuser@testuser.com" in emails, "added email address is there");
-  });
 
+    var id = storage.getEmail("testuser@testuser.com");
+    equal("key", id.priv, "email that was added is retrieved");
+  });
 
   test("removeEmail, getEmails", function() {
     storage.addEmail("testuser@testuser.com", {priv: "key"});
@@ -71,25 +73,90 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/storage", func
     equal(_.size(emails), 0, "object should have no items");
   });
 
+  test("removeEmail with invalid address", function() {
+    var error;
+    try {
+      storage.removeEmail("testuser@testuser.com");
+    }
+    catch(e) {
+      error = e;
+    }
+    equal(error.toString(), "unknown email address", "removing an unknown email address");
+  });
+
 
-  test("clearEmails", function() {
+  test("clear", function() {
     storage.addEmail("testuser@testuser.com", {priv: "key"});
-    storage.clearEmails();
+    storage.clear();
 
     var emails = storage.getEmails();
     equal(_.size(emails), 0, "object should have no items");
   });
 
-  test("invalidateEmail", function() {
+  test("invalidateEmail with valid email address", function() {
     storage.addEmail("testuser@testuser.com", {priv: "key", pub: "pub", cert: "cert"});
 
     storage.invalidateEmail("testuser@testuser.com");
-    var id = storage.getEmails()["testuser@testuser.com"];
+    var id = storage.getEmail("testuser@testuser.com");
     ok(id && !("priv" in id), "private key was removed");
     ok(id && !("pub" in id), "public key was removed");
     ok(id && !("cert" in id), "cert was removed");
   });
 
+  test("invalidateEmail with invalid email address", function() {
+    var error;
+    try {
+      storage.invalidateEmail("testuser@testuser.com");
+    }
+    catch(e) {
+      error = e;
+    }
+    equal(error.toString(), "unknown email address", "Invalidating an unknown email address");
+  });
+
+  test("getSiteEmail with site not found", function() {
+    var email = storage.getSiteEmail("www.testsite.com");
+
+    equal(typeof email, "undefined", "if site not found, returned undefined");
+  });
+
+  test("setSiteEmail with email that is not known about", function() {
+    var error;
+    try {
+      storage.setSiteEmail("www.testsite.com", "testuser@testuser.com");
+    } catch(e) {
+      error = e; 
+    }
+    
+    equal(error.toString(), "unknown email address", "An unknown email address was added");
+  });
+
+  test("setSiteEmail with valid email", function() {
+    storage.addEmail("testuser@testuser.com", {});
+    storage.setSiteEmail("www.testsite.com", "testuser@testuser.com");
+    var email = storage.getSiteEmail("www.testsite.com");
+
+    equal(email, "testuser@testuser.com", "set/get have the same email for the site");
+  });
+
+  test("removeEmail after setSiteEmail removes site", function() {
+    storage.addEmail("testuser@testuser.com", {});
+    storage.setSiteEmail("www.testsite.com", "testuser@testuser.com");
+    storage.removeEmail("testuser@testuser.com");
+    var email = storage.getSiteEmail("www.testsite.com");
+
+    equal(typeof email, "undefined", "after removing an email address, email for site is no longer available");
+  });
+
+  test("clear clears site email info", function() {
+    storage.addEmail("testuser@testuser.com", {});
+    storage.setSiteEmail("www.testsite.com", "testuser@testuser.com");
+    storage.clear();
+    var email = storage.getSiteEmail("www.testsite.com");
+
+    equal(typeof email, "undefined", "after clearing, site email is not found");
+  });
+
   test("storeTemporaryKeypair", function() {
     // XXX needs a test
   });
diff --git a/browserid/static/dialog/test/qunit/browserid-identities_unit_test.js b/browserid/static/dialog/test/qunit/user_unit_test.js
similarity index 77%
rename from browserid/static/dialog/test/qunit/browserid-identities_unit_test.js
rename to browserid/static/dialog/test/qunit/user_unit_test.js
index 50be6a8b48e2a723b129a131920e1e6a32be8079..d9516191bbdaf3356bf018ef9a494c91337342df 100644
--- a/browserid/static/dialog/test/qunit/browserid-identities_unit_test.js
+++ b/browserid/static/dialog/test/qunit/user_unit_test.js
@@ -37,8 +37,8 @@
 var jwk = require("./jwk");
 var jwcert = require("./jwcert");
 
-steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/browserid-identities", function() {
-  var lib = BrowserID.Identities,
+steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/user", function() {
+  var lib = BrowserID.User,
       network = BrowserID.Network,
       storage = BrowserID.Storage;
 
@@ -158,9 +158,10 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/browserid-iden
     }
   };
 
-  module("browserid-identities-unit", {
+  module("user", {
     setup: function() {
       lib.setNetwork(netStub);
+      lib.clearStoredEmailKeypairs();
       netStub.reset();
       userCheckCount = 0;
       emailCheckCount = 0;
@@ -209,22 +210,22 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/browserid-iden
     stop();
   });
 
-  test("waitForUserRegistration with success response", function() {
-    lib.waitForUserRegistration("testuser@testuser.com", function() {
+  test("waitForUserValidation with success response", function() {
+    lib.waitForUserValidation("testuser@testuser.com", function() {
       ok(true);
       start();
-    }, failure("waitForUserRegistration failure"));
+    }, failure("waitForUserValidation failure"));
 
     stop();
   });
 
-  test("waitForUserRegistration with mustAuth response", function() {
+  test("waitForUserValidation with mustAuth response", function() {
     registrationResponse = "mustAuth";
 
-    lib.waitForUserRegistration("testuser@testuser.com", function() {
+    lib.waitForUserValidation("testuser@testuser.com", function() {
       ok(true);
       start();
-    }, failure("waitForUserRegistration failure"));
+    }, failure("waitForUserValidation failure"));
 
     stop();
   });
@@ -249,50 +250,6 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/browserid-iden
     stop();
   });
 
-  test("confirmEmail on staged identity", function() {
-    lib.createUser("testuser@testuser.com", function(created) {
-      lib.confirmEmail("testuser@testuser.com", function() {
-        ok(true, "confirming staged identity");
-        start();
-      });
-    }, failure("createUser failure"));
-
-    stop();
-  });
-
-
-  test("confirmEmail on non staged identity", function() {
-    lib.createUser("testuser@testuser.com", function(created) {
-      lib.confirmEmail("testuser2@testuser.com", function onSuccess() {
-        ok(false, "confirming unstaged identity");
-        start();
-      }, function onFailure() {
-        ok(true, "confirming unstaged identity should fail");
-        start();
-      });
-    }, failure("createUser failure"));
-
-    stop();
-  });
-
-
-  test("confirmEmail on previously confirmed identity", function() {
-    lib.createUser("testuser@testuser.com", function(created) {
-      lib.confirmEmail("testuser@testuser.com", function() {
-        lib.confirmEmail("testuser@testuser.com", function() {
-          ok(false, "confirming previously confirmed identity should fail");
-          start();
-        }, function onFailure() {
-          ok(true, "confirming previously confirmed identity should fail");  
-          start();
-        });
-      });
-    }, failure("createUser failure"));
-
-    stop();
-  });
-
-
 
   test("authenticateAndSync with valid credentials", function() {
     lib.authenticateAndSync("testuser@testuser.com", "testuser", function() {
@@ -375,7 +332,6 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/browserid-iden
   test("authenticateAndSync with valid authentication", function() {
     credentialsValid = true;
     keyRefresh = ["testuser@testuser.com"]; 
-    storage.clearEmails();
 
     lib.authenticateAndSync("testuser@testuser.com", "testuser", function() {
     }, function(authenticated) {
@@ -393,7 +349,6 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/browserid-iden
   test("authenticateAndSync with invalid authentication", function() {
     credentialsValid = false;
     keyRefresh = ["testuser@testuser.com"]; 
-    storage.clearEmails();
 
     lib.authenticateAndSync("testuser@testuser.com", "testuser", function() {
     }, function(authenticated) {
@@ -407,8 +362,8 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/browserid-iden
   });
 
 
-  test("emailRegistered with registered email", function() {
-    lib.emailRegistered("registered", function(registered) {
+  test("isEmailRegistered with registered email", function() {
+    lib.isEmailRegistered("registered", function(registered) {
       ok(registered);
       start();
     }, function onFailure() {
@@ -419,6 +374,18 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/browserid-iden
     stop();
   });
 
+  test("isEmailRegistered with non-registered email", function() {
+    lib.isEmailRegistered("nonregistered", function(registered) {
+      equal(registered, false);
+      start();
+    }, function onFailure() {
+      ok(false);
+      start();
+    });
+
+    stop();
+  });
+
   test("addEmail", function() {
     lib.addEmail("testemail@testemail.com", function(added) {
       ok(added, "user was added");
@@ -433,19 +400,17 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/browserid-iden
   });
 
 
-  test("waitForEmailRegistration", function() {
-    lib.waitForEmailRegistration("testuser@testuser.com", function() {
+  test("waitForEmailValidation", function() {
+    lib.waitForEmailValidation("testuser@testuser.com", function() {
       ok(true);
       start();
-    }, failure("waitForEmailRegistration failure"));
+    }, failure("waitForEmailValidation failure"));
 
     stop();
   });
 
 
   test("syncEmailKeypair with successful sync", function() {
-    storage.clearEmails();
-
     syncValid = true;
     lib.syncEmailKeypair("testemail@testemail.com", function(keypair) {
       var identities = lib.getStoredEmailKeypairs();
@@ -459,8 +424,6 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/browserid-iden
 
 
   test("syncEmailKeypair with invalid sync", function() {
-    storage.clearEmails();
-
     syncValid = false;
     lib.syncEmailKeypair("testemail@testemail.com", function(keypair) {
       ok(false, "sync was invalid, this should have failed");
@@ -476,71 +439,6 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/browserid-iden
   });
 
 
-  test("persistEmail", function() {
-    lib.clearStoredEmailKeypairs();
-
-    lib.persistEmail("testemail@testemail.com", function onSuccess() {
-      var identities = lib.getStoredEmailKeypairs();
-      ok("testemail@testemail.com" in identities, "Our new email is added");
-      start(); 
-    });
-
-    stop();
-  });
-
-
-  test("persistEmailKeypair with new email", function() {
-    lib.clearStoredEmailKeypairs();
-
-    var user_kp = jwk.KeyPair.generate("RS",64);
-    lib.persistEmailKeypair("testemail@testemail.com", user_kp, "cert", function onSuccess() {
-      var id = lib.getStoredEmailKeypairs()["testemail@testemail.com"];
-
-      ok(id, "Email is added");
-      ok(id.created, "A create date is generated");
-      ok(id.updated, "An updated date is generated");
-      equal(id.created, id.updated, "Create and update dates are the same");
-
-      ok(id.pub, "A public key is generated");
-      ok(id.priv, "A private key is generated");
-      ok(id.cert, "A certificate is generated");
-
-      start(); 
-    });
-
-    stop();
-  });
-
-  test("persistEmailKeypair with already saved email", function() {
-    lib.clearStoredEmailKeypairs();
-
-    var user_kp = jwk.KeyPair.generate("RS",64);
-    lib.persistEmailKeypair("testemail@testemail.com", user_kp, "cert", function onSuccess() {
-      setTimeout(function() {
-        lib.persistEmailKeypair("testemail@testemail.com", user_kp, "cert", function onSuccess() {
-
-        var id = lib.getStoredEmailKeypairs()["testemail@testemail.com"];
-
-        ok(id, "Email is added");
-        ok(id.created, "A create date is generated");
-        ok(id.updated, "An updated date is generated");
-        notEqual(id.created, id.updated, "Create and update dates are NOT the same when an address is updated");
-
-        ok(id.pub, "A public key is generated");
-        ok(id.priv, "A private key is generated");
-        ok(id.cert, "A certificate is generated");
-
-        start(); 
-        });
-      }, 500);
-    });
-
-    stop();
-
-  });
-
-
-
   test("removeEmail that is added", function() {
     storage.addEmail("testemail@testemail.com", {pub: "pub", priv: "priv"});
 
@@ -556,8 +454,6 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/browserid-iden
 
 
   test("removeEmail that is not added", function() {
-    storage.clearEmails();
-
     lib.removeEmail("testemail@testemail.com", function() {
       var identities = lib.getStoredEmailKeypairs();
       equal(false, "testemail@testemail.com" in identities, "Our new email is removed");
@@ -570,7 +466,6 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/browserid-iden
 
 
   test("syncEmails with no pre-loaded identities and no identities to add", function() {
-    storage.clearEmails();
     userEmails = {};
 
     lib.syncEmails(function onSuccess() {
@@ -584,7 +479,6 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/browserid-iden
   });
 
   test("syncEmails with no pre-loaded identities and identities to add", function() {
-    storage.clearEmails();
     userEmails = {"testuser@testuser.com": {}};
 
     lib.syncEmails(function onSuccess() {
@@ -598,7 +492,6 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/browserid-iden
   });
 
   test("syncEmails with identities preloaded and none to add", function() {
-    storage.clearEmails();
     userEmails = {"testuser@testuser.com": {}};
     storage.addEmail("testuser@testuser.com", {});
     lib.syncEmails(function onSuccess() {
@@ -613,7 +506,6 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/browserid-iden
 
 
   test("syncEmails with identities preloaded and one to add", function() {
-    storage.clearEmails();
     storage.addEmail("testuser@testuser.com", {pubkey: pubkey, cert: random_cert});
     userEmails = {"testuser@testuser.com": {pubkey: pubkey, cert: random_cert},
                   "testuser2@testuser.com": {pubkey: pubkey, cert: random_cert}};
@@ -631,7 +523,6 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/browserid-iden
 
 
   test("syncEmails with identities preloaded and one to remove", function() {
-    storage.clearEmails();
     storage.addEmail("testuser@testuser.com", {pub: pubkey, cert: random_cert});
     storage.addEmail("testuser2@testuser.com", {pub: pubkey, cert: random_cert});
     userEmails = {"testuser@testuser.com":  { pub: pubkey, cert: random_cert}};
@@ -649,36 +540,30 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/browserid-iden
 
 
   test("getAssertion with known email that has key", function() {
-    storage.clearEmails();
-    var keypair = jwk.KeyPair.generate("RS",64);
-    lib.certifyEmailKeypair("testuser@testuser.com", keypair, function() {
+    lib.syncEmailKeypair("testuser@testuser.com", function() {
       lib.getAssertion("testuser@testuser.com", function onSuccess(assertion) {
         equal("string", typeof assertion, "we have an assertion!");
         start();
       }, failure("getAssertion failure"));
-    }, failure("certifyEmailKeypair failure"));
+    }, failure("syncEmailKeypair failure"));
 
     stop();
   });
 
 
   test("getAssertion with known email that does not have a key", function() {
-    storage.clearEmails();
-    lib.persistEmail("testuser@testuser.com", function() {
-      lib.getAssertion("testuser@testuser.com", function onSuccess(assertion) {
-        equal("string", typeof assertion, "we have an assertion!");
-        start();
-      }, failure("getAssertion failure"));
-    }, failure("persistEmail failure"));
+    storage.addEmail("testuser@testuser.com", {});
+    lib.getAssertion("testuser@testuser.com", function onSuccess(assertion) {
+      equal("string", typeof assertion, "we have an assertion!");
+      start();
+    }, failure("getAssertion failure"));
 
     stop();
   });
 
 
   test("getAssertion with unknown email", function() {
-    storage.clearEmails();
-    var keypair = jwk.KeyPair.generate("RS",64);
-    lib.certifyEmailKeypair("testuser@testuser.com", keypair, function() {
+    lib.syncEmailKeypair("testuser@testuser.com", function() {
       lib.getAssertion("testuser2@testuser.com", function onSuccess(assertion) {
         equal("undefined", typeof assertion, "email was unknown, we do not have an assertion");
         start();
@@ -691,7 +576,6 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/browserid-iden
   test("logoutUser", function(onSuccess) {
     credentialsValid = true;
     keyRefresh = ["testuser@testuser.com"]; 
-    storage.clearEmails();
 
     lib.authenticateAndSync("testuser@testuser.com", "testuser", function() {
     }, function(authenticated) {
diff --git a/browserid/static/dialog/test/qunit/validation_unit_test.js b/browserid/static/dialog/test/qunit/validation_unit_test.js
index 5a0be014fb44be2d3ead64b13b6f5126a564d17c..1366573ce557a706e38e0d79ae67a7ad66b5a4f6 100644
--- a/browserid/static/dialog/test/qunit/validation_unit_test.js
+++ b/browserid/static/dialog/test/qunit/validation_unit_test.js
@@ -46,7 +46,7 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/browserid", fu
     tooltipShown = true;
   }
 
-  module("validation-unit", {
+  module("validation", {
     setup: function() {
       origShowTooltip = bid.Tooltip.showTooltip;
       bid.Tooltip.showTooltip = showTooltip;
diff --git a/browserid/static/js/browserid.js b/browserid/static/js/browserid.js
index 1f4030df7e70b4f076fde39b0970b7304c7be0a1..7ed223b97bc95ec1492183dba38ccddfda3c44c9 100644
--- a/browserid/static/js/browserid.js
+++ b/browserid/static/js/browserid.js
@@ -57,7 +57,7 @@ $(function() {
   var token = getParameterByName("token"),
       path = document.location.pathname,
       bid = BrowserID,
-      identities = bid.Identities;
+      user = bid.User;
 
   if (!path || path === "/") {
     bid.index();
@@ -85,12 +85,12 @@ $(function() {
   $(".signOut").click(function(event) {
     event.preventDefault();
 
-    identities.logoutUser(function() {
+    user.logoutUser(function() {
       document.location = "/";
     });
   });
 
-  identities.checkAuthentication(function(authenticated) {
+  user.checkAuthentication(function(authenticated) {
     if (authenticated) {
       $("#content").fadeIn("slow");
       if ($('#emailList').length) {
diff --git a/browserid/static/js/pages/forgot.js b/browserid/static/js/pages/forgot.js
index bbd205ee04fabdceaadb01a5bd417fe99a4ea923..1ab1bb6f0d1b4a9732829c8b8f1ac8df2d8dbe01 100644
--- a/browserid/static/js/pages/forgot.js
+++ b/browserid/static/js/pages/forgot.js
@@ -53,7 +53,7 @@ BrowserID.forgot = (function() {
         return false;
       }
 
-      BrowserID.Identities.createUser(email, function onSuccess(keypair) {
+      BrowserID.User.createUser(email, function onSuccess(keypair) {
         $('#sent_to_email').html(email);
         $('#forminputs').fadeOut();
         $(".notifications .notification.emailsent").fadeIn();
diff --git a/browserid/static/js/pages/manage_account.js b/browserid/static/js/pages/manage_account.js
index ddce8179fa38602070038c159a1f720f9c98daa1..0260199f8cf6f3a9e04e27bf2c6f9d9bc39fd0fc 100644
--- a/browserid/static/js/pages/manage_account.js
+++ b/browserid/static/js/pages/manage_account.js
@@ -37,6 +37,8 @@
 (function() {
   "use strict";
 
+  var User = BrowserID.User;
+
   function relativeDate(date) {
     var diff = (((new Date()).getTime() - date.getTime()) / 1000),
         day_diff = Math.floor(diff / 86400),
@@ -122,8 +124,8 @@
   function syncAndDisplayEmails() {
     var emails = {};
 
-    BrowserID.Identities.syncEmails(function() {
-      emails = BrowserID.Identities.getStoredEmailKeypairs();
+    User.syncEmails(function() {
+      emails = User.getStoredEmailKeypairs();
       if (_.isEmpty(emails)) {
         $("#content").hide();
       } else {
@@ -163,7 +165,7 @@
     event.preventDefault();
 
     if (confirm("Remove " + email + " from your BrowserID?")) {
-      BrowserID.Identities.removeEmail(email, syncAndDisplayEmails);
+      User.removeEmail(email, syncAndDisplayEmails);
     }
   }
 
diff --git a/browserid/static/js/pages/signup.js b/browserid/static/js/pages/signup.js
index b834fcb7046f6f1bd3e9172bf22d722b181bd72e..7539111a44c17771e6560dfce7ec6220b0a0c4f3 100644
--- a/browserid/static/js/pages/signup.js
+++ b/browserid/static/js/pages/signup.js
@@ -38,7 +38,7 @@
   "use strict";
 
   var bid = BrowserID,
-      identities = bid.Identities,
+      user = bid.User,
       ANIMATION_SPEED = 250;
 
   bid.signUp = function () {
@@ -77,9 +77,9 @@
           return;
         }
 
-        identities.emailRegistered(email, function(registered) {
+        user.emailRegistered(email, function(registered) {
           if (!registered) {
-            identities.createUser(email, function onSuccess(keypair) {
+            user.createUser(email, function onSuccess(keypair) {
               $('#sentToEmail').html(email);
               replaceWithMessage(".emailsent");
             }, onFailure);
diff --git a/browserid/views/layout.ejs b/browserid/views/layout.ejs
index d3abf4d625e6381816eba13b64e88049878f5d4f..a2d77390ebdc8e5bc477ca99a15c1f33b3f79ae1 100644
--- a/browserid/views/layout.ejs
+++ b/browserid/views/layout.ejs
@@ -22,8 +22,8 @@
     <script src="/js/browserid.js" type="text/javascript"></script>
     <script src="/js/pages/index.js" type="text/javascript"></script>
     <script src="/dialog/resources/storage.js" type="text/javascript"></script>
-    <script src="/dialog/resources/browserid-network.js" type="text/javascript"></script>
-    <script src="/dialog/resources/browserid-identities.js" type="text/javascript"></script>
+    <script src="/dialog/resources/network.js" type="text/javascript"></script>
+    <script src="/dialog/resources/user.js" type="text/javascript"></script>
     <script src="/dialog/resources/tooltip.js" type="text/javascript"></script>
     <script src="/dialog/resources/validation.js" type="text/javascript"></script>
     <script src="/js/pages/add_email_address.js" type="text/javascript"></script>