browser: Remove dependence on Vanadium JS

This proposal changes our code so that it will build again with npm/
browserify.

Note: The minifier still doesn't work, so NOMINIFY=1 is required.

We also depend on a local server to perform all of our namespace
client (and RPC-related) requests.

To run locally:
  NOMINIFY=1 make start
  make start-browserd

You can visit the namespace browser at localhost:9001, and the go
program will support it at localhost:9002.

Tracking Issue: https://github.com/vanadium/issues/issues/1268

Change-Id: Ib0dc34fb8638cbd71580f4740975ff71ba50842e
diff --git a/Makefile b/Makefile
index aec95d9..c56efcc 100644
--- a/Makefile
+++ b/Makefile
@@ -26,6 +26,7 @@
 
 VANADIUM_JS:=$(JIRI_ROOT)/release/javascript/core
 SOURCE_FILES = $(shell find src -name "*")
+GO_FILES = $(shell find src -name "*.go")
 
 ifndef TMPDIR
 	export TMPDIR:=/tmp
@@ -75,7 +76,7 @@
 	make -C $(JIRI_ROOT)/infrastructure/deploy browser-staging
 
 # Creating the bundle JS file.
-public/bundle.js: $(SOURCE_FILES) node_modules src/services/sample-world/ifc/index.js
+public/bundle.js: $(SOURCE_FILES) node_modules
 	:;jshint src # lint all src JavaScript files.
 ifdef NOMINIFY
 	$(call BROWSERIFY,src/app.js,$@)
@@ -88,6 +89,7 @@
 	:;vulcanize --output public/bundle.html web-component-dependencies.html --inline
 
 # Generate VDL for JavaScript
+# TODO(alexfandrianto): The JS Sample World is unused, so we can remove this.
 src/services/sample-world/ifc/index.js:
 	VDLPATH=$(JIRI_ROOT)/release/projects/browser/src \
 		vdl generate -lang=javascript \
@@ -98,9 +100,6 @@
 node_modules: package.json
 	:;node $(NPM) prune
 	:;node $(NPM) install --quiet
-	# TODO(aghassemi) Temporarily use local release/javascript/core add github/npm to package.json later
-	cd "$(JIRI_ROOT)/release/javascript/core" && node $(NPM) link
-	:;node $(NPM) link vanadium
 
 	touch node_modules
 
@@ -110,9 +109,20 @@
 	:;bower install --config.interactive=false --quiet
 	touch bower_components
 
-go/bin: directories
+go/bin: $(GO_FILES)
+	jiri go install v.io/x/ref/cmd/principal
 	jiri go install v.io/x/ref/services/mounttable/mounttabled
 	jiri go install v.io/x/browser/runner
+	jiri go install v.io/x/browser/namespace-browserd
+
+credentials: go/bin
+	./go/bin/principal seekblessings
+
+.PHONY: start-browserd
+# Runs namespace browser with the credentials from V23_CREDENTIALS
+start-browserd: credentials go/bin/namespace-browserd
+	./go/bin/namespace-browserd
+
 
 # PHONY targets:
 
diff --git a/bower.json b/bower.json
index e379da3..f0f9b79 100644
--- a/bower.json
+++ b/bower.json
@@ -4,5 +4,8 @@
     "polymer": "Polymer/polymer#0.5.4",
     "core-elements": "Polymer/core-elements#0.5.4",
     "paper-elements": "Polymer/paper-elements#0.5.4"
+  },
+  "resolutions": {
+    "webcomponentsjs": "0.7.1"
   }
 }
