js: Use cache for blessings between go and js

Part 2 will be to convert from JsBlessing object to more complete
blessings

MultiPart: 2/2
Change-Id: I02d5d117e03c6540b83616745b274424a0ca2a2b
diff --git a/.gitignore b/.gitignore
index cd0b069..5854afc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,6 +14,7 @@
 go/bin
 xunit.xml
 tmp
+.idea
 
 # Vanadium
 /.v23
diff --git a/src/gen-vdl/v.io/x/ref/services/wspr/internal/app/index.js b/src/gen-vdl/v.io/x/ref/services/wspr/internal/app/index.js
index 5850954..8ff3aad 100644
--- a/src/gen-vdl/v.io/x/ref/services/wspr/internal/app/index.js
+++ b/src/gen-vdl/v.io/x/ref/services/wspr/internal/app/index.js
@@ -22,7 +22,6 @@
 
 // Types:
 var _type1 = new vdl.Type();
-var _type10 = new vdl.Type();
 var _type2 = new vdl.Type();
 var _type3 = new vdl.Type();
 var _type4 = new vdl.Type();
@@ -41,9 +40,6 @@
 _type1.kind = vdl.kind.LIST;
 _type1.name = "";
 _type1.elem = _typeRpcCallOption;
-_type10.kind = vdl.kind.LIST;
-_type10.name = "";
-_type10.elem = new principal.BlessingsHandle()._type;
 _type2.kind = vdl.kind.LIST;
 _type2.name = "";
 _type2.elem = new security.BlessingPattern()._type;
@@ -56,19 +52,19 @@
 _type5.kind = vdl.kind.LIST;
 _type5.name = "";
 _type5.elem = new security.Caveat()._type;
-_type6.kind = vdl.kind.OPTIONAL;
+_type6.kind = vdl.kind.LIST;
 _type6.name = "";
-_type6.elem = new principal.JsBlessings()._type;
-_type7.kind = vdl.kind.LIST;
+_type6.elem = vdl.types.STRING;
+_type7.kind = vdl.kind.MAP;
 _type7.name = "";
-_type7.elem = vdl.types.STRING;
-_type8.kind = vdl.kind.MAP;
+_type7.elem = new principal.BlessingsId()._type;
+_type7.key = new security.BlessingPattern()._type;
+_type8.kind = vdl.kind.LIST;
 _type8.name = "";
-_type8.elem = _type6;
-_type8.key = new security.BlessingPattern()._type;
+_type8.elem = new signature.Interface()._type;
 _type9.kind = vdl.kind.LIST;
 _type9.name = "";
-_type9.elem = new signature.Interface()._type;
+_type9.elem = new principal.BlessingsHandle()._type;
 _typeGranterHandle.kind = vdl.kind.INT32;
 _typeGranterHandle.name = "v.io/x/ref/services/wspr/internal/app.GranterHandle";
 _typeGranterRequest.kind = vdl.kind.STRUCT;
@@ -90,7 +86,6 @@
 _typeRpcServerOption.name = "v.io/x/ref/services/wspr/internal/app.RpcServerOption";
 _typeRpcServerOption.fields = [{name: "IsLeaf", type: vdl.types.BOOL}, {name: "ServesMountTable", type: vdl.types.BOOL}];
 _type1.freeze();
-_type10.freeze();
 _type2.freeze();
 _type3.freeze();
 _type4.freeze();
@@ -386,14 +381,9 @@
     },
     ],
     outArgs: [{
-      name: 'publicKeyOut',
+      name: '',
       doc: "",
-      type: vdl.types.STRING
-    },
-    {
-      name: 'handleOut',
-      doc: "",
-      type: new principal.BlessingsHandle()._type
+      type: new principal.BlessingsId()._type
     },
     ],
     inStream: null,
@@ -417,14 +407,9 @@
     },
     ],
     outArgs: [{
-      name: 'publicKeyOut',
+      name: '',
       doc: "",
-      type: vdl.types.STRING
-    },
-    {
-      name: 'handleOut',
-      doc: "",
-      type: new principal.BlessingsHandle()._type
+      type: new principal.BlessingsId()._type
     },
     ],
     inStream: null,
@@ -466,7 +451,7 @@
     outArgs: [{
       name: '',
       doc: "",
-      type: _type6
+      type: new principal.BlessingsId()._type
     },
     ],
     inStream: null,
@@ -481,13 +466,13 @@
     inArgs: [{
       name: 'peerBlessings',
       doc: "",
-      type: _type7
+      type: _type6
     },
     ],
     outArgs: [{
       name: '',
       doc: "",
-      type: _type6
+      type: new principal.BlessingsId()._type
     },
     ],
     inStream: null,
@@ -519,7 +504,7 @@
     outArgs: [{
       name: '',
       doc: "",
-      type: _type6
+      type: new principal.BlessingsId()._type
     },
     ],
     inStream: null,
@@ -551,7 +536,7 @@
     outArgs: [{
       name: '',
       doc: "",
-      type: _type8
+      type: _type7
     },
     ],
     inStream: null,
@@ -593,7 +578,7 @@
     outArgs: [{
       name: '',
       doc: "",
-      type: _type7
+      type: _type6
     },
     ],
     inStream: null,
@@ -614,7 +599,7 @@
     outArgs: [{
       name: '',
       doc: "",
-      type: _type9
+      type: _type8
     },
     ],
     inStream: null,
@@ -629,13 +614,13 @@
     inArgs: [{
       name: 'toJoin',
       doc: "",
-      type: _type10
+      type: _type9
     },
     ],
     outArgs: [{
       name: '',
       doc: "",
-      type: _type6
+      type: new principal.BlessingsId()._type
     },
     ],
     inStream: null,
diff --git a/src/gen-vdl/v.io/x/ref/services/wspr/internal/principal/index.js b/src/gen-vdl/v.io/x/ref/services/wspr/internal/principal/index.js
index a799f10..f11c056 100644
--- a/src/gen-vdl/v.io/x/ref/services/wspr/internal/principal/index.js
+++ b/src/gen-vdl/v.io/x/ref/services/wspr/internal/principal/index.js
@@ -16,16 +16,39 @@
 
 
 // Types:
+var _typeBlessingsCacheAddMessage = new vdl.Type();
+var _typeBlessingsCacheDeleteMessage = new vdl.Type();
+var _typeBlessingsCacheMessage = new vdl.Type();
 var _typeBlessingsHandle = new vdl.Type();
+var _typeBlessingsId = new vdl.Type();
 var _typeJsBlessings = new vdl.Type();
+_typeBlessingsCacheAddMessage.kind = vdl.kind.STRUCT;
+_typeBlessingsCacheAddMessage.name = "v.io/x/ref/services/wspr/internal/principal.BlessingsCacheAddMessage";
+_typeBlessingsCacheAddMessage.fields = [{name: "CacheId", type: _typeBlessingsId}, {name: "Blessings", type: _typeJsBlessings}];
+_typeBlessingsCacheDeleteMessage.kind = vdl.kind.STRUCT;
+_typeBlessingsCacheDeleteMessage.name = "v.io/x/ref/services/wspr/internal/principal.BlessingsCacheDeleteMessage";
+_typeBlessingsCacheDeleteMessage.fields = [{name: "CacheId", type: _typeBlessingsId}, {name: "DeleteAfter", type: vdl.types.UINT32}];
+_typeBlessingsCacheMessage.kind = vdl.kind.UNION;
+_typeBlessingsCacheMessage.name = "v.io/x/ref/services/wspr/internal/principal.BlessingsCacheMessage";
+_typeBlessingsCacheMessage.fields = [{name: "Add", type: _typeBlessingsCacheAddMessage}, {name: "Delete", type: _typeBlessingsCacheDeleteMessage}];
 _typeBlessingsHandle.kind = vdl.kind.INT32;
 _typeBlessingsHandle.name = "v.io/x/ref/services/wspr/internal/principal.BlessingsHandle";
+_typeBlessingsId.kind = vdl.kind.UINT32;
+_typeBlessingsId.name = "v.io/x/ref/services/wspr/internal/principal.BlessingsId";
 _typeJsBlessings.kind = vdl.kind.STRUCT;
 _typeJsBlessings.name = "v.io/x/ref/services/wspr/internal/principal.JsBlessings";
 _typeJsBlessings.fields = [{name: "Handle", type: _typeBlessingsHandle}, {name: "PublicKey", type: vdl.types.STRING}];
+_typeBlessingsCacheAddMessage.freeze();
+_typeBlessingsCacheDeleteMessage.freeze();
+_typeBlessingsCacheMessage.freeze();
 _typeBlessingsHandle.freeze();
+_typeBlessingsId.freeze();
 _typeJsBlessings.freeze();
+module.exports.BlessingsCacheAddMessage = (vdl.registry.lookupOrCreateConstructor(_typeBlessingsCacheAddMessage));
+module.exports.BlessingsCacheDeleteMessage = (vdl.registry.lookupOrCreateConstructor(_typeBlessingsCacheDeleteMessage));
+module.exports.BlessingsCacheMessage = (vdl.registry.lookupOrCreateConstructor(_typeBlessingsCacheMessage));
 module.exports.BlessingsHandle = (vdl.registry.lookupOrCreateConstructor(_typeBlessingsHandle));
