diff --git a/app/index.html b/app/index.html
index 2466189..e345eee 100644
--- a/app/index.html
+++ b/app/index.html
@@ -40,7 +40,7 @@
     
     
     
-    
+
   
   
     
@@ -207,6 +207,7 @@
               
+              
Set this DNS Record
               Hostname
               loading...
               TXT Host
diff --git a/app/js/app.js b/app/js/app.js
index c14d7e3..f32ab86 100644
--- a/app/js/app.js
+++ b/app/js/app.js
@@ -2,7 +2,7 @@
 'use strict';
 
   /*global URLSearchParams,Headers*/
-  var BROWSER_SUPPORTS_ECDSA = navigator.userAgent.toLowerCase().indexOf('firefox') === -1;
+  var BROWSER_SUPPORTS_ECDSA;
   var $qs = function (s) { return window.document.querySelector(s); };
   var $qsa = function (s) { return window.document.querySelectorAll(s); };
   var info = {};
@@ -31,16 +31,50 @@
       });
     });
   }
+  function testRsaSupport() {
+    var opts = {
+      type: 'RSA'
+    , bitlength: '2048'
+    };
+    return BACME.accounts.generateKeypair(opts).then(function (jwk) {
+      return crypto.subtle.importKey(
+        "jwk"
+      , jwk
+      , { name: "RSASSA-PKCS1-v1_5", hash: { name: "SHA-256" } }
+      , true
+      , ["sign"]
+      ).then(function (privateKey) {
+        return window.crypto.subtle.exportKey("pkcs8", privateKey);
+      });
+    });
+  }
   testEcdsaSupport().then(function () {
-    console.log("supports ECDSA");
+    console.info("[crypto] ECDSA is supported");
     BROWSER_SUPPORTS_ECDSA = true;
+    localStorage.setItem('version', '1');
     return true;
   }).catch(function () {
-    console.log("DOES NOT supports ECDSA");
+    console.warn("[crypto] ECDSA is NOT fully supported");
     BROWSER_SUPPORTS_ECDSA = false;
+
+    // fix previous firefox browsers
+    if (!localStorage.getItem('version')) {
+      localStorage.clear();
+      localStorage.getItem('version', '1');
+    }
+
+    // DO NOT RETURN HERE
+    testRsaSupport().then(function () {
+      console.info('[crypto] RSA is supported');
+    }).catch(function (err) {
+      console.error('[crypto] could not use either EC nor RSA.');
+      console.error(err);
+      window.alert("Your browser is cryptography support (neither RSA or EC is usable). Please use Chrome, Firefox, or Safari.");
+    });
+
+    // RETURN HERE
     return false;
   });
-  // TODO test RSA support
 
   var apiUrl = 'https://acme-{{env}}.api.letsencrypt.org/directory';
   function updateApiType() {
@@ -83,7 +117,10 @@
     var j = i;
     i += 1;
 
-    steps[j].submit(ev);
+    return PromiseA.resolve(steps[j].submit(ev)).catch(function (err) {
+      console.error(err);
+      window.alert.error("Something went wrong. It's our fault not yours. Please email aj@greenlock.domains and let him know that 'step " + j + "' failed.");
+    });
   }
 
   $qsa('.js-acme-form').forEach(function ($el) {
@@ -355,8 +392,9 @@
         });
       });
     }).catch(function (err) {
-      console.error('Step \'' + i + '\' Error:');
+      console.error('Step \'\' Error:');
       console.error(err, err.stack);
+      window.alert("An error happened at Step " + i + ", but it's not your fault. Email aj@greenlock.domains and let him know.");
     });
   };
 
@@ -421,9 +459,22 @@
               if ('pending' === poll.status) {
                 return true;
               }
+
+              if ('invalid' === poll.status) {
+                allsWell = false;
+                window.alert("verification failed:" + poll.error.detail);
+                return;
+              }
+
+              if (poll.error) {
+                window.alert("verification failed:" + poll.error.detail);
+                return;
+              }
+
               if ('valid' !== poll.status) {
                 allsWell = false;
                 console.warn('BAD POLL STATUS', poll);
+                window.alert("unknown error: " + JSON.stringify(poll, null, 2));
               }
               // TODO show status in HTML
             });
