diff --git a/bin/browserid b/bin/browserid
index d4fcfc3c749f7d8f8dceab1186c26ffadedcee1b..e1db01419e9a46588e9280758af75b5fdf11cfe9 100755
--- a/bin/browserid
+++ b/bin/browserid
@@ -8,7 +8,7 @@ const
 fs = require('fs'),
 path = require('path'),
 url = require('url'),
-http = require('http');
+http = require('http'),
 urlparse = require('urlparse'),
 express = require('express');
 
@@ -62,7 +62,7 @@ app.use(i18n.abide({
 
 var statsd_config = config.get('statsd');
 if (statsd_config && statsd_config.enabled) {
-  logger_statsd = require("connect-logger-statsd");
+  var logger_statsd = require("connect-logger-statsd");
   app.use(logger_statsd({
     host: statsd_config.hostname || "localhost",
     port: statsd_config.port || 8125,
@@ -157,7 +157,7 @@ db.open(config.get('database'), function (error) {
   // shut down express gracefully on SIGINT
   shutdown.handleTerminationSignals(app, function(readyForShutdownCB) {
     require('../lib/bcrypt.js').shutdown();
-    db.close(readyForShutdownCB)
+    db.close(readyForShutdownCB);
   });
 
   var bindTo = config.get('bind_to');
diff --git a/example/primary/provision.html b/example/primary/provision.html
index c8b7cd583c4c38008d9df3181fa2ee3539bbce92..01394cb48057b0a3ac2cdb3863b538686876e991 100644
--- a/example/primary/provision.html
+++ b/example/primary/provision.html
@@ -9,9 +9,6 @@
 <script type="text/javascript" src="/jquery.js"></script>
 <script type="text/javascript">
 
-  // an alias
-  var fail = navigator.id.raiseProvisioningFailure;
-
   // begin provisioning!  This both gives us indicated to browserid that we're
   // a well formed provisioning page and gives us the parameters of the provisioning
   navigator.id.beginProvisioning(function(email, cert_duration) {
@@ -22,7 +19,7 @@
     $.get('/api/whoami')
       .success(function(who) {
         if (user != who) {
-          return fail('user is not authenticated as target user');
+          return navigator.id.raiseProvisioningFailure('user is not authenticated as target user');
         }
 
         // Awesome!  The user is authenticated as who we want to provision.  let's
@@ -44,13 +41,13 @@
               navigator.id.registerCertificate(r.cert);
             },
             error: function(r) {
-              fail("couldn't certify key");
+              navigator.id.raiseProvisioningFailure("couldn't certify key");
             }
           });
         });
       })
       .error(function() {
-        fail('user is not authenticated');
+        navigator.id.raiseProvisioningFailure('user is not authenticated');
       });
   });
 </script>
diff --git a/example/primary/sign_in.html b/example/primary/sign_in.html
index 51a0eaccae58c47b4ffa31f88220cb0a9b2aec60..838080d2fb96fb83dfe87695538437e7269e1d4d 100644
--- a/example/primary/sign_in.html
+++ b/example/primary/sign_in.html
@@ -33,37 +33,30 @@ button { line-height: 20px; }
 </div>
 
 <script type="text/javascript" src="jquery.js"></script>
+<script type="text/javascript" src="https://login.persona.org/authentication_api.js"></script>
 <script type="text/javascript">
 
-function getParameterByName(name)
-{
-  name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
-  var regexS = "[\\?&]" + name + "=([^&#]*)";
-  var regex = new RegExp(regexS);
-  var results = regex.exec(window.location.href);
-  if(results == null)
-    return "";
-  else
-    return decodeURIComponent(results[1].replace(/\+/g, " "));
-}
+var who = null;
 
 $(document).ready(function() {
   try {
-    var who = getParameterByName("email").replace(/@.*$/, "");
-    $('#who').text(who);
+    navigator.id.beginAuthentication(function(email) {
+      who = /^([^@]+)@/.exec(email)[1];
+      $('#who').text(who);
+    });
   } catch(e) {
     alert("uh oh: " + e);
   }
   $("button").click(function(e) {
     $.get('/api/login', { user: who })
       .success(function(r) {
-        window.location = getParameterByName('return_to');
+        navigator.id.completeAuthentication();
       });
   });
 
   $("#cancel").click(function(e) {
     e.preventDefault();
-    window.location = getParameterByName('return_to');
+    navigator.id.raiseAuthenticationFailure('cancel');
   });
 });
 </script>
diff --git a/example/rp/index.html b/example/rp/index.html
index 19ddc0533b343d33ef00de82318cc0f11ddf8655..50f5808a8da6400b14728ef5d541909b156e29a3 100644
--- a/example/rp/index.html
+++ b/example/rp/index.html
@@ -77,9 +77,6 @@ pre {
       <input type="checkbox" id="returnTo">
       <label for="returnTo">Supply returnTo</label><br />
     </li>
-    </li><li>
-      <input type="text" id="requiredEmail" width="80">
-      <label for="requiredEmail">Require a specific email</label><br />
     </li>
   </ul>
     <button class="assertion">Get an assertion</button>
@@ -181,15 +178,6 @@ navigator.id.watch({
 
 $(document).ready(function() {
   $(".specify button.assertion").click(function() {
-    var requiredEmail = $.trim($('#requiredEmail').val());
-    if (!requiredEmail.length) requiredEmail = undefined;
-    if (requiredEmail && requiredEmail.indexOf('@') === -1) {
-      alert('Invalid Email in "Require a specific email" field. Adding @example.com');
-      $('#requiredEmail').val(requiredEmail + '@example.com');
-      $('#requiredEmail').focus();
-      return;
-    }
-
     $(".specify button.assertion").attr('disabled', 'true');
 
     navigator.id.request({
@@ -198,7 +186,6 @@ $(document).ready(function() {
       siteName: $('#siteName').attr('checked') ? "Persona Test Relying Party" : undefined,
       siteLogo: $('#siteLogo').attr('checked') ? "/i/logo.png" : undefined,
       returnTo: $('#returnTo').attr('checked') ? "/postVerificationReturn.html" : undefined,
-      requiredEmail: requiredEmail,
       oncancel: function() {
         loggit("oncancel");
         $(".specify button.assertion").removeAttr('disabled');
diff --git a/lib/configuration.js b/lib/configuration.js
index 0c5b68dce08d4963c117b79ced4bf9663e2d4c09..1d0bd7a30883b49af16ba955e5068e698a53a367 100644
--- a/lib/configuration.js
+++ b/lib/configuration.js
@@ -108,10 +108,6 @@ var conf = module.exports = convict({
       format: 'string?',
       env: 'DATABASE_NAME'
     },
-    password: {
-      format: 'string?',
-      env: 'MYSQL_PASSWORD'
-    },
     max_query_time_ms: {
       format: 'integer = 5000',
       doc: "The maximum amount of time we'll allow a query to run before considering the database to be sick",
diff --git a/lib/static_resources.js b/lib/static_resources.js
index 7a6863eab6dc14be472780fe7f44b08d8859fd97..7b14d260da15c063e0260419c81c2420b4c03eb8 100644
--- a/lib/static_resources.js
+++ b/lib/static_resources.js
@@ -131,6 +131,9 @@ exports.resources = {
     '/common/css/ie8.css',
     '/dialog/css/ie8.css'
   ],
+  '/production/html5shim.js': [
+    '/common/js/lib/html5shim.js'
+  ],
   '/production/communication_iframe.js': [
     '/common/js/lib/jschannel.js',
     '/common/js/lib/winchan.js',
diff --git a/resources/static/authentication_api.js b/resources/static/authentication_api.js
index 5bc44cd704cabf66c811e6933da34cc720c4bc3a..ca9b16c49990977dac2d828dacfdf2a9ea669571 100644
--- a/resources/static/authentication_api.js
+++ b/resources/static/authentication_api.js
@@ -23,7 +23,6 @@
       return decodeURIComponent(results[1].replace(/\+/g, " "));
   }
 
-
   if (!navigator.id.beginAuthentication || navigator.id._primaryAPIIsShimmed) {
     navigator.id.beginAuthentication = function(cb) {
       if (typeof cb !== 'function') {
@@ -34,11 +33,11 @@
     };
 
     navigator.id.completeAuthentication = function(cb) {
-      window.location = getParameterByName('return_to');
+      window.location = 'https://login.persona.org/sign_in#AUTH_RETURN';
     };
 
     navigator.id.raiseAuthenticationFailure = function(reason) {
-      window.location = getParameterByName('return_to');
+      window.location = 'https://login.persona.org/sign_in#AUTH_RETURN_CANCEL';
     };
 
     navigator.id._primaryAPIIsShimmed = true;
diff --git a/resources/static/dialog/js/modules/dialog.js b/resources/static/dialog/js/modules/dialog.js
index 7e40c8c531c50ae78e90a79a06045908ef0a2b49..8a4169fa8e5a3467885c6740109beb6b7ff1a9bc 100644
--- a/resources/static/dialog/js/modules/dialog.js
+++ b/resources/static/dialog/js/modules/dialog.js
@@ -12,6 +12,7 @@ BrowserID.Modules.Dialog = (function() {
       user = bid.User,
       errors = bid.Errors,
       dom = bid.DOM,
+      helpers = bid.Helpers,
       win = window,
       startExternalDependencies = true,
       channel,
@@ -155,9 +156,7 @@ BrowserID.Modules.Dialog = (function() {
       // verify params
       try {
         if (paramsFromRP.requiredEmail) {
-          if (!bid.verifyEmail(paramsFromRP.requiredEmail))
-            throw "invalid requiredEmail: (" + paramsFromRP.requiredEmail + ")";
-          params.requiredEmail = paramsFromRP.requiredEmail;
+          helpers.log("requiredEmail has been deprecated");
         }
 
         // support old parameter names...
@@ -192,24 +191,19 @@ BrowserID.Modules.Dialog = (function() {
           user.setReturnTo(returnTo);
         }
 
-
-        if (hash.indexOf("#CREATE_EMAIL=") === 0) {
-          var email = hash.replace(/#CREATE_EMAIL=/, "");
-          if (!bid.verifyEmail(email))
-            throw "invalid #CREATE_EMAIL= (" + email + ")";
-          params.type = "primary";
-          params.email = email;
-          params.add = false;
-        }
-        else if (hash.indexOf("#ADD_EMAIL=") === 0) {
-          var email = hash.replace(/#ADD_EMAIL=/, "");
-          if (!bid.verifyEmail(email))
-            throw "invalid #ADD_EMAIL= (" + email + ")";
+        if (hash.indexOf("#AUTH_RETURN") === 0) {
+          var primaryParams = JSON.parse(win.sessionStorage.primaryVerificationFlow);
+          params.email = primaryParams.email;
+          params.add = primaryParams.add;
           params.type = "primary";
-          params.email = email;
-          params.add = true;
+
+          // FIXME: if it's AUTH_RETURN_CANCEL, we should short-circuit
+          // the attempt at provisioning. For now, we let provisioning
+          // be tried and fail.
         }
 
+        // no matter what, we clear the primary flow state for this window
+        win.sessionStorage.primaryVerificationFlow = undefined;
       } catch(e) {
         // note: renderError accepts HTML and cheerfully injects it into a
         // frame with a powerful origin. So convert 'e' first.
diff --git a/resources/static/dialog/js/modules/verify_primary_user.js b/resources/static/dialog/js/modules/verify_primary_user.js
index c92fdb25219fcb4f666c1ff5e3493e41b9f2c7c5..8a35914e2665c6a07d5814f439da440e5699a752 100644
--- a/resources/static/dialog/js/modules/verify_primary_user.js
+++ b/resources/static/dialog/js/modules/verify_primary_user.js
@@ -20,15 +20,14 @@ BrowserID.Modules.VerifyPrimaryUser = (function() {
   function verify(callback) {
     this.publish("primary_user_authenticating");
 
-    // replace any hashes that may be there already.
-    var returnTo = win.document.location.href.replace(/#.*$/, "");
-
-    var type = add ? "ADD_EMAIL" : "CREATE_EMAIL";
-    var url = helpers.toURL(auth_url, {
-      email: email,
-      return_to: returnTo + "#" + type + "=" +email
+    // set up some information about what we're doing
+    win.sessionStorage.primaryVerificationFlow = JSON.stringify({
+      add: add,
+      email: email
     });
 
+    var url = helpers.toURL(auth_url, {email: email});
+    
     win.document.location = url;
 
     complete(callback);
diff --git a/resources/static/dialog/js/start.js b/resources/static/dialog/js/start.js
index 70c574bad3f2e645532c48aee54fdec314b886fc..3536e1f25e1b9f346debb65f5ecc0966f04d0d21 100644
--- a/resources/static/dialog/js/start.js
+++ b/resources/static/dialog/js/start.js
@@ -15,7 +15,7 @@
   network.init();
 
   var hash = window.location.hash || "",
-      continuation = hash.indexOf("#CREATE_EMAIL") > -1 || hash.indexOf("#ADD_EMAIL") > -1;
+      continuation = hash.indexOf("#AUTH_RETURN") > -1;
 
   moduleManager.register("interaction_data", modules.InteractionData);
   moduleManager.start("interaction_data", { continuation: continuation });
diff --git a/resources/static/include_js/include.js b/resources/static/include_js/include.js
index f81e73d4356726eac2b5ebb3ba0334d0cb029f04..3516f7af707e0fc15aa87f1f4140ec8b68b34539 100644
--- a/resources/static/include_js/include.js
+++ b/resources/static/include_js/include.js
@@ -1027,18 +1027,42 @@
 
       _open_hidden_iframe();
 
+      // back compat support for loggedInEmail
+      if (typeof options.loggedInEmail !== 'undefined' &&
+          typeof options.loggedInUser !== 'undefined') {
+        throw "you cannot supply *both* loggedInEmail and loggedInUser";
+      }
+      else if(typeof options.loggedInEmail !== 'undefined') {
+        try {
+          console.log("loggedInEmail has been deprecated");
+        } catch(e) {
+          /* ignore error */
+        }
+
+        options.loggedInUser = options.loggedInEmail;
+        delete options.loggedInEmail;
+      }
+
       // check that the commChan was properly initialized before interacting with it.
       // on unsupported browsers commChan might still be undefined, in which case
       // we let the dialog display the "unsupported browser" message upon spawning.
-      if (typeof options.loggedInEmail !== 'undefined' && commChan) {
+      if (typeof options.loggedInUser !== 'undefined' && commChan) {
         commChan.notify({
           method: 'loggedInUser',
-          params: options.loggedInEmail
+          params: options.loggedInUser
         });
       }
     }
 
     function internalRequest(options) {
+      if (options.requiredEmail) {
+        try {
+          console.log("requiredEmail has been deprecated");
+        } catch(e) {
+          /* ignore error */
+        }
+      }
+
       // focus an existing window
       if (w) {
         try {
diff --git a/resources/static/test/cases/dialog/js/modules/dialog.js b/resources/static/test/cases/dialog/js/modules/dialog.js
index e7a75af818d6174a4737a623b8c99df74ca15066..2519af8c14c3c13e0c77ae4b66b80fcd156036fc 100644
--- a/resources/static/test/cases/dialog/js/modules/dialog.js
+++ b/resources/static/test/cases/dialog/js/modules/dialog.js
@@ -50,6 +50,8 @@
     },
 
     navigator: {},
+
+    sessionStorage: {}
   };
 
   function createController(config) {
@@ -134,8 +136,12 @@
     });
   });
 
-  asyncTest("initialization with #CREATE_EMAIL=testuser@testuser.com - trigger start with correct params", function() {
-    winMock.location.hash = "#CREATE_EMAIL=testuser@testuser.com";
+  asyncTest("initialization with #AUTH_RETURN and add=false - trigger start with correct params", function() {
+    winMock.location.hash = "#AUTH_RETURN";
+    winMock.sessionStorage.primaryVerificationFlow = JSON.stringify({
+      add: false,
+      email: TESTEMAIL
+    });
 
     createController({
       ready: function() {
@@ -157,8 +163,12 @@
     });
   });
 
-  asyncTest("initialization with #ADD_EMAIL=testuser@testuser.com - trigger start with correct params", function() {
-    winMock.location.hash = "#ADD_EMAIL=testuser@testuser.com";
+  asyncTest("initialization with #AUTH_RETURN and add=true - trigger start with correct params", function() {
+    winMock.location.hash = "#AUTH_RETURN";
+    winMock.sessionStorage.primaryVerificationFlow = JSON.stringify({
+      add: true,
+      email: TESTEMAIL
+    });
 
     createController({
       ready: function() {
@@ -198,64 +208,6 @@
     });
   });
 
-  asyncTest("get with invalid requiredEmail - print error screen", function() {
-    createController({
-      ready: function() {
-        mediator.subscribe("start", function(msg, info) {
-          ok(false, "start should not have been called");
-        });
-
-        var retval = controller.get(HTTP_TEST_DOMAIN, {
-          requiredEmail: "bademail"
-        });
-        equal(retval, "invalid requiredEmail: (bademail)", "expected error");
-        testErrorVisible();
-        start();
-      }
-    });
-  });
-
-  asyncTest("get with script containing requiredEmail - print error screen", function() {
-    createController({
-      ready: function() {
-        mediator.subscribe("start", function(msg, info) {
-          ok(false, "start should not have been called");
-        });
-
-        var retval = controller.get(HTTP_TEST_DOMAIN, {
-          requiredEmail: "<script>window.scriptRun=true;</script>testuser@testuser.com"
-        });
-
-        // If requiredEmail is not properly escaped, scriptRun will be true.
-        equal(typeof window.scriptRun, "undefined", "script was not run");
-        equal(retval, "invalid requiredEmail: (<script>window.scriptRun=true;</script>testuser@testuser.com)", "expected error");
-        testErrorVisible();
-        start();
-      }
-    });
-  });
-
-  asyncTest("get with valid requiredEmail - go to start", function() {
-    createController({
-      ready: function() {
-        var startInfo;
-        mediator.subscribe("start", function(msg, info) {
-          startInfo = info;
-        });
-
-        var retval = controller.get(HTTP_TEST_DOMAIN, {
-          requiredEmail: TESTEMAIL
-        });
-
-        testHelpers.testObjectValuesEqual(startInfo, {
-          requiredEmail: TESTEMAIL
-        });
-        equal(typeof retval, "undefined", "no error expected");
-        testErrorNotVisible();
-        start();
-      }
-    });
-  });
 
   asyncTest("get with relative termsOfService & valid privacyPolicy - print error screen", function() {
     createController({
diff --git a/resources/static/test/cases/dialog/js/modules/verify_primary_user.js b/resources/static/test/cases/dialog/js/modules/verify_primary_user.js
index bde1c0d47b4396c0433077e6796ed4d4b6ad568b..08083a9fe9d3d0ef7908a0d18c1e958a8bed00e9 100644
--- a/resources/static/test/cases/dialog/js/modules/verify_primary_user.js
+++ b/resources/static/test/cases/dialog/js/modules/verify_primary_user.js
@@ -61,8 +61,7 @@
     testElementNotExists("#persona_tospp");
   });
 
-
-  asyncTest("submit with `add: false` option opens a new tab with CREATE_EMAIL URL", function() {
+  asyncTest("submit with `add: false` option opens a new tab with proper URL (updated for sessionStorage)", function() {
     var messageTriggered = false;
     createController({
       window: win,
@@ -83,13 +82,13 @@
     win.document.location.hash = "#NATIVE";
 
     controller.submit(function() {
-      equal(win.document.location, "http://testuser.com/sign_in?email=unregistered%40testuser.com&return_to=sign_in%23CREATE_EMAIL%3Dunregistered%40testuser.com");
+      equal(win.document.location, "http://testuser.com/sign_in?email=unregistered%40testuser.com");
       equal(messageTriggered, true, "primary_user_authenticating triggered");
       start();
     });
   });
 
-  asyncTest("submit with `add: true` option opens a new tab with ADD_EMAIL URL", function() {
+  asyncTest("submit with `add: true` option opens a new tab with proper URL (updated for sessionStorage)", function() {
     createController({
       window: win,
       add: true,
@@ -105,7 +104,7 @@
     win.document.location.hash = "#NATIVE";
 
     controller.submit(function() {
-      equal(win.document.location, "http://testuser.com/sign_in?email=unregistered%40testuser.com&return_to=sign_in%23ADD_EMAIL%3Dunregistered%40testuser.com");
+      equal(win.document.location, "http://testuser.com/sign_in?email=unregistered%40testuser.com");
       start();
     });
   });
diff --git a/resources/static/test/mocks/window.js b/resources/static/test/mocks/window.js
index 510514288a7897747ba90d6c00b38be5641a0ba4..a633ba0d20180139f7bcca5d6230f0d30379c1aa 100644
--- a/resources/static/test/mocks/window.js
+++ b/resources/static/test/mocks/window.js
@@ -15,6 +15,7 @@ BrowserID.Mocks.WindowMock = (function() {
 
   function WindowMock() {
     this.document = new DocumentMock();
+    this.sessionStorage = {};
   }
   WindowMock.prototype = {
     open: function(url, name, options) {
diff --git a/resources/views/about.ejs b/resources/views/about.ejs
index b16b28ac52a5a143b96774b0c7622c6b9e29b890..4ea433948b01e4d7b87b5c5957fad13fcda54322 100644
--- a/resources/views/about.ejs
+++ b/resources/views/about.ejs
@@ -13,13 +13,13 @@
                 </div>
 
                 <div class="graphic">
-                    <img src="/pages/i/one-password-graphic.png" alt="One password to rule them all.">
+                    <img src="<%- cachify('/pages/i/one-password-graphic.png') %>" alt="One password to rule them all.">
                 </div>
             </article>
 
             <article class="blurb flexible">
                 <div class="graphic first">
-                    <img src="/pages/i/flexible-graphic.png" alt="Use multiple email addresses">
+                    <img src="<%- cachify('/pages/i/flexible-graphic.png') %>" alt="Use multiple email addresses">
                 </div>
 
                 <div class="info">
@@ -42,7 +42,7 @@
             </article>
         </section>
 
-        <a href="https://developer.mozilla.org/en/BrowserID/Quick_Setup" class="developers"><img src="/pages/i/developers-link.png" alt="Persona for developers"><span>Implement Persona on your site </span>Developer guides and API documentation</a>
+        <a href="https://developer.mozilla.org/en/BrowserID/Quick_Setup" class="developers"><img src="<%- cachify('/pages/i/developers-link.png') %>" alt="Persona for developers"><span>Implement Persona on your site </span>Developer guides and API documentation</a>
     </div><!-- #dashboard -->
 </div>
 
diff --git a/resources/views/layout.ejs b/resources/views/layout.ejs
index bfaa83682d1ecbd45b97896476d22b672b498aef..cbc7cc078e9ea6f1f6e0dbb0f6b13347196e1094 100644
--- a/resources/views/layout.ejs
+++ b/resources/views/layout.ejs
@@ -8,7 +8,7 @@
   <meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, width=device-width" />
   <meta name="format-detection" content="email=no" />
   <!--[if lt IE 9]>
-    <script src="/common/js/lib/html5shim.js"></script>
+    <%- cachify_js('/production/html5shim.js') %>
   <![endif]-->
   <%- cachify_css('/production/browserid.css') %>
   <!--[if lt IE 9]>
diff --git a/tests/static-resource-test.js b/tests/static-resource-test.js
index ceda594e43ea9f697b56507f2b909bf9f66286f8..66a83a9ba88fb4ef3428474f635e8acfc5ac9142 100755
--- a/tests/static-resource-test.js
+++ b/tests/static-resource-test.js
@@ -23,7 +23,7 @@ suite.addBatch({
       var res = resources.resources;
       assert.ok(files['/production/dialog.css'].length >= 3);
       // Get ride of non-localized asset bundles
-      ['/production/communication_iframe.js', '/production/include.js', '/production/dialog.css', '/production/browserid.css', '/production/ie8_main.css', '/production/ie8_dialog.css', '/production/relay.js'].forEach(
+      ['/production/communication_iframe.js', '/production/include.js', '/production/dialog.css', '/production/browserid.css', '/production/ie8_main.css', '/production/ie8_dialog.css', '/production/relay.js', '/production/html5shim.js'].forEach(
         function (nonLocaleAsset) {
           delete res[nonLocaleAsset];
           delete files[nonLocaleAsset];