+module.exports.BlessingsId = (vdl.registry.lookupOrCreateConstructor(_typeBlessingsId));
 module.exports.JsBlessings = (vdl.registry.lookupOrCreateConstructor(_typeJsBlessings));
 
 
@@ -44,6 +67,8 @@
 // Services:
 
    
+
+   
  
 
 
diff --git a/src/gen-vdl/v.io/x/ref/services/wspr/internal/rpc/server/index.js b/src/gen-vdl/v.io/x/ref/services/wspr/internal/rpc/server/index.js
index b27766f..4d2cfe6 100644
--- a/src/gen-vdl/v.io/x/ref/services/wspr/internal/rpc/server/index.js
+++ b/src/gen-vdl/v.io/x/ref/services/wspr/internal/rpc/server/index.js
@@ -28,7 +28,6 @@
 var _type4 = new vdl.Type();
 var _type5 = new vdl.Type();
 var _type6 = new vdl.Type();
-var _type7 = new vdl.Type();
 var _typeAuthReply = new vdl.Type();
 var _typeCaveatValidationRequest = new vdl.Type();
 var _typeCaveatValidationResponse = new vdl.Type();
@@ -52,12 +51,9 @@
 _type5.kind = vdl.kind.LIST;
 _type5.name = "";
 _type5.elem = vdl.types.ERROR;
-_type6.kind = vdl.kind.OPTIONAL;
+_type6.kind = vdl.kind.LIST;
 _type6.name = "";
-_type6.elem = new principal.JsBlessings()._type;
-_type7.kind = vdl.kind.LIST;
-_type7.name = "";
-_type7.elem = new signature.Interface()._type;
+_type6.elem = new signature.Interface()._type;
 _typeAuthReply.kind = vdl.kind.STRUCT;
 _typeAuthReply.name = "v.io/x/ref/services/wspr/internal/rpc/server.AuthReply";
 _typeAuthReply.fields = [{name: "Err", type: vdl.types.ERROR}];
@@ -72,23 +68,22 @@
 _typeContext.fields = [{name: "Language", type: vdl.types.STRING}];
 _typeLookupReply.kind = vdl.kind.STRUCT;
 _typeLookupReply.name = "v.io/x/ref/services/wspr/internal/rpc/server.LookupReply";
-_typeLookupReply.fields = [{name: "Handle", type: vdl.types.INT32}, {name: "HasAuthorizer", type: vdl.types.BOOL}, {name: "HasGlobber", type: vdl.types.BOOL}, {name: "Signature", type: _type7}, {name: "Err", type: vdl.types.ERROR}];
+_typeLookupReply.fields = [{name: "Handle", type: vdl.types.INT32}, {name: "HasAuthorizer", type: vdl.types.BOOL}, {name: "HasGlobber", type: vdl.types.BOOL}, {name: "Signature", type: _type6}, {name: "Err", type: vdl.types.ERROR}];
 _typeSecurityCall.kind = vdl.kind.STRUCT;
 _typeSecurityCall.name = "v.io/x/ref/services/wspr/internal/rpc/server.SecurityCall";
-_typeSecurityCall.fields = [{name: "Method", type: vdl.types.STRING}, {name: "Suffix", type: vdl.types.STRING}, {name: "MethodTags", type: _type1}, {name: "LocalBlessings", type: new principal.JsBlessings()._type}, {name: "LocalBlessingStrings", type: _type2}, {name: "RemoteBlessings", type: new principal.JsBlessings()._type}, {name: "RemoteBlessingStrings", type: _type2}, {name: "LocalEndpoint", type: vdl.types.STRING}, {name: "RemoteEndpoint", type: vdl.types.STRING}];
+_typeSecurityCall.fields = [{name: "Method", type: vdl.types.STRING}, {name: "Suffix", type: vdl.types.STRING}, {name: "MethodTags", type: _type1}, {name: "LocalBlessings", type: new principal.BlessingsId()._type}, {name: "LocalBlessingStrings", type: _type2}, {name: "RemoteBlessings", type: new principal.BlessingsId()._type}, {name: "RemoteBlessingStrings", type: _type2}, {name: "LocalEndpoint", type: vdl.types.STRING}, {name: "RemoteEndpoint", type: vdl.types.STRING}];
 _typeServerRpcRequest.kind = vdl.kind.STRUCT;
 _typeServerRpcRequest.name = "v.io/x/ref/services/wspr/internal/rpc/server.ServerRpcRequest";
 _typeServerRpcRequest.fields = [{name: "ServerId", type: vdl.types.UINT32}, {name: "Handle", type: vdl.types.INT32}, {name: "Method", type: vdl.types.STRING}, {name: "Args", type: _type1}, {name: "Call", type: _typeServerRpcRequestCall}];
 _typeServerRpcRequestCall.kind = vdl.kind.STRUCT;
 _typeServerRpcRequestCall.name = "v.io/x/ref/services/wspr/internal/rpc/server.ServerRpcRequestCall";
-_typeServerRpcRequestCall.fields = [{name: "SecurityCall", type: _typeSecurityCall}, {name: "Deadline", type: new time.WireDeadline()._type}, {name: "Context", type: _typeContext}, {name: "TraceRequest", type: new vtrace.Request()._type}, {name: "GrantedBlessings", type: _type6}];
+_typeServerRpcRequestCall.fields = [{name: "SecurityCall", type: _typeSecurityCall}, {name: "Deadline", type: new time.WireDeadline()._type}, {name: "Context", type: _typeContext}, {name: "TraceRequest", type: new vtrace.Request()._type}, {name: "GrantedBlessings", type: new principal.BlessingsId()._type}];
 _type1.freeze();
 _type2.freeze();
 _type3.freeze();
 _type4.freeze();
 _type5.freeze();
 _type6.freeze();
-_type7.freeze();
 _typeAuthReply.freeze();
 _typeCaveatValidationRequest.freeze();
 _typeCaveatValidationResponse.freeze();
diff --git a/src/proxy/message-type.js b/src/proxy/message-type.js
index 04600e1..769ae7d 100644
--- a/src/proxy/message-type.js
+++ b/src/proxy/message-type.js
@@ -30,6 +30,7 @@
     CANCEL: 7, // A request to cancel a previously invoked JS method.
     CAVEAT_VALIDATION_REQUEST: 8, // A request to validate a set of caveats
     LOG_MESSAGE: 9,  // A request to log a message.
-    GRANTER_REQUEST: 10 // A request to call a granter
+    GRANTER_REQUEST: 10, // A request to call a granter
+    BLESSINGS_CACHE_MESSAGE: 11, // A request to update the blessings cache
   }
 };
diff --git a/src/proxy/stream-handler.js b/src/proxy/stream-handler.js
index 79b2332..b0df421 100644
--- a/src/proxy/stream-handler.js
+++ b/src/proxy/stream-handler.js
@@ -6,9 +6,9 @@
 var emitStreamError = require('../lib/emit-stream-error');
 var vError = require('../gen-vdl/v.io/v23/verror');
 var SharedContextKeys = require('../runtime/shared-context-keys');
-var Blessings = require('../security/blessings');
-var JsBlessings =
-  require('../gen-vdl/v.io/x/ref/services/wspr/internal/principal').JsBlessings;
+var BlessingsId =
+  require('../gen-vdl/v.io/x/ref/services/wspr/internal/principal').BlessingsId;
+var runtimeFromContext = require('../runtime/runtime-from-context');
 var TaskSequence = require('../lib/task-sequence');
 var Promise = require('../lib/promise');
 var vom = require('../vom');