diff --git a/go/src/v.io/x/browser/namespace-browserd/main.go b/go/src/v.io/x/browser/namespace-browserd/main.go
new file mode 100644
index 0000000..510f0ad
--- /dev/null
+++ b/go/src/v.io/x/browser/namespace-browserd/main.go
@@ -0,0 +1,294 @@
+// Copyright 2016 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.
+
+package main
+
+import (
+	"encoding/json"
+	"fmt"
+	"log"
+	"net/http"
+	"net/url"
+	"time"
+
+	"v.io/v23"
+	"v.io/v23/context"
+	"v.io/v23/namespace"
+	"v.io/v23/naming"
+	"v.io/v23/rpc"
+	"v.io/v23/vdl"
+	"v.io/v23/vdlroot/signature"
+
+	_ "v.io/x/ref/runtime/factories/generic"
+)
+
+const (
+	RPC_TIMEOUT = 15 // 15 seconds per RPC
+
+	// TODO(alexfandrianto): Make this configurable here and on the JS end.
+	// https://github.com/vanadium/issues/issues/1268
+	SERVER_ADDRESS = "localhost:9002"
+)
+
+type NamespaceBrowser struct {
+	// The base Vanadium context. Used for all RPCs.
+	ctx       *context.T
+	namespace namespace.T
+	client    rpc.Client
+}
+
+// NamespaceBrowser factory
+func NewNamespaceBrowser(ctx *context.T) *NamespaceBrowser {
+	return &NamespaceBrowser{
+		ctx:       ctx,
+		namespace: v23.GetNamespace(ctx),
+		client:    v23.GetClient(ctx),
+	}
+}
+
+func (b *NamespaceBrowser) timed() *context.T {
+	ctx, _ := context.WithTimeout(b.ctx, RPC_TIMEOUT*time.Second)
+	return ctx
+}
+
+func writeAndFlush(rw http.ResponseWriter, data interface{}) {
+	// Make sure that the writer supports flushing.
+	flusher, ok := rw.(http.Flusher)
+	if !ok {
+		http.Error(rw, "The HTTP response writer cannot flush, so we cannot stream results!", http.StatusInternalServerError)
+		return
+	}
+
+	// Write data and flush
+	encoded, err := json.Marshal(data)
+	if err != nil {
+		log.Printf("Failed to encode data: %v, %v", data, err)
+	}
+	fmt.Fprintf(rw, "data: %s\n\n", string(encoded))
+	flusher.Flush()
+}
+
+func extractJsonString(params string) (s string, err error) {
+	err = json.Unmarshal([]byte(params), &s)
+	return
+}
+
+func extractJsonMap(params string) (m map[string]interface{}, err error) {
+	err = json.Unmarshal([]byte(params), &m)
+	return
+}
+
+/* ServeHTTP must handle EventSource requests from the browser.
+ * The requests have a "request" and "params" portion.
+ * The format is as follows:
+ *
+ * accountName: <no parameters>  => { accountName: <string>, err: <err> }
+ * glob: string pattern  => a stream of responses
+ *   { globRes: <glob res>, globErr: <glob err>, globEnd: <bool>, err: <err> }
+ * permissions: string name =>  { permissions: <permissions>, err: <err> }
+ * deleteMountPoint: string name => { err: <err string> }
+ * resolveToMounttable: string name => { addresses: []<string>, err: <err> }
+ * objectAddresses: string name => { addresses: []<string>, err: <err> }
+ * remoteBlessings: string name => { blessings: []<string>, err: <err> }
+ * signature: string name => { signature: <signature>, err: <err> }
+ * makeRPC: { name: <string>, methodName: <string>, args: []<string>,
+ *            numOutArgs: <int> } =>
+ *          { response: <undefined, output, OR []outputs>, err: <err> }
+ */
+func (b *NamespaceBrowser) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
+	// Set the headers related to event streaming.
+	rw.Header().Set("Content-Type", "text/event-stream")
+	rw.Header().Set("Cache-Control", "no-cache")
+	rw.Header().Set("Connection", "keep-alive")
+	rw.Header().Set("Access-Control-Allow-Origin", "*")
+
+	request, err := url.QueryUnescape(req.FormValue("request"))
+	if err != nil {
+		fmt.Println(err)
+		return
+	}
+	params, err := url.QueryUnescape(req.FormValue("params"))
+	if err != nil {
+		fmt.Println(err)
+		return
+	}
+
+	fmt.Println("request", request, "params", params)
+
+	// The response depends on the request type.
+	switch request {
+	case "accountName":
+		// Obtain the default blessing and return that.
+		blessing, _ := v23.GetPrincipal(b.ctx).BlessingStore().Default()
+		writeAndFlush(rw, accountNameReturn{AccountName: blessing.String()})
+	case "glob":
+		pattern, err := extractJsonString(params)
+		if err != nil {
+			fmt.Println(err)
+			return
+		}
+
+		// Obtain the glob stream.
+		globCh, err := b.namespace.Glob(b.timed(), pattern)
+		if err != nil {
+			writeAndFlush(rw, globReturn{Err: fmt.Sprintf("%v", err)})
+			return
+		}
+
+		// Afterwards, go through the stream and forward results and errors.
+		writeAndFlush(rw, globReturn{})
+		for entry := range globCh { // These GlobReply could be a reply or an error.
+			switch v := entry.(type) {
+			case *naming.GlobReplyEntry:
+				writeAndFlush(rw, globReturn{GlobRes: &v.Value})
+			case *naming.GlobReplyError:
+				writeAndFlush(rw, globReturn{GlobErr: &v.Value})
+			}
+		}
+		writeAndFlush(rw, globReturn{GlobEnd: true})
+	case "deleteMountPoint":
+		name, err := extractJsonString(params)
+		if err != nil {
+			fmt.Println(err)
+			return
+		}
+
+		// Delete the chosen name from the namespace.
+		err = b.namespace.Delete(b.timed(), name, true)
+		if err != nil {
+			writeAndFlush(rw, deleteReturn{Err: fmt.Sprintf("%v", err)})
+			return
+		}
+		writeAndFlush(rw, deleteReturn{})
+	case "resolveToMounttable":
+		name, err := extractJsonString(params)
+		if err != nil {
+			fmt.Println(err)
+			return
+		}
+
+		// Use the MountEntry for this name to find its server addresses.
+		entry, err := b.namespace.ResolveToMountTable(b.timed(), name)
+		if err != nil {
+			writeAndFlush(rw, addressesReturn{Err: fmt.Sprintf("%v", err)})
+			return
+		}
+		addrs := []string{}
+		for _, server := range entry.Servers {
+			addrs = append(addrs, server.Server)
+		}
+		writeAndFlush(rw, addressesReturn{Addresses: addrs})
+	case "objectAddresses":
+		name, err := extractJsonString(params)
+		if err != nil {
+			fmt.Println(err)
+			return
+		}
+
+		// Use the MountEntry for this name to find its object addresses.
+		entry, err := b.namespace.Resolve(b.timed(), name)
+		if err != nil {
+			writeAndFlush(rw, addressesReturn{Err: fmt.Sprintf("%v", err)})
+			return
+		}
+		addrs := []string{}
+		for _, server := range entry.Servers {
+			addrs = append(addrs, server.Server)
+		}
+		writeAndFlush(rw, addressesReturn{Addresses: addrs})
+	case "permissions":
+		name, err := extractJsonString(params)
+		if err != nil {
+			fmt.Println(err)
+			return
+		}
+
+		// Obtain the mount table permissions at this name.
+		perms, _, err := b.namespace.GetPermissions(b.timed(), name)
+		if err != nil {
+			writeAndFlush(rw, permissionsReturn{Err: fmt.Sprintf("%v", err)})
+			return
+		}
+		writeAndFlush(rw, permissionsReturn{Permissions: perms})
+	case "remoteBlessings":
+		name, err := extractJsonString(params)
+		if err != nil {
+			fmt.Println(err)
+			return
+		}
+
+		// Obtain the remote blessings for the server running at this name.
+		clientCall, err := b.client.StartCall(b.timed(), name, rpc.ReservedMethodSignature, nil)
+		defer clientCall.Finish()
+
+		if err != nil {
+			writeAndFlush(rw, blessingsReturn{Err: fmt.Sprintf("%v", err)})
+			return
+		}
+		rbs, _ := clientCall.RemoteBlessings()
+		writeAndFlush(rw, blessingsReturn{Blessings: rbs})
+	case "signature":
+		name, err := extractJsonString(params)
+		if err != nil {
+			fmt.Println(err)
+			return
+		}
+
+		// Obtain the signature(s) of the server running at this name.
+		var sig []signature.Interface
+		err = b.client.Call(b.timed(), name, rpc.ReservedSignature, nil, []interface{}{&sig})
+		if err != nil {
+			writeAndFlush(rw, signatureReturn{Err: fmt.Sprintf("%v", err)})
+			return
+		}
+		convertedSig := convertSignature(sig)
+		writeAndFlush(rw, signatureReturn{Signature: convertedSig})
+	case "makeRPC":
+		data, err := extractJsonMap(params)
+		if err != nil {
+			fmt.Println(err)
+			return
+		}
+		name := data["name"].(string)
+		method := data["methodName"].(string)
+		params := data["args"].([]interface{})
+		fmt.Printf("Make RPC: %s %s %v\n", name, method, params)
+		numOutArgs := int(data["numOutArgs"].(float64))
+
+		// Prepare outargs as *vdl.Value
+		outargs := make([]*vdl.Value, numOutArgs)
+		outptrs := make([]interface{}, numOutArgs)
+		for i := range outargs {
+			outptrs[i] = &outargs[i]
+		}
+
+		// Make the call to name's method with the given params.
+		err = b.client.Call(b.timed(), name, method, params, outptrs)
+		if err != nil {
+			writeAndFlush(rw, makeRPCReturn{Err: fmt.Sprintf("%v", err)})
+			return
+		}
+
+		// Convert the *vdl.Value outputs to readable strings
+		resStrings := []string{}
+		for _, outarg := range outargs {
+			resStrings = append(resStrings, outarg.String())
+		}
+		writeAndFlush(rw, makeRPCReturn{Response: resStrings})
+	default:
+		writeAndFlush(rw, "Please connect from the namespace browser.")
+	}
+
+	// I think this keeps the connection open until the other side closes it?
+	notify := rw.(http.CloseNotifier).CloseNotify()
+	<-notify
+}
+
+func main() {
+	ctx, shutdown := v23.Init()
+	defer shutdown()
+
+	browser := NewNamespaceBrowser(ctx)
+	log.Fatal("HTTP server error: ", http.ListenAndServe(SERVER_ADDRESS, browser))
+}
diff --git a/go/src/v.io/x/browser/namespace-browserd/types.go b/go/src/v.io/x/browser/namespace-browserd/types.go
new file mode 100644
index 0000000..8059e8f
--- /dev/null
+++ b/go/src/v.io/x/browser/namespace-browserd/types.go
@@ -0,0 +1,171 @@
+// Copyright 2016 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.
+
+package main
+
+/*
+ * Defines types to organize the HTTP response values.
+ * Since json.Marshal only works on exported fields, the types here force the
+ * fields to be converted to lowercase (for better JS consumption).
+ */
+
+import (
+	"v.io/v23/naming"
+	"v.io/v23/security/access"
+	"v.io/v23/vdl"
+	"v.io/v23/vdlroot/signature"
+)
+
+type accountNameReturn struct {
+	AccountName string `json:"accountName"`
+	Err         string `json:"err"`
+}
+
+type globReturn struct {
+	GlobRes *naming.MountEntry `json:"globRes"`
+	GlobErr *naming.GlobError  `json:"globErr"`
+	GlobEnd bool               `json:"globEnd"`
+	Err     string             `json:"err"`
+}
+
+type deleteReturn struct {
+	Err string `json:"err"`
+}
+
+type addressesReturn struct {
+	Addresses []string `json:"addresses"`
+	Err       string   `json:"err"`
+}
+
+type permissionsReturn struct {
+	Permissions access.Permissions `json:"permissions"`
+	Err         string             `json:"err"`
+}
+
+type blessingsReturn struct {
+	Blessings []string `json:"blessings"`
+	Err       string   `json:"err"`
+}
+
+type signatureReturn struct {
+	Signature []pInterface `json:"signature"`
+	Err       string       `json:"err"`
+}
+
+type makeRPCReturn struct {
+	Response []string `json:"response"`
+	Err      string   `json:"err"`
+}
+
+// pInterface describes the signature of an interface.
+// This is a parallel data structure to signature.Interface.
+// The reason to do this is so that Type and Tags can be converted to strings
+// before being sent along the wire.
+type pInterface struct {
+	Name    string    `json:"name"`
+	PkgPath string    `json:"pkgPath"`
+	Doc     string    `json:"doc"`
+	Embeds  []pEmbed  `json:"embeds"`  // No special ordering.
+	Methods []pMethod `json:"methods"` // Ordered by method name.
+}
+
+// pEmbed describes the signature of an embedded interface.
+type pEmbed struct {
+	Name    string `json:"name"`
+	PkgPath string `json:"pkgPath"`
+	Doc     string `json:"doc"`
+}
+
+// pMethod describes the signature of an interface method.
+type pMethod struct {
+	Name      string   `json:"name"`
+	Doc       string   `json:"doc"`
+	InArgs    []pArg   `json:"inArgs"`    // Input arguments
+	OutArgs   []pArg   `json:"outArgs"`   // Output arguments
+	InStream  *pArg    `json:"inStream"`  // Input stream (optional)
+	OutStream *pArg    `json:"outStream"` // Output stream (optional)
+	Tags      []string `json:"tags"`      // Method tags
+}
+
+// pArg describes the signature of a single argument.
+type pArg struct {
+	Name string `json:"name"`
+	Doc  string `json:"doc"`
+	Type string `json:"type"` // Type of the argument.
+}
+
+// Helper method to convert []signature.Interface to the parallel data
+// structure, []pInterface.
+func convertSignature(sig []signature.Interface) []pInterface {
+	ret := []pInterface{}
+	for _, ifc := range sig {
+		pIfc := pInterface{
+			Name:    ifc.Name,
+			PkgPath: ifc.PkgPath,
+			Doc:     ifc.Doc,
+			Embeds:  convertEmbeds(ifc.Embeds),
+			Methods: convertMethods(ifc.Methods),
+		}
+		ret = append(ret, pIfc)
+	}
+	return ret
+}
+
+func convertEmbeds(embeds []signature.Embed) []pEmbed {
+	ret := []pEmbed{}
+	for _, e := range embeds {
+		pE := pEmbed{
+			Name:    e.Name,
+			PkgPath: e.PkgPath,
+			Doc:     e.Doc,
+		}
+		ret = append(ret, pE)
+	}
+	return ret
+}
+
+func convertMethods(methods []signature.Method) []pMethod {
+	ret := []pMethod{}
+	for _, m := range methods {
+		pM := pMethod{
+			Name:      m.Name,
+			Doc:       m.Doc,
+			InArgs:    convertArgs(m.InArgs),
+			OutArgs:   convertArgs(m.OutArgs),
+			InStream:  convertOptArg(m.InStream),
+			OutStream: convertOptArg(m.OutStream),
+			Tags:      convertTags(m.Tags),
+		}
+		ret = append(ret, pM)
+	}
+	return ret
+}
+
+func convertArgs(args []signature.Arg) []pArg {
+	ret := []pArg{}
+	for _, a := range args {
+		pA := *convertOptArg(&a)
+		ret = append(ret, pA)
+	}
+	return ret
+}
+
+func convertOptArg(arg *signature.Arg) *pArg {
+	if arg == nil {
+		return nil
+	}
+	return &pArg{
+		Name: arg.Name,
+		Doc:  arg.Doc,
+		Type: arg.Type.String(),
+	}
+}
+
+func convertTags(tags []*vdl.Value) []string {
+	ret := []string{}
+	for _, t := range tags {
+		ret = append(ret, t.String())
+	}
+	return ret
+}
diff --git a/package.json b/package.json
index 40b83f0..e80e96b 100644
--- a/package.json
+++ b/package.json
@@ -8,7 +8,6 @@
     "browserify": "^4.2.3",
     "exorcist": "^0.1.6",
     "jshint": "^2.6.0",
