diff --git a/lib/secrets.js b/lib/secrets.js
index 2c2166e259e7ae4377e9a2e74166b8fee3e4a8df..4b854c3436d02d0238fb84823851dad088d367ba 100644
--- a/lib/secrets.js
+++ b/lib/secrets.js
@@ -20,7 +20,7 @@ function bytesToChars(buf) {
   for (var i=0; i < buf.length; i++) {
     str += alphabet.charAt(buf[i] % alphabet.length);
   }
-  
+
   return str;
 }
 
diff --git a/lib/version.js b/lib/version.js
new file mode 100644
index 0000000000000000000000000000000000000000..0262c59bf2f7ff346d164789546b87c900ce22f6
--- /dev/null
+++ b/lib/version.js
@@ -0,0 +1,53 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// determine the software 'version'.  This is the 7 char abbrevieated SHA
+// we try to read this from ver.txt at the top level, then try to use git,
+// then finally fall back to a randomly generated 7 char string
+// this version will be used for features like cache busting
+
+const
+fs = require('fs'),
+path = require('path'),
+logger = require('./logging.js').logger,
+spawn = require('child_process').spawn,
+secrets = require('./secrets.js');
+
+var sha;
+
+// first try ver.txt which by convention is placed in repo root at
+// deployment time
+try {
+  var contents = fs.readFileSync(path.join(__dirname, '..', 'resources', 'static', 'ver.txt'));
+  sha = contents.toString().split(' ')[0];
+  if (sha.length != 7) throw "bad sha in ver.txt";
+} catch(e) {
+  sha = undefined;
+  logger.debug('cannot read code version from ver.txt: ' + e);
+}
+
+// now set the SHA to either the read SHA or a random string
+module.exports = sha ? sha : secrets.weakGenerate(7);
+
+// if ver.txt discovery failed, try using git to get the sha.
+if (!sha) {
+  // next try using git
+  var p = spawn('git', [ 'log', '--pretty=%h', '-1' ]);
+  var buf = "";
+  p.stdout.on('data', function(d) {
+    buf += d;
+  });
+  p.on('exit', function(code, signal) {
+    var sha = buf.toString().trim();
+    if (sha && sha.length === 7) {
+      module.exports = sha;
+      logger.info('code version (via git) is: ' + module.exports);
+    } else {
+      logger.warn('code version (randomly generated) is: ' + module.exports);
+    }
+  });
+} else {
+  logger.info('code version (from ver.txt) is: ' + module.exports);
+}
+
diff --git a/tests/software-version-test.js b/tests/software-version-test.js
new file mode 100755
index 0000000000000000000000000000000000000000..65b89072b067d890a39f10553d78817d612cd003
--- /dev/null
+++ b/tests/software-version-test.js
@@ -0,0 +1,32 @@
+#!/usr/bin/env node
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+require('./lib/test_env.js');
+
+const
+assert = require('assert'),
+vows = require('vows'),
+fs = require('fs'),
+path = require('path'),
+version = require('../lib/version.js');
+
+var suite = vows.describe('software-version');
+suite.options.error = false;
+
+
+suite.addBatch({
+  "version": {
+    topic: function() { return version; },
+    "works": function(r) {
+      assert.isString(r);
+      assert.equal(r.length, 7);
+    }
+  }
+});
+
+// run or export the suite.
+if (process.argv[1] === __filename) suite.run();
+else suite.export(module);