@@ -58,12 +58,16 @@
   }
   var handler = this;
   return vom.decode(data).then(function(data) {
-    if (data instanceof JsBlessings) {
-      data = new Blessings(data.handle, data.publicKey,
-                           handler._controller);
-                           data.retain();
+    if (data instanceof BlessingsId) {
+      var runtime = runtimeFromContext(handler._ctx);
+      runtime.blessingsManager.blessingsFromId(data)
+      .then(function(blessings) {
+        blessings.retain();
+        handler._stream._queueRead(blessings);
+      });
+    } else {
+      handler._stream._queueRead(data);
     }
-    handler._stream._queueRead(data);
   }, function(e) {
     emitStreamError(handler._stream,
                     new vError.InternalError(
diff --git a/src/rpc/client.js b/src/rpc/client.js
index 00975e0..6246266 100644
--- a/src/rpc/client.js
+++ b/src/rpc/client.js
@@ -39,13 +39,14 @@
 var SharedContextKeys = require('../runtime/shared-context-keys');
 var vtrace = require('../vtrace');
 var Blessings = require('../security/blessings');
-var JsBlessings =
-  require('../gen-vdl/v.io/x/ref/services/wspr/internal/principal').JsBlessings;
-var ByteStreamMessageWriter = require('../vom/byte-stream-message-writer');
+var BlessingsId =
+  require('../gen-vdl/v.io/x/ref/services/wspr/internal/principal').BlessingsId;
 var ByteStreamMessageReader = require('../vom/byte-stream-message-reader');
+var ByteStreamMessageWriter = require('../vom/byte-stream-message-writer');
 var Encoder = require('../vom/encoder');
 var Decoder = require('../vom/decoder');
 var TaskSequence = require('../lib/task-sequence');
+var runtimeFromContext = require('../runtime/runtime-from-context');
 var vom = require('../vom');
 
 var OutstandingRPC = function(ctx, options, cb) {
@@ -70,37 +71,34 @@
 };
 
 // Helper function to convert an out argument to the given type.
-function convertOutArg(arg, type, controller) {
-  var canonOutArg = arg;
-  var unwrappedArg = unwrap(arg);
-  if (unwrappedArg instanceof JsBlessings) {
-    var res =
-      new Blessings(unwrappedArg.handle, unwrappedArg.publicKey, controller);
-    res.retain();
-    return res;
+function convertOutArg(ctx, arg, type, controller) {
+  if (arg instanceof BlessingsId) {
+    var runtime = runtimeFromContext(ctx);
+    return runtime.blessingsManager.blessingsFromId(arg)
+    .then(function(blessings) {
+      if (blessings) {
+        blessings.retain();
+      }
+      return blessings;
+    });
   }
 
   // There's no protection against bad out args if it's a JSValue.
   // Otherwise, convert to the out arg type to ensure type correctness.
   if (!type.equals(vdl.types.JSVALUE)) {
-    canonOutArg = vdl.canonicalize.reduce(arg, type);
+    try {
+      return Promise.resolve(unwrap(vdl.canonicalize.reduce(arg, type)));
+    } catch(err) {
+      return Promise.reject(err);
+    }
   }
 
-  return unwrap(canonOutArg);
-}
-
-// Helper function to safely convert an out argument.
-// The returned error, if any is useful for a callback.
-function convertOutArgSafe(arg, type, controller) {
-  try {
-    return [undefined, convertOutArg(arg, type, controller)];
-  } catch(err) {
-    return [err, undefined];
-  }
+  return Promise.resolve(unwrap(arg));
 }
 
 OutstandingRPC.prototype.start = function() {
   this._id = this._proxy.nextId();
+  var ctx = this._ctx;
   var self = this;
 
   var cb;
@@ -113,25 +111,25 @@
     cb = function convertToMultiArgs(err, results) { // jshint ignore:line
       // If called from a deferred, the results are undefined.
 
-      // Each out argument should also be unwrapped. (results was []any)
-      results = results ? results.map(function(res, i) {
-        var errOrArg = convertOutArgSafe(res, outArgTypes[i], self._controller);
-        if (errOrArg[0] && !err) {
-          err = errOrArg[0];
-        }
-        return errOrArg[1];
-      }) : [];
+      if (err) {
+        origCb(err);
+        return;
+      }
 
-      // TODO(alexfandrianto): Callbacks seem to be able to get both error and
-      // results, but I think we want to limit it to one or the other.
-      var resultsCopy = results.slice();
-      resultsCopy.unshift(err);
-      origCb.apply(null, resultsCopy);
+      // Each out argument should also be unwrapped. (results was []any)
+      results = results || [];
+      var resultPromises = results.map(function(res, i) {
+        return convertOutArg(ctx, res, outArgTypes[i], self._controller);
+      });
+      Promise.all(resultPromises)
+      .then(function(results) {
+        results.unshift(null);
+        origCb.apply(null, results);
+      }).catch(origCb);
     };
   }
 
   var def = new Deferred(cb);
-  var ctx = this._ctx;
 
   if (!this._cb) {
     // If we are using a promise, strip single args out of the arg array.
@@ -142,26 +140,29 @@
           'Internal error: incorrectly formatted out args in client');
       }
 
+
       // Each out argument should also be unwrapped. (args was []any)
-      var unwrappedArgs = args.map(function(outArg, i) {
-        return convertOutArg(outArg, outArgTypes[i], self._controller);
+      var unwrappedArgPromises = args.map(function(outArg, i) {
+        return convertOutArg(ctx, outArg, outArgTypes[i], self._controller);
       });
 
-      // We expect:
-      // 0 args - return; // NOT return [];
-      // 1 args - return a; // NOT return [a];
-      // 2 args - return [a, b] ;
-      //
-      // Convert the results from array style to the expected return style.
-      // undefined, a, [a, b], [a, b, c] etc
-      switch(unwrappedArgs.length) {
-        case 0:
-          return undefined;
-        case 1:
-          return unwrappedArgs[0];
-        default:
-          return unwrappedArgs;
-      }
+      return Promise.all(unwrappedArgPromises).then(function(unwrappedArgs) {
+        // We expect:
+        // 0 args - return; // NOT return [];
+        // 1 args - return a; // NOT return [a];
+        // 2 args - return [a, b] ;
+        //
+        // Convert the results from array style to the expected return style.
+        // undefined, a, [a, b], [a, b, c] etc
+        switch(unwrappedArgs.length) {
+          case 0:
+            return undefined;
+          case 1:
+            return unwrappedArgs[0];
+          default:
+            return unwrappedArgs;
+        }
+      });
     });
   }
 
diff --git a/src/rpc/create-server-call.js b/src/rpc/create-server-call.js
new file mode 100644
index 0000000..616f787
--- /dev/null
+++ b/src/rpc/create-server-call.js
@@ -0,0 +1,63 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+var createSecurityCall = require('../security/create-security-call');
+
+module.exports = createServerCall;
+
+/**
+ * Create a server call object. This exists so that we can resolve blessings
+ * before the user is given the object.
+ * @private
+ */
+function createServerCall(request, blessingsManager) {
+  var serverCall = new ServerCall();
+  if (request instanceof ServerCall) {
+    serverCall.securityCall = request.securityCall.clone();
+    serverCall.grantedBlessings = request.grantedBlessings;
+    return Promise.resolve(serverCall);
+  } else {
+    var promises = [];
+    promises.push(createSecurityCall(request.call.securityCall,
+      blessingsManager).then(function(securityCall) {
+      serverCall.securityCall = securityCall;
+    }));
+    if (request.call.grantedBlessings) {
+      promises.push(
+        blessingsManager.blessingsFromId(request.call.grantedBlessings)
+        .then(function(grantedBlessings) {
+          serverCall.grantedBlessings = grantedBlessings;
+        })
+      );
+    }
+    return Promise.all(promises).then(function() {
+      return serverCall;
+    });
+  }
+}
+
+/**
+ * @summary
+ * A ServerCall is a context.Context subclass that includes additional
+ * information about an ongoing server call.
+ * @description
+ * <p>Private Constructor, an instance of ServerCall is passed to every service
+ * method as the first argument.</p>
+ * @inner
+ * @constructor
+ *
+ * @property {module:vanadium.security~SecurityCall} securityCall The
+ * Security Call for the request.
+ *
+ * @property {module:vanadium.security~Blessings} grantedBlessings The
+ * blessings optionally granted to the server from the client through a
+ * granter.
+ *
+ * @property {*} methodTags The tags attached to the method,
+ * interface specification in VDL.
+ *
+ * @memberof module:vanadium.rpc
+ */
+function ServerCall() {
+}
diff --git a/src/rpc/granter-router.js b/src/rpc/granter-router.js
index dee6e7a..075d46c 100644
--- a/src/rpc/granter-router.js
+++ b/src/rpc/granter-router.js
@@ -12,7 +12,7 @@
 var MessageType = require('../proxy/message-type');
 var Incoming = MessageType.Incoming;
 var Outgoing = MessageType.Outgoing;
-var SecurityCall = require('../security/call');
+var createSecurityCall = require('../security/create-security-call');
 var InspectableFunction = require('../lib/inspectable-function');
 var GranterResponse =
 require('../gen-vdl/v.io/x/ref/services/wspr/internal/app').GranterResponse;
@@ -25,13 +25,14 @@
  * grant requests.
  * @private
  */
-function GranterRouter(proxy, rootCtx) {
+function GranterRouter(proxy, rootCtx, blessingsManager) {
   proxy.addIncomingHandler(Incoming.GRANTER_REQUEST, this);
 
   this._proxy = proxy;
   this._rootCtx = rootCtx;
   this.nextGranterId = 0;
   this.activeGranters = {};
+  this._blessingsManager = blessingsManager;
 }
 
 /**
@@ -54,9 +55,10 @@
   }
 
   var router = this;
+  var granter;
   return vom.decode(request).then(function(request) {
     request = request.val;
-    var granter = router.activeGranters[request.granterHandle];
+    granter = router.activeGranters[request.granterHandle];
     if (!granter) {
       // TODO(bjornick): Pass in context here so we can generate useful error
       // messages
@@ -64,7 +66,11 @@
         new verror.NoExistError(router._rootCtx, 'unknown granter'));
     }
     delete router.activeGranters[request.granterHandle];
-    var securityCall = new SecurityCall(request.call, router._controller);
+    return createSecurityCall(request.call, router._blessingsManager);
+  }, function(e) {
+    return Promise.reject(
+      new verror.NoExistError(router._rootCtx, 'failed to decode message'));
+  }).then(function(securityCall) {
     var ctx = router._rootCtx;
     var inspectFn = new InspectableFunction(granter);
     var resolve;
@@ -81,9 +87,6 @@
                 return resolve(res);
               });
     return promise;
-  }, function(e) {
-    return Promise.reject(
-      new verror.NoExistError(router._rootCtx, 'failed to decode message'));
   }).then(function(outBlessings) {
     var result = new GranterResponse({blessings: outBlessings[0]._id});
     var data = hexVom.encode(result);
diff --git a/src/rpc/server-call.js b/src/rpc/server-call.js
deleted file mode 100644
index ae9401e..0000000
--- a/src/rpc/server-call.js
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-var SecurityCall = require('../security/call');
-var Blessings = require('../security/blessings');
-
-module.exports = ServerCall;
-
-/**
- * @summary
- * A ServerCall is a context.Context subclass that includes additional
- * information about an ongoing server call.
- * @description
- * <p>Private Constructor, an instance of ServerCall is passed to every service
- * method as the first argument.</p>
- * @inner
- * @constructor
- *
- * @property {module:vanadium.security~SecurityCall} securityCall The
- * Security Call for the request.
- *
- * @property {*} methodTags The tags attached to the method,
- * interface specification in VDL.
- *
- * @memberof module:vanadium.rpc
- */
-function ServerCall(request, controller) {
-  if (!(this instanceof ServerCall)) {
-    return new ServerCall(request, controller);
-  }
-
-  if (request instanceof ServerCall) {
-    this.securityCall = request.securityCall.clone();
-    this.grantedBlessings = request.grantedBlessings;
-  } else {
-    this.securityCall = new SecurityCall(request.call.securityCall,
-                                       controller);
-    if (request.call.grantedBlessings) {
-      this.grantedBlessings = new Blessings(
-        request.call.grantedBlessings.handle,
-        request.call.grantedBlessings.publicKey,
-        controller);
-    }
-  }
-}
diff --git a/src/rpc/server-router.js b/src/rpc/server-router.js
index 82ac8c4..6c4155c 100644
--- a/src/rpc/server-router.js
+++ b/src/rpc/server-router.js
@@ -16,8 +16,8 @@
 var vlog = require('./../lib/vlog');
 var StreamHandler = require('../proxy/stream-handler');
 var verror = require('../gen-vdl/v.io/v23/verror');
-var SecurityCall = require('../security/call');
-var ServerCall = require('./server-call');
+var createSecurityCall = require('../security/create-security-call');
+var createServerCall = require('./create-server-call');
 var vdl = require('../vdl');
 var typeUtil = require('../vdl/type-util');
 var Deferred = require('../lib/deferred');
@@ -37,8 +37,8 @@
 var lib =
   require('../gen-vdl/v.io/x/ref/services/wspr/internal/lib');
 var Blessings = require('../security/blessings');
-var JsBlessings =
-  require('../gen-vdl/v.io/x/ref/services/wspr/internal/principal').JsBlessings;
+var BlessingsId =
+  require('../gen-vdl/v.io/x/ref/services/wspr/internal/principal').BlessingsId;
 var WireBlessings =
   require('../gen-vdl/v.io/v23/security').WireBlessings;
 var SharedContextKeys = require('../runtime/shared-context-keys');
@@ -52,7 +52,8 @@
  * @constructor
  * @private
  */
-var Router = function(proxy, appName, rootCtx, controller, caveatRegistry) {
+var Router = function(
+  proxy, appName, rootCtx, controller, caveatRegistry, blessingsManager) {
   this._servers = {};
   this._proxy = proxy;
   this._streamMap = {};
@@ -62,6 +63,7 @@
   this._caveatRegistry = caveatRegistry;
   this._outstandingRequestForId = {};
   this._controller = controller;
+  this._blessingsManager = blessingsManager;
 
   proxy.addIncomingHandler(Incoming.INVOKE_REQUEST, this);
   proxy.addIncomingHandler(Incoming.LOOKUP_REQUEST, this);
@@ -117,11 +119,15 @@
   }
 
   var router = this;
-  var call;
-  vom.decode(request).then(function(request) {
+  var decodedRequest;
+  vom.decode(request).catch(function(e) {
+    return Promise.reject(new verror.InternalError(router._rootCtx,
+      'Failed to decode ', e));
+  }).then(function(req) {
+    decodedRequest = req;
     var ctx = router._rootCtx.withValue(SharedContextKeys.LANG_KEY,
-                                      request.context.language);
-    var server = router._servers[request.serverId];
+                                        decodedRequest.context.language);
+    var server = router._servers[decodedRequest.serverId];
     if (!server) {
       var authReply = new AuthReply({
         // TODO(bjornick): Use the real context
@@ -132,22 +138,20 @@
                                 null, messageId);
       return;
     }
-    call = new SecurityCall(request.call, router._controller);
-
-    return server.handleAuthorization(request.handle, ctx, call);
-  }, function(e) {
-    return Promise.reject(new verror.InternalError(router._rootCtx,
-                                                   'Failed to decode ', e));
+    return createSecurityCall(decodedRequest.call, router._blessingsManager)
+    .then(function(call) {
+      return server.handleAuthorization(decodedRequest.handle, ctx, call);
+    });
   }).then(function() {
     var authReply = new AuthReply({});
     router._proxy.sendRequest(hexVom.encode(authReply),
                               Outgoing.AUTHORIZATION_RESPONSE, null, messageId);
   }).catch(function(e) {
-    var authReply = new AuthReply({
-      err: ErrorConversion.fromNativeValue(e, router._appName,
-                                           call.method)
-    });
-    router._proxy.sendRequest(hexVom.encode(authReply),
+    var errMsg = {
+      err: ErrorConversion.fromNativeValue(e, this._appName,
+                                           decodedRequest.call.method)
+    };
+    router._proxy.sendRequest(hexVom.encode(errMsg),
                               Outgoing.AUTHORIZATION_RESPONSE, null,
                               messageId);
   });
@@ -179,24 +183,24 @@
 };
 
 Router.prototype.handleCaveatValidationRequest = function(messageId, request) {
-  var resultPromises = new Array(request.cavs.length);
-  var call = new SecurityCall(request.call);
-  var ctx = this._rootCtx.withValue(SharedContextKeys.LANG_KEY,
-                                    request.context.language);
-  for (var i = 0; i < request.cavs.length; i++) {
-    resultPromises[i] = this._validateChain(ctx, call, request.cavs[i]);
-  }
-  var self = this;
-  Promise.all(resultPromises).then(function(results) {
-    var response = new CaveatValidationResponse({
-      results: results
+  var router = this;
+  createSecurityCall(request.call, this._blessingsManager)
+  .then(function(call) {
+    var ctx = router._rootCtx.withValue(SharedContextKeys.LANG_KEY,
+      request.context.language);
+    var resultPromises = request.cavs.map(function(cav) {
+      return router._validateChain(ctx, call, cav);
     });
-    self._proxy.sendRequest(hexVom.encode(response),
-                            Outgoing.CAVEAT_VALIDATION_RESPONSE, null,
-                            messageId);
+    return Promise.all(resultPromises).then(function(results) {
+      var response = new CaveatValidationResponse({
+        results: results
+      });
+      var data = hexVom.encode(response);
+      router._proxy.sendRequest(data, Outgoing.CAVEAT_VALIDATION_RESPONSE, null,
+        messageId);
+    });
   }).catch(function(err) {
-    vlog.logger.error(
-      new Error('Unexpected error (all promises should resolve): ' + err));
+    throw new Error('Unexpected error (all promises should resolve): ' + err);
   });
 };
 
@@ -215,24 +219,24 @@
 
   var self = this;
   return server._handleLookup(request.suffix).then(function(value) {
-    var signatureList = value.invoker.signature();
-    var hasAuthorizer = (typeof value.authorizer === 'function');
-    var hasGlobber = value.invoker.hasGlobber();
-    var reply = new LookupReply({
-      handle: value._handle,
-      signature: signatureList,
-      hasAuthorizer: hasAuthorizer,
-      hasGlobber: hasGlobber
-    });
-    self._proxy.sendRequest(hexVom.encode(reply), Outgoing.LOOKUP_RESPONSE,
-                            null, messageId);
-  }).catch(function(err) {
-    var reply = new LookupReply({
-      err: ErrorConversion.fromNativeValue(err, self._appName, '__Signature')
-    });
-    self._proxy.sendRequest(hexVom.encode(reply), Outgoing.LOOKUP_RESPONSE,
-                            null, messageId);
-  });
+   var signatureList = value.invoker.signature();
+   var hasAuthorizer = (typeof value.authorizer === 'function');
+   var hasGlobber = value.invoker.hasGlobber();
+   var reply = new LookupReply({
+     handle: value._handle,
+     signature: signatureList,
+     hasAuthorizer: hasAuthorizer,
+     hasGlobber: hasGlobber
+   });
+   self._proxy.sendRequest(hexVom.encode(reply), Outgoing.LOOKUP_RESPONSE,
+                           null, messageId);
+ }).catch(function(err) {
+   var reply = new LookupReply({
+     err: ErrorConversion.fromNativeValue(err, self._appName, '__Signature')
+   });
+   self._proxy.sendRequest(hexVom.encode(reply), Outgoing.LOOKUP_RESPONSE,
+                           null, messageId);
+ });
 };
 
 /**
@@ -306,111 +310,118 @@
   var ctx = this.createRPCContext(request);
   var self = this;
   var stream;
-  var call = new ServerCall(request, this._controller);
-  if (request.method === 'Glob__') {
-    if (!invoker.hasGlobber()) {
-      err = new Error('Glob is not implemented');
-      self.sendResult(messageId, 'Glob__', null, err);
-      return;
-    }
-    // Glob takes no streaming input and has GlobReply as output.
-    stream = new Stream(messageId, this._proxy.senderPromise, false,
-      null, naming.GlobReply.prototype._type);
-    this._streamMap[messageId] = stream;
-    this._contextMap[messageId] = ctx;
-    this._outstandingRequestForId[messageId] = 0;
-    this.incrementOutstandingRequestForId(messageId);
-    var globPattern = typeUtil.unwrap(request.args[0]);
-    this.handleGlobRequest(messageId, call.securityCall.suffix,
-                           server, new Glob(globPattern), ctx, call, invoker,
-                           completion);
-    return;
-  }
-
-  function completion() {
-    // There is no results to a glob method.  Everything is sent back
-    // through the stream.
-    self.sendResult(messageId, methodName, null, undefined, 1);
-  }
-
-  // Find the method signature.
-  var signature = invoker.signature();
-  var methodSig;
-  signature.forEach(function(ifaceSig) {
-    ifaceSig.methods.forEach(function(method) {
-      if (method.name === methodName) {
-        methodSig = method;
+  createServerCall(request, this._blessingsManager).then(function(call) {
+    if (request.method === 'Glob__') {
+      if (!invoker.hasGlobber()) {
+        err = new Error('Glob is not implemented');
+        self.sendResult(messageId, 'Glob__', null, err);
+        return;
       }
-    });
-  });
-  if (methodSig === undefined) {
-    err = new verror.NoExistError(
-      call, 'Requested method', methodName, 'not found on');
-    this.sendResult(messageId, methodName, null, err);
-    return;
-  }
-
-  // Unwrap the RPC arguments sent to the JS server.
-  var unwrappedArgs = request.args.map(function(arg, i) {
-    // If an any type was expected, unwrapping is not needed.
-    if (methodSig.inArgs[i].type.kind === vdl.kind.ANY) {
-      return arg;
-    }
-    var unwrapped = typeUtil.unwrap(arg);
-    if (unwrapped instanceof JsBlessings) {
-      return new Blessings(unwrapped.handle, unwrapped.publicKey,
-                           self._controller);
-    }
-    return unwrapped;
-  });
-  var options = {
-    methodName: methodName,
-    args: unwrappedArgs,
-    methodSig: methodSig,
-    ctx: ctx,
-    call: call,
-  };
-
-  this._contextMap[messageId] = options.ctx;
-  if (methodIsStreaming(methodSig)) {
-    var readType = (methodSig.inStream ? methodSig.inStream.type : null);
-    var writeType = (methodSig.outStream ? methodSig.outStream.type : null);
-    stream = new Stream(messageId, this._proxy.senderPromise, false, readType,
-      writeType);
-    this._streamMap[messageId] = stream;
-    var rpc = new StreamHandler(options.ctx, stream);
-    this._proxy.addIncomingStreamHandler(messageId, rpc);
-    options.stream = stream;
-  }
-
-  // Invoke the method;
-  this.invokeMethod(invoker, options, function(err, results) {
-    if (err) {
-      var stackTrace;
-      if (err instanceof Error && err.stack !== undefined) {
-        stackTrace = err.stack;
-      }
-      vlog.logger.debug('Requested method ' + methodName +
-          ' threw an exception on invoke: ', err, stackTrace);
-
-      // The error case has no results; only send the error.
-      self.sendResult(messageId, methodName, undefined, err,
-          methodSig.outArgs.length);
+      // Glob takes no streaming input and has GlobReply as output.
+      stream = new Stream(messageId, self._proxy.senderPromise, false,
+        null, naming.GlobReply.prototype._type);
+      self._streamMap[messageId] = stream;
+      self._contextMap[messageId] = ctx;
+      self._outstandingRequestForId[messageId] = 0;
+      self.incrementOutstandingRequestForId(messageId);
+      var globPattern = typeUtil.unwrap(request.args[0]);
+      self.handleGlobRequest(messageId, call.securityCall.suffix,
+                             server, new Glob(globPattern), ctx, call, invoker,
+                             completion);
       return;
     }
 
-    // Has results; associate the types of the outArgs.
-    var canonResults = results.map(function(result, i) {
-      var t = methodSig.outArgs[i].type;
-      if (t.equals(WireBlessings.prototype._type)) {
-        return result;
-      }
-      return vdl.canonicalize.fill(result, t);
-    });
-    self.sendResult(messageId, methodName, canonResults, undefined,
-                    methodSig.outArgs.length);
-  });
+    function completion() {
+      // There is no results to a glob method.  Everything is sent back
+      // through the stream.
+      self.sendResult(messageId, methodName, null, undefined, 1);
+    }
 
+    // Find the method signature.
+    var signature = invoker.signature();
+    var methodSig;
+    signature.forEach(function(ifaceSig) {
+      ifaceSig.methods.forEach(function(method) {
+        if (method.name === methodName) {
+          methodSig = method;
+        }
+      });
+    });
+    if (methodSig === undefined) {
+      err = new verror.NoExistError(
+        call, 'Requested method', methodName, 'not found on');
+      self.sendResult(messageId, methodName, null, err);
+      return;
+    }
+
+    // Unwrap the RPC arguments sent to the JS server.
+    var unwrappedArgPromises = request.args.map(function(arg, i) {
+      // If an any type was expected, unwrapping is not needed.
+      if (methodSig.inArgs[i].type.kind === vdl.kind.ANY) {
+        return Promise.resolve(arg);
+      }
+      var unwrapped = typeUtil.unwrap(arg);
+      if (unwrapped instanceof BlessingsId) {
+        return self._blessingsManager.blessingsFromId(unwrapped);
+      }
+      return Promise.resolve(unwrapped);
+    });
+
+    return Promise.all(unwrappedArgPromises).then(function(unwrappedArgs) {
+      var options = {
+        methodName: methodName,
+        args: unwrappedArgs,
+        methodSig: methodSig,
+        ctx: ctx,
+        call: call,
+      };
+
+      self._contextMap[messageId] = options.ctx;
+      if (methodIsStreaming(methodSig)) {
+        var readType = (methodSig.inStream ? methodSig.inStream.type : null);
+        var writeType = (methodSig.outStream ? methodSig.outStream.type : null);
+        stream = new Stream(messageId, self._proxy.senderPromise, false,
+          readType, writeType);
+        self._streamMap[messageId] = stream;
+        var rpc = new StreamHandler(options.ctx, stream);
+        self._proxy.addIncomingStreamHandler(messageId, rpc);
+        options.stream = stream;
+      }
+
+      // Invoke the method;
+      self.invokeMethod(invoker, options, function(err, results) {
+        if (err) {
+          var stackTrace;
+          if (err instanceof Error && err.stack !== undefined) {
+            stackTrace = err.stack;
+          }
+          vlog.logger.debug('Requested method ' + methodName +
+              ' threw an exception on invoke: ', err, stackTrace);
+
+          // The error case has no results; only send the error.
+          self.sendResult(messageId, methodName, undefined, err,
+              methodSig.outArgs.length);
+          return;
+        }
+
+        // Has results; associate the types of the outArgs.
+        var canonResults = results.map(function(result, i) {
+          var t = methodSig.outArgs[i].type;
+          if (t.equals(WireBlessings.prototype._type)) {
+            if (!(result instanceof Blessings)) {
+              vlog.logger.error(
+                'Encoding non-blessings value as wire blessings');
+                return null;
+            }
+            return result;
+          }
+          return vdl.canonicalize.fill(result, t);
+        });
+        self.sendResult(messageId, methodName, canonResults, undefined,
+                        methodSig.outArgs.length);
+      });
+    });
+  });
 };
 /**
  * Handles incoming requests from the server to invoke methods on registered
@@ -550,10 +561,13 @@
 
       var suffix = namespaceUtil.join(name, child);
       self.incrementOutstandingRequestForId(messageId);
-      var subCall = new ServerCall(call);
-      subCall.securityCall.suffix = suffix;
       var nextInvoker;
-      server._handleLookup(suffix).then(function(value) {
+      var subCall;
+      createServerCall(call, this._blessingsManager).then(function(servCall) {
+        subCall = servCall;
+        subCall.securityCall.suffix = suffix;
+        return server._handleLookup(suffix);
+      }).then(function(value) {
         nextInvoker = value.invoker;
         return server.handleAuthorization(value._handle, context,
                                           subCall.securityCall);
diff --git a/src/runtime/index.js b/src/runtime/index.js
index 8f4a379..7c51044 100644
--- a/src/runtime/index.js
+++ b/src/runtime/index.js
@@ -23,6 +23,8 @@
 var vtrace = require('../vtrace');
 var Controller =
   require('../gen-vdl/v.io/x/ref/services/wspr/internal/app').Controller;
+var BlessingsManager = require('../security/blessings-manager');
+var BlessingsRouter = require('../security/blessings-router');
 
 module.exports = {
   init: init
@@ -117,6 +119,9 @@
   this._name = options.appName;
   this._language = options.language;
   this.caveatRegistry = new CaveatValidatorRegistry();
+  this.blessingsManager = new BlessingsManager(this._controller);
+  this._blessingsRouter = new BlessingsRouter(this._getProxyConnection(),
+    this.blessingsManager);
 }
 
 inherits(Runtime, EE);
@@ -249,7 +254,8 @@
       this._name,
       this.getContext(),
       this._controller,
-      this.caveatRegistry);
+      this.caveatRegistry,
+      this.blessingsManager);
   }
   return this._router;
 };
@@ -263,7 +269,8 @@
   if (!this._granterRouter) {
     this._granterRouter = new GranterRouter(
       this._getProxyConnection(),
-      this.getContext());
+      this.getContext(),
+      this.blessingsManager);
   }
   return this._granterRouter;
 };
diff --git a/src/security/access/permissions-authorizer.js b/src/security/access/permissions-authorizer.js
index c2a03ad..117b5db 100644
--- a/src/security/access/permissions-authorizer.js
+++ b/src/security/access/permissions-authorizer.js
@@ -12,7 +12,7 @@
 var NoPermissionsError = vdlAccess.NoPermissionsError;
 var Permissions = vdlAccess.Permissions;
 
-module.exports = authorizer;
+module.exports = permissionsAuthorizer;
 var pkgPath = 'v.io/v23/security/access';
 var MultipleTagsError = makeError(
   pkgPath + '.errMultipleMethodTags',
@@ -38,7 +38,7 @@
  * @return {module:vanadium.security.Authorize} An authorizer that applies
  * the perms.
  */
-function authorizer(perms, type) {
+function permissionsAuthorizer(perms, type) {
   // Force the Permissions to have the correct Permissions format.
   var permissions = unwrap(new Permissions(perms));
 
@@ -70,4 +70,4 @@
     }
     return;
   };
-}
\ No newline at end of file
+}
diff --git a/src/security/blessings-cache.js b/src/security/blessings-cache.js
new file mode 100644
index 0000000..e3b0444
--- /dev/null
+++ b/src/security/blessings-cache.js
@@ -0,0 +1,130 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+var unwrap = require('../vdl/type-util').unwrap;
+var vlog = require('../lib/vlog');
+var Deferred = require('../lib/deferred');
+
+/**
+ * @fileoverview A cache of blessings, used in conjunction with the cache
+ * in WSPR (principal/cache.go) to reduce the number of times blessings must
+ * be sent across the wire.
+ * This is kept in sync with the WSPR cache.
+ * @private
+ */
+
+module.exports = BlessingsCache;
+
+/**
+ * @summary Cache of blessings received from WSPR.
+ * @description This cache is kept in sync with WSPR to reduce the
+ * number of times that blessings must be sent across the wire.
+ * @constructor
+ * @private
+ */
+function BlessingsCache() {
+ // Each entry has the following fields (which may or may not exist):
+ // - deferredBlessings: a deferred object whose promise resolves to the
+ // blessings
+ // - refCount: number of references to the blessings
+ // - deleteAfter: delete the entry after this number of blessings
+ this._entries = {};
+}
+
+/**
+ * @summary addBlessings adds blessings to the blessings cache.
+ * @param {wspr.internal.principal.BlessingsCacheAddMessage} addMessage
+ */
+BlessingsCache.prototype.addBlessings = function(addMessage) {
+  var id = this._unwrappedId(addMessage.cacheId);
+  var entry = this._getOrCreateEntry(id);
+  entry.deferredBlessings.resolve(addMessage.blessings);
+};
+
+/**
+ * @summary deleteBlessings removes blessings from the blessings cache.
+ * @param {wspr.internal.principal.BlessingsCacheAddMessage} addMessage
+ */
+BlessingsCache.prototype.deleteBlessings = function(deleteMessage) {
+  var id = this._unwrappedId(deleteMessage.cacheId);
+  var entry = this._getOrCreateEntry(id);
+  entry.deleteAfter = deleteMessage.deleteAfter;
+
+  this._deleteIfNoLongerNeeded(id);
+};
+
+/**
+ * @summary blessingsFromId looks up a blessing by id or waits for it if it
+ * has not been put in the cache yet
+ * @param {wspr.internal.principal.BlessingsId} blessingsId
+ */
+BlessingsCache.prototype.blessingsFromId = function(blessingsId) {
+  var id = unwrap(blessingsId);
+
+  if (typeof id !== 'number') {
+    throw new Error('Expected numeric blessings id');
+  }
+  if (id === 0) {
+    // Zero is not a valid id.
+    // TODO(bprosnitz) Replace this with null once we switch to full blessings
+    // objects. It is currently a number because there are no optional numbers
+    // now in VDL.
+    return Promise.resolve(null);
+  }
+
+  var entry = this._getOrCreateEntry(id);
+  var cache = this;
+  return entry.deferredBlessings.promise.then(function(blessings) {
+    cache._increaseRefCount(id);
+    cache._deleteIfNoLongerNeeded(id);
+    return blessings;
+  });
+};
+
+BlessingsCache.prototype._increaseRefCount = function(cacheId) {
+  var entry = this._entries[cacheId];
+  if (!entry) {
+    throw new Error('Unexpectedly got id of missing entry');
+  }
+  entry.refCount++;
+};
+
+BlessingsCache.prototype._deleteIfNoLongerNeeded = function(cacheId) {
+  var entry = this._entries[cacheId];
+  if (!entry) {
+    throw new Error('Entry unexpectedly not present');
+  }
+
+  if (entry.refCount >= entry.deleteAfter) {
+    if (entry.refCount > entry.deleteAfter) {
+      vlog.logger.warn('Got more references than expected');
+    }
+    if (entry.waiting) {
+      vlog.logger.warn(
+        'There should not be anything waiting on entry to be deleted');
+    }
+    delete this._entries[cacheId];
+  }
+};
+
+BlessingsCache.prototype._getOrCreateEntry = function(cacheId) {
+  if (!this._entries[cacheId]) {
+    this._entries[cacheId] = {
+      refCount: 0,
+      deferredBlessings: new Deferred()
+    };
+  }
+  return this._entries[cacheId];
+};
+
+BlessingsCache.prototype._unwrappedId = function(cacheId) {
+  var id = unwrap(cacheId);
+  if (typeof id !== 'number') {
+    throw new Error('Got non-numeric id');
+  }
+  if (id <= 0) {
+    throw new Error('Unexpected non-positive id ' + id);
+  }
+  return id;
+};
diff --git a/src/security/blessings-manager.js b/src/security/blessings-manager.js
new file mode 100644
index 0000000..723b1d7
--- /dev/null
+++ b/src/security/blessings-manager.js
@@ -0,0 +1,49 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/**
+ * @fileoverview Manager of cache blessings. This differs from BlessingsCache
+ * because it converts JsBlessings to Blessings objects.
+ * TODO(bprosnitz) Remove this after switching to performing WireBlessings to
+ * Blessings conversion with native types.
+ * @private
+ */
+
+var BlessingsCache = require('../security/blessings-cache');
+var Blessings = require('../security/blessings');
+
+
+module.exports = BlessingsManager;
+
+/*
+ * TODO(bprosnitz) Replace this with just BlessingsCache after switching
+ * to using native type conversion with blessings.
+ */
+/*
+ * @summary Manager of blessings received from WSPR.
+ * @constructor
+ * @inner
+ */
+function BlessingsManager(controller) {
+  this._blessingsCache = new BlessingsCache();
+  this._controller = controller;
+}
+
+BlessingsManager.prototype.blessingsFromId = function(id) {
+  var controller = this._controller;
+  return this._blessingsCache.blessingsFromId(id).then(function(jsBless) {
+    if (!jsBless) {
+      return null;
+    }
+    return new Blessings(jsBless.handle, jsBless.publicKey, controller);
+  });
+};
+
+BlessingsManager.prototype.addBlessings = function(addMessage) {
+  return this._blessingsCache.addBlessings(addMessage);
+};
+
+BlessingsManager.prototype.deleteBlessings = function(deleteMessage) {
+  return this._blessingsCache.deleteBlessings(deleteMessage);
+};
diff --git a/src/security/blessings-router.js b/src/security/blessings-router.js
new file mode 100644
index 0000000..4254bf9
--- /dev/null
+++ b/src/security/blessings-router.js
@@ -0,0 +1,49 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/**
+ * @fileoveriew A router that handles incoming requests to update the state
+ * of the blessings cache.
+ * @private
+ */
+
+var vlog = require('./../lib/vlog');
+var Incoming = require('../proxy/message-type').Incoming;
+
+module.exports = BlessingsRouter;
+
+/**
+ * A router that handles incoming requests to update the state of the blessings
+ * cache.
+ * @constructor
+ * @private
+ */
+function BlessingsRouter(proxy, blessingsManager) {
+  this._blessingsManager = blessingsManager;
+
+  proxy.addIncomingHandler(Incoming.BLESSINGS_CACHE_MESSAGE, this);
+}
+
+BlessingsRouter.prototype.handleRequest = function(messageId, type, request) {
+  switch (type) {
+  case Incoming.BLESSINGS_CACHE_MESSAGE:
+    this.handleBlessingsCacheMessages(request);
+    return;
+  default:
+    vlog.logger.error('Unknown request type given to blessings router ' + type);
+  }
+};
+
+BlessingsRouter.prototype.handleBlessingsCacheMessages = function(messages) {
+  for (var i = 0; i < messages.length; i++) {
+    var message = messages[i];
+    if (message.hasOwnProperty('add')) {
+      this._blessingsManager.addBlessings(message.add);
+    } else if (message.hasOwnProperty('delete')) {
+      this._blessingsManager.deleteBlessings(message.delete);
+    } else {
+      vlog.logger.error('Unknown blessings cache message: ', message);
+    }
+  }
+};
diff --git a/src/security/blessingstore.js b/src/security/blessingstore.js
index 4ce7a31..ec74dd6 100644
--- a/src/security/blessingstore.js
+++ b/src/security/blessingstore.js
@@ -8,7 +8,7 @@
  */
 
  var Deferred = require('../lib/deferred');
- var Blessings = require('./blessings');
+ var runtimeFromContext = require('../../src/runtime/runtime-from-context');
  var verror = require('../gen-vdl/v.io/v23/verror');
 
  module.exports = BlessingStore;
@@ -185,14 +185,17 @@
    controller.blessingStorePeerBlessings(ctx)
    .then(function(peerBlessings) {
      var outPeerBlessings = new Map();
-     peerBlessings.forEach(function(jsBlessings, pattern) {
-       var blessingObj = new Blessings(
-         jsBlessings.handle,
-         jsBlessings.publicKey,
-         controller);
-       outPeerBlessings.set(pattern, blessingObj);
+     var promises = [];
+     peerBlessings.forEach(function(blessId, pattern) {
+       var runtime = runtimeFromContext(ctx);
+       promises.push(runtime.blessingsManager.blessingsFromId(blessId)
+       .then(function(blessingsObj) {
+         outPeerBlessings.set(pattern, blessingsObj);
+       }));
      });
-     def.resolve(outPeerBlessings);
+     return Promise.all(promises).then(function() {
+       def.resolve(outPeerBlessings);
+     });
    }).catch(function(err) {
      def.reject(err);
    });
diff --git a/src/security/call.js b/src/security/call.js
deleted file mode 100644
index daf095f..0000000
--- a/src/security/call.js
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-/**
- * @fileoverview A context passed to the authorizer
- * @private
- */
-var Blessings = require('./blessings.js');
-module.exports = Call;
-
-/**
- * @summary Call defines the state available for authorizing a principal.
- * @name SecurityCall
- * @property {string} method The method being invoked.
- * @property {string} suffix The object name suffix of the request.
- * @property {module:vanadium.security~Blessings} localBlessings The blessings
- * bound to the local end.
- * @property {string} localBlessingStrings The validated names for the local
- * end.
- * @property {module:vanadium.security~Blessings} remoteBlessings The blessings
- * bound to the remote end.
- * @property {string} remoteBlessingStrings The validated names for the remote
- * end.
- * @property {string} localEndpoint The endpoint string for the local end.
- * @property {string} remoteEndpoint The endpoint string for the remote end.
- * @inner
- * @memberof module:vanadium.security
- */
-function Call(call, controller) {
-  this.method = call.method;
-  this.suffix = call.suffix;
-  // TODO(bjornick): Use the enums.
-  this.methodTags = call.methodTags;
-  this.localBlessings = new Blessings(call.localBlessings.handle,
-                                      call.localBlessings.publicKey,
-                                      controller);
-  this.remoteBlessings = new Blessings(call.remoteBlessings.handle,
-                                       call.remoteBlessings.publicKey,
-                                       controller);
-  if (call.grantedBlessings) {
-    this.grantedBlessings = new Blessings(call.grantedBlessings.handle,
-                                          call.grantedBlessings.publicKey,
-                                          controller);
-  }
-  this.localBlessingStrings = call.localBlessingStrings;
-  this.remoteBlessingStrings = call.remoteBlessingStrings;
-  // TODO(bjornick): Create endpoints.
-  this.localEndpoint = call.localEndpoint;
-  this.remoteEndpoint = call.remoteEndpoint;
-}
-
-Call.prototype.clone = function() {
-  var res = Object.create(this.constructor.prototype);
-  Object.defineProperty(res, 'constructor', { value: this.constructor });
-  for (var key in this) {
-    if (!this.hasOwnProperty(key)) {
-      continue;
-    }
-    res[key] = this[key];
-  }
-  return res;
-};
diff --git a/src/security/create-security-call.js b/src/security/create-security-call.js
new file mode 100644
index 0000000..8cdd467
--- /dev/null
+++ b/src/security/create-security-call.js
@@ -0,0 +1,74 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/**
+ * @fileoverview A security information passes to authorizer and validator.
+ * @private
+ */
+module.exports = createSecurityCall;
+
+/**
+ * Create a security call object. This exists so that we can resolve blessings
+ * before the user is given the object.
+ * @private
+ */
+function createSecurityCall(input, blessingsManager) {
+  var call = new Call();
+  call.method = input.method;
+  call.suffix = input.suffix;
+  call.methodTags = input.methodTags;
+  call.localBlessingStrings = input.localBlessingStrings;
+  call.remoteBlessingStrings = input.remoteBlessingStrings;
+  // TODO(bjornick): Create endpoints.
+  call.localEndpoint = input.localEndpoint;
+  call.remoteEndpoint = input.remoteEndpoint;
+
+  var promises = [];
+  promises.push(blessingsManager.blessingsFromId(input.localBlessings)
+  .then(function(localBlessings) {
+    call.localBlessings = localBlessings;
+  }));
+  promises.push(blessingsManager.blessingsFromId(input.remoteBlessings)
+  .then(function(remoteBlessings) {
+    call.remoteBlessings = remoteBlessings;
+    return call;
+  }));
+
+  return Promise.all(promises).then(function() {
+    return call;
+  });
+}
+
+/**
+ * @summary Call defines the state available for authorizing a principal.
+ * @name SecurityCall
+ * @property {string} method The method being invoked.
+ * @property {string} suffix The object name suffix of the request.
+ * @property {module:vanadium.security~Blessings} localBlessings The blessings
+ * bound to the local end.
+ * @property {string} localBlessingStrings The validated names for the local
+ * end.
+ * @property {module:vanadium.security~Blessings} remoteBlessings The blessings
+ * bound to the remote end.
+ * @property {string} remoteBlessingStrings The validated names for the remote
+ * end.
+ * @property {string} localEndpoint The endpoint string for the local end.
+ * @property {string} remoteEndpoint The endpoint string for the remote end.
+ * @inner
+ * @memberof module:vanadium.security
+ */
+function Call() {
+}
+
+Call.prototype.clone = function() {
+  var res = Object.create(this.constructor.prototype);
+  Object.defineProperty(res, 'constructor', { value: this.constructor });
+  for (var key in this) {
+    if (!this.hasOwnProperty(key)) {
+      continue;
+    }
+    res[key] = this[key];
+  }
+  return res;
+};
diff --git a/src/security/default-authorizer.js b/src/security/default-authorizer.js
index a16958f..6a952b4 100644
--- a/src/security/default-authorizer.js
+++ b/src/security/default-authorizer.js
@@ -5,9 +5,9 @@
 var blessingMatches = require('./access/blessing-matching');
 var vError = require('./../gen-vdl/v.io/v23/verror');
 
-module.exports = authorizer;
+module.exports = defaultAuthorizer;
 
-function authorizer(ctx, call, cb) {
+function defaultAuthorizer(ctx, call, cb) {
   // If the remoteBlessings has a public key, and it refers to ourselves
   // (i.e a self rpc), then we always authorize.
   if (call.remoteBlessings.publicKey &&
diff --git a/src/security/principal.js b/src/security/principal.js
index bd69964..afc40cc 100644
--- a/src/security/principal.js
+++ b/src/security/principal.js
@@ -8,7 +8,6 @@
  */
 
 var Deferred = require('../lib/deferred');
-var Blessings = require('./blessings');
 var BlessingStore = require('./blessingstore');
 var verror = require('../gen-vdl/v.io/v23/verror');
 
@@ -81,12 +80,9 @@
 
   var caveats = args.slice(4);
 
-  var controller = this._controller;
   this._controller.bless(ctx, publicKey, blessings._id, extension, caveats)
-  .then(function(res) {
-    var publicKey = res[0];
-    var handle = res[1];
-    def.resolve(new Blessings(handle, publicKey, controller));
+  .then(function(blessings) {
+    def.resolve(blessings);
   }).catch(function(err) {
     def.reject(err);
   });
@@ -121,14 +117,11 @@
 
   var controller = this._controller;
   controller.blessSelf(ctx, name, caveats)
-  .then(function(res) {
-    var publicKey = res[0];
-    var handle = res[1];
-    def.resolve(new Blessings(handle, publicKey, controller));
+  .then(function(blessings) {
+    def.resolve(blessings);
   }).catch(function(err) {
     def.reject(err);
   });
-
   return def.promise;
 };
 
diff --git a/test/integration/test-authorizer.js b/test/integration/test-authorizer.js
index 895ead4..b8ff575 100644
--- a/test/integration/test-authorizer.js
+++ b/test/integration/test-authorizer.js
@@ -56,10 +56,9 @@
     var ctx = res.runtime.getContext();
     client.bindTo(ctx, 'authorizerTestService/auth').then(function(service) {
       service.call(ctx, 'foo').then(function(value) {
-        assert.error(new Error('call should not have succeeded' + value));
+        assert.error(new Error('call should not have succeeded. res:' + value));
         res.end(assert);
       }).catch(function(err) {
-        // We expect to get to the error case.
         assert.ok(err);
         res.end(assert);
       });
diff --git a/test/unit/test-blessings-cache.js b/test/unit/test-blessings-cache.js
new file mode 100644
index 0000000..1999e3c
--- /dev/null
+++ b/test/unit/test-blessings-cache.js
@@ -0,0 +1,337 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+var test = require('prova');
+var BlessingsCache = require('../../src/security/blessings-cache');
+var principal =
+  require('../../src/gen-vdl/v.io/x/ref/services/wspr/internal/principal');
+
+test('Blessing cache - add before use', function(t) {
+  var blessingsA = {
+    publicKey: 'A'
+  };
+  var messages = [
+    {
+      type: 'add',
+      value: {
+        cacheId: 1,
+        blessings: blessingsA
+      }
+    },
+    {
+      type: 'blessingsFromId',
+      cacheId: 1,
+      expected: blessingsA
+    }
+  ];
+
+  testCache(t, messages);
+});
+
+test('Blessing cache - add after use', function(t) {
+  var blessingsA = {
+    publicKey: 'A'
+  };
+  var messages = [
+    {
+      type: 'blessingsFromId',
+      cacheId: 1,
+      expected: blessingsA
+    },
+    {
+      type: 'add',
+      value: {
+        cacheId: 1,
+        blessings: blessingsA
+      },
+      dontWaitPrevious: true
+    }
+  ];
+
+  testCache(t, messages);
+});
+
+test('Blessing cache - delete after add', function(t) {
+  var blessingsA = {
+    publicKey: 'A'
+  };
+  var messages = [
+    {
+      type: 'add',
+      value: {
+        cacheId: 1,
+        blessings: blessingsA
+      }
+    },
+    {
+      type: 'delete',
+      value: {
+        cacheId: 1,
+        deleteAfter: 0
+      },
+    },
+    {
+      type: 'confirmDelete',
+      cacheId: 1
+    }
+  ];
+
+  testCache(t, messages);
+});
+
+test('Blessing cache - reference counting delete', function(t) {
+  var blessingsA = {
+    publicKey: 'A'
+  };
+  var messages = [
+    {
+      type: 'add',
+      value: {
+        cacheId: 1,
+        blessings: blessingsA
+      }
+    },
+    {
+      type: 'delete',
+      value: {
+        cacheId: 1,
+        deleteAfter: 2
+      },
+    },
+    {
+      type: 'blessingsFromId',
+      cacheId: 1,
+      expected: blessingsA
+    },
+    {
+      type: 'blessingsFromId',
+      cacheId: 1,
+      expected: blessingsA
+    },
+    {
+      type: 'confirmDelete',
+      cacheId: 1
+    }
+  ];
+
+  testCache(t, messages);
+});
+
+test('Blessing cache - add after delete', function(t) {
+  var blessingsA = {
+    publicKey: 'A'
+  };
+  var messages = [
+    {
+      type: 'delete',
+      value: {
+        cacheId: 1,
+        deleteAfter: 1
+      },
+    },
+    {
+      type: 'add',
+      value: {
+        cacheId: 1,
+        blessings: blessingsA
+      }
+    },
+    {
+      type: 'blessingsFromId',
+      cacheId: 1,
+      expected: blessingsA
+    },
+    {
+      type: 'confirmDelete',
+      cacheId: 1
+    }
+  ];
+
+  testCache(t, messages);
+});
+
+test('Blessing cache - multiple entries', function(t) {
+  var blessingsA = {
+    publicKey: 'A'
+  };
+  var blessingsB = {
+    publicKey: 'B'
+  };
+  var blessingsC = {
+    publicKey: 'C'
+  };
+  var messages = [
+    {
+      type: 'add',
+      value: {
+        cacheId: 1,
+        blessings: blessingsA
+      }
+    },
+    {
+      type: 'add',
+      value: {
+        cacheId: 2,
+        blessings: blessingsB
+      }
+    },
+    {
+      type: 'blessingsFromId',
+      cacheId: 2,
+      expected: blessingsB
+    },
+    {
+      type: 'blessingsFromId',
+      cacheId: 1,
+      expected: blessingsA
+    },
+    {
+      type: 'delete',
+      value: {
+        cacheId: 1,
+        deleteAfter: 1
+      },
+    },
+    {
+      type: 'confirmDelete',
+      cacheId: 1
+    },
+    {
+      type: 'add',
+      value: {
+        cacheId: 3,
+        blessings: blessingsC
+      }
+    },
+    {
+      type: 'delete',
+      value: {
+        cacheId: 2,
+        deleteAfter: 3
+      },
+    },
+    {
+      type: 'blessingsFromId',
+      cacheId: 2,
+      expected: blessingsB
+    },
+    {
+      type: 'blessingsFromId',
+      cacheId: 3,
+      expected: blessingsC
+    },
+    {
+      type: 'delete',
+      value: {
+        cacheId: 3,
+        deleteAfter: 1
+      },
+    },
+    {
+      type: 'confirmDelete',
+      cacheId: 3
+    },
+    {
+      type: 'blessingsFromId',
+      cacheId: 2,
+      expected: blessingsB
+    },
+    {
+      type: 'confirmDelete',
+      cacheId: 2
+    }
+  ];
+
+  testCache(t, messages);
+});
+
+test('Blessing cache handles typed BlessingsId objects', function(t) {
+  var blessingsA = {
+    publicKey: 'A'
+  };
+  var messages = [
+    {
+      type: 'add',
+      value: {
+        cacheId: 1,
+        blessings: blessingsA
+      }
+    },
+    {
+      type: 'blessingsFromId',
+      cacheId: new principal.BlessingsId(1),
+      expected: blessingsA
+    }
+  ];
+
+  testCache(t, messages);
+});
+
+test('Blessing cache handles zero blessing id', function(t) {
+  var messages = [
+    {
+      type: 'blessingsFromId',
+      cacheId: new principal.BlessingsId(0),
+      expected: null
+    }
+  ];
+
+  testCache(t, messages);
+});
+
+/**
+ * Tests the cache by handling a sequence of messages.
+ * @private
+ */
+function testCache(t, messages) {
+  var cache = new BlessingsCache();
+  var promises = [];
+  messages.forEach(function(message, index) {
+    // Wait for the previous messages to finish unless dontWaitPrevious is
+    // specified.
+    var preCondPromise = Promise.all(promises);
+    if (message.dontWaitPrevious) {
+      preCondPromise = Promise.resolve();
+    }
+
+    var result = preCondPromise.then(function() {
+      return handleCacheTestMessage(t, cache, message, index);
+    }).catch(function(err) {
+      t.fail('Error in message ' + index + ': ' + err);
+    });
+    promises.push(result);
+  });
+
+  // Wait for all promises to complete.
+  Promise.all(promises).then(function() {
+    t.end();
+  }).catch(function(err) {
+    t.end(err);
+  });
+}
+
+function handleCacheTestMessage(t, cache, message, index) {
+  if (message.type === 'add') {
+    var addMsg = new principal.BlessingsCacheAddMessage(message.value);
+    return cache.addBlessings(addMsg);
+  } else if (message.type === 'delete') {
+    var delMsg = new principal.BlessingsCacheDeleteMessage(message.value);
+    return cache.deleteBlessings(delMsg);
+  } else if (message.type === 'confirmDelete') {
+    t.ok(cache._entries, 'Entries table should exist');
+    t.notOk(message.cacheId in cache._entries,
+      'Cache entry not deleted correctly on message ' + index);
+  } else if (message.type === 'blessingsFromId') {
+    return cache.blessingsFromId(message.cacheId).then(function(blessings) {
+      var expected = null;
+      if (message.expected !== null) {
+        expected = new principal.JsBlessings(message.expected);
+      }
+      t.deepEqual(blessings, expected,
+        'Should get expected blessings on message ' + index);
+    });
+  } else {
+    throw new Error('unknown message type');
+  }
+}
diff --git a/test/unit/test-caveat-validator-registry.js b/test/unit/test-caveat-validator-registry.js
index 67fbf9d..f43a275 100644
--- a/test/unit/test-caveat-validator-registry.js
+++ b/test/unit/test-caveat-validator-registry.js
@@ -6,13 +6,12 @@
 var CaveatValidatorRegistry =
   require('../../src/security/caveat-validator-registry');
 var context = require('../../src/context');
-var SecurityCall = require('../../src/security/call');
 var caveats = require('../../src/security/caveats');
 
 var testCaveats = require('../vdl-out/javascript-test/security/caveat');
 
 function getMockSecurityCall() {
-  return new SecurityCall({
+  return {
     method: '',
     suffix: '',
     methodTags: [],
@@ -28,9 +27,7 @@
     remoteBlessingStrings: [],
     localEndpoint: '',
     remoteEndpoint: ''
-  },
-  null, // controller
-  context.Context());
+  };
 }
 
 
diff --git a/test/unit/test-standard-caveat-validators.js b/test/unit/test-standard-caveat-validators.js
index 0285090..41e545e 100644
--- a/test/unit/test-standard-caveat-validators.js
+++ b/test/unit/test-standard-caveat-validators.js
@@ -8,12 +8,11 @@
 var caveats = require('../../src/security/caveats');
 var vdlSecurity = require('../../src/gen-vdl/v.io/v23/security');
 var context = require('../../src/context');
-var SecurityCall = require('../../src/security/call');
 var Time = require('../../src/gen-vdl/v.io/v23/vdlroot/time').Time;
 var vdl = require('../../src/vdl');
 
 function getMockSecurityCall() {
-  return new SecurityCall({
+  return {
     method: 'aMethod', // only field currently used
     suffix: '',
     methodTags: [],
@@ -29,9 +28,7 @@
     remoteBlessingStrings: [],
     localEndpoint: '',
     remoteEndpoint: ''
-  },
-  null, // controller
-  context.Context());
+  };
 }
 
 function assertValidation(t, cavType, val, cb) {