diff --git a/lib/browserid/primary.js b/lib/browserid/primary.js
new file mode 100644
index 0000000000000000000000000000000000000000..c27ee3207ba31694e8c3612750da7319417668db
--- /dev/null
+++ b/lib/browserid/primary.js
@@ -0,0 +1,141 @@
+/* ***** 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):
+ *  Lloyd Hilaiel <lloyd@hilaiel.com>
+ *
+ * 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 file is an abstraction around "primary identity authority" support,
+// specifically checks and a cache to see if a primary supports browserid
+// natively.
+
+const
+https = require('https'),
+logger = require('../logging.js').logger,
+urlparse = require('urlparse'),
+jwk = require('jwcrypto/jwk');
+
+const WELL_KNOWN_URL = "/.well-known/vep";
+
+// cache .well-known/vep for six hours
+const MAX_CACHE_MS = (6 * 60 * 1000);
+
+function parseWellKnownBody(body, domain) {
+  var v = JSON.parse(body);
+
+  const want = [ 'public-key', 'authentication', 'provisioning' ];
+  var got = Object.keys(v);
+
+  want.forEach(function(k) {
+    if (-1 === got.indexOf(k)) throw "missing required key: " + k;
+  });
+
+  var urls = {
+    auth: 'https://' + domain + v.authentication,
+    prov: 'https://' + domain + v.provisioning,
+  };
+
+  // validate the urls
+  urlparse(urls.auth).validate();
+  urlparse(urls.prov).validate();
+
+  // parse the public key
+  return {
+    publicKey: jwk.PublicKey.fromSimpleObject(v['public-key']),
+    urls: urls
+  };
+}
+
+// a cache of network responses.  We want to move this into
+// fast and efficient external key/value storage as we scale
+var g_cache = { };
+
+exports.checkSupport = function(domain, cb) {
+  if (!cb) throw "missing required callback function";
+
+  if (typeof domain !== 'string' || !domain.length) {
+    return process.nextTick(function() { cb("invalid domain"); });
+  }
+
+  // check cache age
+  if (g_cache[domain]) {
+    if (!g_cache[domain].when || (new Date() - g_cache[domain].when) > MAX_CACHE_MS) {
+      delete g_cache[domain];
+    }
+
+    if (g_cache[domain]) {
+      logger.debug("returning primary support status for '" + domain + "' from cache");
+      return process.nextTick(function() { cb(null, g_cache[domain].status); });
+    }
+  }
+
+  function cacheAndReturn(cacheValue, publicKey) {
+    g_cache[domain] = {
+      when: new Date(),
+      status: cacheValue,
+      publicKey: publicKey
+    };
+    cb(null, cacheValue);
+  }
+
+  // now we need to check to see if domain purports to being a primary for browserid
+  var req = https.get({
+    host: domain,
+    path: WELL_KNOWN_URL,
+    agent: false
+  }, function (res) {
+    if (res.statusCode !== 200) {
+      logger.debug(domain + ' is not a browserid primary - non-200 response code to ' + WELL_KNOWN_URL);
+      return cacheAndReturn(false);
+    }
+    if (res.headers['content-type'].indexOf('application/json') !== 0) {
+      logger.debug(domain + ' is not a browserid primary - non "application/json" response to ' + WELL_KNOWN_URL);
+      return cacheAndReturn(false);
+    }
+
+    var body = "";
+    res.on('data', function(chunk) { body += chunk; });
+    res.on('end', function() {
+      try {
+        var r = parseWellKnownBody(body, domain);
+        logger.info(domain + ' is a valid browserid primary');
+        return cacheAndReturn(r.urls, r.publicKey);
+      } catch(e) {
+        logger.debug(domain + ' is a broken browserid primary, malformed dec of support: ' + e.toString());
+        return cacheAndReturn(false);
+      }
+    });
+  }).on('error', function(e) {
+    logger.debug(domain + ' is not a browserid primary: ' + e.toString());
+    cacheAndReturn(false);
+  });
+};
diff --git a/scripts/check_primary_support b/scripts/check_primary_support
new file mode 100755
index 0000000000000000000000000000000000000000..42c2bb98e2ba942655ca8828b9ac3b2b84174d1d
--- /dev/null
+++ b/scripts/check_primary_support
@@ -0,0 +1,21 @@
+#!/usr/bin/env node
+
+const
+primary = require('../lib/browserid/primary'),
+logging = require('../lib/logging.js');
+
+logging.enableConsoleLogging();
+
+if (process.argv.length !== 3) {
+  console.log('Checks to see if a domain has a proper declaration of support as a browserid primary');
+  console.log('Usage:', process.argv[1], '<domain>');
+  process.exit(1);
+}
+
+primary.checkSupport(process.argv[2], function(err, rv) {
+  if (err) {
+    process.stderr.write("error: " + err + "\n");
+    process.exit(1);
+  }
+  console.log(rv);
+});