-    "prova": "git+https://github.com/aghassemi/prova#0.0.4",
     "proxyquireify": "^0.5.0",
     "rework": "^1.0.1",
     "rework-import": "^1.2.1",
diff --git a/src/app.js b/src/app.js
index 3bd8ef3..1ebb55b 100644
--- a/src/app.js
+++ b/src/app.js
@@ -4,7 +4,6 @@
 
 var uuid = require('uuid');
 var mercury = require('mercury');
-var vanadium = require('vanadium');
 var addDelegatedEvents = require('./lib/mercury/add-delegated-events');
 var onboarding = require('./onboarding');
 var router = require('./router');
@@ -17,7 +16,6 @@
 var views = require('./components/browse/views');
 var userAccount = require('./components/user-account');
 var namespaceService = require('./services/namespace/service');
-var sampleWorld = require('./services/sample-world');
 var stateService = require('./services/state/service');
 var errorRoute = require('./routes/error');
 var browseRoute = require('./routes/browse');
@@ -264,16 +262,9 @@
  */
 function initVanadium() {
   viewport.setSplashMessage('Initializing Vanadium...');
-  namespaceService.initVanadium().then(function(vruntime) {
-    vruntime.once('crash', onVanadiumCrash);
-
+  namespaceService.getEmailAddress().then(function(email) {
     // Onboarding Hook for new users (async). Currently does not block.
-    onboarding(vruntime, state);
-
-    window.addEventListener('beforeunload', function() {
-      log.debug('closing runtime');
-      vruntime.close();
-    });
+    onboarding(email, state);
 
     if (state.demo()) {
       return intializeDemo();
@@ -293,7 +284,10 @@
      * Initialized the demo mode
      */
     function intializeDemo() {
-      viewport.setSplashMessage('Initializing Sample World Demo...');
+      // TODO(alexfandrianto): With JS deprecated, we may want to delete the old
+      // demo code. Instead, demo services should be launched by cmdline or Go.
+      initialized();
+      /*viewport.setSplashMessage('Initializing Sample World Demo...');
       var sampleWorldDirectory = '';
       return sampleWorld.getRootedName().then(function(name) {
         sampleWorldDirectory = name;
@@ -307,22 +301,11 @@
             viewType: 'tree'
           })
         });
-      });
+      });*/
     }
   }).catch(function(err) {
-    if (err instanceof vanadium.verror.ExtensionNotInstalledError) {
-      vanadium.extension.promptUserToInstallExtension();
-    } else {
-      var isError = true;
-      viewport.setSplashMessage(err.toString(), isError);
-      log.error(err);
-    }
+    var isError = true;
+    viewport.setSplashMessage(err.toString(), isError);
+    log.error(err);
   });
 }
-
-/*
- * Handler if Vanadium runtime crashes
- */
-function onVanadiumCrash(crashErr) {
-  events.browse.error(crashErr);
-}
\ No newline at end of file
diff --git a/src/components/browse/item-details/method-form/index.js b/src/components/browse/item-details/method-form/index.js
index 8ab341b..80c5b7d 100644
--- a/src/components/browse/item-details/method-form/index.js
+++ b/src/components/browse/item-details/method-form/index.js
@@ -146,9 +146,12 @@
  */
 function initializeInputArguments(state) {
   var param = getMethodData(state.interface(), state.methodName());
-  var startingArgs = _.range(param.inArgs.length).map(function() {
-    return ''; // Initialize array with empty string values using lodash.
-  });
+  var startingArgs = [];
+  if (param.inArgs) {
+    startingArgs = _.range(param.inArgs.length).map(function() {
+      return ''; // Initialize array with empty string values using lodash.
+    });
+  }
   setMercuryArray(state.args, startingArgs);
 }
 
@@ -161,9 +164,11 @@
     interface: state.interface(),
     methodName: state.methodName(),
   };
+  var inArgList = param.inArgs || [];
+
   return Promise.all(
     // For each argname, predict which inputs should be suggested.
-    param.inArgs.map(function(inArg, i) {
+    inArgList.map(function(inArg, i) {
       return smartService.predict(
         'learner-method-input',
         _.assign({argName: inArg.name}, input)
@@ -388,7 +393,7 @@
   var tooltip = methodSig.doc || '<no description>';
 
   // If the method takes input, add documentation about the input arguments.
-  if (methodSig.inArgs.length > 0) {
+  if (methodSig.inArgs && methodSig.inArgs.length > 0) {
     tooltip += '\n\nParameters';
     methodSig.inArgs.forEach(function(inArg) {
       tooltip += '\n';
@@ -400,7 +405,7 @@
   }
 
   // If the method returns output, add documentation about the output arguments.
-  if (methodSig.outArgs.length > 0) {
+  if (methodSig.outArgs && methodSig.outArgs.length > 0) {
     tooltip += '\n\nOutput';
     methodSig.outArgs.forEach(function(outArg) {
       tooltip += '\n';
@@ -412,11 +417,11 @@
   }
 
   // If the method has tags, show the tag types and values.
-  if (methodSig.tags.length > 0) {
+  if (methodSig.tags && methodSig.tags.length > 0) {
     tooltip += '\n\nTags';
     methodSig.tags.forEach(function(tag) {
       tooltip += '\n';
-      tooltip += '- ' + tag.val + ': ' + tag._type.name;
+      tooltip += '- ' + tag;
     });
   }
 
@@ -459,12 +464,13 @@
   var methodName = state.methodName;
   var param = getMethodData(state.interface, methodName);
   var text = methodName;
-  var hasArgs = (param.inArgs.length > 0);
+  var inArgList = param.inArgs || [];
+  var hasArgs = (inArgList.length > 0);
   if (hasArgs) {
     text += '(';
   }
   if (args !== undefined) {
-    for (var i = 0; i < param.inArgs.length; i++) {
+    for (var i = 0; i < inArgList.length; i++) {
       if (i > 0) {
         text += ', ';
       }
@@ -674,10 +680,13 @@
  * from state.args, a starred invocation's arguments, or a recommendation's.
  */
 function getRunEvent(state, events, args) {
+  var methodData = getMethodData(state.interface, state.methodName);
+  var outArgList = methodData.outArgs || [];
   return mercury.event(events.runAction, {
     name: state.itemName,
     methodName: state.methodName,
-    args: args
+    args: args,
+    numOutArgs: outArgList.length
   });
 }
 
diff --git a/src/components/browse/item-details/method-form/make-rpc.js b/src/components/browse/item-details/method-form/make-rpc.js
index c1b3093..04eed02 100644
--- a/src/components/browse/item-details/method-form/make-rpc.js
+++ b/src/components/browse/item-details/method-form/make-rpc.js
@@ -30,8 +30,8 @@
     }
   });
 
-  return namespaceService.makeRPC(data.name, data.methodName, args).catch(
-    function(err) {
+  return namespaceService.makeRPC(
+    data.name, data.methodName, args, data.numOutArgs).catch(function(err) {
       log.error('Error during RPC',
         data.name,
         data.methodName,
diff --git a/src/components/browse/item-details/mount-point-details/display-mountpoint-details.js b/src/components/browse/item-details/mount-point-details/display-mountpoint-details.js
index 2bd3733..7b83c93 100644
--- a/src/components/browse/item-details/mount-point-details/display-mountpoint-details.js
+++ b/src/components/browse/item-details/mount-point-details/display-mountpoint-details.js
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-var vanadium = require('vanadium');
 var namespaceService = require('../../../../services/namespace/service');
 
 var log = require('../../../../lib/log')(
@@ -74,7 +73,9 @@
         state.put('permissions', permissions);
       }
     }).catch(function(err) {
-      if (err instanceof vanadium.verror.NoAccessError) {
+      // TODO(alexfandrianto): We don't have access to VErrors, so this is the
+      // closest we can get to determining "notAuthorizedToSeePermissions".
+      if (err.toString().contains('NoAccess')) {
         state.put('notAuthorizedToSeePermissions', true);
       }
       log.error('Failed to get mountpoint permissions for:', name, err);
diff --git a/src/onboarding.js b/src/onboarding.js
index 66be5a8..6273fd2 100644
--- a/src/onboarding.js
+++ b/src/onboarding.js
@@ -10,11 +10,11 @@
 module.exports = onboarding;
 
 // When a new user visits the namespace browser, do a simple onboarding.
-function onboarding(rt, appState) {
+function onboarding(email, appState) {
   store.getValue('returning_user').then(function(returning) {
     if (!returning) {
       log.debug('Welcome to the Vanadium Namespace Browser!');
-      addDefaultBookmarks(rt);
+      addDefaultBookmarks(email);
 
       // TODO(alexfandrianto): We can improve the onboarding experience
       // By changing the appState variable, we can do other things to help a new
@@ -27,13 +27,11 @@
 }
 
 // Add default bookmarks to the user's store.
-function addDefaultBookmarks(rt) {
+function addDefaultBookmarks(email) {
   function bookmarkFail(err) {
     log.warn('Could not add default bookmark', err);
   }
 
-  var email = getEmailFromAccountName(rt.accountName);
-
   // Determine the default bookmarks.
   var globalMT = '/ns.dev.v.io:8101';
   var personal;
@@ -61,11 +59,6 @@
   }).catch(bookmarkFail);
 }
 
-function getEmailFromAccountName(accountName) {
-  // Use a regular expression to extract the email.
-  return /dev.v.io\/u\/(.*?)\//.exec(accountName)[1];
-}
-
 // Set the returning_user flag to true.
 function completeOnboarding() {
   store.setValue('returning_user', true).catch(function(err) {
diff --git a/src/services/namespace/interface-util.js b/src/services/namespace/interface-util.js
index be6b62a..0f30ec5 100644
--- a/src/services/namespace/interface-util.js
+++ b/src/services/namespace/interface-util.js
@@ -33,9 +33,10 @@
 function hashInterface(interface) {
   var pkgPath = interface.pkgPath + '.' + interface.name;
   var methods = interface.methods.map(function(methodData) {
+    var inArgList = methodData.inArgs || [];
     return {
       name: methodData.name,
-      inArgs: methodData.inArgs.map(function(inArg) {
+      inArgs: inArgList.map(function(inArg) {
         return inArg.name;
       })
     };
diff --git a/src/services/namespace/naming-util.js b/src/services/namespace/naming-util.js
new file mode 100644
index 0000000..5379fb4
--- /dev/null
+++ b/src/services/namespace/naming-util.js
@@ -0,0 +1,175 @@
+// Copyright 2016 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 Helpers for manipulating vanadium names.
+ * See vanadium/release/go/src/v.io/v23/naming/parse.go for the
+ * corresponding operations in golang.
+ * @private
+ */
+
+module.exports = {
+  clean: clean,
+  join: join,
+  isRooted: isRooted,
+  basename: basename,
+  stripBasename: stripBasename,
+  splitAddressName: splitAddressName,
+};
+
+/**
+ * Normalizes a name by collapsing multiple slashes and removing any
+ * trailing slashes.
+ * @param {string} name The vanadium name.
+ * @returns {string} The clean name.
+ * @memberof module:vanadium.naming
+ */
+function clean(name) {
+  return _removeTailSlash(_squashMultipleSlashes(name));
+}
+
+/**
+ * <p>Joins parts of a name into a whole. The joined name will be cleaned; it
+ * only preserved the rootedness of the name components.</p>
+ * <p>Examples:</p>
+ * <pre>
+ * join(['a, b']) -> 'a/b'
+ * join('/a/b/', '//d') -> '/a/b/d'
+ * join('//a/b', 'c/') -> '/a/b/c'
+ * </pre>
+ * @param {...string} parts Either a single array that contains the strings
+ * to join or a variable number of string arguments that will be joined.
+ * @return {string} A joined string.
+ * @memberof module:vanadium.naming
+ */
+function join(parts) {
+  if (Array.isArray(parts)) {
+    while (parts.length > 0 && parts[0] === '') {
+      parts.splice(0, 1); // Remove empty strings; they add nothing to the join.
+    }
+    var joined = parts.join('/');
+    return clean(joined);
+  }
+  return join(Array.prototype.slice.call(arguments));
+}
+
+/**
+ * Determines if a name is rooted, that is beginning with a single '/'.
+ * @param {string} name The vanadium name.
+ * @return {boolean} True iff the name is rooted.
+ * @memberof module:vanadium.naming
+ */
+function isRooted(name) {
+  return name[0] === '/';
+}
+
+/**
+ * SplitAddressName takes an object name and returns the server address and
+ * the name relative to the server.
+ * The name parameter may be a rooted name or a relative name; an empty string
+ * address is returned for the latter case.
+ * @param {string} name The vanadium name.
+ * @return {Object.<string, string>}  An object with the address and suffix
+ * split. Returned object will be in the format of:
+ * <pre>
+ * {address: string, suffix: string}
+ * </pre>
+ * Address may be in endpoint format or host:port format.
+ * @memberof module:vanadium.naming
+ */
+function splitAddressName(name) {
+  name = clean(name);
+
+  if (!isRooted(name)) {
+    return {
+      address: '',
+      suffix: name
+    };
+  }
+  name = name.substr(1); // trim the beginning "/"
+  if (name.length === 0) {
+    return {
+      address: '',
+      suffix: ''
+    };
+  }
+
+  if (name[0] === '@') { // <endpoint>/<suffix>
+    var split = _splitIntoTwo(name, '@@/');
+    if (split.suffix.length > 0) { // The trailing "@@" was stripped, restore
+      split.address = split.address + '@@';
+    }
+    return split;
+  }
+  if (name[0] === '(') { // (blessing)@host:[port]/suffix
+    var tmp = _splitIntoTwo(name, ')@').suffix;
+    var suffix = _splitIntoTwo(tmp, '/').suffix;
+    return {
+      address: _trimEnd(name, '/' + suffix),
+      suffix: suffix
+    };
+  }
+  // host:[port]/suffix
+  return _splitIntoTwo(name, '/');
+
+  function _splitIntoTwo(str, separator) {
+    var elems = str.split(separator);
+    return {
+      address: elems[0],
+      suffix: elems.splice(1).join(separator)
+    };
+  }
+}
+
+/**
+ * Gets the basename of the given vanadium name.
+ * @param {string} name The vanadium name.
+ * @return {string} The basename of the given name.
+ * @memberof module:vanadium.naming
+ */
+function basename(name) {
+  name = clean(name);
+  var split = splitAddressName(name);
+  if (split.suffix !== '') {
+    return split.suffix.substring(split.suffix.lastIndexOf('/') + 1);
+  } else {
+    return split.address;
+  }
+}
+
+/**
+ * Retrieves the parent of the given name.
+ * @param {string} name The vanadium name.
+ * @return {string | null} The parent's name or null, if there isn't one.
+ * @memberof module:vanadium.naming
+ */
+function stripBasename(name) {
+  name = clean(name);
+  var split = splitAddressName(name);
+  if (split.suffix !== '') {
+    return name.substring(0, name.lastIndexOf('/'));
+  } else {
+    return '';
+  }
+}
+
+// Replace every group of slashes in the string with a single slash.
+function _squashMultipleSlashes(s) {
+  return s.replace(/\/{2,}/g, '/');
+}
+
+// Remove the last slash in the string, if any.
+function _removeTailSlash(s) {
+  return s.replace(/\/$/g, '');
+}
+
+// Helper util that removes the given suf from the end of str
+function _trimEnd(str, suf) {
+  var index = str.lastIndexOf(suf);
+  if (index + suf.length === str.length) {
+    return str.substring(0, index);
+  } else {
+    return str;
+  }
+}
diff --git a/src/services/namespace/service.js b/src/services/namespace/service.js
index 97451fe..7c418fe 100644
--- a/src/services/namespace/service.js
+++ b/src/services/namespace/service.js
@@ -2,16 +2,14 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-var vanadium = require('vanadium');
 var mercury = require('mercury');
 var LRU = require('lru-cache');
 var EventEmitter = require('events').EventEmitter;
-var vanadiumConfig = require('../../vanadium-config');
 var itemFactory = require('./item');
 var freeze = require('../../lib/mercury/freeze');
 var sortedPush = require('../../lib/mercury/sorted-push-array');
 var log = require('../../lib/log')('services:namespace:service');
-var naming = vanadium.naming;
+var naming = require('./naming-util.js');
 naming.parseName = parseName;
 
 module.exports = {
@@ -20,42 +18,97 @@
   getRemoteBlessings: getRemoteBlessings,
   getSignature: getSignature,
   getAccountName: getAccountName,
+  getEmailAddress: getEmailAddress,
   getObjectAddresses: getObjectAddresses,
   getPermissions: getPermissions,
   resolveToMounttable: resolveToMounttable,
   makeRPC: makeRPC,
   search: search,
   util: naming,
-  initVanadium: getRuntime,
   clearCache: clearCache,
   deleteMountPoint: deleteMountPoint,
   prefixes: prefixes
 };
 
-//TODO(aghassemi) What's a good timeout? It should be shorter than this.
-//Can we use ML to dynamically change the timeout?
-//Should this be a user settings?
-var RPC_TIMEOUT = 15 * 1000;
 
 /*
- * Lazy getter and initializer for Vanadium runtime
+ * Returns an EventSource object connected to the local network.
+ * Only certain types of requests are allowed.
+ *
+ * accountName: <no parameters>  => { accountName: <string>, err: <err> }
+ * glob: string pattern  => a stream of responses
+ *   { globRes: <glob res>, globErr: <glob err>, globEnd: <bool>, err: <err> }
+ * permissions: string name =>  { permissions: <permissions>, err: <err> }
+ * deleteMountPoint: string name => { err: <err string> }
+ * resolveToMounttable: string name => { addresses: []<string>, err: <err> }
+ * objectAddresses: string name => { addresses: []<string>, err: <err> }
+ * remoteBlessings: string name => { blessings: []<string>, err: <err> }
+ * signature: string name => { signature: <signature>, err: <err> }
+ * makeRPC: { name: <string>, methodName: <string>, args: []<string>,
+ *            numOutArgs: <int> } =>
+ *          { response: <undefined, output, OR []outputs>, err: <err> }
  */
-var _runtimePromiseInstance;
+// TODO(alexfandrianto): Make this configurable here and on the Go end.
+// https://github.com/vanadium/issues/issues/1268
+var eventSourceURL = 'http://127.0.0.1:9002';
+function connectToEventSource(requestType, parameters) {
+  parameters = parameters || '';
+  var requestData = eventSourceURL +
+    '?request=' + encodeURIComponent(requestType) +
+    '&params=' + encodeURIComponent(JSON.stringify(parameters));
 
-function getRuntime() {
-  if (!_runtimePromiseInstance) {
-    _runtimePromiseInstance = vanadium.init(vanadiumConfig);
-  }
-  return _runtimePromiseInstance;
+  // Create the EventSource. Note: node does not have EventSource.
+  return new EventSource(requestData); // jshint ignore:line
+}
+
+/*
+ * Returns a Promise<value> drawn from the connected Event Source.
+ * See connectToEventSource.
+ */
+function getSingleEvent(type, params, field) {
+  return new Promise(function(resolve, reject) {
+    var ev = connectToEventSource(type, params);
+    ev.addEventListener('message', function(message) {
+      ev.close();
+      try {
+        var data = JSON.parse(message.data);
+        if (data.err) {
+          reject(data.err);
+        } else {
+          resolve(data[field]);
+        }
+      } catch (err) {
+        reject(err);
+      }
+    });
+    ev.addEventListener('error', function(err) {
+      ev.close();
+      reject(err);
+    });
+  });
 }
 
 /*
  * Returns the accountName for the currently logged in user
  * @return {Promise.<string>}
  */
+var _accountNamePromise;
 function getAccountName() {
-  return getRuntime().then(function(rt) {
-    return rt.accountName;
+  if (!_accountNamePromise) {
+    _accountNamePromise =
+      getSingleEvent('accountName', undefined, 'accountName');
+  }
+  return _accountNamePromise;
+}
+
+/*
+ * Returns the email address for the currently logged in user
+ * @return {Promise.<string>}
+ */
+function getEmailAddress() {
+  return getAccountName().then(function(accountName) {
+    // TODO(alexfandrianto): Assumes a lot about the format of the blessings.
+    return accountName.split(':')[2];
   });
 }
 
@@ -104,43 +157,92 @@
   var globItemsObservArr = mercury.array([]);
   var immutableResult = freeze(globItemsObservArr);
   immutableResult.events = new EventEmitter();
-  var ctx;
   var globItemsObservArrPromise =
-    getRuntime().then(function callGlobOnNamespace(rt) {
-      ctx = rt.getContext().withTimeout(RPC_TIMEOUT);
-      // TODO(aghassemi) use watchGlob when available
-      var namespace = rt.getNamespace();
-      return namespace.glob(ctx, pattern).stream;
-    }).then(function updateResult(globStream) {
-      globStream.on('data', function createItem(globResult) {
-        // Create an item as glob results come in and add the item to result
-        var item = createNamespaceItem(globResult);
+    Promise.resolve().then(function callGlobOnNamespace() {
+      return new Promise(function (resolve, reject) {
+        var ev = connectToEventSource('glob', pattern);
+        ev.addEventListener('error', function(err) {
+          ev.close();
+          reject(err);
+        });
 
-        var existingItem = globItemsObservArr.filter(function(curItem) {
-          return curItem().objectName === item().objectName;
-        }).get(0);
-        if (existingItem) {
-          // override the old one if new item has server
-          if (item().hasServer) {
-            var index = globItemsObservArr.indexOf(existingItem);
-            globItemsObservArr.put(index, item);
+        function handleMessageConnectionResponse(response) {
+          try {
+            var data = JSON.parse(response.data);
+            if (data.err) {
+              ev.close();
+              reject(data.err);
+            } else {
+              // We have successfully established the stream.
+              // Keep the event source open to listen for more.
+              resolve();
+            }
+          } catch (err) {
+            ev.close();
+            reject(err);
           }
-        } else {
-          var sorter = 'mountedName';
-          sortedPush(globItemsObservArr, item, sorter);
         }
-      });
 
-      globStream.on('end', function() {
-        immutableResult.events.emit('end');
-        immutableResult._hasEnded = true;
-      });
+        function handleStreamEvent(message) {
+          try {
+            var data = JSON.parse(message.data);
 
-      globStream.on('error', function emitGlobErrorAndLog(err) {
-        immutableResult.events.emit('globError', err);
-        log.warn('Glob stream error for', name, err);
-      });
+            if (data.globRes) {
+              var globResult = data.globRes;
 
+              // Handle a glob result by creating an item.
+              var item = createNamespaceItem(lowercasifyJSONObject(globResult));
+
+              var existingItem = globItemsObservArr.filter(function(curItem) {
+                return curItem().objectName === item().objectName;
+              }).get(0);
+              if (existingItem) {
+                // override the old one if new item has server
+                if (item().hasServer) {
+                  var index = globItemsObservArr.indexOf(existingItem);
+                  globItemsObservArr.put(index, item);
+                }
+              } else {
+                var sorter = 'mountedName';
+                sortedPush(globItemsObservArr, item, sorter);
+              }
+
+            } else if (data.globErr) {
+              var err = data.globErr;
+              // Handle a glob error by emitting that event.
+              immutableResult.events.emit('globError', err);
+              log.warn('Glob stream error', err);
+            } else if (data.globEnd) {
+              // Handle a glob end by emitting it. Close event source.
+              immutableResult.events.emit('end');
+              immutableResult._hasEnded = true;
+              ev.close();
+            } else {
+              // There was a data error. Close event source.
+              log.error('Event source error for', name, data.err);
+              ev.close();
+
+              // If this were an RPC, we probably would have failed earlier.
+              // So, we must also clear the cache key.
+              globCache.del(cacheKey);
+            }
+          } catch (err) {
+            log.error(err);
+          }
+        }
+
+        var initialResponse = false;
+        ev.addEventListener('message', function(response) {
+          if (!initialResponse) {
+            // Check whether the RPC and stream connection was established.
+            initialResponse = true;
+            handleMessageConnectionResponse(response);
+          } else {
+            // Handle the Glob Results and Errors from the stream.
+            handleStreamEvent(response);
+          }
+        });
+      });
     }).then(function cacheAndReturnResult() {
       globCache.set(cacheKey, immutableResult);
       return immutableResult;
@@ -215,28 +317,29 @@
  * vanadium.security.Permissions object.
  */
 function getPermissions(name) {
-  return getRuntime().then(function(rt) {
-    var ctx = rt.getContext().withTimeout(RPC_TIMEOUT);
-    var ns = rt.getNamespace();
-    return ns.getPermissions(ctx, name);
-  }).then(function(results) {
-    // getPermissions return multiple results, permissions is at
-    // outArg position 0
-    return mercury.value(results[0]);
+  return getSingleEvent('permissions', name, 'permissions').then(
+    function(perms) {
+      // perms is an object, but we want a map instead.
+      var p2 = new Map();
+      for (var key in perms) {
+        if (perms.hasOwnProperty(key)) {
+          p2.set(key, lowercasifyJSONObject(perms[key]));
+        }
+      }
+      return mercury.value(p2);
   });
 }
 
+
+
 /*
  * Deletes a mount point.
  * @param {string} name mountpoint name to delete.
  * @return {Promise<void>} Success or failure promise.
  */
 function deleteMountPoint(name) {
-  return getRuntime().then(function(rt) {
-    var ctx = rt.getContext().withTimeout(RPC_TIMEOUT);
-    var ns = rt.getNamespace();
-    return ns.delete(ctx, name, true);
-  });
+  // Note: The return value on success is undefined.
+  return getSingleEvent('deleteMountPoint', name, 'deleteMountPoint');
 }
 
 /*
@@ -246,12 +349,9 @@
  * objectAddress strings.
  */
 function resolveToMounttable(name) {
-  return getRuntime().then(function(rt) {
-    var ctx = rt.getContext().withTimeout(RPC_TIMEOUT);
-    var ns = rt.getNamespace();
-    return ns.resolveToMounttable(ctx, name);
-  }).then(function(objectAddresses) {
-    return mercury.array(objectAddresses);
+  return getSingleEvent('resolveToMounttable', name, 'addresses').then(
+    function(objectAddresses) {
+      return mercury.array(objectAddresses);
   });
 }
 
@@ -262,12 +362,9 @@
  * array of string objectAddresses
  */
 function getObjectAddresses(name) {
-  return getRuntime().then(function resolve(rt) {
-    var resolveCtx = rt.getContext().withTimeout(RPC_TIMEOUT);
-    var ns = rt.getNamespace();
-    return ns.resolve(resolveCtx, name);
-  }).then(function(objectAddresses) {
-    return mercury.array(objectAddresses);
+  return getSingleEvent('objectAddresses', name, 'addresses').then(
+    function(objectAddresses) {
+      return mercury.array(objectAddresses);
   });
 }
 
@@ -292,14 +389,11 @@
   if (cacheHit) {
     return Promise.resolve(cacheHit);
   }
-  return getRuntime().then(function invokeRemoteBlessingsMethod(rt) {
-    var ctx = rt.getContext().withTimeout(RPC_TIMEOUT);
-    var client = rt.getClient();
-    return client.remoteBlessings(ctx, objectName);
-  }).then(function cacheAndReturnRemoteBlessings(remoteBlessings) {
-    // Remote Blessings is []string representing the principals of the service.
-    remoteBlessingsCache.set(cacheKey, remoteBlessings);
-    return remoteBlessings;
+  return getSingleEvent('remoteBlessings', objectName, 'blessings').then(
+    function cacheAndReturnRemoteBlessings(remoteBlessings) {
+      // Remote Blessings is []string of the service's blessings.
+      remoteBlessingsCache.set(cacheKey, remoteBlessings);
+      return remoteBlessings;
   });
 }
 
@@ -324,40 +418,66 @@
   if (cacheHit) {
     return Promise.resolve(cacheHit);
   }
-  return getRuntime().then(function invokeSignatureMethod(rt) {
-    var ctx = rt.getContext().withTimeout(RPC_TIMEOUT);
-    var client = rt.getClient();
-    return client.signature(ctx, objectName);
-  }).then(function cacheAndReturnSignature(signature) {
-    // Signature is []interface; each interface contains method data.
-    signatureCache.set(cacheKey, signature);
-    return signature;
+  return getSingleEvent('signature', objectName, 'signature').then(
+    function cacheAndReturnSignature(signature) {
+      // Signature is []interface; each interface contains method data.
+      signatureCache.set(cacheKey, signature);
+      return signature;
   });
 }
 
+// Go through the JSON object and lowercase everything recursively.
+// We need this because it is annoying to change every Go struct to have the
+// json annotation to lowercase its field name.
+function lowercasifyJSONObject(obj) {
+  // number, string, boolean, or null
+  if (typeof obj !== 'object' || obj === null) {
+    return obj; // It's actually primitive.
+  }
+
+  // array
+  if (Array.isArray(obj)) {
+    var a = [];
+    for (var i = 0; i < obj.length; i++) {
+      a[i] = lowercasifyJSONObject(obj[i]);
+    }
+    return a;
+  }
+
+  // object
+  var cp = {};
+  for (var k in obj) {
+    if (obj.hasOwnProperty(k)) {
+      var lowerK = k[0].toLowerCase() + k.substr(1);
+      cp[lowerK] = lowercasifyJSONObject(obj[k]);
+    }
+  }
+  return cp;
+}
+
 /*
  * Make an RPC call on a service object.
  * name: string representing the name of the service
  * methodName: string for the service method name
  * args (optional): array of arguments for the service method
  */
-function makeRPC(name, methodName, args) {
-  // Adapt the method name to be lowercase again.
-  methodName = methodName[0].toLowerCase() + methodName.substr(1);
-
-  var ctx;
-  return getRuntime().then(function bindToName(rt) {
-    ctx = rt.getContext();
-    var client = rt.getClient();
-    return client.bindTo(ctx, name);
-  }).then(function callMethod(service) {
-    log.debug('Calling', methodName, 'on', name, 'with', args);
-    args.unshift(ctx.withTimeout(RPC_TIMEOUT));
-    return service[methodName].apply(null, args);
-  }).then(function returnResult(result) {
+function makeRPC(name, methodName, args, numOutArgs) {
+  log.debug('Calling', methodName, 'on', name, 'with', args, 'for', numOutArgs);
+  var call = {
+    name: name,
+    methodName: methodName,
+    args: args,
+    numOutArgs: numOutArgs
+  };
+  return getSingleEvent('makeRPC', call, 'response').then(function (result) {
     // If the result was for 0 outArg, then this returns undefined.
     // If the result was for 1 outArg, then it gets a single output.
     // If the result was for >1 outArgs, then we return []output.
+    if (numOutArgs === 0) {
+      return;
+    } else if (numOutArgs === 1) {
+      return result[0];
+    }
     return result;
   });
 }
@@ -371,18 +491,16 @@
  * about an item.
  */
 function createNamespaceItem(mountEntry) {
-
   var name = mountEntry.name;
 
   // mounted name relative to parent
   var mountedName = naming.basename(name);
   var isLeaf = mountEntry.isLeaf;
-  var hasServer = mountEntry.servers.length > 0 ||
-    !mountEntry.servesMountTable;
-  var hasMountPoint = mountEntry.servers.length > 0 ||
-    mountEntry.servesMountTable;
-  var isMounttable = mountEntry.servers.length > 0 &&
-    mountEntry.servesMountTable;
+  var hasServers = (mountEntry.servers && mountEntry.servers.length > 0);
+
+  var hasServer = hasServers || !mountEntry.servesMountTable;
+  var hasMountPoint = hasServers || mountEntry.servesMountTable;
+  var isMounttable = hasServers && mountEntry.servesMountTable;
 
   var item = itemFactory.createItem({
     objectName: name,
@@ -399,8 +517,8 @@
 /*
  * Given an arbitrary Vanadium name, parses it into an array
  * of strings.
- * For example, if name is "/ns.dev.v.io:8101/global/rps"
- * returns ["ns.dev.v.io:8101", "global", "rps"]
+ * For example, if name is '/ns.dev.v.io:8101/global/rps'
+ * returns ['ns.dev.v.io:8101', 'global', 'rps']
  * Can use namespaceService.util.isRooted to see if the name
  * is rooted (begins with a slash).
  * Note that the address part can contain slashes.
diff --git a/src/vanadium-config.js b/src/vanadium-config.js
deleted file mode 100644
index 04d6d21..0000000
--- a/src/vanadium-config.js
+++ /dev/null
@@ -1,10 +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 logLevels = require('vanadium').vlog.levels;
-var vanadiumConfig = {
-  logLevel: logLevels.INFO
-};
-
-module.exports = vanadiumConfig;