@@ -444,6 +495,38 @@
     });
   };
 
+  // https://stackoverflow.com/questions/40314257/export-webcrypto-key-to-pem-format
+  function spkiToPEM(keydata, pemName){
+      var keydataS = arrayBufferToString(keydata);
+      var keydataB64 = window.btoa(keydataS);
+      var keydataB64Pem = formatAsPem(keydataB64, pemName);
+      return keydataB64Pem;
+  }
+
+  function arrayBufferToString( buffer ) {
+      var binary = '';
+      var bytes = new Uint8Array( buffer );
+      var len = bytes.byteLength;
+      for (var i = 0; i < len; i++) {
+          binary += String.fromCharCode( bytes[ i ] );
+      }
+      return binary;
+  }
+
+
+  function formatAsPem(str, pemName) {
+      var finalString = '-----BEGIN ' + pemName + ' PRIVATE KEY-----\n';
+
+      while(str.length > 0) {
+          finalString += str.substring(0, 64) + '\n';
+          str = str.substring(64);
+      }
+
+      finalString = finalString + '-----END ' + pemName + ' PRIVATE KEY-----';
+
+      return finalString;
+  }
+
   // spinner
   steps[4] = function () {
     updateProgress(1);
@@ -460,16 +543,10 @@
     function createKeypair() {
       var opts;
 
-      if(BROWSER_SUPPORTS_ECDSA) {
-        opts = {
-          type: 'ECDSA'
-        , bitlength: '256'
-        };
+      if (BROWSER_SUPPORTS_ECDSA) {
+        opts = { type: 'ECDSA', bitlength: '256' };
       } else {
-        opts = {
-          type: 'RSA'
-        , bitlength: '2048'
-        };
+        opts = { type: 'RSA', bitlength: '2048' };
       }
 
       return BACME.domains.generateKeypair(opts).then(function (serverJwk) {
@@ -524,52 +601,14 @@
         $qs("#js-download-fullchain-link").href =
           "data:text/octet-stream;base64," + window.btoa(certs);
 
-        // https://stackoverflow.com/questions/40314257/export-webcrypto-key-to-pem-format
-        function spkiToPEM(keydata){
-            var keydataS = arrayBufferToString(keydata);
-            var keydataB64 = window.btoa(keydataS);
-            var keydataB64Pem = formatAsPem(keydataB64);
-            return keydataB64Pem;
-        }
-
-        function arrayBufferToString( buffer ) {
-            var binary = '';
-            var bytes = new Uint8Array( buffer );
-            var len = bytes.byteLength;
-            for (var i = 0; i < len; i++) {
-                binary += String.fromCharCode( bytes[ i ] );
-            }
-            return binary;
-        }
-
-
-        function formatAsPem(str) {
-            var finalString = '-----BEGIN ' + pemName + ' PRIVATE KEY-----\n';
-
-            while(str.length > 0) {
-                finalString += str.substring(0, 64) + '\n';
-                str = str.substring(64);
-            }
-
-            finalString = finalString + '-----END ' + pemName + ' PRIVATE KEY-----';
-
-            return finalString;
-        }
-
         var wcOpts;
         var pemName;
         if (/^R/.test(info.serverJwk.kty)) {
           pemName = 'RSA';
-          wcOpts = {
-            name: "RSASSA-PKCS1-v1_5"
-          , hash: { name: "SHA-256" }
-          };
+          wcOpts = { name: "RSASSA-PKCS1-v1_5", hash: { name: "SHA-256" } };
         } else {
           pemName = 'EC';
-          wcOpts = {
-            name: "ECDSA"
-          , namedCurve: "P-256"
-          };
+          wcOpts = { name: "ECDSA", namedCurve: "P-256" };
         }
         return crypto.subtle.importKey(
           "jwk"
@@ -580,15 +619,16 @@
         ).then(function (privateKey) {
           return window.crypto.subtle.exportKey("pkcs8", privateKey);
         }).then (function (keydata) {
-          var pem = spkiToPEM(keydata);
+          var pem = spkiToPEM(keydata, pemName);
           $qs('#js-privkey').innerHTML = pem;
           $qs("#js-download-privkey-link").href =
             "data:text/octet-stream;base64," + window.btoa(pem);
           steps[i]();
-        }).catch(function(err){
-          console.error(err.toString());
         });
       });
+    }).catch(function (err) {
+      console.error(err.toString());
+      window.alert("An error happened in the final step, but it's not your fault. Email aj@greenlock.domains and let him know.");
     });
   };
 
diff --git a/app/js/bacme.js b/app/js/bacme.js
index 978e166..fb63042 100644
--- a/app/js/bacme.js
+++ b/app/js/bacme.js
@@ -38,13 +38,16 @@ BACME._logBody = function (body) {
 BACME.directory = function (opts) {
   return webFetch(opts.directoryUrl || directoryUrl, { mode: 'cors' }).then(function (resp) {
     BACME._logHeaders(resp);
-    return resp.json().then(function (body) {
-      directory = body;
+    return resp.json().then(function (reply) {
+      if (/error/.test(reply.type)) {
+        return Promise.reject(new Error(reply.detail || reply.type));
+      }
+      directory = reply;
       nonceUrl = directory.newNonce || 'https://acme-staging-v02.api.letsencrypt.org/acme/new-nonce';
       accountUrl = directory.newAccount || 'https://acme-staging-v02.api.letsencrypt.org/acme/new-account';
       orderUrl = directory.newOrder || "https://acme-staging-v02.api.letsencrypt.org/acme/new-order";
-      BACME._logBody(body);
-      return body;
+      BACME._logBody(reply);
+      return reply;
     });
   });
 };
@@ -227,18 +230,30 @@ BACME.accounts.sign = function (opts) {
       payloadJson
     );
 
-    // TODO RSA
     var protectedJson =
       { nonce: opts.nonce
       , url: accountUrl
       , alg: abstractKey.meta.alg
-      , jwk: {
-          kty: opts.jwk.kty
-        , crv: opts.jwk.crv
-        , x: opts.jwk.x
-        , y: opts.jwk.y
-        }
+      , jwk: null
       };
+
+    if (/EC/i.test(opts.jwk.kty)) {
+      protectedJson.jwk = {
+        crv: opts.jwk.crv
+      , kty: opts.jwk.kty
+      , x: opts.jwk.x
+      , y: opts.jwk.y
+      };
+    } else if (/RS/i.test(opts.jwk.kty)) {
+      protectedJson.jwk = {
+        e: opts.jwk.e
+      , kty: opts.jwk.kty
+      , n: opts.jwk.n
+      };
+    } else {
+      return Promise.reject(new Error("[acme.accounts.sign] unsupported key type '" + opts.jwk.kty + "'"));
+    }
+
     console.log('protected:');
     console.log(protectedJson);
     var protected64 = BACME._jsto64(
@@ -486,9 +501,7 @@ var challengePollUrl;
 
 // { jwk, challengeUrl, accountId (kid) }
 BACME.challenges.accept = function (opts) {
-  var payload64 = BACME._jsto64(
-    {}
-  );
+  var payload64 = BACME._jsto64({});
 
   return BACME._importKey(opts.jwk).then(function (abstractKey) {
     var protected64 = BACME._jsto64(
@@ -530,6 +543,9 @@ BACME.challenges.check = function (opts) {
     BACME._logHeaders(resp);
 
     return resp.json().then(function (reply) {
+      if (/error/.test(reply.type)) {
+        return Promise.reject(new Error(reply.detail || reply.type));
+      }
       challengePollUrl = reply.url;
 
       BACME._logBody(reply);
@@ -630,6 +646,9 @@ BACME.orders.finalize = function (opts) {
       nonce = resp.headers.get('replay-nonce');
 
       return resp.json().then(function (reply) {
+        if (/error/.test(reply.type)) {
+          return Promise.reject(new Error(reply.detail || reply.type));
+        }
         certificateUrl = reply.certificate;
         BACME._logBody(reply);
 
@@ -667,6 +686,9 @@ BACME.orders.check = function (opts) {
     BACME._logHeaders(resp);
 
     return resp.json().then(function (reply) {
+      if (/error/.test(reply.type)) {
+        return Promise.reject(new Error(reply.detail || reply.type));
+      }
       BACME._logBody(reply);
 
       return reply;