Merge branch 'master' into decode3

Change-Id: I5d440b812757a6dc8ae8746538db05b7b17bc0c0
diff --git a/cmd/mounttable/impl_test.go b/cmd/mounttable/impl_test.go
index cc46ed1..5bc59cf 100644
--- a/cmd/mounttable/impl_test.go
+++ b/cmd/mounttable/impl_test.go
@@ -113,7 +113,7 @@
 }
 
 func TestMountTableClient(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	server, endpoint, err := startServer(t, ctx)
diff --git a/cmd/principal/bless.go b/cmd/principal/bless.go
index ee0ef1a..ca6c46c 100644
--- a/cmd/principal/bless.go
+++ b/cmd/principal/bless.go
@@ -85,8 +85,13 @@
 	http.HandleFunc("/macaroon", func(w http.ResponseWriter, r *http.Request) {
 		w.Header().Set("Content-Type", "text/html")
 		tmplArgs := struct {
-			Blessings, ErrShort, ErrLong string
-		}{}
+			Blessings string
+			ErrShort  string
+			ErrLong   string
+			Browser   bool
+		}{
+			Browser: browser,
+		}
 		defer func() {
 			if len(tmplArgs.ErrShort) > 0 {
 				w.WriteHeader(http.StatusBadRequest)
@@ -174,7 +179,7 @@
 <meta charset="UTF-8">
 <!--Excluding any third-party hosted resources like scripts and stylesheets because otherwise we run the risk of leaking the macaroon out of this page (e.g., via the referrer header) -->
 <title>Vanadium Identity: Google</title>
-{{if .Blessings}}
+{{if and .Browser .Blessings}}
 <!--Attempt to close the window. Though this script does not work on many browser configurations-->
 <script type="text/javascript">window.close();</script>
 {{end}}
diff --git a/cmd/principal/v23_test.go b/cmd/principal/v23_test.go
index dddc89a..842bc1f 100644
--- a/cmd/principal/v23_test.go
+++ b/cmd/principal/v23_test.go
@@ -4,16 +4,21 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package main_test
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
-import "v.io/x/ref/test/v23tests"
+	"v.io/x/ref/test"
+	"v.io/x/ref/test/modules"
+	"v.io/x/ref/test/v23tests"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
+	modules.DispatchAndExitIfChild()
 	cleanup := v23tests.UseSharedBinDir()
 	r := m.Run()
 	cleanup()
diff --git a/cmd/servicerunner/main.go b/cmd/servicerunner/main.go
index ba2cda6..d0695fa 100644
--- a/cmd/servicerunner/main.go
+++ b/cmd/servicerunner/main.go
@@ -17,7 +17,6 @@
 import (
 	"encoding/json"
 	"fmt"
-	"io"
 	"os"
 	"strings"
 	"time"
@@ -46,12 +45,8 @@
 	wsprlib.OverrideCaveatValidation()
 	cmdServiceRunner.Flags.IntVar(&port, "port", 8124, "Port for wspr to listen on.")
 	cmdServiceRunner.Flags.StringVar(&identd, "identd", "", "Name of wspr identd server.")
-	modules.RegisterChild("rootMT", ``, rootMT)
-	modules.RegisterChild(wsprdCommand, modules.Usage(&cmdServiceRunner.Flags), startWSPR)
 }
 
-const wsprdCommand = "wsprd"
-
 func main() {
 	cmdline.HideGlobalFlagsExcept()
 	cmdline.Main(cmdServiceRunner)
@@ -68,7 +63,7 @@
 `,
 }
 
-func rootMT(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+var rootMT = modules.Register(func(env *modules.Env, args ...string) error {
 	ctx, shutdown := v23.Init()
 	defer shutdown()
 
@@ -88,13 +83,13 @@
 	if err := server.ServeDispatcher("", mt); err != nil {
 		return fmt.Errorf("root failed: %s", err)
 	}
-	fmt.Fprintf(stdout, "PID=%d\n", os.Getpid())
+	fmt.Fprintf(env.Stdout, "PID=%d\n", os.Getpid())
 	for _, ep := range eps {
-		fmt.Fprintf(stdout, "MT_NAME=%s\n", ep.Name())
+		fmt.Fprintf(env.Stdout, "MT_NAME=%s\n", ep.Name())
 	}
-	modules.WaitForEOF(stdin)
+	modules.WaitForEOF(env.Stdin)
 	return nil
-}
+}, "rootMT")
 
 // updateVars captures the vars from the given Handle's stdout and adds them to
 // the given vars map, overwriting existing entries.
@@ -130,7 +125,7 @@
 	// The dispatch to modules children must occur after the call to cmdline.Main
 	// (which calls cmdline.Parse), so that servicerunner flags are registered on
 	// the global flag.CommandLine.
-	if modules.IsModulesChildProcess() {
+	if modules.IsChildProcess() {
 		return modules.Dispatch()
 	}
 
@@ -146,7 +141,7 @@
 	}
 	defer sh.Cleanup(os.Stderr, os.Stderr)
 
-	h, err := sh.Start("rootMT", nil, "--v23.tcp.protocol=ws", "--v23.tcp.address=127.0.0.1:0")
+	h, err := sh.Start(nil, rootMT, "--v23.tcp.protocol=ws", "--v23.tcp.address=127.0.0.1:0")
 	if err != nil {
 		return err
 	}
@@ -164,7 +159,7 @@
 	defer proxyShutdown()
 	vars["PROXY_NAME"] = proxyEndpoint.Name()
 
-	h, err = sh.Start(wsprdCommand, nil, "--v23.tcp.protocol=ws", "--v23.tcp.address=127.0.0.1:0", "--v23.proxy=test/proxy", "--identd=test/identd")
+	h, err = sh.Start(nil, wsprd, "--v23.tcp.protocol=ws", "--v23.tcp.address=127.0.0.1:0", "--v23.proxy=test/proxy", "--identd=test/identd")
 	if err != nil {
 		return err
 	}
@@ -172,7 +167,7 @@
 		return err
 	}
 
-	h, err = sh.Start(identitylib.TestIdentitydCommand, nil, "--v23.tcp.protocol=ws", "--v23.tcp.address=127.0.0.1:0", "--v23.proxy=test/proxy", "--http-addr=localhost:0")
+	h, err = sh.Start(nil, identitylib.TestIdentityd, "--v23.tcp.protocol=ws", "--v23.tcp.address=127.0.0.1:0", "--v23.proxy=test/proxy", "--http-addr=localhost:0")
 	if err != nil {
 		return err
 	}
@@ -190,7 +185,7 @@
 	return nil
 }
 
-func startWSPR(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+var wsprd = modules.Register(func(env *modules.Env, args ...string) error {
 	ctx, shutdown := v23.Init()
 	defer shutdown()
 
@@ -203,7 +198,7 @@
 		proxy.Serve()
 	}()
 
-	fmt.Fprintf(stdout, "WSPR_ADDR=%s\n", addr)
-	modules.WaitForEOF(stdin)
+	fmt.Fprintf(env.Stdout, "WSPR_ADDR=%s\n", addr)
+	modules.WaitForEOF(env.Stdin)
 	return nil
-}
+}, "wsprd")
diff --git a/cmd/vdl/arith_test.go b/cmd/vdl/arith_test.go
index 4a1e6b5..f5769b6 100644
--- a/cmd/vdl/arith_test.go
+++ b/cmd/vdl/arith_test.go
@@ -108,7 +108,7 @@
 }
 
 func TestCalculator(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	server := newServer(ctx)
@@ -281,7 +281,7 @@
 }
 
 func TestArith(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	// TODO(bprosnitz) Split this test up -- it is quite long and hard to debug.
diff --git a/cmd/vdl/v23_internal_test.go b/cmd/vdl/v23_internal_test.go
index dcd0029..ae59080 100644
--- a/cmd/vdl/v23_internal_test.go
+++ b/cmd/vdl/v23_internal_test.go
@@ -4,12 +4,15 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package main
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
+	"v.io/x/ref/test"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
diff --git a/cmd/vdl/vdl_test.go b/cmd/vdl/vdl_test.go
index 0607fab..da17bb9 100644
--- a/cmd/vdl/vdl_test.go
+++ b/cmd/vdl/vdl_test.go
@@ -33,7 +33,7 @@
 	defer os.RemoveAll(outDir)
 	// TODO(toddw): test the generated java and javascript files too.
 	outOpt := fmt.Sprintf("--go-out-dir=%s", outDir)
-	env := cmdline.NewEnv()
+	env := cmdline.EnvFromOS()
 	if err := cmdline.ParseAndRun(cmdVDL, env, []string{"generate", "--lang=go", outOpt, testDir}); err != nil {
 		t.Fatalf("Execute() failed: %v", err)
 	}
diff --git a/cmd/vom/vom.go b/cmd/vom/vom.go
index 24b1d7a..e14a0bd 100644
--- a/cmd/vom/vom.go
+++ b/cmd/vom/vom.go
@@ -139,8 +139,7 @@
 		return nil
 	}
 	// Handle streaming from stdin.
-	// TODO(toddw): Add a flag to configure stdout/stderr dumping.
-	dumper := vom.NewDumper(dumpWriter{env.Stdout, env.Stdout})
+	dumper := vom.NewDumper(vom.NewDumpWriter(env.Stdout))
 	defer dumper.Close()
 	// Handle simple non-hex cases.
 	switch flagDataRep {
@@ -250,17 +249,3 @@
 	}
 	return r
 }
-
-type dumpWriter struct {
-	atom, status io.Writer
-}
-
-var _ vom.DumpWriter = dumpWriter{}
-
-func (w dumpWriter) WriteAtom(atom vom.DumpAtom) {
-	w.atom.Write([]byte(atom.String() + "\n"))
-}
-
-func (w dumpWriter) WriteStatus(status vom.DumpStatus) {
-	w.status.Write([]byte(status.String() + "\n"))
-}
diff --git a/cmd/vrpc/v23_internal_test.go b/cmd/vrpc/v23_internal_test.go
index dcd0029..ae59080 100644
--- a/cmd/vrpc/v23_internal_test.go
+++ b/cmd/vrpc/v23_internal_test.go
@@ -4,12 +4,15 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package main
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
+	"v.io/x/ref/test"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
diff --git a/cmd/vrpc/vrpc_test.go b/cmd/vrpc/vrpc_test.go
index 4ff920f..5fc6067 100644
--- a/cmd/vrpc/vrpc_test.go
+++ b/cmd/vrpc/vrpc_test.go
@@ -117,7 +117,7 @@
 }
 
 func initTest(t *testing.T) (ctx *context.T, name string, shutdown v23.Shutdown) {
-	ctx, shutdown = test.InitForTest()
+	ctx, shutdown = test.V23Init()
 
 	rpcServer, err := v23.NewServer(ctx)
 	if err != nil {
diff --git a/cmd/vrun/v23_test.go b/cmd/vrun/v23_test.go
index dd6a542..ece399a 100644
--- a/cmd/vrun/v23_test.go
+++ b/cmd/vrun/v23_test.go
@@ -4,16 +4,21 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package main_test
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
-import "v.io/x/ref/test/v23tests"
+	"v.io/x/ref/test"
+	"v.io/x/ref/test/modules"
+	"v.io/x/ref/test/v23tests"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
+	modules.DispatchAndExitIfChild()
 	cleanup := v23tests.UseSharedBinDir()
 	r := m.Run()
 	cleanup()
diff --git a/examples/rps/rpsbot/impl_test.go b/examples/rps/rpsbot/impl_test.go
index de82e86..ec531e2 100644
--- a/examples/rps/rpsbot/impl_test.go
+++ b/examples/rps/rpsbot/impl_test.go
@@ -29,7 +29,7 @@
 
 //go:generate v23 test generate
 
-func rootMT(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+var rootMT = modules.Register(func(env *modules.Env, args ...string) error {
 	ctx, shutdown := v23.Init()
 	defer shutdown()
 
@@ -49,13 +49,13 @@
 	if err := server.ServeDispatcher("", mt); err != nil {
 		return fmt.Errorf("root failed: %s", err)
 	}
-	fmt.Fprintf(stdout, "PID=%d\n", os.Getpid())
+	fmt.Fprintf(env.Stdout, "PID=%d\n", os.Getpid())
 	for _, ep := range eps {
-		fmt.Fprintf(stdout, "MT_NAME=%s\n", ep.Name())
+		fmt.Fprintf(env.Stdout, "MT_NAME=%s\n", ep.Name())
 	}
-	modules.WaitForEOF(stdin)
+	modules.WaitForEOF(env.Stdin)
 	return nil
-}
+}, "rootMT")
 
 var spec = rpc.ListenSpec{Addrs: rpc.ListenAddrs{{"tcp", "127.0.0.1:0"}}}
 
@@ -88,7 +88,7 @@
 // TestRockPaperScissorsImpl runs one rock-paper-scissors game and verifies
 // that all the counters are consistent.
 func TestRockPaperScissorsImpl(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	sh, err := modules.NewShell(ctx, nil, testing.Verbose(), t)
@@ -96,7 +96,7 @@
 		t.Fatalf("Could not create shell: %v", err)
 	}
 	defer sh.Cleanup(os.Stdout, os.Stderr)
-	h, err := sh.Start("rootMT", nil, "--v23.tcp.address=127.0.0.1:0")
+	h, err := sh.Start(nil, rootMT, "--v23.tcp.address=127.0.0.1:0")
 	if err != nil {
 		if h != nil {
 			h.Shutdown(nil, os.Stderr)
diff --git a/examples/rps/rpsbot/v23_internal_test.go b/examples/rps/rpsbot/v23_internal_test.go
index e8fff94..a80e0ec 100644
--- a/examples/rps/rpsbot/v23_internal_test.go
+++ b/examples/rps/rpsbot/v23_internal_test.go
@@ -4,27 +4,19 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package main
 
-import "fmt"
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
-import "v.io/x/ref/test/modules"
-
-func init() {
-	modules.RegisterChild("rootMT", ``, rootMT)
-}
+	"v.io/x/ref/test"
+	"v.io/x/ref/test/modules"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
-	if modules.IsModulesChildProcess() {
-		if err := modules.Dispatch(); err != nil {
-			fmt.Fprintf(os.Stderr, "modules.Dispatch failed: %v\n", err)
-			os.Exit(1)
-		}
-		return
-	}
+	modules.DispatchAndExitIfChild()
 	os.Exit(m.Run())
 }
diff --git a/examples/tunnel/tunneld/v23_test.go b/examples/tunnel/tunneld/v23_test.go
index c2a1b48..99c37d9 100644
--- a/examples/tunnel/tunneld/v23_test.go
+++ b/examples/tunnel/tunneld/v23_test.go
@@ -4,16 +4,21 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package main_test
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
-import "v.io/x/ref/test/v23tests"
+	"v.io/x/ref/test"
+	"v.io/x/ref/test/modules"
+	"v.io/x/ref/test/v23tests"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
+	modules.DispatchAndExitIfChild()
 	cleanup := v23tests.UseSharedBinDir()
 	r := m.Run()
 	cleanup()
diff --git a/lib/apilog/apilog.go b/lib/apilog/apilog.go
index 83c11b0..0ab9f7b 100644
--- a/lib/apilog/apilog.go
+++ b/lib/apilog/apilog.go
@@ -6,12 +6,9 @@
 // In particular, logcop will inject calls to these functions as the first
 // statement in methods that implement the v23 API. The output can
 // be controlled by vlog verbosity or vtrace.
-// The log lines generated have a header that refers to this file
-// and the message includes the file and line # of the caller. This is
-// currently the best we can do given the functionality of the underlying
-// vlog package. It has the advantage the api logging can be selectively
-// enabled/disabled globally using --vmodule=apilog=<level>, but the
-// disadvantage that it can't be controlled at the per file level.
+// --vmodule=apilog=<level> can be used to globally control logging,
+// and --vmodule=module=<level> can also be used to control logging
+// on a per-file basis.
 package apilog
 
 import (
@@ -115,7 +112,7 @@
 	} else {
 		output = fmt.Sprintf("call[%s %s]", callerLocation, invocationId)
 	}
-	logger.Info(output)
+	logger.InfoDepth(1, output)
 
 	// TODO(mattr): annotate vtrace span.
 	return func(ctx *context.T, v ...interface{}) {
@@ -125,7 +122,7 @@
 		} else {
 			output = fmt.Sprintf("return[%s %s]", callerLocation, invocationId)
 		}
-		logger.Info(output)
+		logger.InfoDepth(1, output)
 		// TODO(mattr): annotate vtrace span.
 	}
 }
@@ -146,11 +143,11 @@
 	callerLocation := callerLocation()
 	invocationId := newInvocationIdentifier()
 	output := fmt.Sprintf("call[%s %s]: %s", callerLocation, invocationId, fmt.Sprintf(format, v...))
-	logger.Info(output)
+	logger.InfoDepth(1, output)
 	// TODO(mattr): annotate vtrace span.
 	return func(ctx *context.T, format string, v ...interface{}) {
 		output := fmt.Sprintf("return[%s %s]: %v", callerLocation, invocationId, fmt.Sprintf(format, derefSlice(v)...))
-		logger.Info(output)
+		logger.InfoDepth(1, output)
 		// TODO(mattr): annotate vtrace span.
 	}
 }
diff --git a/lib/apilog/apilog_test.go b/lib/apilog/apilog_test.go
index d085efc..2157fae 100644
--- a/lib/apilog/apilog_test.go
+++ b/lib/apilog/apilog_test.go
@@ -68,7 +68,7 @@
 	if want, got := 2, len(contents); want != got {
 		t.Errorf("Expected %d info lines, got %d instead", want, got)
 	}
-	logCallLineRE := regexp.MustCompile(`\S+ \S+ \S+ ([^:]*):.*(call|return)\[(\S*) (\S*)`)
+	logCallLineRE := regexp.MustCompile(`\S+ \S+\s+\S+ ([^:]*):.*(call|return)\[(\S*) (\S*)`)
 	for _, line := range contents {
 		match := logCallLineRE.FindStringSubmatch(line)
 		if len(match) != 5 {
@@ -76,7 +76,7 @@
 			continue
 		}
 		fileName, callType, location, funcName := match[1], match[2], match[3], match[4]
-		if fileName != "apilog.go" {
+		if fileName != "apilog_test.go" {
 			t.Errorf("unexpected file name: %s", fileName)
 			continue
 		}
diff --git a/lib/security/securityflag/flag.go b/lib/security/securityflag/flag.go
index 0c7b3fc..dea39a0 100644
--- a/lib/security/securityflag/flag.go
+++ b/lib/security/securityflag/flag.go
@@ -45,9 +45,9 @@
 	if literal == "" {
 		a, err = access.PermissionsAuthorizerFromFile(fname, access.TypicalTagType())
 	} else {
-		var tam access.Permissions
-		if tam, err = access.ReadPermissions(bytes.NewBufferString(literal)); err == nil {
-			a, err = access.PermissionsAuthorizer(tam, access.TypicalTagType())
+		var perms access.Permissions
+		if perms, err = access.ReadPermissions(bytes.NewBufferString(literal)); err == nil {
+			a, err = access.PermissionsAuthorizer(perms, access.TypicalTagType())
 		}
 	}
 	if err != nil {
diff --git a/lib/security/securityflag/flag_test.go b/lib/security/securityflag/flag_test.go
index 29cff52..04ca9ff 100644
--- a/lib/security/securityflag/flag_test.go
+++ b/lib/security/securityflag/flag_test.go
@@ -8,7 +8,6 @@
 	"bytes"
 	"flag"
 	"fmt"
-	"io"
 	"io/ioutil"
 	"os"
 	"reflect"
@@ -45,21 +44,21 @@
 	return a
 }
 
-func tamFromFlag(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+var permFromFlag = modules.Register(func(env *modules.Env, args ...string) error {
 	nfargs := flag.CommandLine.Args()
 	tam, err := PermissionsFromFlag()
 	if err != nil {
-		fmt.Fprintf(stdout, "PermissionsFromFlag() failed: %v", err)
+		fmt.Fprintf(env.Stdout, "PermissionsFromFlag() failed: %v", err)
 		return nil
 	}
 	got := auth(access.PermissionsAuthorizer(tam, access.TypicalTagType()))
 	want := expectedAuthorizer[nfargs[0]]
 	if !reflect.DeepEqual(got, want) {
-		fmt.Fprintf(stdout, "args %#v\n", args)
-		fmt.Fprintf(stdout, "AuthorizerFromFlags() got Authorizer: %v, want: %v", got, want)
+		fmt.Fprintf(env.Stdout, "args %#v\n", args)
+		fmt.Fprintf(env.Stdout, "AuthorizerFromFlags() got Authorizer: %v, want: %v", got, want)
 	}
 	return nil
-}
+}, "permFromFlag")
 
 func writePermissionsToFile(perms access.Permissions) (string, error) {
 	f, err := ioutil.TempFile("", "permissions")
@@ -88,30 +87,29 @@
 	defer os.Remove(filename)
 
 	testdata := []struct {
-		cmd   string
+		prog  modules.Program
 		flags []string
 		auth  string
 	}{
 		{
-			cmd:   "tamFromFlag",
+			prog:  permFromFlag,
 			flags: []string{"--v23.permissions.file", "runtime:" + filename},
 			auth:  "perms2",
 		},
 		{
-			cmd:   "tamFromFlag",
+			prog:  permFromFlag,
 			flags: []string{"--v23.permissions.literal", "{}"},
 			auth:  "empty",
 		},
 		{
-			cmd:   "tamFromFlag",
+			prog:  permFromFlag,
 			flags: []string{"--v23.permissions.literal", `{"Read": {"In":["v23/alice/$", "v23/bob"]}, "Write": {"In":["v23/alice/$"]}}`},
 			auth:  "perms2",
 		},
 	}
-
 	for _, td := range testdata {
 		fp := append(td.flags, td.auth)
-		h, err := sh.Start(td.cmd, nil, fp...)
+		h, err := sh.Start(nil, td.prog, fp...)
 		if err != nil {
 			t.Errorf("unexpected error: %s", err)
 		}
diff --git a/lib/security/securityflag/v23_internal_test.go b/lib/security/securityflag/v23_internal_test.go
index d4e2d18..38cf5e0 100644
--- a/lib/security/securityflag/v23_internal_test.go
+++ b/lib/security/securityflag/v23_internal_test.go
@@ -4,27 +4,19 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package securityflag
 
-import "fmt"
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
-import "v.io/x/ref/test/modules"
-
-func init() {
-	modules.RegisterChild("tamFromFlag", ``, tamFromFlag)
-}
+	"v.io/x/ref/test"
+	"v.io/x/ref/test/modules"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
-	if modules.IsModulesChildProcess() {
-		if err := modules.Dispatch(); err != nil {
-			fmt.Fprintf(os.Stderr, "modules.Dispatch failed: %v\n", err)
-			os.Exit(1)
-		}
-		return
-	}
+	modules.DispatchAndExitIfChild()
 	os.Exit(m.Run())
 }
diff --git a/lib/signals/signals_test.go b/lib/signals/signals_test.go
index fc2f40e..63b0097 100644
--- a/lib/signals/signals_test.go
+++ b/lib/signals/signals_test.go
@@ -45,7 +45,7 @@
 }
 
 func program(stdin io.Reader, stdout io.Writer, signals ...os.Signal) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	closeStopLoop := make(chan struct{})
 	go stopLoop(v23.GetAppCycle(ctx).Stop, stdin, closeStopLoop)
 	wait := ShutdownOnSignals(ctx, signals...)
@@ -55,32 +55,32 @@
 	<-closeStopLoop
 }
 
-func handleDefaults(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
-	program(stdin, stdout)
+var handleDefaults = modules.Register(func(env *modules.Env, args ...string) error {
+	program(env.Stdin, env.Stdout)
 	return nil
-}
+}, "handleDefaults")
 
-func handleCustom(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
-	program(stdin, stdout, syscall.SIGABRT)
+var handleCustom = modules.Register(func(env *modules.Env, args ...string) error {
+	program(env.Stdin, env.Stdout, syscall.SIGABRT)
 	return nil
-}
+}, "handleCustom")
 
-func handleCustomWithStop(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
-	program(stdin, stdout, STOP, syscall.SIGABRT, syscall.SIGHUP)
+var handleCustomWithStop = modules.Register(func(env *modules.Env, args ...string) error {
+	program(env.Stdin, env.Stdout, STOP, syscall.SIGABRT, syscall.SIGHUP)
 	return nil
-}
+}, "handleCustomWithStop")
 
-func handleDefaultsIgnoreChan(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
-	ctx, shutdown := test.InitForTest()
+var handleDefaultsIgnoreChan = modules.Register(func(env *modules.Env, args ...string) error {
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	closeStopLoop := make(chan struct{})
-	go stopLoop(v23.GetAppCycle(ctx).Stop, stdin, closeStopLoop)
+	go stopLoop(v23.GetAppCycle(ctx).Stop, env.Stdin, closeStopLoop)
 	ShutdownOnSignals(ctx)
-	fmt.Fprintf(stdout, "ready\n")
+	fmt.Fprintf(env.Stdout, "ready\n")
 	<-closeStopLoop
 	return nil
-}
+}, "handleDefaultsIgnoreChan")
 
 func isSignalInSet(sig os.Signal, set []os.Signal) bool {
 	for _, s := range set {
@@ -103,12 +103,12 @@
 	}
 }
 
-func newShell(t *testing.T, ctx *context.T, command string) (*modules.Shell, modules.Handle) {
+func newShell(t *testing.T, ctx *context.T, prog modules.Program) (*modules.Shell, modules.Handle) {
 	sh, err := modules.NewShell(ctx, nil, testing.Verbose(), t)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
-	handle, err := sh.Start(command, nil)
+	handle, err := sh.Start(nil, prog)
 	if err != nil {
 		sh.Cleanup(os.Stderr, os.Stderr)
 		t.Fatalf("unexpected error: %s", err)
@@ -120,10 +120,10 @@
 // TestCleanShutdownSignal verifies that sending a signal to a child that
 // handles it by default causes the child to shut down cleanly.
 func TestCleanShutdownSignal(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
-	sh, h := newShell(t, ctx, "handleDefaults")
+	sh, h := newShell(t, ctx, handleDefaults)
 	defer sh.Cleanup(os.Stderr, os.Stderr)
 	h.Expect("ready")
 	checkSignalIsDefault(t, syscall.SIGINT)
@@ -136,10 +136,10 @@
 // TestCleanShutdownStop verifies that sending a stop comamnd to a child that
 // handles stop commands by default causes the child to shut down cleanly.
 func TestCleanShutdownStop(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
-	sh, h := newShell(t, ctx, "handleDefaults")
+	sh, h := newShell(t, ctx, handleDefaults)
 	defer sh.Cleanup(os.Stderr, os.Stderr)
 	h.Expect("ready")
 	fmt.Fprintf(h.Stdin(), "stop\n")
@@ -153,10 +153,10 @@
 // that handles stop command as part of a custom set of signals handled, causes
 // the child to shut down cleanly.
 func TestCleanShutdownStopCustom(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
-	sh, h := newShell(t, ctx, "handleCustomWithStop")
+	sh, h := newShell(t, ctx, handleCustomWithStop)
 	defer sh.Cleanup(os.Stderr, os.Stderr)
 	h.Expect("ready")
 	fmt.Fprintf(h.Stdin(), "stop\n")
@@ -177,10 +177,10 @@
 // TestStopNoHandler verifies that sending a stop command to a child that does
 // not handle stop commands causes the child to exit immediately.
 func TestStopNoHandler(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
-	sh, h := newShell(t, ctx, "handleCustom")
+	sh, h := newShell(t, ctx, handleCustom)
 	defer sh.Cleanup(os.Stderr, os.Stderr)
 	h.Expect("ready")
 	fmt.Fprintf(h.Stdin(), "stop\n")
@@ -191,10 +191,10 @@
 // that handles these signals by default causes the child to exit immediately
 // upon receiving the second signal.
 func TestDoubleSignal(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
-	sh, h := newShell(t, ctx, "handleDefaults")
+	sh, h := newShell(t, ctx, handleDefaults)
 	defer sh.Cleanup(os.Stderr, os.Stderr)
 	h.Expect("ready")
 	checkSignalIsDefault(t, syscall.SIGTERM)
@@ -209,10 +209,10 @@
 // to a child that handles these by default causes the child to exit immediately
 // upon receiving the stop command.
 func TestSignalAndStop(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
-	sh, h := newShell(t, ctx, "handleDefaults")
+	sh, h := newShell(t, ctx, handleDefaults)
 	defer sh.Cleanup(os.Stderr, os.Stderr)
 	h.Expect("ready")
 	checkSignalIsDefault(t, syscall.SIGTERM)
@@ -226,10 +226,10 @@
 // that handles stop commands by default causes the child to exit immediately
 // upon receiving the second stop command.
 func TestDoubleStop(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
-	sh, h := newShell(t, ctx, "handleDefaults")
+	sh, h := newShell(t, ctx, handleDefaults)
 	defer sh.Cleanup(os.Stderr, os.Stderr)
 	h.Expect("ready")
 	fmt.Fprintf(h.Stdin(), "stop\n")
@@ -241,10 +241,10 @@
 // TestSendUnhandledSignal verifies that sending a signal that the child does
 // not handle causes the child to exit as per the signal being sent.
 func TestSendUnhandledSignal(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
-	sh, h := newShell(t, ctx, "handleDefaults")
+	sh, h := newShell(t, ctx, handleDefaults)
 	defer sh.Cleanup(os.Stderr, os.Stderr)
 	h.Expect("ready")
 	checkSignalIsNotDefault(t, syscall.SIGABRT)
@@ -257,10 +257,10 @@
 // process to exit (ensures that there is no dependency in ShutdownOnSignals
 // on having a goroutine read from the returned channel).
 func TestDoubleSignalIgnoreChan(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
-	sh, h := newShell(t, ctx, "handleDefaultsIgnoreChan")
+	sh, h := newShell(t, ctx, handleDefaultsIgnoreChan)
 	defer sh.Cleanup(os.Stderr, os.Stderr)
 	h.Expect("ready")
 	// Even if we ignore the channel that ShutdownOnSignals returns,
@@ -275,10 +275,10 @@
 // TestHandlerCustomSignal verifies that sending a non-default signal to a
 // server that listens for that signal causes the server to shut down cleanly.
 func TestHandlerCustomSignal(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
-	sh, h := newShell(t, ctx, "handleCustom")
+	sh, h := newShell(t, ctx, handleCustom)
 	defer sh.Cleanup(os.Stderr, os.Stderr)
 	h.Expect("ready")
 	checkSignalIsNotDefault(t, syscall.SIGABRT)
@@ -292,12 +292,12 @@
 // to a server that listens for that signal causes the server to shut down
 // cleanly, even when a STOP signal is also among the handled signals.
 func TestHandlerCustomSignalWithStop(t *testing.T) {
-	rootCtx, shutdown := test.InitForTest()
+	rootCtx, shutdown := test.V23Init()
 	defer shutdown()
 
 	for _, signal := range []syscall.Signal{syscall.SIGABRT, syscall.SIGHUP} {
 		ctx, _ := vtrace.WithNewTrace(rootCtx)
-		sh, h := newShell(t, ctx, "handleCustomWithStop")
+		sh, h := newShell(t, ctx, handleCustomWithStop)
 		h.Expect("ready")
 		checkSignalIsNotDefault(t, signal)
 		syscall.Kill(h.Pid(), signal)
@@ -353,7 +353,7 @@
 
 // TestCleanRemoteShutdown verifies that remote shutdown works correctly.
 func TestCleanRemoteShutdown(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	sh, err := modules.NewShell(ctx, nil, testing.Verbose(), t)
@@ -367,7 +367,7 @@
 	sh.SetConfigKey(mgmt.ParentNameConfigKey, configServiceName)
 	sh.SetConfigKey(mgmt.ProtocolConfigKey, "tcp")
 	sh.SetConfigKey(mgmt.AddressConfigKey, "127.0.0.1:0")
-	h, err := sh.Start("handleDefaults", nil)
+	h, err := sh.Start(nil, handleDefaults)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
diff --git a/lib/signals/v23_internal_test.go b/lib/signals/v23_internal_test.go
index 4d2767f..e047d2e 100644
--- a/lib/signals/v23_internal_test.go
+++ b/lib/signals/v23_internal_test.go
@@ -4,30 +4,19 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package signals
 
-import "fmt"
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
-import "v.io/x/ref/test/modules"
-
-func init() {
-	modules.RegisterChild("handleDefaults", ``, handleDefaults)
-	modules.RegisterChild("handleCustom", ``, handleCustom)
-	modules.RegisterChild("handleCustomWithStop", ``, handleCustomWithStop)
-	modules.RegisterChild("handleDefaultsIgnoreChan", ``, handleDefaultsIgnoreChan)
-}
+	"v.io/x/ref/test"
+	"v.io/x/ref/test/modules"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
-	if modules.IsModulesChildProcess() {
-		if err := modules.Dispatch(); err != nil {
-			fmt.Fprintf(os.Stderr, "modules.Dispatch failed: %v\n", err)
-			os.Exit(1)
-		}
-		return
-	}
+	modules.DispatchAndExitIfChild()
 	os.Exit(m.Run())
 }
diff --git a/runtime/factories/fake/naming.go b/runtime/factories/fake/naming.go
index 416825d..289ce46 100644
--- a/runtime/factories/fake/naming.go
+++ b/runtime/factories/fake/naming.go
@@ -8,14 +8,18 @@
 	"v.io/v23/context"
 	"v.io/v23/namespace"
 	"v.io/v23/naming"
+	"v.io/x/ref/lib/apilog"
 )
 
 func (r *Runtime) NewEndpoint(ep string) (naming.Endpoint, error) {
+	defer apilog.LogCallf(nil, "ep=%.10s...", ep)(nil, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	panic("unimplemented")
 }
 func (r *Runtime) WithNewNamespace(ctx *context.T, roots ...string) (*context.T, namespace.T, error) {
+	defer apilog.LogCallf(ctx, "roots...=%v", roots)(ctx, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	panic("unimplemented")
 }
 func (r *Runtime) GetNamespace(ctx *context.T) namespace.T {
+	defer apilog.LogCall(ctx)(ctx) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	panic("unimplemented")
 }
diff --git a/runtime/factories/fake/rpc.go b/runtime/factories/fake/rpc.go
index d0c43ee..d80e123 100644
--- a/runtime/factories/fake/rpc.go
+++ b/runtime/factories/fake/rpc.go
@@ -7,6 +7,7 @@
 import (
 	"v.io/v23/context"
 	"v.io/v23/rpc"
+	"v.io/x/ref/lib/apilog"
 )
 
 // SetClient can be used to inject a mock client implementation into the context.
@@ -14,20 +15,25 @@
 	return context.WithValue(ctx, clientKey, client)
 }
 func (r *Runtime) WithNewClient(ctx *context.T, opts ...rpc.ClientOpt) (*context.T, rpc.Client, error) {
+	defer apilog.LogCallf(ctx, "opts...=%v", opts)(ctx, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	panic("unimplemented")
 }
 func (r *Runtime) GetClient(ctx *context.T) rpc.Client {
+	defer apilog.LogCall(ctx)(ctx) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	c, _ := ctx.Value(clientKey).(rpc.Client)
 	return c
 }
 
 func (r *Runtime) NewServer(ctx *context.T, opts ...rpc.ServerOpt) (rpc.Server, error) {
+	defer apilog.LogCallf(ctx, "opts...=%v", opts)(ctx, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	panic("unimplemented")
 }
 func (r *Runtime) WithNewStreamManager(ctx *context.T) (*context.T, error) {
+	defer apilog.LogCall(ctx)(ctx) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	panic("unimplemented")
 }
 
 func (r *Runtime) GetListenSpec(ctx *context.T) rpc.ListenSpec {
+	defer apilog.LogCall(ctx)(ctx) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	return rpc.ListenSpec{}
 }
diff --git a/runtime/factories/fake/runtime.go b/runtime/factories/fake/runtime.go
index 9a17fa7..2812aaf 100644
--- a/runtime/factories/fake/runtime.go
+++ b/runtime/factories/fake/runtime.go
@@ -9,6 +9,7 @@
 	"v.io/v23/context"
 	"v.io/v23/rpc"
 	"v.io/v23/security"
+	"v.io/x/ref/lib/apilog"
 	vsecurity "v.io/x/ref/lib/security"
 )
 
@@ -33,23 +34,28 @@
 }
 
 func (r *Runtime) Init(ctx *context.T) error {
+	defer apilog.LogCall(ctx)(ctx) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	return nil
 }
 
 func (r *Runtime) WithPrincipal(ctx *context.T, principal security.Principal) (*context.T, error) {
+	defer apilog.LogCallf(ctx, "principal=")(ctx, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	return context.WithValue(ctx, principalKey, principal), nil
 }
 
 func (r *Runtime) GetPrincipal(ctx *context.T) security.Principal {
+	defer apilog.LogCall(ctx)(ctx) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	p, _ := ctx.Value(principalKey).(security.Principal)
 	return p
 }
 
 func (r *Runtime) GetAppCycle(ctx *context.T) v23.AppCycle {
+	defer apilog.LogCall(ctx)(ctx) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	panic("unimplemented")
 }
 
 func (r *Runtime) WithBackgroundContext(ctx *context.T) *context.T {
+	defer apilog.LogCall(ctx)(ctx) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	// Note we add an extra context with a nil value here.
 	// This prevents users from travelling back through the
 	// chain of background contexts.
@@ -58,6 +64,7 @@
 }
 
 func (r *Runtime) GetBackgroundContext(ctx *context.T) *context.T {
+	defer apilog.LogCall(ctx)(ctx) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	bctx, _ := ctx.Value(backgroundKey).(*context.T)
 	if bctx == nil {
 		// There should always be a background context.  If we don't find
@@ -70,9 +77,11 @@
 }
 
 func (*Runtime) WithReservedNameDispatcher(ctx *context.T, d rpc.Dispatcher) *context.T {
+	defer apilog.LogCallf(ctx, "d=")(ctx, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	panic("unimplemented")
 }
 
 func (*Runtime) GetReservedNameDispatcher(ctx *context.T) rpc.Dispatcher {
+	defer apilog.LogCall(ctx)(ctx) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	panic("unimplmeneted")
 }
diff --git a/runtime/internal/lib/appcycle/appcycle.go b/runtime/internal/lib/appcycle/appcycle.go
index 9e9bc49..9356afc 100644
--- a/runtime/internal/lib/appcycle/appcycle.go
+++ b/runtime/internal/lib/appcycle/appcycle.go
@@ -14,6 +14,7 @@
 	"v.io/v23/rpc"
 	"v.io/v23/security"
 	"v.io/x/lib/vlog"
+	"v.io/x/ref/lib/apilog"
 
 	public "v.io/v23/services/appcycle"
 )
@@ -68,24 +69,24 @@
 }
 
 func (m *AppCycle) Stop() {
-	defer vlog.LogCall()() // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCall(nil)(nil) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	m.stop(v23.LocalStop)
 }
 
 func (*AppCycle) ForceStop() {
-	defer vlog.LogCall()() // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCall(nil)(nil) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	os.Exit(v23.ForceStopExitCode)
 }
 
 func (m *AppCycle) WaitForStop(ch chan<- string) {
-	defer vlog.LogCallf("ch=")("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(nil, "ch=")(nil, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	m.Lock()
 	defer m.Unlock()
 	m.waiters = append(m.waiters, ch)
 }
 
 func (m *AppCycle) TrackTask(ch chan<- v23.Task) {
-	defer vlog.LogCallf("ch=")("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(nil, "ch=")(nil, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	m.Lock()
 	defer m.Unlock()
 	if m.shutDown {
@@ -113,7 +114,7 @@
 }
 
 func (m *AppCycle) AdvanceGoal(delta int32) {
-	defer vlog.LogCallf("delta=%v", delta)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(nil, "delta=%v", delta)(nil, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	if delta <= 0 {
 		return
 	}
@@ -121,7 +122,7 @@
 }
 
 func (m *AppCycle) AdvanceProgress(delta int32) {
-	defer vlog.LogCallf("delta=%v", delta)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(nil, "delta=%v", delta)(nil, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	if delta <= 0 {
 		return
 	}
@@ -129,12 +130,12 @@
 }
 
 func (m *AppCycle) Remote() interface{} {
-	defer vlog.LogCall()() // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCall(nil)(nil) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	return public.AppCycleServer(m.disp)
 }
 
 func (d *invoker) Stop(ctx *context.T, call public.AppCycleStopServerCall) error {
-	defer vlog.LogCallf("ctx=,call=")("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(ctx, "call=")(ctx, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	blessings, _ := security.RemoteBlessingNames(ctx, call.Security())
 	vlog.Infof("AppCycle Stop request from %v", blessings)
 	// The size of the channel should be reasonably sized to expect not to
@@ -158,7 +159,7 @@
 }
 
 func (d *invoker) ForceStop(*context.T, rpc.ServerCall) error {
-	defer vlog.LogCall()() // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCall(nil)(nil) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	d.ac.ForceStop()
 	return fmt.Errorf("ForceStop should not reply as the process should be dead")
 }
diff --git a/runtime/internal/lib/deque/v23_internal_test.go b/runtime/internal/lib/deque/v23_internal_test.go
index 5091465..e8e5310 100644
--- a/runtime/internal/lib/deque/v23_internal_test.go
+++ b/runtime/internal/lib/deque/v23_internal_test.go
@@ -4,12 +4,15 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package deque
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
+	"v.io/x/ref/test"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
diff --git a/runtime/internal/lib/pcqueue/v23_internal_test.go b/runtime/internal/lib/pcqueue/v23_internal_test.go
index 6475bd5..40cedde 100644
--- a/runtime/internal/lib/pcqueue/v23_internal_test.go
+++ b/runtime/internal/lib/pcqueue/v23_internal_test.go
@@ -4,12 +4,15 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package pcqueue
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
+	"v.io/x/ref/test"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
diff --git a/runtime/internal/lib/publisher/publisher.go b/runtime/internal/lib/publisher/publisher.go
index 6c87030..99ccda0 100644
--- a/runtime/internal/lib/publisher/publisher.go
+++ b/runtime/internal/lib/publisher/publisher.go
@@ -54,29 +54,26 @@
 // appropriate channels.
 type publisher struct {
 	cmdchan  chan interface{} // value is one of {server,name,debug}Cmd
+	stopchan chan struct{}    // closed when no longer accepting commands.
 	donechan chan struct{}    // closed when the publisher is done
 }
 
 type addServerCmd struct {
-	server string        // server to add
-	done   chan struct{} // closed when the cmd is done
+	server string // server to add
 }
 
 type removeServerCmd struct {
-	server string        // server to remove
-	done   chan struct{} // closed when the cmd is done
+	server string // server to remove
 }
 
 type addNameCmd struct {
-	name string        // name to add
-	mt   bool          // true if server serves a mount table
-	leaf bool          // true if server is a leaf
-	done chan struct{} // closed when the cmd is done
+	name string // name to add
+	mt   bool   // true if server serves a mount table
+	leaf bool   // true if server is a leaf
 }
 
 type removeNameCmd struct {
-	name string        // name to remove
-	done chan struct{} // closed when the cmd is done
+	name string // name to remove
 }
 
 type debugCmd chan string // debug string is sent when the cmd is done
@@ -89,6 +86,7 @@
 func New(ctx *context.T, ns namespace.T, period time.Duration) Publisher {
 	p := &publisher{
 		cmdchan:  make(chan interface{}),
+		stopchan: make(chan struct{}),
 		donechan: make(chan struct{}),
 	}
 	go runLoop(ctx, p.cmdchan, p.donechan, ns, period)
@@ -99,37 +97,27 @@
 	select {
 	case p.cmdchan <- cmd:
 		return true
+	case <-p.stopchan:
+		return false
 	case <-p.donechan:
 		return false
 	}
 }
 
 func (p *publisher) AddServer(server string) {
-	done := make(chan struct{})
-	if p.sendCmd(addServerCmd{server, done}) {
-		<-done
-	}
+	p.sendCmd(addServerCmd{server})
 }
 
 func (p *publisher) RemoveServer(server string) {
-	done := make(chan struct{})
-	if p.sendCmd(removeServerCmd{server, done}) {
-		<-done
-	}
+	p.sendCmd(removeServerCmd{server})
 }
 
 func (p *publisher) AddName(name string, mt bool, leaf bool) {
-	done := make(chan struct{})
-	if p.sendCmd(addNameCmd{name, mt, leaf, done}) {
-		<-done
-	}
+	p.sendCmd(addNameCmd{name, mt, leaf})
 }
 
 func (p *publisher) RemoveName(name string) {
-	done := make(chan struct{})
-	if p.sendCmd(removeNameCmd{name, done}) {
-		<-done
-	}
+	p.sendCmd(removeNameCmd{name})
 }
 
 func (p *publisher) Status() rpc.MountState {
@@ -161,6 +149,7 @@
 // (including Stop) are no-ops.
 func (p *publisher) Stop() {
 	p.sendCmd(stopCmd{})
+	close(p.stopchan) // stop accepting new commands now.
 }
 
 func (p *publisher) WaitForStop() {
@@ -170,7 +159,6 @@
 func runLoop(ctx *context.T, cmdchan chan interface{}, donechan chan struct{}, ns namespace.T, period time.Duration) {
 	vlog.VI(2).Info("rpc pub: start runLoop")
 	state := newPubState(ctx, ns, period)
-
 	for {
 		select {
 		case cmd := <-cmdchan:
@@ -182,16 +170,12 @@
 				return
 			case addServerCmd:
 				state.addServer(tcmd.server)
-				close(tcmd.done)
 			case removeServerCmd:
 				state.removeServer(tcmd.server)
-				close(tcmd.done)
 			case addNameCmd:
 				state.addName(tcmd.name, tcmd.mt, tcmd.leaf)
-				close(tcmd.done)
 			case removeNameCmd:
 				state.removeName(tcmd.name)
-				close(tcmd.done)
 			case statusCmd:
 				tcmd <- state.getStatus()
 				close(tcmd)
diff --git a/runtime/internal/lib/publisher/publisher_test.go b/runtime/internal/lib/publisher/publisher_test.go
index 04d272f..cd69d15 100644
--- a/runtime/internal/lib/publisher/publisher_test.go
+++ b/runtime/internal/lib/publisher/publisher_test.go
@@ -34,41 +34,63 @@
 	return ctx
 }
 
-func resolve(t *testing.T, ns namespace.T, name string) []string {
-	me, err := ns.Resolve(testContext(), name)
-	if err != nil {
-		t.Fatalf("failed to resolve %q", name)
+func resolveWithRetry(t *testing.T, ns namespace.T, ctx *context.T, name string, expected int) []string {
+	deadline := time.Now().Add(time.Minute)
+	for {
+		me, err := ns.Resolve(ctx, name)
+		if err == nil && len(me.Names()) == expected {
+			return me.Names()
+		}
+		if time.Now().After(deadline) {
+			t.Fatalf("failed to resolve %q", name)
+		} else {
+			continue
+		}
+		time.Sleep(100 * time.Millisecond)
 	}
-	return me.Names()
+}
+
+func verifyMissing(t *testing.T, ns namespace.T, ctx *context.T, name string) {
+	deadline := time.Now().Add(time.Minute)
+	for {
+		if _, err := ns.Resolve(ctx, "foo"); err == nil {
+			if time.Now().After(deadline) {
+				t.Errorf("%q is still mounted", name)
+			}
+			time.Sleep(100 * time.Millisecond)
+		} else {
+			break
+		}
+	}
 }
 
 func TestAddAndRemove(t *testing.T) {
+	tctx := testContext()
 	ns := tnaming.NewSimpleNamespace()
 	pub := publisher.New(testContext(), ns, time.Second)
 	pub.AddName("foo", false, false)
 	pub.AddServer("foo-addr")
-	if got, want := resolve(t, ns, "foo"), []string{"/foo-addr"}; !reflect.DeepEqual(got, want) {
+	if got, want := resolveWithRetry(t, ns, tctx, "foo", 1), []string{"/foo-addr"}; !reflect.DeepEqual(got, want) {
 		t.Errorf("got %q, want %q", got, want)
 	}
 	pub.AddServer("bar-addr")
-	got, want := resolve(t, ns, "foo"), []string{"/bar-addr", "/foo-addr"}
+	got, want := resolveWithRetry(t, ns, tctx, "foo", 2), []string{"/bar-addr", "/foo-addr"}
 	sort.Strings(got)
 	if !reflect.DeepEqual(got, want) {
 		t.Errorf("got %q, want %q", got, want)
 	}
 	pub.AddName("baz", false, false)
-	got = resolve(t, ns, "baz")
+	got = resolveWithRetry(t, ns, tctx, "baz", 2)
 	sort.Strings(got)
 	if !reflect.DeepEqual(got, want) {
 		t.Errorf("got %q, want %q", got, want)
 	}
 	pub.RemoveName("foo")
-	if _, err := ns.Resolve(testContext(), "foo"); err == nil {
-		t.Errorf("expected an error")
-	}
+	verifyMissing(t, ns, tctx, "foo")
 }
 
 func TestStatus(t *testing.T) {
+	tctx := testContext()
 	ns := tnaming.NewSimpleNamespace()
 	pub := publisher.New(testContext(), ns, time.Second)
 	pub.AddName("foo", false, false)
@@ -78,15 +100,15 @@
 	}
 	pub.AddServer("foo-addr")
 
-	// Wait for the publisher to asynchronously publish server the
+	// Wait for the publisher to asynchronously publish the
 	// requisite number of servers.
 	ch := make(chan error, 1)
 	waitFor := func(n int) {
-		then := time.Now()
+		deadline := time.Now().Add(time.Minute)
 		for {
 			status = pub.Status()
 			if got, want := len(status), n; got != want {
-				if time.Now().Sub(then) > time.Minute {
+				if time.Now().After(deadline) {
 					ch <- fmt.Errorf("got %d, want %d", got, want)
 					return
 				}
@@ -105,6 +127,12 @@
 
 	pub.AddServer("bar-addr")
 	pub.AddName("baz", false, false)
+
+	go waitFor(4)
+	if err := <-ch; err != nil {
+		t.Fatalf("%s", err)
+	}
+
 	status = pub.Status()
 	names := status.Names()
 	if got, want := names, []string{"baz", "foo"}; !reflect.DeepEqual(got, want) {
@@ -115,11 +143,9 @@
 		t.Errorf("got %q, want %q", got, want)
 	}
 	pub.RemoveName("foo")
-	if _, err := ns.Resolve(testContext(), "foo"); err == nil {
-		t.Errorf("expected an error")
-	}
-	status = pub.Status()
+	verifyMissing(t, ns, tctx, "foo")
 
+	status = pub.Status()
 	go waitFor(2)
 	if err := <-ch; err != nil {
 		t.Fatalf("%s", err)
diff --git a/runtime/internal/lib/publisher/v23_internal_test.go b/runtime/internal/lib/publisher/v23_internal_test.go
index 399d8d5..569e177 100644
--- a/runtime/internal/lib/publisher/v23_internal_test.go
+++ b/runtime/internal/lib/publisher/v23_internal_test.go
@@ -4,12 +4,15 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package publisher
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
+	"v.io/x/ref/test"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
diff --git a/runtime/internal/lib/sync/v23_internal_test.go b/runtime/internal/lib/sync/v23_internal_test.go
index 008ca9f..16f69e9 100644
--- a/runtime/internal/lib/sync/v23_internal_test.go
+++ b/runtime/internal/lib/sync/v23_internal_test.go
@@ -4,12 +4,15 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package sync
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
+	"v.io/x/ref/test"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
diff --git a/runtime/internal/lib/upcqueue/v23_internal_test.go b/runtime/internal/lib/upcqueue/v23_internal_test.go
index af1b3c8..794f10f 100644
--- a/runtime/internal/lib/upcqueue/v23_internal_test.go
+++ b/runtime/internal/lib/upcqueue/v23_internal_test.go
@@ -4,12 +4,15 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package upcqueue
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
+	"v.io/x/ref/test"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
diff --git a/runtime/internal/lib/websocket/v23_internal_test.go b/runtime/internal/lib/websocket/v23_internal_test.go
index 2051301..a77fac4 100644
--- a/runtime/internal/lib/websocket/v23_internal_test.go
+++ b/runtime/internal/lib/websocket/v23_internal_test.go
@@ -4,12 +4,15 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package websocket
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
+	"v.io/x/ref/test"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
diff --git a/runtime/internal/naming/endpoint.go b/runtime/internal/naming/endpoint.go
index d1b238f..014b179 100644
--- a/runtime/internal/naming/endpoint.go
+++ b/runtime/internal/naming/endpoint.go
@@ -14,7 +14,7 @@
 
 	"v.io/v23/naming"
 	"v.io/x/lib/metadata"
-	"v.io/x/lib/vlog"
+	"v.io/x/ref/lib/apilog"
 )
 
 const (
@@ -157,7 +157,7 @@
 var defaultVersion = 5
 
 func (ep *Endpoint) VersionedString(version int) string {
-	defer vlog.LogCallf("version=%v", version)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(nil, "version=%v", version)(nil, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	switch version {
 	default:
 		return ep.VersionedString(defaultVersion)
diff --git a/runtime/internal/naming/namespace/all_test.go b/runtime/internal/naming/namespace/all_test.go
index fd269d5..457fc23 100644
--- a/runtime/internal/naming/namespace/all_test.go
+++ b/runtime/internal/naming/namespace/all_test.go
@@ -31,8 +31,19 @@
 
 //go:generate v23 test generate
 
+func resolveWithRetry(ctx *context.T, name string, opts ...naming.NamespaceOpt) *naming.MountEntry {
+	ns := v23.GetNamespace(ctx)
+	for {
+		mp, err := ns.Resolve(ctx, name, opts...)
+		if err == nil {
+			return mp
+		}
+		time.Sleep(100 * time.Millisecond)
+	}
+}
+
 func createContexts(t *testing.T) (sc, c *context.T, cleanup func()) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	var (
 		err error
 		psc = testutil.NewPrincipal("sc")
@@ -222,6 +233,8 @@
 		boom(t, "Failed to serve mount table at %s: %s", mountPoint, err)
 	}
 	t.Logf("server %q -> %s", eps[0].Name(), mountPoint)
+	// Wait until the mount point appears in the mount table.
+	resolveWithRetry(ctx, mountPoint)
 	return &serverEntry{mountPoint: mountPoint, server: s, endpoint: eps[0], name: eps[0].Name()}
 }
 
@@ -644,17 +657,12 @@
 		mtCtx, _       = v23.WithPrincipal(ctx, testutil.NewPrincipal()) // intermediate mounttable
 		serverCtx, _   = v23.WithPrincipal(ctx, testutil.NewPrincipal()) // end server
 		clientCtx, _   = v23.WithPrincipal(ctx, testutil.NewPrincipal()) // client process (doing Resolves).
-		idp            = testutil.NewIDProvider("idp")                   // identity provider
+		clientNs       = v23.GetNamespace(clientCtx)
+		serverNs       = v23.GetNamespace(serverCtx)
+		idp            = testutil.NewIDProvider("idp") // identity provider
 		serverEndpoint = naming.FormatEndpoint("tcp", "127.0.0.1:14141")
-
-		resolve = func(name string, opts ...naming.NamespaceOpt) (*naming.MountEntry, error) {
-			return v23.GetNamespace(clientCtx).Resolve(clientCtx, name, opts...)
-		}
-
-		mount = func(name, server string, ttl time.Duration, opts ...naming.NamespaceOpt) error {
-			return v23.GetNamespace(serverCtx).Mount(serverCtx, name, server, ttl, opts...)
-		}
 	)
+
 	// Setup default blessings for the processes.
 	idp.Bless(v23.GetPrincipal(rootMtCtx), "rootmt")
 	idp.Bless(v23.GetPrincipal(serverCtx), "server")
@@ -668,45 +676,46 @@
 	}
 	// Disable caching in the client so that any Mount calls by the server
 	// are noticed immediately.
-	v23.GetNamespace(clientCtx).CacheCtl(naming.DisableCache(true))
+	clientNs.CacheCtl(naming.DisableCache(true))
 
 	// Intermediate mounttables should be authenticated.
 	mt := runMT(t, mtCtx, "mt")
 	defer func() {
 		mt.server.Stop()
 	}()
+
 	// Mount a server on "mt".
-	if err := mount("mt/server", serverEndpoint, time.Minute, naming.ReplaceMount(true)); err != nil {
+	if err := serverNs.Mount(serverCtx, "mt/server", serverEndpoint, time.Minute, naming.ReplaceMount(true)); err != nil {
 		t.Error(err)
 	}
+
 	// The namespace root should be authenticated too
-	if e, err := resolve("mt/server"); err != nil {
-		t.Errorf("Got (%v, %v)", e, err)
-	} else {
-		// Host:Port and Endpoint versions of the other namespace root
-		// (which has different blessings)
-		hproot := fmt.Sprintf("(otherroot)@%v", rootmt.endpoint.Addr())
-		eproot := naming.FormatEndpoint(rootmt.endpoint.Addr().Network(), rootmt.endpoint.Addr().String(), rootmt.endpoint.RoutingID(), naming.BlessingOpt("otherroot"), naming.ServesMountTable(rootmt.endpoint.ServesMountTable()))
-		for _, root := range []string{hproot, eproot} {
-			name := naming.JoinAddressName(root, "mt")
-			// Rooted name resolutions should fail authorization because of the "otherrot"
-			if e, err := resolve(name); verror.ErrorID(err) != verror.ErrNotTrusted.ID {
-				t.Errorf("resolve(%q) returned (%v, errorid=%v %v), wanted errorid=%v", name, e, verror.ErrorID(err), err, verror.ErrNotTrusted.ID)
-			}
-			// But not fail if the skip-authorization option is provided
-			if e, err := resolve(name, options.SkipServerEndpointAuthorization{}); err != nil {
-				t.Errorf("resolve(%q): Got (%v, %v), expected resolution to succeed", name, e, err)
-			}
-			// The namespace root from the context should be authorized as well.
-			ctx, ns, _ := v23.WithNewNamespace(clientCtx, naming.JoinAddressName(root, ""))
-			if e, err := ns.Resolve(ctx, "mt/server"); verror.ErrorID(err) != verror.ErrNotTrusted.ID {
-				t.Errorf("resolve with root=%q returned (%v, errorid=%v %v), wanted errorid=%v: %s", root, e, verror.ErrorID(err), err, verror.ErrNotTrusted.ID, verror.DebugString(err))
-			}
-			if _, err := ns.Resolve(ctx, "mt/server", options.SkipServerEndpointAuthorization{}); err != nil {
-				t.Errorf("resolve with root=%q should have succeeded when authorization checks are skipped. Got %v: %s", root, err, verror.DebugString(err))
-			}
+	resolveWithRetry(clientCtx, "mt/server")
+	// Host:Port and Endpoint versions of the other namespace root
+	// (which has different blessings)
+	hproot := fmt.Sprintf("(otherroot)@%v", rootmt.endpoint.Addr())
+	eproot := naming.FormatEndpoint(rootmt.endpoint.Addr().Network(), rootmt.endpoint.Addr().String(), rootmt.endpoint.RoutingID(), naming.BlessingOpt("otherroot"), naming.ServesMountTable(rootmt.endpoint.ServesMountTable()))
+	for _, root := range []string{hproot, eproot} {
+		name := naming.JoinAddressName(root, "mt")
+		// Rooted name resolutions should fail authorization because of the "otherrot"
+		if e, err := clientNs.Resolve(clientCtx, name); verror.ErrorID(err) != verror.ErrNotTrusted.ID {
+			t.Errorf("resolve(%q) returned (%v, errorid=%v %v), wanted errorid=%v", name, e, verror.ErrorID(err), err, verror.ErrNotTrusted.ID)
+		}
+		// But not fail if the skip-authorization option is provided
+		if e, err := clientNs.Resolve(clientCtx, name, options.SkipServerEndpointAuthorization{}); err != nil {
+			t.Errorf("resolve(%q): Got (%v, %v), expected resolution to succeed", name, e, err)
+		}
+
+		// The namespace root from the context should be authorized as well.
+		ctx, ns, _ := v23.WithNewNamespace(clientCtx, naming.JoinAddressName(root, ""))
+		if e, err := ns.Resolve(ctx, "mt/server"); verror.ErrorID(err) != verror.ErrNotTrusted.ID {
+			t.Errorf("resolve with root=%q returned (%v, errorid=%v %v), wanted errorid=%v: %s", root, e, verror.ErrorID(err), err, verror.ErrNotTrusted.ID, verror.DebugString(err))
+		}
+		if _, err := ns.Resolve(ctx, "mt/server", options.SkipServerEndpointAuthorization{}); err != nil {
+			t.Errorf("resolve with root=%q should have succeeded when authorization checks are skipped. Got %v: %s", root, err, verror.DebugString(err))
 		}
 	}
+
 	// Imagine that the network address of "mt" has been taken over by an
 	// attacker. However, this attacker cannot mess with the mount entry
 	// for "mt". This would result in "mt" and its mount entry (in the
@@ -717,9 +726,9 @@
 		t.Error(err)
 	}
 
-	if e, err := resolve("mt/server", options.SkipServerEndpointAuthorization{}); err != nil {
+	if e, err := clientNs.Resolve(serverCtx, "mt/server", options.SkipServerEndpointAuthorization{}); err != nil {
 		t.Errorf("Resolve should succeed when skipping server authorization. Got (%v, %v) %s", e, err, verror.DebugString(err))
-	} else if e, err := resolve("mt/server"); verror.ErrorID(err) != verror.ErrNotTrusted.ID {
+	} else if e, err := clientNs.Resolve(serverCtx, "mt/server"); verror.ErrorID(err) != verror.ErrNotTrusted.ID {
 		t.Errorf("Resolve should have failed with %q because an attacker has taken over the intermediate mounttable. Got (%+v, errorid=%q:%v)", verror.ErrNotTrusted.ID, e, verror.ErrorID(err), err)
 	}
 }
@@ -784,10 +793,7 @@
 	}
 	defer server.Stop()
 
-	mountEntry, err := ns.Resolve(ctx, "leaf")
-	if err != nil {
-		boom(t, "ns.Resolve failed: %v", err)
-	}
+	mountEntry := resolveWithRetry(ctx, "leaf")
 	if expected := true; mountEntry.IsLeaf != expected {
 		boom(t, "unexpected mountEntry.IsLeaf value. Got %v, expected %v", mountEntry.IsLeaf, expected)
 	}
diff --git a/runtime/internal/naming/namespace/glob.go b/runtime/internal/naming/namespace/glob.go
index 732b48c..c062d08 100644
--- a/runtime/internal/naming/namespace/glob.go
+++ b/runtime/internal/naming/namespace/glob.go
@@ -17,6 +17,7 @@
 	"v.io/v23/rpc"
 	"v.io/v23/verror"
 
+	"v.io/x/ref/lib/apilog"
 	"v.io/x/ref/lib/glob"
 )
 
@@ -231,7 +232,7 @@
 
 // Glob implements naming.MountTable.Glob.
 func (ns *namespace) Glob(ctx *context.T, pattern string, opts ...naming.NamespaceOpt) (chan naming.GlobReply, error) {
-	defer vlog.LogCallf("ctx=,pattern=%.10s...,opts...=%v", pattern, opts)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(ctx, "pattern=%.10s...,opts...=%v", pattern, opts)(ctx, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	// Root the pattern.  If we have no servers to query, give up.
 	e, patternWasRooted := ns.rootMountEntry(pattern)
 	if len(e.Servers) == 0 {
diff --git a/runtime/internal/naming/namespace/mount.go b/runtime/internal/naming/namespace/mount.go
index ccdc7fb..9bf8494 100644
--- a/runtime/internal/naming/namespace/mount.go
+++ b/runtime/internal/naming/namespace/mount.go
@@ -14,6 +14,7 @@
 	"v.io/v23/rpc"
 	"v.io/v23/security"
 	"v.io/x/lib/vlog"
+	"v.io/x/ref/lib/apilog"
 )
 
 // mountIntoMountTable mounts a single server into a single mount table.
@@ -26,7 +27,7 @@
 
 // Mount implements Namespace.Mount.
 func (ns *namespace) Mount(ctx *context.T, name, server string, ttl time.Duration, opts ...naming.NamespaceOpt) error {
-	defer vlog.LogCallf("ctx=,name=%.10s...,server=%.10s...,ttl=%v,opts...=%v", name, server, ttl, opts)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(ctx, "name=%.10s...,server=%.10s...,ttl=%v,opts...=%v", name, server, ttl, opts)(ctx, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	var flags naming.MountFlag
 	for _, o := range opts {
 		// NB: used a switch since we'll be adding more options.
@@ -66,7 +67,7 @@
 
 // Unmount implements Namespace.Unmount.
 func (ns *namespace) Unmount(ctx *context.T, name, server string, opts ...naming.NamespaceOpt) error {
-	defer vlog.LogCallf("ctx=,name=%.10s...,server=%.10s...,opts...=%v", name, server, opts)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(ctx, "name=%.10s...,server=%.10s...,opts...=%v", name, server, opts)(ctx, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	// Unmount the server from all the mount tables.
 	client := v23.GetClient(ctx)
 	f := func(ctx *context.T, mt, id string) status {
@@ -88,7 +89,7 @@
 
 // RDeleteemove implements Namespace.Delete.
 func (ns *namespace) Delete(ctx *context.T, name string, deleteSubtree bool, opts ...naming.NamespaceOpt) error {
-	defer vlog.LogCallf("ctx=,name=%.10s...,deleteSubtree=%v,opts...=%v", name, deleteSubtree, opts)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(ctx, "name=%.10s...,deleteSubtree=%v,opts...=%v", name, deleteSubtree, opts)(ctx, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	// Remove from all the mount tables.
 	client := v23.GetClient(ctx)
 	f := func(ctx *context.T, mt, id string) status {
diff --git a/runtime/internal/naming/namespace/namespace.go b/runtime/internal/naming/namespace/namespace.go
index 649a2fd..dc8db7c 100644
--- a/runtime/internal/naming/namespace/namespace.go
+++ b/runtime/internal/naming/namespace/namespace.go
@@ -8,15 +8,15 @@
 	"sync"
 	"time"
 
-	inaming "v.io/x/ref/runtime/internal/naming"
-
 	"v.io/v23/context"
 	"v.io/v23/naming"
 	"v.io/v23/rpc"
 	"v.io/v23/security"
 	vdltime "v.io/v23/vdlroot/time"
 	"v.io/v23/verror"
-	"v.io/x/lib/vlog"
+
+	"v.io/x/ref/lib/apilog"
+	inaming "v.io/x/ref/runtime/internal/naming"
 )
 
 const defaultMaxResolveDepth = 32
@@ -72,7 +72,7 @@
 
 // SetRoots implements naming.Namespace.SetRoots
 func (ns *namespace) SetRoots(roots ...string) error {
-	defer vlog.LogCallf("roots...=%v", roots)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(nil, "roots...=%v", roots)(nil, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	// Allow roots to be cleared with a call of SetRoots()
 	if len(roots) > 0 && !rooted(roots) {
 		return badRoots(roots)
@@ -193,7 +193,7 @@
 
 // CacheCtl implements naming.Namespace.CacheCtl
 func (ns *namespace) CacheCtl(ctls ...naming.CacheCtl) []naming.CacheCtl {
-	defer vlog.LogCallf("ctls...=%v", ctls)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(nil, "ctls...=%v", ctls)(nil, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	for _, c := range ctls {
 		switch v := c.(type) {
 		case naming.DisableCache:
diff --git a/runtime/internal/naming/namespace/perms.go b/runtime/internal/naming/namespace/perms.go
index 6e12398..7ee0b16 100644
--- a/runtime/internal/naming/namespace/perms.go
+++ b/runtime/internal/naming/namespace/perms.go
@@ -12,6 +12,7 @@
 	"v.io/v23/rpc"
 	"v.io/v23/security/access"
 	"v.io/x/lib/vlog"
+	"v.io/x/ref/lib/apilog"
 )
 
 // setPermsInMountTable sets the Permissions in a single server.
@@ -23,7 +24,7 @@
 }
 
 func (ns *namespace) SetPermissions(ctx *context.T, name string, perms access.Permissions, version string, opts ...naming.NamespaceOpt) error {
-	defer vlog.LogCallf("ctx=,name=%.10s...,perms=,version=%.10s...,opts...=%v", name, version, opts)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(ctx, "name=%.10s...,perms=,version=%.10s...,opts...=%v", name, version, opts)(ctx, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	client := v23.GetClient(ctx)
 
 	// Apply to all mount tables implementing the name.
@@ -37,7 +38,7 @@
 
 // GetPermissions gets Permissions from a mount table.
 func (ns *namespace) GetPermissions(ctx *context.T, name string, opts ...naming.NamespaceOpt) (perms access.Permissions, version string, err error) {
-	defer vlog.LogCallf("ctx=,name=%.10s...,opts...=%v", name, opts)("perms=,version=%.10s...,err=%v", &version, &err) // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(ctx, "name=%.10s...,opts...=%v", name, opts)(ctx, "perms=,version=%.10s...,err=%v", &version, &err) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	client := v23.GetClient(ctx)
 
 	// Resolve to all the mount tables implementing name.
diff --git a/runtime/internal/naming/namespace/perms_test.go b/runtime/internal/naming/namespace/perms_test.go
index 3a0c916..92090fb 100644
--- a/runtime/internal/naming/namespace/perms_test.go
+++ b/runtime/internal/naming/namespace/perms_test.go
@@ -27,7 +27,7 @@
 }
 
 func initTest() (rootCtx *context.T, aliceCtx *context.T, bobCtx *context.T, shutdown v23.Shutdown) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	var err error
 	if rootCtx, err = v23.WithPrincipal(ctx, testutil.NewPrincipal("root")); err != nil {
 		panic("failed to set root principal")
@@ -254,8 +254,5 @@
 	}
 
 	// Root should be able to resolve it.
-	_, err = ns.Resolve(rootCtx, name)
-	if err != nil {
-		t.Fatalf("as root Resolve %s: %s", name, err)
-	}
+	resolveWithRetry(rootCtx, name)
 }
diff --git a/runtime/internal/naming/namespace/resolve.go b/runtime/internal/naming/namespace/resolve.go
index 7539d40..f0abc5e 100644
--- a/runtime/internal/naming/namespace/resolve.go
+++ b/runtime/internal/naming/namespace/resolve.go
@@ -15,6 +15,7 @@
 	"v.io/v23/rpc"
 	"v.io/v23/verror"
 	"v.io/x/lib/vlog"
+	"v.io/x/ref/lib/apilog"
 )
 
 func (ns *namespace) resolveAgainstMountTable(ctx *context.T, client rpc.Client, e *naming.MountEntry, opts ...rpc.CallOpt) (*naming.MountEntry, error) {
@@ -61,7 +62,7 @@
 
 // Resolve implements v.io/v23/naming.Namespace.
 func (ns *namespace) Resolve(ctx *context.T, name string, opts ...naming.NamespaceOpt) (*naming.MountEntry, error) {
-	defer vlog.LogCallf("ctx=,name=%.10s...,opts...=%v", name, opts)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(ctx, "name=%.10s...,opts...=%v", name, opts)(ctx, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	e, _ := ns.rootMountEntry(name, opts...)
 	if vlog.V(2) {
 		_, file, line, _ := runtime.Caller(1)
@@ -105,7 +106,7 @@
 
 // ResolveToMountTable implements v.io/v23/naming.Namespace.
 func (ns *namespace) ResolveToMountTable(ctx *context.T, name string, opts ...naming.NamespaceOpt) (*naming.MountEntry, error) {
-	defer vlog.LogCallf("ctx=,name=%.10s...,opts...=%v", name, opts)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(ctx, "name=%.10s...,opts...=%v", name, opts)(ctx, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	e, _ := ns.rootMountEntry(name, opts...)
 	if vlog.V(2) {
 		_, file, line, _ := runtime.Caller(1)
@@ -156,7 +157,7 @@
 // FlushCache flushes the most specific entry found for name.  It returns true if anything was
 // actually flushed.
 func (ns *namespace) FlushCacheEntry(name string) bool {
-	defer vlog.LogCallf("name=%.10s...", name)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(nil, "name=%.10s...", name)(nil, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	flushed := false
 	for _, n := range ns.rootName(name) {
 		// Walk the cache as we would in a resolution.  Unlike a resolution, we have to follow
diff --git a/runtime/internal/naming/namespace/v23_internal_test.go b/runtime/internal/naming/namespace/v23_internal_test.go
index 15b3504..cf82452 100644
--- a/runtime/internal/naming/namespace/v23_internal_test.go
+++ b/runtime/internal/naming/namespace/v23_internal_test.go
@@ -4,12 +4,15 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package namespace
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
+	"v.io/x/ref/test"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
diff --git a/runtime/internal/rpc/benchmark/benchmark_test.go b/runtime/internal/rpc/benchmark/benchmark_test.go
index fe41273..5d98e34 100644
--- a/runtime/internal/rpc/benchmark/benchmark_test.go
+++ b/runtime/internal/rpc/benchmark/benchmark_test.go
@@ -110,7 +110,7 @@
 	// We do not use defer here since this program will exit at the end of
 	// this function through os.Exit().
 	var shutdown v23.Shutdown
-	ctx, shutdown = test.InitForTest()
+	ctx, shutdown = test.V23Init()
 
 	var serverStop func()
 	serverEP, serverStop := internal.StartServer(ctx, v23.GetListenSpec(ctx))
diff --git a/runtime/internal/rpc/benchmark/glob/glob_test.go b/runtime/internal/rpc/benchmark/glob/glob_test.go
index 44454b9..3bb9c31 100644
--- a/runtime/internal/rpc/benchmark/glob/glob_test.go
+++ b/runtime/internal/rpc/benchmark/glob/glob_test.go
@@ -159,7 +159,7 @@
 }
 
 func RunBenchmarkGlob(b *testing.B, obj interface{}) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	addr, stop, err := startServer(b, ctx, obj)
diff --git a/runtime/internal/rpc/benchmark/simple/main.go b/runtime/internal/rpc/benchmark/simple/main.go
index b5ba01c..6806775 100644
--- a/runtime/internal/rpc/benchmark/simple/main.go
+++ b/runtime/internal/rpc/benchmark/simple/main.go
@@ -121,7 +121,7 @@
 	test.Init()
 
 	var shutdown v23.Shutdown
-	ctx, shutdown = test.InitForTest()
+	ctx, shutdown = test.V23Init()
 	defer shutdown()
 
 	var serverStop func()
diff --git a/runtime/internal/rpc/client.go b/runtime/internal/rpc/client.go
index ff1ee51..8b74229 100644
--- a/runtime/internal/rpc/client.go
+++ b/runtime/internal/rpc/client.go
@@ -28,6 +28,7 @@
 	"v.io/v23/vom"
 	"v.io/v23/vtrace"
 
+	"v.io/x/ref/lib/apilog"
 	inaming "v.io/x/ref/runtime/internal/naming"
 	"v.io/x/ref/runtime/internal/rpc/stream"
 	"v.io/x/ref/runtime/internal/rpc/stream/vc"
@@ -185,12 +186,12 @@
 }
 
 func (c *client) StartCall(ctx *context.T, name, method string, args []interface{}, opts ...rpc.CallOpt) (rpc.ClientCall, error) {
-	defer vlog.LogCallf("ctx=,name=%.10s...,method=%.10s...,args=,opts...=%v", name, method, opts)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(ctx, "name=%.10s...,method=%.10s...,args=,opts...=%v", name, method, opts)(ctx, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	return c.startCall(ctx, name, method, args, opts)
 }
 
 func (c *client) Call(ctx *context.T, name, method string, inArgs, outArgs []interface{}, opts ...rpc.CallOpt) error {
-	defer vlog.LogCallf("ctx=,name=%.10s...,method=%.10s...,inArgs=,outArgs=,opts...=%v", name, method, opts)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(ctx, "name=%.10s...,method=%.10s...,inArgs=,outArgs=,opts...=%v", name, method, opts)(ctx, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	deadline := getDeadline(ctx, opts)
 
 	var lastErr error
@@ -728,7 +729,7 @@
 }
 
 func (c *client) Close() {
-	defer vlog.LogCall()() // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCall(nil)(nil) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	for _, v := range c.vcCache.Close() {
 		c.streamMgr.ShutdownEndpoint(v.RemoteEndpoint())
 	}
@@ -851,7 +852,7 @@
 }
 
 func (fc *flowClient) Send(item interface{}) error {
-	defer vlog.LogCallf("item=")("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(nil, "item=")(nil, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	if fc.sendClosed {
 		return verror.New(verror.ErrAborted, fc.ctx)
 	}
@@ -895,7 +896,7 @@
 }
 
 func (fc *flowClient) Recv(itemptr interface{}) error {
-	defer vlog.LogCallf("itemptr=")("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(nil, "itemptr=")(nil, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	switch {
 	case fc.response.Error != nil:
 		return verror.New(verror.ErrBadProtocol, fc.ctx, fc.response.Error)
@@ -930,7 +931,7 @@
 }
 
 func (fc *flowClient) CloseSend() error {
-	defer vlog.LogCall()() // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCall(nil)(nil) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	return fc.closeSend()
 }
 
@@ -962,7 +963,7 @@
 }
 
 func (fc *flowClient) Finish(resultptrs ...interface{}) error {
-	defer vlog.LogCallf("resultptrs...=%v", resultptrs)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(nil, "resultptrs...=%v", resultptrs)(nil, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	err := fc.finish(resultptrs...)
 	vtrace.GetSpan(fc.ctx).Finish()
 	return err
@@ -1039,7 +1040,7 @@
 }
 
 func (fc *flowClient) RemoteBlessings() ([]string, security.Blessings) {
-	defer vlog.LogCall()() // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCall(nil)(nil) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	return fc.server, fc.flow.RemoteBlessings()
 }
 
diff --git a/runtime/internal/rpc/discharges.go b/runtime/internal/rpc/discharges.go
index c67c530..46017ee 100644
--- a/runtime/internal/rpc/discharges.go
+++ b/runtime/internal/rpc/discharges.go
@@ -10,6 +10,7 @@
 	"sync"
 	"time"
 
+	"v.io/x/ref/lib/apilog"
 	"v.io/x/ref/runtime/internal/rpc/stream/vc"
 
 	"v.io/v23/context"
@@ -24,10 +25,10 @@
 type NoDischarges struct{}
 
 func (NoDischarges) RPCCallOpt() {
-	defer vlog.LogCall()() // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCall(nil)(nil) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 }
 func (NoDischarges) NSOpt() {
-	defer vlog.LogCall()() // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCall(nil)(nil) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 }
 
 // discharger implements vc.DischargeClient.
diff --git a/runtime/internal/rpc/full_test.go b/runtime/internal/rpc/full_test.go
index 1fd3ca8..f1c08a8 100644
--- a/runtime/internal/rpc/full_test.go
+++ b/runtime/internal/rpc/full_test.go
@@ -282,18 +282,23 @@
 }
 
 func verifyMount(t *testing.T, ctx *context.T, ns namespace.T, name string) []string {
-	me, err := ns.Resolve(ctx, name)
-	if err != nil {
-		t.Errorf("%s: %s not found in mounttable", loc(1), name)
-		return nil
+	for {
+		me, err := ns.Resolve(ctx, name)
+		if err == nil {
+			return me.Names()
+		}
+		time.Sleep(10 * time.Millisecond)
 	}
-	return me.Names()
 }
 
 func verifyMountMissing(t *testing.T, ctx *context.T, ns namespace.T, name string) {
-	if me, err := ns.Resolve(ctx, name); err == nil {
-		names := me.Names()
-		t.Errorf("%s: %s not supposed to be found in mounttable; got %d servers instead: %v (%+v)", loc(1), name, len(names), names, me)
+	for {
+		if _, err := ns.Resolve(ctx, name); err != nil {
+			// Assume that any error (since we're using a mock namespace) means
+			// that the name is no longer present.
+			return
+		}
+		time.Sleep(10 * time.Millisecond)
 	}
 }
 
diff --git a/runtime/internal/rpc/options.go b/runtime/internal/rpc/options.go
index 16004aa..d046e32 100644
--- a/runtime/internal/rpc/options.go
+++ b/runtime/internal/rpc/options.go
@@ -11,7 +11,7 @@
 	"v.io/v23/options"
 	"v.io/v23/rpc"
 
-	"v.io/x/lib/vlog"
+	"v.io/x/ref/lib/apilog"
 	"v.io/x/ref/runtime/internal/rpc/stream"
 )
 
@@ -21,7 +21,7 @@
 type PreferredProtocols []string
 
 func (PreferredProtocols) RPCClientOpt() {
-	defer vlog.LogCall()() // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCall(nil)(nil) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 }
 
 // This option is used to sort and filter the endpoints when resolving the
@@ -29,7 +29,7 @@
 type PreferredServerResolveProtocols []string
 
 func (PreferredServerResolveProtocols) RPCServerOpt() {
-	defer vlog.LogCall()() // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCall(nil)(nil) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 }
 
 // ReservedNameDispatcher specifies the dispatcher that controls access
@@ -39,7 +39,7 @@
 }
 
 func (ReservedNameDispatcher) RPCServerOpt() {
-	defer vlog.LogCall()() // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCall(nil)(nil) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 }
 
 func getRetryTimeoutOpt(opts []rpc.CallOpt) (time.Duration, bool) {
diff --git a/runtime/internal/rpc/reserved.go b/runtime/internal/rpc/reserved.go
index 03a63fc..57b582f 100644
--- a/runtime/internal/rpc/reserved.go
+++ b/runtime/internal/rpc/reserved.go
@@ -18,6 +18,7 @@
 	"v.io/v23/verror"
 	"v.io/x/lib/vlog"
 
+	"v.io/x/ref/lib/apilog"
 	"v.io/x/ref/lib/glob"
 )
 
@@ -44,7 +45,7 @@
 }
 
 func (r *reservedMethods) Describe__() []rpc.InterfaceDesc {
-	defer vlog.LogCall()() // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCall(nil)(nil) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	return []rpc.InterfaceDesc{{
 		Name: "__Reserved",
 		Doc:  `Reserved methods implemented by the RPC framework.  Each method name is prefixed with a double underscore "__".`,
@@ -356,11 +357,11 @@
 }
 
 func (c *derivedServerCall) Suffix() string {
-	defer vlog.LogCall()() // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCall(nil)(nil) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	return c.suffix
 }
 func (c *derivedServerCall) Security() security.Call {
-	defer vlog.LogCall()() // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCall(nil)(nil) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	return c.security
 }
 
@@ -379,10 +380,10 @@
 }
 
 func (c *derivedSecurityCall) Suffix() string {
-	defer vlog.LogCall()() // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCall(nil)(nil) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	return c.suffix
 }
 func (c *derivedSecurityCall) MethodTags() []*vdl.Value {
-	defer vlog.LogCall()() // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCall(nil)(nil) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	return c.methodTags
 }
diff --git a/runtime/internal/rpc/resolve_test.go b/runtime/internal/rpc/resolve_test.go
index 73ace16..08b400a 100644
--- a/runtime/internal/rpc/resolve_test.go
+++ b/runtime/internal/rpc/resolve_test.go
@@ -7,7 +7,6 @@
 import (
 	"flag"
 	"fmt"
-	"io"
 	"os"
 	"testing"
 	"time"
@@ -66,7 +65,7 @@
 	fake.InjectRuntime(runtime, ctx, shutdown)
 }
 
-func rootMountTable(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+var rootMT = modules.Register(func(env *modules.Env, args ...string) error {
 	setupRuntime()
 	ctx, shutdown := v23.Init()
 	defer shutdown()
@@ -88,16 +87,16 @@
 	if err := server.ServeDispatcher(mp, mt); err != nil {
 		return fmt.Errorf("root failed: %s", err)
 	}
-	fmt.Fprintf(stdout, "PID=%d\n", os.Getpid())
+	fmt.Fprintf(env.Stdout, "PID=%d\n", os.Getpid())
 	for _, ep := range eps {
-		fmt.Fprintf(stdout, "MT_NAME=%s\n", ep.Name())
+		fmt.Fprintf(env.Stdout, "MT_NAME=%s\n", ep.Name())
 	}
-	modules.WaitForEOF(stdin)
+	modules.WaitForEOF(env.Stdin)
 	return nil
-}
+}, "rootMT")
 
 func startMT(t *testing.T, sh *modules.Shell) string {
-	h, err := sh.Start("rootMountTable", nil)
+	h, err := sh.Start(nil, rootMT)
 	if err != nil {
 		t.Fatalf("unexpected error for root mt: %s", err)
 	}
diff --git a/runtime/internal/rpc/server.go b/runtime/internal/rpc/server.go
index 3505ef5..e5fbf51 100644
--- a/runtime/internal/rpc/server.go
+++ b/runtime/internal/rpc/server.go
@@ -31,6 +31,7 @@
 	"v.io/v23/vom"
 	"v.io/v23/vtrace"
 
+	"v.io/x/ref/lib/apilog"
 	"v.io/x/ref/lib/stats"
 	"v.io/x/ref/runtime/internal/lib/publisher"
 	inaming "v.io/x/ref/runtime/internal/naming"
@@ -253,9 +254,8 @@
 }
 
 func (s *server) Status() rpc.ServerStatus {
-	defer vlog.LogCall()() // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCall(nil)(nil) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	status := rpc.ServerStatus{}
-	defer vlog.LogCall()()
 	s.Lock()
 	defer s.Unlock()
 	status.State = externalStates[s.state]
@@ -281,7 +281,7 @@
 }
 
 func (s *server) WatchNetwork(ch chan<- rpc.NetworkChange) {
-	defer vlog.LogCallf("ch=")("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(nil, "ch=")(nil, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	s.Lock()
 	defer s.Unlock()
 	if s.dhcpState != nil {
@@ -290,7 +290,7 @@
 }
 
 func (s *server) UnwatchNetwork(ch chan<- rpc.NetworkChange) {
-	defer vlog.LogCallf("ch=")("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(nil, "ch=")(nil, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	s.Lock()
 	defer s.Unlock()
 	if s.dhcpState != nil {
@@ -364,7 +364,7 @@
 }
 
 func (s *server) Listen(listenSpec rpc.ListenSpec) ([]naming.Endpoint, error) {
-	defer vlog.LogCallf("listenSpec=")("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(nil, "listenSpec=")(nil, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	useProxy := len(listenSpec.Proxy) > 0
 	if !useProxy && len(listenSpec.Addrs) == 0 {
 		return nil, verror.New(verror.ErrBadArg, s.ctx, "ListenSpec contains no proxy or addresses to listen on")
@@ -744,7 +744,7 @@
 }
 
 func (d leafDispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) {
-	defer vlog.LogCallf("suffix=%.10s...", suffix)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(nil, "suffix=%.10s...", suffix)(nil, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	if suffix != "" {
 		return nil, nil, verror.New(verror.ErrUnknownSuffix, nil, suffix)
 	}
@@ -752,7 +752,7 @@
 }
 
 func (s *server) Serve(name string, obj interface{}, authorizer security.Authorizer) error {
-	defer vlog.LogCallf("name=%.10s...,obj=,authorizer=", name)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(nil, "name=%.10s...,obj=,authorizer=", name)(nil, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	if obj == nil {
 		return verror.New(verror.ErrBadArg, s.ctx, "nil object")
 	}
@@ -776,7 +776,7 @@
 }
 
 func (s *server) ServeDispatcher(name string, disp rpc.Dispatcher) error {
-	defer vlog.LogCallf("name=%.10s...,disp=", name)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(nil, "name=%.10s...,disp=", name)(nil, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	if disp == nil {
 		return verror.New(verror.ErrBadArg, s.ctx, "nil dispatcher")
 	}
@@ -799,7 +799,7 @@
 }
 
 func (s *server) AddName(name string) error {
-	defer vlog.LogCallf("name=%.10s...", name)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(nil, "name=%.10s...", name)(nil, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	if len(name) == 0 {
 		return verror.New(verror.ErrBadArg, s.ctx, "name is empty")
 	}
@@ -814,7 +814,7 @@
 }
 
 func (s *server) RemoveName(name string) {
-	defer vlog.LogCallf("name=%.10s...", name)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(nil, "name=%.10s...", name)(nil, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	s.Lock()
 	defer s.Unlock()
 	if err := s.allowed(publishing, "RemoveName"); err != nil {
@@ -825,9 +825,8 @@
 }
 
 func (s *server) Stop() error {
-	defer vlog.LogCall()() // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCall(nil)(nil) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	serverDebug := fmt.Sprintf("Dispatcher: %T, Status:[%v]", s.disp, s.Status())
-	defer vlog.LogCall()()
 	vlog.VI(1).Infof("Stop: %s", serverDebug)
 	defer vlog.VI(1).Infof("Stop done: %s", serverDebug)
 	s.Lock()
@@ -1272,7 +1271,7 @@
 
 // Send implements the rpc.Stream method.
 func (fs *flowServer) Send(item interface{}) error {
-	defer vlog.LogCallf("item=")("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(nil, "item=")(nil, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	// The empty response header indicates what follows is a streaming result.
 	if err := fs.enc.Encode(rpc.Response{}); err != nil {
 		return err
@@ -1282,7 +1281,7 @@
 
 // Recv implements the rpc.Stream method.
 func (fs *flowServer) Recv(itemptr interface{}) error {
-	defer vlog.LogCallf("itemptr=")("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(nil, "itemptr=")(nil, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	var req rpc.Request
 	if err := fs.dec.Decode(&req); err != nil {
 		return err
diff --git a/runtime/internal/rpc/server_authorizer.go b/runtime/internal/rpc/server_authorizer.go
index cda735d..86874ce 100644
--- a/runtime/internal/rpc/server_authorizer.go
+++ b/runtime/internal/rpc/server_authorizer.go
@@ -12,7 +12,8 @@
 	"v.io/v23/rpc"
 	"v.io/v23/security"
 	"v.io/v23/verror"
-	"v.io/x/lib/vlog"
+
+	"v.io/x/ref/lib/apilog"
 )
 
 // TODO(ribrdb): Flip this to true once everything is updated and also update
@@ -67,7 +68,7 @@
 }
 
 func (a *serverAuthorizer) Authorize(ctx *context.T, call security.Call) error {
-	defer vlog.LogCallf("ctx=,call=")("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(ctx, "call=")(ctx, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	if call.RemoteBlessings().IsZero() {
 		return verror.New(errNoBlessingsFromServer, ctx)
 	}
diff --git a/runtime/internal/rpc/stream/errors.go b/runtime/internal/rpc/stream/errors.go
index 7ba12b2..2a676d4 100644
--- a/runtime/internal/rpc/stream/errors.go
+++ b/runtime/internal/rpc/stream/errors.go
@@ -8,7 +8,8 @@
 	"net"
 
 	"v.io/v23/verror"
-	"v.io/x/lib/vlog"
+
+	"v.io/x/ref/lib/apilog"
 )
 
 const pkgPath = "v.io/x/ref/runtime/internal/rpc/stream"
@@ -52,7 +53,7 @@
 
 func (t NetError) Err() error { return t.err }
 func (t NetError) Error() string {
-	defer vlog.LogCall()() // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCall(nil)(nil) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	return t.err.Error()
 }
 func (t NetError) Timeout() bool   { return t.timeout }
diff --git a/runtime/internal/rpc/stream/manager/error_test.go b/runtime/internal/rpc/stream/manager/error_test.go
index 4c61f49..a2fbf01 100644
--- a/runtime/internal/rpc/stream/manager/error_test.go
+++ b/runtime/internal/rpc/stream/manager/error_test.go
@@ -94,7 +94,7 @@
 }
 
 func TestDialErrors(t *testing.T) {
-	_, shutdown := test.InitForTest()
+	_, shutdown := test.V23Init()
 	defer shutdown()
 	server := manager.InternalNew(naming.FixedRoutingID(0x55555555))
 	client := manager.InternalNew(naming.FixedRoutingID(0xcccccccc))
diff --git a/runtime/internal/rpc/stream/manager/manager.go b/runtime/internal/rpc/stream/manager/manager.go
index a297962..bfa3deb 100644
--- a/runtime/internal/rpc/stream/manager/manager.go
+++ b/runtime/internal/rpc/stream/manager/manager.go
@@ -18,6 +18,7 @@
 	"v.io/v23/verror"
 	"v.io/x/lib/vlog"
 
+	"v.io/x/ref/lib/apilog"
 	"v.io/x/ref/lib/stats"
 	"v.io/x/ref/lib/stats/counter"
 	inaming "v.io/x/ref/runtime/internal/naming"
@@ -84,7 +85,7 @@
 
 func (DialTimeout) RPCStreamVCOpt() {}
 func (DialTimeout) RPCClientOpt() {
-	defer vlog.LogCall()() // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCall(nil)(nil) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 }
 
 func dial(d rpc.DialerFunc, network, address string, timeout time.Duration) (net.Conn, error) {
diff --git a/runtime/internal/rpc/stream/manager/manager_test.go b/runtime/internal/rpc/stream/manager/manager_test.go
index 2c20fcb..b2a5cad 100644
--- a/runtime/internal/rpc/stream/manager/manager_test.go
+++ b/runtime/internal/rpc/stream/manager/manager_test.go
@@ -37,11 +37,6 @@
 	"v.io/x/ref/test/testutil"
 )
 
-func init() {
-	modules.RegisterChild("runServer", "", runServer)
-	modules.RegisterChild("runRLimitedServer", "", runRLimitedServer)
-}
-
 // We write our own TestMain here instead of relying on v23 test generate because
 // we need to set runtime.GOMAXPROCS.
 func TestMain(m *testing.M) {
@@ -51,13 +46,7 @@
 	// condition that occurs when closing the server; also, using 1 cpu
 	// introduces less variance in the behavior of the test.
 	runtime.GOMAXPROCS(1)
-	if modules.IsModulesChildProcess() {
-		if err := modules.Dispatch(); err != nil {
-			fmt.Fprintf(os.Stderr, "modules.Dispatch failed: %v\n", err)
-			os.Exit(1)
-		}
-		return
-	}
+	modules.DispatchAndExitIfChild()
 	os.Exit(m.Run())
 }
 
@@ -676,7 +665,7 @@
 		t.Fatalf("unexpected error: %s", err)
 	}
 	defer sh.Cleanup(nil, nil)
-	h, err := sh.Start("runServer", nil, protocol, "127.0.0.1:0")
+	h, err := sh.Start(nil, runServer, protocol, "127.0.0.1:0")
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -697,7 +686,7 @@
 		t.Fatal("Expected client.Dial to fail since server is dead")
 	}
 
-	h, err = sh.Start("runServer", nil, protocol, ep.Addr().String())
+	h, err = sh.Start(nil, runServer, protocol, ep.Addr().String())
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -714,34 +703,36 @@
 	}
 }
 
-func runServer(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+var runServer = modules.Register(runServerFunc, "runServer")
+
+func runServerFunc(env *modules.Env, args ...string) error {
 	server := InternalNew(naming.FixedRoutingID(0x55555555))
 	principal := testutil.NewPrincipal("test")
 	_, ep, err := server.Listen(args[0], args[1], principal, principal.BlessingStore().Default())
 	if err != nil {
-		fmt.Fprintln(stderr, err)
+		fmt.Fprintln(env.Stderr, err)
 		return err
 	}
-	fmt.Fprintf(stdout, "ENDPOINT=%v\n", ep)
+	fmt.Fprintf(env.Stdout, "ENDPOINT=%v\n", ep)
 	// Live forever (till the process is explicitly killed)
-	modules.WaitForEOF(stdin)
+	modules.WaitForEOF(env.Stdin)
 	return nil
 }
 
-func runRLimitedServer(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+var runRLimitedServer = modules.Register(func(env *modules.Env, args ...string) error {
 	var rlimit syscall.Rlimit
 	if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rlimit); err != nil {
-		fmt.Fprintln(stderr, err)
+		fmt.Fprintln(env.Stderr, err)
 		return err
 	}
 	rlimit.Cur = 9
 	if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rlimit); err != nil {
-		fmt.Fprintln(stderr, err)
+		fmt.Fprintln(env.Stderr, err)
 		return err
 	}
-	fmt.Fprintf(stdout, "RLIMIT_NOFILE=%d\n", rlimit.Cur)
-	return runServer(stdin, stdout, stderr, env, args...)
-}
+	fmt.Fprintf(env.Stdout, "RLIMIT_NOFILE=%d\n", rlimit.Cur)
+	return runServerFunc(env, args...)
+}, "runRLimitedServer")
 
 func readLine(f stream.Flow) (string, error) {
 	var result bytes.Buffer
@@ -873,7 +864,7 @@
 		t.Fatal(err)
 	}
 	defer sh.Cleanup(nil, nil)
-	h, err := sh.Start("runRLimitedServer", nil, "--logtostderr=true", "tcp", "127.0.0.1:0")
+	h, err := sh.Start(nil, runRLimitedServer, "--logtostderr=true", "tcp", "127.0.0.1:0")
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/runtime/internal/rpc/stream/proxy/proxy_test.go b/runtime/internal/rpc/stream/proxy/proxy_test.go
index a438489..f583e9f 100644
--- a/runtime/internal/rpc/stream/proxy/proxy_test.go
+++ b/runtime/internal/rpc/stream/proxy/proxy_test.go
@@ -482,7 +482,7 @@
 }
 
 func v23Init() (*context.T, func()) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	ctx, err := v23.WithPrincipal(ctx, testutil.NewPrincipal("proxy"))
 	if err != nil {
 		panic(err)
diff --git a/runtime/internal/rpc/stream/proxy/v23_internal_test.go b/runtime/internal/rpc/stream/proxy/v23_internal_test.go
index 84bea54..73903ec 100644
--- a/runtime/internal/rpc/stream/proxy/v23_internal_test.go
+++ b/runtime/internal/rpc/stream/proxy/v23_internal_test.go
@@ -4,12 +4,15 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package proxy
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
+	"v.io/x/ref/test"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
diff --git a/runtime/internal/rpc/stream/vc/v23_internal_test.go b/runtime/internal/rpc/stream/vc/v23_internal_test.go
index 945d8c4..a48d6f6 100644
--- a/runtime/internal/rpc/stream/vc/v23_internal_test.go
+++ b/runtime/internal/rpc/stream/vc/v23_internal_test.go
@@ -4,12 +4,15 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package vc
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
+	"v.io/x/ref/test"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
diff --git a/runtime/internal/rpc/stream/vc/vc.go b/runtime/internal/rpc/stream/vc/vc.go
index 1e6e7d2..12c2527 100644
--- a/runtime/internal/rpc/stream/vc/vc.go
+++ b/runtime/internal/rpc/stream/vc/vc.go
@@ -24,6 +24,7 @@
 	"v.io/v23/vom"
 
 	"v.io/x/lib/vlog"
+	"v.io/x/ref/lib/apilog"
 	"v.io/x/ref/runtime/internal/lib/bqueue"
 	"v.io/x/ref/runtime/internal/lib/iobuf"
 	vsync "v.io/x/ref/runtime/internal/lib/sync"
@@ -73,7 +74,7 @@
 
 func (DischargeExpiryBuffer) RPCStreamListenerOpt() {}
 func (DischargeExpiryBuffer) RPCServerOpt() {
-	defer vlog.LogCall()() // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCall(nil)(nil) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 }
 
 const DefaultServerDischargeExpiryBuffer = 20 * time.Second
diff --git a/runtime/internal/rpc/stream/vif/v23_internal_test.go b/runtime/internal/rpc/stream/vif/v23_internal_test.go
index 161553c..70e47de 100644
--- a/runtime/internal/rpc/stream/vif/v23_internal_test.go
+++ b/runtime/internal/rpc/stream/vif/v23_internal_test.go
@@ -4,12 +4,15 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package vif
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
+	"v.io/x/ref/test"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
diff --git a/runtime/internal/rpc/test/client_test.go b/runtime/internal/rpc/test/client_test.go
index a1e9013..a42a2a9 100644
--- a/runtime/internal/rpc/test/client_test.go
+++ b/runtime/internal/rpc/test/client_test.go
@@ -6,7 +6,6 @@
 
 import (
 	"fmt"
-	"io"
 	"net"
 	"os"
 	"path/filepath"
@@ -38,15 +37,15 @@
 
 //go:generate v23 test generate .
 
-func rootMT(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+var rootMT = modules.Register(func(env *modules.Env, args ...string) error {
 	seclevel := options.SecurityConfidential
 	if len(args) == 1 && args[0] == "nosec" {
 		seclevel = options.SecurityNone
 	}
-	return runRootMT(stdin, stdout, stderr, seclevel, env, args...)
-}
+	return runRootMT(seclevel, env, args...)
+}, "rootMT")
 
-func runRootMT(stdin io.Reader, stdout, stderr io.Writer, seclevel options.SecurityLevel, env map[string]string, args ...string) error {
+func runRootMT(seclevel options.SecurityLevel, env *modules.Env, args ...string) error {
 	ctx, shutdown := v23.Init()
 	defer shutdown()
 
@@ -66,11 +65,11 @@
 	if err := server.ServeDispatcher("", mt); err != nil {
 		return fmt.Errorf("root failed: %s", err)
 	}
-	fmt.Fprintf(stdout, "PID=%d\n", os.Getpid())
+	fmt.Fprintf(env.Stdout, "PID=%d\n", os.Getpid())
 	for _, ep := range eps {
-		fmt.Fprintf(stdout, "MT_NAME=%s\n", ep.Name())
+		fmt.Fprintf(env.Stdout, "MT_NAME=%s\n", ep.Name())
 	}
-	modules.WaitForEOF(stdin)
+	modules.WaitForEOF(env.Stdin)
 	return nil
 }
 
@@ -100,7 +99,7 @@
 	return nil
 }
 
-func echoServer(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+var echoServer = modules.Register(func(env *modules.Env, args ...string) error {
 	ctx, shutdown := v23.Init()
 	defer shutdown()
 
@@ -118,15 +117,15 @@
 	if err := server.ServeDispatcher(mp, disp); err != nil {
 		return err
 	}
-	fmt.Fprintf(stdout, "PID=%d\n", os.Getpid())
+	fmt.Fprintf(env.Stdout, "PID=%d\n", os.Getpid())
 	for _, ep := range eps {
-		fmt.Fprintf(stdout, "NAME=%s\n", ep.Name())
+		fmt.Fprintf(env.Stdout, "NAME=%s\n", ep.Name())
 	}
-	modules.WaitForEOF(stdin)
+	modules.WaitForEOF(env.Stdin)
 	return nil
-}
+}, "echoServer")
 
-func echoClient(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+var echoClient = modules.Register(func(env *modules.Env, args ...string) error {
 	ctx, shutdown := v23.Init()
 	defer shutdown()
 
@@ -138,13 +137,13 @@
 		if err := client.Call(ctx, name, "Echo", []interface{}{a}, []interface{}{&r}); err != nil {
 			return err
 		}
-		fmt.Fprintf(stdout, r)
+		fmt.Fprintf(env.Stdout, r)
 	}
 	return nil
-}
+}, "echoClient")
 
 func newCtx() (*context.T, v23.Shutdown) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	v23.GetNamespace(ctx).CacheCtl(naming.DisableCache(true))
 	return ctx, shutdown
 }
@@ -154,7 +153,7 @@
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
-	root, err := sh.Start("rootMT", nil, args...)
+	root, err := sh.Start(nil, rootMT, args...)
 	if err != nil {
 		t.Fatalf("unexpected error for root mt: %s", err)
 	}
@@ -174,7 +173,7 @@
 }
 
 func runClient(t *testing.T, sh *modules.Shell) error {
-	clt, err := sh.Start("echoClient", nil, "echoServer", "a message")
+	clt, err := sh.Start(nil, echoClient, "echoServer", "a message")
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -186,12 +185,14 @@
 	return nil
 }
 
-func numServers(t *testing.T, ctx *context.T, name string) int {
-	me, err := v23.GetNamespace(ctx).Resolve(ctx, name)
-	if err != nil {
-		return 0
+func numServers(t *testing.T, ctx *context.T, name string, expected int) int {
+	for {
+		me, err := v23.GetNamespace(ctx).Resolve(ctx, name)
+		if err == nil && len(me.Servers) == expected {
+			return expected
+		}
+		time.Sleep(10 * time.Millisecond)
 	}
-	return len(me.Servers)
 }
 
 // TODO(cnicolaou): figure out how to test and see what the internals
@@ -202,7 +203,7 @@
 
 	sh, fn := runMountTable(t, ctx)
 	defer fn()
-	srv, err := sh.Start("echoServer", nil, "echoServer", "echoServer")
+	srv, err := sh.Start(nil, echoServer, "echoServer", "echoServer")
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -211,7 +212,7 @@
 	s.ExpectVar("NAME")
 
 	// Verify that there are 1 entries for echoServer in the mount table.
-	if got, want := numServers(t, ctx, "echoServer"), 1; got != want {
+	if got, want := numServers(t, ctx, "echoServer", 1), 1; got != want {
 		t.Fatalf("got: %d, want: %d", got, want)
 	}
 
@@ -228,7 +229,7 @@
 	}
 
 	// Verify that there are 101 entries for echoServer in the mount table.
-	if got, want := numServers(t, ctx, "echoServer"), 101; got != want {
+	if got, want := numServers(t, ctx, "echoServer", 101), 101; got != want {
 		t.Fatalf("got: %q, want: %q", got, want)
 	}
 
@@ -243,7 +244,7 @@
 	srv.Shutdown(nil, nil)
 
 	// Verify that there are 100 entries for echoServer in the mount table.
-	if got, want := numServers(t, ctx, "echoServer"), 100; got != want {
+	if got, want := numServers(t, ctx, "echoServer", 100), 100; got != want {
 		t.Fatalf("got: %d, want: %d", got, want)
 	}
 }
@@ -508,8 +509,8 @@
 	logErr("client does not trust server", err)
 }
 
-func childPing(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
-	ctx, shutdown := test.InitForTest()
+var childPing = modules.Register(func(env *modules.Env, args ...string) error {
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 	v23.GetNamespace(ctx).CacheCtl(naming.DisableCache(true))
 
@@ -518,9 +519,9 @@
 	if err := v23.GetClient(ctx).Call(ctx, name, "Ping", nil, []interface{}{&got}); err != nil {
 		fmt.Errorf("unexpected error: %s", err)
 	}
-	fmt.Fprintf(stdout, "RESULT=%s\n", got)
+	fmt.Fprintf(env.Stdout, "RESULT=%s\n", got)
 	return nil
-}
+}, "childPing")
 
 func initServer(t *testing.T, ctx *context.T, opts ...rpc.ServerOpt) (string, func()) {
 	server, err := v23.NewServer(ctx, opts...)
@@ -578,7 +579,7 @@
 }
 
 func TestAccessDenied(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	name, fn := initServer(t, ctx)
@@ -654,7 +655,7 @@
 	// backoff of some minutes.
 	startServer := func() {
 		time.Sleep(100 * time.Millisecond)
-		srv, _ := sh.Start("echoServer", nil, "message", name)
+		srv, _ := sh.Start(nil, echoServer, "message", name)
 		s := expect.NewSession(t, srv.Stdout(), time.Minute)
 		s.ExpectVar("PID")
 		s.ExpectVar("NAME")
@@ -686,7 +687,7 @@
 	name, fn := initServer(t, ctx)
 	defer fn()
 
-	srv, err := sh.Start("childPing", nil, name)
+	srv, err := sh.Start(nil, childPing, name)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -806,7 +807,7 @@
 // endpoint).
 func TestReconnect(t *testing.T) {
 	t.Skip()
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	sh, err := modules.NewShell(ctx, v23.GetPrincipal(ctx), testing.Verbose(), t)
@@ -814,7 +815,7 @@
 		t.Fatalf("unexpected error: %s", err)
 	}
 	defer sh.Cleanup(os.Stderr, os.Stderr)
-	server, err := sh.Start("echoServer", nil, "--v23.tcp.address=127.0.0.1:0", "mymessage", "")
+	server, err := sh.Start(nil, echoServer, "--v23.tcp.address=127.0.0.1:0", "mymessage", "")
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -852,7 +853,7 @@
 	// Resurrect the server with the same address, verify client
 	// re-establishes the connection. This is racy if another
 	// process grabs the port.
-	server, err = sh.Start("echoServer", nil, "--v23.tcp.address="+ep.Address, "mymessage again", "")
+	server, err = sh.Start(nil, echoServer, "--v23.tcp.address="+ep.Address, "mymessage again", "")
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
diff --git a/runtime/internal/rpc/test/glob_test.go b/runtime/internal/rpc/test/glob_test.go
index 7e5bd43..471b6f2 100644
--- a/runtime/internal/rpc/test/glob_test.go
+++ b/runtime/internal/rpc/test/glob_test.go
@@ -42,7 +42,7 @@
 }
 
 func TestGlob(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	namespace := []string{
@@ -203,7 +203,7 @@
 }
 
 func TestGlobDeny(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	tree := newNode()
diff --git a/runtime/internal/rpc/test/proxy_test.go b/runtime/internal/rpc/test/proxy_test.go
index 05e1c36..9a6c7b8 100644
--- a/runtime/internal/rpc/test/proxy_test.go
+++ b/runtime/internal/rpc/test/proxy_test.go
@@ -6,7 +6,6 @@
 
 import (
 	"fmt"
-	"io"
 	"os"
 	"reflect"
 	"sort"
@@ -49,7 +48,7 @@
 	return ctx, shutdown
 }
 
-func proxyServer(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+var proxyServer = modules.Register(func(env *modules.Env, args ...string) error {
 	ctx, shutdown := v23.Init()
 	defer shutdown()
 
@@ -58,11 +57,11 @@
 	listenSpec := rpc.ListenSpec{Addrs: rpc.ListenAddrs{{"tcp", "127.0.0.1:0"}}}
 	proxyShutdown, proxyEp, err := proxy.New(ctx, listenSpec, security.AllowEveryone())
 	if err != nil {
-		fmt.Fprintf(stderr, "%s\n", verror.DebugString(err))
+		fmt.Fprintf(env.Stderr, "%s\n", verror.DebugString(err))
 		return err
 	}
 	defer proxyShutdown()
-	fmt.Fprintf(stdout, "PID=%d\n", os.Getpid())
+	fmt.Fprintf(env.Stdout, "PID=%d\n", os.Getpid())
 	if expected > 0 {
 		pub := publisher.New(ctx, v23.GetNamespace(ctx), time.Minute)
 		defer pub.WaitForStop()
@@ -84,11 +83,11 @@
 			time.Sleep(delay)
 		}
 	}
-	fmt.Fprintf(stdout, "PROXY_NAME=%s\n", proxyEp.Name())
-	modules.WaitForEOF(stdin)
-	fmt.Fprintf(stdout, "DONE\n")
+	fmt.Fprintf(env.Stdout, "PROXY_NAME=%s\n", proxyEp.Name())
+	modules.WaitForEOF(env.Stdin)
+	fmt.Fprintf(env.Stdout, "DONE\n")
 	return nil
-}
+}, "")
 
 type testServer struct{}
 
@@ -121,7 +120,7 @@
 		t.Fatalf("unexpected error: %s", err)
 	}
 	h.sh = sh
-	p, err := sh.Start("proxyServer", nil, args...)
+	p, err := sh.Start(nil, proxyServer, args...)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
diff --git a/runtime/internal/rpc/test/signature_test.go b/runtime/internal/rpc/test/signature_test.go
index 480bd77..f6197c0 100644
--- a/runtime/internal/rpc/test/signature_test.go
+++ b/runtime/internal/rpc/test/signature_test.go
@@ -60,7 +60,7 @@
 }
 
 func TestMethodSignature(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 	ep, stop, err := startSigServer(ctx, sigImpl{})
 	if err != nil {
@@ -106,7 +106,7 @@
 }
 
 func TestSignature(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 	ep, stop, err := startSigServer(ctx, sigImpl{})
 	if err != nil {
diff --git a/runtime/internal/rpc/test/v23_internal_test.go b/runtime/internal/rpc/test/v23_internal_test.go
index 0862cd2..d7274da 100644
--- a/runtime/internal/rpc/test/v23_internal_test.go
+++ b/runtime/internal/rpc/test/v23_internal_test.go
@@ -4,31 +4,19 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package test
 
-import "fmt"
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
-import "v.io/x/ref/test/modules"
-
-func init() {
-	modules.RegisterChild("rootMT", ``, rootMT)
-	modules.RegisterChild("echoServer", ``, echoServer)
-	modules.RegisterChild("echoClient", ``, echoClient)
-	modules.RegisterChild("childPing", ``, childPing)
-	modules.RegisterChild("proxyServer", ``, proxyServer)
-}
+	"v.io/x/ref/test"
+	"v.io/x/ref/test/modules"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
-	if modules.IsModulesChildProcess() {
-		if err := modules.Dispatch(); err != nil {
-			fmt.Fprintf(os.Stderr, "modules.Dispatch failed: %v\n", err)
-			os.Exit(1)
-		}
-		return
-	}
+	modules.DispatchAndExitIfChild()
 	os.Exit(m.Run())
 }
diff --git a/runtime/internal/rpc/testutil_test.go b/runtime/internal/rpc/testutil_test.go
index 4f298dc..9a2e8f3 100644
--- a/runtime/internal/rpc/testutil_test.go
+++ b/runtime/internal/rpc/testutil_test.go
@@ -79,7 +79,7 @@
 }
 
 func initForTest() (*context.T, v23.Shutdown) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	ctx, err := ivtrace.Init(ctx, flags.VtraceFlags{})
 	if err != nil {
 		panic(err)
diff --git a/runtime/internal/rpc/v23_test.go b/runtime/internal/rpc/v23_test.go
index b874fc4..fac4210 100644
--- a/runtime/internal/rpc/v23_test.go
+++ b/runtime/internal/rpc/v23_test.go
@@ -4,27 +4,19 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package rpc_test
 
-import "fmt"
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
-import "v.io/x/ref/test/modules"
-
-func init() {
-	modules.RegisterChild("rootMountTable", ``, rootMountTable)
-}
+	"v.io/x/ref/test"
+	"v.io/x/ref/test/modules"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
-	if modules.IsModulesChildProcess() {
-		if err := modules.Dispatch(); err != nil {
-			fmt.Fprintf(os.Stderr, "modules.Dispatch failed: %v\n", err)
-			os.Exit(1)
-		}
-		return
-	}
+	modules.DispatchAndExitIfChild()
 	os.Exit(m.Run())
 }
diff --git a/runtime/internal/rt/ipc_test.go b/runtime/internal/rt/ipc_test.go
index 0bbd0ef..f14549f 100644
--- a/runtime/internal/rt/ipc_test.go
+++ b/runtime/internal/rt/ipc_test.go
@@ -99,7 +99,7 @@
 }
 
 func TestClientServerBlessings(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	var (
@@ -179,7 +179,7 @@
 }
 
 func TestServerEndpointBlessingNames(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 	ctx, _ = v23.WithPrincipal(ctx, testutil.NewPrincipal("default"))
 
@@ -272,7 +272,7 @@
 }
 
 func TestServerDischarges(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	var (
diff --git a/runtime/internal/rt/mgmt_test.go b/runtime/internal/rt/mgmt_test.go
index 73860e5..0a0d49d 100644
--- a/runtime/internal/rt/mgmt_test.go
+++ b/runtime/internal/rt/mgmt_test.go
@@ -6,7 +6,6 @@
 
 import (
 	"fmt"
-	"io"
 	"os"
 	"reflect"
 	"strings"
@@ -30,16 +29,10 @@
 
 //go:generate v23 test generate
 
-const (
-	noWaitersCmd = "noWaiters"
-	forceStopCmd = "forceStop"
-	appCmd       = "app"
-)
-
 // TestBasic verifies that the basic plumbing works: LocalStop calls result in
 // stop messages being sent on the channel passed to WaitForStop.
 func TestBasic(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	m := v23.GetAppCycle(ctx)
@@ -61,7 +54,7 @@
 // TestMultipleWaiters verifies that the plumbing works with more than one
 // registered wait channel.
 func TestMultipleWaiters(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	m := v23.GetAppCycle(ctx)
@@ -84,7 +77,7 @@
 // channel is not being drained: once the channel's buffer fills up, future
 // Stops become no-ops.
 func TestMultipleStops(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	m := v23.GetAppCycle(ctx)
@@ -103,17 +96,17 @@
 	}
 }
 
-func noWaiters(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
-	ctx, shutdown := test.InitForTest()
+var noWaiters = modules.Register(func(env *modules.Env, args ...string) error {
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	m := v23.GetAppCycle(ctx)
-	fmt.Fprintf(stdout, "ready\n")
-	modules.WaitForEOF(stdin)
+	fmt.Fprintf(env.Stdout, "ready\n")
+	modules.WaitForEOF(env.Stdin)
 	m.Stop()
 	os.Exit(42) // This should not be reached.
 	return nil
-}
+}, "noWaiters")
 
 // TestNoWaiters verifies that the child process exits in the absence of any
 // wait channel being registered with its runtime.
@@ -123,7 +116,7 @@
 		t.Fatalf("unexpected error: %s", err)
 	}
 	defer sh.Cleanup(os.Stderr, os.Stderr)
-	h, err := sh.Start(noWaitersCmd, nil)
+	h, err := sh.Start(nil, noWaiters)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -134,18 +127,18 @@
 	}
 }
 
-func forceStop(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
-	ctx, shutdown := test.InitForTest()
+var forceStop = modules.Register(func(env *modules.Env, args ...string) error {
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	m := v23.GetAppCycle(ctx)
-	fmt.Fprintf(stdout, "ready\n")
-	modules.WaitForEOF(stdin)
+	fmt.Fprintf(env.Stdout, "ready\n")
+	modules.WaitForEOF(env.Stdin)
 	m.WaitForStop(make(chan string, 1))
 	m.ForceStop()
 	os.Exit(42) // This should not be reached.
 	return nil
-}
+}, "forceStop")
 
 // TestForceStop verifies that ForceStop causes the child process to exit
 // immediately.
@@ -155,7 +148,7 @@
 		t.Fatalf("unexpected error: %s", err)
 	}
 	defer sh.Cleanup(os.Stderr, os.Stderr)
-	h, err := sh.Start(forceStopCmd, nil)
+	h, err := sh.Start(nil, forceStop)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -184,7 +177,7 @@
 // TestProgress verifies that the ticker update/track logic works for a single
 // tracker.
 func TestProgress(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 
 	m := v23.GetAppCycle(ctx)
 	m.AdvanceGoal(50)
@@ -216,7 +209,7 @@
 // works for more than one tracker.  It also ensures that the runtime doesn't
 // block when the tracker channels are full.
 func TestProgressMultipleTrackers(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 
 	m := v23.GetAppCycle(ctx)
 	// ch1 is 1-buffered, ch2 is 2-buffered.
@@ -250,21 +243,21 @@
 	}
 }
 
-func app(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
-	ctx, shutdown := test.InitForTest()
+var app = modules.Register(func(env *modules.Env, args ...string) error {
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	m := v23.GetAppCycle(ctx)
 	ch := make(chan string, 1)
 	m.WaitForStop(ch)
-	fmt.Fprintf(stdout, "Got %s\n", <-ch)
+	fmt.Fprintf(env.Stdout, "Got %s\n", <-ch)
 	m.AdvanceGoal(10)
-	fmt.Fprintf(stdout, "Doing some work\n")
+	fmt.Fprintf(env.Stdout, "Doing some work\n")
 	m.AdvanceProgress(2)
-	fmt.Fprintf(stdout, "Doing some more work\n")
+	fmt.Fprintf(env.Stdout, "Doing some more work\n")
 	m.AdvanceProgress(5)
 	return nil
-}
+}, "app")
 
 type configServer struct {
 	ch chan<- string
@@ -296,7 +289,7 @@
 }
 
 func setupRemoteAppCycleMgr(t *testing.T) (*context.T, modules.Handle, appcycle.AppCycleClientMethods, func()) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 
 	configServer, configServiceName, ch := createConfigServer(t, ctx)
 	sh, err := modules.NewShell(ctx, v23.GetPrincipal(ctx), testing.Verbose(), t)
@@ -306,7 +299,7 @@
 	sh.SetConfigKey(mgmt.ParentNameConfigKey, configServiceName)
 	sh.SetConfigKey(mgmt.ProtocolConfigKey, "tcp")
 	sh.SetConfigKey(mgmt.AddressConfigKey, "127.0.0.1:0")
-	h, err := sh.Start("app", nil)
+	h, err := sh.Start(nil, app)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
diff --git a/runtime/internal/rt/rt_test.go b/runtime/internal/rt/rt_test.go
index 3e53548..b3b3e29 100644
--- a/runtime/internal/rt/rt_test.go
+++ b/runtime/internal/rt/rt_test.go
@@ -6,7 +6,6 @@
 
 import (
 	"fmt"
-	"io"
 	"io/ioutil"
 	"os"
 	"regexp"
@@ -53,17 +52,17 @@
 	}
 }
 
-func child(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
-	_, shutdown := test.InitForTest()
+var child = modules.Register(func(env *modules.Env, args ...string) error {
+	_, shutdown := test.V23Init()
 	defer shutdown()
 
 	logger := vlog.Log
 	vlog.Infof("%s\n", logger)
-	fmt.Fprintf(stdout, "%s\n", logger)
-	modules.WaitForEOF(stdin)
-	fmt.Fprintf(stdout, "done\n")
+	fmt.Fprintf(env.Stdout, "%s\n", logger)
+	modules.WaitForEOF(env.Stdin)
+	fmt.Fprintf(env.Stdout, "done\n")
 	return nil
-}
+}, "child")
 
 func TestInitArgs(t *testing.T) {
 	sh, err := modules.NewShell(nil, nil, testing.Verbose(), t)
@@ -71,7 +70,7 @@
 		t.Fatalf("unexpected error: %s", err)
 	}
 	defer sh.Cleanup(os.Stderr, os.Stderr)
-	h, err := sh.Start("child", nil, "--logtostderr=true", "--vmodule=*=3", "--", "foobar")
+	h, err := sh.Start(nil, child, "--logtostderr=true", "--vmodule=*=3", "--", "foobar")
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -121,40 +120,40 @@
 	return dir
 }
 
-func principal(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
-	ctx, shutdown := test.InitForTest()
+var principal = modules.Register(func(env *modules.Env, args ...string) error {
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	p := v23.GetPrincipal(ctx)
 	if err := validatePrincipal(p); err != nil {
 		return err
 	}
-	fmt.Fprintf(stdout, "DEFAULT_BLESSING=%s\n", defaultBlessing(p))
+	fmt.Fprintf(env.Stdout, "DEFAULT_BLESSING=%s\n", defaultBlessing(p))
 	return nil
-}
+}, "principal")
 
 // Runner runs a principal as a subprocess and reports back with its
 // own security info and it's childs.
-func runner(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
-	ctx, shutdown := test.InitForTest()
+var runner = modules.Register(func(env *modules.Env, args ...string) error {
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	p := v23.GetPrincipal(ctx)
 	if err := validatePrincipal(p); err != nil {
 		return err
 	}
-	fmt.Fprintf(stdout, "RUNNER_DEFAULT_BLESSING=%v\n", defaultBlessing(p))
+	fmt.Fprintf(env.Stdout, "RUNNER_DEFAULT_BLESSING=%v\n", defaultBlessing(p))
 	sh, err := modules.NewShell(ctx, p, false, nil)
 	if err != nil {
 		return err
 	}
-	if _, err := sh.Start("principal", nil, args...); err != nil {
+	if _, err := sh.Start(nil, principal, args...); err != nil {
 		return err
 	}
 	// Cleanup copies the output of sh to these Writers.
-	sh.Cleanup(stdout, stderr)
+	sh.Cleanup(env.Stdout, env.Stderr)
 	return nil
-}
+}, "runner")
 
 func createCredentialsInDir(t *testing.T, dir string, blessing string) {
 	principal, err := vsecurity.CreatePersistentPrincipal(dir, nil)
@@ -186,7 +185,7 @@
 	// directory supplied by the environment.
 	credEnv := []string{ref.EnvCredentials + "=" + cdir}
 
-	h, err := sh.Start("runner", credEnv)
+	h, err := sh.Start(credEnv, runner)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -209,7 +208,7 @@
 func TestPrincipalInit(t *testing.T) {
 	// Collect the process' public key and error status
 	collect := func(sh *modules.Shell, env []string, args ...string) string {
-		h, err := sh.Start("principal", env, args...)
+		h, err := sh.Start(env, principal, args...)
 		if err != nil {
 			t.Fatalf("unexpected error: %s", err)
 		}
@@ -234,7 +233,7 @@
 	}
 	defer sh.Cleanup(os.Stderr, os.Stderr)
 
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	agentSh, err := modules.NewShell(ctx, v23.GetPrincipal(ctx), testing.Verbose(), t)
diff --git a/runtime/internal/rt/runtime.go b/runtime/internal/rt/runtime.go
index 9ebfbef..1d006f4 100644
--- a/runtime/internal/rt/runtime.go
+++ b/runtime/internal/rt/runtime.go
@@ -27,6 +27,7 @@
 	"v.io/v23/vtrace"
 	"v.io/x/lib/metadata"
 	"v.io/x/lib/vlog"
+	"v.io/x/ref/lib/apilog"
 	"v.io/x/ref/lib/flags"
 	"v.io/x/ref/lib/stats"
 	_ "v.io/x/ref/lib/stats/sysstats"
@@ -94,7 +95,7 @@
 	}
 
 	err := vlog.ConfigureLibraryLoggerFromFlags()
-	if err != nil && err != vlog.Configured {
+	if err != nil && err != vlog.ErrConfigured {
 		return nil, nil, nil, err
 	}
 	// We want to print out metadata only into the log files, to avoid
@@ -179,7 +180,7 @@
 }
 
 func (r *Runtime) Init(ctx *context.T) error {
-	defer vlog.LogCallf("ctx=")("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCall(ctx)(ctx) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	return r.initMgmt(ctx)
 }
 
@@ -214,12 +215,12 @@
 }
 
 func (*Runtime) NewEndpoint(ep string) (naming.Endpoint, error) {
-	defer vlog.LogCallf("ep=%.10s...", ep)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(nil, "ep=%.10s...", ep)(nil, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	return inaming.NewEndpoint(ep)
 }
 
 func (r *Runtime) NewServer(ctx *context.T, opts ...rpc.ServerOpt) (rpc.Server, error) {
-	defer vlog.LogCallf("ctx=,opts...=%v", opts)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(ctx, "opts...=%v", opts)(ctx, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	// Create a new RoutingID (and StreamManager) for each server.
 	sm, err := newStreamManager()
 	if err != nil {
@@ -298,7 +299,7 @@
 }
 
 func (r *Runtime) WithNewStreamManager(ctx *context.T) (*context.T, error) {
-	defer vlog.LogCallf("ctx=")("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCall(ctx)(ctx) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	newctx, err := r.setNewStreamManager(ctx)
 	if err != nil {
 		return ctx, err
@@ -324,7 +325,7 @@
 }
 
 func (r *Runtime) WithPrincipal(ctx *context.T, principal security.Principal) (*context.T, error) {
-	defer vlog.LogCallf("ctx=,principal=")("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(ctx, "principal=")(ctx, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	var err error
 	newctx := ctx
 
@@ -350,13 +351,13 @@
 }
 
 func (*Runtime) GetPrincipal(ctx *context.T) security.Principal {
-	defer vlog.LogCallf("ctx=")("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCall(ctx)(ctx) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	p, _ := ctx.Value(principalKey).(security.Principal)
 	return p
 }
 
 func (r *Runtime) WithNewClient(ctx *context.T, opts ...rpc.ClientOpt) (*context.T, rpc.Client, error) {
-	defer vlog.LogCallf("ctx=,opts...=%v", opts)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(ctx, "opts...=%v", opts)(ctx, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	otherOpts := append([]rpc.ClientOpt{}, opts...)
 
 	p, _ := ctx.Value(principalKey).(security.Principal)
@@ -383,7 +384,7 @@
 }
 
 func (*Runtime) GetClient(ctx *context.T) rpc.Client {
-	defer vlog.LogCallf("ctx=")("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCall(ctx)(ctx) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	cl, _ := ctx.Value(clientKey).(rpc.Client)
 	return cl
 }
@@ -405,7 +406,7 @@
 }
 
 func (r *Runtime) WithNewNamespace(ctx *context.T, roots ...string) (*context.T, namespace.T, error) {
-	defer vlog.LogCallf("ctx=,roots...=%v", roots)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(ctx, "roots...=%v", roots)(ctx, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	newctx, ns, err := r.setNewNamespace(ctx, roots...)
 	if err != nil {
 		return ctx, nil, err
@@ -441,7 +442,7 @@
 }
 
 func (*Runtime) WithBackgroundContext(ctx *context.T) *context.T {
-	defer vlog.LogCallf("ctx=")("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCall(ctx)(ctx) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	// Note we add an extra context with a nil value here.
 	// This prevents users from travelling back through the
 	// chain of background contexts.
@@ -463,7 +464,7 @@
 }
 
 func (*Runtime) WithReservedNameDispatcher(ctx *context.T, d rpc.Dispatcher) *context.T {
-	defer vlog.LogCallf("ctx=,d=")("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(ctx, "d=")(ctx, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	return context.WithValue(ctx, reservedNameKey, d)
 }
 
diff --git a/runtime/internal/rt/shutdown_servers_test.go b/runtime/internal/rt/shutdown_servers_test.go
index 1a05535..38dd5e3 100644
--- a/runtime/internal/rt/shutdown_servers_test.go
+++ b/runtime/internal/rt/shutdown_servers_test.go
@@ -23,11 +23,6 @@
 	"v.io/x/ref/test/modules"
 )
 
-func init() {
-	modules.RegisterChild("simpleServerProgram", "", simpleServerProgram)
-	modules.RegisterChild("complexServerProgram", "", complexServerProgram)
-}
-
 type dummy struct{}
 
 func (*dummy) Echo(*context.T, rpc.ServerCall) error { return nil }
@@ -73,9 +68,9 @@
 // complex server application (with several servers, a mix of interruptible
 // and blocking cleanup, and parallel and sequential cleanup execution).
 // For a more typical server, see simpleServerProgram.
-func complexServerProgram(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+var complexServerProgram = modules.Register(func(env *modules.Env, args ...string) error {
 	// Initialize the runtime.  This is boilerplate.
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	// shutdown is optional, but it's a good idea to clean up, especially
 	// since it takes care of flushing the logs before exiting.
 	defer shutdown()
@@ -84,7 +79,7 @@
 	// commands from the parent process to simulate Stop and
 	// RemoteStop commands that would normally be issued from
 	// application code.
-	defer remoteCmdLoop(ctx, stdin)()
+	defer remoteCmdLoop(ctx, env.Stdin)()
 
 	// Create a couple servers, and start serving.
 	server1 := makeServer(ctx)
@@ -118,9 +113,9 @@
 		case sig := <-sigChan:
 			// If the developer wants to take different actions
 			// depending on the type of signal, they can do it here.
-			fmt.Fprintln(stdout, "Received signal", sig)
+			fmt.Fprintln(env.Stdout, "Received signal", sig)
 		case stop := <-stopChan:
-			fmt.Fprintln(stdout, "Stop", stop)
+			fmt.Fprintln(env.Stdout, "Stop", stop)
 		}
 		// This commences the cleanup stage.
 		done.Done()
@@ -138,7 +133,7 @@
 	// This communicates to the parent test driver process in our unit test
 	// that this server is ready and waiting on signals or stop commands.
 	// It's purely an artifact of our test setup.
-	fmt.Fprintln(stdout, "Ready")
+	fmt.Fprintln(env.Stdout, "Ready")
 
 	// Wait for shutdown.
 	done.Wait()
@@ -174,7 +169,7 @@
 	parallelCleanup.Add(1)
 	blocking.Add(1)
 	go func() {
-		fmt.Fprintln(stdout, "Parallel blocking cleanup1")
+		fmt.Fprintln(env.Stdout, "Parallel blocking cleanup1")
 		blocking.Done()
 		parallelCleanup.Done()
 	}()
@@ -182,42 +177,42 @@
 	parallelCleanup.Add(1)
 	blocking.Add(1)
 	go func() {
-		fmt.Fprintln(stdout, "Parallel blocking cleanup2")
+		fmt.Fprintln(env.Stdout, "Parallel blocking cleanup2")
 		blocking.Done()
 		parallelCleanup.Done()
 	}()
 
 	parallelCleanup.Add(1)
 	go func() {
-		fmt.Fprintln(stdout, "Parallel interruptible cleanup1")
+		fmt.Fprintln(env.Stdout, "Parallel interruptible cleanup1")
 		parallelCleanup.Done()
 	}()
 
 	parallelCleanup.Add(1)
 	go func() {
-		fmt.Fprintln(stdout, "Parallel interruptible cleanup2")
+		fmt.Fprintln(env.Stdout, "Parallel interruptible cleanup2")
 		parallelCleanup.Done()
 	}()
 
 	// Simulate two sequential cleanup steps, one blocking and one
 	// interruptible.
-	fmt.Fprintln(stdout, "Sequential blocking cleanup")
+	fmt.Fprintln(env.Stdout, "Sequential blocking cleanup")
 	blocking.Wait()
 	close(blockingCh)
 
-	fmt.Fprintln(stdout, "Sequential interruptible cleanup")
+	fmt.Fprintln(env.Stdout, "Sequential interruptible cleanup")
 
 	parallelCleanup.Wait()
 	return nil
-}
+}, "complexServerProgram")
 
 // simpleServerProgram demonstrates the recommended way to write a typical
 // simple server application (with one server and a clean shutdown triggered by
 // a signal or a stop command).  For an example of something more involved, see
 // complexServerProgram.
-func simpleServerProgram(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+var simpleServerProgram = modules.Register(func(env *modules.Env, args ...string) error {
 	// Initialize the runtime.  This is boilerplate.
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	// Calling shutdown is optional, but it's a good idea to clean up, especially
 	// since it takes care of flushing the logs before exiting.
 	//
@@ -230,7 +225,7 @@
 	// commands from the parent process to simulate Stop and
 	// RemoteStop commands that would normally be issued from
 	// application code.
-	defer remoteCmdLoop(ctx, stdin)()
+	defer remoteCmdLoop(ctx, env.Stdin)()
 
 	// Create a server, and start serving.
 	server := makeServer(ctx)
@@ -246,17 +241,17 @@
 	// This communicates to the parent test driver process in our unit test
 	// that this server is ready and waiting on signals or stop commands.
 	// It's purely an artifact of our test setup.
-	fmt.Fprintln(stdout, "Ready")
+	fmt.Fprintln(env.Stdout, "Ready")
 
 	// Use defer for anything that should still execute even if a panic
 	// occurs.
-	defer fmt.Fprintln(stdout, "Deferred cleanup")
+	defer fmt.Fprintln(env.Stdout, "Deferred cleanup")
 
 	// Wait for shutdown.
 	sig := <-waiter
 	// The developer could take different actions depending on the type of
 	// signal.
-	fmt.Fprintln(stdout, "Received signal", sig)
+	fmt.Fprintln(env.Stdout, "Received signal", sig)
 
 	// Cleanup code starts here.  Alternatively, these steps could be
 	// invoked through defer, but we list them here to make the order of
@@ -268,7 +263,7 @@
 	// Note, this will not execute in cases of forced shutdown
 	// (e.g. SIGSTOP), when the process calls os.Exit (e.g. via log.Fatal),
 	// or when a panic occurs.
-	fmt.Fprintln(stdout, "Interruptible cleanup")
+	fmt.Fprintln(env.Stdout, "Interruptible cleanup")
 
 	return nil
-}
+}, "simpleServerProgram")
diff --git a/runtime/internal/rt/shutdown_test.go b/runtime/internal/rt/shutdown_test.go
index 85798b3..efb7aff 100644
--- a/runtime/internal/rt/shutdown_test.go
+++ b/runtime/internal/rt/shutdown_test.go
@@ -39,7 +39,7 @@
 func TestSimpleServerSignal(t *testing.T) {
 	sh := newShell(t)
 	defer sh.Cleanup(os.Stdout, cstderr)
-	h, _ := sh.Start("simpleServerProgram", nil)
+	h, _ := sh.Start(nil, simpleServerProgram)
 	h.Expect("Ready")
 	syscall.Kill(h.Pid(), syscall.SIGINT)
 	h.Expect("Received signal interrupt")
@@ -54,7 +54,7 @@
 func TestSimpleServerLocalStop(t *testing.T) {
 	sh := newShell(t)
 	defer sh.Cleanup(os.Stdout, cstderr)
-	h, _ := sh.Start("simpleServerProgram", nil)
+	h, _ := sh.Start(nil, simpleServerProgram)
 	h.Expect("Ready")
 	fmt.Fprintln(h.Stdin(), "stop")
 	h.Expect(fmt.Sprintf("Received signal %s", v23.LocalStop))
@@ -70,7 +70,7 @@
 func TestSimpleServerDoubleSignal(t *testing.T) {
 	sh := newShell(t)
 	defer sh.Cleanup(os.Stdout, cstderr)
-	h, _ := sh.Start("simpleServerProgram", nil)
+	h, _ := sh.Start(nil, simpleServerProgram)
 	h.Expect("Ready")
 	syscall.Kill(h.Pid(), syscall.SIGINT)
 	h.Expect("Received signal interrupt")
@@ -89,7 +89,7 @@
 func TestSimpleServerLocalForceStop(t *testing.T) {
 	sh := newShell(t)
 	defer sh.Cleanup(os.Stdout, cstderr)
-	h, _ := sh.Start("simpleServerProgram", nil)
+	h, _ := sh.Start(nil, simpleServerProgram)
 	h.Expect("Ready")
 	fmt.Fprintln(h.Stdin(), "forcestop")
 	h.Expect("straight exit")
@@ -107,7 +107,7 @@
 func TestSimpleServerKill(t *testing.T) {
 	sh := newShell(t)
 	defer sh.Cleanup(os.Stdout, cstderr)
-	h, _ := sh.Start("simpleServerProgram", nil)
+	h, _ := sh.Start(nil, simpleServerProgram)
 	h.Expect("Ready")
 	syscall.Kill(h.Pid(), syscall.SIGKILL)
 	err := h.Shutdown(os.Stdout, cstderr)
@@ -126,7 +126,7 @@
 func TestComplexServerSignal(t *testing.T) {
 	sh := newShell(t)
 	defer sh.Cleanup(os.Stdout, cstderr)
-	h, _ := sh.Start("complexServerProgram", nil)
+	h, _ := sh.Start(nil, complexServerProgram)
 	h.Expect("Ready")
 	syscall.Kill(h.Pid(), syscall.SIGINT)
 	h.Expect("Received signal interrupt")
@@ -147,7 +147,7 @@
 func TestComplexServerLocalStop(t *testing.T) {
 	sh := newShell(t)
 	defer sh.Cleanup(os.Stdout, cstderr)
-	h, _ := sh.Start("complexServerProgram", nil)
+	h, _ := sh.Start(nil, complexServerProgram)
 	h.Expect("Ready")
 
 	fmt.Fprintln(h.Stdin(), "stop")
@@ -173,7 +173,7 @@
 func TestComplexServerDoubleSignal(t *testing.T) {
 	sh := newShell(t)
 	defer sh.Cleanup(os.Stdout, cstderr)
-	h, _ := sh.Start("complexServerProgram", nil)
+	h, _ := sh.Start(nil, complexServerProgram)
 	h.Expect("Ready")
 	syscall.Kill(h.Pid(), syscall.SIGINT)
 	h.Expect("Received signal interrupt")
@@ -196,7 +196,7 @@
 func TestComplexServerLocalForceStop(t *testing.T) {
 	sh := newShell(t)
 	defer sh.Cleanup(os.Stdout, cstderr)
-	h, _ := sh.Start("complexServerProgram", nil)
+	h, _ := sh.Start(nil, complexServerProgram)
 	h.Expect("Ready")
 	fmt.Fprintln(h.Stdin(), "forcestop")
 	h.Expect("straight exit")
@@ -214,7 +214,7 @@
 func TestComplexServerKill(t *testing.T) {
 	sh := newShell(t)
 	defer sh.Cleanup(os.Stdout, cstderr)
-	h, _ := sh.Start("complexServerProgram", nil)
+	h, _ := sh.Start(nil, complexServerProgram)
 	h.Expect("Ready")
 	syscall.Kill(h.Pid(), syscall.SIGKILL)
 	err := h.Shutdown(os.Stdout, cstderr)
diff --git a/runtime/internal/rt/signal_test.go b/runtime/internal/rt/signal_test.go
index d84455b..7d7c466 100644
--- a/runtime/internal/rt/signal_test.go
+++ b/runtime/internal/rt/signal_test.go
@@ -18,11 +18,6 @@
 	"v.io/x/ref/test/modules"
 )
 
-func init() {
-	modules.RegisterChild("withRuntime", "", withRuntime)
-	modules.RegisterChild("withoutRuntime", "", withoutRuntime)
-}
-
 func simpleEchoProgram(stdin io.Reader, stdout io.Writer) {
 	fmt.Fprintf(stdout, "ready\n")
 	scanner := bufio.NewScanner(stdin)
@@ -32,18 +27,18 @@
 	modules.WaitForEOF(stdin)
 }
 
-func withRuntime(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
-	_, shutdown := test.InitForTest()
+var withRuntime = modules.Register(func(env *modules.Env, args ...string) error {
+	_, shutdown := test.V23Init()
 	defer shutdown()
 
-	simpleEchoProgram(stdin, stdout)
+	simpleEchoProgram(env.Stdin, env.Stdout)
 	return nil
-}
+}, "withRuntime")
 
-func withoutRuntime(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
-	simpleEchoProgram(stdin, stdout)
+var withoutRuntime = modules.Register(func(env *modules.Env, args ...string) error {
+	simpleEchoProgram(env.Stdin, env.Stdout)
 	return nil
-}
+}, "withoutRuntime")
 
 func TestWithRuntime(t *testing.T) {
 	sh, err := modules.NewShell(nil, nil, testing.Verbose(), t)
@@ -51,7 +46,7 @@
 		t.Fatalf("unexpected error: %s", err)
 	}
 	defer sh.Cleanup(os.Stderr, os.Stderr)
-	h, err := sh.Start("withRuntime", nil)
+	h, err := sh.Start(nil, withRuntime)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -72,7 +67,7 @@
 	defer sh.Cleanup(os.Stderr, os.Stderr)
 	opts := sh.DefaultStartOpts()
 	opts.ShutdownTimeout = 5 * time.Second
-	h, err := sh.StartWithOpts(opts, nil, "withoutRuntime")
+	h, err := sh.StartWithOpts(opts, nil, withoutRuntime)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
diff --git a/runtime/internal/rt/v23_test.go b/runtime/internal/rt/v23_test.go
index 774f3c3..2145611 100644
--- a/runtime/internal/rt/v23_test.go
+++ b/runtime/internal/rt/v23_test.go
@@ -4,43 +4,19 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package rt_test
 
-import "fmt"
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
-import "v.io/x/ref/test/modules"
-
-func init() {
-	modules.RegisterChild("noWaiters", ``, noWaiters)
-	modules.RegisterChild("forceStop", ``, forceStop)
-	modules.RegisterChild("app", ``, app)
-	modules.RegisterChild("child", ``, child)
-	modules.RegisterChild("principal", ``, principal)
-	modules.RegisterChild("runner", `Runner runs a principal as a subprocess and reports back with its
-own security info and it's childs.`, runner)
-	modules.RegisterChild("complexServerProgram", `complexServerProgram demonstrates the recommended way to write a more
-complex server application (with several servers, a mix of interruptible
-and blocking cleanup, and parallel and sequential cleanup execution).
-For a more typical server, see simpleServerProgram.`, complexServerProgram)
-	modules.RegisterChild("simpleServerProgram", `simpleServerProgram demonstrates the recommended way to write a typical
-simple server application (with one server and a clean shutdown triggered by
-a signal or a stop command).  For an example of something more involved, see
-complexServerProgram.`, simpleServerProgram)
-	modules.RegisterChild("withRuntime", ``, withRuntime)
-	modules.RegisterChild("withoutRuntime", ``, withoutRuntime)
-}
+	"v.io/x/ref/test"
+	"v.io/x/ref/test/modules"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
-	if modules.IsModulesChildProcess() {
-		if err := modules.Dispatch(); err != nil {
-			fmt.Fprintf(os.Stderr, "modules.Dispatch failed: %v\n", err)
-			os.Exit(1)
-		}
-		return
-	}
+	modules.DispatchAndExitIfChild()
 	os.Exit(m.Run())
 }
diff --git a/runtime/internal/testing/concurrency/v23_internal_test.go b/runtime/internal/testing/concurrency/v23_internal_test.go
index e2d51cf..be5e421 100644
--- a/runtime/internal/testing/concurrency/v23_internal_test.go
+++ b/runtime/internal/testing/concurrency/v23_internal_test.go
@@ -4,12 +4,15 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package concurrency
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
+	"v.io/x/ref/test"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
diff --git a/runtime/internal/testing/mocks/mocknet/mocknet_test.go b/runtime/internal/testing/mocks/mocknet/mocknet_test.go
index ccf1cf9..f038325 100644
--- a/runtime/internal/testing/mocks/mocknet/mocknet_test.go
+++ b/runtime/internal/testing/mocks/mocknet/mocknet_test.go
@@ -325,7 +325,7 @@
 }
 
 func newCtx() (*context.T, v23.Shutdown) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	v23.GetNamespace(ctx).CacheCtl(naming.DisableCache(true))
 	return ctx, shutdown
 }
diff --git a/runtime/internal/testing/mocks/mocknet/v23_internal_test.go b/runtime/internal/testing/mocks/mocknet/v23_internal_test.go
index bc46709..ec445eb 100644
--- a/runtime/internal/testing/mocks/mocknet/v23_internal_test.go
+++ b/runtime/internal/testing/mocks/mocknet/v23_internal_test.go
@@ -4,12 +4,15 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package mocknet
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
+	"v.io/x/ref/test"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
diff --git a/runtime/internal/testing/mocks/naming/namespace.go b/runtime/internal/testing/mocks/naming/namespace.go
index 2b48c30..19148e0 100644
--- a/runtime/internal/testing/mocks/naming/namespace.go
+++ b/runtime/internal/testing/mocks/naming/namespace.go
@@ -16,8 +16,8 @@
 	"v.io/v23/security"
 	"v.io/v23/security/access"
 	"v.io/v23/verror"
-	"v.io/x/lib/vlog"
 
+	"v.io/x/ref/lib/apilog"
 	inamespace "v.io/x/ref/runtime/internal/naming/namespace"
 )
 
@@ -40,7 +40,7 @@
 }
 
 func (ns *namespaceMock) Mount(ctx *context.T, name, server string, _ time.Duration, opts ...naming.NamespaceOpt) error {
-	defer vlog.LogCallf("ctx=,name=%.10s...,server=%.10s...,opts...=%v", name, server, opts)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(ctx, "name=%.10s...,server=%.10s...,opts...=%v", name, server, opts)(ctx, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	ns.Lock()
 	defer ns.Unlock()
 	for n, _ := range ns.mounts {
@@ -53,12 +53,23 @@
 		e = &naming.MountEntry{}
 		ns.mounts[name] = e
 	}
-	e.Servers = append(e.Servers, naming.MountedServer{Server: server})
+
+	isdup := func(n string) bool {
+		for _, s := range e.Servers {
+			if n == s.Server {
+				return true
+			}
+		}
+		return false
+	}
+	if !isdup(server) {
+		e.Servers = append(e.Servers, naming.MountedServer{Server: server})
+	}
 	return nil
 }
 
 func (ns *namespaceMock) Unmount(ctx *context.T, name, server string, opts ...naming.NamespaceOpt) error {
-	defer vlog.LogCallf("ctx=,name=%.10s...,server=%.10s...,opts...=%v", name, server, opts)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(ctx, "name=%.10s...,server=%.10s...,opts...=%v", name, server, opts)(ctx, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	ns.Lock()
 	defer ns.Unlock()
 	e := ns.mounts[name]
@@ -84,7 +95,7 @@
 }
 
 func (ns *namespaceMock) Delete(ctx *context.T, name string, removeSubtree bool, opts ...naming.NamespaceOpt) error {
-	defer vlog.LogCallf("ctx=,name=%.10s...,removeSubtree=%v,opts...=%v", name, removeSubtree, opts)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(ctx, "name=%.10s...,removeSubtree=%v,opts...=%v", name, removeSubtree, opts)(ctx, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	ns.Lock()
 	defer ns.Unlock()
 	e := ns.mounts[name]
@@ -104,7 +115,7 @@
 }
 
 func (ns *namespaceMock) Resolve(ctx *context.T, name string, opts ...naming.NamespaceOpt) (*naming.MountEntry, error) {
-	defer vlog.LogCallf("ctx=,name=%.10s...,opts...=%v", name, opts)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(ctx, "name=%.10s...,opts...=%v", name, opts)(ctx, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	_, name = security.SplitPatternName(name)
 	if address, suffix := naming.SplitAddressName(name); len(address) > 0 {
 		return &naming.MountEntry{
@@ -125,43 +136,43 @@
 }
 
 func (ns *namespaceMock) ResolveToMountTable(ctx *context.T, name string, opts ...naming.NamespaceOpt) (*naming.MountEntry, error) {
-	defer vlog.LogCallf("ctx=,name=%.10s...,opts...=%v", name, opts)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(ctx, "name=%.10s...,opts...=%v", name, opts)(ctx, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	// TODO(mattr): Implement this method for tests that might need it.
 	panic("ResolveToMountTable not implemented")
 }
 
 func (ns *namespaceMock) FlushCacheEntry(name string) bool {
-	defer vlog.LogCallf("name=%.10s...", name)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(nil, "name=%.10s...", name)(nil, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	return false
 }
 
 func (ns *namespaceMock) CacheCtl(ctls ...naming.CacheCtl) []naming.CacheCtl {
-	defer vlog.LogCallf("ctls...=%v", ctls)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(nil, "ctls...=%v", ctls)(nil, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	return nil
 }
 
 func (ns *namespaceMock) Glob(ctx *context.T, pattern string, opts ...naming.NamespaceOpt) (chan naming.GlobReply, error) {
-	defer vlog.LogCallf("ctx=,pattern=%.10s...,opts...=%v", pattern, opts)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(ctx, "pattern=%.10s...,opts...=%v", pattern, opts)(ctx, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	// TODO(mattr): Implement this method for tests that might need it.
 	panic("Glob not implemented")
 }
 
 func (ns *namespaceMock) SetRoots(...string) error {
-	defer vlog.LogCall()() // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCall(nil)(nil) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	panic("Calling SetRoots on a mock namespace.  This is not supported.")
 }
 
 func (ns *namespaceMock) Roots() []string {
-	defer vlog.LogCall()() // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCall(nil)(nil) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	panic("Calling Roots on a mock namespace.  This is not supported.")
 }
 
 func (ns *namespaceMock) GetPermissions(ctx *context.T, name string, opts ...naming.NamespaceOpt) (perms access.Permissions, version string, err error) {
-	defer vlog.LogCallf("ctx=,name=%.10s...,opts...=%v", name, opts)("perms=,version=%.10s...,err=%v", &version, &err) // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(ctx, "name=%.10s...,opts...=%v", name, opts)(ctx, "perms=,version=%.10s...,err=%v", &version, &err) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	panic("Calling GetPermissions on a mock namespace.  This is not supported.")
 }
 
 func (ns *namespaceMock) SetPermissions(ctx *context.T, name string, perms access.Permissions, version string, opts ...naming.NamespaceOpt) error {
-	defer vlog.LogCallf("ctx=,name=%.10s...,perms=,version=%.10s...,opts...=%v", name, version, opts)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(ctx, "name=%.10s...,perms=,version=%.10s...,opts...=%v", name, version, opts)(ctx, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	panic("Calling SetPermissions on a mock namespace.  This is not supported.")
 }
diff --git a/runtime/internal/vtrace/store.go b/runtime/internal/vtrace/store.go
index 9c0d77b..55bf928 100644
--- a/runtime/internal/vtrace/store.go
+++ b/runtime/internal/vtrace/store.go
@@ -13,7 +13,7 @@
 	"v.io/v23/uniqueid"
 	"v.io/v23/vtrace"
 
-	"v.io/x/lib/vlog"
+	"v.io/x/ref/lib/apilog"
 	"v.io/x/ref/lib/flags"
 )
 
@@ -61,7 +61,7 @@
 }
 
 func (s *Store) ForceCollect(id uniqueid.Id) {
-	defer vlog.LogCallf("id=")("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(nil, "id=")(nil, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	s.mu.Lock()
 	s.forceCollectLocked(id)
 	s.mu.Unlock()
@@ -85,7 +85,7 @@
 
 // Merge merges a vtrace.Response into the current store.
 func (s *Store) Merge(t vtrace.Response) {
-	defer vlog.LogCallf("t=")("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(nil, "t=")(nil, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	s.mu.Lock()
 	defer s.mu.Unlock()
 
@@ -161,7 +161,7 @@
 
 // TraceRecords returns TraceRecords for all traces saved in the store.
 func (s *Store) TraceRecords() []vtrace.TraceRecord {
-	defer vlog.LogCall()() // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCall(nil)(nil) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	s.mu.Lock()
 	defer s.mu.Unlock()
 
@@ -177,7 +177,7 @@
 // TraceRecord returns a TraceRecord for a given Id.  Returns
 // nil if the given id is not present.
 func (s *Store) TraceRecord(id uniqueid.Id) *vtrace.TraceRecord {
-	defer vlog.LogCallf("id=")("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+	defer apilog.LogCallf(nil, "id=")(nil, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	s.mu.Lock()
 	defer s.mu.Unlock()
 	out := &vtrace.TraceRecord{}
diff --git a/runtime/internal/vtrace/vtrace_test.go b/runtime/internal/vtrace/vtrace_test.go
index 38e3df5..7e3ee2e 100644
--- a/runtime/internal/vtrace/vtrace_test.go
+++ b/runtime/internal/vtrace/vtrace_test.go
@@ -9,6 +9,7 @@
 	"fmt"
 	"strings"
 	"testing"
+	"time"
 
 	"v.io/v23"
 	"v.io/v23/context"
@@ -35,7 +36,7 @@
 // initForTest initializes the vtrace runtime and starts a mounttable.
 func initForTest(t *testing.T) (*context.T, v23.Shutdown, *testutil.IDProvider) {
 	idp := testutil.NewIDProvider("base")
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	if err := idp.Bless(v23.GetPrincipal(ctx), "alice"); err != nil {
 		t.Fatalf("Could not bless initial principal %v", err)
 	}
@@ -105,6 +106,16 @@
 	return nil
 }
 
+func verifyMount(ctx *context.T, name string) error {
+	ns := v23.GetNamespace(ctx)
+	for {
+		if _, err := ns.Resolve(ctx, name); err == nil {
+			return nil
+		}
+		time.Sleep(10 * time.Millisecond)
+	}
+}
+
 func runCallChain(t *testing.T, ctx *context.T, idp *testutil.IDProvider, force1, force2 bool) *vtrace.TraceRecord {
 	ctx, span := vtrace.WithNewSpan(ctx, "")
 	span.Annotate("c0-begin")
@@ -146,6 +157,9 @@
 		}
 		c.forceCollect = f
 		out = append(out, c)
+		// Make sure the server is mounted to avoid any retries in when StartCall
+		// is invoked in runCallChain which complicate the span comparisons.
+		verifyMount(ctx, name)
 	}
 	return out, func() {
 		for _, s := range out {
diff --git a/services/agent/agentlib/agent_test.go b/services/agent/agentlib/agent_test.go
index 93621a9..46170bf 100644
--- a/services/agent/agentlib/agent_test.go
+++ b/services/agent/agentlib/agent_test.go
@@ -6,7 +6,6 @@
 
 import (
 	"fmt"
-	"io"
 	"io/ioutil"
 	"os"
 	"reflect"
@@ -43,15 +42,15 @@
 
 //go:generate v23 test generate
 
-func getPrincipalAndHang(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
-	ctx, shutdown := test.InitForTest()
+var getPrincipalAndHang = modules.Register(func(env *modules.Env, args ...string) error {
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	p := v23.GetPrincipal(ctx)
-	fmt.Fprintf(stdout, "DEFAULT_BLESSING=%s\n", p.BlessingStore().Default())
-	ioutil.ReadAll(stdin)
+	fmt.Fprintf(env.Stdout, "DEFAULT_BLESSING=%s\n", p.BlessingStore().Default())
+	ioutil.ReadAll(env.Stdin)
 	return nil
-}
+}, "getPrincipalAndHang")
 
 func newAgent(ctx *context.T, endpoint string, cached bool) (security.Principal, error) {
 	ep, err := v23.NewEndpoint(endpoint)
@@ -98,7 +97,7 @@
 }
 
 func TestAgent(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	var (
@@ -157,7 +156,7 @@
 }
 
 func TestAgentShutdown(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 
 	// This starts an agent
 	sh, err := modules.NewShell(ctx, nil, testing.Verbose(), t)
@@ -165,7 +164,7 @@
 		t.Fatal(err)
 	}
 	// The child process will connect to the agent
-	h, err := sh.Start("getPrincipalAndHang", nil)
+	h, err := sh.Start(nil, getPrincipalAndHang)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -235,73 +234,73 @@
 }
 
 func BenchmarkSignNoAgent(b *testing.B) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 	runSignBenchmark(b, v23.GetPrincipal(ctx))
 }
 
 func BenchmarkSignCachedAgent(b *testing.B) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 	runSignBenchmark(b, setupAgent(ctx, true))
 }
 
 func BenchmarkSignUncachedAgent(b *testing.B) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 	runSignBenchmark(b, setupAgent(ctx, false))
 }
 
 func BenchmarkDefaultNoAgent(b *testing.B) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 	runDefaultBenchmark(b, v23.GetPrincipal(ctx))
 }
 
 func BenchmarkDefaultCachedAgent(b *testing.B) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 	runDefaultBenchmark(b, setupAgent(ctx, true))
 }
 
 func BenchmarkDefaultUncachedAgent(b *testing.B) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 	runDefaultBenchmark(b, setupAgent(ctx, false))
 }
 
 func BenchmarkRecognizedNegativeNoAgent(b *testing.B) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 	runRecognizedNegativeBenchmark(b, v23.GetPrincipal(ctx))
 }
 
 func BenchmarkRecognizedNegativeCachedAgent(b *testing.B) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 	runRecognizedNegativeBenchmark(b, setupAgent(ctx, true))
 }
 
 func BenchmarkRecognizedNegativeUncachedAgent(b *testing.B) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 	runRecognizedNegativeBenchmark(b, setupAgent(ctx, false))
 }
 
 func BenchmarkRecognizedNoAgent(b *testing.B) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 	runRecognizedBenchmark(b, v23.GetPrincipal(ctx))
 }
 
 func BenchmarkRecognizedCachedAgent(b *testing.B) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 	runRecognizedBenchmark(b, setupAgent(ctx, true))
 }
 
 func BenchmarkRecognizedUncachedAgent(b *testing.B) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 	runRecognizedBenchmark(b, setupAgent(ctx, false))
 }
diff --git a/services/agent/agentlib/v23_test.go b/services/agent/agentlib/v23_test.go
index 3e4061b..334393a 100644
--- a/services/agent/agentlib/v23_test.go
+++ b/services/agent/agentlib/v23_test.go
@@ -4,29 +4,21 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package agentlib_test
 
-import "fmt"
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
-import "v.io/x/ref/test/modules"
-import "v.io/x/ref/test/v23tests"
-
-func init() {
-	modules.RegisterChild("getPrincipalAndHang", ``, getPrincipalAndHang)
-}
+	"v.io/x/ref/test"
+	"v.io/x/ref/test/modules"
+	"v.io/x/ref/test/v23tests"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
-	if modules.IsModulesChildProcess() {
-		if err := modules.Dispatch(); err != nil {
-			fmt.Fprintf(os.Stderr, "modules.Dispatch failed: %v\n", err)
-			os.Exit(1)
-		}
-		return
-	}
+	modules.DispatchAndExitIfChild()
 	cleanup := v23tests.UseSharedBinDir()
 	r := m.Run()
 	cleanup()
diff --git a/services/agent/keymgr/keymgr_test.go b/services/agent/keymgr/keymgr_test.go
index bbe20be..dd0a334 100644
--- a/services/agent/keymgr/keymgr_test.go
+++ b/services/agent/keymgr/keymgr_test.go
@@ -44,7 +44,7 @@
 }
 
 func TestNoDeviceManager(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	agent, cleanup, err := createAgent(ctx, "")
@@ -80,7 +80,7 @@
 }
 
 func TestSigning(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	path, err := ioutil.TempDir("", "agent")
@@ -147,7 +147,7 @@
 }
 
 func TestInMemorySigning(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	path, err := ioutil.TempDir("", "agent")
diff --git a/services/agent/vbecome/v23_test.go b/services/agent/vbecome/v23_test.go
index 35ff567..69f2726 100644
--- a/services/agent/vbecome/v23_test.go
+++ b/services/agent/vbecome/v23_test.go
@@ -4,16 +4,21 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package main_test
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
-import "v.io/x/ref/test/v23tests"
+	"v.io/x/ref/test"
+	"v.io/x/ref/test/modules"
+	"v.io/x/ref/test/v23tests"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
+	modules.DispatchAndExitIfChild()
 	cleanup := v23tests.UseSharedBinDir()
 	r := m.Run()
 	cleanup()
diff --git a/services/application/application/impl_test.go b/services/application/application/impl_test.go
index 8f7a084..676676a 100644
--- a/services/application/application/impl_test.go
+++ b/services/application/application/impl_test.go
@@ -146,7 +146,7 @@
 }
 
 func TestApplicationClient(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	server, endpoint, err := startServer(t, ctx)
diff --git a/services/application/application/v23_internal_test.go b/services/application/application/v23_internal_test.go
index dcd0029..ae59080 100644
--- a/services/application/application/v23_internal_test.go
+++ b/services/application/application/v23_internal_test.go
@@ -4,12 +4,15 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package main
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
+	"v.io/x/ref/test"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
diff --git a/services/application/applicationd/impl_test.go b/services/application/applicationd/impl_test.go
index aa53303..7284d8a 100644
--- a/services/application/applicationd/impl_test.go
+++ b/services/application/applicationd/impl_test.go
@@ -41,7 +41,7 @@
 // TestInterface tests that the implementation correctly implements
 // the Application interface.
 func TestInterface(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	dir, prefix := "", ""
@@ -230,7 +230,7 @@
 }
 
 func TestPreserveAcrossRestarts(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	dir, prefix := "", ""
diff --git a/services/application/applicationd/perms_test.go b/services/application/applicationd/perms_test.go
index e787080..ad04614 100644
--- a/services/application/applicationd/perms_test.go
+++ b/services/application/applicationd/perms_test.go
@@ -6,7 +6,6 @@
 
 import (
 	"fmt"
-	"io"
 	"os"
 	"reflect"
 	"syscall"
@@ -19,34 +18,30 @@
 	"v.io/v23/services/application"
 	"v.io/v23/verror"
 	"v.io/x/lib/vlog"
-
 	"v.io/x/ref/lib/signals"
 	appd "v.io/x/ref/services/application/applicationd"
 	"v.io/x/ref/services/internal/servicetest"
 	"v.io/x/ref/services/repository"
 	"v.io/x/ref/test"
+	"v.io/x/ref/test/modules"
 	"v.io/x/ref/test/testutil"
 )
 
 //go:generate v23 test generate
 
-const (
-	repoCmd = "appRepository"
-)
-
-func appRepository(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+var appRepository = modules.Register(func(env *modules.Env, args ...string) error {
 	if len(args) < 2 {
 		vlog.Fatalf("repository expected at least name and store arguments and optionally Permissions flags per PermissionsFromFlag")
 	}
 	publishName := args[0]
 	storedir := args[1]
 
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	v23.GetNamespace(ctx).CacheCtl(naming.DisableCache(true))
 
-	defer fmt.Fprintf(stdout, "%v terminating\n", publishName)
+	defer fmt.Fprintf(env.Stdout, "%v terminating\n", publishName)
 	defer vlog.VI(1).Infof("%v terminating", publishName)
 	server, endpoint := servicetest.NewServer(ctx)
 	defer server.Stop()
@@ -62,14 +57,14 @@
 		vlog.Fatalf("Serve(%v) failed: %v", publishName, err)
 	}
 
-	fmt.Fprintf(stdout, "ready:%d\n", os.Getpid())
+	fmt.Fprintf(env.Stdout, "ready:%d\n", os.Getpid())
 	<-signals.ShutdownOnSignals(ctx)
 
 	return nil
-}
+}, "appRepository")
 
 func TestApplicationUpdatePermissions(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 	v23.GetNamespace(ctx).CacheCtl(naming.DisableCache(true))
 
@@ -88,7 +83,7 @@
 	storedir, cleanup := servicetest.SetupRootDir(t, "application")
 	defer cleanup()
 
-	nmh := servicetest.RunCommand(t, sh, nil, repoCmd, "repo", storedir)
+	nmh := servicetest.RunCommand(t, sh, nil, appRepository, "repo", storedir)
 	pid := servicetest.ReadPID(t, nmh)
 	defer syscall.Kill(pid, syscall.SIGINT)
 
@@ -215,7 +210,7 @@
 }
 
 func TestPerAppPermissions(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 	v23.GetNamespace(ctx).CacheCtl(naming.DisableCache(true))
 	// By default, all principals in this test will have blessings generated based
@@ -241,7 +236,7 @@
 		t.Fatal(err)
 	}
 
-	nmh := servicetest.RunCommand(t, sh, nil, repoCmd, "repo", storedir)
+	nmh := servicetest.RunCommand(t, sh, nil, appRepository, "repo", storedir)
 	pid := servicetest.ReadPID(t, nmh)
 	defer syscall.Kill(pid, syscall.SIGINT)
 
diff --git a/services/application/applicationd/v23_test.go b/services/application/applicationd/v23_test.go
index f032617..fff5ec8 100644
--- a/services/application/applicationd/v23_test.go
+++ b/services/application/applicationd/v23_test.go
@@ -4,29 +4,21 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package main_test
 
-import "fmt"
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
-import "v.io/x/ref/test/modules"
-import "v.io/x/ref/test/v23tests"
-
-func init() {
-	modules.RegisterChild("appRepository", ``, appRepository)
-}
+	"v.io/x/ref/test"
+	"v.io/x/ref/test/modules"
+	"v.io/x/ref/test/v23tests"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
-	if modules.IsModulesChildProcess() {
-		if err := modules.Dispatch(); err != nil {
-			fmt.Fprintf(os.Stderr, "modules.Dispatch failed: %v\n", err)
-			os.Exit(1)
-		}
-		return
-	}
+	modules.DispatchAndExitIfChild()
 	cleanup := v23tests.UseSharedBinDir()
 	r := m.Run()
 	cleanup()
diff --git a/services/binary/binary/impl_test.go b/services/binary/binary/impl_test.go
index 36a5c01..e4dc1a6 100644
--- a/services/binary/binary/impl_test.go
+++ b/services/binary/binary/impl_test.go
@@ -127,7 +127,7 @@
 }
 
 func TestBinaryClient(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	server, endpoint, err := startServer(t, ctx)
diff --git a/services/binary/binary/v23_internal_test.go b/services/binary/binary/v23_internal_test.go
index dcd0029..ae59080 100644
--- a/services/binary/binary/v23_internal_test.go
+++ b/services/binary/binary/v23_internal_test.go
@@ -4,12 +4,15 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package main
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
+	"v.io/x/ref/test"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
diff --git a/services/binary/binaryd/v23_test.go b/services/binary/binaryd/v23_test.go
index b7aae18..5c12adf 100644
--- a/services/binary/binaryd/v23_test.go
+++ b/services/binary/binaryd/v23_test.go
@@ -4,16 +4,21 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package main_test
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
-import "v.io/x/ref/test/v23tests"
+	"v.io/x/ref/test"
+	"v.io/x/ref/test/modules"
+	"v.io/x/ref/test/v23tests"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
+	modules.DispatchAndExitIfChild()
 	cleanup := v23tests.UseSharedBinDir()
 	r := m.Run()
 	cleanup()
diff --git a/services/build/build/impl_test.go b/services/build/build/impl_test.go
index c37c367..64c1479 100644
--- a/services/build/build/impl_test.go
+++ b/services/build/build/impl_test.go
@@ -70,7 +70,7 @@
 }
 
 func TestBuildClient(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	server, endpoint := startServer(ctx, t)
diff --git a/services/build/build/v23_internal_test.go b/services/build/build/v23_internal_test.go
index dcd0029..ae59080 100644
--- a/services/build/build/v23_internal_test.go
+++ b/services/build/build/v23_internal_test.go
@@ -4,12 +4,15 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package main
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
+	"v.io/x/ref/test"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
diff --git a/services/build/buildd/impl_test.go b/services/build/buildd/impl_test.go
index dacf6eb..6956828 100644
--- a/services/build/buildd/impl_test.go
+++ b/services/build/buildd/impl_test.go
@@ -142,7 +142,7 @@
 // TestSuccess checks that the build server successfully builds a
 // package that depends on the standard Go library.
 func TestSuccess(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	client := startServer(t, ctx)
@@ -177,7 +177,7 @@
 // TestEmpty checks that the build server successfully builds a
 // package that does not produce a binary.
 func TestEmpty(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	client := startServer(t, ctx)
@@ -212,7 +212,7 @@
 // TestFailure checks that the build server fails to build a package
 // consisting of an empty file.
 func TestFailure(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	client := startServer(t, ctx)
diff --git a/services/build/buildd/v23_test.go b/services/build/buildd/v23_test.go
index 6ec6ef0..dbfb5ac 100644
--- a/services/build/buildd/v23_test.go
+++ b/services/build/buildd/v23_test.go
@@ -4,16 +4,21 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package main_test
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
-import "v.io/x/ref/test/v23tests"
+	"v.io/x/ref/test"
+	"v.io/x/ref/test/modules"
+	"v.io/x/ref/test/v23tests"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
+	modules.DispatchAndExitIfChild()
 	cleanup := v23tests.UseSharedBinDir()
 	r := m.Run()
 	cleanup()
diff --git a/services/debug/debug/v23_test.go b/services/debug/debug/v23_test.go
index b9cd380..007a5fd 100644
--- a/services/debug/debug/v23_test.go
+++ b/services/debug/debug/v23_test.go
@@ -4,16 +4,21 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package main_test
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
-import "v.io/x/ref/test/v23tests"
+	"v.io/x/ref/test"
+	"v.io/x/ref/test/modules"
+	"v.io/x/ref/test/v23tests"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
+	modules.DispatchAndExitIfChild()
 	cleanup := v23tests.UseSharedBinDir()
 	r := m.Run()
 	cleanup()
diff --git a/services/debug/debuglib/dispatcher_test.go b/services/debug/debuglib/dispatcher_test.go
index d5879fb..5bcd18c 100644
--- a/services/debug/debuglib/dispatcher_test.go
+++ b/services/debug/debuglib/dispatcher_test.go
@@ -57,7 +57,7 @@
 }
 
 func TestDebugServer(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	tracedContext := func(ctx *context.T) *context.T {
diff --git a/services/debug/debuglib/v23_internal_test.go b/services/debug/debuglib/v23_internal_test.go
index cdbaeda..77006f9 100644
--- a/services/debug/debuglib/v23_internal_test.go
+++ b/services/debug/debuglib/v23_internal_test.go
@@ -4,12 +4,15 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package debuglib
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
+	"v.io/x/ref/test"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
diff --git a/services/device/device/acl_impl.go b/services/device/device/acl.go
similarity index 100%
rename from services/device/device/acl_impl.go
rename to services/device/device/acl.go
diff --git a/services/device/device/acl_test.go b/services/device/device/acl_test.go
index 1a12227..4ad61bc 100644
--- a/services/device/device/acl_test.go
+++ b/services/device/device/acl_test.go
@@ -28,7 +28,7 @@
 )
 
 func TestAccessListGetCommand(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	tapes := newTapeMap()
@@ -76,7 +76,7 @@
 }
 
 func TestAccessListSetCommand(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	tapes := newTapeMap()
diff --git a/services/device/device/associate_impl.go b/services/device/device/associate.go
similarity index 100%
rename from services/device/device/associate_impl.go
rename to services/device/device/associate.go
diff --git a/services/device/device/associate_test.go b/services/device/device/associate_test.go
new file mode 100644
index 0000000..20d01d5
--- /dev/null
+++ b/services/device/device/associate_test.go
@@ -0,0 +1,165 @@
+// 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.
+
+package main_test
+
+import (
+	"bytes"
+	"reflect"
+	"strings"
+	"testing"
+
+	"v.io/v23/naming"
+	"v.io/v23/services/device"
+	"v.io/x/lib/cmdline"
+	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/test"
+
+	cmd_device "v.io/x/ref/services/device/device"
+)
+
+func TestListCommand(t *testing.T) {
+	ctx, shutdown := test.V23Init()
+	defer shutdown()
+
+	tapes := newTapeMap()
+	server, endpoint, err := startServer(t, ctx, tapes)
+	if err != nil {
+		return
+	}
+	defer stopServer(t, server)
+
+	// Setup the command-line.
+	cmd := cmd_device.CmdRoot
+	var stdout, stderr bytes.Buffer
+	env := &cmdline.Env{Stdout: &stdout, Stderr: &stderr}
+	deviceName := naming.JoinAddressName(endpoint.String(), "")
+
+	rootTape := tapes.forSuffix("")
+	// Test the 'list' command.
+	rootTape.SetResponses(ListAssociationResponse{
+		na: []device.Association{
+			{
+				"root/self",
+				"alice_self_account",
+			},
+			{
+				"root/other",
+				"alice_other_account",
+			},
+		},
+		err: nil,
+	})
+
+	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"associate", "list", deviceName}); err != nil {
+		t.Fatalf("%v", err)
+	}
+	if expected, got := "root/self alice_self_account\nroot/other alice_other_account", strings.TrimSpace(stdout.String()); got != expected {
+		t.Fatalf("Unexpected output from list. Got %q, expected %q", got, expected)
+	}
+	if got, expected := rootTape.Play(), []interface{}{"ListAssociations"}; !reflect.DeepEqual(expected, got) {
+		t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
+	}
+	rootTape.Rewind()
+	stdout.Reset()
+
+	// Test list with bad parameters.
+	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"associate", "list", deviceName, "hello"}); err == nil {
+		t.Fatalf("wrongly failed to receive a non-nil error.")
+	}
+	if got, expected := len(rootTape.Play()), 0; got != expected {
+		t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
+	}
+}
+
+func TestAddCommand(t *testing.T) {
+	ctx, shutdown := test.V23Init()
+	defer shutdown()
+
+	tapes := newTapeMap()
+	server, endpoint, err := startServer(t, ctx, tapes)
+	if err != nil {
+		return
+	}
+	defer stopServer(t, server)
+
+	// Setup the command-line.
+	cmd := cmd_device.CmdRoot
+	var stdout, stderr bytes.Buffer
+	env := &cmdline.Env{Stdout: &stdout, Stderr: &stderr}
+	deviceName := naming.JoinAddressName(endpoint.String(), "")
+
+	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"add", "one"}); err == nil {
+		t.Fatalf("wrongly failed to receive a non-nil error.")
+	}
+	rootTape := tapes.forSuffix("")
+	if got, expected := len(rootTape.Play()), 0; got != expected {
+		t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
+	}
+	rootTape.Rewind()
+	stdout.Reset()
+
+	rootTape.SetResponses(nil)
+	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"associate", "add", deviceName, "alice", "root/self"}); err != nil {
+		t.Fatalf("%v", err)
+	}
+	expected := []interface{}{
+		AddAssociationStimulus{"AssociateAccount", []string{"root/self"}, "alice"},
+	}
+	if got := rootTape.Play(); !reflect.DeepEqual(expected, got) {
+		t.Errorf("unexpected result. Got %v want %v", got, expected)
+	}
+	rootTape.Rewind()
+	stdout.Reset()
+
+	rootTape.SetResponses(nil)
+	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"associate", "add", deviceName, "alice", "root/other", "root/self"}); err != nil {
+		t.Fatalf("%v", err)
+	}
+	expected = []interface{}{
+		AddAssociationStimulus{"AssociateAccount", []string{"root/other", "root/self"}, "alice"},
+	}
+	if got := rootTape.Play(); !reflect.DeepEqual(expected, got) {
+		t.Errorf("unexpected result. Got %v want %v", got, expected)
+	}
+}
+
+func TestRemoveCommand(t *testing.T) {
+	ctx, shutdown := test.V23Init()
+	defer shutdown()
+
+	tapes := newTapeMap()
+	server, endpoint, err := startServer(t, ctx, tapes)
+	if err != nil {
+		return
+	}
+	defer stopServer(t, server)
+
+	// Setup the command-line.
+	cmd := cmd_device.CmdRoot
+	var stdout, stderr bytes.Buffer
+	env := &cmdline.Env{Stdout: &stdout, Stderr: &stderr}
+	deviceName := naming.JoinAddressName(endpoint.String(), "")
+
+	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"remove", "one"}); err == nil {
+		t.Fatalf("wrongly failed to receive a non-nil error.")
+	}
+	rootTape := tapes.forSuffix("")
+	if got, expected := len(rootTape.Play()), 0; got != expected {
+		t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
+	}
+	rootTape.Rewind()
+	stdout.Reset()
+
+	rootTape.SetResponses(nil)
+	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"associate", "remove", deviceName, "root/self"}); err != nil {
+		t.Fatalf("%v", err)
+	}
+	expected := []interface{}{
+		AddAssociationStimulus{"AssociateAccount", []string{"root/self"}, ""},
+	}
+	if got := rootTape.Play(); !reflect.DeepEqual(expected, got) {
+		t.Errorf("unexpected result. Got %v want %v", got, expected)
+	}
+}
diff --git a/services/device/device/claim.go b/services/device/device/claim.go
new file mode 100644
index 0000000..8d65e4d
--- /dev/null
+++ b/services/device/device/claim.go
@@ -0,0 +1,67 @@
+// 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.
+
+package main
+
+import (
+	"encoding/base64"
+	"fmt"
+
+	"v.io/v23/context"
+	"v.io/v23/options"
+	"v.io/v23/rpc"
+	"v.io/v23/security"
+	"v.io/v23/services/device"
+	"v.io/x/lib/cmdline"
+	"v.io/x/ref/lib/v23cmd"
+)
+
+var cmdClaim = &cmdline.Command{
+	Runner:   v23cmd.RunnerFunc(runClaim),
+	Name:     "claim",
+	Short:    "Claim the device.",
+	Long:     "Claim the device.",
+	ArgsName: "<device> <grant extension> <pairing token> <device publickey>",
+	ArgsLong: `
+<device> is the vanadium object name of the device manager's device service.
+
+<grant extension> is used to extend the default blessing of the
+current principal when blessing the app instance.
+
+<pairing token> is a token that the device manager expects to be replayed
+during a claim operation on the device.
+
+<device publickey> is the marshalled public key of the device manager we
+are claiming.`,
+}
+
+func runClaim(ctx *context.T, env *cmdline.Env, args []string) error {
+	if expected, max, got := 2, 4, len(args); expected > got || got > max {
+		return env.UsageErrorf("claim: incorrect number of arguments, expected atleast %d (max: %d), got %d", expected, max, got)
+	}
+	deviceName, grant := args[0], args[1]
+	var pairingToken string
+	if len(args) > 2 {
+		pairingToken = args[2]
+	}
+	var serverKeyOpts rpc.CallOpt
+	if len(args) > 3 {
+		marshalledPublicKey, err := base64.URLEncoding.DecodeString(args[3])
+		if err != nil {
+			return fmt.Errorf("Failed to base64 decode publickey: %v", err)
+		}
+		if deviceKey, err := security.UnmarshalPublicKey(marshalledPublicKey); err != nil {
+			return fmt.Errorf("Failed to unmarshal device public key:%v", err)
+		} else {
+			serverKeyOpts = options.ServerPublicKey{deviceKey}
+		}
+	}
+	// Skip server endpoint authorization since an unclaimed device might have
+	// roots that will not be recognized by the claimer.
+	if err := device.ClaimableClient(deviceName).Claim(ctx, pairingToken, &granter{extension: grant}, serverKeyOpts, options.SkipServerEndpointAuthorization{}); err != nil {
+		return err
+	}
+	fmt.Fprintln(env.Stdout, "Successfully claimed.")
+	return nil
+}
diff --git a/services/device/device/claim_test.go b/services/device/device/claim_test.go
new file mode 100644
index 0000000..f62baea
--- /dev/null
+++ b/services/device/device/claim_test.go
@@ -0,0 +1,117 @@
+// 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.
+
+package main_test
+
+import (
+	"bytes"
+	"encoding/base64"
+	"reflect"
+	"strings"
+	"testing"
+
+	"v.io/v23"
+	"v.io/v23/naming"
+	"v.io/v23/verror"
+	"v.io/x/lib/cmdline"
+	"v.io/x/ref/lib/security"
+	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/test"
+
+	cmd_device "v.io/x/ref/services/device/device"
+)
+
+func TestClaimCommand(t *testing.T) {
+	ctx, shutdown := test.V23Init()
+	defer shutdown()
+
+	tapes := newTapeMap()
+	server, endpoint, err := startServer(t, ctx, tapes)
+	if err != nil {
+		return
+	}
+	defer stopServer(t, server)
+
+	// Setup the command-line.
+	cmd := cmd_device.CmdRoot
+	var stdout, stderr bytes.Buffer
+	env := &cmdline.Env{Stdout: &stdout, Stderr: &stderr}
+	deviceName := naming.JoinAddressName(endpoint.String(), "")
+	deviceKey, err := v23.GetPrincipal(ctx).PublicKey().MarshalBinary()
+	if err != nil {
+		t.Fatalf("Failed to marshal principal public key: %v", err)
+	}
+
+	// Confirm that we correctly enforce the number of arguments.
+	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"claim", "nope"}); err == nil {
+		t.Fatalf("wrongly failed to receive a non-nil error.")
+	}
+	if expected, got := "ERROR: claim: incorrect number of arguments, expected atleast 2 (max: 4), got 1", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
+		t.Fatalf("Unexpected output from claim. Got %q, expected prefix %q", got, expected)
+	}
+	stdout.Reset()
+	stderr.Reset()
+	rootTape := tapes.forSuffix("")
+	rootTape.Rewind()
+
+	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"claim", "nope", "nope", "nope", "nope", "nope"}); err == nil {
+		t.Fatalf("wrongly failed to receive a non-nil error.")
+	}
+	if expected, got := "ERROR: claim: incorrect number of arguments, expected atleast 2 (max: 4), got 5", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
+		t.Fatalf("Unexpected output from claim. Got %q, expected prefix %q", got, expected)
+	}
+	stdout.Reset()
+	stderr.Reset()
+	rootTape.Rewind()
+
+	// Incorrect operation
+	var pairingToken string
+	var deviceKeyWrong []byte
+	if publicKey, _, err := security.NewPrincipalKey(); err != nil {
+		t.Fatalf("NewPrincipalKey failed: %v", err)
+	} else {
+		if deviceKeyWrong, err = publicKey.MarshalBinary(); err != nil {
+			t.Fatalf("Failed to marshal principal public key: %v", err)
+		}
+	}
+	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"claim", deviceName, "grant", pairingToken, base64.URLEncoding.EncodeToString(deviceKeyWrong)}); verror.ErrorID(err) != verror.ErrNotTrusted.ID {
+		t.Fatalf("wrongly failed to receive correct error on claim with incorrect device key:%v id:%v", err, verror.ErrorID(err))
+	}
+	stdout.Reset()
+	stderr.Reset()
+	rootTape.Rewind()
+
+	// Correct operation.
+	rootTape.SetResponses(nil)
+	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"claim", deviceName, "grant", pairingToken, base64.URLEncoding.EncodeToString(deviceKey)}); err != nil {
+		t.Fatalf("Claim(%s, %s, %s) failed: %v", deviceName, "grant", pairingToken, err)
+	}
+	if got, expected := len(rootTape.Play()), 1; got != expected {
+		t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
+	}
+	if expected, got := "Successfully claimed.", strings.TrimSpace(stdout.String()); !strings.HasPrefix(got, expected) {
+		t.Fatalf("Unexpected output from claim. Got %q, expected prefix %q", got, expected)
+	}
+	expected := []interface{}{
+		"Claim",
+	}
+	if got := rootTape.Play(); !reflect.DeepEqual(expected, got) {
+		t.Errorf("unexpected result. Got %v want %v", got, expected)
+	}
+	rootTape.Rewind()
+	stdout.Reset()
+	stderr.Reset()
+
+	// Error operation.
+	rootTape.SetResponses(verror.New(errOops, nil))
+	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"claim", deviceName, "grant", pairingToken}); err == nil {
+		t.Fatal("claim() failed to detect error:", err)
+	}
+	expected = []interface{}{
+		"Claim",
+	}
+	if got := rootTape.Play(); !reflect.DeepEqual(expected, got) {
+		t.Errorf("unexpected result. Got %v want %v", got, expected)
+	}
+}
diff --git a/services/device/device/debug.go b/services/device/device/debug.go
new file mode 100644
index 0000000..1ba6fc4
--- /dev/null
+++ b/services/device/device/debug.go
@@ -0,0 +1,35 @@
+// 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.
+
+package main
+
+import (
+	"fmt"
+	"io"
+	"strings"
+
+	"v.io/v23/context"
+	"v.io/v23/services/device"
+	"v.io/x/lib/cmdline"
+)
+
+var cmdDebug = &cmdline.Command{
+	Runner:   globRunner(runDebug),
+	Name:     "debug",
+	Short:    "Debug the device.",
+	Long:     "Get internal debug information about application installations and instances.",
+	ArgsName: "<app name patterns...>",
+	ArgsLong: `
+<app name patterns...> are vanadium object names or glob name patterns corresponding to application installations and instances.`,
+}
+
+func runDebug(entry globResult, ctx *context.T, stdout, _ io.Writer) error {
+	if description, err := device.DeviceClient(entry.name).Debug(ctx); err != nil {
+		return fmt.Errorf("Debug failed: %v", err)
+	} else {
+		line := strings.Repeat("*", len(entry.name)+4)
+		fmt.Fprintf(stdout, "%s\n* %s *\n%s\n%v\n", line, entry.name, line, description)
+	}
+	return nil
+}
diff --git a/services/device/device/debug_test.go b/services/device/device/debug_test.go
new file mode 100644
index 0000000..32a7678
--- /dev/null
+++ b/services/device/device/debug_test.go
@@ -0,0 +1,54 @@
+// 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.
+
+package main_test
+
+import (
+	"bytes"
+	"fmt"
+	"reflect"
+	"strings"
+	"testing"
+
+	"v.io/v23/naming"
+	"v.io/x/lib/cmdline"
+	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/test"
+
+	cmd_device "v.io/x/ref/services/device/device"
+)
+
+func TestDebugCommand(t *testing.T) {
+	ctx, shutdown := test.V23Init()
+	defer shutdown()
+	tapes := newTapeMap()
+	server, endpoint, err := startServer(t, ctx, tapes)
+	if err != nil {
+		return
+	}
+	defer stopServer(t, server)
+
+	cmd := cmd_device.CmdRoot
+	globName := naming.JoinAddressName(endpoint.String(), "glob")
+	appName := naming.JoinAddressName(endpoint.String(), "app")
+	rootTape, appTape := tapes.forSuffix(""), tapes.forSuffix("app")
+	rootTape.SetResponses(GlobResponse{[]string{"app"}})
+
+	var stdout, stderr bytes.Buffer
+	env := &cmdline.Env{Stdout: &stdout, Stderr: &stderr}
+
+	debugMessage := "the secrets of the universe, revealed"
+	appTape.SetResponses(instanceRunning, debugMessage)
+	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"debug", globName}); err != nil {
+		t.Fatalf("%v", err)
+	}
+	line := strings.Repeat("*", len(appName)+4)
+	expected := fmt.Sprintf("%s\n* %s *\n%s\n%s", line, appName, line, debugMessage)
+	if got := strings.TrimSpace(stdout.String()); got != expected {
+		t.Fatalf("Unexpected output from debug. Got:\n%v\nExpected:\n%v", got, expected)
+	}
+	if got, expected := appTape.Play(), []interface{}{"Status", "Debug"}; !reflect.DeepEqual(expected, got) {
+		t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
+	}
+}
diff --git a/services/device/device/delete.go b/services/device/device/delete.go
new file mode 100644
index 0000000..faff0df
--- /dev/null
+++ b/services/device/device/delete.go
@@ -0,0 +1,37 @@
+// 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.
+
+package main
+
+import (
+	"fmt"
+
+	"v.io/v23/context"
+	"v.io/v23/services/device"
+	"v.io/x/lib/cmdline"
+	"v.io/x/ref/lib/v23cmd"
+)
+
+var cmdDelete = &cmdline.Command{
+	Runner:   v23cmd.RunnerFunc(runDelete),
+	Name:     "delete",
+	Short:    "Delete the given application instance.",
+	Long:     "Delete the given application instance.",
+	ArgsName: "<app instance>",
+	ArgsLong: `
+<app instance> is the vanadium object name of the application instance to delete.`,
+}
+
+func runDelete(ctx *context.T, env *cmdline.Env, args []string) error {
+	if expected, got := 1, len(args); expected != got {
+		return env.UsageErrorf("delete: incorrect number of arguments, expected %d, got %d", expected, got)
+	}
+	appName := args[0]
+
+	if err := device.ApplicationClient(appName).Delete(ctx); err != nil {
+		return fmt.Errorf("Delete failed: %v", err)
+	}
+	fmt.Fprintf(env.Stdout, "Delete succeeded\n")
+	return nil
+}
diff --git a/services/device/device/delete_test.go b/services/device/device/delete_test.go
new file mode 100644
index 0000000..f1530c9
--- /dev/null
+++ b/services/device/device/delete_test.go
@@ -0,0 +1,11 @@
+// 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.
+
+package main_test
+
+import "testing"
+
+func TestDeleteCommand(t *testing.T) {
+	testHelper(t, "delete", "Delete")
+}
diff --git a/services/device/device/describe.go b/services/device/device/describe.go
new file mode 100644
index 0000000..1964dbd
--- /dev/null
+++ b/services/device/device/describe.go
@@ -0,0 +1,37 @@
+// 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.
+
+package main
+
+import (
+	"fmt"
+
+	"v.io/v23/context"
+	"v.io/v23/services/device"
+	"v.io/x/lib/cmdline"
+	"v.io/x/ref/lib/v23cmd"
+)
+
+var cmdDescribe = &cmdline.Command{
+	Runner:   v23cmd.RunnerFunc(runDescribe),
+	Name:     "describe",
+	Short:    "Describe the device.",
+	Long:     "Describe the device.",
+	ArgsName: "<device>",
+	ArgsLong: `
+<device> is the vanadium object name of the device manager's device service.`,
+}
+
+func runDescribe(ctx *context.T, env *cmdline.Env, args []string) error {
+	if expected, got := 1, len(args); expected != got {
+		return env.UsageErrorf("describe: incorrect number of arguments, expected %d, got %d", expected, got)
+	}
+	deviceName := args[0]
+	if description, err := device.DeviceClient(deviceName).Describe(ctx); err != nil {
+		return fmt.Errorf("Describe failed: %v", err)
+	} else {
+		fmt.Fprintf(env.Stdout, "%+v\n", description)
+	}
+	return nil
+}
diff --git a/services/device/device/devicemanager_mock_test.go b/services/device/device/devicemanager_mock_test.go
index 6298fbb..2d34144 100644
--- a/services/device/device/devicemanager_mock_test.go
+++ b/services/device/device/devicemanager_mock_test.go
@@ -233,7 +233,9 @@
 
 func (*mockDeviceInvoker) Uninstall(*context.T, rpc.ServerCall) error { return nil }
 
-func (i *mockDeviceInvoker) Update(*context.T, rpc.ServerCall) error { return nil }
+func (mdi *mockDeviceInvoker) Update(*context.T, rpc.ServerCall) error {
+	return mdi.simpleCore("Update", "Update")
+}
 
 func (*mockDeviceInvoker) UpdateTo(*context.T, rpc.ServerCall, string) error { return nil }
 
diff --git a/services/device/device/doc.go b/services/device/device/doc.go
index a476a9e..881e621 100644
--- a/services/device/device/doc.go
+++ b/services/device/device/doc.go
@@ -24,9 +24,8 @@
    run           Run the given application instance.
    kill          Kill the given application instance.
    revert        Revert the device manager or application
-   update        Update the device manager or application
-   updateall     Update all installations/instances of an application
-   status        Get application status.
+   update        Update device manager or applications.
+   status        Get device manager or application status.
    debug         Debug the device.
    acl           Tool for setting device manager Permissions
    publish       Publish the given application(s).
@@ -73,6 +72,9 @@
    If set, only consider installations.
  -only-instances=false
    If set, only consider instances.
+ -parallelism=BYKIND
+   Specifies the level of parallelism for the handler execution. One of:
+   BYKIND,FULL,NONE
  -progname=unnamed_app
    Visible name of the application, used in argv[0]
  -rm=false
@@ -291,52 +293,34 @@
 
 Device update
 
-Update the device manager or application
+Update the device manager or application instances and installations
 
 Usage:
-   device update <object>
+   device update <app name patterns...>
 
-<object> is the vanadium object name of the device manager or application
-installation or instance to update.
-
-Device updateall
-
-Given a name that can refer to an app instance or app installation or app or all
-apps on a device, updates all installations and instances under that name
-
-Usage:
-   device updateall <object name>
-
-<object name> is the vanadium object name to update, as follows:
-
-<devicename>/apps/apptitle/installationid/instanceid: updates the given
-instance, killing/restarting it if running
-
-<devicename>/apps/apptitle/installationid: updates the given installation and
-then all its instances
-
-<devicename>/apps/apptitle: updates all installations for the given app
-
-<devicename>/apps: updates all apps on the device
+<app name patterns...> are vanadium object names or glob name patterns
+corresponding to the device manager service, or to application installations and
+instances.
 
 Device status
 
-Get the status of an application installation or instance.
+Get the status of the device manager or application instances and installations.
 
 Usage:
-   device status <app name patterns...>
+   device status <name patterns...>
 
-<app name patterns...> are vanadium object names or glob name patterns
-corresponding to app installations and instances.
+<name patterns...> are vanadium object names or glob name patterns corresponding
+to the device manager service, or to application installations and instances.
 
 Device debug
 
-Debug the device.
+Get internal debug information about application installations and instances.
 
 Usage:
-   device debug <app name>
+   device debug <app name patterns...>
 
-<app name> is the vanadium object name of an app installation or instance.
+<app name patterns...> are vanadium object names or glob name patterns
+corresponding to application installations and instances.
 
 Device acl
 
@@ -423,7 +407,7 @@
    device ls <app name patterns...>
 
 <app name patterns...> are vanadium object names or glob name patterns
-corresponding to app installations and instances.
+corresponding to application installations and instances.
 
 Device help
 
diff --git a/services/device/device/glob.go b/services/device/device/glob.go
index ee14b37..63a0e4c 100644
--- a/services/device/device/glob.go
+++ b/services/device/device/glob.go
@@ -13,6 +13,7 @@
 	"sort"
 	"strings"
 	"sync"
+	"sync/atomic"
 	"time"
 
 	"v.io/x/lib/cmdline"
@@ -34,7 +35,7 @@
 //
 // Typical usage:
 //
-// func myCmdHandler(entry globResult, stdout, stderr io.Writer) error {
+// func myCmdHandler(entry globResult, ctx *context.T, stdout, stderr io.Writer) error {
 //   output := myCmdProcessing(entry)
 //   fmt.Fprintf(stdout, output)
 //   ...
@@ -58,7 +59,7 @@
 //   Runner: globRunner(myCmdHandler)
 //   ...
 // }
-type globHandler func(entry globResult, stdout, stderr io.Writer) error
+type globHandler func(entry globResult, ctx *context.T, stdout, stderr io.Writer) error
 
 func globRunner(handler globHandler) cmdline.Runner {
 	return v23cmd.RunnerFunc(func(ctx *context.T, env *cmdline.Env, args []string) error {
@@ -66,10 +67,32 @@
 	})
 }
 
+type objectKind int
+
+const (
+	applicationInstallationObject objectKind = iota
+	applicationInstanceObject
+	deviceServiceObject
+	sentinelObjectKind
+)
+
+var objectKinds = []objectKind{
+	applicationInstallationObject,
+	applicationInstanceObject,
+	deviceServiceObject,
+}
+
+func init() {
+	// TODO(caprita): Move to glob_test.go once that exists.
+	if len(objectKinds) != int(sentinelObjectKind) {
+		panic(fmt.Sprintf("broken invariant: mismatching number of object kinds"))
+	}
+}
+
 type globResult struct {
-	name       string
-	isInstance bool
-	status     device.Status
+	name   string
+	status device.Status
+	kind   objectKind
 }
 
 type byTypeAndName []globResult
@@ -79,20 +102,12 @@
 
 func (a byTypeAndName) Less(i, j int) bool {
 	r1, r2 := a[i], a[j]
-	if r1.isInstance {
-		if !r2.isInstance {
-			return false
-		}
-	} else if r2.isInstance {
-		return true
+	if r1.kind != r2.kind {
+		return r1.kind < r2.kind
 	}
 	return r1.name < r2.name
 }
 
-// TODO(caprita): Allow clients to control the parallelism.  For example, for
-// updateall, we need to first update the installations before we update the
-// instances.
-
 // run runs the given handler in parallel against each of the results obtained
 // by globbing args, after performing filtering based on type
 // (instance/installation) and state.  No de-duping of results is performed.
@@ -101,13 +116,65 @@
 func run(ctx *context.T, env *cmdline.Env, args []string, handler globHandler) error {
 	results := glob(ctx, env, args)
 	sort.Sort(byTypeAndName(results))
+	results = filterResults(results)
 	stdouts, stderrs := make([]bytes.Buffer, len(results)), make([]bytes.Buffer, len(results))
-	var wg sync.WaitGroup
-	errors := make(chan struct {
-		name string
-		err  error
-	}, len(results))
-	for i, r := range results {
+	var errorCounter uint32 = 0
+	perResult := func(r globResult, index int) {
+		if err := handler(r, ctx, &stdouts[index], &stderrs[index]); err != nil {
+			fmt.Fprintf(&stderrs[index], "ERROR for \"%s\": %v.\n", r.name, err)
+			atomic.AddUint32(&errorCounter, 1)
+		}
+	}
+	// TODO(caprita): Add unit test logic to cover all parallelism options.
+	switch handlerParallelism {
+	case fullParallelism:
+		var wg sync.WaitGroup
+		for i, r := range results {
+			wg.Add(1)
+			go func(r globResult, i int) {
+				perResult(r, i)
+				wg.Done()
+			}(r, i)
+		}
+		wg.Wait()
+	case noParallelism:
+		for i, r := range results {
+			perResult(r, i)
+		}
+	case kindParallelism:
+		processed := 0
+		for _, k := range objectKinds {
+			var wg sync.WaitGroup
+			for i, r := range results {
+				if r.kind != k {
+					continue
+				}
+				wg.Add(1)
+				processed++
+				go func(r globResult, i int) {
+					perResult(r, i)
+					wg.Done()
+				}(r, i)
+			}
+			wg.Wait()
+		}
+		if processed != len(results) {
+			return fmt.Errorf("broken invariant: unhandled object kind")
+		}
+	}
+	for i := range results {
+		io.Copy(env.Stdout, &stdouts[i])
+		io.Copy(env.Stderr, &stderrs[i])
+	}
+	if errorCounter > 0 {
+		return fmt.Errorf("encountered a total of %d error(s)", errorCounter)
+	}
+	return nil
+}
+
+func filterResults(results []globResult) []globResult {
+	var ret []globResult
+	for _, r := range results {
 		switch s := r.status.(type) {
 		case device.StatusInstance:
 			if onlyInstallations || !instanceStateFilter.apply(s.Value.State) {
@@ -118,32 +185,9 @@
 				continue
 			}
 		}
-		wg.Add(1)
-		go func(r globResult, index int) {
-			errors <- struct {
-				name string
-				err  error
-			}{r.name, handler(r, &stdouts[index], &stderrs[index])}
-			wg.Done()
-		}(r, i)
+		ret = append(ret, r)
 	}
-	wg.Wait()
-	for i := range results {
-		io.Copy(env.Stdout, &stdouts[i])
-		io.Copy(env.Stderr, &stderrs[i])
-	}
-	close(errors)
-	nErrors := 0
-	for e := range errors {
-		if e.err != nil {
-			fmt.Fprintf(env.Stderr, "ERROR for %q: %v", e.name, e.err)
-			nErrors++
-		}
-	}
-	if nErrors > 0 {
-		return fmt.Errorf("encountered a total of %d errors", nErrors)
-	}
-	return nil
+	return ret
 }
 
 // TODO(caprita): We need to filter out debug objects under the app instances'
@@ -162,8 +206,19 @@
 		fmt.Fprintf(env.Stderr, "Status(%v) failed: %v\n", name, err)
 		return
 	}
-	_, isInstance := status.(device.StatusInstance)
-	resultsCh <- globResult{name, isInstance, status}
+	var kind objectKind
+	switch status.(type) {
+	case device.StatusInstallation:
+		kind = applicationInstallationObject
+	case device.StatusInstance:
+		kind = applicationInstanceObject
+	case device.StatusDevice:
+		kind = deviceServiceObject
+	default:
+		fmt.Fprintf(env.Stderr, "Status(%v) returned unrecognized status type %T\n", name, status)
+		return
+	}
+	resultsCh <- globResult{name, status, kind}
 }
 
 func globOne(ctx *context.T, env *cmdline.Env, pattern string, resultsCh chan<- globResult) {
@@ -291,11 +346,53 @@
 	return nil
 }
 
+type parallelismFlag int
+
+const (
+	fullParallelism parallelismFlag = iota
+	noParallelism
+	kindParallelism
+	sentinelParallelismFlag
+)
+
+var parallelismStrings = map[parallelismFlag]string{
+	fullParallelism: "FULL",
+	noParallelism:   "NONE",
+	kindParallelism: "BYKIND",
+}
+
+const defaultParallelism = kindParallelism
+
+func init() {
+	if len(parallelismStrings) != int(sentinelParallelismFlag) {
+		panic(fmt.Sprintf("broken invariant: mismatching number of parallelism types"))
+	}
+}
+
+func (p *parallelismFlag) String() string {
+	s, ok := parallelismStrings[*p]
+	if !ok {
+		return "UNKNOWN"
+	}
+	return s
+}
+
+func (p *parallelismFlag) Set(s string) error {
+	for k, v := range parallelismStrings {
+		if s == v {
+			*p = k
+			return nil
+		}
+	}
+	return fmt.Errorf("unrecognized parallelism type: %v", s)
+}
+
 var (
 	instanceStateFilter     instanceStateFlag
 	installationStateFilter installationStateFlag
 	onlyInstances           bool
 	onlyInstallations       bool
+	handlerParallelism      parallelismFlag = defaultParallelism
 )
 
 func init() {
@@ -305,6 +402,12 @@
 	flag.Var(&installationStateFilter, "installation-state", fmt.Sprintf("If non-empty, specifies allowed installation states (all others installations get filtered out). The value of the flag is a comma-separated list of values from among: %v.", device.InstallationStateAll))
 	flag.BoolVar(&onlyInstances, "only-instances", false, "If set, only consider instances.")
 	flag.BoolVar(&onlyInstallations, "only-installations", false, "If set, only consider installations.")
+	var parallelismValues []string
+	for _, v := range parallelismStrings {
+		parallelismValues = append(parallelismValues, v)
+	}
+	sort.Strings(parallelismValues)
+	flag.Var(&handlerParallelism, "parallelism", "Specifies the level of parallelism for the handler execution. One of: "+strings.Join(parallelismValues, ","))
 }
 
 // ResetGlobFlags is meant for tests to restore the values of flag-configured
@@ -314,4 +417,5 @@
 	installationStateFilter = make(installationStateFlag)
 	onlyInstances = false
 	onlyInstallations = false
+	handlerParallelism = defaultParallelism
 }
diff --git a/services/device/device/impl.go b/services/device/device/impl.go
deleted file mode 100644
index 7641456..0000000
--- a/services/device/device/impl.go
+++ /dev/null
@@ -1,339 +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.
-
-package main
-
-import (
-	"encoding/base64"
-	"encoding/json"
-	"fmt"
-	"io"
-
-	"v.io/v23"
-	"v.io/v23/context"
-	"v.io/v23/naming"
-	"v.io/v23/options"
-	"v.io/v23/rpc"
-	"v.io/v23/security"
-	"v.io/v23/services/application"
-	"v.io/v23/services/device"
-	"v.io/x/lib/cmdline"
-	"v.io/x/ref/lib/v23cmd"
-)
-
-type configFlag device.Config
-
-func (c *configFlag) String() string {
-	jsonConfig, _ := json.Marshal(c)
-	return string(jsonConfig)
-}
-func (c *configFlag) Set(s string) error {
-	if err := json.Unmarshal([]byte(s), c); err != nil {
-		return fmt.Errorf("Unmarshal(%v) failed: %v", s, err)
-	}
-	return nil
-}
-
-var configOverride configFlag = configFlag{}
-
-type packagesFlag application.Packages
-
-func (c *packagesFlag) String() string {
-	jsonPackages, _ := json.Marshal(c)
-	return string(jsonPackages)
-}
-func (c *packagesFlag) Set(s string) error {
-	if err := json.Unmarshal([]byte(s), c); err != nil {
-		return fmt.Errorf("Unmarshal(%v) failed: %v", s, err)
-	}
-	return nil
-}
-
-var packagesOverride packagesFlag = packagesFlag{}
-
-func init() {
-	cmdInstall.Flags.Var(&configOverride, "config", "JSON-encoded device.Config object, of the form: '{\"flag1\":\"value1\",\"flag2\":\"value2\"}'")
-	cmdInstall.Flags.Var(&packagesOverride, "packages", "JSON-encoded application.Packages object, of the form: '{\"pkg1\":{\"File\":\"object name 1\"},\"pkg2\":{\"File\":\"object name 2\"}}'")
-}
-
-var cmdInstall = &cmdline.Command{
-	Runner:   v23cmd.RunnerFunc(runInstall),
-	Name:     "install",
-	Short:    "Install the given application.",
-	Long:     "Install the given application and print the name of the new installation.",
-	ArgsName: "<device> <application>",
-	ArgsLong: `
-<device> is the vanadium object name of the device manager's app service.
-
-<application> is the vanadium object name of the application.
-`,
-}
-
-func runInstall(ctx *context.T, env *cmdline.Env, args []string) error {
-	if expected, got := 2, len(args); expected != got {
-		return env.UsageErrorf("install: incorrect number of arguments, expected %d, got %d", expected, got)
-	}
-	deviceName, appName := args[0], args[1]
-	appID, err := device.ApplicationClient(deviceName).Install(ctx, appName, device.Config(configOverride), application.Packages(packagesOverride))
-	// Reset the value for any future invocations of "install" or
-	// "install-local" (we run more than one command per process in unit
-	// tests).
-	configOverride = configFlag{}
-	packagesOverride = packagesFlag{}
-	if err != nil {
-		return fmt.Errorf("Install failed: %v", err)
-	}
-	fmt.Fprintf(env.Stdout, "%s\n", naming.Join(deviceName, appID))
-	return nil
-}
-
-var cmdUninstall = &cmdline.Command{
-	Runner:   v23cmd.RunnerFunc(runUninstall),
-	Name:     "uninstall",
-	Short:    "Uninstall the given application installation.",
-	Long:     "Uninstall the given application installation.",
-	ArgsName: "<installation>",
-	ArgsLong: `
-<installation> is the vanadium object name of the application installation to
-uninstall.
-`,
-}
-
-func runUninstall(ctx *context.T, env *cmdline.Env, args []string) error {
-	if expected, got := 1, len(args); expected != got {
-		return env.UsageErrorf("uninstall: incorrect number of arguments, expected %d, got %d", expected, got)
-	}
-	installName := args[0]
-	if err := device.ApplicationClient(installName).Uninstall(ctx); err != nil {
-		return fmt.Errorf("Uninstall failed: %v", err)
-	}
-	fmt.Fprintf(env.Stdout, "Successfully uninstalled: %q\n", installName)
-	return nil
-}
-
-var cmdInstantiate = &cmdline.Command{
-	Runner:   v23cmd.RunnerFunc(runInstantiate),
-	Name:     "instantiate",
-	Short:    "Create an instance of the given application.",
-	Long:     "Create an instance of the given application, provide it with a blessing, and print the name of the new instance.",
-	ArgsName: "<application installation> <grant extension>",
-	ArgsLong: `
-<application installation> is the vanadium object name of the
-application installation from which to create an instance.
-
-<grant extension> is used to extend the default blessing of the
-current principal when blessing the app instance.`,
-}
-
-type granter struct {
-	rpc.CallOpt
-	extension string
-}
-
-func (g *granter) Grant(ctx *context.T, call security.Call) (security.Blessings, error) {
-	p := call.LocalPrincipal()
-	return p.Bless(call.RemoteBlessings().PublicKey(), p.BlessingStore().Default(), g.extension, security.UnconstrainedUse())
-}
-
-func runInstantiate(ctx *context.T, env *cmdline.Env, args []string) error {
-	if expected, got := 2, len(args); expected != got {
-		return env.UsageErrorf("instantiate: incorrect number of arguments, expected %d, got %d", expected, got)
-	}
-	appInstallation, grant := args[0], args[1]
-
-	ctx, cancel := context.WithCancel(ctx)
-	defer cancel()
-	principal := v23.GetPrincipal(ctx)
-
-	call, err := device.ApplicationClient(appInstallation).Instantiate(ctx)
-	if err != nil {
-		return fmt.Errorf("Instantiate failed: %v", err)
-	}
-	for call.RecvStream().Advance() {
-		switch msg := call.RecvStream().Value().(type) {
-		case device.BlessServerMessageInstancePublicKey:
-			pubKey, err := security.UnmarshalPublicKey(msg.Value)
-			if err != nil {
-				return fmt.Errorf("Instantiate failed: %v", err)
-			}
-			// TODO(caprita,rthellend): Get rid of security.UnconstrainedUse().
-			blessings, err := principal.Bless(pubKey, principal.BlessingStore().Default(), grant, security.UnconstrainedUse())
-			if err != nil {
-				return fmt.Errorf("Instantiate failed: %v", err)
-			}
-			call.SendStream().Send(device.BlessClientMessageAppBlessings{blessings})
-		default:
-			fmt.Fprintf(env.Stderr, "Received unexpected message: %#v\n", msg)
-		}
-	}
-	var instanceID string
-	if instanceID, err = call.Finish(); err != nil {
-		return fmt.Errorf("Instantiate failed: %v", err)
-	}
-	fmt.Fprintf(env.Stdout, "%s\n", naming.Join(appInstallation, instanceID))
-	return nil
-}
-
-var cmdClaim = &cmdline.Command{
-	Runner:   v23cmd.RunnerFunc(runClaim),
-	Name:     "claim",
-	Short:    "Claim the device.",
-	Long:     "Claim the device.",
-	ArgsName: "<device> <grant extension> <pairing token> <device publickey>",
-	ArgsLong: `
-<device> is the vanadium object name of the device manager's device service.
-
-<grant extension> is used to extend the default blessing of the
-current principal when blessing the app instance.
-
-<pairing token> is a token that the device manager expects to be replayed
-during a claim operation on the device.
-
-<device publickey> is the marshalled public key of the device manager we
-are claiming.`,
-}
-
-func runClaim(ctx *context.T, env *cmdline.Env, args []string) error {
-	if expected, max, got := 2, 4, len(args); expected > got || got > max {
-		return env.UsageErrorf("claim: incorrect number of arguments, expected atleast %d (max: %d), got %d", expected, max, got)
-	}
-	deviceName, grant := args[0], args[1]
-	var pairingToken string
-	if len(args) > 2 {
-		pairingToken = args[2]
-	}
-	var serverKeyOpts rpc.CallOpt
-	if len(args) > 3 {
-		marshalledPublicKey, err := base64.URLEncoding.DecodeString(args[3])
-		if err != nil {
-			return fmt.Errorf("Failed to base64 decode publickey: %v", err)
-		}
-		if deviceKey, err := security.UnmarshalPublicKey(marshalledPublicKey); err != nil {
-			return fmt.Errorf("Failed to unmarshal device public key:%v", err)
-		} else {
-			serverKeyOpts = options.ServerPublicKey{deviceKey}
-		}
-	}
-	// Skip server endpoint authorization since an unclaimed device might have
-	// roots that will not be recognized by the claimer.
-	if err := device.ClaimableClient(deviceName).Claim(ctx, pairingToken, &granter{extension: grant}, serverKeyOpts, options.SkipServerEndpointAuthorization{}); err != nil {
-		return err
-	}
-	fmt.Fprintln(env.Stdout, "Successfully claimed.")
-	return nil
-}
-
-var cmdDescribe = &cmdline.Command{
-	Runner:   v23cmd.RunnerFunc(runDescribe),
-	Name:     "describe",
-	Short:    "Describe the device.",
-	Long:     "Describe the device.",
-	ArgsName: "<device>",
-	ArgsLong: `
-<device> is the vanadium object name of the device manager's device service.`,
-}
-
-func runDescribe(ctx *context.T, env *cmdline.Env, args []string) error {
-	if expected, got := 1, len(args); expected != got {
-		return env.UsageErrorf("describe: incorrect number of arguments, expected %d, got %d", expected, got)
-	}
-	deviceName := args[0]
-	if description, err := device.DeviceClient(deviceName).Describe(ctx); err != nil {
-		return fmt.Errorf("Describe failed: %v", err)
-	} else {
-		fmt.Fprintf(env.Stdout, "%+v\n", description)
-	}
-	return nil
-}
-
-var cmdUpdate = &cmdline.Command{
-	Runner:   v23cmd.RunnerFunc(runUpdate),
-	Name:     "update",
-	Short:    "Update the device manager or application",
-	Long:     "Update the device manager or application",
-	ArgsName: "<object>",
-	ArgsLong: `
-<object> is the vanadium object name of the device manager or application
-installation or instance to update.`,
-}
-
-func runUpdate(ctx *context.T, env *cmdline.Env, args []string) error {
-	if expected, got := 1, len(args); expected != got {
-		return env.UsageErrorf("update: incorrect number of arguments, expected %d, got %d", expected, got)
-	}
-	name := args[0]
-	if err := device.ApplicationClient(name).Update(ctx); err != nil {
-		return err
-	}
-	fmt.Fprintln(env.Stdout, "Update successful.")
-	return nil
-}
-
-var cmdRevert = &cmdline.Command{
-	Runner:   v23cmd.RunnerFunc(runRevert),
-	Name:     "revert",
-	Short:    "Revert the device manager or application",
-	Long:     "Revert the device manager or application to its previous version",
-	ArgsName: "<object>",
-	ArgsLong: `
-<object> is the vanadium object name of the device manager or application
-installation to revert.`,
-}
-
-func runRevert(ctx *context.T, env *cmdline.Env, args []string) error {
-	if expected, got := 1, len(args); expected != got {
-		return env.UsageErrorf("revert: incorrect number of arguments, expected %d, got %d", expected, got)
-	}
-	deviceName := args[0]
-	if err := device.ApplicationClient(deviceName).Revert(ctx); err != nil {
-		return err
-	}
-	fmt.Fprintln(env.Stdout, "Revert successful.")
-	return nil
-}
-
-var cmdDebug = &cmdline.Command{
-	Runner:   v23cmd.RunnerFunc(runDebug),
-	Name:     "debug",
-	Short:    "Debug the device.",
-	Long:     "Debug the device.",
-	ArgsName: "<app name>",
-	ArgsLong: `
-<app name> is the vanadium object name of an app installation or instance.`,
-}
-
-func runDebug(ctx *context.T, env *cmdline.Env, args []string) error {
-	if expected, got := 1, len(args); expected != got {
-		return env.UsageErrorf("debug: incorrect number of arguments, expected %d, got %d", expected, got)
-	}
-	appName := args[0]
-	if description, err := device.DeviceClient(appName).Debug(ctx); err != nil {
-		return fmt.Errorf("Debug failed: %v", err)
-	} else {
-		fmt.Fprintf(env.Stdout, "%v\n", description)
-	}
-	return nil
-}
-
-var cmdStatus = &cmdline.Command{
-	Runner:   globRunner(runStatus),
-	Name:     "status",
-	Short:    "Get application status.",
-	Long:     "Get the status of an application installation or instance.",
-	ArgsName: "<app name patterns...>",
-	ArgsLong: `
-<app name patterns...> are vanadium object names or glob name patterns corresponding to app installations and instances.`,
-}
-
-func runStatus(entry globResult, stdout, stderr io.Writer) error {
-	switch s := entry.status.(type) {
-	case device.StatusInstance:
-		fmt.Fprintf(stdout, "Instance %v [State:%v,Version:%v]\n", entry.name, s.Value.State, s.Value.Version)
-	case device.StatusInstallation:
-		fmt.Fprintf(stdout, "Installation %v [State:%v,Version:%v]\n", entry.name, s.Value.State, s.Value.Version)
-	default:
-		return fmt.Errorf("Status returned unknown type: %T", s)
-	}
-	return nil
-}
diff --git a/services/device/device/impl_test.go b/services/device/device/impl_test.go
deleted file mode 100644
index db1bd97..0000000
--- a/services/device/device/impl_test.go
+++ /dev/null
@@ -1,556 +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.
-
-package main_test
-
-import (
-	"bytes"
-	"encoding/base64"
-	"encoding/json"
-	"fmt"
-	"reflect"
-	"strings"
-	"testing"
-
-	"v.io/v23"
-	"v.io/v23/naming"
-	"v.io/v23/services/application"
-	"v.io/v23/services/device"
-	"v.io/v23/verror"
-	"v.io/x/lib/cmdline"
-	"v.io/x/ref/lib/security"
-	"v.io/x/ref/lib/v23cmd"
-	"v.io/x/ref/test"
-
-	cmd_device "v.io/x/ref/services/device/device"
-)
-
-//go:generate v23 test generate
-
-func TestListCommand(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
-	defer shutdown()
-
-	tapes := newTapeMap()
-	server, endpoint, err := startServer(t, ctx, tapes)
-	if err != nil {
-		return
-	}
-	defer stopServer(t, server)
-
-	// Setup the command-line.
-	cmd := cmd_device.CmdRoot
-	var stdout, stderr bytes.Buffer
-	env := &cmdline.Env{Stdout: &stdout, Stderr: &stderr}
-	deviceName := naming.JoinAddressName(endpoint.String(), "")
-
-	rootTape := tapes.forSuffix("")
-	// Test the 'list' command.
-	rootTape.SetResponses(ListAssociationResponse{
-		na: []device.Association{
-			{
-				"root/self",
-				"alice_self_account",
-			},
-			{
-				"root/other",
-				"alice_other_account",
-			},
-		},
-		err: nil,
-	})
-
-	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"associate", "list", deviceName}); err != nil {
-		t.Fatalf("%v", err)
-	}
-	if expected, got := "root/self alice_self_account\nroot/other alice_other_account", strings.TrimSpace(stdout.String()); got != expected {
-		t.Fatalf("Unexpected output from list. Got %q, expected %q", got, expected)
-	}
-	if got, expected := rootTape.Play(), []interface{}{"ListAssociations"}; !reflect.DeepEqual(expected, got) {
-		t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
-	}
-	rootTape.Rewind()
-	stdout.Reset()
-
-	// Test list with bad parameters.
-	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"associate", "list", deviceName, "hello"}); err == nil {
-		t.Fatalf("wrongly failed to receive a non-nil error.")
-	}
-	if got, expected := len(rootTape.Play()), 0; got != expected {
-		t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
-	}
-}
-
-func TestAddCommand(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
-	defer shutdown()
-
-	tapes := newTapeMap()
-	server, endpoint, err := startServer(t, ctx, tapes)
-	if err != nil {
-		return
-	}
-	defer stopServer(t, server)
-
-	// Setup the command-line.
-	cmd := cmd_device.CmdRoot
-	var stdout, stderr bytes.Buffer
-	env := &cmdline.Env{Stdout: &stdout, Stderr: &stderr}
-	deviceName := naming.JoinAddressName(endpoint.String(), "")
-
-	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"add", "one"}); err == nil {
-		t.Fatalf("wrongly failed to receive a non-nil error.")
-	}
-	rootTape := tapes.forSuffix("")
-	if got, expected := len(rootTape.Play()), 0; got != expected {
-		t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
-	}
-	rootTape.Rewind()
-	stdout.Reset()
-
-	rootTape.SetResponses(nil)
-	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"associate", "add", deviceName, "alice", "root/self"}); err != nil {
-		t.Fatalf("%v", err)
-	}
-	expected := []interface{}{
-		AddAssociationStimulus{"AssociateAccount", []string{"root/self"}, "alice"},
-	}
-	if got := rootTape.Play(); !reflect.DeepEqual(expected, got) {
-		t.Errorf("unexpected result. Got %v want %v", got, expected)
-	}
-	rootTape.Rewind()
-	stdout.Reset()
-
-	rootTape.SetResponses(nil)
-	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"associate", "add", deviceName, "alice", "root/other", "root/self"}); err != nil {
-		t.Fatalf("%v", err)
-	}
-	expected = []interface{}{
-		AddAssociationStimulus{"AssociateAccount", []string{"root/other", "root/self"}, "alice"},
-	}
-	if got := rootTape.Play(); !reflect.DeepEqual(expected, got) {
-		t.Errorf("unexpected result. Got %v want %v", got, expected)
-	}
-}
-
-func TestRemoveCommand(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
-	defer shutdown()
-
-	tapes := newTapeMap()
-	server, endpoint, err := startServer(t, ctx, tapes)
-	if err != nil {
-		return
-	}
-	defer stopServer(t, server)
-
-	// Setup the command-line.
-	cmd := cmd_device.CmdRoot
-	var stdout, stderr bytes.Buffer
-	env := &cmdline.Env{Stdout: &stdout, Stderr: &stderr}
-	deviceName := naming.JoinAddressName(endpoint.String(), "")
-
-	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"remove", "one"}); err == nil {
-		t.Fatalf("wrongly failed to receive a non-nil error.")
-	}
-	rootTape := tapes.forSuffix("")
-	if got, expected := len(rootTape.Play()), 0; got != expected {
-		t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
-	}
-	rootTape.Rewind()
-	stdout.Reset()
-
-	rootTape.SetResponses(nil)
-	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"associate", "remove", deviceName, "root/self"}); err != nil {
-		t.Fatalf("%v", err)
-	}
-	expected := []interface{}{
-		AddAssociationStimulus{"AssociateAccount", []string{"root/self"}, ""},
-	}
-	if got := rootTape.Play(); !reflect.DeepEqual(expected, got) {
-		t.Errorf("unexpected result. Got %v want %v", got, expected)
-	}
-}
-
-func TestInstallCommand(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
-	defer shutdown()
-
-	tapes := newTapeMap()
-	server, endpoint, err := startServer(t, ctx, tapes)
-	if err != nil {
-		return
-	}
-	defer stopServer(t, server)
-
-	// Setup the command-line.
-	cmd := cmd_device.CmdRoot
-	var stdout, stderr bytes.Buffer
-	env := &cmdline.Env{Stdout: &stdout, Stderr: &stderr}
-	deviceName := naming.JoinAddressName(endpoint.String(), "")
-	appId := "myBestAppID"
-	cfg := device.Config{"someflag": "somevalue"}
-	pkg := application.Packages{"pkg": application.SignedFile{File: "somename"}}
-	rootTape := tapes.forSuffix("")
-	for i, c := range []struct {
-		args         []string
-		config       device.Config
-		packages     application.Packages
-		shouldErr    bool
-		tapeResponse interface{}
-		expectedTape interface{}
-	}{
-		{
-			[]string{"blech"},
-			nil,
-			nil,
-			true,
-			nil,
-			nil,
-		},
-		{
-			[]string{"blech1", "blech2", "blech3", "blech4"},
-			nil,
-			nil,
-			true,
-			nil,
-			nil,
-		},
-		{
-			[]string{deviceName, appNameNoFetch, "not-valid-json"},
-			nil,
-			nil,
-			true,
-			nil,
-			nil,
-		},
-		{
-			[]string{deviceName, appNameNoFetch},
-			nil,
-			nil,
-			false,
-			InstallResponse{appId, nil},
-			InstallStimulus{"Install", appNameNoFetch, nil, nil, application.Envelope{}, nil},
-		},
-		{
-			[]string{deviceName, appNameNoFetch},
-			cfg,
-			pkg,
-			false,
-			InstallResponse{appId, nil},
-			InstallStimulus{"Install", appNameNoFetch, cfg, pkg, application.Envelope{}, nil},
-		},
-	} {
-		rootTape.SetResponses(c.tapeResponse)
-		if c.config != nil {
-			jsonConfig, err := json.Marshal(c.config)
-			if err != nil {
-				t.Fatalf("test case %d: Marshal(%v) failed: %v", i, c.config, err)
-			}
-			c.args = append([]string{fmt.Sprintf("--config=%s", string(jsonConfig))}, c.args...)
-		}
-		if c.packages != nil {
-			jsonPackages, err := json.Marshal(c.packages)
-			if err != nil {
-				t.Fatalf("test case %d: Marshal(%v) failed: %v", i, c.packages, err)
-			}
-			c.args = append([]string{fmt.Sprintf("--packages=%s", string(jsonPackages))}, c.args...)
-		}
-		c.args = append([]string{"install"}, c.args...)
-		err := v23cmd.ParseAndRunForTest(cmd, ctx, env, c.args)
-		if c.shouldErr {
-			if err == nil {
-				t.Fatalf("test case %d: wrongly failed to receive a non-nil error.", i)
-			}
-			if got, expected := len(rootTape.Play()), 0; got != expected {
-				t.Errorf("test case %d: invalid call sequence. Got %v, want %v", i, got, expected)
-			}
-		} else {
-			if err != nil {
-				t.Fatalf("test case %d: %v", i, err)
-			}
-			if expected, got := naming.Join(deviceName, appId), strings.TrimSpace(stdout.String()); got != expected {
-				t.Fatalf("test case %d: Unexpected output from Install. Got %q, expected %q", i, got, expected)
-			}
-			if got, expected := rootTape.Play(), []interface{}{c.expectedTape}; !reflect.DeepEqual(expected, got) {
-				t.Errorf("test case %d: invalid call sequence. Got %#v, want %#v", i, got, expected)
-			}
-		}
-		rootTape.Rewind()
-		stdout.Reset()
-	}
-}
-
-func TestClaimCommand(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
-	defer shutdown()
-
-	tapes := newTapeMap()
-	server, endpoint, err := startServer(t, ctx, tapes)
-	if err != nil {
-		return
-	}
-	defer stopServer(t, server)
-
-	// Setup the command-line.
-	cmd := cmd_device.CmdRoot
-	var stdout, stderr bytes.Buffer
-	env := &cmdline.Env{Stdout: &stdout, Stderr: &stderr}
-	deviceName := naming.JoinAddressName(endpoint.String(), "")
-	deviceKey, err := v23.GetPrincipal(ctx).PublicKey().MarshalBinary()
-	if err != nil {
-		t.Fatalf("Failed to marshal principal public key: %v", err)
-	}
-
-	// Confirm that we correctly enforce the number of arguments.
-	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"claim", "nope"}); err == nil {
-		t.Fatalf("wrongly failed to receive a non-nil error.")
-	}
-	if expected, got := "ERROR: claim: incorrect number of arguments, expected atleast 2 (max: 4), got 1", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
-		t.Fatalf("Unexpected output from claim. Got %q, expected prefix %q", got, expected)
-	}
-	stdout.Reset()
-	stderr.Reset()
-	rootTape := tapes.forSuffix("")
-	rootTape.Rewind()
-
-	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"claim", "nope", "nope", "nope", "nope", "nope"}); err == nil {
-		t.Fatalf("wrongly failed to receive a non-nil error.")
-	}
-	if expected, got := "ERROR: claim: incorrect number of arguments, expected atleast 2 (max: 4), got 5", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
-		t.Fatalf("Unexpected output from claim. Got %q, expected prefix %q", got, expected)
-	}
-	stdout.Reset()
-	stderr.Reset()
-	rootTape.Rewind()
-
-	// Incorrect operation
-	var pairingToken string
-	var deviceKeyWrong []byte
-	if publicKey, _, err := security.NewPrincipalKey(); err != nil {
-		t.Fatalf("NewPrincipalKey failed: %v", err)
-	} else {
-		if deviceKeyWrong, err = publicKey.MarshalBinary(); err != nil {
-			t.Fatalf("Failed to marshal principal public key: %v", err)
-		}
-	}
-	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"claim", deviceName, "grant", pairingToken, base64.URLEncoding.EncodeToString(deviceKeyWrong)}); verror.ErrorID(err) != verror.ErrNotTrusted.ID {
-		t.Fatalf("wrongly failed to receive correct error on claim with incorrect device key:%v id:%v", err, verror.ErrorID(err))
-	}
-	stdout.Reset()
-	stderr.Reset()
-	rootTape.Rewind()
-
-	// Correct operation.
-	rootTape.SetResponses(nil)
-	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"claim", deviceName, "grant", pairingToken, base64.URLEncoding.EncodeToString(deviceKey)}); err != nil {
-		t.Fatalf("Claim(%s, %s, %s) failed: %v", deviceName, "grant", pairingToken, err)
-	}
-	if got, expected := len(rootTape.Play()), 1; got != expected {
-		t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
-	}
-	if expected, got := "Successfully claimed.", strings.TrimSpace(stdout.String()); !strings.HasPrefix(got, expected) {
-		t.Fatalf("Unexpected output from claim. Got %q, expected prefix %q", got, expected)
-	}
-	expected := []interface{}{
-		"Claim",
-	}
-	if got := rootTape.Play(); !reflect.DeepEqual(expected, got) {
-		t.Errorf("unexpected result. Got %v want %v", got, expected)
-	}
-	rootTape.Rewind()
-	stdout.Reset()
-	stderr.Reset()
-
-	// Error operation.
-	rootTape.SetResponses(verror.New(errOops, nil))
-	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"claim", deviceName, "grant", pairingToken}); err == nil {
-		t.Fatal("claim() failed to detect error:", err)
-	}
-	expected = []interface{}{
-		"Claim",
-	}
-	if got := rootTape.Play(); !reflect.DeepEqual(expected, got) {
-		t.Errorf("unexpected result. Got %v want %v", got, expected)
-	}
-}
-
-func TestInstantiateCommand(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
-	defer shutdown()
-
-	tapes := newTapeMap()
-	server, endpoint, err := startServer(t, ctx, tapes)
-	if err != nil {
-		return
-	}
-	defer stopServer(t, server)
-
-	// Setup the command-line.
-	cmd := cmd_device.CmdRoot
-	var stdout, stderr bytes.Buffer
-	env := &cmdline.Env{Stdout: &stdout, Stderr: &stderr}
-	appName := naming.JoinAddressName(endpoint.String(), "")
-
-	// Confirm that we correctly enforce the number of arguments.
-	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"instantiate", "nope"}); err == nil {
-		t.Fatalf("wrongly failed to receive a non-nil error.")
-	}
-	if expected, got := "ERROR: instantiate: incorrect number of arguments, expected 2, got 1", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
-		t.Fatalf("Unexpected output from instantiate. Got %q, expected prefix %q", got, expected)
-	}
-	stdout.Reset()
-	stderr.Reset()
-	rootTape := tapes.forSuffix("")
-	rootTape.Rewind()
-
-	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"instantiate", "nope", "nope", "nope"}); err == nil {
-		t.Fatalf("wrongly failed to receive a non-nil error.")
-	}
-	if expected, got := "ERROR: instantiate: incorrect number of arguments, expected 2, got 3", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
-		t.Fatalf("Unexpected output from instantiate. Got %q, expected prefix %q", got, expected)
-	}
-	stdout.Reset()
-	stderr.Reset()
-	rootTape.Rewind()
-
-	// Correct operation.
-	rootTape.SetResponses(InstantiateResponse{
-		err:        nil,
-		instanceID: "app1",
-	})
-	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"instantiate", appName, "grant"}); err != nil {
-		t.Fatalf("instantiate %s %s failed: %v", appName, "grant", err)
-	}
-
-	b := new(bytes.Buffer)
-	fmt.Fprintf(b, "%s", appName+"/app1")
-	if expected, got := b.String(), strings.TrimSpace(stdout.String()); !strings.HasPrefix(got, expected) {
-		t.Fatalf("Unexpected output from instantiate. Got %q, expected prefix %q", got, expected)
-	}
-	expected := []interface{}{
-		"Instantiate",
-	}
-	if got := rootTape.Play(); !reflect.DeepEqual(expected, got) {
-		t.Errorf("unexpected result. Got %v want %v", got, expected)
-	}
-	rootTape.Rewind()
-	stdout.Reset()
-	stderr.Reset()
-
-	// Error operation.
-	rootTape.SetResponses(InstantiateResponse{
-		verror.New(errOops, nil),
-		"",
-	})
-	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"instantiate", appName, "grant"}); err == nil {
-		t.Fatalf("instantiate failed to detect error")
-	}
-	expected = []interface{}{
-		"Instantiate",
-	}
-	if got := rootTape.Play(); !reflect.DeepEqual(expected, got) {
-		t.Errorf("unexpected result. Got %v want %v", got, expected)
-	}
-}
-
-func TestDebugCommand(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
-	defer shutdown()
-	tapes := newTapeMap()
-	server, endpoint, err := startServer(t, ctx, tapes)
-	if err != nil {
-		return
-	}
-	defer stopServer(t, server)
-	// Setup the command-line.
-	cmd := cmd_device.CmdRoot
-	var stdout, stderr bytes.Buffer
-	env := &cmdline.Env{Stdout: &stdout, Stderr: &stderr}
-	appName := naming.JoinAddressName(endpoint.String(), "")
-
-	debugMessage := "the secrets of the universe, revealed"
-	rootTape := tapes.forSuffix("")
-	rootTape.SetResponses(debugMessage)
-	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"debug", appName}); err != nil {
-		t.Fatalf("%v", err)
-	}
-	if expected, got := debugMessage, strings.TrimSpace(stdout.String()); got != expected {
-		t.Fatalf("Unexpected output from debug. Got %q, expected %q", got, expected)
-	}
-	if got, expected := rootTape.Play(), []interface{}{"Debug"}; !reflect.DeepEqual(expected, got) {
-		t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
-	}
-}
-
-var (
-	installationUninstalled = device.StatusInstallation{device.InstallationStatus{
-		State:   device.InstallationStateUninstalled,
-		Version: "director's cut",
-	}}
-	installationActive = device.StatusInstallation{device.InstallationStatus{
-		State:   device.InstallationStateActive,
-		Version: "extended cut",
-	}}
-	instanceUpdating = device.StatusInstance{device.InstanceStatus{
-		State:   device.InstanceStateUpdating,
-		Version: "theatrical version",
-	}}
-	instanceRunning = device.StatusInstance{device.InstanceStatus{
-		State:   device.InstanceStateRunning,
-		Version: "special edition",
-	}}
-	instanceNotRunning = device.StatusInstance{device.InstanceStatus{
-		State:   device.InstanceStateNotRunning,
-		Version: "special edition",
-	}}
-)
-
-func TestStatusCommand(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
-	defer shutdown()
-	tapes := newTapeMap()
-	server, endpoint, err := startServer(t, ctx, tapes)
-	if err != nil {
-		return
-	}
-	defer stopServer(t, server)
-
-	cmd := cmd_device.CmdRoot
-	globName := naming.JoinAddressName(endpoint.String(), "glob")
-	appName := naming.JoinAddressName(endpoint.String(), "app")
-
-	rootTape, appTape := tapes.forSuffix(""), tapes.forSuffix("app")
-	for _, c := range []struct {
-		tapeResponse device.Status
-		expected     string
-	}{
-		{
-			installationUninstalled,
-			fmt.Sprintf("Installation %v [State:Uninstalled,Version:director's cut]", appName),
-		},
-		{
-			instanceUpdating,
-			fmt.Sprintf("Instance %v [State:Updating,Version:theatrical version]", appName),
-		},
-	} {
-		var stdout, stderr bytes.Buffer
-		env := &cmdline.Env{Stdout: &stdout, Stderr: &stderr}
-		tapes.rewind()
-		rootTape.SetResponses(GlobResponse{[]string{"app"}})
-		appTape.SetResponses(c.tapeResponse)
-		if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"status", globName}); err != nil {
-			t.Errorf("%v", err)
-		}
-		if expected, got := c.expected, strings.TrimSpace(stdout.String()); got != expected {
-			t.Errorf("Unexpected output from status. Got %q, expected %q", got, expected)
-		}
-		if got, expected := rootTape.Play(), []interface{}{GlobStimulus{"glob"}}; !reflect.DeepEqual(expected, got) {
-			t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
-		}
-		if got, expected := appTape.Play(), []interface{}{"Status"}; !reflect.DeepEqual(expected, got) {
-			t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
-		}
-	}
-}
diff --git a/services/device/device/install.go b/services/device/device/install.go
new file mode 100644
index 0000000..b9f055f
--- /dev/null
+++ b/services/device/device/install.go
@@ -0,0 +1,83 @@
+// 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.
+
+package main
+
+import (
+	"encoding/json"
+	"fmt"
+
+	"v.io/v23/context"
+	"v.io/v23/naming"
+	"v.io/v23/services/application"
+	"v.io/v23/services/device"
+	"v.io/x/lib/cmdline"
+	"v.io/x/ref/lib/v23cmd"
+)
+
+type configFlag device.Config
+
+func (c *configFlag) String() string {
+	jsonConfig, _ := json.Marshal(c)
+	return string(jsonConfig)
+}
+func (c *configFlag) Set(s string) error {
+	if err := json.Unmarshal([]byte(s), c); err != nil {
+		return fmt.Errorf("Unmarshal(%v) failed: %v", s, err)
+	}
+	return nil
+}
+
+var configOverride configFlag = configFlag{}
+
+type packagesFlag application.Packages
+
+func (c *packagesFlag) String() string {
+	jsonPackages, _ := json.Marshal(c)
+	return string(jsonPackages)
+}
+func (c *packagesFlag) Set(s string) error {
+	if err := json.Unmarshal([]byte(s), c); err != nil {
+		return fmt.Errorf("Unmarshal(%v) failed: %v", s, err)
+	}
+	return nil
+}
+
+var packagesOverride packagesFlag = packagesFlag{}
+
+var cmdInstall = &cmdline.Command{
+	Runner:   v23cmd.RunnerFunc(runInstall),
+	Name:     "install",
+	Short:    "Install the given application.",
+	Long:     "Install the given application and print the name of the new installation.",
+	ArgsName: "<device> <application>",
+	ArgsLong: `
+<device> is the vanadium object name of the device manager's app service.
+
+<application> is the vanadium object name of the application.
+`,
+}
+
+func init() {
+	cmdInstall.Flags.Var(&configOverride, "config", "JSON-encoded device.Config object, of the form: '{\"flag1\":\"value1\",\"flag2\":\"value2\"}'")
+	cmdInstall.Flags.Var(&packagesOverride, "packages", "JSON-encoded application.Packages object, of the form: '{\"pkg1\":{\"File\":\"object name 1\"},\"pkg2\":{\"File\":\"object name 2\"}}'")
+}
+
+func runInstall(ctx *context.T, env *cmdline.Env, args []string) error {
+	if expected, got := 2, len(args); expected != got {
+		return env.UsageErrorf("install: incorrect number of arguments, expected %d, got %d", expected, got)
+	}
+	deviceName, appName := args[0], args[1]
+	appID, err := device.ApplicationClient(deviceName).Install(ctx, appName, device.Config(configOverride), application.Packages(packagesOverride))
+	// Reset the value for any future invocations of "install" or
+	// "install-local" (we run more than one command per process in unit
+	// tests).
+	configOverride = configFlag{}
+	packagesOverride = packagesFlag{}
+	if err != nil {
+		return fmt.Errorf("Install failed: %v", err)
+	}
+	fmt.Fprintf(env.Stdout, "%s\n", naming.Join(deviceName, appID))
+	return nil
+}
diff --git a/services/device/device/install_test.go b/services/device/device/install_test.go
new file mode 100644
index 0000000..216916d
--- /dev/null
+++ b/services/device/device/install_test.go
@@ -0,0 +1,132 @@
+// 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.
+
+package main_test
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"reflect"
+	"strings"
+	"testing"
+
+	"v.io/v23/naming"
+	"v.io/v23/services/application"
+	"v.io/v23/services/device"
+	"v.io/x/lib/cmdline"
+	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/test"
+
+	cmd_device "v.io/x/ref/services/device/device"
+)
+
+func TestInstallCommand(t *testing.T) {
+	ctx, shutdown := test.V23Init()
+	defer shutdown()
+
+	tapes := newTapeMap()
+	server, endpoint, err := startServer(t, ctx, tapes)
+	if err != nil {
+		return
+	}
+	defer stopServer(t, server)
+
+	// Setup the command-line.
+	cmd := cmd_device.CmdRoot
+	var stdout, stderr bytes.Buffer
+	env := &cmdline.Env{Stdout: &stdout, Stderr: &stderr}
+	deviceName := naming.JoinAddressName(endpoint.String(), "")
+	appId := "myBestAppID"
+	cfg := device.Config{"someflag": "somevalue"}
+	pkg := application.Packages{"pkg": application.SignedFile{File: "somename"}}
+	rootTape := tapes.forSuffix("")
+	for i, c := range []struct {
+		args         []string
+		config       device.Config
+		packages     application.Packages
+		shouldErr    bool
+		tapeResponse interface{}
+		expectedTape interface{}
+	}{
+		{
+			[]string{"blech"},
+			nil,
+			nil,
+			true,
+			nil,
+			nil,
+		},
+		{
+			[]string{"blech1", "blech2", "blech3", "blech4"},
+			nil,
+			nil,
+			true,
+			nil,
+			nil,
+		},
+		{
+			[]string{deviceName, appNameNoFetch, "not-valid-json"},
+			nil,
+			nil,
+			true,
+			nil,
+			nil,
+		},
+		{
+			[]string{deviceName, appNameNoFetch},
+			nil,
+			nil,
+			false,
+			InstallResponse{appId, nil},
+			InstallStimulus{"Install", appNameNoFetch, nil, nil, application.Envelope{}, nil},
+		},
+		{
+			[]string{deviceName, appNameNoFetch},
+			cfg,
+			pkg,
+			false,
+			InstallResponse{appId, nil},
+			InstallStimulus{"Install", appNameNoFetch, cfg, pkg, application.Envelope{}, nil},
+		},
+	} {
+		rootTape.SetResponses(c.tapeResponse)
+		if c.config != nil {
+			jsonConfig, err := json.Marshal(c.config)
+			if err != nil {
+				t.Fatalf("test case %d: Marshal(%v) failed: %v", i, c.config, err)
+			}
+			c.args = append([]string{fmt.Sprintf("--config=%s", string(jsonConfig))}, c.args...)
+		}
+		if c.packages != nil {
+			jsonPackages, err := json.Marshal(c.packages)
+			if err != nil {
+				t.Fatalf("test case %d: Marshal(%v) failed: %v", i, c.packages, err)
+			}
+			c.args = append([]string{fmt.Sprintf("--packages=%s", string(jsonPackages))}, c.args...)
+		}
+		c.args = append([]string{"install"}, c.args...)
+		err := v23cmd.ParseAndRunForTest(cmd, ctx, env, c.args)
+		if c.shouldErr {
+			if err == nil {
+				t.Fatalf("test case %d: wrongly failed to receive a non-nil error.", i)
+			}
+			if got, expected := len(rootTape.Play()), 0; got != expected {
+				t.Errorf("test case %d: invalid call sequence. Got %v, want %v", i, got, expected)
+			}
+		} else {
+			if err != nil {
+				t.Fatalf("test case %d: %v", i, err)
+			}
+			if expected, got := naming.Join(deviceName, appId), strings.TrimSpace(stdout.String()); got != expected {
+				t.Fatalf("test case %d: Unexpected output from Install. Got %q, expected %q", i, got, expected)
+			}
+			if got, expected := rootTape.Play(), []interface{}{c.expectedTape}; !reflect.DeepEqual(expected, got) {
+				t.Errorf("test case %d: invalid call sequence. Got %#v, want %#v", i, got, expected)
+			}
+		}
+		rootTape.Rewind()
+		stdout.Reset()
+	}
+}
diff --git a/services/device/device/instance_impl.go b/services/device/device/instance_impl.go
deleted file mode 100644
index 779f9bd..0000000
--- a/services/device/device/instance_impl.go
+++ /dev/null
@@ -1,88 +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.
-
-package main
-
-// Commands to modify instance.
-
-import (
-	"fmt"
-	"time"
-
-	"v.io/v23/context"
-	"v.io/v23/services/device"
-	"v.io/x/lib/cmdline"
-	"v.io/x/ref/lib/v23cmd"
-)
-
-var cmdDelete = &cmdline.Command{
-	Runner:   v23cmd.RunnerFunc(runDelete),
-	Name:     "delete",
-	Short:    "Delete the given application instance.",
-	Long:     "Delete the given application instance.",
-	ArgsName: "<app instance>",
-	ArgsLong: `
-<app instance> is the vanadium object name of the application instance to delete.`,
-}
-
-func runDelete(ctx *context.T, env *cmdline.Env, args []string) error {
-	if expected, got := 1, len(args); expected != got {
-		return env.UsageErrorf("delete: incorrect number of arguments, expected %d, got %d", expected, got)
-	}
-	appName := args[0]
-
-	if err := device.ApplicationClient(appName).Delete(ctx); err != nil {
-		return fmt.Errorf("Delete failed: %v", err)
-	}
-	fmt.Fprintf(env.Stdout, "Delete succeeded\n")
-	return nil
-}
-
-const killDeadline = 10 * time.Second
-
-var cmdKill = &cmdline.Command{
-	Runner:   v23cmd.RunnerFunc(runKill),
-	Name:     "kill",
-	Short:    "Kill the given application instance.",
-	Long:     "Kill the given application instance.",
-	ArgsName: "<app instance>",
-	ArgsLong: `
-<app instance> is the vanadium object name of the application instance to kill.`,
-}
-
-func runKill(ctx *context.T, env *cmdline.Env, args []string) error {
-	if expected, got := 1, len(args); expected != got {
-		return env.UsageErrorf("kill: incorrect number of arguments, expected %d, got %d", expected, got)
-	}
-	appName := args[0]
-
-	if err := device.ApplicationClient(appName).Kill(ctx, killDeadline); err != nil {
-		return fmt.Errorf("Kill failed: %v", err)
-	}
-	fmt.Fprintf(env.Stdout, "Kill succeeded\n")
-	return nil
-}
-
-var cmdRun = &cmdline.Command{
-	Runner:   v23cmd.RunnerFunc(runRun),
-	Name:     "run",
-	Short:    "Run the given application instance.",
-	Long:     "Run the given application instance.",
-	ArgsName: "<app instance>",
-	ArgsLong: `
-<app instance> is the vanadium object name of the application instance to run.`,
-}
-
-func runRun(ctx *context.T, env *cmdline.Env, args []string) error {
-	if expected, got := 1, len(args); expected != got {
-		return env.UsageErrorf("run: incorrect number of arguments, expected %d, got %d", expected, got)
-	}
-	appName := args[0]
-
-	if err := device.ApplicationClient(appName).Run(ctx); err != nil {
-		return fmt.Errorf("Run failed: %v,\nView log with:\n debug logs read `debug glob %s/logs/STDERR-*`", err, appName)
-	}
-	fmt.Fprintf(env.Stdout, "Run succeeded\n")
-	return nil
-}
diff --git a/services/device/device/instance_impl_test.go b/services/device/device/instance_impl_test.go
deleted file mode 100644
index 1397cca..0000000
--- a/services/device/device/instance_impl_test.go
+++ /dev/null
@@ -1,163 +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.
-
-package main_test
-
-import (
-	"bytes"
-	"reflect"
-	"strings"
-	"testing"
-	"time"
-
-	"v.io/v23/naming"
-	"v.io/v23/verror"
-	"v.io/x/lib/cmdline"
-	"v.io/x/ref/lib/v23cmd"
-	"v.io/x/ref/test"
-
-	cmd_device "v.io/x/ref/services/device/device"
-)
-
-func TestKillCommand(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
-	defer shutdown()
-
-	tapes := newTapeMap()
-	server, endpoint, err := startServer(t, ctx, tapes)
-	if err != nil {
-		return
-	}
-	defer stopServer(t, server)
-
-	// Setup the command-line.
-	cmd := cmd_device.CmdRoot
-	var stdout, stderr bytes.Buffer
-	env := &cmdline.Env{Stdout: &stdout, Stderr: &stderr}
-	appName := naming.JoinAddressName(endpoint.String(), "appname")
-
-	// Confirm that we correctly enforce the number of arguments.
-	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"kill"}); err == nil {
-		t.Fatalf("wrongly failed to receive a non-nil error.")
-	}
-	if expected, got := "ERROR: kill: incorrect number of arguments, expected 1, got 0", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
-		t.Fatalf("Unexpected output from kill. Got %q, expected prefix %q", got, expected)
-	}
-	stdout.Reset()
-	stderr.Reset()
-	appTape := tapes.forSuffix("appname")
-	appTape.Rewind()
-
-	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"kill", "nope", "nope"}); err == nil {
-		t.Fatalf("wrongly failed to receive a non-nil error.")
-	}
-	if expected, got := "ERROR: kill: incorrect number of arguments, expected 1, got 2", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
-		t.Fatalf("Unexpected output from kill. Got %q, expected prefix %q", got, expected)
-	}
-	stdout.Reset()
-	stderr.Reset()
-	appTape.Rewind()
-
-	// Test the 'kill' command.
-	appTape.SetResponses(nil)
-
-	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"kill", appName}); err != nil {
-		t.Fatalf("kill failed when it shouldn't: %v", err)
-	}
-	if expected, got := "Kill succeeded", strings.TrimSpace(stdout.String()); got != expected {
-		t.Fatalf("Unexpected output from list. Got %q, expected %q", got, expected)
-	}
-	expected := []interface{}{
-		KillStimulus{"Kill", 10 * time.Second},
-	}
-	if got := appTape.Play(); !reflect.DeepEqual(expected, got) {
-		t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
-	}
-	appTape.Rewind()
-	stderr.Reset()
-	stdout.Reset()
-
-	// Test kill with bad parameters.
-	appTape.SetResponses(verror.New(errOops, nil))
-	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"kill", appName}); err == nil {
-		t.Fatalf("wrongly didn't receive a non-nil error.")
-	}
-	// expected the same.
-	if got := appTape.Play(); !reflect.DeepEqual(expected, got) {
-		t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
-	}
-}
-
-func testHelper(t *testing.T, lower, upper string) {
-	ctx, shutdown := test.InitForTest()
-	defer shutdown()
-
-	tapes := newTapeMap()
-	server, endpoint, err := startServer(t, ctx, tapes)
-	if err != nil {
-		return
-	}
-	defer stopServer(t, server)
-
-	// Setup the command-line.
-	cmd := cmd_device.CmdRoot
-	var stdout, stderr bytes.Buffer
-	env := &cmdline.Env{Stdout: &stdout, Stderr: &stderr}
-	appName := naming.JoinAddressName(endpoint.String(), "appname")
-
-	// Confirm that we correctly enforce the number of arguments.
-	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{lower}); err == nil {
-		t.Fatalf("wrongly failed to receive a non-nil error.")
-	}
-	if expected, got := "ERROR: "+lower+": incorrect number of arguments, expected 1, got 0", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
-		t.Fatalf("Unexpected output from %s. Got %q, expected prefix %q", lower, got, expected)
-	}
-	stdout.Reset()
-	stderr.Reset()
-	appTape := tapes.forSuffix("appname")
-	appTape.Rewind()
-
-	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{lower, "nope", "nope"}); err == nil {
-		t.Fatalf("wrongly failed to receive a non-nil error.")
-	}
-	if expected, got := "ERROR: "+lower+": incorrect number of arguments, expected 1, got 2", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
-		t.Fatalf("Unexpected output from %s. Got %q, expected prefix %q", lower, got, expected)
-	}
-	stdout.Reset()
-	stderr.Reset()
-	appTape.Rewind()
-
-	// Correct operation.
-	appTape.SetResponses(nil)
-	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{lower, appName}); err != nil {
-		t.Fatalf("%s failed when it shouldn't: %v", lower, err)
-	}
-	if expected, got := upper+" succeeded", strings.TrimSpace(stdout.String()); got != expected {
-		t.Fatalf("Unexpected output from %s. Got %q, expected %q", lower, got, expected)
-	}
-	if expected, got := []interface{}{upper}, appTape.Play(); !reflect.DeepEqual(expected, got) {
-		t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
-	}
-	appTape.Rewind()
-	stderr.Reset()
-	stdout.Reset()
-
-	// Test list with bad parameters.
-	appTape.SetResponses(verror.New(errOops, nil))
-	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{lower, appName}); err == nil {
-		t.Fatalf("wrongly didn't receive a non-nil error.")
-	}
-	// expected the same.
-	if expected, got := []interface{}{upper}, appTape.Play(); !reflect.DeepEqual(expected, got) {
-		t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
-	}
-}
-
-func TestDeleteCommand(t *testing.T) {
-	testHelper(t, "delete", "Delete")
-}
-
-func TestRunCommand(t *testing.T) {
-	testHelper(t, "run", "Run")
-}
diff --git a/services/device/device/instantiate.go b/services/device/device/instantiate.go
new file mode 100644
index 0000000..8d8ab2c
--- /dev/null
+++ b/services/device/device/instantiate.go
@@ -0,0 +1,81 @@
+// 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.
+
+package main
+
+import (
+	"fmt"
+
+	"v.io/v23"
+	"v.io/v23/context"
+	"v.io/v23/naming"
+	"v.io/v23/rpc"
+	"v.io/v23/security"
+	"v.io/v23/services/device"
+	"v.io/x/lib/cmdline"
+	"v.io/x/ref/lib/v23cmd"
+)
+
+var cmdInstantiate = &cmdline.Command{
+	Runner:   v23cmd.RunnerFunc(runInstantiate),
+	Name:     "instantiate",
+	Short:    "Create an instance of the given application.",
+	Long:     "Create an instance of the given application, provide it with a blessing, and print the name of the new instance.",
+	ArgsName: "<application installation> <grant extension>",
+	ArgsLong: `
+<application installation> is the vanadium object name of the
+application installation from which to create an instance.
+
+<grant extension> is used to extend the default blessing of the
+current principal when blessing the app instance.`,
+}
+
+type granter struct {
+	rpc.CallOpt
+	extension string
+}
+
+func (g *granter) Grant(ctx *context.T, call security.Call) (security.Blessings, error) {
+	p := call.LocalPrincipal()
+	return p.Bless(call.RemoteBlessings().PublicKey(), p.BlessingStore().Default(), g.extension, security.UnconstrainedUse())
+}
+
+func runInstantiate(ctx *context.T, env *cmdline.Env, args []string) error {
+	if expected, got := 2, len(args); expected != got {
+		return env.UsageErrorf("instantiate: incorrect number of arguments, expected %d, got %d", expected, got)
+	}
+	appInstallation, grant := args[0], args[1]
+
+	ctx, cancel := context.WithCancel(ctx)
+	defer cancel()
+	principal := v23.GetPrincipal(ctx)
+
+	call, err := device.ApplicationClient(appInstallation).Instantiate(ctx)
+	if err != nil {
+		return fmt.Errorf("Instantiate failed: %v", err)
+	}
+	for call.RecvStream().Advance() {
+		switch msg := call.RecvStream().Value().(type) {
+		case device.BlessServerMessageInstancePublicKey:
+			pubKey, err := security.UnmarshalPublicKey(msg.Value)
+			if err != nil {
+				return fmt.Errorf("Instantiate failed: %v", err)
+			}
+			// TODO(caprita,rthellend): Get rid of security.UnconstrainedUse().
+			blessings, err := principal.Bless(pubKey, principal.BlessingStore().Default(), grant, security.UnconstrainedUse())
+			if err != nil {
+				return fmt.Errorf("Instantiate failed: %v", err)
+			}
+			call.SendStream().Send(device.BlessClientMessageAppBlessings{blessings})
+		default:
+			fmt.Fprintf(env.Stderr, "Received unexpected message: %#v\n", msg)
+		}
+	}
+	var instanceID string
+	if instanceID, err = call.Finish(); err != nil {
+		return fmt.Errorf("Instantiate failed: %v", err)
+	}
+	fmt.Fprintf(env.Stdout, "%s\n", naming.Join(appInstallation, instanceID))
+	return nil
+}
diff --git a/services/device/device/instantiate_test.go b/services/device/device/instantiate_test.go
new file mode 100644
index 0000000..edfa861
--- /dev/null
+++ b/services/device/device/instantiate_test.go
@@ -0,0 +1,100 @@
+// 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.
+
+package main_test
+
+import (
+	"bytes"
+	"fmt"
+	"reflect"
+	"strings"
+	"testing"
+
+	"v.io/v23/naming"
+	"v.io/v23/verror"
+	"v.io/x/lib/cmdline"
+	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/test"
+
+	cmd_device "v.io/x/ref/services/device/device"
+)
+
+func TestInstantiateCommand(t *testing.T) {
+	ctx, shutdown := test.V23Init()
+	defer shutdown()
+
+	tapes := newTapeMap()
+	server, endpoint, err := startServer(t, ctx, tapes)
+	if err != nil {
+		return
+	}
+	defer stopServer(t, server)
+
+	// Setup the command-line.
+	cmd := cmd_device.CmdRoot
+	var stdout, stderr bytes.Buffer
+	env := &cmdline.Env{Stdout: &stdout, Stderr: &stderr}
+	appName := naming.JoinAddressName(endpoint.String(), "")
+
+	// Confirm that we correctly enforce the number of arguments.
+	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"instantiate", "nope"}); err == nil {
+		t.Fatalf("wrongly failed to receive a non-nil error.")
+	}
+	if expected, got := "ERROR: instantiate: incorrect number of arguments, expected 2, got 1", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
+		t.Fatalf("Unexpected output from instantiate. Got %q, expected prefix %q", got, expected)
+	}
+	stdout.Reset()
+	stderr.Reset()
+	rootTape := tapes.forSuffix("")
+	rootTape.Rewind()
+
+	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"instantiate", "nope", "nope", "nope"}); err == nil {
+		t.Fatalf("wrongly failed to receive a non-nil error.")
+	}
+	if expected, got := "ERROR: instantiate: incorrect number of arguments, expected 2, got 3", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
+		t.Fatalf("Unexpected output from instantiate. Got %q, expected prefix %q", got, expected)
+	}
+	stdout.Reset()
+	stderr.Reset()
+	rootTape.Rewind()
+
+	// Correct operation.
+	rootTape.SetResponses(InstantiateResponse{
+		err:        nil,
+		instanceID: "app1",
+	})
+	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"instantiate", appName, "grant"}); err != nil {
+		t.Fatalf("instantiate %s %s failed: %v", appName, "grant", err)
+	}
+
+	b := new(bytes.Buffer)
+	fmt.Fprintf(b, "%s", appName+"/app1")
+	if expected, got := b.String(), strings.TrimSpace(stdout.String()); !strings.HasPrefix(got, expected) {
+		t.Fatalf("Unexpected output from instantiate. Got %q, expected prefix %q", got, expected)
+	}
+	expected := []interface{}{
+		"Instantiate",
+	}
+	if got := rootTape.Play(); !reflect.DeepEqual(expected, got) {
+		t.Errorf("unexpected result. Got %v want %v", got, expected)
+	}
+	rootTape.Rewind()
+	stdout.Reset()
+	stderr.Reset()
+
+	// Error operation.
+	rootTape.SetResponses(InstantiateResponse{
+		verror.New(errOops, nil),
+		"",
+	})
+	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"instantiate", appName, "grant"}); err == nil {
+		t.Fatalf("instantiate failed to detect error")
+	}
+	expected = []interface{}{
+		"Instantiate",
+	}
+	if got := rootTape.Play(); !reflect.DeepEqual(expected, got) {
+		t.Errorf("unexpected result. Got %v want %v", got, expected)
+	}
+}
diff --git a/services/device/device/kill.go b/services/device/device/kill.go
new file mode 100644
index 0000000..4f156df
--- /dev/null
+++ b/services/device/device/kill.go
@@ -0,0 +1,40 @@
+// 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.
+
+package main
+
+import (
+	"fmt"
+	"time"
+
+	"v.io/v23/context"
+	"v.io/v23/services/device"
+	"v.io/x/lib/cmdline"
+	"v.io/x/ref/lib/v23cmd"
+)
+
+const killDeadline = 10 * time.Second
+
+var cmdKill = &cmdline.Command{
+	Runner:   v23cmd.RunnerFunc(runKill),
+	Name:     "kill",
+	Short:    "Kill the given application instance.",
+	Long:     "Kill the given application instance.",
+	ArgsName: "<app instance>",
+	ArgsLong: `
+<app instance> is the vanadium object name of the application instance to kill.`,
+}
+
+func runKill(ctx *context.T, env *cmdline.Env, args []string) error {
+	if expected, got := 1, len(args); expected != got {
+		return env.UsageErrorf("kill: incorrect number of arguments, expected %d, got %d", expected, got)
+	}
+	appName := args[0]
+
+	if err := device.ApplicationClient(appName).Kill(ctx, killDeadline); err != nil {
+		return fmt.Errorf("Kill failed: %v", err)
+	}
+	fmt.Fprintf(env.Stdout, "Kill succeeded\n")
+	return nil
+}
diff --git a/services/device/device/kill_test.go b/services/device/device/kill_test.go
new file mode 100644
index 0000000..adf7662
--- /dev/null
+++ b/services/device/device/kill_test.go
@@ -0,0 +1,90 @@
+// 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.
+
+package main_test
+
+import (
+	"bytes"
+	"reflect"
+	"strings"
+	"testing"
+	"time"
+
+	"v.io/v23/naming"
+	"v.io/v23/verror"
+	"v.io/x/lib/cmdline"
+	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/test"
+
+	cmd_device "v.io/x/ref/services/device/device"
+)
+
+func TestKillCommand(t *testing.T) {
+	ctx, shutdown := test.V23Init()
+	defer shutdown()
+
+	tapes := newTapeMap()
+	server, endpoint, err := startServer(t, ctx, tapes)
+	if err != nil {
+		return
+	}
+	defer stopServer(t, server)
+
+	// Setup the command-line.
+	cmd := cmd_device.CmdRoot
+	var stdout, stderr bytes.Buffer
+	env := &cmdline.Env{Stdout: &stdout, Stderr: &stderr}
+	appName := naming.JoinAddressName(endpoint.String(), "appname")
+
+	// Confirm that we correctly enforce the number of arguments.
+	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"kill"}); err == nil {
+		t.Fatalf("wrongly failed to receive a non-nil error.")
+	}
+	if expected, got := "ERROR: kill: incorrect number of arguments, expected 1, got 0", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
+		t.Fatalf("Unexpected output from kill. Got %q, expected prefix %q", got, expected)
+	}
+	stdout.Reset()
+	stderr.Reset()
+	appTape := tapes.forSuffix("appname")
+	appTape.Rewind()
+
+	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"kill", "nope", "nope"}); err == nil {
+		t.Fatalf("wrongly failed to receive a non-nil error.")
+	}
+	if expected, got := "ERROR: kill: incorrect number of arguments, expected 1, got 2", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
+		t.Fatalf("Unexpected output from kill. Got %q, expected prefix %q", got, expected)
+	}
+	stdout.Reset()
+	stderr.Reset()
+	appTape.Rewind()
+
+	// Test the 'kill' command.
+	appTape.SetResponses(nil)
+
+	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"kill", appName}); err != nil {
+		t.Fatalf("kill failed when it shouldn't: %v", err)
+	}
+	if expected, got := "Kill succeeded", strings.TrimSpace(stdout.String()); got != expected {
+		t.Fatalf("Unexpected output from list. Got %q, expected %q", got, expected)
+	}
+	expected := []interface{}{
+		KillStimulus{"Kill", 10 * time.Second},
+	}
+	if got := appTape.Play(); !reflect.DeepEqual(expected, got) {
+		t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
+	}
+	appTape.Rewind()
+	stderr.Reset()
+	stdout.Reset()
+
+	// Test kill with bad parameters.
+	appTape.SetResponses(verror.New(errOops, nil))
+	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"kill", appName}); err == nil {
+		t.Fatalf("wrongly didn't receive a non-nil error.")
+	}
+	// expected the same.
+	if got := appTape.Play(); !reflect.DeepEqual(expected, got) {
+		t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
+	}
+}
diff --git a/services/device/device/local_install_test.go b/services/device/device/local_install_test.go
index e3880e7..f3e938c 100644
--- a/services/device/device/local_install_test.go
+++ b/services/device/device/local_install_test.go
@@ -33,7 +33,7 @@
 }
 
 func TestInstallLocalCommand(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	tapes := newTapeMap()
diff --git a/services/device/device/ls.go b/services/device/device/ls.go
index 0bc03cd..80939da 100644
--- a/services/device/device/ls.go
+++ b/services/device/device/ls.go
@@ -8,6 +8,8 @@
 	"fmt"
 	"io"
 
+	"v.io/v23/context"
+
 	"v.io/x/lib/cmdline"
 )
 
@@ -18,10 +20,10 @@
 	Long:     "List application installations or instances.",
 	ArgsName: "<app name patterns...>",
 	ArgsLong: `
-<app name patterns...> are vanadium object names or glob name patterns corresponding to app installations and instances.`,
+<app name patterns...> are vanadium object names or glob name patterns corresponding to application installations and instances.`,
 }
 
-func runLs(entry globResult, stdout, stderr io.Writer) error {
+func runLs(entry globResult, _ *context.T, stdout, _ io.Writer) error {
 	fmt.Fprintf(stdout, "%v\n", entry.name)
 	return nil
 }
diff --git a/services/device/device/ls_test.go b/services/device/device/ls_test.go
index 9ceefd0..420dd05 100644
--- a/services/device/device/ls_test.go
+++ b/services/device/device/ls_test.go
@@ -22,7 +22,7 @@
 // glob functionality, by trying out various combinations of
 // instances/installations in glob results.
 func TestLsCommand(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 	tapes := newTapeMap()
 	server, endpoint, err := startServer(t, ctx, tapes)
@@ -157,7 +157,7 @@
 		}
 
 		if expected, got := c.expected, strings.TrimSpace(stdout.String()); got != expected {
-			t.Errorf("Unexpected output from status. Got %q, expected %q", got, expected)
+			t.Errorf("Unexpected output from ls. Got %q, expected %q", got, expected)
 		}
 		cmd_device.ResetGlobFlags()
 	}
diff --git a/services/device/device/mock_test.go b/services/device/device/mock_test.go
index e52a469..7925dc4 100644
--- a/services/device/device/mock_test.go
+++ b/services/device/device/mock_test.go
@@ -24,7 +24,12 @@
 	t.stimuli = append(t.stimuli, call)
 
 	if len(t.responses) < 1 {
-		return fmt.Errorf("Record(%#v) had no response", call)
+		// Returning an error at this point will likely cause the mock
+		// device manager to panic trying to cast the response to what
+		// it expects.  Panic'ing here at least makes the issue more
+		// apparent.
+		// TODO(caprita): Don't panic.
+		panic(fmt.Errorf("Record(%#v) had no response", call))
 	}
 	resp := t.responses[0]
 	t.responses = t.responses[1:]
@@ -92,9 +97,6 @@
 
 func TestOneTape(t *testing.T) {
 	tm := newTapeMap()
-	if _, ok := tm.forSuffix("mytape").Record("foo").(error); !ok {
-		t.Errorf("Expected error")
-	}
 	tm.forSuffix("mytape").SetResponses("b", "c")
 	if want, got := "b", tm.forSuffix("mytape").Record("bar"); want != got {
 		t.Errorf("Expected %v, got %v", want, got)
@@ -102,7 +104,7 @@
 	if want, got := "c", tm.forSuffix("mytape").Record("baz"); want != got {
 		t.Errorf("Expected %v, got %v", want, got)
 	}
-	if want, got := []interface{}{"foo", "bar", "baz"}, tm.forSuffix("mytape").Play(); !reflect.DeepEqual(want, got) {
+	if want, got := []interface{}{"bar", "baz"}, tm.forSuffix("mytape").Play(); !reflect.DeepEqual(want, got) {
 		t.Errorf("Expected %v, got %v", want, got)
 	}
 	tm.forSuffix("mytape").Rewind()
diff --git a/services/device/device/revert.go b/services/device/device/revert.go
new file mode 100644
index 0000000..0f27078
--- /dev/null
+++ b/services/device/device/revert.go
@@ -0,0 +1,37 @@
+// 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.
+
+package main
+
+import (
+	"fmt"
+
+	"v.io/v23/context"
+	"v.io/v23/services/device"
+	"v.io/x/lib/cmdline"
+	"v.io/x/ref/lib/v23cmd"
+)
+
+var cmdRevert = &cmdline.Command{
+	Runner:   v23cmd.RunnerFunc(runRevert),
+	Name:     "revert",
+	Short:    "Revert the device manager or application",
+	Long:     "Revert the device manager or application to its previous version",
+	ArgsName: "<object>",
+	ArgsLong: `
+<object> is the vanadium object name of the device manager or application
+installation to revert.`,
+}
+
+func runRevert(ctx *context.T, env *cmdline.Env, args []string) error {
+	if expected, got := 1, len(args); expected != got {
+		return env.UsageErrorf("revert: incorrect number of arguments, expected %d, got %d", expected, got)
+	}
+	deviceName := args[0]
+	if err := device.ApplicationClient(deviceName).Revert(ctx); err != nil {
+		return err
+	}
+	fmt.Fprintln(env.Stdout, "Revert successful.")
+	return nil
+}
diff --git a/services/device/device/root.go b/services/device/device/root.go
index 5325016..6caa02e 100644
--- a/services/device/device/root.go
+++ b/services/device/device/root.go
@@ -20,7 +20,7 @@
 	Long: `
 Command device facilitates interaction with the Vanadium device manager.
 `,
-	Children: []*cmdline.Command{cmdInstall, cmdInstallLocal, cmdUninstall, cmdAssociate, cmdDescribe, cmdClaim, cmdInstantiate, cmdDelete, cmdRun, cmdKill, cmdRevert, cmdUpdate, cmdUpdateAll, cmdStatus, cmdDebug, cmdACL, cmdPublish, cmdLs},
+	Children: []*cmdline.Command{cmdInstall, cmdInstallLocal, cmdUninstall, cmdAssociate, cmdDescribe, cmdClaim, cmdInstantiate, cmdDelete, cmdRun, cmdKill, cmdRevert, cmdUpdate, cmdStatus, cmdDebug, cmdACL, cmdPublish, cmdLs},
 }
 
 func main() {
diff --git a/services/device/device/run.go b/services/device/device/run.go
new file mode 100644
index 0000000..6528708
--- /dev/null
+++ b/services/device/device/run.go
@@ -0,0 +1,37 @@
+// 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.
+
+package main
+
+import (
+	"fmt"
+
+	"v.io/v23/context"
+	"v.io/v23/services/device"
+	"v.io/x/lib/cmdline"
+	"v.io/x/ref/lib/v23cmd"
+)
+
+var cmdRun = &cmdline.Command{
+	Runner:   v23cmd.RunnerFunc(runRun),
+	Name:     "run",
+	Short:    "Run the given application instance.",
+	Long:     "Run the given application instance.",
+	ArgsName: "<app instance>",
+	ArgsLong: `
+<app instance> is the vanadium object name of the application instance to run.`,
+}
+
+func runRun(ctx *context.T, env *cmdline.Env, args []string) error {
+	if expected, got := 1, len(args); expected != got {
+		return env.UsageErrorf("run: incorrect number of arguments, expected %d, got %d", expected, got)
+	}
+	appName := args[0]
+
+	if err := device.ApplicationClient(appName).Run(ctx); err != nil {
+		return fmt.Errorf("Run failed: %v,\nView log with:\n debug logs read `debug glob %s/logs/STDERR-*`", err, appName)
+	}
+	fmt.Fprintf(env.Stdout, "Run succeeded\n")
+	return nil
+}
diff --git a/services/device/device/run_test.go b/services/device/device/run_test.go
new file mode 100644
index 0000000..7d9f5d9
--- /dev/null
+++ b/services/device/device/run_test.go
@@ -0,0 +1,11 @@
+// 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.
+
+package main_test
+
+import "testing"
+
+func TestRunCommand(t *testing.T) {
+	testHelper(t, "run", "Run")
+}
diff --git a/services/device/device/status.go b/services/device/device/status.go
new file mode 100644
index 0000000..d32430e
--- /dev/null
+++ b/services/device/device/status.go
@@ -0,0 +1,38 @@
+// 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.
+
+package main
+
+import (
+	"fmt"
+	"io"
+
+	"v.io/v23/context"
+	"v.io/v23/services/device"
+	"v.io/x/lib/cmdline"
+)
+
+var cmdStatus = &cmdline.Command{
+	Runner:   globRunner(runStatus),
+	Name:     "status",
+	Short:    "Get device manager or application status.",
+	Long:     "Get the status of the device manager or application instances and installations.",
+	ArgsName: "<name patterns...>",
+	ArgsLong: `
+<name patterns...> are vanadium object names or glob name patterns corresponding to the device manager service, or to application installations and instances.`,
+}
+
+func runStatus(entry globResult, _ *context.T, stdout, _ io.Writer) error {
+	switch s := entry.status.(type) {
+	case device.StatusInstance:
+		fmt.Fprintf(stdout, "Instance %v [State:%v,Version:%v]\n", entry.name, s.Value.State, s.Value.Version)
+	case device.StatusInstallation:
+		fmt.Fprintf(stdout, "Installation %v [State:%v,Version:%v]\n", entry.name, s.Value.State, s.Value.Version)
+	case device.StatusDevice:
+		fmt.Fprintf(stdout, "Device Service %v [State:%v,Version:%v]\n", entry.name, s.Value.State, s.Value.Version)
+	default:
+		return fmt.Errorf("Status returned unknown type: %T", s)
+	}
+	return nil
+}
diff --git a/services/device/device/status_test.go b/services/device/device/status_test.go
new file mode 100644
index 0000000..4284226
--- /dev/null
+++ b/services/device/device/status_test.go
@@ -0,0 +1,73 @@
+// 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.
+
+package main_test
+
+import (
+	"bytes"
+	"fmt"
+	"reflect"
+	"strings"
+	"testing"
+
+	"v.io/v23/naming"
+	"v.io/v23/services/device"
+	"v.io/x/lib/cmdline"
+	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/test"
+
+	cmd_device "v.io/x/ref/services/device/device"
+)
+
+func TestStatusCommand(t *testing.T) {
+	ctx, shutdown := test.V23Init()
+	defer shutdown()
+	tapes := newTapeMap()
+	server, endpoint, err := startServer(t, ctx, tapes)
+	if err != nil {
+		return
+	}
+	defer stopServer(t, server)
+
+	cmd := cmd_device.CmdRoot
+	globName := naming.JoinAddressName(endpoint.String(), "glob")
+	appName := naming.JoinAddressName(endpoint.String(), "app")
+
+	rootTape, appTape := tapes.forSuffix(""), tapes.forSuffix("app")
+	for _, c := range []struct {
+		tapeResponse device.Status
+		expected     string
+	}{
+		{
+			installationUninstalled,
+			fmt.Sprintf("Installation %v [State:Uninstalled,Version:director's cut]", appName),
+		},
+		{
+			instanceUpdating,
+			fmt.Sprintf("Instance %v [State:Updating,Version:theatrical version]", appName),
+		},
+		{
+			deviceService,
+			fmt.Sprintf("Device Service %v [State:Running,Version:han shot first]", appName),
+		},
+	} {
+		var stdout, stderr bytes.Buffer
+		env := &cmdline.Env{Stdout: &stdout, Stderr: &stderr}
+		tapes.rewind()
+		rootTape.SetResponses(GlobResponse{[]string{"app"}})
+		appTape.SetResponses(c.tapeResponse)
+		if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"status", globName}); err != nil {
+			t.Errorf("%v", err)
+		}
+		if expected, got := c.expected, strings.TrimSpace(stdout.String()); got != expected {
+			t.Errorf("Unexpected output from status. Got %q, expected %q", got, expected)
+		}
+		if got, expected := rootTape.Play(), []interface{}{GlobStimulus{"glob"}}; !reflect.DeepEqual(expected, got) {
+			t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
+		}
+		if got, expected := appTape.Play(), []interface{}{"Status"}; !reflect.DeepEqual(expected, got) {
+			t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
+		}
+	}
+}
diff --git a/services/device/device/uninstall.go b/services/device/device/uninstall.go
new file mode 100644
index 0000000..9beb224
--- /dev/null
+++ b/services/device/device/uninstall.go
@@ -0,0 +1,38 @@
+// 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.
+
+package main
+
+import (
+	"fmt"
+
+	"v.io/v23/context"
+	"v.io/v23/services/device"
+	"v.io/x/lib/cmdline"
+	"v.io/x/ref/lib/v23cmd"
+)
+
+var cmdUninstall = &cmdline.Command{
+	Runner:   v23cmd.RunnerFunc(runUninstall),
+	Name:     "uninstall",
+	Short:    "Uninstall the given application installation.",
+	Long:     "Uninstall the given application installation.",
+	ArgsName: "<installation>",
+	ArgsLong: `
+<installation> is the vanadium object name of the application installation to
+uninstall.
+`,
+}
+
+func runUninstall(ctx *context.T, env *cmdline.Env, args []string) error {
+	if expected, got := 1, len(args); expected != got {
+		return env.UsageErrorf("uninstall: incorrect number of arguments, expected %d, got %d", expected, got)
+	}
+	installName := args[0]
+	if err := device.ApplicationClient(installName).Uninstall(ctx); err != nil {
+		return fmt.Errorf("Uninstall failed: %v", err)
+	}
+	fmt.Fprintf(env.Stdout, "Successfully uninstalled: %q\n", installName)
+	return nil
+}
diff --git a/services/device/device/update.go b/services/device/device/update.go
new file mode 100644
index 0000000..e6cb007
--- /dev/null
+++ b/services/device/device/update.go
@@ -0,0 +1,111 @@
+// 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.
+
+package main
+
+import (
+	"fmt"
+	"io"
+	"time"
+
+	"v.io/v23/context"
+	"v.io/v23/services/device"
+	"v.io/v23/verror"
+
+	"v.io/x/lib/cmdline"
+	deviceimpl "v.io/x/ref/services/device/internal/impl"
+)
+
+var cmdUpdate = &cmdline.Command{
+	Runner:   globRunner(runUpdate),
+	Name:     "update",
+	Short:    "Update device manager or applications.",
+	Long:     "Update the device manager or application instances and installations",
+	ArgsName: "<app name patterns...>",
+	ArgsLong: `
+<app name patterns...> are vanadium object names or glob name patterns corresponding to the device manager service, or to application installations and instances.`,
+}
+
+func instanceIsRunning(ctx *context.T, von string) (bool, error) {
+	status, err := device.ApplicationClient(von).Status(ctx)
+	if err != nil {
+		return false, fmt.Errorf("Failed to get status for instance %q: %v", von, err)
+	}
+	s, ok := status.(device.StatusInstance)
+	if !ok {
+		return false, fmt.Errorf("Status for instance %q of wrong type (%T)", von, status)
+	}
+	return s.Value.State == device.InstanceStateRunning, nil
+}
+
+func updateInstance(ctx *context.T, stdout, stderr io.Writer, name string, status device.StatusInstance) (retErr error) {
+	if status.Value.State == device.InstanceStateRunning {
+		if err := device.ApplicationClient(name).Kill(ctx, killDeadline); err != nil {
+			// Check the app's state again in case we killed it,
+			// nevermind any errors.  The sleep is because Kill
+			// currently (4/29/15) returns asynchronously with the
+			// device manager shooting the app down.
+			time.Sleep(time.Second)
+			running, rerr := instanceIsRunning(ctx, name)
+			if rerr != nil {
+				return rerr
+			}
+			if running {
+				return fmt.Errorf("Kill failed: %v", err)
+			}
+			fmt.Fprintf(stderr, "WARNING for \"%s\": recovered from Kill error (%s). Proceeding with update.\n", name, err)
+		}
+		// App was running, and we killed it, so we need to run it again
+		// after the update.
+		defer func() {
+			if err := device.ApplicationClient(name).Run(ctx); err != nil {
+				err = fmt.Errorf("Run failed: %v", err)
+				if retErr == nil {
+					retErr = err
+				} else {
+					fmt.Fprintf(stderr, "ERROR for \"%s\": %v.\n", name, err)
+				}
+			}
+		}()
+	}
+	// Update the instance.
+	switch err := device.ApplicationClient(name).Update(ctx); {
+	case err == nil:
+		fmt.Fprintf(stdout, "Successfully updated instance \"%s\".\n", name)
+		return nil
+	case verror.ErrorID(err) == deviceimpl.ErrUpdateNoOp.ID:
+		// TODO(caprita): Ideally, we wouldn't even attempt a kill /
+		// restart if there's no newer version of the application.
+		fmt.Fprintf(stdout, "Instance \"%s\" already up to date.\n", name)
+		return nil
+	default:
+		return fmt.Errorf("Update failed: %v", err)
+	}
+}
+
+func updateOne(ctx *context.T, what string, stdout, stderr io.Writer, name string) error {
+	switch err := device.ApplicationClient(name).Update(ctx); {
+	case err == nil:
+		fmt.Fprintf(stdout, "Successfully updated version for %s \"%s\".\n", what, name)
+		return nil
+	case verror.ErrorID(err) == deviceimpl.ErrUpdateNoOp.ID:
+		fmt.Fprintf(stdout, "%s \"%s\" already up to date.\n", what, name)
+		return nil
+	default:
+		return fmt.Errorf("Update failed: %v", err)
+	}
+}
+
+func runUpdate(entry globResult, ctx *context.T, stdout, stderr io.Writer) error {
+	switch entry.kind {
+	case applicationInstanceObject:
+		return updateInstance(ctx, stdout, stderr, entry.name, entry.status.(device.StatusInstance))
+	case applicationInstallationObject:
+		return updateOne(ctx, "installation", stdout, stderr, entry.name)
+	case deviceServiceObject:
+		return updateOne(ctx, "device service", stdout, stderr, entry.name)
+	default:
+		return fmt.Errorf("unhandled object kind %v", entry.kind)
+	}
+}
diff --git a/services/device/device/update_test.go b/services/device/device/update_test.go
new file mode 100644
index 0000000..1c90110
--- /dev/null
+++ b/services/device/device/update_test.go
@@ -0,0 +1,137 @@
+// 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.
+
+package main_test
+
+import (
+	"bytes"
+	"fmt"
+	"reflect"
+	"strings"
+	"testing"
+	"time"
+
+	"v.io/v23/naming"
+	"v.io/x/lib/cmdline"
+	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/test"
+
+	cmd_device "v.io/x/ref/services/device/device"
+)
+
+// TestUpdateCommand verifies the device update command.
+func TestUpdateCommand(t *testing.T) {
+	ctx, shutdown := test.V23Init()
+	defer shutdown()
+	tapes := newTapeMap()
+	server, endpoint, err := startServer(t, ctx, tapes)
+	if err != nil {
+		return
+	}
+	defer stopServer(t, server)
+
+	cmd := cmd_device.CmdRoot
+	appName := naming.JoinAddressName(endpoint.String(), "app")
+	rootTape := tapes.forSuffix("")
+	globName := naming.JoinAddressName(endpoint.String(), "glob")
+	// TODO(caprita): Move joinLines to a common place.
+	joinLines := func(args ...string) string {
+		return strings.Join(args, "\n")
+	}
+	for _, c := range []struct {
+		globResponses   []string
+		statusResponses map[string][]interface{}
+		expectedStimuli map[string][]interface{}
+		expectedStdout  string
+		expectedStderr  string
+		expectedError   string
+	}{
+		{ // Everything succeeds.
+			[]string{"app/2", "app/1", "app/3"},
+			map[string][]interface{}{
+				"app/1": []interface{}{instanceRunning, nil, nil, nil},
+				"app/2": []interface{}{instanceNotRunning, nil},
+				"app/3": []interface{}{installationActive, nil},
+			},
+			map[string][]interface{}{
+				"app/1": []interface{}{"Status", KillStimulus{"Kill", 10 * time.Second}, "Update", "Run"},
+				"app/2": []interface{}{"Status", "Update"},
+				"app/3": []interface{}{"Status", "Update"},
+			},
+			joinLines(
+				fmt.Sprintf("Successfully updated version for installation \"%s/3\".", appName),
+				fmt.Sprintf("Successfully updated instance \"%s/1\".", appName),
+				fmt.Sprintf("Successfully updated instance \"%s/2\".", appName)),
+			"",
+			"",
+		},
+		{ // Assorted failure modes.
+			[]string{"app/1", "app/2", "app/3", "app/4", "app/5"},
+			map[string][]interface{}{
+				// Starts as running, fails Kill, but then
+				// recovers. This ultimately counts as a success.
+				"app/1": []interface{}{instanceRunning, fmt.Errorf("Simulate Kill failing"), instanceNotRunning, nil, nil},
+				// Starts as running, fails Kill, and stays running.
+				"app/2": []interface{}{instanceRunning, fmt.Errorf("Simulate Kill failing"), instanceRunning},
+				// Starts as running, Kill and Update succeed, but Run fails.
+				"app/3": []interface{}{instanceRunning, nil, nil, fmt.Errorf("Simulate Run failing")},
+				// Starts as running, Kill succeeds, Update fails, but Run succeeds.
+				"app/4": []interface{}{instanceRunning, nil, fmt.Errorf("Simulate Update failing"), nil},
+				// Starts as running, Kill succeeds, Update fails, and Run fails.
+				"app/5": []interface{}{instanceRunning, nil, fmt.Errorf("Simulate Update failing"), fmt.Errorf("Simulate Run failing")},
+			},
+			map[string][]interface{}{
+				"app/1": []interface{}{"Status", KillStimulus{"Kill", 10 * time.Second}, "Status", "Update", "Run"},
+				"app/2": []interface{}{"Status", KillStimulus{"Kill", 10 * time.Second}, "Status"},
+				"app/3": []interface{}{"Status", KillStimulus{"Kill", 10 * time.Second}, "Update", "Run"},
+				"app/4": []interface{}{"Status", KillStimulus{"Kill", 10 * time.Second}, "Update", "Run"},
+				"app/5": []interface{}{"Status", KillStimulus{"Kill", 10 * time.Second}, "Update", "Run"},
+			},
+			joinLines(
+				fmt.Sprintf("Successfully updated instance \"%s/1\".", appName),
+				fmt.Sprintf("Successfully updated instance \"%s/3\".", appName),
+			),
+			joinLines(
+				fmt.Sprintf("WARNING for \"%s/1\": recovered from Kill error (device.test:<rpc.Client>\"%s/1\".Kill: Error: Simulate Kill failing). Proceeding with update.", appName, appName),
+				fmt.Sprintf("ERROR for \"%s/2\": Kill failed: device.test:<rpc.Client>\"%s/2\".Kill: Error: Simulate Kill failing.", appName, appName),
+				fmt.Sprintf("ERROR for \"%s/3\": Run failed: device.test:<rpc.Client>\"%s/3\".Run: Error: Simulate Run failing.", appName, appName),
+				fmt.Sprintf("ERROR for \"%s/4\": Update failed: device.test:<rpc.Client>\"%s/4\".Update: Error: Simulate Update failing.", appName, appName),
+				fmt.Sprintf("ERROR for \"%s/5\": Run failed: device.test:<rpc.Client>\"%s/5\".Run: Error: Simulate Run failing.", appName, appName),
+				fmt.Sprintf("ERROR for \"%s/5\": Update failed: device.test:<rpc.Client>\"%s/5\".Update: Error: Simulate Update failing.", appName, appName),
+			),
+			"encountered a total of 4 error(s)",
+		},
+	} {
+		var stdout, stderr bytes.Buffer
+		env := &cmdline.Env{Stdout: &stdout, Stderr: &stderr}
+		tapes.rewind()
+		rootTape.SetResponses(GlobResponse{c.globResponses})
+		for n, r := range c.statusResponses {
+			tapes.forSuffix(n).SetResponses(r...)
+		}
+		args := []string{"update", globName}
+		if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, args); err != nil {
+			if want, got := c.expectedError, err.Error(); want != got {
+				t.Errorf("Unexpected error: want %v, got %v", want, got)
+			}
+		} else {
+			if c.expectedError != "" {
+				t.Errorf("Expected to get error %v, but didn't get any error.", c.expectedError)
+			}
+		}
+
+		if expected, got := c.expectedStdout, strings.TrimSpace(stdout.String()); got != expected {
+			t.Errorf("Unexpected stdout output from update. Got %q, expected %q", got, expected)
+		}
+		if expected, got := c.expectedStderr, strings.TrimSpace(stderr.String()); got != expected {
+			t.Errorf("Unexpected stderr output from update. Got %q, expected %q", got, expected)
+		}
+		for n, m := range c.expectedStimuli {
+			if want, got := m, tapes.forSuffix(n).Play(); !reflect.DeepEqual(want, got) {
+				t.Errorf("Unexpected stimuli for %v. Want: %v, got %v.", n, want, got)
+			}
+		}
+		cmd_device.ResetGlobFlags()
+	}
+}
diff --git a/services/device/device/updateall.go b/services/device/device/updateall.go
deleted file mode 100644
index e772ee9..0000000
--- a/services/device/device/updateall.go
+++ /dev/null
@@ -1,236 +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.
-
-package main
-
-import (
-	"fmt"
-	"strings"
-	"sync"
-	"time"
-
-	"v.io/v23"
-	"v.io/v23/context"
-	"v.io/v23/naming"
-	"v.io/v23/services/device"
-	"v.io/v23/verror"
-	"v.io/x/lib/cmdline"
-	"v.io/x/ref/lib/v23cmd"
-	deviceimpl "v.io/x/ref/services/device/internal/impl"
-)
-
-// TODO(caprita): Re-implement this with Glob, so that one can say instead,
-// update <devicename>/apps/... or <devicename>/apps/appname/* etc.
-
-// TODO(caprita): Add unit test.
-
-var cmdUpdateAll = &cmdline.Command{
-	Runner:   v23cmd.RunnerFunc(runUpdateAll),
-	Name:     "updateall",
-	Short:    "Update all installations/instances of an application",
-	Long:     "Given a name that can refer to an app instance or app installation or app or all apps on a device, updates all installations and instances under that name",
-	ArgsName: "<object name>",
-	ArgsLong: `
-<object name> is the vanadium object name to update, as follows:
-
-<devicename>/apps/apptitle/installationid/instanceid: updates the given instance, killing/restarting it if running
-
-<devicename>/apps/apptitle/installationid: updates the given installation and then all its instances
-
-<devicename>/apps/apptitle: updates all installations for the given app
-
-<devicename>/apps: updates all apps on the device
-`,
-}
-
-type updater func(ctx *context.T, env *cmdline.Env, von string) error
-
-func updateChildren(ctx *context.T, env *cmdline.Env, von string, updateChild updater) error {
-	ns := v23.GetNamespace(ctx)
-	pattern := naming.Join(von, "*")
-	c, err := ns.Glob(ctx, pattern)
-	if err != nil {
-		return fmt.Errorf("ns.Glob(%q) failed: %v", pattern, err)
-	}
-	var (
-		pending     sync.WaitGroup
-		numErrors   int
-		numErrorsMu sync.Mutex
-	)
-	for res := range c {
-		switch v := res.(type) {
-		case *naming.GlobReplyEntry:
-			pending.Add(1)
-			go func() {
-				if err := updateChild(ctx, env, v.Value.Name); err != nil {
-					numErrorsMu.Lock()
-					numErrors++
-					numErrorsMu.Unlock()
-				}
-				pending.Done()
-			}()
-		case *naming.GlobReplyError:
-			fmt.Fprintf(env.Stderr, "Glob error for %q: %v\n", v.Value.Name, v.Value.Error)
-			numErrorsMu.Lock()
-			numErrors++
-			numErrorsMu.Unlock()
-		}
-	}
-	pending.Wait()
-	if numErrors > 0 {
-		return fmt.Errorf("%d error(s) encountered while updating children", numErrors)
-	}
-	return nil
-}
-
-func instanceIsRunning(ctx *context.T, von string) (bool, error) {
-	status, err := device.ApplicationClient(von).Status(ctx)
-	if err != nil {
-		return false, fmt.Errorf("Failed to get status for instance %q: %v", von, err)
-	}
-	s, ok := status.(device.StatusInstance)
-	if !ok {
-		return false, fmt.Errorf("Status for instance %q of wrong type (%T)", von, status)
-	}
-	return s.Value.State == device.InstanceStateRunning, nil
-}
-
-func updateInstance(ctx *context.T, env *cmdline.Env, von string) (retErr error) {
-	defer func() {
-		if retErr == nil {
-			fmt.Fprintf(env.Stdout, "Successfully updated instance %q.\n", von)
-		} else {
-			retErr = fmt.Errorf("failed to update instance %q: %v", von, retErr)
-			fmt.Fprintf(env.Stderr, "ERROR: %v.\n", retErr)
-		}
-	}()
-	running, err := instanceIsRunning(ctx, von)
-	if err != nil {
-		return err
-	}
-	if running {
-		// Try killing the app.
-		if err := device.ApplicationClient(von).Kill(ctx, killDeadline); err != nil {
-			// Check the app's state again in case we killed it,
-			// nevermind any errors.  The sleep is because Kill
-			// currently (4/29/15) returns asynchronously with the
-			// device manager shooting the app down.
-			time.Sleep(time.Second)
-			running, rerr := instanceIsRunning(ctx, von)
-			if rerr != nil {
-				return rerr
-			}
-			if running {
-				return fmt.Errorf("failed to kill instance %q: %v", von, err)
-			}
-			fmt.Fprintf(env.Stderr, "Kill(%s) returned an error (%s) but app is now not running.\n", von, err)
-		}
-		// App was running, and we killed it.
-		defer func() {
-			// Re-start the instance.
-			if err := device.ApplicationClient(von).Run(ctx); err != nil {
-				err = fmt.Errorf("failed to run instance %q: %v", von, err)
-				if retErr == nil {
-					retErr = err
-				} else {
-					fmt.Fprintf(env.Stderr, "ERROR: %v.\n", err)
-				}
-			}
-		}()
-	}
-	// Update the instance.
-	switch err := device.ApplicationClient(von).Update(ctx); {
-	case err == nil:
-		return nil
-	case verror.ErrorID(err) == deviceimpl.ErrUpdateNoOp.ID:
-		// TODO(caprita): Ideally, we wouldn't even attempt a kill /
-		// restart if there's no newer version of the application.
-		fmt.Fprintf(env.Stdout, "Instance %q already up to date.\n", von)
-		return nil
-	default:
-		return err
-	}
-}
-
-func updateInstallation(ctx *context.T, env *cmdline.Env, von string) (retErr error) {
-	defer func() {
-		if retErr == nil {
-			fmt.Fprintf(env.Stdout, "Successfully updated installation %q.\n", von)
-		} else {
-			retErr = fmt.Errorf("failed to update installation %q: %v", von, retErr)
-			fmt.Fprintf(env.Stderr, "ERROR: %v.\n", retErr)
-		}
-	}()
-
-	// First, update the installation.
-	switch err := device.ApplicationClient(von).Update(ctx); {
-	case err == nil:
-		fmt.Fprintf(env.Stdout, "Successfully updated version for installation %q.\n", von)
-	case verror.ErrorID(err) == deviceimpl.ErrUpdateNoOp.ID:
-		fmt.Fprintf(env.Stdout, "Installation %q already up to date.\n", von)
-		// NOTE: we still proceed to update the instances in this case,
-		// since it's possible that some instances are still running
-		// from older versions.
-	default:
-		return err
-	}
-	// Then, update all the instances for the installation.
-	return updateChildren(ctx, env, von, updateInstance)
-}
-
-func updateApp(ctx *context.T, env *cmdline.Env, von string) error {
-	if err := updateChildren(ctx, env, von, updateInstallation); err != nil {
-		err = fmt.Errorf("failed to update app %q: %v", von, err)
-		fmt.Fprintf(env.Stderr, "ERROR: %v.\n", err)
-		return err
-	}
-	fmt.Fprintf(env.Stdout, "Successfully updated app %q.\n", von)
-	return nil
-}
-
-func updateAllApps(ctx *context.T, env *cmdline.Env, von string) error {
-	if err := updateChildren(ctx, env, von, updateApp); err != nil {
-		err = fmt.Errorf("failed to update all apps %q: %v", von, err)
-		fmt.Fprintf(env.Stderr, "ERROR: %v.\n", err)
-		return err
-	}
-	fmt.Fprintf(env.Stdout, "Successfully updated all apps %q.\n", von)
-	return nil
-}
-
-func runUpdateAll(ctx *context.T, env *cmdline.Env, args []string) error {
-	if expected, got := 1, len(args); expected != got {
-		return env.UsageErrorf("updateall: incorrect number of arguments, expected %d, got %d", expected, got)
-	}
-	appVON := args[0]
-	components := strings.Split(appVON, "/")
-	var prefix string
-	// TODO(caprita): Trying to figure out what the app suffix is by looking
-	// for "/apps/" in the name is hacky and error-prone (e.g., what if an
-	// app has the title "apps").  Instead, we should either query the
-	// server or use resolution to split up the name into address and
-	// suffix.
-	for i := len(components) - 1; i >= 0; i-- {
-		if components[i] == "apps" {
-			prefix = naming.Join(components[:i+1]...)
-			components = components[i+1:]
-			break
-		}
-	}
-	if prefix == "" {
-		return fmt.Errorf("couldn't recognize app name: %q", appVON)
-	}
-	fmt.Printf("prefix: %q, components: %q\n", prefix, components)
-	switch len(components) {
-	case 0:
-		return updateAllApps(ctx, env, appVON)
-	case 1:
-		return updateApp(ctx, env, appVON)
-	case 2:
-		return updateInstallation(ctx, env, appVON)
-	case 3:
-		return updateInstance(ctx, env, appVON)
-	}
-	return env.UsageErrorf("updateall: name %q does not refer to a supported app hierarchy object", appVON)
-}
diff --git a/services/device/device/util_test.go b/services/device/device/util_test.go
new file mode 100644
index 0000000..01bd228
--- /dev/null
+++ b/services/device/device/util_test.go
@@ -0,0 +1,115 @@
+// 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.
+
+package main_test
+
+import (
+	"bytes"
+	"reflect"
+	"strings"
+	"testing"
+
+	"v.io/v23/naming"
+	"v.io/v23/services/device"
+	"v.io/v23/verror"
+	"v.io/x/lib/cmdline"
+	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/test"
+
+	cmd_device "v.io/x/ref/services/device/device"
+)
+
+//go:generate v23 test generate
+
+var (
+	installationUninstalled = device.StatusInstallation{device.InstallationStatus{
+		State:   device.InstallationStateUninstalled,
+		Version: "director's cut",
+	}}
+	installationActive = device.StatusInstallation{device.InstallationStatus{
+		State:   device.InstallationStateActive,
+		Version: "extended cut",
+	}}
+	instanceUpdating = device.StatusInstance{device.InstanceStatus{
+		State:   device.InstanceStateUpdating,
+		Version: "theatrical version",
+	}}
+	instanceRunning = device.StatusInstance{device.InstanceStatus{
+		State:   device.InstanceStateRunning,
+		Version: "special edition",
+	}}
+	instanceNotRunning = device.StatusInstance{device.InstanceStatus{
+		State:   device.InstanceStateNotRunning,
+		Version: "special edition",
+	}}
+	deviceService = device.StatusDevice{device.DeviceStatus{
+		State:   device.InstanceStateRunning,
+		Version: "han shot first",
+	}}
+)
+
+func testHelper(t *testing.T, lower, upper string) {
+	ctx, shutdown := test.V23Init()
+	defer shutdown()
+
+	tapes := newTapeMap()
+	server, endpoint, err := startServer(t, ctx, tapes)
+	if err != nil {
+		return
+	}
+	defer stopServer(t, server)
+
+	// Setup the command-line.
+	cmd := cmd_device.CmdRoot
+	var stdout, stderr bytes.Buffer
+	env := &cmdline.Env{Stdout: &stdout, Stderr: &stderr}
+	appName := naming.JoinAddressName(endpoint.String(), "appname")
+
+	// Confirm that we correctly enforce the number of arguments.
+	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{lower}); err == nil {
+		t.Fatalf("wrongly failed to receive a non-nil error.")
+	}
+	if expected, got := "ERROR: "+lower+": incorrect number of arguments, expected 1, got 0", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
+		t.Fatalf("Unexpected output from %s. Got %q, expected prefix %q", lower, got, expected)
+	}
+	stdout.Reset()
+	stderr.Reset()
+	appTape := tapes.forSuffix("appname")
+	appTape.Rewind()
+
+	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{lower, "nope", "nope"}); err == nil {
+		t.Fatalf("wrongly failed to receive a non-nil error.")
+	}
+	if expected, got := "ERROR: "+lower+": incorrect number of arguments, expected 1, got 2", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
+		t.Fatalf("Unexpected output from %s. Got %q, expected prefix %q", lower, got, expected)
+	}
+	stdout.Reset()
+	stderr.Reset()
+	appTape.Rewind()
+
+	// Correct operation.
+	appTape.SetResponses(nil)
+	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{lower, appName}); err != nil {
+		t.Fatalf("%s failed when it shouldn't: %v", lower, err)
+	}
+	if expected, got := upper+" succeeded", strings.TrimSpace(stdout.String()); got != expected {
+		t.Fatalf("Unexpected output from %s. Got %q, expected %q", lower, got, expected)
+	}
+	if expected, got := []interface{}{upper}, appTape.Play(); !reflect.DeepEqual(expected, got) {
+		t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
+	}
+	appTape.Rewind()
+	stderr.Reset()
+	stdout.Reset()
+
+	// Test with bad parameters.
+	appTape.SetResponses(verror.New(errOops, nil))
+	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{lower, appName}); err == nil {
+		t.Fatalf("wrongly didn't receive a non-nil error.")
+	}
+	// expected the same.
+	if expected, got := []interface{}{upper}, appTape.Play(); !reflect.DeepEqual(expected, got) {
+		t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
+	}
+}
diff --git a/services/device/device/v23_internal_test.go b/services/device/device/v23_internal_test.go
index dcd0029..ae59080 100644
--- a/services/device/device/v23_internal_test.go
+++ b/services/device/device/v23_internal_test.go
@@ -4,12 +4,15 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package main
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
+	"v.io/x/ref/test"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
diff --git a/services/device/internal/impl/app_service.go b/services/device/internal/impl/app_service.go
index a393bb0..8b1ce68 100644
--- a/services/device/internal/impl/app_service.go
+++ b/services/device/internal/impl/app_service.go
@@ -1047,7 +1047,7 @@
 	return transitionInstallation(installationDir, device.InstallationStateActive, device.InstallationStateUninstalled)
 }
 
-func updateInstance(instanceDir string, ctx *context.T) (err error) {
+func updateInstance(ctx *context.T, instanceDir string) (err error) {
 	// Only not-running instances can be updated.
 	if err := transitionInstance(instanceDir, device.InstanceStateNotRunning, device.InstanceStateUpdating); err != nil {
 		return err
@@ -1128,7 +1128,7 @@
 		return updateInstallation(ctx, installationDir)
 	}
 	if instanceDir, err := i.instanceDir(); err == nil {
-		return updateInstance(instanceDir, ctx)
+		return updateInstance(ctx, instanceDir)
 	}
 	return verror.New(ErrInvalidSuffix, nil)
 }
@@ -1138,11 +1138,38 @@
 	return nil
 }
 
-func (i *appService) Revert(ctx *context.T, _ rpc.ServerCall) error {
-	installationDir, err := i.installationDir()
-	if err != nil {
+func revertInstance(ctx *context.T, instanceDir string) (err error) {
+	// Only not-running instances can be reverted.
+	if err := transitionInstance(instanceDir, device.InstanceStateNotRunning, device.InstanceStateUpdating); err != nil {
 		return err
 	}
+	defer func() {
+		terr := transitionInstance(instanceDir, device.InstanceStateUpdating, device.InstanceStateNotRunning)
+		if err == nil {
+			err = terr
+		}
+	}()
+	versionLink := filepath.Join(instanceDir, "version")
+	versionDir, err := filepath.EvalSymlinks(versionLink)
+	if err != nil {
+		return verror.New(ErrOperationFailed, ctx, fmt.Sprintf("EvalSymlinks(%v) failed: %v", versionLink, err))
+	}
+	previousLink := filepath.Join(versionDir, "previous")
+	if _, err := os.Lstat(previousLink); err != nil {
+		if os.IsNotExist(err) {
+			// No 'previous' link -- must be the first version.
+			return verror.New(ErrUpdateNoOp, ctx)
+		}
+		return verror.New(ErrOperationFailed, ctx, fmt.Sprintf("Lstat(%v) failed: %v", previousLink, err))
+	}
+	prevVersionDir, err := filepath.EvalSymlinks(previousLink)
+	if err != nil {
+		return verror.New(ErrOperationFailed, ctx, fmt.Sprintf("EvalSymlinks(%v) failed: %v", previousLink, err))
+	}
+	return updateLink(prevVersionDir, versionLink)
+}
+
+func revertInstallation(ctx *context.T, installationDir string) error {
 	if !installationStateIs(installationDir, device.InstallationStateActive) {
 		return verror.New(ErrInvalidOperation, ctx)
 	}
@@ -1171,6 +1198,16 @@
 	return updateLink(prevVersionDir, currLink)
 }
 
+func (i *appService) Revert(ctx *context.T, _ rpc.ServerCall) error {
+	if installationDir, err := i.installationDir(); err == nil {
+		return revertInstallation(ctx, installationDir)
+	}
+	if instanceDir, err := i.instanceDir(); err == nil {
+		return revertInstance(ctx, instanceDir)
+	}
+	return verror.New(ErrInvalidSuffix, nil)
+}
+
 type treeNode struct {
 	children map[string]*treeNode
 }
diff --git a/services/device/internal/impl/applife/app_life_test.go b/services/device/internal/impl/applife/app_life_test.go
index 8484495..53ff693 100644
--- a/services/device/internal/impl/applife/app_life_test.go
+++ b/services/device/internal/impl/applife/app_life_test.go
@@ -57,7 +57,7 @@
 // TestLifeOfAnApp installs an app, instantiates, runs, kills, and deletes
 // several instances, and performs updates.
 func TestLifeOfAnApp(t *testing.T) {
-	ctx, shutdown := utiltest.InitForTest()
+	ctx, shutdown := utiltest.V23Init()
 	defer shutdown()
 
 	sh, deferFn := servicetest.CreateShellAndMountTable(t, ctx, nil)
@@ -78,7 +78,7 @@
 
 	// Set up the device manager.  Since we won't do device manager updates,
 	// don't worry about its application envelope and current link.
-	dmh := servicetest.RunCommand(t, sh, nil, utiltest.DeviceManagerCmd, "dm", root, helperPath, "unused_app_repo_name", "unused_curr_link")
+	dmh := servicetest.RunCommand(t, sh, nil, utiltest.DeviceManager, "dm", root, helperPath, "unused_app_repo_name", "unused_curr_link")
 	servicetest.ReadPID(t, dmh)
 	utiltest.ClaimDevice(t, ctx, "claimable", "dm", "mydevice", utiltest.NoPairingToken)
 
@@ -86,10 +86,10 @@
 	pingCh, cleanup := utiltest.SetupPingServer(t, ctx)
 	defer cleanup()
 
-	utiltest.Resolve(t, ctx, "pingserver", 1)
+	utiltest.Resolve(t, ctx, "pingserver", 1, true)
 
 	// Create an envelope for a first version of the app.
-	*envelope = utiltest.EnvelopeFromShell(sh, []string{utiltest.TestEnvVarName + "=env-val-envelope"}, utiltest.AppCmd, "google naps", 0, 0, fmt.Sprintf("--%s=flag-val-envelope", utiltest.TestFlagName), "appV1")
+	*envelope = utiltest.EnvelopeFromShell(sh, []string{utiltest.TestEnvVarName + "=env-val-envelope"}, utiltest.App, "google naps", 0, 0, fmt.Sprintf("--%s=flag-val-envelope", utiltest.TestFlagName), "appV1")
 
 	// Install the app.  The config-specified flag value for testFlagName
 	// should override the value specified in the envelope above, and the
@@ -139,18 +139,18 @@
 	// Wait until the app pings us that it's ready.
 	pingCh.VerifyPingArgs(t, utiltest.UserName(t), "flag-val-install", "env-val-envelope")
 
-	v1EP1 := utiltest.Resolve(t, ctx, "appV1", 1)[0]
+	v1EP1 := utiltest.Resolve(t, ctx, "appV1", 1, true)[0]
 
 	// Stop the app instance.
 	utiltest.KillApp(t, ctx, appID, instance1ID)
 	utiltest.VerifyState(t, ctx, device.InstanceStateNotRunning, appID, instance1ID)
-	utiltest.ResolveExpectNotFound(t, ctx, "appV1")
+	utiltest.ResolveExpectNotFound(t, ctx, "appV1", true)
 
 	utiltest.RunApp(t, ctx, appID, instance1ID)
 	utiltest.VerifyState(t, ctx, device.InstanceStateRunning, appID, instance1ID)
 	pingCh.VerifyPingArgs(t, utiltest.UserName(t), "flag-val-install", "env-val-envelope") // Wait until the app pings us that it's ready.
 	oldV1EP1 := v1EP1
-	if v1EP1 = utiltest.Resolve(t, ctx, "appV1", 1)[0]; v1EP1 == oldV1EP1 {
+	if v1EP1 = utiltest.Resolve(t, ctx, "appV1", 1, true)[0]; v1EP1 == oldV1EP1 {
 		t.Fatalf("Expected a new endpoint for the app after kill/run")
 	}
 
@@ -160,7 +160,7 @@
 
 	// There should be two endpoints mounted as "appV1", one for each
 	// instance of the app.
-	endpoints := utiltest.Resolve(t, ctx, "appV1", 2)
+	endpoints := utiltest.Resolve(t, ctx, "appV1", 2, true)
 	v1EP2 := endpoints[0]
 	if endpoints[0] == v1EP1 {
 		v1EP2 = endpoints[1]
@@ -177,7 +177,8 @@
 	// Kill the first instance.
 	utiltest.KillApp(t, ctx, appID, instance1ID)
 	// Only the second instance should still be running and mounted.
-	if want, got := v1EP2, utiltest.Resolve(t, ctx, "appV1", 1)[0]; want != got {
+	// In this case, we don't want to retry since we shouldn't need to.
+	if want, got := v1EP2, utiltest.Resolve(t, ctx, "appV1", 1, false)[0]; want != got {
 		t.Fatalf("Resolve(%v): want: %v, got %v", "appV1", want, got)
 	}
 
@@ -185,12 +186,12 @@
 	utiltest.UpdateAppExpectError(t, ctx, appID, impl.ErrUpdateNoOp.ID)
 
 	// Updating the installation should not work with a mismatched title.
-	*envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.AppCmd, "bogus", 0, 0)
+	*envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.App, "bogus", 0, 0)
 
 	utiltest.UpdateAppExpectError(t, ctx, appID, impl.ErrAppTitleMismatch.ID)
 
 	// Create a second version of the app and update the app to it.
-	*envelope = utiltest.EnvelopeFromShell(sh, []string{utiltest.TestEnvVarName + "=env-val-envelope"}, utiltest.AppCmd, "google naps", 0, 0, "appV2")
+	*envelope = utiltest.EnvelopeFromShell(sh, []string{utiltest.TestEnvVarName + "=env-val-envelope"}, utiltest.App, "google naps", 0, 0, "appV2")
 
 	utiltest.UpdateApp(t, ctx, appID)
 
@@ -199,8 +200,8 @@
 		t.Fatalf("Version did not change for %v: %v", appID, v1)
 	}
 
-	// Second instance should still be running.
-	if want, got := v1EP2, utiltest.Resolve(t, ctx, "appV1", 1)[0]; want != got {
+	// Second instance should still be running, don't retry.
+	if want, got := v1EP2, utiltest.Resolve(t, ctx, "appV1", 1, false)[0]; want != got {
 		t.Fatalf("Resolve(%v): want: %v, got %v", "appV1", want, got)
 	}
 	if v := utiltest.VerifyState(t, ctx, device.InstanceStateRunning, appID, instance2ID); v != v1 {
@@ -216,7 +217,7 @@
 	// Both instances should still be running the first version of the app.
 	// Check that the mounttable contains two endpoints, one of which is
 	// v1EP2.
-	endpoints = utiltest.Resolve(t, ctx, "appV1", 2)
+	endpoints = utiltest.Resolve(t, ctx, "appV1", 2, true)
 	if endpoints[0] == v1EP2 {
 		if endpoints[1] == v1EP2 {
 			t.Fatalf("Both endpoints are the same")
@@ -229,8 +230,8 @@
 	utiltest.UpdateInstanceExpectError(t, ctx, appID, instance1ID, impl.ErrInvalidOperation.ID)
 	// Stop first instance and try again.
 	utiltest.KillApp(t, ctx, appID, instance1ID)
-	// Only the second instance should still be running and mounted.
-	if want, got := v1EP2, utiltest.Resolve(t, ctx, "appV1", 1)[0]; want != got {
+	// Only the second instance should still be running and mounted, don't retry.
+	if want, got := v1EP2, utiltest.Resolve(t, ctx, "appV1", 1, false)[0]; want != got {
 		t.Fatalf("Resolve(%v): want: %v, got %v", "appV1", want, got)
 	}
 	// Update succeeds now.
@@ -241,13 +242,22 @@
 	// Resume the first instance and verify it's running v2 now.
 	utiltest.RunApp(t, ctx, appID, instance1ID)
 	pingCh.VerifyPingArgs(t, utiltest.UserName(t), "flag-val-install", "env-val-envelope")
-	utiltest.Resolve(t, ctx, "appV1", 1)
-	utiltest.Resolve(t, ctx, "appV2", 1)
+	utiltest.Resolve(t, ctx, "appV1", 1, false)
+	utiltest.Resolve(t, ctx, "appV2", 1, false)
 
-	// Stop first instance.
-	utiltest.TerminateApp(t, ctx, appID, instance1ID)
+	// Reverting first instance fails since it's still running.
+	utiltest.RevertAppExpectError(t, ctx, appID+"/"+instance1ID, impl.ErrInvalidOperation.ID)
+	// Stop first instance and try again.
+	utiltest.KillApp(t, ctx, appID, instance1ID)
 	verifyAppWorkspace(t, root, appID, instance1ID)
-	utiltest.ResolveExpectNotFound(t, ctx, "appV2")
+	utiltest.ResolveExpectNotFound(t, ctx, "appV2", true)
+	utiltest.RevertApp(t, ctx, appID+"/"+instance1ID)
+	// Resume the first instance and verify it's running v1 now.
+	utiltest.RunApp(t, ctx, appID, instance1ID)
+	pingCh.VerifyPingArgs(t, utiltest.UserName(t), "flag-val-install", "env-val-envelope")
+	utiltest.Resolve(t, ctx, "appV1", 2, false)
+	utiltest.TerminateApp(t, ctx, appID, instance1ID)
+	utiltest.Resolve(t, ctx, "appV1", 1, false)
 
 	// Start a third instance.
 	instance3ID := utiltest.LaunchApp(t, ctx, appID)
@@ -257,15 +267,18 @@
 	// Wait until the app pings us that it's ready.
 	pingCh.VerifyPingArgs(t, utiltest.UserName(t), "flag-val-install", "env-val-envelope")
 
-	utiltest.Resolve(t, ctx, "appV2", 1)
+	utiltest.Resolve(t, ctx, "appV2", 1, true)
 
-	// Stop second instance.
-	utiltest.TerminateApp(t, ctx, appID, instance2ID)
-	utiltest.ResolveExpectNotFound(t, ctx, "appV1")
+	// Suspend second instance.
+	utiltest.KillApp(t, ctx, appID, instance2ID)
+	utiltest.ResolveExpectNotFound(t, ctx, "appV1", true)
+
+	// Reverting second instance is a no-op since it's already running v1.
+	utiltest.RevertAppExpectError(t, ctx, appID+"/"+instance2ID, impl.ErrUpdateNoOp.ID)
 
 	// Stop third instance.
 	utiltest.TerminateApp(t, ctx, appID, instance3ID)
-	utiltest.ResolveExpectNotFound(t, ctx, "appV2")
+	utiltest.ResolveExpectNotFound(t, ctx, "appV2", true)
 
 	// Revert the app.
 	utiltest.RevertApp(t, ctx, appID)
@@ -279,9 +292,9 @@
 		t.Fatalf("Instance version expected to be %v, got %v instead", v1, v)
 	}
 	pingCh.VerifyPingArgs(t, utiltest.UserName(t), "flag-val-install", "env-val-envelope") // Wait until the app pings us that it's ready.
-	utiltest.Resolve(t, ctx, "appV1", 1)
+	utiltest.Resolve(t, ctx, "appV1", 1, true)
 	utiltest.TerminateApp(t, ctx, appID, instance4ID)
-	utiltest.ResolveExpectNotFound(t, ctx, "appV1")
+	utiltest.ResolveExpectNotFound(t, ctx, "appV1", true)
 
 	// We are already on the first version, no further revert possible.
 	utiltest.RevertAppExpectError(t, ctx, appID, impl.ErrUpdateNoOp.ID)
@@ -303,7 +316,7 @@
 	// cleanly Do this by installing, instantiating, running, and killing
 	// hangingApp, which sleeps (rather than exits) after being asked to
 	// Stop()
-	*envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.HangingAppCmd, "hanging ap", 0, 0, "hAppV1")
+	*envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.HangingApp, "hanging ap", 0, 0, "hAppV1")
 	hAppID := utiltest.InstallApp(t, ctx)
 	hInstanceID := utiltest.LaunchApp(t, ctx, hAppID)
 	hangingPid := pingCh.WaitForPingArgs(t).Pid
diff --git a/services/device/internal/impl/applife/instance_reaping_test.go b/services/device/internal/impl/applife/instance_reaping_test.go
index dfe3592..78d0eca 100644
--- a/services/device/internal/impl/applife/instance_reaping_test.go
+++ b/services/device/internal/impl/applife/instance_reaping_test.go
@@ -23,7 +23,7 @@
 
 	// Set up the device manager.  Since we won't do device manager updates,
 	// don't worry about its application envelope and current link.
-	dmh := servicetest.RunCommand(t, sh, nil, utiltest.DeviceManagerCmd, "dm", root, helperPath, "unused_app_repo_name", "unused_curr_link")
+	dmh := servicetest.RunCommand(t, sh, nil, utiltest.DeviceManager, "dm", root, helperPath, "unused_app_repo_name", "unused_curr_link")
 	servicetest.ReadPID(t, dmh)
 	utiltest.ClaimDevice(t, ctx, "claimable", "dm", "mydevice", utiltest.NoPairingToken)
 
@@ -31,10 +31,10 @@
 	pingCh, cleanup := utiltest.SetupPingServer(t, ctx)
 	defer cleanup()
 
-	utiltest.Resolve(t, ctx, "pingserver", 1)
+	utiltest.Resolve(t, ctx, "pingserver", 1, true)
 
 	// Create an envelope for a first version of the app.
-	*envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.AppCmd, "google naps", 0, 0, "appV1")
+	*envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.App, "google naps", 0, 0, "appV1")
 
 	// Install the app.  The config-specified flag value for testFlagName
 	// should override the value specified in the envelope above.
diff --git a/services/device/internal/impl/daemonreap/daemon_reaping_test.go b/services/device/internal/impl/daemonreap/daemon_reaping_test.go
index 84d0602..bcf4fc9 100644
--- a/services/device/internal/impl/daemonreap/daemon_reaping_test.go
+++ b/services/device/internal/impl/daemonreap/daemon_reaping_test.go
@@ -24,7 +24,7 @@
 
 	// Set up the device manager.  Since we won't do device manager updates,
 	// don't worry about its application envelope and current link.
-	dmh := servicetest.RunCommand(t, sh, nil, utiltest.DeviceManagerCmd, "dm", root, helperPath, "unused_app_repo_name", "unused_curr_link")
+	dmh := servicetest.RunCommand(t, sh, nil, utiltest.DeviceManager, "dm", root, helperPath, "unused_app_repo_name", "unused_curr_link")
 	servicetest.ReadPID(t, dmh)
 	utiltest.ClaimDevice(t, ctx, "claimable", "dm", "mydevice", utiltest.NoPairingToken)
 
@@ -32,10 +32,10 @@
 	pingCh, cleanup := utiltest.SetupPingServer(t, ctx)
 	defer cleanup()
 
-	utiltest.Resolve(t, ctx, "pingserver", 1)
+	utiltest.Resolve(t, ctx, "pingserver", 1, true)
 
 	// Create an envelope for a first version of the app that will be restarted once.
-	*envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.AppCmd, "google naps", 1, 10*time.Second, "appV1")
+	*envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.App, "google naps", 1, 10*time.Second, "appV1")
 	appID := utiltest.InstallApp(t, ctx)
 
 	// Start an instance of the app.
diff --git a/services/device/internal/impl/daemonreap/instance_reaping_kill_test.go b/services/device/internal/impl/daemonreap/instance_reaping_kill_test.go
index c04f8b0..b756afd 100644
--- a/services/device/internal/impl/daemonreap/instance_reaping_kill_test.go
+++ b/services/device/internal/impl/daemonreap/instance_reaping_kill_test.go
@@ -32,17 +32,17 @@
 	defer os.RemoveAll(dmCreds)
 	dmEnv := []string{fmt.Sprintf("%v=%v", ref.EnvCredentials, dmCreds)}
 
-	dmh := servicetest.RunCommand(t, sh, dmEnv, utiltest.DeviceManagerCmd, "dm", root, helperPath, "unused_app_repo_name", "unused_curr_link")
+	dmh := servicetest.RunCommand(t, sh, dmEnv, utiltest.DeviceManager, "dm", root, helperPath, "unused_app_repo_name", "unused_curr_link")
 	servicetest.ReadPID(t, dmh)
 	utiltest.ClaimDevice(t, ctx, "claimable", "dm", "mydevice", utiltest.NoPairingToken)
 
 	// Create the local server that the app uses to let us know it's ready.
 	pingCh, cleanup := utiltest.SetupPingServer(t, ctx)
 	defer cleanup()
-	utiltest.Resolve(t, ctx, "pingserver", 1)
+	utiltest.Resolve(t, ctx, "pingserver", 1, true)
 
 	// Create an envelope for the app.
-	*envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.AppCmd, "google naps", 0, 0, "appV1")
+	*envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.App, "google naps", 0, 0, "appV1")
 
 	// Install the app.
 	appID := utiltest.InstallApp(t, ctx)
@@ -62,7 +62,7 @@
 	dmh.Expect("dm terminated")
 	dmh.ExpectEOF()
 	dmh.Shutdown(os.Stderr, os.Stderr)
-	utiltest.ResolveExpectNotFound(t, ctx, "dm") // Ensure a clean slate.
+	utiltest.ResolveExpectNotFound(t, ctx, "dm", false) // Ensure a clean slate.
 
 	// Kill instance[0] and wait until it exits before proceeding.
 	syscall.Kill(pid, 9)
@@ -77,9 +77,9 @@
 	}
 
 	// Run another device manager to replace the dead one.
-	dmh = servicetest.RunCommand(t, sh, dmEnv, utiltest.DeviceManagerCmd, "dm", root, helperPath, "unused_app_repo_name", "unused_curr_link")
+	dmh = servicetest.RunCommand(t, sh, dmEnv, utiltest.DeviceManager, "dm", root, helperPath, "unused_app_repo_name", "unused_curr_link")
 	servicetest.ReadPID(t, dmh)
-	utiltest.Resolve(t, ctx, "dm", 1) // Verify the device manager has published itself.
+	utiltest.Resolve(t, ctx, "dm", 1, true) // Verify the device manager has published itself.
 
 	// By now, we've reconciled the state of the tree with which processes
 	// are actually alive. instance-0 is not alive.
diff --git a/services/device/internal/impl/device_service.go b/services/device/internal/impl/device_service.go
index ce87157..1b033b9 100644
--- a/services/device/internal/impl/device_service.go
+++ b/services/device/internal/impl/device_service.go
@@ -671,7 +671,7 @@
 	if versionDir == "." {
 		versionDir = "base"
 	}
-	return device.StatusInstance{Value: device.InstanceStatus{
+	return device.StatusDevice{Value: device.DeviceStatus{
 		State:   state,
 		Version: versionDir,
 	}}, nil
diff --git a/services/device/internal/impl/globsuid/glob_test.go b/services/device/internal/impl/globsuid/glob_test.go
index 30fdc23..dfa826d 100644
--- a/services/device/internal/impl/globsuid/glob_test.go
+++ b/services/device/internal/impl/globsuid/glob_test.go
@@ -15,7 +15,7 @@
 )
 
 func TestDeviceManagerGlobAndDebug(t *testing.T) {
-	ctx, shutdown := utiltest.InitForTest()
+	ctx, shutdown := utiltest.V23Init()
 	defer shutdown()
 
 	sh, deferFn := servicetest.CreateShellAndMountTable(t, ctx, nil)
@@ -36,7 +36,7 @@
 
 	// Set up the device manager.  Since we won't do device manager updates,
 	// don't worry about its application envelope and current link.
-	dmh := servicetest.RunCommand(t, sh, nil, utiltest.DeviceManagerCmd, "dm", root, helperPath, "unused_app_repo_name", "unused_curr_link")
+	dmh := servicetest.RunCommand(t, sh, nil, utiltest.DeviceManager, "dm", root, helperPath, "unused_app_repo_name", "unused_curr_link")
 	pid := servicetest.ReadPID(t, dmh)
 	defer syscall.Kill(pid, syscall.SIGINT)
 
@@ -45,7 +45,7 @@
 	defer cleanup()
 
 	// Create the envelope for the first version of the app.
-	*envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.AppCmd, "google naps", 0, 0, "appV1")
+	*envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.App, "google naps", 0, 0, "appV1")
 
 	// Device must be claimed before applications can be installed.
 	utiltest.ClaimDevice(t, ctx, "claimable", "dm", "mydevice", utiltest.NoPairingToken)
diff --git a/services/device/internal/impl/globsuid/signature_match_test.go b/services/device/internal/impl/globsuid/signature_match_test.go
index ded825f..fa79511 100644
--- a/services/device/internal/impl/globsuid/signature_match_test.go
+++ b/services/device/internal/impl/globsuid/signature_match_test.go
@@ -26,7 +26,7 @@
 )
 
 func TestDownloadSignatureMatch(t *testing.T) {
-	ctx, shutdown := utiltest.InitForTest()
+	ctx, shutdown := utiltest.V23Init()
 	defer shutdown()
 
 	sh, deferFn := servicetest.CreateShellAndMountTable(t, ctx, nil)
@@ -73,7 +73,7 @@
 
 	// Set up the device manager.  Since we won't do device manager updates,
 	// don't worry about its application envelope and current link.
-	dmh := servicetest.RunCommand(t, sh, nil, utiltest.DeviceManagerCmd, "dm", root, helperPath, "unused_app_repo_name", "unused_curr_link")
+	dmh := servicetest.RunCommand(t, sh, nil, utiltest.DeviceManager, "dm", root, helperPath, "unused_app_repo_name", "unused_curr_link")
 	pid := servicetest.ReadPID(t, dmh)
 	defer syscall.Kill(pid, syscall.SIGINT)
 	utiltest.ClaimDevice(t, ctx, "claimable", "dm", "mydevice", utiltest.NoPairingToken)
diff --git a/services/device/internal/impl/globsuid/suid_test.go b/services/device/internal/impl/globsuid/suid_test.go
index 12afe73..de99a2f 100644
--- a/services/device/internal/impl/globsuid/suid_test.go
+++ b/services/device/internal/impl/globsuid/suid_test.go
@@ -38,7 +38,7 @@
 }
 
 func TestAppWithSuidHelper(t *testing.T) {
-	ctx, shutdown := utiltest.InitForTest()
+	ctx, shutdown := utiltest.V23Init()
 	defer shutdown()
 
 	// Identity provider used to ensure that all processes recognize each
@@ -67,7 +67,7 @@
 	// Create a script wrapping the test target that implements suidhelper.
 	helperPath := utiltest.GenerateSuidHelperScript(t, root)
 
-	dmh := servicetest.RunCommand(t, sh, nil, utiltest.DeviceManagerCmd, "-mocksetuid", "dm", root, helperPath, "unused_app_repo_name", "unused_curr_link")
+	dmh := servicetest.RunCommand(t, sh, nil, utiltest.DeviceManager, "-mocksetuid", "dm", root, helperPath, "unused_app_repo_name", "unused_curr_link")
 	pid := servicetest.ReadPID(t, dmh)
 	defer syscall.Kill(pid, syscall.SIGINT)
 	defer utiltest.VerifyNoRunningProcesses(t)
@@ -82,7 +82,7 @@
 	defer cleanup()
 
 	// Create an envelope for a first version of the app.
-	*envelope = utiltest.EnvelopeFromShell(sh, []string{utiltest.TestEnvVarName + "=env-var"}, utiltest.AppCmd, "google naps", 0, 0, fmt.Sprintf("--%s=flag-val-envelope", utiltest.TestFlagName), "appV1")
+	*envelope = utiltest.EnvelopeFromShell(sh, []string{utiltest.TestEnvVarName + "=env-var"}, utiltest.App, "google naps", 0, 0, fmt.Sprintf("--%s=flag-val-envelope", utiltest.TestFlagName), "appV1")
 
 	// Install and start the app as root/self.
 	appID := utiltest.InstallApp(t, selfCtx)
diff --git a/services/device/internal/impl/impl_test.go b/services/device/internal/impl/impl_test.go
index 8e21f14..a0cb125 100644
--- a/services/device/internal/impl/impl_test.go
+++ b/services/device/internal/impl/impl_test.go
@@ -74,7 +74,7 @@
 // command. Further versions are running through the soft link that the device
 // manager itself updates.
 func TestDeviceManagerUpdateAndRevert(t *testing.T) {
-	ctx, shutdown := utiltest.InitForTest()
+	ctx, shutdown := utiltest.V23Init()
 	defer shutdown()
 
 	sh, deferFn := servicetest.CreateShellAndMountTable(t, ctx, v23.GetPrincipal(ctx))
@@ -108,7 +108,7 @@
 	defer os.RemoveAll(dmCreds)
 	dmEnv := []string{fmt.Sprintf("%v=%v", ref.EnvCredentials, dmCreds)}
 	dmArgs := []string{"factoryDM", root, "unused_helper", utiltest.MockApplicationRepoName, currLink}
-	args, env := sh.CommandEnvelope(utiltest.DeviceManagerCmd, dmEnv, dmArgs...)
+	args, env := sh.ProgramEnvelope(dmEnv, utiltest.DeviceManager, dmArgs...)
 	scriptPathFactory := generateDeviceManagerScript(t, root, args, env)
 
 	if err := os.Symlink(scriptPathFactory, currLink); err != nil {
@@ -126,14 +126,14 @@
 	// demonstrates that the initial device manager could be running by hand
 	// as long as the right initial configuration is passed into the device
 	// manager implementation.
-	dmh := servicetest.RunCommand(t, sh, dmPauseBeforeStopEnv, utiltest.DeviceManagerCmd, dmArgs...)
+	dmh := servicetest.RunCommand(t, sh, dmPauseBeforeStopEnv, utiltest.DeviceManager, dmArgs...)
 	defer func() {
 		syscall.Kill(dmh.Pid(), syscall.SIGINT)
 		utiltest.VerifyNoRunningProcesses(t)
 	}()
 
 	servicetest.ReadPID(t, dmh)
-	utiltest.Resolve(t, ctx, "claimable", 1)
+	utiltest.Resolve(t, ctx, "claimable", 1, true)
 	// Brand new device manager must be claimed first.
 	utiltest.ClaimDevice(t, ctx, "claimable", "factoryDM", "mydevice", utiltest.NoPairingToken)
 
@@ -142,7 +142,7 @@
 	}
 
 	// Simulate an invalid envelope in the application repository.
-	*envelope = utiltest.EnvelopeFromShell(sh, dmPauseBeforeStopEnv, utiltest.DeviceManagerCmd, "bogus", 0, 0, dmArgs...)
+	*envelope = utiltest.EnvelopeFromShell(sh, dmPauseBeforeStopEnv, utiltest.DeviceManager, "bogus", 0, 0, dmArgs...)
 
 	utiltest.UpdateDeviceExpectError(t, ctx, "factoryDM", impl.ErrAppTitleMismatch.ID)
 	utiltest.RevertDeviceExpectError(t, ctx, "factoryDM", impl.ErrUpdateNoOp.ID)
@@ -150,7 +150,7 @@
 	// Set up a second version of the device manager. The information in the
 	// envelope will be used by the device manager to stage the next
 	// version.
-	*envelope = utiltest.EnvelopeFromShell(sh, dmEnv, utiltest.DeviceManagerCmd, application.DeviceManagerTitle, 0, 0, "v2DM")
+	*envelope = utiltest.EnvelopeFromShell(sh, dmEnv, utiltest.DeviceManager, application.DeviceManagerTitle, 0, 0, "v2DM")
 	utiltest.UpdateDevice(t, ctx, "factoryDM")
 
 	// Current link should have been updated to point to v2.
@@ -177,12 +177,12 @@
 
 	// A successful update means the device manager has stopped itself.  We
 	// relaunch it from the current link.
-	utiltest.ResolveExpectNotFound(t, ctx, "v2DM") // Ensure a clean slate.
+	utiltest.ResolveExpectNotFound(t, ctx, "v2DM", false) // Ensure a clean slate.
 
-	dmh = servicetest.RunCommand(t, sh, dmEnv, utiltest.ExecScriptCmd, currLink)
+	dmh = servicetest.RunCommand(t, sh, dmEnv, utiltest.ExecScript, currLink)
 
 	servicetest.ReadPID(t, dmh)
-	utiltest.Resolve(t, ctx, "v2DM", 1) // Current link should have been launching v2.
+	utiltest.Resolve(t, ctx, "v2DM", 1, true) // Current link should have been launching v2.
 
 	// Try issuing an update without changing the envelope in the
 	// application repository: this should fail, and current link should be
@@ -194,8 +194,8 @@
 
 	// Try issuing an update with a binary that has a different major version
 	// number. It should fail.
-	utiltest.ResolveExpectNotFound(t, ctx, "v2.5DM") // Ensure a clean slate.
-	*envelope = utiltest.EnvelopeFromShell(sh, dmEnv, utiltest.DeviceManagerV10Cmd, application.DeviceManagerTitle, 0, 0, "v2.5DM")
+	utiltest.ResolveExpectNotFound(t, ctx, "v2.5DM", false) // Ensure a clean slate.
+	*envelope = utiltest.EnvelopeFromShell(sh, dmEnv, utiltest.DeviceManagerV10, application.DeviceManagerTitle, 0, 0, "v2.5DM")
 	utiltest.UpdateDeviceExpectError(t, ctx, "v2DM", impl.ErrOperationFailed.ID)
 
 	if evalLink() != scriptPathV2 {
@@ -203,7 +203,7 @@
 	}
 
 	// Create a third version of the device manager and issue an update.
-	*envelope = utiltest.EnvelopeFromShell(sh, dmEnv, utiltest.DeviceManagerCmd, application.DeviceManagerTitle, 0, 0, "v3DM")
+	*envelope = utiltest.EnvelopeFromShell(sh, dmEnv, utiltest.DeviceManager, application.DeviceManagerTitle, 0, 0, "v3DM")
 	utiltest.UpdateDevice(t, ctx, "v2DM")
 
 	scriptPathV3 := evalLink()
@@ -216,15 +216,15 @@
 
 	dmh.Shutdown(os.Stderr, os.Stderr)
 
-	utiltest.ResolveExpectNotFound(t, ctx, "v3DM") // Ensure a clean slate.
+	utiltest.ResolveExpectNotFound(t, ctx, "v3DM", false) // Ensure a clean slate.
 
 	// Re-lanuch the device manager from current link.  We instruct the
 	// device manager to pause before stopping its server, so that we can
 	// verify that a second revert fails while a revert is in progress.
-	dmh = servicetest.RunCommand(t, sh, dmPauseBeforeStopEnv, utiltest.ExecScriptCmd, currLink)
+	dmh = servicetest.RunCommand(t, sh, dmPauseBeforeStopEnv, utiltest.ExecScript, currLink)
 
 	servicetest.ReadPID(t, dmh)
-	utiltest.Resolve(t, ctx, "v3DM", 1) // Current link should have been launching v3.
+	utiltest.Resolve(t, ctx, "v3DM", 1, true) // Current link should have been launching v3.
 	v3 := utiltest.VerifyDeviceState(t, ctx, device.InstanceStateRunning, "v3DM")
 	if v2 == v3 {
 		t.Fatalf("version didn't change")
@@ -241,11 +241,11 @@
 	}
 	dmh.Shutdown(os.Stderr, os.Stderr)
 
-	utiltest.ResolveExpectNotFound(t, ctx, "v2DM") // Ensure a clean slate.
+	utiltest.ResolveExpectNotFound(t, ctx, "v2DM", false) // Ensure a clean slate.
 
-	dmh = servicetest.RunCommand(t, sh, dmEnv, utiltest.ExecScriptCmd, currLink)
+	dmh = servicetest.RunCommand(t, sh, dmEnv, utiltest.ExecScript, currLink)
 	servicetest.ReadPID(t, dmh)
-	utiltest.Resolve(t, ctx, "v2DM", 1) // Current link should have been launching v2.
+	utiltest.Resolve(t, ctx, "v2DM", 1, true) // Current link should have been launching v2.
 
 	// Revert the device manager to its previous version (factory).
 	utiltest.RevertDevice(t, ctx, "v2DM")
@@ -256,20 +256,20 @@
 	}
 	dmh.Shutdown(os.Stderr, os.Stderr)
 
-	utiltest.ResolveExpectNotFound(t, ctx, "factoryDM") // Ensure a clean slate.
+	utiltest.ResolveExpectNotFound(t, ctx, "factoryDM", false) // Ensure a clean slate.
 
-	dmh = servicetest.RunCommand(t, sh, dmEnv, utiltest.ExecScriptCmd, currLink)
+	dmh = servicetest.RunCommand(t, sh, dmEnv, utiltest.ExecScript, currLink)
 	servicetest.ReadPID(t, dmh)
-	utiltest.Resolve(t, ctx, "factoryDM", 1) // Current link should have been launching factory version.
+	utiltest.Resolve(t, ctx, "factoryDM", 1, true) // Current link should have been launching factory version.
 	utiltest.ShutdownDevice(t, ctx, "factoryDM")
 	dmh.Expect("factoryDM terminated")
 	dmh.ExpectEOF()
 
 	// Re-launch the device manager, to exercise the behavior of Stop.
-	utiltest.ResolveExpectNotFound(t, ctx, "factoryDM") // Ensure a clean slate.
-	dmh = servicetest.RunCommand(t, sh, dmEnv, utiltest.ExecScriptCmd, currLink)
+	utiltest.ResolveExpectNotFound(t, ctx, "factoryDM", false) // Ensure a clean slate.
+	dmh = servicetest.RunCommand(t, sh, dmEnv, utiltest.ExecScript, currLink)
 	servicetest.ReadPID(t, dmh)
-	utiltest.Resolve(t, ctx, "factoryDM", 1)
+	utiltest.Resolve(t, ctx, "factoryDM", 1, true)
 	utiltest.KillDevice(t, ctx, "factoryDM")
 	dmh.Expect("restart handler")
 	dmh.Expect("factoryDM terminated")
@@ -292,7 +292,7 @@
 // This should bring up a functioning device manager.  In the end it runs
 // Uninstall and verifies that the installation is gone.
 func TestDeviceManagerInstallation(t *testing.T) {
-	ctx, shutdown := utiltest.InitForTest()
+	ctx, shutdown := utiltest.V23Init()
 	defer shutdown()
 
 	sh, deferFn := servicetest.CreateShellAndMountTable(t, ctx, nil)
@@ -310,7 +310,7 @@
 	// Create an 'envelope' for the device manager that we can pass to the
 	// installer, to ensure that the device manager that the installer
 	// configures can run.
-	dmargs, dmenv := sh.CommandEnvelope(utiltest.DeviceManagerCmd, nil, "dm")
+	dmargs, dmenv := sh.ProgramEnvelope(nil, utiltest.DeviceManager, "dm")
 	dmDir := filepath.Join(testDir, "dm")
 	// TODO(caprita): Add test logic when initMode = true.
 	singleUser, sessionMode, initMode := true, true, false
@@ -318,7 +318,7 @@
 		t.Fatalf("SelfInstall failed: %v", err)
 	}
 
-	utiltest.ResolveExpectNotFound(t, ctx, "dm")
+	utiltest.ResolveExpectNotFound(t, ctx, "dm", false)
 	// Start the device manager.
 	stdout := make(simpleRW, 100)
 	defer os.Setenv(utiltest.RedirectEnv, os.Getenv(utiltest.RedirectEnv))
@@ -354,7 +354,7 @@
 // TODO(caprita): We need better test coverage for how updating/reverting apps
 // affects the package configured for the app.
 func TestDeviceManagerPackages(t *testing.T) {
-	ctx, shutdown := utiltest.InitForTest()
+	ctx, shutdown := utiltest.V23Init()
 	defer shutdown()
 
 	sh, deferFn := servicetest.CreateShellAndMountTable(t, ctx, nil)
@@ -404,7 +404,7 @@
 
 	// Set up the device manager.  Since we won't do device manager updates,
 	// don't worry about its application envelope and current link.
-	dmh := servicetest.RunCommand(t, sh, nil, utiltest.DeviceManagerCmd, "dm", root, helperPath, "unused_app_repo_name", "unused_curr_link")
+	dmh := servicetest.RunCommand(t, sh, nil, utiltest.DeviceManager, "dm", root, helperPath, "unused_app_repo_name", "unused_curr_link")
 	pid := servicetest.ReadPID(t, dmh)
 	defer syscall.Kill(pid, syscall.SIGINT)
 	defer utiltest.VerifyNoRunningProcesses(t)
@@ -414,7 +414,7 @@
 	defer cleanup()
 
 	// Create the envelope for the first version of the app.
-	*envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.AppCmd, "google naps", 0, 0, "appV1")
+	*envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.App, "google naps", 0, 0, "appV1")
 	envelope.Packages = map[string]application.SignedFile{
 		"test": application.SignedFile{
 			File: "realbin/testpkg",
@@ -494,7 +494,7 @@
 // TODO(rjkroege): Verify that associations persist across restarts once
 // permanent storage is added.
 func TestAccountAssociation(t *testing.T) {
-	ctx, shutdown := utiltest.InitForTest()
+	ctx, shutdown := utiltest.V23Init()
 	defer shutdown()
 
 	sh, deferFn := servicetest.CreateShellAndMountTable(t, ctx, nil)
@@ -519,7 +519,7 @@
 		v23.GetPrincipal(c).AddToRoots(v23.GetPrincipal(ctx).BlessingStore().Default())
 	}
 
-	dmh := servicetest.RunCommand(t, sh, nil, utiltest.DeviceManagerCmd, "dm", root, "unused_helper", "unused_app_repo_name", "unused_curr_link")
+	dmh := servicetest.RunCommand(t, sh, nil, utiltest.DeviceManager, "dm", root, "unused_helper", "unused_app_repo_name", "unused_curr_link")
 	pid := servicetest.ReadPID(t, dmh)
 	defer syscall.Kill(pid, syscall.SIGINT)
 	defer utiltest.VerifyNoRunningProcesses(t)
diff --git a/services/device/internal/impl/perms/debug_perms_test.go b/services/device/internal/impl/perms/debug_perms_test.go
index 296c3b8..e643c12 100644
--- a/services/device/internal/impl/perms/debug_perms_test.go
+++ b/services/device/internal/impl/perms/debug_perms_test.go
@@ -44,14 +44,14 @@
 	defer cleanup()
 
 	// Set up the device manager.
-	dmh := servicetest.RunCommand(t, sh, nil, utiltest.DeviceManagerCmd, "dm", root, helperPath, "unused_app_repo_name", "unused_curr_link")
+	dmh := servicetest.RunCommand(t, sh, nil, utiltest.DeviceManager, "dm", root, helperPath, "unused_app_repo_name", "unused_curr_link")
 	servicetest.ReadPID(t, dmh)
 	utiltest.ClaimDevice(t, ctx, "claimable", "dm", "mydevice", utiltest.NoPairingToken)
 
 	// Create the local server that the app uses to let us know it's ready.
 	pingCh, cleanup := utiltest.SetupPingServer(t, ctx)
 	defer cleanup()
-	utiltest.Resolve(t, ctx, "pingserver", 1)
+	utiltest.Resolve(t, ctx, "pingserver", 1, true)
 
 	// Make some users.
 	selfCtx := ctx
@@ -62,7 +62,7 @@
 	// TODO(rjkroege): Set AccessLists here that conflict with the one provided by the device
 	// manager and show that the one set here is overridden.
 	// Create the envelope for the first version of the app.
-	*envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.AppCmd, "google naps", 0, 0, "appV1")
+	*envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.App, "google naps", 0, 0, "appV1")
 
 	// Install the app.
 	appID := utiltest.InstallApp(t, ctx)
@@ -200,7 +200,7 @@
 	}
 
 	// Set up the device manager.
-	dmh := servicetest.RunCommand(t, sh, nil, utiltest.DeviceManagerCmd, "--log_dir="+extraLogDir, "dm", root, helperPath, "unused", "unused_curr_link")
+	dmh := servicetest.RunCommand(t, sh, nil, utiltest.DeviceManager, "--log_dir="+extraLogDir, "dm", root, helperPath, "unused", "unused_curr_link")
 	servicetest.ReadPID(t, dmh)
 
 	// Make some users.
diff --git a/services/device/internal/impl/perms/perms_test.go b/services/device/internal/impl/perms/perms_test.go
index 69fe3c1..99b4e84 100644
--- a/services/device/internal/impl/perms/perms_test.go
+++ b/services/device/internal/impl/perms/perms_test.go
@@ -26,7 +26,7 @@
 // TestDeviceManagerClaim claims a devicemanager and tests AccessList permissions on
 // its methods.
 func TestDeviceManagerClaim(t *testing.T) {
-	ctx, shutdown := utiltest.InitForTest()
+	ctx, shutdown := utiltest.V23Init()
 	defer shutdown()
 
 	// root blessing provider so that the principals of all the contexts
@@ -55,11 +55,11 @@
 	// Set up the device manager.  Since we won't do device manager updates,
 	// don't worry about its application envelope and current link.
 	pairingToken := "abcxyz"
-	dmh := servicetest.RunCommand(t, sh, nil, utiltest.DeviceManagerCmd, "dm", root, helperPath, "unused_app_repo_name", "unused_curr_link", pairingToken)
+	dmh := servicetest.RunCommand(t, sh, nil, utiltest.DeviceManager, "dm", root, helperPath, "unused_app_repo_name", "unused_curr_link", pairingToken)
 	pid := servicetest.ReadPID(t, dmh)
 	defer syscall.Kill(pid, syscall.SIGINT)
 
-	*envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.AppCmd, "google naps", 0, 0, "trapp")
+	*envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.App, "google naps", 0, 0, "trapp")
 
 	claimantCtx := utiltest.CtxWithNewPrincipal(t, ctx, idp, "claimant")
 	octx, err := v23.WithPrincipal(ctx, testutil.NewPrincipal("other"))
@@ -103,14 +103,14 @@
 
 	// Wait until the app pings us that it's ready.
 	pingCh.WaitForPingArgs(t)
-	utiltest.Resolve(t, ctx, "trapp", 1)
+	utiltest.Resolve(t, ctx, "trapp", 1, false)
 	utiltest.KillApp(t, claimantCtx, appID, instanceID)
 
 	// TODO(gauthamt): Test that AccessLists persist across devicemanager restarts
 }
 
 func TestDeviceManagerUpdateAccessList(t *testing.T) {
-	ctx, shutdown := utiltest.InitForTest()
+	ctx, shutdown := utiltest.V23Init()
 	defer shutdown()
 
 	// Identity provider to ensure that all processes recognize each
@@ -136,13 +136,13 @@
 
 	// Set up the device manager.  Since we won't do device manager updates,
 	// don't worry about its application envelope and current link.
-	dmh := servicetest.RunCommand(t, sh, nil, utiltest.DeviceManagerCmd, "dm", root, "unused_helper", "unused_app_repo_name", "unused_curr_link")
+	dmh := servicetest.RunCommand(t, sh, nil, utiltest.DeviceManager, "dm", root, "unused_helper", "unused_app_repo_name", "unused_curr_link")
 	pid := servicetest.ReadPID(t, dmh)
 	defer syscall.Kill(pid, syscall.SIGINT)
 	defer utiltest.VerifyNoRunningProcesses(t)
 
 	// Create an envelope for an app.
-	*envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.AppCmd, "google naps", 0, 0)
+	*envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.App, "google naps", 0, 0)
 
 	// On an unclaimed device manager, there will be no AccessLists.
 	if _, _, err := device.DeviceClient("claimable").GetPermissions(selfCtx); err == nil {
diff --git a/services/device/internal/impl/proxy_invoker_test.go b/services/device/internal/impl/proxy_invoker_test.go
index 8cb7bf7..ad4a8aa 100644
--- a/services/device/internal/impl/proxy_invoker_test.go
+++ b/services/device/internal/impl/proxy_invoker_test.go
@@ -23,7 +23,7 @@
 // TODO(toddw): Add tests of Signature and MethodSignature.
 
 func TestProxyInvoker(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	v23.GetNamespace(ctx).CacheCtl(naming.DisableCache(true))
diff --git a/services/device/internal/impl/reaping/instance_reaping_test.go b/services/device/internal/impl/reaping/instance_reaping_test.go
index 81dd2ac..55ed52f 100644
--- a/services/device/internal/impl/reaping/instance_reaping_test.go
+++ b/services/device/internal/impl/reaping/instance_reaping_test.go
@@ -33,17 +33,17 @@
 	defer os.RemoveAll(dmCreds)
 	dmEnv := []string{fmt.Sprintf("%v=%v", ref.EnvCredentials, dmCreds), fmt.Sprintf("%v=%v", impl.AppcycleReconciliation, "1")}
 
-	dmh := servicetest.RunCommand(t, sh, dmEnv, utiltest.DeviceManagerCmd, "dm", root, helperPath, "unused_app_repo_name", "unused_curr_link")
+	dmh := servicetest.RunCommand(t, sh, dmEnv, utiltest.DeviceManager, "dm", root, helperPath, "unused_app_repo_name", "unused_curr_link")
 	servicetest.ReadPID(t, dmh)
 	utiltest.ClaimDevice(t, ctx, "claimable", "dm", "mydevice", utiltest.NoPairingToken)
 
 	// Create the local server that the app uses to let us know it's ready.
 	pingCh, cleanup := utiltest.SetupPingServer(t, ctx)
 	defer cleanup()
-	utiltest.Resolve(t, ctx, "pingserver", 1)
+	utiltest.Resolve(t, ctx, "pingserver", 1, true)
 
 	// Create an envelope for the app.
-	*envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.AppCmd, "google naps", 0, 0, "appV1")
+	*envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.App, "google naps", 0, 0, "appV1")
 
 	// Install the app.
 	appID := utiltest.InstallApp(t, ctx)
@@ -63,7 +63,7 @@
 	dmh.Expect("dm terminated")
 	dmh.ExpectEOF()
 	dmh.Shutdown(os.Stderr, os.Stderr)
-	utiltest.ResolveExpectNotFound(t, ctx, "dm") // Ensure a clean slate.
+	utiltest.ResolveExpectNotFound(t, ctx, "dm", false) // Ensure a clean slate.
 
 	// Kill instance[0] and wait until it exits before proceeding.
 	syscall.Kill(pid, 9)
@@ -78,9 +78,9 @@
 	}
 
 	// Run another device manager to replace the dead one.
-	dmh = servicetest.RunCommand(t, sh, dmEnv, utiltest.DeviceManagerCmd, "dm", root, helperPath, "unused_app_repo_name", "unused_curr_link")
+	dmh = servicetest.RunCommand(t, sh, dmEnv, utiltest.DeviceManager, "dm", root, helperPath, "unused_app_repo_name", "unused_curr_link")
 	servicetest.ReadPID(t, dmh)
-	utiltest.Resolve(t, ctx, "dm", 1) // Verify the device manager has published itself.
+	utiltest.Resolve(t, ctx, "dm", 1, true) // Verify the device manager has published itself.
 
 	// By now, we've reconciled the state of the tree with which processes
 	// are actually alive. instance-0 is not alive.
diff --git a/services/device/internal/impl/utiltest/app.go b/services/device/internal/impl/utiltest/app.go
index 47d88a4..7ef524b 100644
--- a/services/device/internal/impl/utiltest/app.go
+++ b/services/device/internal/impl/utiltest/app.go
@@ -8,7 +8,6 @@
 	"encoding/json"
 	"flag"
 	"fmt"
-	"io"
 	"io/ioutil"
 	"os"
 	"path/filepath"
@@ -22,13 +21,12 @@
 	"v.io/v23/naming"
 	"v.io/v23/rpc"
 	"v.io/v23/security"
-
 	"v.io/x/lib/vlog"
-
 	"v.io/x/ref/lib/signals"
 	"v.io/x/ref/services/device/internal/suid"
 	"v.io/x/ref/services/internal/servicetest"
 	"v.io/x/ref/test"
+	"v.io/x/ref/test/modules"
 	"v.io/x/ref/test/testutil"
 )
 
@@ -113,9 +111,11 @@
 	return content, nil
 }
 
-// app is a test application. It pings the invoking device manager with state information.
-func app(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
-	ctx, shutdown := test.InitForTest()
+// App is a test application. It pings the invoking device manager with state information.
+var App = modules.Register(appFunc, "App")
+
+func appFunc(env *modules.Env, args ...string) error {
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	v23.GetNamespace(ctx).CacheCtl(naming.DisableCache(true))
@@ -193,9 +193,10 @@
 	}
 }
 
-// Same as app, except that it does not exit properly after being stopped
-func hangingApp(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
-	err := app(stdin, stdout, stderr, env, args...)
+// HangingApp is the same as App, except that it does not exit properly after
+// being stopped.
+var HangingApp = modules.Register(func(env *modules.Env, args ...string) error {
+	err := appFunc(env, args...)
 	time.Sleep(24 * time.Hour)
 	return err
-}
+}, "HangingApp")
diff --git a/services/device/internal/impl/utiltest/helpers.go b/services/device/internal/impl/utiltest/helpers.go
index d461eb2..614ff22 100644
--- a/services/device/internal/impl/utiltest/helpers.go
+++ b/services/device/internal/impl/utiltest/helpers.go
@@ -69,8 +69,8 @@
 	}
 }
 
-func EnvelopeFromShell(sh *modules.Shell, env []string, cmd, title string, retries int, window time.Duration, args ...string) application.Envelope {
-	args, nenv := sh.CommandEnvelope(cmd, env, args...)
+func EnvelopeFromShell(sh *modules.Shell, env []string, prog modules.Program, title string, retries int, window time.Duration, args ...string) application.Envelope {
+	args, nenv := sh.ProgramEnvelope(env, prog, args...)
 	return application.Envelope{
 		Title: title,
 		Args:  args[1:],
@@ -84,32 +84,51 @@
 }
 
 // ResolveExpectNotFound verifies that the given name is not in the mounttable.
-func ResolveExpectNotFound(t *testing.T, ctx *context.T, name string) {
-	if me, err := v23.GetNamespace(ctx).Resolve(ctx, name); err == nil {
-		t.Fatalf(testutil.FormatLogLine(2, "Resolve(%v) succeeded with results %v when it was expected to fail", name, me.Names()))
-	} else if expectErr := naming.ErrNoSuchName.ID; verror.ErrorID(err) != expectErr {
-		t.Fatalf(testutil.FormatLogLine(2, "Resolve(%v) failed with error %v, expected error ID %v", name, err, expectErr))
+func ResolveExpectNotFound(t *testing.T, ctx *context.T, name string, retry bool) {
+	expectErr := naming.ErrNoSuchName.ID
+	for {
+		me, err := v23.GetNamespace(ctx).Resolve(ctx, name)
+		if err == nil || verror.ErrorID(err) != expectErr {
+			if retry {
+				time.Sleep(10 * time.Millisecond)
+				continue
+			}
+			if err == nil {
+				t.Fatalf(testutil.FormatLogLine(2, "Resolve(%v) succeeded with results %v when it was expected to fail", name, me.Names()))
+			} else {
+				t.Fatalf(testutil.FormatLogLine(2, "Resolve(%v) failed with error %v, expected error ID %v", name, err, expectErr))
+			}
+		} else {
+			return
+		}
 	}
 }
 
 // Resolve looks up the given name in the mounttable.
-func Resolve(t *testing.T, ctx *context.T, name string, replicas int) []string {
-	me, err := v23.GetNamespace(ctx).Resolve(ctx, name)
-	if err != nil {
-		t.Fatalf("Resolve(%v) failed: %v", name, err)
-	}
-
-	filteredResults := []string{}
-	for _, r := range me.Names() {
-		if strings.Index(r, "@tcp") != -1 {
-			filteredResults = append(filteredResults, r)
+func Resolve(t *testing.T, ctx *context.T, name string, replicas int, retry bool) []string {
+	for {
+		me, err := v23.GetNamespace(ctx).Resolve(ctx, name)
+		if err != nil {
+			if retry {
+				time.Sleep(10 * time.Millisecond)
+				continue
+			} else {
+				t.Fatalf("Resolve(%v) failed: %v", name, err)
+			}
 		}
+
+		filteredResults := []string{}
+		for _, r := range me.Names() {
+			if strings.Index(r, "@tcp") != -1 {
+				filteredResults = append(filteredResults, r)
+			}
+		}
+		// We are going to get a websocket and a tcp endpoint for each replica.
+		if want, got := replicas, len(filteredResults); want != got {
+			t.Fatalf("Resolve(%v) expected %d result(s), got %d instead", name, want, got)
+		}
+		return filteredResults
 	}
-	// We are going to get a websocket and a tcp endpoint for each replica.
-	if want, got := replicas, len(filteredResults); want != got {
-		t.Fatalf("Resolve(%v) expected %d result(s), got %d instead", name, want, got)
-	}
-	return filteredResults
 }
 
 // The following set of functions are convenience wrappers around Update and
@@ -390,7 +409,7 @@
 	if err != nil {
 		t.Fatalf(testutil.FormatLogLine(2, "Status(%v) failed: %v [%v]", name, verror.ErrorID(err), err))
 	}
-	status, ok := s.(device.StatusInstance)
+	status, ok := s.(device.StatusDevice)
 	if !ok {
 		t.Fatalf(testutil.FormatLogLine(2, "Status(%v) returned unknown type: %T", name, s))
 	}
@@ -509,7 +528,7 @@
 // TODO(rjkroege): This helper is generally useful. Use it to reduce
 // boiler plate across all device manager tests.
 func StartupHelper(t *testing.T) (func(), *context.T, *modules.Shell, *application.Envelope, string, string, *testutil.IDProvider) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	v23.GetNamespace(ctx).CacheCtl(naming.DisableCache(true))
 
 	// Make a new identity context.
@@ -700,12 +719,12 @@
 	return u.Username
 }
 
-func InitForTest() (*context.T, v23.Shutdown) {
+func V23Init() (*context.T, v23.Shutdown) {
 	roots, _ := ref.EnvNamespaceRoots()
 	for key, _ := range roots {
 		os.Unsetenv(key)
 	}
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	v23.GetNamespace(ctx).CacheCtl(naming.DisableCache(true))
 	return ctx, shutdown
 }
diff --git a/services/device/internal/impl/utiltest/modules.go b/services/device/internal/impl/utiltest/modules.go
index 96d6f6b..84f1d00 100644
--- a/services/device/internal/impl/utiltest/modules.go
+++ b/services/device/internal/impl/utiltest/modules.go
@@ -6,7 +6,6 @@
 
 import (
 	"fmt"
-	"io"
 	"os"
 	goexec "os/exec"
 	"strings"
@@ -31,48 +30,42 @@
 	RedirectEnv    = "DEVICE_MANAGER_DONT_REDIRECT_STDOUT_STDERR"
 	TestEnvVarName = "V23_RANDOM_ENV_VALUE"
 	NoPairingToken = ""
-
-	// Modules names.
-	ExecScriptCmd       = "execScript"
-	DeviceManagerCmd    = "deviceManager"
-	DeviceManagerV10Cmd = "deviceManagerV10" // deviceManager with a different major version number
-	AppCmd              = "app"
-	HangingAppCmd       = "hangingApp"
 )
 
-// execScript launches the script passed as argument.
-func execScript(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+// ExecScript launches the script passed as argument.
+var ExecScript = modules.Register(func(env *modules.Env, args ...string) error {
 	if want, got := 1, len(args); want != got {
-		vlog.Fatalf("execScript expected %d arguments, got %d instead", want, got)
+		vlog.Fatalf("ExecScript expected %d arguments, got %d instead", want, got)
 	}
 	script := args[0]
 	osenv := []string{RedirectEnv + "=1"}
-	if env["PAUSE_BEFORE_STOP"] == "1" {
+	if env.Vars["PAUSE_BEFORE_STOP"] == "1" {
 		osenv = append(osenv, "PAUSE_BEFORE_STOP=1")
 	}
 
 	cmd := goexec.Cmd{
 		Path:   script,
 		Env:    osenv,
-		Stdin:  stdin,
-		Stderr: stderr,
-		Stdout: stdout,
+		Stdin:  env.Stdin,
+		Stderr: env.Stderr,
+		Stdout: env.Stdout,
 	}
-
 	return cmd.Run()
-}
+}, "ExecScript")
 
-// deviceManager sets up a device manager server.  It accepts the name to
+// DeviceManager sets up a device manager server.  It accepts the name to
 // publish the server under as an argument.  Additional arguments can optionally
 // specify device manager config settings.
-func deviceManager(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
-	ctx, shutdown := test.InitForTest()
+var DeviceManager = modules.Register(deviceManagerFunc, "DeviceManager")
+
+func deviceManagerFunc(env *modules.Env, args ...string) error {
+	ctx, shutdown := test.V23Init()
 	if len(args) == 0 {
 		vlog.Fatalf("deviceManager expected at least an argument")
 	}
 	publishName := args[0]
 	args = args[1:]
-	defer fmt.Fprintf(stdout, "%v terminated\n", publishName)
+	defer fmt.Fprintf(env.Stdout, "%v terminated\n", publishName)
 	defer vlog.VI(1).Infof("%v terminated", publishName)
 	defer shutdown()
 	v23.GetNamespace(ctx).CacheCtl(naming.DisableCache(true))
@@ -134,11 +127,11 @@
 	}
 	// Manually mount the claimable service in the 'global' mounttable.
 	v23.GetNamespace(ctx).Mount(ctx, "claimable", claimableName, 0)
-	fmt.Fprintf(stdout, "ready:%d\n", os.Getpid())
+	fmt.Fprintf(env.Stdout, "ready:%d\n", os.Getpid())
 
 	<-shutdownChan
-	if val, present := env["PAUSE_BEFORE_STOP"]; present && val == "1" {
-		modules.WaitForEOF(stdin)
+	if val, present := env.Vars["PAUSE_BEFORE_STOP"]; present && val == "1" {
+		modules.WaitForEOF(env.Stdin)
 	}
 	// TODO(ashankar): Figure out a way to incorporate this check in the test.
 	// if impl.DispatcherLeaking(dispatcher) {
@@ -148,15 +141,15 @@
 }
 
 // This is the same as DeviceManager above, except that it has a different major version number
-func deviceManagerV10(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+var DeviceManagerV10 = modules.Register(func(env *modules.Env, args ...string) error {
 	impl.CurrentVersion = impl.Version{10, 0} // Set the version number to 10.0
-	return deviceManager(stdin, stdout, stderr, env, args...)
-}
+	return deviceManagerFunc(env, args...)
+}, "DeviceManagerV10")
 
 func TestMainImpl(m *testing.M) {
 	test.Init()
 	isSuidHelper := len(os.Getenv("V23_SUIDHELPER_TEST")) > 0
-	if modules.IsModulesChildProcess() && !isSuidHelper {
+	if modules.IsChildProcess() && !isSuidHelper {
 		if err := modules.Dispatch(); err != nil {
 			fmt.Fprintf(os.Stderr, "modules.Dispatch failed: %v\n", err)
 			os.Exit(1)
@@ -177,13 +170,3 @@
 		vlog.Fatalf("Failed to Run() setuidhelper: %v", err)
 	}
 }
-
-func init() {
-	modules.RegisterChild("execScript", `execScript launches the script passed as argument.`, execScript)
-	modules.RegisterChild("deviceManager", `deviceManager sets up a device manager server.  It accepts the name to
-publish the server under as an argument.  Additional arguments can optionally
- specify device manager config settings.`, deviceManager)
-	modules.RegisterChild("deviceManagerV10", `This is the same as deviceManager above, except that it has a different major version number`, deviceManagerV10)
-	modules.RegisterChild("app", ``, app)
-	modules.RegisterChild("hangingApp", `Same as app, except that it does not exit properly after being stopped`, hangingApp)
-}
diff --git a/services/device/mgmt_v23_test.go b/services/device/mgmt_v23_test.go
index c07c9da..61d2ada 100644
--- a/services/device/mgmt_v23_test.go
+++ b/services/device/mgmt_v23_test.go
@@ -126,7 +126,7 @@
 		// Create those credentials and options to use to setup the
 		// binaries with them.
 		aliceCreds, _ = i.Shell().NewChildCredentials("alice")
-		aliceOpts     = i.Shell().DefaultStartOpts().ExternalCommand().WithCustomCredentials(aliceCreds)
+		aliceOpts     = i.Shell().DefaultStartOpts().ExternalProgram().WithCustomCredentials(aliceCreds)
 
 		// Build all the command-line tools and set them up to run as alice.
 		// applicationd/binaryd servers will be run by alice too.
diff --git a/services/device/v23_test.go b/services/device/v23_test.go
index 3eeab2f..0bb141e 100644
--- a/services/device/v23_test.go
+++ b/services/device/v23_test.go
@@ -4,16 +4,21 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package device_test
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
-import "v.io/x/ref/test/v23tests"
+	"v.io/x/ref/test"
+	"v.io/x/ref/test/modules"
+	"v.io/x/ref/test/v23tests"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
+	modules.DispatchAndExitIfChild()
 	cleanup := v23tests.UseSharedBinDir()
 	r := m.Run()
 	cleanup()
diff --git a/services/identity/identityd/v23_test.go b/services/identity/identityd/v23_test.go
index 13c2816..17cc37d 100644
--- a/services/identity/identityd/v23_test.go
+++ b/services/identity/identityd/v23_test.go
@@ -4,16 +4,21 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package main_test
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
-import "v.io/x/ref/test/v23tests"
+	"v.io/x/ref/test"
+	"v.io/x/ref/test/modules"
+	"v.io/x/ref/test/v23tests"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
+	modules.DispatchAndExitIfChild()
 	cleanup := v23tests.UseSharedBinDir()
 	r := m.Run()
 	cleanup()
diff --git a/services/identity/identitylib/test_identityd.go b/services/identity/identitylib/test_identityd.go
index 03cdf0a..a633bbd 100644
--- a/services/identity/identitylib/test_identityd.go
+++ b/services/identity/identitylib/test_identityd.go
@@ -9,7 +9,6 @@
 import (
 	"flag"
 	"fmt"
-	"io"
 	"net"
 	"strconv"
 	"time"
@@ -32,15 +31,7 @@
 	tlsConfig        = flag.CommandLine.String("tls-config", "", "Comma-separated list of TLS certificate and private key files. This must be provided.")
 )
 
-const (
-	TestIdentitydCommand = "test_identityd"
-)
-
-func init() {
-	modules.RegisterChild(TestIdentitydCommand, modules.Usage(flag.CommandLine), startTestIdentityd)
-}
-
-func startTestIdentityd(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+var TestIdentityd = modules.Register(func(env *modules.Env, args ...string) error {
 	// Duration to use for tls cert and blessing duration.
 	duration := 365 * 24 * time.Hour
 
@@ -101,12 +92,12 @@
 
 	_, eps, externalHttpAddress := s.Listen(ctx, &l, *externalHttpAddr, *httpAddr, *tlsConfig)
 
-	fmt.Fprintf(stdout, "TEST_IDENTITYD_NAME=%s\n", eps[0])
-	fmt.Fprintf(stdout, "TEST_IDENTITYD_HTTP_ADDR=%s\n", externalHttpAddress)
+	fmt.Fprintf(env.Stdout, "TEST_IDENTITYD_NAME=%s\n", eps[0])
+	fmt.Fprintf(env.Stdout, "TEST_IDENTITYD_HTTP_ADDR=%s\n", externalHttpAddress)
 
-	modules.WaitForEOF(stdin)
+	modules.WaitForEOF(env.Stdin)
 	return nil
-}
+}, "TestIdentityd")
 
 func freePort() string {
 	l, _ := net.Listen("tcp", ":0")
diff --git a/services/identity/internal/revocation/revocation_test.go b/services/identity/internal/revocation/revocation_test.go
index 0b07b0c..662af26 100644
--- a/services/identity/internal/revocation/revocation_test.go
+++ b/services/identity/internal/revocation/revocation_test.go
@@ -42,7 +42,7 @@
 }
 
 func TestDischargeRevokeDischargeRevokeDischarge(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	dcKey, dc, revoker, closeFunc := revokerSetup(t, ctx)
diff --git a/services/identity/internal/revocation/v23_internal_test.go b/services/identity/internal/revocation/v23_internal_test.go
index ed4c9f9..d53fea3 100644
--- a/services/identity/internal/revocation/v23_internal_test.go
+++ b/services/identity/internal/revocation/v23_internal_test.go
@@ -4,12 +4,15 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package revocation
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
+	"v.io/x/ref/test"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
diff --git a/services/internal/binarylib/client_test.go b/services/internal/binarylib/client_test.go
index 2d91162..255dad4 100644
--- a/services/internal/binarylib/client_test.go
+++ b/services/internal/binarylib/client_test.go
@@ -83,7 +83,7 @@
 // TestBufferAPI tests the binary repository client-side library
 // interface using buffers.
 func TestBufferAPI(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	v23.GetNamespace(ctx).CacheCtl(naming.DisableCache(true))
@@ -127,7 +127,7 @@
 // TestFileAPI tests the binary repository client-side library
 // interface using files.
 func TestFileAPI(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	v23.GetNamespace(ctx).CacheCtl(naming.DisableCache(true))
@@ -184,7 +184,7 @@
 // TestDownloadUrl tests the binary repository client-side library
 // DownloadUrl method.
 func TestDownloadUrl(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	v23.GetNamespace(ctx).CacheCtl(naming.DisableCache(true))
diff --git a/services/internal/binarylib/http_test.go b/services/internal/binarylib/http_test.go
index 60968c7..77f0f2d 100644
--- a/services/internal/binarylib/http_test.go
+++ b/services/internal/binarylib/http_test.go
@@ -23,7 +23,7 @@
 
 // TestHTTP checks that HTTP download works.
 func TestHTTP(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	v23.GetNamespace(ctx).CacheCtl(naming.DisableCache(true))
diff --git a/services/internal/binarylib/impl_test.go b/services/internal/binarylib/impl_test.go
index 21ebb2a..93bbb35 100644
--- a/services/internal/binarylib/impl_test.go
+++ b/services/internal/binarylib/impl_test.go
@@ -77,7 +77,7 @@
 // all possible valid values of the depth used for the directory
 // hierarchy that stores binary objects in the local file system.
 func TestHierarchy(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	v23.GetNamespace(ctx).CacheCtl(naming.DisableCache(true))
@@ -131,7 +131,7 @@
 // uploads and downloads ranging the number of parts the test binary
 // consists of.
 func TestMultiPart(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	v23.GetNamespace(ctx).CacheCtl(naming.DisableCache(true))
@@ -185,7 +185,7 @@
 // resumption ranging the number of parts the uploaded binary consists
 // of.
 func TestResumption(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	v23.GetNamespace(ctx).CacheCtl(naming.DisableCache(true))
@@ -232,7 +232,7 @@
 
 // TestErrors checks that the binary interface correctly reports errors.
 func TestErrors(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	v23.GetNamespace(ctx).CacheCtl(naming.DisableCache(true))
@@ -299,7 +299,7 @@
 }
 
 func TestGlob(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	v23.GetNamespace(ctx).CacheCtl(naming.DisableCache(true))
diff --git a/services/internal/binarylib/perms_test.go b/services/internal/binarylib/perms_test.go
index 59de734..80b5307 100644
--- a/services/internal/binarylib/perms_test.go
+++ b/services/internal/binarylib/perms_test.go
@@ -6,7 +6,6 @@
 
 import (
 	"fmt"
-	"io"
 	"os"
 	"reflect"
 	"syscall"
@@ -25,25 +24,22 @@
 	"v.io/x/ref/services/internal/binarylib"
 	"v.io/x/ref/services/internal/servicetest"
 	"v.io/x/ref/test"
+	"v.io/x/ref/test/modules"
 	"v.io/x/ref/test/testutil"
 )
 
 //go:generate v23 test generate
 
-const (
-	binaryCmd = "binaryd"
-)
-
-func binaryd(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+var binaryd = modules.Register(func(env *modules.Env, args ...string) error {
 	if len(args) < 2 {
 		vlog.Fatalf("binaryd expected at least name and store arguments and optionally AccessList flags per PermissionsFromFlag")
 	}
 	publishName := args[0]
 	storedir := args[1]
 
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 
-	defer fmt.Fprintf(stdout, "%v terminating\n", publishName)
+	defer fmt.Fprintf(env.Stdout, "%v terminating\n", publishName)
 	defer vlog.VI(1).Infof("%v terminating", publishName)
 	defer shutdown()
 
@@ -64,11 +60,11 @@
 		vlog.Fatalf("Serve(%v) failed: %v", publishName, err)
 	}
 
-	fmt.Fprintf(stdout, "ready:%d\n", os.Getpid())
+	fmt.Fprintf(env.Stdout, "ready:%d\n", os.Getpid())
 	<-signals.ShutdownOnSignals(ctx)
 
 	return nil
-}
+}, "binaryd")
 
 func b(name string) repository.BinaryClientStub {
 	return repository.BinaryClient(name)
@@ -88,7 +84,7 @@
 }
 
 func TestBinaryCreateAccessList(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 	v23.GetNamespace(ctx).CacheCtl(naming.DisableCache(true))
 
@@ -112,7 +108,7 @@
 	defer cleanup()
 	prepDirectory(t, storedir)
 
-	nmh := servicetest.RunCommand(t, sh, nil, binaryCmd, "bini", storedir)
+	nmh := servicetest.RunCommand(t, sh, nil, binaryd, "bini", storedir)
 	pid := servicetest.ReadPID(t, nmh)
 	defer syscall.Kill(pid, syscall.SIGINT)
 
@@ -143,7 +139,7 @@
 }
 
 func TestBinaryRootAccessList(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 	v23.GetNamespace(ctx).CacheCtl(naming.DisableCache(true))
 
@@ -169,7 +165,7 @@
 		t.Fatalf("WithPrincipal() failed: %v", err)
 	}
 
-	nmh := servicetest.RunCommand(t, sh, nil, binaryCmd, "bini", storedir)
+	nmh := servicetest.RunCommand(t, sh, nil, binaryd, "bini", storedir)
 	pid := servicetest.ReadPID(t, nmh)
 	defer syscall.Kill(pid, syscall.SIGINT)
 
@@ -427,7 +423,7 @@
 }
 
 func TestBinaryRationalStartingValueForGetPermissions(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 	v23.GetNamespace(ctx).CacheCtl(naming.DisableCache(true))
 
@@ -449,7 +445,7 @@
 		t.Fatalf("otherPrincipal.AddToRoots() failed: %v", err)
 	}
 
-	nmh := servicetest.RunCommand(t, sh, nil, binaryCmd, "bini", storedir)
+	nmh := servicetest.RunCommand(t, sh, nil, binaryd, "bini", storedir)
 	pid := servicetest.ReadPID(t, nmh)
 	defer syscall.Kill(pid, syscall.SIGINT)
 
diff --git a/services/internal/binarylib/v23_test.go b/services/internal/binarylib/v23_test.go
index 06b7552..9e2e792 100644
--- a/services/internal/binarylib/v23_test.go
+++ b/services/internal/binarylib/v23_test.go
@@ -4,27 +4,19 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package binarylib_test
 
-import "fmt"
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
-import "v.io/x/ref/test/modules"
-
-func init() {
-	modules.RegisterChild("binaryd", ``, binaryd)
-}
+	"v.io/x/ref/test"
+	"v.io/x/ref/test/modules"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
-	if modules.IsModulesChildProcess() {
-		if err := modules.Dispatch(); err != nil {
-			fmt.Fprintf(os.Stderr, "modules.Dispatch failed: %v\n", err)
-			os.Exit(1)
-		}
-		return
-	}
+	modules.DispatchAndExitIfChild()
 	os.Exit(m.Run())
 }
diff --git a/services/internal/logreaderlib/logfile_test.go b/services/internal/logreaderlib/logfile_test.go
index 3f74b34..ca3dd6e 100644
--- a/services/internal/logreaderlib/logfile_test.go
+++ b/services/internal/logreaderlib/logfile_test.go
@@ -67,7 +67,7 @@
 }
 
 func TestReadLogImplNoFollow(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	workdir, err := ioutil.TempDir("", "logreadertest")
@@ -154,7 +154,7 @@
 }
 
 func TestReadLogImplWithFollow(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	workdir, err := ioutil.TempDir("", "logreadertest")
diff --git a/services/internal/logreaderlib/v23_internal_test.go b/services/internal/logreaderlib/v23_internal_test.go
index bcdf9fb..c0b54bf 100644
--- a/services/internal/logreaderlib/v23_internal_test.go
+++ b/services/internal/logreaderlib/v23_internal_test.go
@@ -4,12 +4,15 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package logreaderlib
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
+	"v.io/x/ref/test"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
diff --git a/services/internal/pproflib/proxy_test.go b/services/internal/pproflib/proxy_test.go
index b277b89..6f30246 100644
--- a/services/internal/pproflib/proxy_test.go
+++ b/services/internal/pproflib/proxy_test.go
@@ -29,7 +29,7 @@
 }
 
 func TestPProfProxy(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	s, err := v23.NewServer(ctx)
@@ -56,10 +56,11 @@
 		"/pprof/profile?seconds=1",
 		"/pprof/heap",
 		"/pprof/goroutine",
-		fmt.Sprintf("/pprof/symbol?%#x", TestPProfProxy),
+		fmt.Sprintf("/pprof/symbol?%p", TestPProfProxy),
 	}
 	for _, c := range testcases {
 		url := "http://" + l.Addr().String() + c
+		t.Log(url)
 		resp, err := http.Get(url)
 		if err != nil {
 			t.Fatalf("http.Get failed: %v", err)
diff --git a/services/internal/pproflib/v23_internal_test.go b/services/internal/pproflib/v23_internal_test.go
index 266140e..2b18e2d 100644
--- a/services/internal/pproflib/v23_internal_test.go
+++ b/services/internal/pproflib/v23_internal_test.go
@@ -4,12 +4,15 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package pproflib
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
+	"v.io/x/ref/test"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
diff --git a/services/internal/servicetest/modules.go b/services/internal/servicetest/modules.go
index 7954346..e69c624 100644
--- a/services/internal/servicetest/modules.go
+++ b/services/internal/servicetest/modules.go
@@ -6,7 +6,6 @@
 
 import (
 	"fmt"
-	"io"
 	"io/ioutil"
 	"os"
 	"path/filepath"
@@ -33,11 +32,7 @@
 	preserveWorkspaceEnv = "V23_TEST_PRESERVE_WORKSPACE"
 )
 
-func init() {
-	modules.RegisterChild("rootMT", ``, rootMT)
-}
-
-func rootMT(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+var rootMT = modules.Register(func(env *modules.Env, args ...string) error {
 	ctx, shutdown := v23.Init()
 	defer shutdown()
 
@@ -57,17 +52,17 @@
 	if err := server.ServeDispatcher("", mt); err != nil {
 		return fmt.Errorf("root failed: %s", err)
 	}
-	fmt.Fprintf(stdout, "PID=%d\n", os.Getpid())
+	fmt.Fprintf(env.Stdout, "PID=%d\n", os.Getpid())
 	for _, ep := range eps {
-		fmt.Fprintf(stdout, "MT_NAME=%s\n", ep.Name())
+		fmt.Fprintf(env.Stdout, "MT_NAME=%s\n", ep.Name())
 	}
-	modules.WaitForEOF(stdin)
+	modules.WaitForEOF(env.Stdin)
 	return nil
-}
+}, "rootMT")
 
 // startRootMT sets up a root mount table for tests.
 func startRootMT(t *testing.T, sh *modules.Shell) (string, modules.Handle) {
-	h, err := sh.Start("rootMT", nil, "--v23.tcp.address=127.0.0.1:0")
+	h, err := sh.Start(nil, rootMT, "--v23.tcp.address=127.0.0.1:0")
 	if err != nil {
 		t.Fatalf("failed to start root mount table: %s", err)
 	}
@@ -123,10 +118,10 @@
 }
 
 // RunCommand runs a modules command.
-func RunCommand(t *testing.T, sh *modules.Shell, env []string, cmd string, args ...string) modules.Handle {
-	h, err := sh.StartWithOpts(sh.DefaultStartOpts(), env, cmd, args...)
+func RunCommand(t *testing.T, sh *modules.Shell, env []string, prog modules.Program, args ...string) modules.Handle {
+	h, err := sh.StartWithOpts(sh.DefaultStartOpts(), env, prog, args...)
 	if err != nil {
-		t.Fatalf(testutil.FormatLogLine(2, "failed to start %q: %s", cmd, err))
+		t.Fatalf(testutil.FormatLogLine(2, "failed to start %q: %s", prog, err))
 		return nil
 	}
 	h.SetVerbosity(testing.Verbose())
diff --git a/services/internal/statslib/stats_test.go b/services/internal/statslib/stats_test.go
index d8d4ff3..9e38a73 100644
--- a/services/internal/statslib/stats_test.go
+++ b/services/internal/statslib/stats_test.go
@@ -57,7 +57,7 @@
 }
 
 func TestStatsImpl(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	endpoint, stop := startServer(t, ctx)
diff --git a/services/internal/statslib/v23_internal_test.go b/services/internal/statslib/v23_internal_test.go
index 8f6c7c9..b34a92f 100644
--- a/services/internal/statslib/v23_internal_test.go
+++ b/services/internal/statslib/v23_internal_test.go
@@ -4,12 +4,15 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package statslib
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
+	"v.io/x/ref/test"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
diff --git a/services/internal/vtracelib/v23_internal_test.go b/services/internal/vtracelib/v23_internal_test.go
index 386c81f..2229a8d 100644
--- a/services/internal/vtracelib/v23_internal_test.go
+++ b/services/internal/vtracelib/v23_internal_test.go
@@ -4,12 +4,15 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package vtracelib
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
+	"v.io/x/ref/test"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
diff --git a/services/internal/vtracelib/vtrace_test.go b/services/internal/vtracelib/vtrace_test.go
index 93deb05..5eb8a82 100644
--- a/services/internal/vtracelib/vtrace_test.go
+++ b/services/internal/vtracelib/vtrace_test.go
@@ -20,7 +20,7 @@
 //go:generate v23 test generate
 
 func TestVtraceServer(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	server, err := v23.NewServer(ctx)
diff --git a/services/mounttable/mounttabled/v23_test.go b/services/mounttable/mounttabled/v23_test.go
index 0d570e3..b88590f 100644
--- a/services/mounttable/mounttabled/v23_test.go
+++ b/services/mounttable/mounttabled/v23_test.go
@@ -4,16 +4,21 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package main_test
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
-import "v.io/x/ref/test/v23tests"
+	"v.io/x/ref/test"
+	"v.io/x/ref/test/modules"
+	"v.io/x/ref/test/v23tests"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
+	modules.DispatchAndExitIfChild()
 	cleanup := v23tests.UseSharedBinDir()
 	r := m.Run()
 	cleanup()
diff --git a/services/mounttable/mounttablelib/mounttable_test.go b/services/mounttable/mounttablelib/mounttable_test.go
index 1a07339..a8f9e7f 100644
--- a/services/mounttable/mounttablelib/mounttable_test.go
+++ b/services/mounttable/mounttablelib/mounttable_test.go
@@ -396,7 +396,7 @@
 }
 
 func TestGlob(t *testing.T) {
-	rootCtx, shutdown := test.InitForTest()
+	rootCtx, shutdown := test.V23Init()
 	defer shutdown()
 
 	server, estr := newMT(t, "", "", "testGlob", rootCtx)
@@ -549,7 +549,7 @@
 }
 
 func TestCleanup(t *testing.T) {
-	rootCtx, shutdown := test.InitForTest()
+	rootCtx, shutdown := test.V23Init()
 	defer shutdown()
 
 	server, estr := newMT(t, "", "", "testCleanup", rootCtx)
@@ -604,7 +604,7 @@
 }
 
 func TestServerFormat(t *testing.T) {
-	rootCtx, shutdown := test.InitForTest()
+	rootCtx, shutdown := test.V23Init()
 	defer shutdown()
 
 	server, estr := newMT(t, "", "", "testerverFormat", rootCtx)
@@ -618,7 +618,7 @@
 }
 
 func TestExpiry(t *testing.T) {
-	rootCtx, shutdown := test.InitForTest()
+	rootCtx, shutdown := test.V23Init()
 	defer shutdown()
 
 	server, estr := newMT(t, "", "", "testExpiry", rootCtx)
@@ -683,7 +683,7 @@
 }
 
 func TestStatsCounters(t *testing.T) {
-	rootCtx, shutdown := test.InitForTest()
+	rootCtx, shutdown := test.V23Init()
 	defer shutdown()
 
 	ft := NewFakeTimeClock()
@@ -817,7 +817,7 @@
 
 func initTest() (rootCtx *context.T, aliceCtx *context.T, bobCtx *context.T, shutdown v23.Shutdown) {
 	test.Init()
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	var err error
 	if rootCtx, err = v23.WithPrincipal(ctx, testutil.NewPrincipal("root")); err != nil {
 		panic("failed to set root principal")
diff --git a/services/mounttable/mounttablelib/neighborhood.go b/services/mounttable/mounttablelib/neighborhood.go
index b2a2a71..ac82af2 100644
--- a/services/mounttable/mounttablelib/neighborhood.go
+++ b/services/mounttable/mounttablelib/neighborhood.go
@@ -34,15 +34,21 @@
 	errDoesntImplementUnmount        = verror.Register(pkgPath+".errDoesntImplementUnmount", verror.NoRetry, "{1:}{2:} this server does not implement Unmount{:_}")
 	errDoesntImplementDelete         = verror.Register(pkgPath+".errDoesntImplementDelete", verror.NoRetry, "{1:}{2:} this server does not implement Delete{:_}")
 	errDoesntImplementSetPermissions = verror.Register(pkgPath+".errDoesntImplementSetPermissions", verror.NoRetry, "{1:}{2:} this server does not implement SetPermissions{:_}")
+	errSlashInHostName               = verror.Register(pkgPath+".errSlashInHostName", verror.NoRetry, "{1:}{2:} hostname may not contain '/'{:_}")
 )
 
 const addressPrefix = "address:"
 
+// AnonymousNeighbor is the neighborhood host name to use if you want to be a client of the neighborhood namespace
+// without appearing in it.
+const AnonymousNeighbor = "_"
+
 // neighborhood defines a set of machines on the same multicast media.
 type neighborhood struct {
-	mdns   *mdns.MDNS
-	nelems int
-	nw     netconfig.NetConfigWatcher
+	mdns             *mdns.MDNS
+	nelems           int
+	nw               netconfig.NetConfigWatcher
+	lastSubscription time.Time
 }
 
 var _ rpc.Dispatcher = (*neighborhood)(nil)
@@ -81,6 +87,10 @@
 }
 
 func newNeighborhood(host string, addresses []string, loopback bool) (*neighborhood, error) {
+	if strings.Contains(host, "/") {
+		return nil, verror.New(errSlashInHostName, nil)
+	}
+
 	// Create the TXT contents with addresses to announce. Also pick up a port number.
 	var txt []string
 	var port uint16
@@ -105,7 +115,10 @@
 	}
 	vlog.VI(2).Infof("listening for service vanadium on port %d", port)
 	mdns.SubscribeToService("vanadium")
-	mdns.AddService("vanadium", "", port, txt...)
+	if host != AnonymousNeighbor {
+		mdns.AddService("vanadium", "", port, txt...)
+	}
+	time.Sleep(50 * time.Millisecond)
 
 	nh := &neighborhood{
 		mdns: mdns,
@@ -199,6 +212,12 @@
 
 // neighbors returns all neighbors and their MountedServer structs.
 func (nh *neighborhood) neighbors() map[string][]naming.MountedServer {
+	// If we haven't refreshed in a while, do it now.
+	if time.Now().Sub(nh.lastSubscription) > time.Duration(30)*time.Second {
+		nh.mdns.SubscribeToService("vanadium")
+		time.Sleep(50 * time.Millisecond)
+		nh.lastSubscription = time.Now()
+	}
 	neighbors := make(map[string][]naming.MountedServer, 0)
 	members := nh.mdns.ServiceDiscovery("vanadium")
 	for _, m := range members {
diff --git a/services/mounttable/mounttablelib/neighborhood_test.go b/services/mounttable/mounttablelib/neighborhood_test.go
index a8d67d8..4225985 100644
--- a/services/mounttable/mounttablelib/neighborhood_test.go
+++ b/services/mounttable/mounttablelib/neighborhood_test.go
@@ -31,7 +31,7 @@
 }
 
 func TestNeighborhood(t *testing.T) {
-	rootCtx, shutdown := test.InitForTest()
+	rootCtx, shutdown := test.V23Init()
 	defer shutdown()
 
 	vlog.Infof("TestNeighborhood")
diff --git a/services/mounttable/mounttablelib/v23_internal_test.go b/services/mounttable/mounttablelib/v23_internal_test.go
index 1d210b4..82b7beb 100644
--- a/services/mounttable/mounttablelib/v23_internal_test.go
+++ b/services/mounttable/mounttablelib/v23_internal_test.go
@@ -4,12 +4,15 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package mounttablelib
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
+	"v.io/x/ref/test"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
diff --git a/services/profile/profile/impl_test.go b/services/profile/profile/impl_test.go
index cf2b402..7ac0fcb 100644
--- a/services/profile/profile/impl_test.go
+++ b/services/profile/profile/impl_test.go
@@ -117,7 +117,7 @@
 }
 
 func TestProfileClient(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	server, endpoint, err := startServer(t, ctx)
diff --git a/services/profile/profile/v23_internal_test.go b/services/profile/profile/v23_internal_test.go
index dcd0029..ae59080 100644
--- a/services/profile/profile/v23_internal_test.go
+++ b/services/profile/profile/v23_internal_test.go
@@ -4,12 +4,15 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package main
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
+	"v.io/x/ref/test"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
diff --git a/services/profile/profiled/impl_test.go b/services/profile/profiled/impl_test.go
index a981873..133c88a 100644
--- a/services/profile/profiled/impl_test.go
+++ b/services/profile/profiled/impl_test.go
@@ -36,7 +36,7 @@
 // TestInterface tests that the implementation correctly implements
 // the Profile interface.
 func TestInterface(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	// Setup and start the profile repository server.
@@ -114,7 +114,7 @@
 }
 
 func TestPreserveAcrossRestarts(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	// Setup and start the profile repository server.
diff --git a/services/profile/profiled/v23_test.go b/services/profile/profiled/v23_test.go
index cb53f83..9df2772 100644
--- a/services/profile/profiled/v23_test.go
+++ b/services/profile/profiled/v23_test.go
@@ -4,16 +4,21 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package main_test
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
-import "v.io/x/ref/test/v23tests"
+	"v.io/x/ref/test"
+	"v.io/x/ref/test/modules"
+	"v.io/x/ref/test/v23tests"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
+	modules.DispatchAndExitIfChild()
 	cleanup := v23tests.UseSharedBinDir()
 	r := m.Run()
 	cleanup()
diff --git a/services/proxy/proxyd/proxyd_v23_test.go b/services/proxy/proxyd/proxyd_v23_test.go
index e58c90d..167ea42 100644
--- a/services/proxy/proxyd/proxyd_v23_test.go
+++ b/services/proxy/proxyd/proxyd_v23_test.go
@@ -6,13 +6,11 @@
 
 import (
 	"fmt"
-	"io"
 
 	"v.io/v23"
 	"v.io/v23/context"
 	"v.io/v23/rpc"
 	"v.io/v23/security"
-
 	"v.io/x/ref/test/modules"
 	"v.io/x/ref/test/v23tests"
 )
@@ -20,18 +18,11 @@
 //go:generate v23 test generate
 
 const (
-	serverCmd   = "server"
-	clientCmd   = "client"
 	proxyName   = "proxy"    // Name which the proxy mounts itself at
 	serverName  = "server"   // Name which the server mounts itself at
 	responseVar = "RESPONSE" // Name of the variable used by client program to output the response
 )
 
-func init() {
-	modules.RegisterChild(serverCmd, "server", runServer)
-	modules.RegisterChild(clientCmd, "client", runClient)
-}
-
 func V23TestProxyd(t *v23tests.T) {
 	v23tests.RunRootMT(t, "--v23.tcp.address=127.0.0.1:0")
 	var (
@@ -47,14 +38,14 @@
 	if _, err := t.Shell().StartWithOpts(
 		t.Shell().DefaultStartOpts().WithCustomCredentials(serverCreds),
 		nil,
-		serverCmd); err != nil {
+		runServer); err != nil {
 		t.Fatal(err)
 	}
 	// Run the client.
 	client, err := t.Shell().StartWithOpts(
 		t.Shell().DefaultStartOpts().WithCustomCredentials(clientCreds),
 		nil,
-		clientCmd)
+		runClient)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -63,7 +54,7 @@
 	}
 }
 
-func runServer(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+var runServer = modules.Register(func(env *modules.Env, args ...string) error {
 	ctx, shutdown := v23.Init()
 	defer shutdown()
 
@@ -79,11 +70,11 @@
 		return err
 	}
 
-	modules.WaitForEOF(stdin)
+	modules.WaitForEOF(env.Stdin)
 	return nil
-}
+}, "runServer")
 
-func runClient(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+var runClient = modules.Register(func(env *modules.Env, args ...string) error {
 	ctx, shutdown := v23.Init()
 	defer shutdown()
 
@@ -95,9 +86,9 @@
 	if err := call.Finish(&response); err != nil {
 		return err
 	}
-	fmt.Fprintf(stdout, "%v=%v\n", responseVar, response)
+	fmt.Fprintf(env.Stdout, "%v=%v\n", responseVar, response)
 	return nil
-}
+}, "runClient")
 
 type service struct{}
 
diff --git a/services/proxy/proxyd/v23_test.go b/services/proxy/proxyd/v23_test.go
index 436b072..98cd476 100644
--- a/services/proxy/proxyd/v23_test.go
+++ b/services/proxy/proxyd/v23_test.go
@@ -4,30 +4,21 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package main_test
 
-import "fmt"
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
-import "v.io/x/ref/test/modules"
-import "v.io/x/ref/test/v23tests"
-
-func init() {
-	modules.RegisterChild("runServer", ``, runServer)
-	modules.RegisterChild("runClient", ``, runClient)
-}
+	"v.io/x/ref/test"
+	"v.io/x/ref/test/modules"
+	"v.io/x/ref/test/v23tests"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
-	if modules.IsModulesChildProcess() {
-		if err := modules.Dispatch(); err != nil {
-			fmt.Fprintf(os.Stderr, "modules.Dispatch failed: %v\n", err)
-			os.Exit(1)
-		}
-		return
-	}
+	modules.DispatchAndExitIfChild()
 	cleanup := v23tests.UseSharedBinDir()
 	r := m.Run()
 	cleanup()
diff --git a/services/wspr/internal/app/app.go b/services/wspr/internal/app/app.go
index 5eb77d1..140203f 100644
--- a/services/wspr/internal/app/app.go
+++ b/services/wspr/internal/app/app.go
@@ -92,8 +92,11 @@
 	// the default implementation.
 	writerCreator func(id int32) lib.ClientWriter
 
-	// Cache for all the Blessings that javascript has a handle to.
-	blessingsCache *principal.JSBlessingsHandles
+	// Handles for all the Blessings that javascript has a handle to.
+	blessingsHandles *principal.JSBlessingsHandles
+
+	// Cache of Blessings that were sent to Javascript.
+	blessingsCache *principal.BlessingsCache
 
 	// reservedServices contains a map of reserved service names.  These
 	// are objects that serve requests in wspr without actually making
@@ -106,6 +109,8 @@
 	typeReader  *lib.TypeReader
 }
 
+var _ ControllerServerMethods = (*Controller)(nil)
+
 // NewController creates a new Controller.  writerCreator will be used to create a new flow for rpcs to
 // javascript server.
 func NewController(ctx *context.T, writerCreator func(id int32) lib.ClientWriter, listenSpec *rpc.ListenSpec, namespaceRoots []string, p security.Principal) (*Controller, error) {
@@ -127,13 +132,15 @@
 	}
 
 	controller := &Controller{
-		ctx:            ctx,
-		cancel:         cancel,
-		writerCreator:  writerCreator,
-		listenSpec:     listenSpec,
-		blessingsCache: principal.NewJSBlessingsHandles(),
+		ctx:              ctx,
+		cancel:           cancel,
+		writerCreator:    writerCreator,
+		listenSpec:       listenSpec,
+		blessingsHandles: principal.NewJSBlessingsHandles(),
 	}
 
+	controller.blessingsCache = principal.NewBlessingsCache(controller.SendBlessingsCacheMessages, principal.PeriodicGcPolicy(1*time.Minute))
+
 	controllerInvoker, err := rpc.ReflectInvoker(ControllerServer(controller))
 	if err != nil {
 		return nil, err
@@ -164,7 +171,7 @@
 				return
 			}
 			if blessings, ok := item.(security.Blessings); ok {
-				item = principal.ConvertBlessingsToHandle(blessings, c.blessingsCache.GetOrAddHandle(blessings))
+				item = principal.ConvertBlessingsToHandle(blessings, c.blessingsHandles.GetOrAddHandle(blessings))
 			}
 			vomItem, err := lib.HexVomEncode(item, c.typeEncoder)
 			if err != nil {
@@ -198,7 +205,8 @@
 				w.Error(err)
 				return
 			}
-			results[i] = vdl.ValueOf(principal.ConvertBlessingsToHandle(blessings, c.blessingsCache.GetOrAddHandle(blessings)))
+			jsBless := principal.ConvertBlessingsToHandle(blessings, c.blessingsHandles.GetOrAddHandle(blessings))
+			results[i] = vdl.ValueOf(c.blessingsCache.Put(jsBless))
 		}
 	}
 	c.sendRPCResponse(ctx, w, span, results)
@@ -284,7 +292,7 @@
 	id := c.lastGeneratedId
 	c.lastGeneratedId += 2
 	c.flowMap[id] = s
-	os := newStream(c.blessingsCache, c.typeDecoder)
+	os := newStream(c.blessingsHandles, c.typeDecoder)
 	os.init(stream)
 	c.outstandingRequests[id] = &outstandingRequest{
 		stream: os,
@@ -305,6 +313,11 @@
 	}
 }
 
+// BlessingsCache gets the blessings cache used by the controller.
+func (c *Controller) BlessingsCache() *principal.BlessingsCache {
+	return c.blessingsCache
+}
+
 // RT returns the runtime of the app.
 func (c *Controller) Context() *context.T {
 	return c.ctx
@@ -315,12 +328,12 @@
 // because JS only has a handle to the blessings to avoid shipping the
 // certificate forest to JS and back.
 func (c *Controller) GetOrAddBlessingsHandle(blessings security.Blessings) principal.BlessingsHandle {
-	return c.blessingsCache.GetOrAddHandle(blessings)
+	return c.blessingsHandles.GetOrAddHandle(blessings)
 }
 
 // GetBlessings gets blessings for a given blessings handle.
 func (c *Controller) GetBlessings(handle principal.BlessingsHandle) security.Blessings {
-	return c.blessingsCache.GetBlessings(handle)
+	return c.blessingsHandles.GetBlessings(handle)
 }
 
 // Cleanup cleans up any outstanding rpcs.
@@ -400,7 +413,7 @@
 
 	for i, arg := range inArgs {
 		if jsBlessings, ok := arg.(principal.JsBlessings); ok {
-			inArgs[i] = c.blessingsCache.GetBlessings(jsBlessings.Handle)
+			inArgs[i] = c.blessingsHandles.GetBlessings(jsBlessings.Handle)
 		}
 	}
 	// We have to make the start call synchronous so we can make sure that we populate
@@ -580,7 +593,7 @@
 		// to put the outstanding stream in the map before we make the async call so that
 		// the future send know which queue to write to, even if the client call isn't
 		// actually ready yet.
-		request.stream = newStream(c.blessingsCache, c.typeDecoder)
+		request.stream = newStream(c.blessingsHandles, c.typeDecoder)
 	}
 	c.Lock()
 	c.outstandingRequests[id] = request
@@ -743,20 +756,20 @@
 
 // UnlinkBlessings removes the given blessings from the blessings store.
 func (c *Controller) UnlinkBlessings(_ *context.T, _ rpc.ServerCall, handle principal.BlessingsHandle) error {
-	return c.blessingsCache.RemoveReference(handle)
+	return c.blessingsHandles.RemoveReference(handle)
 }
 
 // Bless binds extensions of blessings held by this principal to
 // another principal (represented by its public key).
-func (c *Controller) Bless(_ *context.T, _ rpc.ServerCall, publicKey string, blessingHandle principal.BlessingsHandle, extension string, caveats []security.Caveat) (string, principal.BlessingsHandle, error) {
+func (c *Controller) Bless(_ *context.T, _ rpc.ServerCall, publicKey string, blessingHandle principal.BlessingsHandle, extension string, caveats []security.Caveat) (principal.BlessingsId, error) {
 	var inputBlessing security.Blessings
 	if inputBlessing = c.GetBlessings(blessingHandle); inputBlessing.IsZero() {
-		return "", principal.ZeroHandle, verror.New(invalidBlessingsHandle, nil, blessingHandle)
+		return 0, verror.New(invalidBlessingsHandle, nil, blessingHandle)
 	}
 
 	key, err := principal.DecodePublicKey(publicKey)
 	if err != nil {
-		return "", principal.ZeroHandle, err
+		return 0, err
 	}
 
 	if len(caveats) == 0 {
@@ -766,60 +779,58 @@
 	p := v23.GetPrincipal(c.ctx)
 	blessings, err := p.Bless(key, inputBlessing, extension, caveats[0], caveats[1:]...)
 	if err != nil {
-		return "", principal.ZeroHandle, err
+		return 0, err
 	}
-	handle := c.blessingsCache.GetOrAddHandle(blessings)
-	return publicKey, handle, nil
+	jsBlessings := principal.ConvertBlessingsToHandle(blessings, c.blessingsHandles.GetOrAddHandle(blessings))
+	return c.blessingsCache.Put(jsBlessings), nil
 }
 
 // BlessSelf creates a blessing with the provided name for this principal.
-func (c *Controller) BlessSelf(_ *context.T, _ rpc.ServerCall, extension string, caveats []security.Caveat) (string, principal.BlessingsHandle, error) {
+func (c *Controller) BlessSelf(_ *context.T, _ rpc.ServerCall, extension string, caveats []security.Caveat) (principal.BlessingsId, error) {
 	p := v23.GetPrincipal(c.ctx)
 	blessings, err := p.BlessSelf(extension)
 	if err != nil {
-		return "", principal.ZeroHandle, verror.Convert(verror.ErrInternal, nil, err)
+		return 0, verror.Convert(verror.ErrInternal, nil, err)
 	}
 
-	handle := c.blessingsCache.GetOrAddHandle(blessings)
-
-	encKey, err := principal.EncodePublicKey(p.PublicKey())
-	return encKey, handle, err
+	jsBlessings := principal.ConvertBlessingsToHandle(blessings, c.blessingsHandles.GetOrAddHandle(blessings))
+	return c.blessingsCache.Put(jsBlessings), err
 }
 
 // BlessingStoreSet puts the specified blessing in the blessing store under the
 // provided pattern.
-func (c *Controller) BlessingStoreSet(_ *context.T, _ rpc.ServerCall, handle principal.BlessingsHandle, pattern security.BlessingPattern) (*principal.JsBlessings, error) {
+func (c *Controller) BlessingStoreSet(_ *context.T, _ rpc.ServerCall, handle principal.BlessingsHandle, pattern security.BlessingPattern) (principal.BlessingsId, error) {
 	var inputBlessings security.Blessings
 	if inputBlessings = c.GetBlessings(handle); inputBlessings.IsZero() {
-		return nil, verror.New(invalidBlessingsHandle, nil, handle)
+		return 0, verror.New(invalidBlessingsHandle, nil, handle)
 	}
 
 	p := v23.GetPrincipal(c.ctx)
 	outBlessings, err := p.BlessingStore().Set(inputBlessings, security.BlessingPattern(pattern))
 	if err != nil {
-		return nil, err
+		return 0, err
 	}
 
 	if outBlessings.IsZero() {
-		return nil, nil
+		return 0, nil
 	}
 
-	jsBlessings := principal.ConvertBlessingsToHandle(outBlessings, c.blessingsCache.GetOrAddHandle(outBlessings))
-	return jsBlessings, nil
+	jsBlessings := principal.ConvertBlessingsToHandle(outBlessings, c.blessingsHandles.GetOrAddHandle(outBlessings))
+	return c.blessingsCache.Put(jsBlessings), nil
 }
 
 // BlessingStoreForPeer puts the specified blessing in the blessing store under the
 // provided pattern.
-func (c *Controller) BlessingStoreForPeer(_ *context.T, _ rpc.ServerCall, peerBlessings []string) (*principal.JsBlessings, error) {
+func (c *Controller) BlessingStoreForPeer(_ *context.T, _ rpc.ServerCall, peerBlessings []string) (principal.BlessingsId, error) {
 	p := v23.GetPrincipal(c.ctx)
 	outBlessings := p.BlessingStore().ForPeer(peerBlessings...)
 
 	if outBlessings.IsZero() {
-		return nil, nil
+		return 0, nil
 	}
 
-	jsBlessings := principal.ConvertBlessingsToHandle(outBlessings, c.blessingsCache.GetOrAddHandle(outBlessings))
-	return jsBlessings, nil
+	jsBlessings := principal.ConvertBlessingsToHandle(outBlessings, c.blessingsHandles.GetOrAddHandle(outBlessings))
+	return c.blessingsCache.Put(jsBlessings), nil
 }
 
 // BlessingStoreSetDefault sets the default blessings in the blessing store.
@@ -834,16 +845,16 @@
 }
 
 // BlessingStoreDefault fetches the default blessings for the principal of the controller.
-func (c *Controller) BlessingStoreDefault(*context.T, rpc.ServerCall) (*principal.JsBlessings, error) {
+func (c *Controller) BlessingStoreDefault(*context.T, rpc.ServerCall) (principal.BlessingsId, error) {
 	p := v23.GetPrincipal(c.ctx)
 	outBlessings := p.BlessingStore().Default()
 
 	if outBlessings.IsZero() {
-		return nil, nil
+		return 0, nil
 	}
 
-	jsBlessing := principal.ConvertBlessingsToHandle(outBlessings, c.blessingsCache.GetOrAddHandle(outBlessings))
-	return jsBlessing, nil
+	jsBlessing := principal.ConvertBlessingsToHandle(outBlessings, c.blessingsHandles.GetOrAddHandle(outBlessings))
+	return c.blessingsCache.Put(jsBlessing), nil
 }
 
 // BlessingStorePublicKey fetches the public key used by the principal associated with the blessing store.
@@ -854,11 +865,12 @@
 }
 
 // BlessingStorePeerBlessings returns all the blessings that the BlessingStore holds.
-func (c *Controller) BlessingStorePeerBlessings(*context.T, rpc.ServerCall) (map[security.BlessingPattern]*principal.JsBlessings, error) {
+func (c *Controller) BlessingStorePeerBlessings(*context.T, rpc.ServerCall) (map[security.BlessingPattern]principal.BlessingsId, error) {
 	p := v23.GetPrincipal(c.ctx)
-	outBlessingsMap := map[security.BlessingPattern]*principal.JsBlessings{}
+	outBlessingsMap := map[security.BlessingPattern]principal.BlessingsId{}
 	for pattern, blessings := range p.BlessingStore().PeerBlessings() {
-		outBlessingsMap[pattern] = principal.ConvertBlessingsToHandle(blessings, c.blessingsCache.GetOrAddHandle(blessings))
+		jsBless := principal.ConvertBlessingsToHandle(blessings, c.blessingsHandles.GetOrAddHandle(blessings))
+		outBlessingsMap[pattern] = c.blessingsCache.Put(jsBless)
 	}
 	return outBlessingsMap, nil
 }
@@ -882,23 +894,23 @@
 }
 
 // UnionOfBlessings returns a Blessings object that carries the union of the provided blessings.
-func (c *Controller) UnionOfBlessings(_ *context.T, _ rpc.ServerCall, handles []principal.BlessingsHandle) (*principal.JsBlessings, error) {
+func (c *Controller) UnionOfBlessings(_ *context.T, _ rpc.ServerCall, handles []principal.BlessingsHandle) (principal.BlessingsId, error) {
 	inputBlessings := make([]security.Blessings, len(handles))
 	for i, handle := range handles {
 		bless := c.GetBlessings(handle)
 		if bless.IsZero() {
-			return nil, verror.New(invalidBlessingsHandle, nil, handle)
+			return 0, verror.New(invalidBlessingsHandle, nil, handle)
 		}
 		inputBlessings[i] = bless
 	}
 
 	outBlessings, err := security.UnionOfBlessings(inputBlessings...)
 	if err != nil {
-		return nil, err
+		return 0, err
 	}
 
-	jsBlessings := principal.ConvertBlessingsToHandle(outBlessings, c.blessingsCache.GetOrAddHandle(outBlessings))
-	return jsBlessings, nil
+	jsBlessings := principal.ConvertBlessingsToHandle(outBlessings, c.blessingsHandles.GetOrAddHandle(outBlessings))
+	return c.blessingsCache.Put(jsBlessings), nil
 }
 
 // HandleGranterResponse handles the result of a Granter request.
@@ -953,3 +965,17 @@
 		Message: msg,
 	})
 }
+
+func (c *Controller) SendBlessingsCacheMessages(messages []principal.BlessingsCacheMessage) {
+	c.Lock()
+	defer c.Unlock()
+	id := c.lastGeneratedId
+	c.lastGeneratedId += 2
+	if err := c.writerCreator(id).Send(lib.ResponseBlessingsCacheMessage, messages); err != nil {
+		vlog.Errorf("unexpected error sending blessings cache message: %v", err)
+	}
+}
+
+func (c *Controller) TypeEncoder() *vom.TypeEncoder {
+	return c.typeEncoder
+}
diff --git a/services/wspr/internal/app/app.go.orig b/services/wspr/internal/app/app.go.orig
deleted file mode 100644
index 7243674..0000000
--- a/services/wspr/internal/app/app.go.orig
+++ /dev/null
@@ -1,947 +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.
-
-// The app package contains the struct that keeps per javascript app state and handles translating
-// javascript requests to vanadium requests and vice versa.
-package app
-
-import (
-	"bytes"
-	"encoding/hex"
-	"fmt"
-	"io"
-	"reflect"
-	"sync"
-	"time"
-
-	"v.io/v23"
-	"v.io/v23/context"
-	"v.io/v23/i18n"
-	"v.io/v23/naming"
-	"v.io/v23/options"
-	"v.io/v23/rpc"
-	"v.io/v23/security"
-	"v.io/v23/vdl"
-	"v.io/v23/vdlroot/signature"
-	"v.io/v23/verror"
-	"v.io/v23/vom"
-	"v.io/v23/vtrace"
-	"v.io/x/lib/vlog"
-	"v.io/x/ref/services/wspr/internal/lib"
-	"v.io/x/ref/services/wspr/internal/namespace"
-	"v.io/x/ref/services/wspr/internal/principal"
-	"v.io/x/ref/services/wspr/internal/rpc/server"
-)
-
-const (
-	// pkgPath is the prefix os errors in this package.
-	pkgPath = "v.io/x/ref/services/wspr/internal/app"
-)
-
-// Errors
-var (
-	marshallingError       = verror.Register(pkgPath+".marshallingError", verror.NoRetry, "{1} {2} marshalling error {_}")
-	noResults              = verror.Register(pkgPath+".noResults", verror.NoRetry, "{1} {2} no results from call {_}")
-	badCaveatType          = verror.Register(pkgPath+".badCaveatType", verror.NoRetry, "{1} {2} bad caveat type {_}")
-	unknownBlessings       = verror.Register(pkgPath+".unknownBlessings", verror.NoRetry, "{1} {2} unknown public id {_}")
-	invalidBlessingsHandle = verror.Register(pkgPath+".invalidBlessingsHandle", verror.NoRetry, "{1} {2} invalid blessings handle {3} {_}")
-)
-
-type outstandingRequest struct {
-	stream *outstandingStream
-	cancel context.CancelFunc
-}
-
-// Controller represents all the state of a Vanadium Web App.  This is the struct
-// that is in charge performing all the vanadium options.
-type Controller struct {
-	// Protects everything.
-	// TODO(bjornick): We need to split this up.
-	sync.Mutex
-
-	// The context of this controller.
-	ctx *context.T
-
-	// The cleanup function for this controller.
-	cancel context.CancelFunc
-
-	// The rpc.ListenSpec to use with server.Listen
-	listenSpec *rpc.ListenSpec
-
-	// Used to generate unique ids for requests initiated by the proxy.
-	// These ids will be even so they don't collide with the ids generated
-	// by the client.
-	lastGeneratedId int32
-
-	// Used to keep track of data (streams and cancellation functions) for
-	// outstanding requests.
-	outstandingRequests map[int32]*outstandingRequest
-
-	// Maps flowids to the server that owns them.
-	flowMap map[int32]interface{}
-
-	// A manager that Handles fetching and caching signature of remote services
-	signatureManager lib.SignatureManager
-
-	// We maintain multiple Vanadium server per pipe for serving JavaScript
-	// services.
-	servers map[uint32]*server.Server
-
-	// Creates a client writer for a given flow.  This is a member so that tests can override
-	// the default implementation.
-	writerCreator func(id int32) lib.ClientWriter
-
-	// Cache for all the Blessings that javascript has a handle to.
-	blessingsCache *principal.JSBlessingsHandles
-
-	// reservedServices contains a map of reserved service names.  These
-	// are objects that serve requests in wspr without actually making
-	// an outgoing rpc call.
-	reservedServices map[string]rpc.Invoker
-}
-
-// NewController creates a new Controller.  writerCreator will be used to create a new flow for rpcs to
-// javascript server.
-func NewController(ctx *context.T, writerCreator func(id int32) lib.ClientWriter, listenSpec *rpc.ListenSpec, namespaceRoots []string, p security.Principal) (*Controller, error) {
-	ctx, cancel := context.WithCancel(ctx)
-
-	if namespaceRoots != nil {
-		var err error
-		ctx, _, err = v23.WithNewNamespace(ctx, namespaceRoots...)
-		if err != nil {
-			return nil, err
-		}
-	}
-
-	ctx, _ = vtrace.WithNewTrace(ctx)
-
-	ctx, err := v23.WithPrincipal(ctx, p)
-	if err != nil {
-		return nil, err
-	}
-
-	controller := &Controller{
-		ctx:            ctx,
-		cancel:         cancel,
-		writerCreator:  writerCreator,
-		listenSpec:     listenSpec,
-		blessingsCache: principal.NewJSBlessingsHandles(),
-	}
-
-	controllerInvoker, err := rpc.ReflectInvoker(ControllerServer(controller))
-	if err != nil {
-		return nil, err
-	}
-	namespaceInvoker, err := rpc.ReflectInvoker(namespace.New(ctx))
-	if err != nil {
-		return nil, err
-	}
-	controller.reservedServices = map[string]rpc.Invoker{
-		"__controller": controllerInvoker,
-		"__namespace":  namespaceInvoker,
-	}
-
-	controller.setup()
-	return controller, nil
-}
-
-// finishCall waits for the call to finish and write out the response to w.
-func (c *Controller) finishCall(ctx *context.T, w lib.ClientWriter, clientCall rpc.ClientCall, msg *RpcRequest, span vtrace.Span) {
-	if msg.IsStreaming {
-		for {
-			var item interface{}
-			if err := clientCall.Recv(&item); err != nil {
-				if err == io.EOF {
-					break
-				}
-				w.Error(err) // Send streaming error as is
-				return
-			}
-			if blessings, ok := item.(security.Blessings); ok {
-				item = principal.ConvertBlessingsToHandle(blessings, c.blessingsCache.GetOrAddHandle(blessings))
-			}
-			vomItem, err := lib.HexVomEncode(item)
-			if err != nil {
-				w.Error(verror.New(marshallingError, ctx, item, err))
-				continue
-			}
-			if err := w.Send(lib.ResponseStream, vomItem); err != nil {
-				w.Error(verror.New(marshallingError, ctx, item))
-			}
-		}
-		if err := w.Send(lib.ResponseStreamClose, nil); err != nil {
-			w.Error(verror.New(marshallingError, ctx, "ResponseStreamClose"))
-		}
-	}
-	results := make([]*vdl.Value, msg.NumOutArgs)
-	wireBlessingsType := vdl.TypeOf(security.WireBlessings{})
-	// This array will have pointers to the values in results.
-	resultptrs := make([]interface{}, msg.NumOutArgs)
-	for i := range results {
-		resultptrs[i] = &results[i]
-	}
-	if err := clientCall.Finish(resultptrs...); err != nil {
-		// return the call system error as is
-		w.Error(err)
-		return
-	}
-	for i, val := range results {
-		if val.Type() == wireBlessingsType {
-			var blessings security.Blessings
-			if err := vdl.Convert(&blessings, val); err != nil {
-				w.Error(err)
-				return
-			}
-			results[i] = vdl.ValueOf(principal.ConvertBlessingsToHandle(blessings, c.blessingsCache.GetOrAddHandle(blessings)))
-		}
-	}
-	c.sendRPCResponse(ctx, w, span, results)
-}
-
-func (c *Controller) sendRPCResponse(ctx *context.T, w lib.ClientWriter, span vtrace.Span, results []*vdl.Value) {
-	span.Finish()
-	response := RpcResponse{
-		OutArgs:       results,
-		TraceResponse: vtrace.GetResponse(ctx),
-	}
-	encoded, err := lib.HexVomEncode(response)
-	if err != nil {
-		w.Error(err)
-		return
-	}
-	if err := w.Send(lib.ResponseFinal, encoded); err != nil {
-		w.Error(verror.Convert(marshallingError, ctx, err))
-	}
-}
-
-// callOpts turns a slice of type []RpcCallOption object into an array of rpc.CallOpt.
-func (c *Controller) callOpts(opts []RpcCallOption) ([]rpc.CallOpt, error) {
-	var callOpts []rpc.CallOpt
-
-	for _, opt := range opts {
-		switch v := opt.(type) {
-		case RpcCallOptionAllowedServersPolicy:
-			callOpts = append(callOpts, options.AllowedServersPolicy(v.Value))
-		case RpcCallOptionRetryTimeout:
-			callOpts = append(callOpts, options.RetryTimeout(v.Value))
-		case RpcCallOptionGranter:
-			callOpts = append(callOpts, &jsGranter{c, v.Value})
-		default:
-			return nil, fmt.Errorf("Unknown RpcCallOption type %T", v)
-		}
-	}
-
-	return callOpts, nil
-}
-
-// serverOpts turns a slice of type []RpcServerOptions object into an array of rpc.ServerOpt.
-func (c *Controller) serverOpts(opts []RpcServerOption) ([]rpc.ServerOpt, error) {
-	var serverOpts []rpc.ServerOpt
-
-	for _, opt := range opts {
-		switch v := opt.(type) {
-		case RpcServerOptionIsLeaf:
-			serverOpts = append(serverOpts, options.IsLeaf(v.Value))
-		case RpcServerOptionServesMountTable:
-			serverOpts = append(serverOpts, options.ServesMountTable(v.Value))
-		default:
-			return nil, fmt.Errorf("Unknown RpcServerOption type %T", v)
-		}
-	}
-
-	return serverOpts, nil
-}
-
-func (c *Controller) startCall(ctx *context.T, w lib.ClientWriter, msg *RpcRequest, inArgs []interface{}) (rpc.ClientCall, error) {
-	methodName := lib.UppercaseFirstCharacter(msg.Method)
-	callOpts, err := c.callOpts(msg.CallOptions)
-	if err != nil {
-		return nil, err
-	}
-	clientCall, err := v23.GetClient(ctx).StartCall(ctx, msg.Name, methodName, inArgs, callOpts...)
-	if err != nil {
-		return nil, fmt.Errorf("error starting call (name: %v, method: %v, args: %v): %v", msg.Name, methodName, inArgs, err)
-	}
-
-	return clientCall, nil
-}
-
-// Implements the serverHelper interface
-
-// CreateNewFlow creats a new server flow that will be used to write out
-// streaming messages to Javascript.
-func (c *Controller) CreateNewFlow(s interface{}, stream rpc.Stream) *server.Flow {
-	c.Lock()
-	defer c.Unlock()
-	id := c.lastGeneratedId
-	c.lastGeneratedId += 2
-	c.flowMap[id] = s
-	os := newStream(c.blessingsCache)
-	os.init(stream)
-	c.outstandingRequests[id] = &outstandingRequest{
-		stream: os,
-	}
-	return &server.Flow{ID: id, Writer: c.writerCreator(id)}
-}
-
-// CleanupFlow removes the bookkeeping for a previously created flow.
-func (c *Controller) CleanupFlow(id int32) {
-	c.Lock()
-	request := c.outstandingRequests[id]
-	delete(c.outstandingRequests, id)
-	delete(c.flowMap, id)
-	c.Unlock()
-	if request != nil && request.stream != nil {
-		request.stream.end()
-		request.stream.waitUntilDone()
-	}
-}
-
-// RT returns the runtime of the app.
-func (c *Controller) Context() *context.T {
-	return c.ctx
-}
-
-// GetOrAddBlessingsHandle adds the Blessings to the local blessings store if they
-// don't already existand returns the handle to it.  This function exists
-// because JS only has a handle to the blessings to avoid shipping the
-// certificate forest to JS and back.
-func (c *Controller) GetOrAddBlessingsHandle(blessings security.Blessings) principal.BlessingsHandle {
-	return c.blessingsCache.GetOrAddHandle(blessings)
-}
-
-// GetBlessings gets blessings for a given blessings handle.
-func (c *Controller) GetBlessings(handle principal.BlessingsHandle) security.Blessings {
-	return c.blessingsCache.GetBlessings(handle)
-}
-
-// Cleanup cleans up any outstanding rpcs.
-func (c *Controller) Cleanup() {
-	vlog.VI(0).Info("Cleaning up controller")
-	c.Lock()
-
-	for _, request := range c.outstandingRequests {
-		if request.cancel != nil {
-			request.cancel()
-		}
-		if request.stream != nil {
-			request.stream.end()
-		}
-	}
-
-	servers := []*server.Server{}
-	for _, server := range c.servers {
-		servers = append(servers, server)
-	}
-
-	c.Unlock()
-
-	// We must unlock before calling server.Stop otherwise it can deadlock.
-	for _, server := range servers {
-		server.Stop()
-	}
-
-	c.cancel()
-}
-
-func (c *Controller) setup() {
-	c.signatureManager = lib.NewSignatureManager()
-	c.outstandingRequests = make(map[int32]*outstandingRequest)
-	c.flowMap = make(map[int32]interface{})
-	c.servers = make(map[uint32]*server.Server)
-}
-
-// SendOnStream writes data on id's stream.  The actual network write will be
-// done asynchronously.  If there is an error, it will be sent to w.
-func (c *Controller) SendOnStream(id int32, data string, w lib.ClientWriter) {
-	c.Lock()
-	request := c.outstandingRequests[id]
-	if request == nil || request.stream == nil {
-		vlog.Errorf("unknown stream: %d", id)
-		c.Unlock()
-		return
-	}
-	stream := request.stream
-	c.Unlock()
-	stream.send(data, w)
-}
-
-// SendVeyronRequest makes a vanadium request for the given flowId.  If signal is non-nil, it will receive
-// the call object after it has been constructed.
-func (c *Controller) sendVeyronRequest(ctx *context.T, id int32, msg *RpcRequest, inArgs []interface{}, w lib.ClientWriter, stream *outstandingStream, span vtrace.Span) {
-	sig, err := c.getSignature(ctx, msg.Name)
-	if err != nil {
-		w.Error(err)
-		return
-	}
-	methName := lib.UppercaseFirstCharacter(msg.Method)
-	methSig, ok := signature.FirstMethod(sig, methName)
-	if !ok {
-		w.Error(fmt.Errorf("method %q not found in signature: %#v", methName, sig))
-		return
-	}
-	if len(methSig.InArgs) != len(inArgs) {
-		w.Error(fmt.Errorf("invalid number of arguments, expected: %v, got:%v", methSig, *msg))
-		return
-	}
-
-	for i, arg := range inArgs {
-		if jsBlessings, ok := arg.(principal.JsBlessings); ok {
-			inArgs[i] = c.blessingsCache.GetBlessings(jsBlessings.Handle)
-		}
-	}
-	// We have to make the start call synchronous so we can make sure that we populate
-	// the call map before we can Handle a recieve call.
-	call, err := c.startCall(ctx, w, msg, inArgs)
-	if err != nil {
-		w.Error(verror.Convert(verror.ErrInternal, ctx, err))
-		return
-	}
-
-	if stream != nil {
-		stream.init(call)
-	}
-
-	c.finishCall(ctx, w, call, msg, span)
-	c.Lock()
-	if request, ok := c.outstandingRequests[id]; ok {
-		delete(c.outstandingRequests, id)
-		if request.cancel != nil {
-			request.cancel()
-		}
-	}
-	c.Unlock()
-}
-
-// TODO(mattr): This is a very limited implementation of ServerCall,
-// but currently none of the methods the controller exports require
-// any of this context information.
-type localCall struct {
-	ctx  *context.T
-	vrpc *RpcRequest
-	tags []*vdl.Value
-	w    lib.ClientWriter
-}
-
-var (
-	_ rpc.StreamServerCall = (*localCall)(nil)
-	_ security.Call        = (*localCall)(nil)
-)
-
-func (l *localCall) Send(item interface{}) error {
-	vomItem, err := lib.HexVomEncode(item)
-	if err != nil {
-		err = verror.New(marshallingError, l.ctx, item, err)
-		l.w.Error(err)
-		return err
-	}
-	if err := l.w.Send(lib.ResponseStream, vomItem); err != nil {
-		err = verror.New(marshallingError, l.ctx, item)
-		l.w.Error(err)
-		return err
-	}
-	return nil
-}
-func (l *localCall) Recv(interface{}) error                          { return nil }
-func (l *localCall) GrantedBlessings() security.Blessings            { return security.Blessings{} }
-func (l *localCall) Server() rpc.Server                              { return nil }
-func (l *localCall) Timestamp() (t time.Time)                        { return }
-func (l *localCall) Method() string                                  { return l.vrpc.Method }
-func (l *localCall) MethodTags() []*vdl.Value                        { return l.tags }
-func (l *localCall) Suffix() string                                  { return l.vrpc.Name }
-func (l *localCall) LocalDischarges() map[string]security.Discharge  { return nil }
-func (l *localCall) RemoteDischarges() map[string]security.Discharge { return nil }
-func (l *localCall) LocalPrincipal() security.Principal              { return nil }
-func (l *localCall) LocalBlessings() security.Blessings              { return security.Blessings{} }
-func (l *localCall) RemoteBlessings() security.Blessings             { return security.Blessings{} }
-func (l *localCall) LocalEndpoint() naming.Endpoint                  { return nil }
-func (l *localCall) RemoteEndpoint() naming.Endpoint                 { return nil }
-func (l *localCall) Security() security.Call                         { return l }
-
-func (c *Controller) handleInternalCall(ctx *context.T, invoker rpc.Invoker, msg *RpcRequest, decoder *vom.Decoder, w lib.ClientWriter, span vtrace.Span) {
-	argptrs, tags, err := invoker.Prepare(msg.Method, int(msg.NumInArgs))
-	if err != nil {
-		w.Error(verror.Convert(verror.ErrInternal, ctx, err))
-		return
-	}
-	for _, argptr := range argptrs {
-		if err := decoder.Decode(argptr); err != nil {
-			w.Error(verror.Convert(verror.ErrInternal, ctx, err))
-			return
-		}
-	}
-	results, err := invoker.Invoke(ctx, &localCall{ctx, msg, tags, w}, msg.Method, argptrs)
-	if err != nil {
-		w.Error(verror.Convert(verror.ErrInternal, ctx, err))
-		return
-	}
-	if msg.IsStreaming {
-		if err := w.Send(lib.ResponseStreamClose, nil); err != nil {
-			w.Error(verror.New(marshallingError, ctx, "ResponseStreamClose"))
-		}
-	}
-
-	// Convert results from []interface{} to []*vdl.Value.
-	vresults := make([]*vdl.Value, len(results))
-	for i, res := range results {
-		vv, err := vdl.ValueFromReflect(reflect.ValueOf(res))
-		if err != nil {
-			w.Error(verror.Convert(verror.ErrInternal, ctx, err))
-			return
-		}
-		vresults[i] = vv
-	}
-	c.sendRPCResponse(ctx, w, span, vresults)
-}
-
-// HandleCaveatValidationResponse handles the response to caveat validation
-// requests.
-func (c *Controller) HandleCaveatValidationResponse(id int32, data string) {
-	c.Lock()
-	server, ok := c.flowMap[id].(*server.Server)
-	c.Unlock()
-	if !ok {
-		vlog.Errorf("unexpected result from JavaScript. No server found matching id %d.", id)
-		return // ignore unknown server
-	}
-	server.HandleCaveatValidationResponse(id, data)
-}
-
-// HandleVeyronRequest starts a vanadium rpc and returns before the rpc has been completed.
-func (c *Controller) HandleVeyronRequest(ctx *context.T, id int32, data string, w lib.ClientWriter) {
-	binbytes, err := hex.DecodeString(data)
-	if err != nil {
-		w.Error(verror.Convert(verror.ErrInternal, ctx, fmt.Errorf("Error decoding hex string %q: %v", data, err)))
-		return
-	}
-	decoder := vom.NewDecoder(bytes.NewReader(binbytes))
-	var msg RpcRequest
-	if err := decoder.Decode(&msg); err != nil {
-		w.Error(verror.Convert(verror.ErrInternal, ctx, err))
-		return
-	}
-	vlog.VI(2).Infof("Rpc: %s.%s(..., streaming=%v)", msg.Name, msg.Method, msg.IsStreaming)
-	spanName := fmt.Sprintf("<wspr>%q.%s", msg.Name, msg.Method)
-	ctx, span := vtrace.WithContinuedTrace(ctx, spanName, msg.TraceRequest)
-	ctx = i18n.WithLangID(ctx, i18n.LangID(msg.Context.Language))
-
-	var cctx *context.T
-	var cancel context.CancelFunc
-
-	// TODO(mattr): To be consistent with go, we should not ignore 0 timeouts.
-	// However as a rollout strategy we must, otherwise there is a circular
-	// dependency between the WSPR change and the JS change that will follow.
-	if msg.Deadline.IsZero() {
-		cctx, cancel = context.WithCancel(ctx)
-	} else {
-		cctx, cancel = context.WithDeadline(ctx, msg.Deadline.Time)
-	}
-
-	// If this message is for an internal service, do a short-circuit dispatch here.
-	if invoker, ok := c.reservedServices[msg.Name]; ok {
-		go c.handleInternalCall(ctx, invoker, &msg, decoder, w, span)
-		return
-	}
-
-	inArgs := make([]interface{}, msg.NumInArgs)
-	for i := range inArgs {
-		var v *vdl.Value
-		if err := decoder.Decode(&v); err != nil {
-			w.Error(err)
-			return
-		}
-		inArgs[i] = v
-	}
-
-	request := &outstandingRequest{
-		cancel: cancel,
-	}
-	if msg.IsStreaming {
-		// If this rpc is streaming, we would expect that the client would try to send
-		// on this stream.  Since the initial handshake is done asynchronously, we have
-		// to put the outstanding stream in the map before we make the async call so that
-		// the future send know which queue to write to, even if the client call isn't
-		// actually ready yet.
-		request.stream = newStream(c.blessingsCache)
-	}
-	c.Lock()
-	c.outstandingRequests[id] = request
-	go c.sendVeyronRequest(cctx, id, &msg, inArgs, w, request.stream, span)
-	c.Unlock()
-}
-
-// HandleVeyronCancellation cancels the request corresponding to the
-// given id if it is still outstanding.
-func (c *Controller) HandleVeyronCancellation(id int32) {
-	c.Lock()
-	defer c.Unlock()
-	if request, ok := c.outstandingRequests[id]; ok && request.cancel != nil {
-		request.cancel()
-	}
-}
-
-// CloseStream closes the stream for a given id.
-func (c *Controller) CloseStream(id int32) {
-	c.Lock()
-	defer c.Unlock()
-	if request, ok := c.outstandingRequests[id]; ok && request.stream != nil {
-		request.stream.end()
-		return
-	}
-	vlog.Errorf("close called on non-existent call: %v", id)
-}
-
-func (c *Controller) maybeCreateServer(serverId uint32, opts ...rpc.ServerOpt) (*server.Server, error) {
-	c.Lock()
-	defer c.Unlock()
-	if server, ok := c.servers[serverId]; ok {
-		return server, nil
-	}
-	server, err := server.NewServer(serverId, c.listenSpec, c, opts...)
-	if err != nil {
-		return nil, err
-	}
-	c.servers[serverId] = server
-	return server, nil
-}
-
-// HandleLookupResponse handles the result of a Dispatcher.Lookup call that was
-// run by the Javascript server.
-func (c *Controller) HandleLookupResponse(id int32, data string) {
-	c.Lock()
-	server, ok := c.flowMap[id].(*server.Server)
-	c.Unlock()
-	if !ok {
-		vlog.Errorf("unexpected result from JavaScript. No channel "+
-			"for MessageId: %d exists. Ignoring the results.", id)
-		//Ignore unknown responses that don't belong to any channel
-		return
-	}
-	server.HandleLookupResponse(id, data)
-}
-
-// HandleAuthResponse handles the result of a Authorizer.Authorize call that was
-// run by the Javascript server.
-func (c *Controller) HandleAuthResponse(id int32, data string) {
-	c.Lock()
-	server, ok := c.flowMap[id].(*server.Server)
-	c.Unlock()
-	if !ok {
-		vlog.Errorf("unexpected result from JavaScript. No channel "+
-			"for MessageId: %d exists. Ignoring the results.", id)
-		//Ignore unknown responses that don't belong to any channel
-		return
-	}
-	server.HandleAuthResponse(id, data)
-}
-
-// Serve instructs WSPR to start listening for calls on behalf
-// of a javascript server.
-func (c *Controller) Serve(_ *context.T, _ rpc.ServerCall, name string, serverId uint32, rpcServerOpts []RpcServerOption) error {
-
-	opts, err := c.serverOpts(rpcServerOpts)
-	if err != nil {
-		return verror.Convert(verror.ErrInternal, nil, err)
-	}
-	server, err := c.maybeCreateServer(serverId, opts...)
-	if err != nil {
-		return verror.Convert(verror.ErrInternal, nil, err)
-	}
-	vlog.VI(2).Infof("serving under name: %q", name)
-	if err := server.Serve(name); err != nil {
-		return verror.Convert(verror.ErrInternal, nil, err)
-	}
-	return nil
-}
-
-// Stop instructs WSPR to stop listening for calls for the
-// given javascript server.
-func (c *Controller) Stop(_ *context.T, _ rpc.ServerCall, serverId uint32) error {
-	c.Lock()
-	server, ok := c.servers[serverId]
-	if !ok {
-		c.Unlock()
-		return nil
-	}
-	delete(c.servers, serverId)
-	c.Unlock()
-
-	server.Stop()
-	return nil
-}
-
-// AddName adds a published name to an existing server.
-func (c *Controller) AddName(_ *context.T, _ rpc.ServerCall, serverId uint32, name string) error {
-	// Create a server for the pipe, if it does not exist already
-	server, err := c.maybeCreateServer(serverId)
-	if err != nil {
-		return verror.Convert(verror.ErrInternal, nil, err)
-	}
-	// Add name
-	if err := server.AddName(name); err != nil {
-		return verror.Convert(verror.ErrInternal, nil, err)
-	}
-	return nil
-}
-
-// RemoveName removes a published name from an existing server.
-func (c *Controller) RemoveName(_ *context.T, _ rpc.ServerCall, serverId uint32, name string) error {
-	// Create a server for the pipe, if it does not exist already
-	server, err := c.maybeCreateServer(serverId)
-	if err != nil {
-		return verror.Convert(verror.ErrInternal, nil, err)
-	}
-	// Remove name
-	server.RemoveName(name)
-	// Remove name from signature cache as well
-	c.signatureManager.FlushCacheEntry(name)
-	return nil
-}
-
-// HandleServerResponse handles the completion of outstanding calls to JavaScript services
-// by filling the corresponding channel with the result from JavaScript.
-func (c *Controller) HandleServerResponse(id int32, data string) {
-	c.Lock()
-	server, ok := c.flowMap[id].(*server.Server)
-	c.Unlock()
-	if !ok {
-		vlog.Errorf("unexpected result from JavaScript. No channel "+
-			"for MessageId: %d exists. Ignoring the results.", id)
-		//Ignore unknown responses that don't belong to any channel
-		return
-	}
-	server.HandleServerResponse(id, data)
-}
-
-// parseVeyronRequest parses a json rpc request into a RpcRequest object.
-func (c *Controller) parseVeyronRequest(data string) (*RpcRequest, error) {
-	var msg RpcRequest
-	if err := lib.HexVomDecode(data, &msg); err != nil {
-		return nil, err
-	}
-	vlog.VI(2).Infof("RpcRequest: %s.%s(..., streaming=%v)", msg.Name, msg.Method, msg.IsStreaming)
-	return &msg, nil
-}
-
-// getSignature uses the signature manager to get and cache the signature of a remote server.
-func (c *Controller) getSignature(ctx *context.T, name string) ([]signature.Interface, error) {
-	return c.signatureManager.Signature(ctx, name)
-}
-
-// Signature uses the signature manager to get and cache the signature of a remote server.
-func (c *Controller) Signature(ctx *context.T, _ rpc.ServerCall, name string) ([]signature.Interface, error) {
-	return c.getSignature(ctx, name)
-}
-
-// UnlinkBlessings removes the given blessings from the blessings store.
-func (c *Controller) UnlinkBlessings(_ *context.T, _ rpc.ServerCall, handle principal.BlessingsHandle) error {
-	return c.blessingsCache.RemoveReference(handle)
-}
-
-// Bless binds extensions of blessings held by this principal to
-// another principal (represented by its public key).
-func (c *Controller) Bless(_ *context.T, _ rpc.ServerCall, publicKey string, blessingHandle principal.BlessingsHandle, extension string, caveats []security.Caveat) (string, principal.BlessingsHandle, error) {
-	var inputBlessing security.Blessings
-	if inputBlessing = c.GetBlessings(blessingHandle); inputBlessing.IsZero() {
-		return "", principal.ZeroHandle, verror.New(invalidBlessingsHandle, nil, blessingHandle)
-	}
-
-	key, err := principal.DecodePublicKey(publicKey)
-	if err != nil {
-		return "", principal.ZeroHandle, err
-	}
-
-	if len(caveats) == 0 {
-		caveats = append(caveats, security.UnconstrainedUse())
-	}
-
-	p := v23.GetPrincipal(c.ctx)
-	blessings, err := p.Bless(key, inputBlessing, extension, caveats[0], caveats[1:]...)
-	if err != nil {
-		return "", principal.ZeroHandle, err
-	}
-	handle := c.blessingsCache.GetOrAddHandle(blessings)
-	return publicKey, handle, nil
-}
-
-// BlessSelf creates a blessing with the provided name for this principal.
-func (c *Controller) BlessSelf(_ *context.T, _ rpc.ServerCall, extension string, caveats []security.Caveat) (string, principal.BlessingsHandle, error) {
-	p := v23.GetPrincipal(c.ctx)
-	blessings, err := p.BlessSelf(extension)
-	if err != nil {
-		return "", principal.ZeroHandle, verror.Convert(verror.ErrInternal, nil, err)
-	}
-
-	handle := c.blessingsCache.GetOrAddHandle(blessings)
-
-	encKey, err := principal.EncodePublicKey(p.PublicKey())
-	return encKey, handle, err
-}
-
-// BlessingStoreSet puts the specified blessing in the blessing store under the
-// provided pattern.
-func (c *Controller) BlessingStoreSet(_ *context.T, _ rpc.ServerCall, handle principal.BlessingsHandle, pattern security.BlessingPattern) (*principal.JsBlessings, error) {
-	var inputBlessings security.Blessings
-	if inputBlessings = c.GetBlessings(handle); inputBlessings.IsZero() {
-		return nil, verror.New(invalidBlessingsHandle, nil, handle)
-	}
-
-	p := v23.GetPrincipal(c.ctx)
-	outBlessings, err := p.BlessingStore().Set(inputBlessings, security.BlessingPattern(pattern))
-	if err != nil {
-		return nil, err
-	}
-
-	if outBlessings.IsZero() {
-		return nil, nil
-	}
-
-	jsBlessings := principal.ConvertBlessingsToHandle(outBlessings, c.blessingsCache.GetOrAddHandle(outBlessings))
-	return jsBlessings, nil
-}
-
-// BlessingStoreForPeer puts the specified blessing in the blessing store under the
-// provided pattern.
-func (c *Controller) BlessingStoreForPeer(_ *context.T, _ rpc.ServerCall, peerBlessings []string) (*principal.JsBlessings, error) {
-	p := v23.GetPrincipal(c.ctx)
-	outBlessings := p.BlessingStore().ForPeer(peerBlessings...)
-
-	if outBlessings.IsZero() {
-		return nil, nil
-	}
-
-	jsBlessings := principal.ConvertBlessingsToHandle(outBlessings, c.blessingsCache.GetOrAddHandle(outBlessings))
-	return jsBlessings, nil
-}
-
-// BlessingStoreSetDefault sets the default blessings in the blessing store.
-func (c *Controller) BlessingStoreSetDefault(_ *context.T, _ rpc.ServerCall, handle principal.BlessingsHandle) error {
-	var inputBlessings security.Blessings
-	if inputBlessings = c.GetBlessings(handle); inputBlessings.IsZero() {
-		return verror.New(invalidBlessingsHandle, nil, handle)
-	}
-
-	p := v23.GetPrincipal(c.ctx)
-	return p.BlessingStore().SetDefault(inputBlessings)
-}
-
-// BlessingStoreDefault fetches the default blessings for the principal of the controller.
-func (c *Controller) BlessingStoreDefault(*context.T, rpc.ServerCall) (*principal.JsBlessings, error) {
-	p := v23.GetPrincipal(c.ctx)
-	outBlessings := p.BlessingStore().Default()
-
-	if outBlessings.IsZero() {
-		return nil, nil
-	}
-
-	jsBlessing := principal.ConvertBlessingsToHandle(outBlessings, c.blessingsCache.GetOrAddHandle(outBlessings))
-	return jsBlessing, nil
-}
-
-<<<<<<< HEAD
-// BlessingStorePublicKey fetches the public key used by the principal associated with the blessing store.
-func (c *Controller) BlessingStorePublicKey(*context.T, rpc.ServerCall) (string, error) {
-	p := v23.GetPrincipal(c.ctx)
-	pk := p.BlessingStore().PublicKey()
-	return principal.EncodePublicKey(pk)
-}
-
-// BlessingStorePeerBlessings returns all the blessings that the BlessingStore holds.
-func (c *Controller) BlessingStorePeerBlessings(*context.T, rpc.ServerCall) (map[security.BlessingPattern]*principal.JsBlessings, error) {
-	p := v23.GetPrincipal(c.ctx)
-	outBlessingsMap := map[security.BlessingPattern]*principal.JsBlessings{}
-	for pattern, blessings := range p.BlessingStore().PeerBlessings() {
-		outBlessingsMap[pattern] = principal.ConvertBlessingsToHandle(blessings, c.blessingsCache.GetOrAddHandle(blessings))
-	}
-	return outBlessingsMap, nil
-}
-
-// BlessingStoreDebugString retrieves a debug string describing the state of the blessing store
-func (c *Controller) BlessingStoreDebugString(*context.T, rpc.ServerCall) (string, error) {
-	p := v23.GetPrincipal(c.ctx)
-	ds := p.BlessingStore().DebugString()
-	return ds, nil
-}
-
-// AddToRoots adds the provided blessing as a root.
-func (c *Controller) AddToRoots(_ *context.T, _ rpc.ServerCall, handle principal.BlessingsHandle) error {
-	var inputBlessings security.Blessings
-	if inputBlessings = c.GetBlessings(handle); inputBlessings.IsZero() {
-		return verror.New(invalidBlessingsHandle, nil, handle)
-	}
-
-	p := v23.GetPrincipal(c.ctx)
-	return p.AddToRoots(inputBlessings)
-}
-
-=======
->>>>>>> master
-// UnionOfBlessings returns a Blessings object that carries the union of the provided blessings.
-func (c *Controller) UnionOfBlessings(_ *context.T, _ rpc.ServerCall, handles []principal.BlessingsHandle) (*principal.JsBlessings, error) {
-	inputBlessings := make([]security.Blessings, len(handles))
-	for i, handle := range handles {
-		bless := c.GetBlessings(handle)
-		if bless.IsZero() {
-			return nil, verror.New(invalidBlessingsHandle, nil, handle)
-		}
-		inputBlessings[i] = bless
-	}
-
-	outBlessings, err := security.UnionOfBlessings(inputBlessings...)
-	if err != nil {
-		return nil, err
-	}
-
-	jsBlessings := principal.ConvertBlessingsToHandle(outBlessings, c.blessingsCache.GetOrAddHandle(outBlessings))
-	return jsBlessings, nil
-}
-
-// HandleGranterResponse handles the result of a Granter request.
-func (c *Controller) HandleGranterResponse(id int32, data string) {
-	c.Lock()
-	granterStr, ok := c.flowMap[id].(*granterStream)
-	c.Unlock()
-	if !ok {
-		vlog.Errorf("unexpected result from JavaScript. Flow was not a granter "+
-			"stream for MessageId: %d exists. Ignoring the results.", id)
-		//Ignore unknown responses that don't belong to any channel
-		return
-	}
-	granterStr.Send(data)
-}
-
-func (c *Controller) BlessingsDebugString(_ *context.T, _ rpc.ServerCall, handle principal.BlessingsHandle) (string, error) {
-	var inputBlessings security.Blessings
-	if inputBlessings = c.GetBlessings(handle); inputBlessings.IsZero() {
-		return "", verror.New(invalidBlessingsHandle, nil, handle)
-	}
-
-	return inputBlessings.String(), nil
-}
-
-func (c *Controller) RemoteBlessings(ctx *context.T, _ rpc.ServerCall, name, method string) ([]string, error) {
-	vlog.VI(2).Infof("requesting remote blessings for %q", name)
-
-	cctx, cancel := context.WithTimeout(ctx, 5*time.Second)
-	defer cancel()
-
-	clientCall, err := v23.GetClient(cctx).StartCall(cctx, name, method, nil)
-	if err != nil {
-		return nil, verror.Convert(verror.ErrInternal, cctx, err)
-	}
-
-	blessings, _ := clientCall.RemoteBlessings()
-	return blessings, nil
-}
-
-func (c *Controller) SendLogMessage(level lib.LogLevel, msg string) error {
-	c.Lock()
-	defer c.Unlock()
-	id := c.lastGeneratedId
-	c.lastGeneratedId += 2
-	return c.writerCreator(id).Send(lib.ResponseLog, lib.LogMessage{
-		Level:   level,
-		Message: msg,
-	})
-}
diff --git a/services/wspr/internal/app/app_test.go b/services/wspr/internal/app/app_test.go
index 537b8fc..b71c6ff 100644
--- a/services/wspr/internal/app/app_test.go
+++ b/services/wspr/internal/app/app_test.go
@@ -136,7 +136,7 @@
 	}
 }
 func TestGetGoServerSignature(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	s, endpoint, err := startAdderServer(ctx)
@@ -181,7 +181,7 @@
 }
 
 func runGoServerTestCase(t *testing.T, testCase goServerTestCase) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	s, endpoint, err := startAdderServer(ctx)
@@ -430,7 +430,7 @@
 }
 
 func runJsServerTestCase(t *testing.T, testCase jsServerTestCase) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	vomClientStream := []string{}
diff --git a/services/wspr/internal/app/controller.vdl b/services/wspr/internal/app/controller.vdl
index 8983e4e..cbe271c 100644
--- a/services/wspr/internal/app/controller.vdl
+++ b/services/wspr/internal/app/controller.vdl
@@ -29,24 +29,24 @@
 	BlessingsDebugString(handle principal.BlessingsHandle) (string | error)
 	// Bless binds extensions of blessings held by this principal to
 	// another principal (represented by its public key).
-	Bless(publicKey string, handle principal.BlessingsHandle, extension string, caveat []security.Caveat) (publicKeyOut string, handleOut principal.BlessingsHandle | error)
+	Bless(publicKey string, handle principal.BlessingsHandle, extension string, caveat []security.Caveat) (principal.BlessingsId | error)
 	// BlessSelf creates a blessing with the provided name for this principal.
-	BlessSelf(name string, caveats []security.Caveat) (publicKeyOut string, handleOut principal.BlessingsHandle | error)
+	BlessSelf(name string, caveats []security.Caveat) (principal.BlessingsId | error)
 	// AddToRoots adds the provided blessing as a root.
 	AddToRoots(handle principal.BlessingsHandle) error
 
 	// BlessingStoreSet puts the specified blessing in the blessing store under the provided pattern.
-	BlessingStoreSet(blessingsHandle principal.BlessingsHandle, pattern security.BlessingPattern) (?principal.JsBlessings | error)
+	BlessingStoreSet(blessingsHandle principal.BlessingsHandle, pattern security.BlessingPattern) (principal.BlessingsId | error)
 	// BlessingStoreForPeer retrieves the blessings marked for the given peers.
-	BlessingStoreForPeer(peerBlessings []string) (?principal.JsBlessings | error)
+	BlessingStoreForPeer(peerBlessings []string) (principal.BlessingsId | error)
 	// BlessingStoreSetDefault sets the default blessings.
 	BlessingStoreSetDefault(blessingsHandle principal.BlessingsHandle) error
 	// BlessingStoreDefault fetches the default blessings for the principal of the controller.
-	BlessingStoreDefault() (?principal.JsBlessings | error)
+	BlessingStoreDefault() (principal.BlessingsId | error)
 	// BlessingStorePublicKey fetches the public key of the principal for which this store hosts blessings.
 	BlessingStorePublicKey() (string | error)
 	// BlessingStorePeerBlessings returns all the blessings that the BlessingStore holds.
-	BlessingStorePeerBlessings() (map[security.BlessingPattern]?principal.JsBlessings | error)
+	BlessingStorePeerBlessings() (map[security.BlessingPattern]principal.BlessingsId | error)
 	// BlessingStoreDebugString retrieves a debug string describing the state of the blessing store
 	BlessingStoreDebugString() (string | error)
 
@@ -57,5 +57,5 @@
 	Signature(name string) ([]signature.Interface | error)
 
   // UnionOfBlessings returns a Blessings object that carries the union of the provided blessings.
-	UnionOfBlessings(toJoin []principal.BlessingsHandle) (?principal.JsBlessings | error)
+	UnionOfBlessings(toJoin []principal.BlessingsHandle) (principal.BlessingsId | error)
 }
diff --git a/services/wspr/internal/app/controller.vdl.go b/services/wspr/internal/app/controller.vdl.go
index 2731e36..4b185de 100644
--- a/services/wspr/internal/app/controller.vdl.go
+++ b/services/wspr/internal/app/controller.vdl.go
@@ -38,23 +38,23 @@
 	BlessingsDebugString(ctx *context.T, handle principal.BlessingsHandle, opts ...rpc.CallOpt) (string, error)
 	// Bless binds extensions of blessings held by this principal to
 	// another principal (represented by its public key).
-	Bless(ctx *context.T, publicKey string, handle principal.BlessingsHandle, extension string, caveat []security.Caveat, opts ...rpc.CallOpt) (publicKeyOut string, handleOut principal.BlessingsHandle, err error)
+	Bless(ctx *context.T, publicKey string, handle principal.BlessingsHandle, extension string, caveat []security.Caveat, opts ...rpc.CallOpt) (principal.BlessingsId, error)
 	// BlessSelf creates a blessing with the provided name for this principal.
-	BlessSelf(ctx *context.T, name string, caveats []security.Caveat, opts ...rpc.CallOpt) (publicKeyOut string, handleOut principal.BlessingsHandle, err error)
+	BlessSelf(ctx *context.T, name string, caveats []security.Caveat, opts ...rpc.CallOpt) (principal.BlessingsId, error)
 	// AddToRoots adds the provided blessing as a root.
 	AddToRoots(ctx *context.T, handle principal.BlessingsHandle, opts ...rpc.CallOpt) error
 	// BlessingStoreSet puts the specified blessing in the blessing store under the provided pattern.
-	BlessingStoreSet(ctx *context.T, blessingsHandle principal.BlessingsHandle, pattern security.BlessingPattern, opts ...rpc.CallOpt) (*principal.JsBlessings, error)
+	BlessingStoreSet(ctx *context.T, blessingsHandle principal.BlessingsHandle, pattern security.BlessingPattern, opts ...rpc.CallOpt) (principal.BlessingsId, error)
 	// BlessingStoreForPeer retrieves the blessings marked for the given peers.
-	BlessingStoreForPeer(ctx *context.T, peerBlessings []string, opts ...rpc.CallOpt) (*principal.JsBlessings, error)
+	BlessingStoreForPeer(ctx *context.T, peerBlessings []string, opts ...rpc.CallOpt) (principal.BlessingsId, error)
 	// BlessingStoreSetDefault sets the default blessings.
 	BlessingStoreSetDefault(ctx *context.T, blessingsHandle principal.BlessingsHandle, opts ...rpc.CallOpt) error
 	// BlessingStoreDefault fetches the default blessings for the principal of the controller.
-	BlessingStoreDefault(*context.T, ...rpc.CallOpt) (*principal.JsBlessings, error)
+	BlessingStoreDefault(*context.T, ...rpc.CallOpt) (principal.BlessingsId, error)
 	// BlessingStorePublicKey fetches the public key of the principal for which this store hosts blessings.
 	BlessingStorePublicKey(*context.T, ...rpc.CallOpt) (string, error)
 	// BlessingStorePeerBlessings returns all the blessings that the BlessingStore holds.
-	BlessingStorePeerBlessings(*context.T, ...rpc.CallOpt) (map[security.BlessingPattern]*principal.JsBlessings, error)
+	BlessingStorePeerBlessings(*context.T, ...rpc.CallOpt) (map[security.BlessingPattern]principal.BlessingsId, error)
 	// BlessingStoreDebugString retrieves a debug string describing the state of the blessing store
 	BlessingStoreDebugString(*context.T, ...rpc.CallOpt) (string, error)
 	// RemoteBlessings fetches the remote blessings for a given name and method.
@@ -62,7 +62,7 @@
 	// Signature fetches the signature for a given name.
 	Signature(ctx *context.T, name string, opts ...rpc.CallOpt) ([]signature.Interface, error)
 	// UnionOfBlessings returns a Blessings object that carries the union of the provided blessings.
-	UnionOfBlessings(ctx *context.T, toJoin []principal.BlessingsHandle, opts ...rpc.CallOpt) (*principal.JsBlessings, error)
+	UnionOfBlessings(ctx *context.T, toJoin []principal.BlessingsHandle, opts ...rpc.CallOpt) (principal.BlessingsId, error)
 }
 
 // ControllerClientStub adds universal methods to ControllerClientMethods.
@@ -110,13 +110,13 @@
 	return
 }
 
-func (c implControllerClientStub) Bless(ctx *context.T, i0 string, i1 principal.BlessingsHandle, i2 string, i3 []security.Caveat, opts ...rpc.CallOpt) (o0 string, o1 principal.BlessingsHandle, err error) {
-	err = v23.GetClient(ctx).Call(ctx, c.name, "Bless", []interface{}{i0, i1, i2, i3}, []interface{}{&o0, &o1}, opts...)
+func (c implControllerClientStub) Bless(ctx *context.T, i0 string, i1 principal.BlessingsHandle, i2 string, i3 []security.Caveat, opts ...rpc.CallOpt) (o0 principal.BlessingsId, err error) {
+	err = v23.GetClient(ctx).Call(ctx, c.name, "Bless", []interface{}{i0, i1, i2, i3}, []interface{}{&o0}, opts...)
 	return
 }
 
-func (c implControllerClientStub) BlessSelf(ctx *context.T, i0 string, i1 []security.Caveat, opts ...rpc.CallOpt) (o0 string, o1 principal.BlessingsHandle, err error) {
-	err = v23.GetClient(ctx).Call(ctx, c.name, "BlessSelf", []interface{}{i0, i1}, []interface{}{&o0, &o1}, opts...)
+func (c implControllerClientStub) BlessSelf(ctx *context.T, i0 string, i1 []security.Caveat, opts ...rpc.CallOpt) (o0 principal.BlessingsId, err error) {
+	err = v23.GetClient(ctx).Call(ctx, c.name, "BlessSelf", []interface{}{i0, i1}, []interface{}{&o0}, opts...)
 	return
 }
 
@@ -125,12 +125,12 @@
 	return
 }
 
-func (c implControllerClientStub) BlessingStoreSet(ctx *context.T, i0 principal.BlessingsHandle, i1 security.BlessingPattern, opts ...rpc.CallOpt) (o0 *principal.JsBlessings, err error) {
+func (c implControllerClientStub) BlessingStoreSet(ctx *context.T, i0 principal.BlessingsHandle, i1 security.BlessingPattern, opts ...rpc.CallOpt) (o0 principal.BlessingsId, err error) {
 	err = v23.GetClient(ctx).Call(ctx, c.name, "BlessingStoreSet", []interface{}{i0, i1}, []interface{}{&o0}, opts...)
 	return
 }
 
-func (c implControllerClientStub) BlessingStoreForPeer(ctx *context.T, i0 []string, opts ...rpc.CallOpt) (o0 *principal.JsBlessings, err error) {
+func (c implControllerClientStub) BlessingStoreForPeer(ctx *context.T, i0 []string, opts ...rpc.CallOpt) (o0 principal.BlessingsId, err error) {
 	err = v23.GetClient(ctx).Call(ctx, c.name, "BlessingStoreForPeer", []interface{}{i0}, []interface{}{&o0}, opts...)
 	return
 }
@@ -140,7 +140,7 @@
 	return
 }
 
-func (c implControllerClientStub) BlessingStoreDefault(ctx *context.T, opts ...rpc.CallOpt) (o0 *principal.JsBlessings, err error) {
+func (c implControllerClientStub) BlessingStoreDefault(ctx *context.T, opts ...rpc.CallOpt) (o0 principal.BlessingsId, err error) {
 	err = v23.GetClient(ctx).Call(ctx, c.name, "BlessingStoreDefault", nil, []interface{}{&o0}, opts...)
 	return
 }
@@ -150,7 +150,7 @@
 	return
 }
 
-func (c implControllerClientStub) BlessingStorePeerBlessings(ctx *context.T, opts ...rpc.CallOpt) (o0 map[security.BlessingPattern]*principal.JsBlessings, err error) {
+func (c implControllerClientStub) BlessingStorePeerBlessings(ctx *context.T, opts ...rpc.CallOpt) (o0 map[security.BlessingPattern]principal.BlessingsId, err error) {
 	err = v23.GetClient(ctx).Call(ctx, c.name, "BlessingStorePeerBlessings", nil, []interface{}{&o0}, opts...)
 	return
 }
@@ -170,7 +170,7 @@
 	return
 }
 
-func (c implControllerClientStub) UnionOfBlessings(ctx *context.T, i0 []principal.BlessingsHandle, opts ...rpc.CallOpt) (o0 *principal.JsBlessings, err error) {
+func (c implControllerClientStub) UnionOfBlessings(ctx *context.T, i0 []principal.BlessingsHandle, opts ...rpc.CallOpt) (o0 principal.BlessingsId, err error) {
 	err = v23.GetClient(ctx).Call(ctx, c.name, "UnionOfBlessings", []interface{}{i0}, []interface{}{&o0}, opts...)
 	return
 }
@@ -194,23 +194,23 @@
 	BlessingsDebugString(ctx *context.T, call rpc.ServerCall, handle principal.BlessingsHandle) (string, error)
 	// Bless binds extensions of blessings held by this principal to
 	// another principal (represented by its public key).
-	Bless(ctx *context.T, call rpc.ServerCall, publicKey string, handle principal.BlessingsHandle, extension string, caveat []security.Caveat) (publicKeyOut string, handleOut principal.BlessingsHandle, err error)
+	Bless(ctx *context.T, call rpc.ServerCall, publicKey string, handle principal.BlessingsHandle, extension string, caveat []security.Caveat) (principal.BlessingsId, error)
 	// BlessSelf creates a blessing with the provided name for this principal.
-	BlessSelf(ctx *context.T, call rpc.ServerCall, name string, caveats []security.Caveat) (publicKeyOut string, handleOut principal.BlessingsHandle, err error)
+	BlessSelf(ctx *context.T, call rpc.ServerCall, name string, caveats []security.Caveat) (principal.BlessingsId, error)
 	// AddToRoots adds the provided blessing as a root.
 	AddToRoots(ctx *context.T, call rpc.ServerCall, handle principal.BlessingsHandle) error
 	// BlessingStoreSet puts the specified blessing in the blessing store under the provided pattern.
-	BlessingStoreSet(ctx *context.T, call rpc.ServerCall, blessingsHandle principal.BlessingsHandle, pattern security.BlessingPattern) (*principal.JsBlessings, error)
+	BlessingStoreSet(ctx *context.T, call rpc.ServerCall, blessingsHandle principal.BlessingsHandle, pattern security.BlessingPattern) (principal.BlessingsId, error)
 	// BlessingStoreForPeer retrieves the blessings marked for the given peers.
-	BlessingStoreForPeer(ctx *context.T, call rpc.ServerCall, peerBlessings []string) (*principal.JsBlessings, error)
+	BlessingStoreForPeer(ctx *context.T, call rpc.ServerCall, peerBlessings []string) (principal.BlessingsId, error)
 	// BlessingStoreSetDefault sets the default blessings.
 	BlessingStoreSetDefault(ctx *context.T, call rpc.ServerCall, blessingsHandle principal.BlessingsHandle) error
 	// BlessingStoreDefault fetches the default blessings for the principal of the controller.
-	BlessingStoreDefault(*context.T, rpc.ServerCall) (*principal.JsBlessings, error)
+	BlessingStoreDefault(*context.T, rpc.ServerCall) (principal.BlessingsId, error)
 	// BlessingStorePublicKey fetches the public key of the principal for which this store hosts blessings.
 	BlessingStorePublicKey(*context.T, rpc.ServerCall) (string, error)
 	// BlessingStorePeerBlessings returns all the blessings that the BlessingStore holds.
-	BlessingStorePeerBlessings(*context.T, rpc.ServerCall) (map[security.BlessingPattern]*principal.JsBlessings, error)
+	BlessingStorePeerBlessings(*context.T, rpc.ServerCall) (map[security.BlessingPattern]principal.BlessingsId, error)
 	// BlessingStoreDebugString retrieves a debug string describing the state of the blessing store
 	BlessingStoreDebugString(*context.T, rpc.ServerCall) (string, error)
 	// RemoteBlessings fetches the remote blessings for a given name and method.
@@ -218,7 +218,7 @@
 	// Signature fetches the signature for a given name.
 	Signature(ctx *context.T, call rpc.ServerCall, name string) ([]signature.Interface, error)
 	// UnionOfBlessings returns a Blessings object that carries the union of the provided blessings.
-	UnionOfBlessings(ctx *context.T, call rpc.ServerCall, toJoin []principal.BlessingsHandle) (*principal.JsBlessings, error)
+	UnionOfBlessings(ctx *context.T, call rpc.ServerCall, toJoin []principal.BlessingsHandle) (principal.BlessingsId, error)
 }
 
 // ControllerServerStubMethods is the server interface containing
@@ -280,11 +280,11 @@
 	return s.impl.BlessingsDebugString(ctx, call, i0)
 }
 
-func (s implControllerServerStub) Bless(ctx *context.T, call rpc.ServerCall, i0 string, i1 principal.BlessingsHandle, i2 string, i3 []security.Caveat) (string, principal.BlessingsHandle, error) {
+func (s implControllerServerStub) Bless(ctx *context.T, call rpc.ServerCall, i0 string, i1 principal.BlessingsHandle, i2 string, i3 []security.Caveat) (principal.BlessingsId, error) {
 	return s.impl.Bless(ctx, call, i0, i1, i2, i3)
 }
 
-func (s implControllerServerStub) BlessSelf(ctx *context.T, call rpc.ServerCall, i0 string, i1 []security.Caveat) (string, principal.BlessingsHandle, error) {
+func (s implControllerServerStub) BlessSelf(ctx *context.T, call rpc.ServerCall, i0 string, i1 []security.Caveat) (principal.BlessingsId, error) {
 	return s.impl.BlessSelf(ctx, call, i0, i1)
 }
 
@@ -292,11 +292,11 @@
 	return s.impl.AddToRoots(ctx, call, i0)
 }
 
-func (s implControllerServerStub) BlessingStoreSet(ctx *context.T, call rpc.ServerCall, i0 principal.BlessingsHandle, i1 security.BlessingPattern) (*principal.JsBlessings, error) {
+func (s implControllerServerStub) BlessingStoreSet(ctx *context.T, call rpc.ServerCall, i0 principal.BlessingsHandle, i1 security.BlessingPattern) (principal.BlessingsId, error) {
 	return s.impl.BlessingStoreSet(ctx, call, i0, i1)
 }
 
-func (s implControllerServerStub) BlessingStoreForPeer(ctx *context.T, call rpc.ServerCall, i0 []string) (*principal.JsBlessings, error) {
+func (s implControllerServerStub) BlessingStoreForPeer(ctx *context.T, call rpc.ServerCall, i0 []string) (principal.BlessingsId, error) {
 	return s.impl.BlessingStoreForPeer(ctx, call, i0)
 }
 
@@ -304,7 +304,7 @@
 	return s.impl.BlessingStoreSetDefault(ctx, call, i0)
 }
 
-func (s implControllerServerStub) BlessingStoreDefault(ctx *context.T, call rpc.ServerCall) (*principal.JsBlessings, error) {
+func (s implControllerServerStub) BlessingStoreDefault(ctx *context.T, call rpc.ServerCall) (principal.BlessingsId, error) {
 	return s.impl.BlessingStoreDefault(ctx, call)
 }
 
@@ -312,7 +312,7 @@
 	return s.impl.BlessingStorePublicKey(ctx, call)
 }
 
-func (s implControllerServerStub) BlessingStorePeerBlessings(ctx *context.T, call rpc.ServerCall) (map[security.BlessingPattern]*principal.JsBlessings, error) {
+func (s implControllerServerStub) BlessingStorePeerBlessings(ctx *context.T, call rpc.ServerCall) (map[security.BlessingPattern]principal.BlessingsId, error) {
 	return s.impl.BlessingStorePeerBlessings(ctx, call)
 }
 
@@ -328,7 +328,7 @@
 	return s.impl.Signature(ctx, call, i0)
 }
 
-func (s implControllerServerStub) UnionOfBlessings(ctx *context.T, call rpc.ServerCall, i0 []principal.BlessingsHandle) (*principal.JsBlessings, error) {
+func (s implControllerServerStub) UnionOfBlessings(ctx *context.T, call rpc.ServerCall, i0 []principal.BlessingsHandle) (principal.BlessingsId, error) {
 	return s.impl.UnionOfBlessings(ctx, call, i0)
 }
 
@@ -407,8 +407,7 @@
 				{"caveat", ``},    // []security.Caveat
 			},
 			OutArgs: []rpc.ArgDesc{
-				{"publicKeyOut", ``}, // string
-				{"handleOut", ``},    // principal.BlessingsHandle
+				{"", ``}, // principal.BlessingsId
 			},
 		},
 		{
@@ -419,8 +418,7 @@
 				{"caveats", ``}, // []security.Caveat
 			},
 			OutArgs: []rpc.ArgDesc{
-				{"publicKeyOut", ``}, // string
-				{"handleOut", ``},    // principal.BlessingsHandle
+				{"", ``}, // principal.BlessingsId
 			},
 		},
 		{
@@ -438,7 +436,7 @@
 				{"pattern", ``},         // security.BlessingPattern
 			},
 			OutArgs: []rpc.ArgDesc{
-				{"", ``}, // *principal.JsBlessings
+				{"", ``}, // principal.BlessingsId
 			},
 		},
 		{
@@ -448,7 +446,7 @@
 				{"peerBlessings", ``}, // []string
 			},
 			OutArgs: []rpc.ArgDesc{
-				{"", ``}, // *principal.JsBlessings
+				{"", ``}, // principal.BlessingsId
 			},
 		},
 		{
@@ -462,7 +460,7 @@
 			Name: "BlessingStoreDefault",
 			Doc:  "// BlessingStoreDefault fetches the default blessings for the principal of the controller.",
 			OutArgs: []rpc.ArgDesc{
-				{"", ``}, // *principal.JsBlessings
+				{"", ``}, // principal.BlessingsId
 			},
 		},
 		{
@@ -476,7 +474,7 @@
 			Name: "BlessingStorePeerBlessings",
 			Doc:  "// BlessingStorePeerBlessings returns all the blessings that the BlessingStore holds.",
 			OutArgs: []rpc.ArgDesc{
-				{"", ``}, // map[security.BlessingPattern]*principal.JsBlessings
+				{"", ``}, // map[security.BlessingPattern]principal.BlessingsId
 			},
 		},
 		{
@@ -514,7 +512,7 @@
 				{"toJoin", ``}, // []principal.BlessingsHandle
 			},
 			OutArgs: []rpc.ArgDesc{
-				{"", ``}, // *principal.JsBlessings
+				{"", ``}, // principal.BlessingsId
 			},
 		},
 	},
diff --git a/services/wspr/internal/app/controller.vdl.go.orig b/services/wspr/internal/app/controller.vdl.go.orig
deleted file mode 100644
index b8d730a..0000000
--- a/services/wspr/internal/app/controller.vdl.go.orig
+++ /dev/null
@@ -1,550 +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.
-
-// This file was auto-generated by the vanadium vdl tool.
-// Source: controller.vdl
-
-package app
-
-import (
-	// VDL system imports
-	"v.io/v23"
-	"v.io/v23/context"
-	"v.io/v23/rpc"
-
-	// VDL user imports
-	"v.io/v23/security"
-	"v.io/v23/vdlroot/signature"
-	"v.io/x/ref/services/wspr/internal/principal"
-)
-
-// ControllerClientMethods is the client interface
-// containing Controller methods.
-type ControllerClientMethods interface {
-	// Serve instructs WSPR to start listening for calls on behalf
-	// of a javascript server.
-	Serve(ctx *context.T, name string, serverId uint32, serverOpts []RpcServerOption, opts ...rpc.CallOpt) error
-	// Stop instructs WSPR to stop listening for calls for the
-	// given javascript server.
-	Stop(ctx *context.T, serverId uint32, opts ...rpc.CallOpt) error
-	// AddName adds a published name to an existing server.
-	AddName(ctx *context.T, serverId uint32, name string, opts ...rpc.CallOpt) error
-	// RemoveName removes a published name from an existing server.
-	RemoveName(ctx *context.T, serverId uint32, name string, opts ...rpc.CallOpt) error
-	// UnlinkBlessings removes the given blessings from the blessings store.
-	UnlinkBlessings(ctx *context.T, handle principal.BlessingsHandle, opts ...rpc.CallOpt) error
-	// BlessingsDebugString gets a string useful for debugging blessings.
-	BlessingsDebugString(ctx *context.T, handle principal.BlessingsHandle, opts ...rpc.CallOpt) (string, error)
-	// Bless binds extensions of blessings held by this principal to
-	// another principal (represented by its public key).
-	Bless(ctx *context.T, publicKey string, handle principal.BlessingsHandle, extension string, caveat []security.Caveat, opts ...rpc.CallOpt) (publicKeyOut string, handleOut principal.BlessingsHandle, err error)
-	// BlessSelf creates a blessing with the provided name for this principal.
-	BlessSelf(ctx *context.T, name string, caveats []security.Caveat, opts ...rpc.CallOpt) (publicKeyOut string, handleOut principal.BlessingsHandle, err error)
-	// AddToRoots adds the provided blessing as a root.
-	AddToRoots(ctx *context.T, handle principal.BlessingsHandle, opts ...rpc.CallOpt) error
-	// BlessingStoreSet puts the specified blessing in the blessing store under the provided pattern.
-	BlessingStoreSet(ctx *context.T, blessingsHandle principal.BlessingsHandle, pattern security.BlessingPattern, opts ...rpc.CallOpt) (*principal.JsBlessings, error)
-	// BlessingStoreForPeer retrieves the blessings marked for the given peers.
-	BlessingStoreForPeer(ctx *context.T, peerBlessings []string, opts ...rpc.CallOpt) (*principal.JsBlessings, error)
-	// BlessingStoreSetDefault sets the default blessings.
-	BlessingStoreSetDefault(ctx *context.T, blessingsHandle principal.BlessingsHandle, opts ...rpc.CallOpt) error
-	// BlessingStoreDefault fetches the default blessings for the principal of the controller.
-	BlessingStoreDefault(*context.T, ...rpc.CallOpt) (*principal.JsBlessings, error)
-	// BlessingStorePublicKey fetches the public key of the principal for which this store hosts blessings.
-	BlessingStorePublicKey(*context.T, ...rpc.CallOpt) (string, error)
-	// BlessingStorePeerBlessings returns all the blessings that the BlessingStore holds.
-	BlessingStorePeerBlessings(*context.T, ...rpc.CallOpt) (map[security.BlessingPattern]*principal.JsBlessings, error)
-	// BlessingStoreDebugString retrieves a debug string describing the state of the blessing store
-	BlessingStoreDebugString(*context.T, ...rpc.CallOpt) (string, error)
-	// RemoteBlessings fetches the remote blessings for a given name and method.
-	RemoteBlessings(ctx *context.T, name string, method string, opts ...rpc.CallOpt) ([]string, error)
-	// Signature fetches the signature for a given name.
-	Signature(ctx *context.T, name string, opts ...rpc.CallOpt) ([]signature.Interface, error)
-<<<<<<< HEAD
-=======
-	// GetDefaultBlessings fetches the default blessings for the principal of the controller.
-	GetDefaultBlessings(*context.T, ...rpc.CallOpt) (*principal.JsBlessings, error)
->>>>>>> master
-	// UnionOfBlessings returns a Blessings object that carries the union of the provided blessings.
-	UnionOfBlessings(ctx *context.T, toJoin []principal.BlessingsHandle, opts ...rpc.CallOpt) (*principal.JsBlessings, error)
-}
-
-// ControllerClientStub adds universal methods to ControllerClientMethods.
-type ControllerClientStub interface {
-	ControllerClientMethods
-	rpc.UniversalServiceMethods
-}
-
-// ControllerClient returns a client stub for Controller.
-func ControllerClient(name string) ControllerClientStub {
-	return implControllerClientStub{name}
-}
-
-type implControllerClientStub struct {
-	name string
-}
-
-func (c implControllerClientStub) Serve(ctx *context.T, i0 string, i1 uint32, i2 []RpcServerOption, opts ...rpc.CallOpt) (err error) {
-	err = v23.GetClient(ctx).Call(ctx, c.name, "Serve", []interface{}{i0, i1, i2}, nil, opts...)
-	return
-}
-
-func (c implControllerClientStub) Stop(ctx *context.T, i0 uint32, opts ...rpc.CallOpt) (err error) {
-	err = v23.GetClient(ctx).Call(ctx, c.name, "Stop", []interface{}{i0}, nil, opts...)
-	return
-}
-
-func (c implControllerClientStub) AddName(ctx *context.T, i0 uint32, i1 string, opts ...rpc.CallOpt) (err error) {
-	err = v23.GetClient(ctx).Call(ctx, c.name, "AddName", []interface{}{i0, i1}, nil, opts...)
-	return
-}
-
-func (c implControllerClientStub) RemoveName(ctx *context.T, i0 uint32, i1 string, opts ...rpc.CallOpt) (err error) {
-	err = v23.GetClient(ctx).Call(ctx, c.name, "RemoveName", []interface{}{i0, i1}, nil, opts...)
-	return
-}
-
-func (c implControllerClientStub) UnlinkBlessings(ctx *context.T, i0 principal.BlessingsHandle, opts ...rpc.CallOpt) (err error) {
-	err = v23.GetClient(ctx).Call(ctx, c.name, "UnlinkBlessings", []interface{}{i0}, nil, opts...)
-	return
-}
-
-func (c implControllerClientStub) BlessingsDebugString(ctx *context.T, i0 principal.BlessingsHandle, opts ...rpc.CallOpt) (o0 string, err error) {
-	err = v23.GetClient(ctx).Call(ctx, c.name, "BlessingsDebugString", []interface{}{i0}, []interface{}{&o0}, opts...)
-	return
-}
-
-func (c implControllerClientStub) Bless(ctx *context.T, i0 string, i1 principal.BlessingsHandle, i2 string, i3 []security.Caveat, opts ...rpc.CallOpt) (o0 string, o1 principal.BlessingsHandle, err error) {
-	err = v23.GetClient(ctx).Call(ctx, c.name, "Bless", []interface{}{i0, i1, i2, i3}, []interface{}{&o0, &o1}, opts...)
-	return
-}
-
-func (c implControllerClientStub) BlessSelf(ctx *context.T, i0 string, i1 []security.Caveat, opts ...rpc.CallOpt) (o0 string, o1 principal.BlessingsHandle, err error) {
-	err = v23.GetClient(ctx).Call(ctx, c.name, "BlessSelf", []interface{}{i0, i1}, []interface{}{&o0, &o1}, opts...)
-	return
-}
-
-func (c implControllerClientStub) AddToRoots(ctx *context.T, i0 principal.BlessingsHandle, opts ...rpc.CallOpt) (err error) {
-	err = v23.GetClient(ctx).Call(ctx, c.name, "AddToRoots", []interface{}{i0}, nil, opts...)
-	return
-}
-
-func (c implControllerClientStub) BlessingStoreSet(ctx *context.T, i0 principal.BlessingsHandle, i1 security.BlessingPattern, opts ...rpc.CallOpt) (o0 *principal.JsBlessings, err error) {
-	err = v23.GetClient(ctx).Call(ctx, c.name, "BlessingStoreSet", []interface{}{i0, i1}, []interface{}{&o0}, opts...)
-	return
-}
-
-func (c implControllerClientStub) BlessingStoreForPeer(ctx *context.T, i0 []string, opts ...rpc.CallOpt) (o0 *principal.JsBlessings, err error) {
-	err = v23.GetClient(ctx).Call(ctx, c.name, "BlessingStoreForPeer", []interface{}{i0}, []interface{}{&o0}, opts...)
-	return
-}
-
-func (c implControllerClientStub) BlessingStoreSetDefault(ctx *context.T, i0 principal.BlessingsHandle, opts ...rpc.CallOpt) (err error) {
-	err = v23.GetClient(ctx).Call(ctx, c.name, "BlessingStoreSetDefault", []interface{}{i0}, nil, opts...)
-	return
-}
-
-func (c implControllerClientStub) BlessingStoreDefault(ctx *context.T, opts ...rpc.CallOpt) (o0 *principal.JsBlessings, err error) {
-	err = v23.GetClient(ctx).Call(ctx, c.name, "BlessingStoreDefault", nil, []interface{}{&o0}, opts...)
-	return
-}
-
-func (c implControllerClientStub) BlessingStorePublicKey(ctx *context.T, opts ...rpc.CallOpt) (o0 string, err error) {
-	err = v23.GetClient(ctx).Call(ctx, c.name, "BlessingStorePublicKey", nil, []interface{}{&o0}, opts...)
-	return
-}
-
-func (c implControllerClientStub) BlessingStorePeerBlessings(ctx *context.T, opts ...rpc.CallOpt) (o0 map[security.BlessingPattern]*principal.JsBlessings, err error) {
-	err = v23.GetClient(ctx).Call(ctx, c.name, "BlessingStorePeerBlessings", nil, []interface{}{&o0}, opts...)
-	return
-}
-
-func (c implControllerClientStub) BlessingStoreDebugString(ctx *context.T, opts ...rpc.CallOpt) (o0 string, err error) {
-	err = v23.GetClient(ctx).Call(ctx, c.name, "BlessingStoreDebugString", nil, []interface{}{&o0}, opts...)
-	return
-}
-
-func (c implControllerClientStub) RemoteBlessings(ctx *context.T, i0 string, i1 string, opts ...rpc.CallOpt) (o0 []string, err error) {
-	err = v23.GetClient(ctx).Call(ctx, c.name, "RemoteBlessings", []interface{}{i0, i1}, []interface{}{&o0}, opts...)
-	return
-}
-
-func (c implControllerClientStub) Signature(ctx *context.T, i0 string, opts ...rpc.CallOpt) (o0 []signature.Interface, err error) {
-	err = v23.GetClient(ctx).Call(ctx, c.name, "Signature", []interface{}{i0}, []interface{}{&o0}, opts...)
-	return
-}
-
-func (c implControllerClientStub) UnionOfBlessings(ctx *context.T, i0 []principal.BlessingsHandle, opts ...rpc.CallOpt) (o0 *principal.JsBlessings, err error) {
-	err = v23.GetClient(ctx).Call(ctx, c.name, "UnionOfBlessings", []interface{}{i0}, []interface{}{&o0}, opts...)
-	return
-}
-
-func (c implControllerClientStub) UnionOfBlessings(ctx *context.T, i0 []principal.BlessingsHandle, opts ...rpc.CallOpt) (o0 *principal.JsBlessings, err error) {
-	err = v23.GetClient(ctx).Call(ctx, c.name, "UnionOfBlessings", []interface{}{i0}, []interface{}{&o0}, opts...)
-	return
-}
-
-// ControllerServerMethods is the interface a server writer
-// implements for Controller.
-type ControllerServerMethods interface {
-	// Serve instructs WSPR to start listening for calls on behalf
-	// of a javascript server.
-	Serve(ctx *context.T, call rpc.ServerCall, name string, serverId uint32, serverOpts []RpcServerOption) error
-	// Stop instructs WSPR to stop listening for calls for the
-	// given javascript server.
-	Stop(ctx *context.T, call rpc.ServerCall, serverId uint32) error
-	// AddName adds a published name to an existing server.
-	AddName(ctx *context.T, call rpc.ServerCall, serverId uint32, name string) error
-	// RemoveName removes a published name from an existing server.
-	RemoveName(ctx *context.T, call rpc.ServerCall, serverId uint32, name string) error
-	// UnlinkBlessings removes the given blessings from the blessings store.
-	UnlinkBlessings(ctx *context.T, call rpc.ServerCall, handle principal.BlessingsHandle) error
-	// BlessingsDebugString gets a string useful for debugging blessings.
-	BlessingsDebugString(ctx *context.T, call rpc.ServerCall, handle principal.BlessingsHandle) (string, error)
-	// Bless binds extensions of blessings held by this principal to
-	// another principal (represented by its public key).
-	Bless(ctx *context.T, call rpc.ServerCall, publicKey string, handle principal.BlessingsHandle, extension string, caveat []security.Caveat) (publicKeyOut string, handleOut principal.BlessingsHandle, err error)
-	// BlessSelf creates a blessing with the provided name for this principal.
-	BlessSelf(ctx *context.T, call rpc.ServerCall, name string, caveats []security.Caveat) (publicKeyOut string, handleOut principal.BlessingsHandle, err error)
-	// AddToRoots adds the provided blessing as a root.
-	AddToRoots(ctx *context.T, call rpc.ServerCall, handle principal.BlessingsHandle) error
-	// BlessingStoreSet puts the specified blessing in the blessing store under the provided pattern.
-	BlessingStoreSet(ctx *context.T, call rpc.ServerCall, blessingsHandle principal.BlessingsHandle, pattern security.BlessingPattern) (*principal.JsBlessings, error)
-	// BlessingStoreForPeer retrieves the blessings marked for the given peers.
-	BlessingStoreForPeer(ctx *context.T, call rpc.ServerCall, peerBlessings []string) (*principal.JsBlessings, error)
-	// BlessingStoreSetDefault sets the default blessings.
-	BlessingStoreSetDefault(ctx *context.T, call rpc.ServerCall, blessingsHandle principal.BlessingsHandle) error
-	// BlessingStoreDefault fetches the default blessings for the principal of the controller.
-	BlessingStoreDefault(*context.T, rpc.ServerCall) (*principal.JsBlessings, error)
-	// BlessingStorePublicKey fetches the public key of the principal for which this store hosts blessings.
-	BlessingStorePublicKey(*context.T, rpc.ServerCall) (string, error)
-	// BlessingStorePeerBlessings returns all the blessings that the BlessingStore holds.
-	BlessingStorePeerBlessings(*context.T, rpc.ServerCall) (map[security.BlessingPattern]*principal.JsBlessings, error)
-	// BlessingStoreDebugString retrieves a debug string describing the state of the blessing store
-	BlessingStoreDebugString(*context.T, rpc.ServerCall) (string, error)
-	// RemoteBlessings fetches the remote blessings for a given name and method.
-	RemoteBlessings(ctx *context.T, call rpc.ServerCall, name string, method string) ([]string, error)
-	// Signature fetches the signature for a given name.
-	Signature(ctx *context.T, call rpc.ServerCall, name string) ([]signature.Interface, error)
-<<<<<<< HEAD
-=======
-	// GetDefaultBlessings fetches the default blessings for the principal of the controller.
-	GetDefaultBlessings(*context.T, rpc.ServerCall) (*principal.JsBlessings, error)
->>>>>>> master
-	// UnionOfBlessings returns a Blessings object that carries the union of the provided blessings.
-	UnionOfBlessings(ctx *context.T, call rpc.ServerCall, toJoin []principal.BlessingsHandle) (*principal.JsBlessings, error)
-}
-
-// ControllerServerStubMethods is the server interface containing
-// Controller methods, as expected by rpc.Server.
-// There is no difference between this interface and ControllerServerMethods
-// since there are no streaming methods.
-type ControllerServerStubMethods ControllerServerMethods
-
-// ControllerServerStub adds universal methods to ControllerServerStubMethods.
-type ControllerServerStub interface {
-	ControllerServerStubMethods
-	// Describe the Controller interfaces.
-	Describe__() []rpc.InterfaceDesc
-}
-
-// ControllerServer returns a server stub for Controller.
-// It converts an implementation of ControllerServerMethods into
-// an object that may be used by rpc.Server.
-func ControllerServer(impl ControllerServerMethods) ControllerServerStub {
-	stub := implControllerServerStub{
-		impl: impl,
-	}
-	// Initialize GlobState; always check the stub itself first, to handle the
-	// case where the user has the Glob method defined in their VDL source.
-	if gs := rpc.NewGlobState(stub); gs != nil {
-		stub.gs = gs
-	} else if gs := rpc.NewGlobState(impl); gs != nil {
-		stub.gs = gs
-	}
-	return stub
-}
-
-type implControllerServerStub struct {
-	impl ControllerServerMethods
-	gs   *rpc.GlobState
-}
-
-func (s implControllerServerStub) Serve(ctx *context.T, call rpc.ServerCall, i0 string, i1 uint32, i2 []RpcServerOption) error {
-	return s.impl.Serve(ctx, call, i0, i1, i2)
-}
-
-func (s implControllerServerStub) Stop(ctx *context.T, call rpc.ServerCall, i0 uint32) error {
-	return s.impl.Stop(ctx, call, i0)
-}
-
-func (s implControllerServerStub) AddName(ctx *context.T, call rpc.ServerCall, i0 uint32, i1 string) error {
-	return s.impl.AddName(ctx, call, i0, i1)
-}
-
-func (s implControllerServerStub) RemoveName(ctx *context.T, call rpc.ServerCall, i0 uint32, i1 string) error {
-	return s.impl.RemoveName(ctx, call, i0, i1)
-}
-
-func (s implControllerServerStub) UnlinkBlessings(ctx *context.T, call rpc.ServerCall, i0 principal.BlessingsHandle) error {
-	return s.impl.UnlinkBlessings(ctx, call, i0)
-}
-
-func (s implControllerServerStub) BlessingsDebugString(ctx *context.T, call rpc.ServerCall, i0 principal.BlessingsHandle) (string, error) {
-	return s.impl.BlessingsDebugString(ctx, call, i0)
-}
-
-func (s implControllerServerStub) Bless(ctx *context.T, call rpc.ServerCall, i0 string, i1 principal.BlessingsHandle, i2 string, i3 []security.Caveat) (string, principal.BlessingsHandle, error) {
-	return s.impl.Bless(ctx, call, i0, i1, i2, i3)
-}
-
-func (s implControllerServerStub) BlessSelf(ctx *context.T, call rpc.ServerCall, i0 string, i1 []security.Caveat) (string, principal.BlessingsHandle, error) {
-	return s.impl.BlessSelf(ctx, call, i0, i1)
-}
-
-func (s implControllerServerStub) AddToRoots(ctx *context.T, call rpc.ServerCall, i0 principal.BlessingsHandle) error {
-	return s.impl.AddToRoots(ctx, call, i0)
-}
-
-func (s implControllerServerStub) BlessingStoreSet(ctx *context.T, call rpc.ServerCall, i0 principal.BlessingsHandle, i1 security.BlessingPattern) (*principal.JsBlessings, error) {
-	return s.impl.BlessingStoreSet(ctx, call, i0, i1)
-}
-
-func (s implControllerServerStub) BlessingStoreForPeer(ctx *context.T, call rpc.ServerCall, i0 []string) (*principal.JsBlessings, error) {
-	return s.impl.BlessingStoreForPeer(ctx, call, i0)
-}
-
-func (s implControllerServerStub) BlessingStoreSetDefault(ctx *context.T, call rpc.ServerCall, i0 principal.BlessingsHandle) error {
-	return s.impl.BlessingStoreSetDefault(ctx, call, i0)
-}
-
-func (s implControllerServerStub) BlessingStoreDefault(ctx *context.T, call rpc.ServerCall) (*principal.JsBlessings, error) {
-	return s.impl.BlessingStoreDefault(ctx, call)
-}
-
-func (s implControllerServerStub) BlessingStorePublicKey(ctx *context.T, call rpc.ServerCall) (string, error) {
-	return s.impl.BlessingStorePublicKey(ctx, call)
-}
-
-func (s implControllerServerStub) BlessingStorePeerBlessings(ctx *context.T, call rpc.ServerCall) (map[security.BlessingPattern]*principal.JsBlessings, error) {
-	return s.impl.BlessingStorePeerBlessings(ctx, call)
-}
-
-func (s implControllerServerStub) BlessingStoreDebugString(ctx *context.T, call rpc.ServerCall) (string, error) {
-	return s.impl.BlessingStoreDebugString(ctx, call)
-}
-
-func (s implControllerServerStub) RemoteBlessings(ctx *context.T, call rpc.ServerCall, i0 string, i1 string) ([]string, error) {
-	return s.impl.RemoteBlessings(ctx, call, i0, i1)
-}
-
-func (s implControllerServerStub) Signature(ctx *context.T, call rpc.ServerCall, i0 string) ([]signature.Interface, error) {
-	return s.impl.Signature(ctx, call, i0)
-}
-
-func (s implControllerServerStub) UnionOfBlessings(ctx *context.T, call rpc.ServerCall, i0 []principal.BlessingsHandle) (*principal.JsBlessings, error) {
-	return s.impl.UnionOfBlessings(ctx, call, i0)
-}
-
-func (s implControllerServerStub) UnionOfBlessings(ctx *context.T, call rpc.ServerCall, i0 []principal.BlessingsHandle) (*principal.JsBlessings, error) {
-	return s.impl.UnionOfBlessings(ctx, call, i0)
-}
-
-func (s implControllerServerStub) Globber() *rpc.GlobState {
-	return s.gs
-}
-
-func (s implControllerServerStub) Describe__() []rpc.InterfaceDesc {
-	return []rpc.InterfaceDesc{ControllerDesc}
-}
-
-// ControllerDesc describes the Controller interface.
-var ControllerDesc rpc.InterfaceDesc = descController
-
-// descController hides the desc to keep godoc clean.
-var descController = rpc.InterfaceDesc{
-	Name:    "Controller",
-	PkgPath: "v.io/x/ref/services/wspr/internal/app",
-	Methods: []rpc.MethodDesc{
-		{
-			Name: "Serve",
-			Doc:  "// Serve instructs WSPR to start listening for calls on behalf\n// of a javascript server.",
-			InArgs: []rpc.ArgDesc{
-				{"name", ``},       // string
-				{"serverId", ``},   // uint32
-				{"serverOpts", ``}, // []RpcServerOption
-			},
-		},
-		{
-			Name: "Stop",
-			Doc:  "// Stop instructs WSPR to stop listening for calls for the\n// given javascript server.",
-			InArgs: []rpc.ArgDesc{
-				{"serverId", ``}, // uint32
-			},
-		},
-		{
-			Name: "AddName",
-			Doc:  "// AddName adds a published name to an existing server.",
-			InArgs: []rpc.ArgDesc{
-				{"serverId", ``}, // uint32
-				{"name", ``},     // string
-			},
-		},
-		{
-			Name: "RemoveName",
-			Doc:  "// RemoveName removes a published name from an existing server.",
-			InArgs: []rpc.ArgDesc{
-				{"serverId", ``}, // uint32
-				{"name", ``},     // string
-			},
-		},
-		{
-			Name: "UnlinkBlessings",
-			Doc:  "// UnlinkBlessings removes the given blessings from the blessings store.",
-			InArgs: []rpc.ArgDesc{
-				{"handle", ``}, // principal.BlessingsHandle
-			},
-		},
-		{
-			Name: "BlessingsDebugString",
-			Doc:  "// BlessingsDebugString gets a string useful for debugging blessings.",
-			InArgs: []rpc.ArgDesc{
-				{"handle", ``}, // principal.BlessingsHandle
-			},
-			OutArgs: []rpc.ArgDesc{
-				{"", ``}, // string
-			},
-		},
-		{
-			Name: "Bless",
-			Doc:  "// Bless binds extensions of blessings held by this principal to\n// another principal (represented by its public key).",
-			InArgs: []rpc.ArgDesc{
-				{"publicKey", ``}, // string
-				{"handle", ``},    // principal.BlessingsHandle
-				{"extension", ``}, // string
-				{"caveat", ``},    // []security.Caveat
-			},
-			OutArgs: []rpc.ArgDesc{
-				{"publicKeyOut", ``}, // string
-				{"handleOut", ``},    // principal.BlessingsHandle
-			},
-		},
-		{
-			Name: "BlessSelf",
-			Doc:  "// BlessSelf creates a blessing with the provided name for this principal.",
-			InArgs: []rpc.ArgDesc{
-				{"name", ``},    // string
-				{"caveats", ``}, // []security.Caveat
-			},
-			OutArgs: []rpc.ArgDesc{
-				{"publicKeyOut", ``}, // string
-				{"handleOut", ``},    // principal.BlessingsHandle
-			},
-		},
-		{
-			Name: "AddToRoots",
-			Doc:  "// AddToRoots adds the provided blessing as a root.",
-			InArgs: []rpc.ArgDesc{
-				{"handle", ``}, // principal.BlessingsHandle
-			},
-		},
-		{
-			Name: "BlessingStoreSet",
-			Doc:  "// BlessingStoreSet puts the specified blessing in the blessing store under the provided pattern.",
-			InArgs: []rpc.ArgDesc{
-				{"blessingsHandle", ``}, // principal.BlessingsHandle
-				{"pattern", ``},         // security.BlessingPattern
-			},
-			OutArgs: []rpc.ArgDesc{
-				{"", ``}, // *principal.JsBlessings
-			},
-		},
-		{
-			Name: "BlessingStoreForPeer",
-			Doc:  "// BlessingStoreForPeer retrieves the blessings marked for the given peers.",
-			InArgs: []rpc.ArgDesc{
-				{"peerBlessings", ``}, // []string
-			},
-			OutArgs: []rpc.ArgDesc{
-				{"", ``}, // *principal.JsBlessings
-			},
-		},
-		{
-			Name: "BlessingStoreSetDefault",
-			Doc:  "// BlessingStoreSetDefault sets the default blessings.",
-			InArgs: []rpc.ArgDesc{
-				{"blessingsHandle", ``}, // principal.BlessingsHandle
-			},
-		},
-		{
-			Name: "BlessingStoreDefault",
-			Doc:  "// BlessingStoreDefault fetches the default blessings for the principal of the controller.",
-			OutArgs: []rpc.ArgDesc{
-				{"", ``}, // *principal.JsBlessings
-			},
-		},
-		{
-			Name: "BlessingStorePublicKey",
-			Doc:  "// BlessingStorePublicKey fetches the public key of the principal for which this store hosts blessings.",
-			OutArgs: []rpc.ArgDesc{
-				{"", ``}, // string
-			},
-		},
-		{
-			Name: "BlessingStorePeerBlessings",
-			Doc:  "// BlessingStorePeerBlessings returns all the blessings that the BlessingStore holds.",
-			OutArgs: []rpc.ArgDesc{
-				{"", ``}, // map[security.BlessingPattern]*principal.JsBlessings
-			},
-		},
-		{
-			Name: "BlessingStoreDebugString",
-			Doc:  "// BlessingStoreDebugString retrieves a debug string describing the state of the blessing store",
-			OutArgs: []rpc.ArgDesc{
-				{"", ``}, // string
-			},
-		},
-		{
-			Name: "RemoteBlessings",
-			Doc:  "// RemoteBlessings fetches the remote blessings for a given name and method.",
-			InArgs: []rpc.ArgDesc{
-				{"name", ``},   // string
-				{"method", ``}, // string
-			},
-			OutArgs: []rpc.ArgDesc{
-				{"", ``}, // []string
-			},
-		},
-		{
-			Name: "Signature",
-			Doc:  "// Signature fetches the signature for a given name.",
-			InArgs: []rpc.ArgDesc{
-				{"name", ``}, // string
-			},
-			OutArgs: []rpc.ArgDesc{
-				{"", ``}, // []signature.Interface
-			},
-		},
-		{
-			Name: "UnionOfBlessings",
-			Doc:  "// UnionOfBlessings returns a Blessings object that carries the union of the provided blessings.",
-			InArgs: []rpc.ArgDesc{
-				{"toJoin", ``}, // []principal.BlessingsHandle
-			},
-			OutArgs: []rpc.ArgDesc{
-				{"", ``}, // *principal.JsBlessings
-			},
-		},
-		{
-			Name: "UnionOfBlessings",
-			Doc:  "// UnionOfBlessings returns a Blessings object that carries the union of the provided blessings.",
-			InArgs: []rpc.ArgDesc{
-				{"toJoin", ``}, // []principal.BlessingsHandle
-			},
-			OutArgs: []rpc.ArgDesc{
-				{"", ``}, // *principal.JsBlessings
-			},
-		},
-	},
-}
diff --git a/services/wspr/internal/app/controller.vdl.orig b/services/wspr/internal/app/controller.vdl.orig
deleted file mode 100644
index 87cc159..0000000
--- a/services/wspr/internal/app/controller.vdl.orig
+++ /dev/null
@@ -1,67 +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.
-
-package app
-
-import (
-	"signature"
-
-	"v.io/v23/security"
-	"v.io/x/ref/services/wspr/internal/principal"
-)
-
-type Controller interface {
-	// Serve instructs WSPR to start listening for calls on behalf
-	// of a javascript server.
-	Serve(name string, serverId uint32, serverOpts []RpcServerOption) error
-	// Stop instructs WSPR to stop listening for calls for the
-	// given javascript server.
-	Stop(serverId uint32) error
-	// AddName adds a published name to an existing server.
-	AddName(serverId uint32, name string) error
-	// RemoveName removes a published name from an existing server.
-	RemoveName(serverId uint32, name string) error
-
-	// UnlinkBlessings removes the given blessings from the blessings store.
-	UnlinkBlessings(handle principal.BlessingsHandle) error
-	// BlessingsDebugString gets a string useful for debugging blessings.
-	BlessingsDebugString(handle principal.BlessingsHandle) (string | error)
-	// Bless binds extensions of blessings held by this principal to
-	// another principal (represented by its public key).
-	Bless(publicKey string, handle principal.BlessingsHandle, extension string, caveat []security.Caveat) (publicKeyOut string, handleOut principal.BlessingsHandle | error)
-	// BlessSelf creates a blessing with the provided name for this principal.
-	BlessSelf(name string, caveats []security.Caveat) (publicKeyOut string, handleOut principal.BlessingsHandle | error)
-	// AddToRoots adds the provided blessing as a root.
-	AddToRoots(handle principal.BlessingsHandle) error
-
-	// BlessingStoreSet puts the specified blessing in the blessing store under the provided pattern.
-	BlessingStoreSet(blessingsHandle principal.BlessingsHandle, pattern security.BlessingPattern) (?principal.JsBlessings | error)
-	// BlessingStoreForPeer retrieves the blessings marked for the given peers.
-	BlessingStoreForPeer(peerBlessings []string) (?principal.JsBlessings | error)
-	// BlessingStoreSetDefault sets the default blessings.
-	BlessingStoreSetDefault(blessingsHandle principal.BlessingsHandle) error
-	// BlessingStoreDefault fetches the default blessings for the principal of the controller.
-	BlessingStoreDefault() (?principal.JsBlessings | error)
-	// BlessingStorePublicKey fetches the public key of the principal for which this store hosts blessings.
-	BlessingStorePublicKey() (string | error)
-	// BlessingStorePeerBlessings returns all the blessings that the BlessingStore holds.
-	BlessingStorePeerBlessings() (map[security.BlessingPattern]?principal.JsBlessings | error)
-	// BlessingStoreDebugString retrieves a debug string describing the state of the blessing store
-	BlessingStoreDebugString() (string | error)
-
-
-	// RemoteBlessings fetches the remote blessings for a given name and method.
-	RemoteBlessings(name, method string) ([]string | error)
-	// Signature fetches the signature for a given name.
-	Signature(name string) ([]signature.Interface | error)
-
-<<<<<<< HEAD
-  // UnionOfBlessings returns a Blessings object that carries the union of the provided blessings.
-=======
-	// GetDefaultBlessings fetches the default blessings for the principal of the controller.
-	GetDefaultBlessings() (?principal.JsBlessings | error)
-	// UnionOfBlessings returns a Blessings object that carries the union of the provided blessings.
->>>>>>> master
-	UnionOfBlessings(toJoin []principal.BlessingsHandle) (?principal.JsBlessings | error)
-}
diff --git a/services/wspr/internal/app/mock_jsServer_test.go b/services/wspr/internal/app/mock_jsServer_test.go
index 663efea..27891d2 100644
--- a/services/wspr/internal/app/mock_jsServer_test.go
+++ b/services/wspr/internal/app/mock_jsServer_test.go
@@ -19,7 +19,6 @@
 	"v.io/v23/vom"
 	"v.io/x/ref/internal/reflectutil"
 	"v.io/x/ref/services/wspr/internal/lib"
-	"v.io/x/ref/services/wspr/internal/principal"
 	"v.io/x/ref/services/wspr/internal/rpc/server"
 )
 
@@ -71,7 +70,7 @@
 		}
 		m.receivedResponse = vdl.ValueOf(msg)
 		return nil
-	case lib.ResponseLog:
+	case lib.ResponseLog, lib.ResponseBlessingsCacheMessage:
 		m.flowCount += 2
 		return nil
 	case lib.ResponseTypeMessage:
@@ -142,11 +141,6 @@
 	return nil
 }
 
-// Returns false if the blessing is malformed
-func validateBlessing(blessings principal.JsBlessings) bool {
-	return blessings.Handle != 0 && blessings.PublicKey != ""
-}
-
 func validateEndpoint(ep string) bool {
 	return ep != ""
 }
@@ -184,13 +178,13 @@
 		return nil
 	}
 
-	// We expect localBlessings and remoteBlessings to be set and the publicKey be a string
-	if !validateBlessing(call.LocalBlessings) {
-		m.controller.HandleAuthResponse(m.flowCount, internalErr(fmt.Sprintf("bad localblessing:%v", call.LocalBlessings)))
+	// We expect localBlessings and remoteBlessings to be a non-zero id
+	if call.LocalBlessings == 0 {
+		m.controller.HandleAuthResponse(m.flowCount, internalErr(fmt.Sprintf("bad local blessing: %v", call.LocalBlessings)))
 		return nil
 	}
-	if !validateBlessing(call.RemoteBlessings) {
-		m.controller.HandleAuthResponse(m.flowCount, internalErr(fmt.Sprintf("bad remoteblessing:%v", call.RemoteBlessings)))
+	if call.RemoteBlessings == 0 {
+		m.controller.HandleAuthResponse(m.flowCount, internalErr(fmt.Sprintf("bad remote blessing: %v", call.RemoteBlessings)))
 		return nil
 	}
 
@@ -256,8 +250,13 @@
 		return nil
 	}
 
-	if !validateBlessing(call.RemoteBlessings) {
-		m.controller.HandleServerResponse(m.flowCount, internalErr(fmt.Sprintf("bad Remoteblessing:%v", call.RemoteBlessings)))
+	// We expect localBlessings and remoteBlessings to be a non-zero id
+	if call.LocalBlessings == 0 {
+		m.controller.HandleAuthResponse(m.flowCount, internalErr(fmt.Sprintf("bad local blessing: %v", call.LocalBlessings)))
+		return nil
+	}
+	if call.RemoteBlessings == 0 {
+		m.controller.HandleAuthResponse(m.flowCount, internalErr(fmt.Sprintf("bad remote blessing: %v", call.RemoteBlessings)))
 		return nil
 	}
 
diff --git a/services/wspr/internal/app/v23_internal_test.go b/services/wspr/internal/app/v23_internal_test.go
index 9ae5683..70d6982 100644
--- a/services/wspr/internal/app/v23_internal_test.go
+++ b/services/wspr/internal/app/v23_internal_test.go
@@ -4,12 +4,15 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package app
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
+	"v.io/x/ref/test"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
diff --git a/services/wspr/internal/browspr/browspr_account_test.go b/services/wspr/internal/browspr/browspr_account_test.go
index eccef39..ff35bbf 100644
--- a/services/wspr/internal/browspr/browspr_account_test.go
+++ b/services/wspr/internal/browspr/browspr_account_test.go
@@ -43,7 +43,7 @@
 }
 
 func setup(t *testing.T) (*Browspr, func()) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 
 	spec := v23.GetListenSpec(ctx)
 	spec.Proxy = "/mock/proxy"
diff --git a/services/wspr/internal/browspr/browspr_test.go b/services/wspr/internal/browspr/browspr_test.go
index 7b110f9..956fe54 100644
--- a/services/wspr/internal/browspr/browspr_test.go
+++ b/services/wspr/internal/browspr/browspr_test.go
@@ -99,7 +99,7 @@
 }
 
 func TestBrowspr(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	proxySpec := rpc.ListenSpec{
diff --git a/services/wspr/internal/browspr/v23_internal_test.go b/services/wspr/internal/browspr/v23_internal_test.go
index 9b21db0..6c94e49 100644
--- a/services/wspr/internal/browspr/v23_internal_test.go
+++ b/services/wspr/internal/browspr/v23_internal_test.go
@@ -4,12 +4,15 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package browspr
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
+	"v.io/x/ref/test"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
diff --git a/services/wspr/internal/lib/signature_manager_test.go b/services/wspr/internal/lib/signature_manager_test.go
index 94a0b43..7e50213 100644
--- a/services/wspr/internal/lib/signature_manager_test.go
+++ b/services/wspr/internal/lib/signature_manager_test.go
@@ -22,7 +22,7 @@
 )
 
 func initContext(t *testing.T) (*context.T, clientWithTimesCalled, v23.Shutdown) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	initialSig := []signature.Interface{
 		{
 			Methods: []signature.Method{
diff --git a/services/wspr/internal/lib/writer.go b/services/wspr/internal/lib/writer.go
index 2a9316c..1c90372 100644
--- a/services/wspr/internal/lib/writer.go
+++ b/services/wspr/internal/lib/writer.go
@@ -7,18 +7,19 @@
 type ResponseType int32
 
 const (
-	ResponseFinal            ResponseType = 0
-	ResponseStream                        = 1
-	ResponseError                         = 2
-	ResponseServerRequest                 = 3
-	ResponseStreamClose                   = 4
-	ResponseDispatcherLookup              = 5
-	ResponseAuthRequest                   = 6
-	ResponseCancel                        = 7
-	ResponseValidate                      = 8 // Request to validate caveats.
-	ResponseLog                           = 9 // Sends a message to be logged.
-	ResponseGranterRequest                = 10
-	ResponseTypeMessage                   = 12
+	ResponseFinal                 ResponseType = 0
+	ResponseStream                             = 1
+	ResponseError                              = 2
+	ResponseServerRequest                      = 3
+	ResponseStreamClose                        = 4
+	ResponseDispatcherLookup                   = 5
+	ResponseAuthRequest                        = 6
+	ResponseCancel                             = 7
+	ResponseValidate                           = 8 // Request to validate caveats.
+	ResponseLog                                = 9 // Sends a message to be logged.
+	ResponseGranterRequest                     = 10
+	ResponseBlessingsCacheMessage              = 11 // Update the blessings cache
+	ResponseTypeMessage                        = 12
 )
 
 type Response struct {
diff --git a/services/wspr/internal/principal/cache.go b/services/wspr/internal/principal/cache.go
new file mode 100644
index 0000000..f4be4d8
--- /dev/null
+++ b/services/wspr/internal/principal/cache.go
@@ -0,0 +1,144 @@
+// 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.
+
+package principal
+
+import (
+	"reflect"
+	"sync"
+	"time"
+)
+
+// BlessingsCacheEntry is an entry in the blessing cache.
+type BlessingsCacheEntry struct {
+	refCount  uint32       // Keep track of uses so JS knows when to clean up.
+	blessings *JsBlessings // For now. Switch to security.Blessings
+	dirty     bool         // True iff modified since last GC.
+}
+
+// BlessingsCache is a cache for blessings.
+// This cache is synced with a corresponding cache in Javascript with the goal
+// of reducing the number of times that blessings objects are sent in their
+// entirety.
+// BlessingsCache provides an id for a blessing after a Put() call that is
+// understood by Javascript.
+// After a new blessings cache is created, recently unused blessings will be
+// cleaned up periodically.
+type BlessingsCache struct {
+	lock       sync.Mutex
+	nextId     BlessingsId
+	entries    map[BlessingsId]*BlessingsCacheEntry
+	notifier   func([]BlessingsCacheMessage)
+	inactiveGc bool
+}
+
+func NewBlessingsCache(notifier func([]BlessingsCacheMessage), gcPolicy *BlessingsCacheGCPolicy) *BlessingsCache {
+	bc := &BlessingsCache{
+		entries:  map[BlessingsId]*BlessingsCacheEntry{},
+		notifier: notifier,
+	}
+	go bc.gcLoop(gcPolicy)
+	return bc
+}
+
+// Put a blessing in the blessing cache and get an id for it that will
+// be understood by the Javascript cache.
+// If the blessings object is already in the cache, a new entry won't be created
+// and the id for the blessings from a previous put will be returned.
+func (bc *BlessingsCache) Put(blessings *JsBlessings) BlessingsId {
+	if blessings == nil {
+		return 0
+	}
+
+	bc.lock.Lock()
+	defer bc.lock.Unlock()
+
+	for id, entry := range bc.entries {
+		if reflect.DeepEqual(blessings, entry.blessings) {
+			entry.refCount++
+			entry.dirty = true
+			return id
+		}
+	}
+
+	bc.nextId++
+	id := bc.nextId
+	bc.entries[id] = &BlessingsCacheEntry{
+		refCount:  1,
+		blessings: blessings,
+		dirty:     true,
+	}
+	addMessage := BlessingsCacheAddMessage{
+		CacheId:   id,
+		Blessings: *blessings,
+	}
+	bc.notifier([]BlessingsCacheMessage{
+		BlessingsCacheMessageAdd{
+			Value: addMessage,
+		},
+	})
+	return id
+}
+
+// BlessingsCacheGCPolicy specifies when GC will occur and provides a function
+// to pass notifications of deletions (used to notify Javascript to delete
+// the cache items).
+type BlessingsCacheGCPolicy struct {
+	nextTrigger func() <-chan time.Time
+}
+
+// PeriodicGcPolicy periodically performs gc at the specified interval.
+func PeriodicGcPolicy(interval time.Duration) *BlessingsCacheGCPolicy {
+	return &BlessingsCacheGCPolicy{
+		nextTrigger: func() <-chan time.Time { return time.After(interval) },
+	}
+}
+
+// Stop stops the GC loop and frees up resources.
+func (bc *BlessingsCache) Stop() {
+	bc.lock.Lock()
+	bc.inactiveGc = true
+	bc.lock.Unlock()
+}
+
+func (bc *BlessingsCache) gcLoop(policy *BlessingsCacheGCPolicy) {
+	for {
+		bc.lock.Lock()
+		if bc.inactiveGc {
+			return
+		}
+		bc.lock.Unlock()
+		<-policy.nextTrigger()
+		bc.gc(policy)
+	}
+}
+
+// Perform Garbage Collection.
+// All blessings that weren't referenced since the last GC will be removed from
+// the cache.
+func (bc *BlessingsCache) gc(policy *BlessingsCacheGCPolicy) {
+	bc.lock.Lock()
+	defer bc.lock.Unlock()
+
+	var toDelete []BlessingsCacheMessage
+	for id, entry := range bc.entries {
+		if !entry.dirty {
+			deleteEntry := BlessingsCacheDeleteMessage{
+				CacheId:     id,
+				DeleteAfter: entry.refCount,
+			}
+			toDelete = append(toDelete, BlessingsCacheMessageDelete{Value: deleteEntry})
+		} else {
+			entry.dirty = false
+		}
+	}
+
+	for _, deleteEntry := range toDelete {
+		delete(bc.entries, deleteEntry.(BlessingsCacheMessageDelete).Value.CacheId)
+	}
+
+	if len(toDelete) > 0 {
+		bc.notifier(toDelete)
+	}
+}
diff --git a/services/wspr/internal/principal/cache.vdl b/services/wspr/internal/principal/cache.vdl
new file mode 100644
index 0000000..ceabd3c
--- /dev/null
+++ b/services/wspr/internal/principal/cache.vdl
@@ -0,0 +1,28 @@
+// 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.
+
+package principal
+
+// Identifier of a blessings cache entry.
+type BlessingsId uint32
+
+type BlessingsCacheAddMessage struct {
+  CacheId BlessingsId
+  Blessings JsBlessings
+}
+
+// Message from Blessings Cache GC to delete a cache entry in Javascript.
+type BlessingsCacheDeleteMessage struct {
+  CacheId BlessingsId
+
+  // Number of references expected. Javascript should wait until this number
+  // has been received before deleting the entry because up until that point
+  // messages with further references are expected.
+  DeleteAfter uint32
+}
+
+type BlessingsCacheMessage union {
+  Add BlessingsCacheAddMessage
+  Delete BlessingsCacheDeleteMessage
+}
diff --git a/services/wspr/internal/principal/cache.vdl.go b/services/wspr/internal/principal/cache.vdl.go
new file mode 100644
index 0000000..efcd507
--- /dev/null
+++ b/services/wspr/internal/principal/cache.vdl.go
@@ -0,0 +1,89 @@
+// 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.
+
+// This file was auto-generated by the vanadium vdl tool.
+// Source: cache.vdl
+
+package principal
+
+import (
+	// VDL system imports
+	"v.io/v23/vdl"
+)
+
+// Identifier of a blessings cache entry.
+type BlessingsId uint32
+
+func (BlessingsId) __VDLReflect(struct {
+	Name string `vdl:"v.io/x/ref/services/wspr/internal/principal.BlessingsId"`
+}) {
+}
+
+type BlessingsCacheAddMessage struct {
+	CacheId   BlessingsId
+	Blessings JsBlessings
+}
+
+func (BlessingsCacheAddMessage) __VDLReflect(struct {
+	Name string `vdl:"v.io/x/ref/services/wspr/internal/principal.BlessingsCacheAddMessage"`
+}) {
+}
+
+// Message from Blessings Cache GC to delete a cache entry in Javascript.
+type BlessingsCacheDeleteMessage struct {
+	CacheId BlessingsId
+	// Number of references expected. Javascript should wait until this number
+	// has been received before deleting the entry because up until that point
+	// messages with further references are expected.
+	DeleteAfter uint32
+}
+
+func (BlessingsCacheDeleteMessage) __VDLReflect(struct {
+	Name string `vdl:"v.io/x/ref/services/wspr/internal/principal.BlessingsCacheDeleteMessage"`
+}) {
+}
+
+type (
+	// BlessingsCacheMessage represents any single field of the BlessingsCacheMessage union type.
+	BlessingsCacheMessage interface {
+		// Index returns the field index.
+		Index() int
+		// Interface returns the field value as an interface.
+		Interface() interface{}
+		// Name returns the field name.
+		Name() string
+		// __VDLReflect describes the BlessingsCacheMessage union type.
+		__VDLReflect(__BlessingsCacheMessageReflect)
+	}
+	// BlessingsCacheMessageAdd represents field Add of the BlessingsCacheMessage union type.
+	BlessingsCacheMessageAdd struct{ Value BlessingsCacheAddMessage }
+	// BlessingsCacheMessageDelete represents field Delete of the BlessingsCacheMessage union type.
+	BlessingsCacheMessageDelete struct{ Value BlessingsCacheDeleteMessage }
+	// __BlessingsCacheMessageReflect describes the BlessingsCacheMessage union type.
+	__BlessingsCacheMessageReflect struct {
+		Name  string `vdl:"v.io/x/ref/services/wspr/internal/principal.BlessingsCacheMessage"`
+		Type  BlessingsCacheMessage
+		Union struct {
+			Add    BlessingsCacheMessageAdd
+			Delete BlessingsCacheMessageDelete
+		}
+	}
+)
+
+func (x BlessingsCacheMessageAdd) Index() int                                  { return 0 }
+func (x BlessingsCacheMessageAdd) Interface() interface{}                      { return x.Value }
+func (x BlessingsCacheMessageAdd) Name() string                                { return "Add" }
+func (x BlessingsCacheMessageAdd) __VDLReflect(__BlessingsCacheMessageReflect) {}
+
+func (x BlessingsCacheMessageDelete) Index() int                                  { return 1 }
+func (x BlessingsCacheMessageDelete) Interface() interface{}                      { return x.Value }
+func (x BlessingsCacheMessageDelete) Name() string                                { return "Delete" }
+func (x BlessingsCacheMessageDelete) __VDLReflect(__BlessingsCacheMessageReflect) {}
+
+func init() {
+	vdl.Register((*BlessingsId)(nil))
+	vdl.Register((*BlessingsCacheAddMessage)(nil))
+	vdl.Register((*BlessingsCacheDeleteMessage)(nil))
+	vdl.Register((*BlessingsCacheMessage)(nil))
+}
diff --git a/services/wspr/internal/principal/cache_test.go b/services/wspr/internal/principal/cache_test.go
new file mode 100644
index 0000000..6aa222e
--- /dev/null
+++ b/services/wspr/internal/principal/cache_test.go
@@ -0,0 +1,236 @@
+// 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.
+
+package principal
+
+import (
+	"reflect"
+	"sync"
+	"testing"
+	"time"
+)
+
+// manualTrigger provides a gc trigger that can be signaled manually
+type manualTrigger struct {
+	gcShouldBeNext bool
+	lock           sync.Mutex
+	cond           *sync.Cond
+	gcHasRun       bool
+}
+
+func newManualTrigger() *manualTrigger {
+	mt := &manualTrigger{
+		gcShouldBeNext: true,
+	}
+	mt.cond = sync.NewCond(&mt.lock)
+	return mt
+}
+
+// policyTrigger is the trigger that should be provided in GC policy config.
+// It waits until it receives a signal and then returns a chan time.Time
+// that resolves immediately.
+func (mt *manualTrigger) waitForNextGc() <-chan time.Time {
+	mt.lock.Lock()
+	if !mt.gcHasRun {
+		mt.gcHasRun = true
+	} else {
+		mt.gcShouldBeNext = false // hand off control
+		mt.cond.Broadcast()
+		for !mt.gcShouldBeNext {
+			mt.cond.Wait()
+		}
+	}
+	mt.lock.Unlock()
+
+	trigger := make(chan time.Time, 1)
+	trigger <- time.Time{}
+	return trigger
+}
+
+// next should be called to trigger the next policy trigger event.
+func (mt *manualTrigger) next() {
+	mt.lock.Lock()
+	mt.gcShouldBeNext = true // hand off control
+	mt.cond.Broadcast()
+	for mt.gcShouldBeNext {
+		mt.cond.Wait()
+	}
+	mt.lock.Unlock()
+}
+
+// Test just to confirm it signals in order as expected.
+func TestManualTrigger(t *testing.T) {
+	mt := newManualTrigger()
+
+	countTriggers := 0
+	go func() {
+		for i := 1; i <= 100; i++ {
+			<-mt.waitForNextGc()
+			countTriggers++
+		}
+	}()
+
+	for i := 1; i <= 99; i++ {
+		mt.next()
+		if countTriggers != i {
+			t.Errorf("Expected %d triggers, got %d", i, countTriggers)
+		}
+	}
+}
+
+func TestBlessingsCache(t *testing.T) {
+	notificationCh := make(chan []BlessingsCacheMessage, 1)
+	notifier := func(msg []BlessingsCacheMessage) {
+		notificationCh <- msg
+	}
+
+	mt := newManualTrigger()
+
+	// Create a BlessingsCache with a GC policy that we can trigger on demand.
+	onDemandGCPolicy := &BlessingsCacheGCPolicy{
+		nextTrigger: mt.waitForNextGc,
+	}
+	bc := NewBlessingsCache(notifier, onDemandGCPolicy)
+
+	// Blessings for the tests.
+	jsBlessA := &JsBlessings{
+		Handle:    1,
+		PublicKey: "A",
+	}
+	jsBlessB := &JsBlessings{
+		Handle:    2,
+		PublicKey: "B",
+	}
+	jsBlessC := &JsBlessings{
+		Handle:    4,
+		PublicKey: "C",
+	}
+
+	// First do puts and make sure the ids are reasonable.
+	idA := bc.Put(jsBlessA)
+	expectAddMessage(t, notificationCh, 1, jsBlessA)
+	idB := bc.Put(jsBlessB)
+	expectAddMessage(t, notificationCh, 2, jsBlessB)
+	if idA == idB {
+		t.Errorf("A and B unexpectedly had same id: %v", idA)
+	}
+
+	idA2 := bc.Put(jsBlessA)
+	expectNoMessage(t, notificationCh)
+	if idA2 != idA {
+		t.Errorf("A and A2 expected to have same id, but they were %v and %v",
+			idA, idA2)
+	}
+
+	// Now perform GC. Check that the values are still in the cache.
+	mt.next()
+	expectNoMessage(t, notificationCh)
+
+	idGc1A := bc.Put(jsBlessA)
+	idGc1B := bc.Put(jsBlessB)
+	expectNoMessage(t, notificationCh)
+	if idA != idGc1A {
+		t.Errorf("Expected to get same id after one gc of A, but got %v and %v",
+			idA, idGc1A)
+	}
+	if idB != idGc1B {
+		t.Errorf("Expected to get same id after one gc of B, but got %v and %v",
+			idB, idGc1B)
+	}
+
+	// Now perform GC to clear the dirty bits.
+	mt.next()
+	expectNoMessage(t, notificationCh)
+
+	// Update B and add C.
+	idGc2B := bc.Put(jsBlessB)
+	expectNoMessage(t, notificationCh)
+	if idB != idGc2B {
+		t.Errorf("Expected to get same id after two gcs of B, but got %v and %v",
+			idB, idGc2B)
+	}
+	idC := bc.Put(jsBlessC)
+	expectAddMessage(t, notificationCh, 3, jsBlessC)
+	if idC == idA || idC == idB {
+		t.Error("C was unexpectedly the same as A or B")
+	}
+
+	// Perform GC. A should be removed but B and C should stay.
+	mt.next()
+	expectDeleteMessage(t, notificationCh, BlessingsCacheDeleteMessage{CacheId: 1, DeleteAfter: 3})
+	if idB != bc.Put(jsBlessB) {
+		t.Errorf("B seems to have been cleaned up as it was given a new id")
+	}
+	if idC != bc.Put(jsBlessC) {
+		t.Errorf("C seems to have been cleaned up as it was given a new id")
+	}
+	expectNoMessage(t, notificationCh)
+
+	// Perform GC twice to remove the other items.
+	mt.next()
+	expectNoMessage(t, notificationCh)
+	mt.next()
+	expectDeleteMessage(t, notificationCh,
+		BlessingsCacheDeleteMessage{CacheId: 2, DeleteAfter: 4},
+		BlessingsCacheDeleteMessage{CacheId: 3, DeleteAfter: 2})
+
+	// No notifications should occur on further GCs.
+	mt.next()
+	mt.next()
+	mt.next()
+	// Note that this should only be reached after the second GC is done because
+	// the GC trigger channel has size 1.
+	expectNoMessage(t, notificationCh)
+
+	bc.Stop()
+}
+
+func expectNoMessage(t *testing.T, notificationCh chan []BlessingsCacheMessage) {
+	select {
+	case <-notificationCh:
+		t.Errorf("Got message when none expected")
+	default:
+	}
+}
+
+func expectAddMessage(t *testing.T, notificationCh chan []BlessingsCacheMessage, id BlessingsId, bless *JsBlessings) {
+	select {
+	case notifications := <-notificationCh:
+		if len(notifications) != 1 {
+			t.Fatalf("Got invalid add message with %d messages", len(notifications))
+		}
+		addMsg := notifications[0].(BlessingsCacheMessageAdd).Value
+		if got, want := addMsg.CacheId, id; got != want {
+			t.Errorf("Unexpected id in add message: %v. Wanted: %v", got, want)
+		}
+		if got, want := addMsg.Blessings, *bless; !reflect.DeepEqual(got, want) {
+			t.Errorf("Blessings unexpectedly not equal. Got %v, want %v", got, want)
+		}
+	case <-time.After(10 * time.Second):
+		t.Fatalf("Timed out waiting for notification")
+	}
+}
+
+func expectDeleteMessage(t *testing.T, notificationCh chan []BlessingsCacheMessage, expected ...BlessingsCacheDeleteMessage) {
+	select {
+	case notifications := <-notificationCh:
+		if len(notifications) != len(expected) {
+			t.Fatalf("Got %d notifications but expected %d delete notifications", len(notifications), len(expected))
+		}
+		for _, notification := range notifications {
+			delNotif := notification.(BlessingsCacheMessageDelete).Value
+			var foundMatch bool
+			for _, expectedNotif := range expected {
+				if reflect.DeepEqual(delNotif, expectedNotif) {
+					foundMatch = true
+				}
+			}
+			if !foundMatch {
+				t.Errorf("Unexpected delete notification: %v", delNotif)
+			}
+		}
+	case <-time.After(10 * time.Second):
+		t.Fatalf("Timed out waiting for notification")
+	}
+}
diff --git a/services/wspr/internal/rpc/server/server.go b/services/wspr/internal/rpc/server/server.go
index c709731..e3cd66e 100644
--- a/services/wspr/internal/rpc/server/server.go
+++ b/services/wspr/internal/rpc/server/server.go
@@ -21,6 +21,7 @@
 	"v.io/v23/vdlroot/signature"
 	vdltime "v.io/v23/vdlroot/time"
 	"v.io/v23/verror"
+	"v.io/v23/vom"
 	"v.io/v23/vtrace"
 	"v.io/x/lib/vlog"
 	"v.io/x/ref/services/wspr/internal/lib"
@@ -49,6 +50,9 @@
 	HandleStore
 
 	SendLogMessage(level lib.LogLevel, msg string) error
+	BlessingsCache() *principal.BlessingsCache
+
+	TypeEncoder() *vom.TypeEncoder
 
 	Context() *context.T
 }
@@ -143,9 +147,10 @@
 			return replyChan
 		}
 
-		var grantedBlessings *principal.JsBlessings
+		var grantedBlessings principal.BlessingsId
 		if !call.GrantedBlessings().IsZero() {
-			grantedBlessings = convertBlessingsToHandle(s.helper, call.GrantedBlessings())
+			grantedBlessings = s.helper.BlessingsCache().Put(
+				convertBlessingsToHandle(s.helper, call.GrantedBlessings()))
 		}
 
 		rpcCall := ServerRpcRequestCall{
@@ -161,7 +166,8 @@
 		var vdlValArgs []*vdl.Value = make([]*vdl.Value, len(args))
 		for i, arg := range args {
 			if blessings, ok := arg.(security.Blessings); ok {
-				arg = principal.ConvertBlessingsToHandle(blessings, s.helper.GetOrAddBlessingsHandle(blessings))
+				jsBless := principal.ConvertBlessingsToHandle(blessings, s.helper.GetOrAddBlessingsHandle(blessings))
+				arg = s.helper.BlessingsCache().Put(jsBless)
 			}
 			vdlValArgs[i] = vdl.ValueOf(arg)
 		}
@@ -200,7 +206,7 @@
 			ch <- &lib.ServerRpcReply{nil, &err, vtrace.Response{}}
 		}()
 
-		go proxyStream(call, flow.Writer, s.helper)
+		go proxyStream(call, flow.Writer, s.helper, s.helper.TypeEncoder())
 
 		return replyChan
 	}
@@ -269,7 +275,7 @@
 		rpcCall := ServerRpcRequestCall{
 			SecurityCall:     securityCall,
 			Deadline:         timeout,
-			GrantedBlessings: grantedBlessings,
+			GrantedBlessings: s.helper.BlessingsCache().Put(grantedBlessings),
 			Context: Context{
 				Language: string(i18n.GetLangID(ctx)),
 			},
@@ -313,14 +319,15 @@
 	}
 }
 
-func proxyStream(stream rpc.Stream, w lib.ClientWriter, blessingsCache HandleStore) {
+func proxyStream(stream rpc.Stream, w lib.ClientWriter, blessingsCache HandleStore, typeEncoder *vom.TypeEncoder) {
 	var item interface{}
-	for err := stream.Recv(&item); err == nil; err = stream.Recv(&item) {
+	var err error
+	for err = stream.Recv(&item); err == nil; err = stream.Recv(&item) {
 		if blessings, ok := item.(security.Blessings); ok {
 			item = principal.ConvertBlessingsToHandle(blessings, blessingsCache.GetOrAddBlessingsHandle(blessings))
 
 		}
-		vomItem, err := lib.HexVomEncode(item, nil)
+		vomItem, err := lib.HexVomEncode(item, typeEncoder)
 		if err != nil {
 			w.Error(verror.Convert(verror.ErrInternal, nil, err))
 			return
@@ -330,6 +337,8 @@
 			return
 		}
 	}
+	vlog.Log.Errorf("Error reading from stream: %v\n", err)
+
 	if err := w.Send(lib.ResponseStreamClose, nil); err != nil {
 		w.Error(verror.Convert(verror.ErrInternal, nil, err))
 		return
@@ -508,14 +517,15 @@
 	for i, mtag := range call.MethodTags() {
 		anymtags[i] = mtag
 	}
+	remoteBlessings := *convertBlessingsToHandle(helper, call.RemoteBlessings())
 	secCall := SecurityCall{
 		Method:          lib.LowercaseFirstCharacter(call.Method()),
 		Suffix:          call.Suffix(),
 		MethodTags:      anymtags,
 		LocalEndpoint:   localEndpoint,
 		RemoteEndpoint:  remoteEndpoint,
-		LocalBlessings:  localBlessings,
-		RemoteBlessings: *convertBlessingsToHandle(helper, call.RemoteBlessings()),
+		LocalBlessings:  helper.BlessingsCache().Put(&localBlessings),
+		RemoteBlessings: helper.BlessingsCache().Put(&remoteBlessings),
 	}
 	if includeBlessingStrings {
 		secCall.LocalBlessingStrings = security.LocalBlessingNames(ctx, call)
@@ -768,6 +778,7 @@
 		s.dispatcher.Cleanup()
 	}
 
+	s.outstandingRequestLock.Lock()
 	for _, ch := range s.outstandingAuthRequests {
 		ch <- fmt.Errorf("Cleaning up server")
 	}
@@ -778,7 +789,6 @@
 		default:
 		}
 	}
-	s.outstandingRequestLock.Lock()
 	s.outstandingAuthRequests = make(map[int32]chan error)
 	s.outstandingServerRequests = make(map[int32]chan *lib.ServerRpcReply)
 	s.outstandingRequestLock.Unlock()
diff --git a/services/wspr/internal/rpc/server/server.vdl b/services/wspr/internal/rpc/server/server.vdl
index 790960c..0ab325e 100644
--- a/services/wspr/internal/rpc/server/server.vdl
+++ b/services/wspr/internal/rpc/server/server.vdl
@@ -21,9 +21,9 @@
     Method                string
     Suffix                string
     MethodTags            []any
-    LocalBlessings        principal.JsBlessings
+    LocalBlessings        principal.BlessingsId
     LocalBlessingStrings  []string
-    RemoteBlessings       principal.JsBlessings
+    RemoteBlessings       principal.BlessingsId
     RemoteBlessingStrings []string
     LocalEndpoint         string
     RemoteEndpoint        string
@@ -50,7 +50,7 @@
 	Deadline     time.WireDeadline
         Context      Context
 	TraceRequest vtrace.Request
-  GrantedBlessings ?principal.JsBlessings
+  GrantedBlessings principal.BlessingsId
 }
 
 // A request from the proxy to javascript to handle an RPC
diff --git a/services/wspr/internal/rpc/server/server.vdl.go b/services/wspr/internal/rpc/server/server.vdl.go
index 6a18704..76bbb0f 100644
--- a/services/wspr/internal/rpc/server/server.vdl.go
+++ b/services/wspr/internal/rpc/server/server.vdl.go
@@ -35,9 +35,9 @@
 	Method                string
 	Suffix                string
 	MethodTags            []*vdl.Value
-	LocalBlessings        principal.JsBlessings
+	LocalBlessings        principal.BlessingsId
 	LocalBlessingStrings  []string
-	RemoteBlessings       principal.JsBlessings
+	RemoteBlessings       principal.BlessingsId
 	RemoteBlessingStrings []string
 	LocalEndpoint         string
 	RemoteEndpoint        string
@@ -73,7 +73,7 @@
 	Deadline         time.Deadline
 	Context          Context
 	TraceRequest     vtrace.Request
-	GrantedBlessings *principal.JsBlessings
+	GrantedBlessings principal.BlessingsId
 }
 
 func (ServerRpcRequestCall) __VDLReflect(struct {
diff --git a/services/wspr/wsprlib/pipe.go b/services/wspr/wsprlib/pipe.go
index 2fa9aaf..c8fe142 100644
--- a/services/wspr/wsprlib/pipe.go
+++ b/services/wspr/wsprlib/pipe.go
@@ -9,7 +9,6 @@
 	"fmt"
 	"io"
 	"net/http"
-	_ "net/http/pprof"
 	"time"
 
 	"v.io/v23"
diff --git a/services/wspr/wsprlib/wspr.go b/services/wspr/wsprlib/wspr.go
index d30e5f4..0544575 100644
--- a/services/wspr/wsprlib/wspr.go
+++ b/services/wspr/wsprlib/wspr.go
@@ -14,7 +14,6 @@
 	"io"
 	"net"
 	"net/http"
-	_ "net/http/pprof"
 	"sync"
 	"time"
 
@@ -89,7 +88,6 @@
 // Starts serving http requests. This method is blocking.
 func (wspr *WSPR) Serve() {
 	// Configure HTTP routes.
-	http.HandleFunc("/debug", wspr.handleDebug)
 	http.HandleFunc("/ws", wspr.handleWS)
 	// Everything else is a 404.
 	// Note: the pattern "/" matches all paths not matched by other registered
@@ -146,24 +144,6 @@
 
 // HTTP Handlers
 
-func (wspr *WSPR) handleDebug(w http.ResponseWriter, r *http.Request) {
-	if r.Method != "GET" {
-		w.WriteHeader(http.StatusMethodNotAllowed)
-		fmt.Fprintf(w, "")
-		return
-	}
-	w.Header().Set("Content-Type", "text/html")
-	w.Write([]byte(`<html>
-<head>
-<title>/debug</title>
-</head>
-<body>
-<ul>
-<li><a href="/debug/pprof">/debug/pprof</a></li>
-</li></ul></body></html>
-`))
-}
-
 func (wspr *WSPR) handleWS(w http.ResponseWriter, r *http.Request) {
 	if r.Method != "GET" {
 		http.Error(w, "Method not allowed.", http.StatusMethodNotAllowed)
diff --git a/test/doc.go b/test/doc.go
index cb84f2b..206164e 100644
--- a/test/doc.go
+++ b/test/doc.go
@@ -14,11 +14,10 @@
 //     os.Exit(m.Run())
 // }
 //
-// InitForTest can be used within test functions as a safe alternative
-// to v23.Init.
+// V23Init can be used within test functions as a safe alternative to v23.Init.
 //
 // func TestFoo(t *testing.T) {
-//    ctx, shutdown := test.InitForTest()
+//    ctx, shutdown := test.V23Init()
 //    defer shutdown()
 //    ...
 // }
diff --git a/test/hello/v23_test.go b/test/hello/v23_test.go
index 515e1cd..6364cfb 100644
--- a/test/hello/v23_test.go
+++ b/test/hello/v23_test.go
@@ -4,16 +4,21 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package hello_test
 
-import "testing"
-import "os"
+import (
+	"os"
+	"testing"
 
-import "v.io/x/ref/test"
-import "v.io/x/ref/test/v23tests"
+	"v.io/x/ref/test"
+	"v.io/x/ref/test/modules"
+	"v.io/x/ref/test/v23tests"
+)
 
 func TestMain(m *testing.M) {
 	test.Init()
+	modules.DispatchAndExitIfChild()
 	cleanup := v23tests.UseSharedBinDir()
 	r := m.Run()
 	cleanup()
diff --git a/test/init.go b/test/init.go
index f01b1df..395cab7 100644
--- a/test/init.go
+++ b/test/init.go
@@ -63,11 +63,11 @@
 	once.Do(init)
 }
 
-// InitForTest initializes a new context.T and sets a freshly created principal
+// V23Init initializes a new context.T and sets a freshly created principal
 // (with a single self-signed blessing) on it. The principal setting step is
 // skipped if this function is invoked from a process run using the modules
 // package.
-func InitForTest() (*context.T, v23.Shutdown) {
+func V23Init() (*context.T, v23.Shutdown) {
 	ctx, shutdown := v23.Init()
 	if len(os.Getenv("V23_SHELL_HELPER_PROCESS_ENTRY_POINT")) != 0 {
 		return ctx, shutdown
diff --git a/test/modules/examples_test.go b/test/modules/examples_test.go
index 8b3113b..18d05a4 100644
--- a/test/modules/examples_test.go
+++ b/test/modules/examples_test.go
@@ -6,52 +6,48 @@
 
 import (
 	"fmt"
-	"io"
 	"os"
 
 	"v.io/x/ref/test"
 	"v.io/x/ref/test/modules"
 )
 
-func init() {
-	modules.RegisterChild("echo", "<args>...", echo)
-}
-
-func echo(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+var Echo = modules.Register(func(env *modules.Env, args ...string) error {
 	for i, a := range args {
-		fmt.Fprintf(stdout, "%d: %s\n", i, a)
+		fmt.Fprintf(env.Stdout, "%d: %s\n", i, a)
 	}
 	return nil
-}
+}, "echo")
 
 func ExampleDispatch() {
-	ctx, shutdown := test.InitForTest()
-	defer shutdown()
-	if modules.IsModulesChildProcess() {
-		// Child process. Dispatch will invoke the 'echo' command
+	if modules.IsChildProcess() {
+		// Child process dispatches to the echo program.
 		if err := modules.Dispatch(); err != nil {
-			panic(fmt.Sprintf("unexpected error: %s", err))
+			panic(err)
 		}
 		return
 	}
-	// Parent process.
+	// Parent process spawns the echo program.
+	ctx, shutdown := test.V23Init()
+	defer shutdown()
 	sh, _ := modules.NewShell(ctx, nil, false, nil)
 	defer sh.Cleanup(nil, nil)
-	h, _ := sh.Start("echo", nil, "a", "b")
+	h, _ := sh.Start(nil, Echo, "a", "b")
 	h.Shutdown(os.Stdout, os.Stderr)
 	// Output:
 	// 0: a
 	// 1: b
 }
 
-func ExampleDispatchAndExit() {
-	ctx, shutdown := test.InitForTest()
+func ExampleDispatchAndExitIfChild() {
+	// Child process dispatches to the echo program.
+	modules.DispatchAndExitIfChild()
+	// Parent process spawns the echo program.
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
-	// DispatchAndExit will call os.Exit(0) when executed within the child.
-	modules.DispatchAndExit()
 	sh, _ := modules.NewShell(ctx, nil, false, nil)
 	defer sh.Cleanup(nil, nil)
-	h, _ := sh.Start("echo", nil, "c", "d")
+	h, _ := sh.Start(nil, Echo, "c", "d")
 	h.Shutdown(os.Stdout, os.Stderr)
 	// Output:
 	// 0: c
diff --git a/test/modules/exec.go b/test/modules/exec.go
index 63e0b94..c85a2c9 100644
--- a/test/modules/exec.go
+++ b/test/modules/exec.go
@@ -21,20 +21,21 @@
 	"v.io/x/ref/test/expect"
 )
 
-// execHandle implements both the command and Handle interfaces.
+// execHandle implements both the Handle interface.
 type execHandle struct {
 	*expect.Session
-	mu        sync.Mutex
-	cmd       *exec.Cmd
-	name      string
-	handle    *vexec.ParentHandle
-	sh        *Shell
-	stderr    *os.File
-	stdout    io.ReadCloser
-	stdin     io.WriteCloser
-	procErrCh chan error
-	opts      *StartOpts
-	external  bool
+	mu         sync.Mutex
+	cmd        *exec.Cmd
+	entryPoint string
+	desc       string
+	handle     *vexec.ParentHandle
+	sh         *Shell
+	stderr     *os.File
+	stdout     io.ReadCloser
+	stdin      io.WriteCloser
+	procErrCh  chan error
+	opts       *StartOpts
+	external   bool
 }
 
 func testFlags() []string {
@@ -51,7 +52,7 @@
 	// must be a go test binary
 	val := timeout.Value.(flag.Getter).Get().(time.Duration)
 	if val.String() != timeout.DefValue {
-		// use supplied command value for subprocesses
+		// use supplied value for subprocesses
 		fl = append(fl, "--test.timeout="+timeout.Value.String())
 	} else {
 		// translate default value into 3m for subproccesses.  The
@@ -62,12 +63,12 @@
 	return fl
 }
 
-func newExecHandle(name string) *execHandle {
-	return &execHandle{name: name, procErrCh: make(chan error, 1)}
+func newExecHandle(entry, desc string) *execHandle {
+	return &execHandle{entryPoint: entry, desc: desc, procErrCh: make(chan error, 1)}
 }
 
-func newExecHandleForExternalCommand(name string) *execHandle {
-	return &execHandle{name: name, procErrCh: make(chan error, 1), external: true}
+func newExecHandleExternal(prog string) *execHandle {
+	return &execHandle{entryPoint: prog, desc: prog, procErrCh: make(chan error, 1), external: true}
 }
 
 func (eh *execHandle) Stdout() io.Reader {
@@ -96,7 +97,7 @@
 
 func (eh *execHandle) envelope(sh *Shell, env []string, args []string) ([]string, []string) {
 	if eh.external {
-		newargs := append([]string{eh.name}, args...)
+		newargs := append([]string{eh.entryPoint}, args...)
 		newenv := envvar.SliceToMap(env)
 		delete(newenv, shellEntryPoint)
 		return newargs, envvar.MapToSlice(newenv)
@@ -104,7 +105,7 @@
 	newargs := append([]string{os.Args[0]}, testFlags()...)
 	newargs = append(newargs, args...)
 	newenv := envvar.SliceToMap(env)
-	newenv[shellEntryPoint] = eh.name
+	newenv[shellEntryPoint] = eh.entryPoint
 	return newargs, envvar.MapToSlice(newenv)
 }
 
@@ -117,7 +118,7 @@
 	cmd := exec.Command(args[0], args[1:]...)
 	cmd.Env = env
 
-	stderr, err := newLogfile("stderr", eh.name)
+	stderr, err := newLogfile("stderr", eh.entryPoint)
 	if err != nil {
 		return nil, err
 	}
@@ -163,7 +164,7 @@
 		execOpts = append(execOpts, vexec.ConfigOpt{Config: config})
 	}
 
-	// TODO(cnicolaou): for external commands, vexec should either not be
+	// TODO(cnicolaou): for external programs, vexec should either not be
 	// used or it should taken an option to not use its protocol, and in
 	// particular to share secrets with children.
 	handle := vexec.NewParentHandle(cmd, execOpts...)
@@ -171,9 +172,9 @@
 	eh.stderr = stderr
 	eh.handle = handle
 	eh.cmd = cmd
-	vlog.VI(1).Infof("Start: %q stderr: %s", eh.name, stderr.Name())
-	vlog.VI(1).Infof("Start: %q args: %v", eh.name, cmd.Args)
-	vlog.VI(2).Infof("Start: %q env: %v", eh.name, cmd.Env)
+	vlog.VI(1).Infof("Start: %q stderr: %s", eh.desc, stderr.Name())
+	vlog.VI(1).Infof("Start: %q args: %v", eh.desc, cmd.Args)
+	vlog.VI(2).Infof("Start: %q env: %v", eh.desc, cmd.Env)
 	if err := handle.Start(); err != nil {
 		// The child process failed to start, either because of some setup
 		// error (e.g. creating pipes for it to use), or a bad binary etc.
@@ -194,7 +195,7 @@
 			return eh, err
 		}
 	}
-	vlog.VI(1).Infof("Started: %q, pid %d", eh.name, cmd.Process.Pid)
+	vlog.VI(1).Infof("Started: %q, pid %d", eh.desc, cmd.Process.Pid)
 	go func() {
 		eh.procErrCh <- eh.handle.Wait(0)
 		// It's now safe to close eh.stdout, since Wait only returns
@@ -215,8 +216,8 @@
 func (eh *execHandle) Shutdown(stdout, stderr io.Writer) error {
 	eh.mu.Lock()
 	defer eh.mu.Unlock()
-	vlog.VI(1).Infof("Shutdown: %q", eh.name)
-	defer vlog.VI(1).Infof("Shutdown: %q [DONE]", eh.name)
+	vlog.VI(1).Infof("Shutdown: %q", eh.desc)
+	defer vlog.VI(1).Infof("Shutdown: %q [DONE]", eh.desc)
 	if eh.stdin != nil {
 		eh.stdin.Close()
 	}
diff --git a/test/modules/modules_test.go b/test/modules/modules_test.go
index b0ce9e4..117b4e4 100644
--- a/test/modules/modules_test.go
+++ b/test/modules/modules_test.go
@@ -32,98 +32,85 @@
 	_ "v.io/x/ref/runtime/factories/generic"
 )
 
-func init() {
-	modules.RegisterChild("envtest", "envtest: <variables to print>...", PrintFromEnv)
-	modules.RegisterChild("printenv", "printenv", PrintEnv)
-	modules.RegisterChild("printblessing", "printblessing", PrintBlessing)
-	modules.RegisterChild("echos", "[args]*", Echo)
-	modules.RegisterChild("errortestChild", "", ErrorMain)
-	modules.RegisterChild("ignores_stdin", "", ignoresStdin)
-	modules.RegisterChild("pipeEcho", "", pipeEcho)
-	modules.RegisterChild("lifo", "", lifo)
-}
-
-// We must call Testmain ourselves because using v23 test generate
+// We must call TestMain ourselves because using v23 test generate
 // creates an import cycle for this package.
 func TestMain(m *testing.M) {
 	test.Init()
-	if modules.IsModulesChildProcess() {
-		if err := modules.Dispatch(); err != nil {
-			fmt.Fprintf(os.Stderr, "modules.Dispatch failed: %v\n", err)
-			os.Exit(1)
-		}
-		return
-	}
+	modules.DispatchAndExitIfChild()
 	os.Exit(m.Run())
 }
 
-func ignoresStdin(io.Reader, io.Writer, io.Writer, map[string]string, ...string) error {
+var ignoreStdin = modules.Register(func(*modules.Env, ...string) error {
 	<-time.After(time.Minute)
 	return nil
-}
+}, "ignoreStdin")
 
-func Echo(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+var echo = modules.Register(func(env *modules.Env, args ...string) error {
 	for _, a := range args {
-		fmt.Fprintf(stdout, "stdout: %s\n", a)
-		fmt.Fprintf(stderr, "stderr: %s\n", a)
+		fmt.Fprintf(env.Stdout, "stdout: %s\n", a)
+		fmt.Fprintf(env.Stderr, "stderr: %s\n", a)
 	}
 	return nil
-}
+}, "Echo")
 
-func pipeEcho(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
-	scanner := bufio.NewScanner(stdin)
+var pipeEcho = modules.Register(pipeEchoFunc, "pipeEcho")
+
+func pipeEchoFunc(env *modules.Env, args ...string) error {
+	scanner := bufio.NewScanner(env.Stdin)
 	for scanner.Scan() {
-		fmt.Fprintf(stdout, "%p: %s\n", pipeEcho, scanner.Text())
+		fmt.Fprintf(env.Stdout, "%p: %s\n", pipeEchoFunc, scanner.Text())
 	}
 	return nil
 }
 
-func lifo(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
-	scanner := bufio.NewScanner(stdin)
+var lifo = modules.Register(lifoFunc, "lifo")
+
+func lifoFunc(env *modules.Env, args ...string) error {
+	scanner := bufio.NewScanner(env.Stdin)
 	scanner.Scan()
 	msg := scanner.Text()
-	modules.WaitForEOF(stdin)
-	fmt.Fprintf(stdout, "%p: %s\n", lifo, msg)
+	modules.WaitForEOF(env.Stdin)
+	fmt.Fprintf(env.Stdout, "%p: %s\n", lifoFunc, msg)
 	return nil
 }
 
-func PrintBlessing(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
-	ctx, shutdown := test.InitForTest()
+var printBlessing = modules.Register(func(env *modules.Env, args ...string) error {
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	blessing := v23.GetPrincipal(ctx).BlessingStore().Default()
-	fmt.Fprintf(stdout, "%s", blessing)
+	fmt.Fprintf(env.Stdout, "%s", blessing)
 	return nil
-}
+}, "printBlessing")
 
-func PrintFromEnv(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+var envTest = modules.Register(func(env *modules.Env, args ...string) error {
 	for _, a := range args {
-		if v := env[a]; len(v) > 0 {
-			fmt.Fprintf(stdout, "%s\n", a+"="+v)
+		if v := env.Vars[a]; len(v) > 0 {
+			fmt.Fprintf(env.Stdout, "%s\n", a+"="+v)
 		} else {
-			fmt.Fprintf(stderr, "missing %s\n", a)
+			fmt.Fprintf(env.Stderr, "missing %s\n", a)
 		}
 	}
-	modules.WaitForEOF(stdin)
-	fmt.Fprintf(stdout, "done\n")
+	modules.WaitForEOF(env.Stdin)
+	fmt.Fprintf(env.Stdout, "done\n")
 	return nil
-}
+}, "envTest")
 
 const printEnvArgPrefix = "PRINTENV_ARG="
 
-func PrintEnv(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+var printEnv = modules.Register(func(env *modules.Env, args ...string) error {
 	for _, a := range args {
-		fmt.Fprintf(stdout, "%s%s\n", printEnvArgPrefix, a)
+		fmt.Fprintf(env.Stdout, "%s%s\n", printEnvArgPrefix, a)
 	}
-	for k, v := range env {
-		fmt.Fprintf(stdout, "%q\n", k+"="+v)
+	for k, v := range env.Vars {
+		fmt.Fprintf(env.Stdout, "%q\n", k+"="+v)
 	}
 	return nil
-}
+}, "printEnv")
 
-func ErrorMain(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+var errorMain = modules.Register(func(env *modules.Env, args ...string) error {
 	return fmt.Errorf("an error")
-}
+}, "errorMain")
 
 func waitForInput(scanner *bufio.Scanner) bool {
 	ch := make(chan struct{})
@@ -139,8 +126,8 @@
 	}
 }
 
-func testCommand(t *testing.T, sh *modules.Shell, name, key, val string) {
-	h, err := sh.Start(name, nil, key)
+func testProgram(t *testing.T, sh *modules.Shell, prog modules.Program, key, val string) {
+	h, err := sh.Start(nil, prog, key)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -180,7 +167,7 @@
 }
 
 func getBlessing(t *testing.T, sh *modules.Shell, env ...string) string {
-	h, err := sh.Start("printblessing", env)
+	h, err := sh.Start(env, printBlessing)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -193,7 +180,7 @@
 }
 
 func getCustomBlessing(t *testing.T, sh *modules.Shell, creds *modules.CustomCredentials) string {
-	h, err := sh.StartWithOpts(sh.DefaultStartOpts().WithCustomCredentials(creds), nil, "printblessing")
+	h, err := sh.StartWithOpts(sh.DefaultStartOpts().WithCustomCredentials(creds), nil, printBlessing)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -206,7 +193,7 @@
 }
 
 func TestChild(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	sh, err := modules.NewShell(ctx, nil, testing.Verbose(), t)
@@ -216,11 +203,11 @@
 	defer sh.Cleanup(nil, nil)
 	key, val := "simpleVar", "foo & bar"
 	sh.SetVar(key, val)
-	testCommand(t, sh, "envtest", key, val)
+	testProgram(t, sh, envTest, key, val)
 }
 
 func TestAgent(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	sh, err := modules.NewShell(ctx, nil, testing.Verbose(), t)
@@ -236,7 +223,7 @@
 }
 
 func TestCustomPrincipal(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	p, err := vsecurity.NewPrincipal()
@@ -266,7 +253,7 @@
 }
 
 func TestCustomCredentials(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	root := testutil.NewIDProvider("myshell")
@@ -357,7 +344,7 @@
 }
 
 func TestChildNoRegistration(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	sh, err := modules.NewShell(ctx, nil, testing.Verbose(), t)
@@ -367,8 +354,8 @@
 	defer sh.Cleanup(os.Stderr, os.Stderr)
 	key, val := "simpleVar", "foo & bar"
 	sh.SetVar(key, val)
-	testCommand(t, sh, "envtest", key, val)
-	_, err = sh.Start("non-existent-command", nil, "random", "args")
+	testProgram(t, sh, envTest, key, val)
+	_, err = sh.Start(nil, modules.Program("non-existent-program"), "random", "args")
 	if err == nil {
 		fmt.Fprintf(os.Stderr, "Failed: %v\n", err)
 		t.Fatalf("expected error")
@@ -376,7 +363,7 @@
 }
 
 func TestErrorChild(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	sh, err := modules.NewShell(ctx, nil, testing.Verbose(), t)
@@ -384,7 +371,7 @@
 		t.Fatalf("unexpected error: %s", err)
 	}
 	defer sh.Cleanup(nil, nil)
-	h, err := sh.Start("errortestChild", nil)
+	h, err := sh.Start(nil, errorMain)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -393,10 +380,10 @@
 	}
 }
 
-func testShutdown(t *testing.T, sh *modules.Shell, command string, isfunc bool) {
+func testShutdown(t *testing.T, sh *modules.Shell, prog modules.Program) {
 	result := ""
 	args := []string{"a", "b c", "ddd"}
-	if _, err := sh.Start(command, nil, args...); err != nil {
+	if _, err := sh.Start(nil, prog, args...); err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
 	var stdoutBuf bytes.Buffer
@@ -410,16 +397,14 @@
 	if got, want := stdoutBuf.String(), stdoutOutput+result; got != want {
 		t.Errorf("got %q want %q", got, want)
 	}
-	if !isfunc {
-		stderrBuf.ReadString('\n') // Skip past the random # generator output
-	}
+	stderrBuf.ReadString('\n') // Skip past the random # generator output
 	if got, want := stderrBuf.String(), stderrOutput; got != want {
 		t.Errorf("got %q want %q", got, want)
 	}
 }
 
 func TestShutdownSubprocess(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	sh, err := modules.NewShell(ctx, nil, false, t)
@@ -427,14 +412,14 @@
 		t.Fatalf("unexpected error: %s", err)
 	}
 	defer sh.Cleanup(nil, nil)
-	testShutdown(t, sh, "echos", false)
+	testShutdown(t, sh, echo)
 }
 
-// TestShutdownSubprocessIgnoresStdin verifies that Shutdown doesn't wait
+// TestShutdownSubprocessIgnoreStdin verifies that Shutdown doesn't wait
 // forever if a child does not die upon closing stdin; but instead times out and
 // returns an appropriate error.
-func TestShutdownSubprocessIgnoresStdin(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+func TestShutdownSubprocessIgnoreStdin(t *testing.T) {
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	sh, err := modules.NewShell(ctx, nil, false, t)
@@ -443,7 +428,7 @@
 	}
 	opts := sh.DefaultStartOpts()
 	opts.ShutdownTimeout = time.Second
-	h, err := sh.StartWithOpts(opts, nil, "ignores_stdin")
+	h, err := sh.StartWithOpts(opts, nil, ignoreStdin)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -463,7 +448,7 @@
 // implementation inappropriately sets stdout to the file that is to be closed
 // in Wait.
 func TestStdoutRace(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	sh, err := modules.NewShell(ctx, nil, testing.Verbose(), t)
@@ -472,7 +457,7 @@
 	}
 	opts := sh.DefaultStartOpts()
 	opts.ShutdownTimeout = time.Second
-	h, err := sh.StartWithOpts(opts, nil, "ignores_stdin")
+	h, err := sh.StartWithOpts(opts, nil, ignoreStdin)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -508,7 +493,7 @@
 }
 
 func TestEnvelope(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	sh, err := modules.NewShell(ctx, nil, testing.Verbose(), t)
@@ -519,7 +504,7 @@
 	sh.SetVar("a", "1")
 	sh.SetVar("b", "2")
 	args := []string{"oh", "ah"}
-	h, err := sh.Start("printenv", nil, args...)
+	h, err := sh.Start(nil, printEnv, args...)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -533,7 +518,7 @@
 			childEnv = append(childEnv, o)
 		}
 	}
-	shArgs, shEnv := sh.CommandEnvelope("printenv", nil, args...)
+	shArgs, shEnv := sh.ProgramEnvelope(nil, printEnv, args...)
 	for i, ev := range shEnv {
 		shEnv[i] = fmt.Sprintf("%q", ev)
 	}
@@ -563,7 +548,7 @@
 }
 
 func TestEnvMerge(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	sh, err := modules.NewShell(ctx, nil, testing.Verbose(), t)
@@ -575,7 +560,7 @@
 	os.Setenv("a", "wrong, should be 1")
 	sh.SetVar("b", "2 also wrong")
 	os.Setenv("b", "wrong, should be 2")
-	h, err := sh.Start("printenv", []string{"b=2"})
+	h, err := sh.Start([]string{"b=2"}, printEnv)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -596,7 +581,7 @@
 }
 
 func TestNoExec(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	sh, err := modules.NewShell(ctx, nil, testing.Verbose(), t)
@@ -604,7 +589,7 @@
 		t.Fatalf("unexpected error: %s", err)
 	}
 	defer sh.Cleanup(nil, nil)
-	h, err := sh.StartWithOpts(sh.DefaultStartOpts().NoExecCommand(), nil, "/bin/echo", "hello", "world")
+	h, err := sh.StartWithOpts(sh.DefaultStartOpts().NoExecProgram(), nil, modules.Program("/bin/echo"), "hello", "world")
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -616,7 +601,7 @@
 }
 
 func TestExternal(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	sh, err := modules.NewShell(ctx, nil, testing.Verbose(), t)
@@ -626,7 +611,7 @@
 	defer sh.Cleanup(nil, nil)
 	cookie := strconv.Itoa(rand.Int())
 	sh.SetConfigKey("cookie", cookie)
-	h, err := sh.StartWithOpts(sh.DefaultStartOpts().ExternalCommand(), nil, os.Args[0], "--test.run=TestExternalTestHelper")
+	h, err := sh.StartWithOpts(sh.DefaultStartOpts().ExternalProgram(), nil, modules.Program(os.Args[0]), "--test.run=TestExternalTestHelper")
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -654,7 +639,7 @@
 }
 
 func TestPipe(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
 	sh, err := modules.NewShell(ctx, nil, testing.Verbose(), t)
@@ -669,7 +654,7 @@
 	}
 	opts := sh.DefaultStartOpts()
 	opts.Stdin = r
-	h, err := sh.StartWithOpts(opts, nil, "pipeEcho")
+	h, err := sh.StartWithOpts(opts, nil, pipeEcho)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -682,8 +667,8 @@
 
 	scanner := bufio.NewScanner(h.Stdout())
 	want := []string{
-		fmt.Sprintf("%p: hello world", pipeEcho),
-		fmt.Sprintf("%p: %s", pipeEcho, cookie),
+		fmt.Sprintf("%p: hello world", pipeEchoFunc),
+		fmt.Sprintf("%p: %s", pipeEchoFunc, cookie),
 	}
 	i := 0
 	for scanner.Scan() {
@@ -702,7 +687,7 @@
 }
 
 func TestLIFO(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 	sh, err := modules.NewShell(ctx, nil, testing.Verbose(), t)
 	if err != nil {
@@ -712,7 +697,7 @@
 
 	cases := []string{"a", "b", "c"}
 	for _, msg := range cases {
-		h, err := sh.Start("lifo", nil)
+		h, err := sh.Start(nil, lifo)
 		if err != nil {
 			t.Fatal(err)
 		}
@@ -728,7 +713,7 @@
 	}
 	sort.Sort(sort.Reverse(sort.StringSlice(cases)))
 	for i, msg := range cases {
-		if got, want := lines[i], fmt.Sprintf("%p: %s", lifo, msg); got != want {
+		if got, want := lines[i], fmt.Sprintf("%p: %s", lifoFunc, msg); got != want {
 			t.Fatalf("got %v, want %v", got, want)
 		}
 	}
@@ -782,20 +767,20 @@
 }
 
 func TestCredentialsAndNoExec(t *testing.T) {
-	ctx, shutdown := test.InitForTest()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 	sh, err := modules.NewShell(ctx, nil, testing.Verbose(), t)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
 	opts := sh.DefaultStartOpts()
-	opts = opts.NoExecCommand()
+	opts = opts.NoExecProgram()
 	creds, err := sh.NewCustomCredentials()
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
 	opts = opts.WithCustomCredentials(creds)
-	h, err := sh.StartWithOpts(opts, nil, "echos", "a")
+	h, err := sh.StartWithOpts(opts, nil, echo, "a")
 
 	if got, want := err, modules.ErrNoExecAndCustomCreds; got != want {
 		t.Fatalf("got %v, want %v", got, want)
diff --git a/test/modules/registry.go b/test/modules/registry.go
index 877d1ba..ed4c871 100644
--- a/test/modules/registry.go
+++ b/test/modules/registry.go
@@ -10,125 +10,109 @@
 	"io"
 	"io/ioutil"
 	"os"
-	"strings"
-	"sync"
+	"path/filepath"
+	"runtime"
+	"strconv"
 	"time"
 
-	"v.io/x/lib/envvar"
 	"v.io/x/lib/vlog"
-
 	vexec "v.io/x/ref/lib/exec"
 )
 
-type commandDesc struct {
-	factory func() *execHandle
+// Program is a symbolic representation of a registered Main function.
+type Program string
+
+type programInfo struct {
 	main    Main
-	help    string
+	factory func() *execHandle
 }
 
-type cmdRegistry struct {
-	sync.Mutex
-	cmds map[string]*commandDesc
+type programRegistry struct {
+	programs []*programInfo
 }
 
-var registry = &cmdRegistry{cmds: make(map[string]*commandDesc)}
+var registry = new(programRegistry)
 
-func (r *cmdRegistry) addCommand(name, help string, factory func() *execHandle, main Main) {
-	r.Lock()
-	defer r.Unlock()
-	r.cmds[name] = &commandDesc{factory, main, help}
+func (r *programRegistry) addProgram(main Main, description string) Program {
+	prog := strconv.Itoa(len(r.programs))
+	factory := func() *execHandle { return newExecHandle(prog, description) }
+	r.programs = append(r.programs, &programInfo{main, factory})
+	return Program(prog)
 }
 
-func (r *cmdRegistry) getCommand(name string) *commandDesc {
-	r.Lock()
-	defer r.Unlock()
-	return r.cmds[name]
+func (r *programRegistry) getProgram(prog Program) *programInfo {
+	index, err := strconv.Atoi(string(prog))
+	if err != nil || index < 0 || index >= len(r.programs) {
+		return nil
+	}
+	return r.programs[index]
 }
 
-func (r *cmdRegistry) getExternalCommand(name string) *commandDesc {
-	h := newExecHandleForExternalCommand(name)
-	return &commandDesc{
+func (r *programRegistry) getExternalProgram(prog Program) *programInfo {
+	h := newExecHandleExternal(string(prog))
+	return &programInfo{
 		factory: func() *execHandle { return h },
 	}
 }
 
-// RegisterChild adds a new command to the registry that will be run
-// as a subprocess. It must be called before Dispatch or DispatchInTest is
-// called, typically from an init function.
-func RegisterChild(name, help string, main Main) {
-	factory := func() *execHandle { return newExecHandle(name) }
-	registry.addCommand(name, help, factory, main)
+func (r *programRegistry) String() string {
+	var s string
+	for _, info := range r.programs {
+		h := info.factory()
+		s += fmt.Sprintf("%s: %s\n", h.entryPoint, h.desc)
+	}
+	return s
 }
 
-// Help returns the help message for the specified command, or a list
-// of all commands if the command parameter is an empty string.
-func Help(command string) string {
-	return registry.help(command)
+// Register adds a new program to the registry that will be run as a subprocess.
+// It must be called before Dispatch is called, typically from an init function.
+func Register(main Main, description string) Program {
+	if _, file, line, ok := runtime.Caller(1); ok {
+		description = fmt.Sprintf("%s:%d %s", shortFile(file), line, description)
+	}
+	return registry.addProgram(main, description)
 }
 
-func (r *cmdRegistry) help(command string) string {
-	r.Lock()
-	defer r.Unlock()
-	if len(command) == 0 {
-		h := ""
-		for c, _ := range r.cmds {
-			h += c + ", "
-		}
-		return strings.TrimRight(h, ", ")
+// shortFile returns the last 3 components of the given file name.
+func shortFile(file string) string {
+	var short string
+	for i := 0; i < 3; i++ {
+		short = filepath.Join(filepath.Base(file), short)
+		file = filepath.Dir(file)
 	}
-	if c := r.cmds[command]; c != nil {
-		return command + ": " + c.help
-	}
-	return ""
+	return short
 }
 
 const shellEntryPoint = "V23_SHELL_HELPER_PROCESS_ENTRY_POINT"
 
-// IsModulesChildProcess returns true if this process was started by
-// the modules package.
-func IsModulesChildProcess() bool {
+// IsChildProcess returns true if this process was started by the modules
+// package.
+func IsChildProcess() bool {
 	return os.Getenv(shellEntryPoint) != ""
 }
 
-// Dispatch will execute the requested subprocess command from a within a
-// a subprocess. It will return without an error if it is executed by a
-// process that does not specify an entry point in its environment.
-//
-// func main() {
-//     if modules.IsModulesChildProcess() {
-//         if err := modules.Dispatch(); err != nil {
-//             panic("error")
-//         }
-//         eturn
-//     }
-//     parent code...
-//
+// Dispatch executes the requested subprocess program from within a subprocess.
+// Returns an error if it is executed by a process that does not specify an
+// entry point in its environment.
 func Dispatch() error {
-	if !IsModulesChildProcess() {
-		return nil
-	}
 	return registry.dispatch()
 }
 
-// DispatchAndExit is like Dispatch except that it will call os.Exit(0)
-// when executed within a child process and the command succeeds, or panic
-// on encountering an error.
-//
-// func main() {
-//     modules.DispatchAndExit()
-//     parent code...
-//
-func DispatchAndExit() {
-	if !IsModulesChildProcess() {
-		return
+// DispatchAndExitIfChild is a convenience function with three possible results:
+//   * os.Exit(0) if called within a child process, and the dispatch succeeds.
+//   * os.Exit(1) if called within a child process, and the dispatch fails.
+//   * return with no side-effects, if not called within a child process.
+func DispatchAndExitIfChild() {
+	if IsChildProcess() {
+		if err := Dispatch(); err != nil {
+			fmt.Fprintf(os.Stderr, "modules.Dispatch failed: %v\n", err)
+			os.Exit(1)
+		}
+		os.Exit(0)
 	}
-	if err := registry.dispatch(); err != nil {
-		panic(fmt.Sprintf("unexpected error: %s", err))
-	}
-	os.Exit(0)
 }
 
-func (r *cmdRegistry) dispatch() error {
+func (r *programRegistry) dispatch() error {
 	ch, err := vexec.GetChildHandle()
 	if err != nil {
 		// This is for debugging only. It's perfectly reasonable for this
@@ -140,18 +124,18 @@
 	// Only signal that the child is ready or failed if we successfully get
 	// a child handle. We most likely failed to get a child handle
 	// because the subprocess was run directly from the command line.
-	command := os.Getenv(shellEntryPoint)
-	if len(command) == 0 {
-		err := fmt.Errorf("Failed to find entrypoint %q", command)
+	prog := os.Getenv(shellEntryPoint)
+	if prog == "" {
+		err := fmt.Errorf("Failed to find entrypoint %q", prog)
 		if ch != nil {
 			ch.SetFailed(err)
 		}
 		return err
 	}
 
-	m := registry.getCommand(command)
+	m := registry.getProgram(Program(prog))
 	if m == nil {
-		err := fmt.Errorf("%s: not registered", command)
+		err := fmt.Errorf("%s: not registered\n%s", prog, registry.String())
 		if ch != nil {
 			ch.SetFailed(err)
 		}
@@ -173,7 +157,7 @@
 	}(os.Getppid())
 
 	flag.Parse()
-	return m.main(os.Stdin, os.Stdout, os.Stderr, envvar.SliceToMap(os.Environ()), flag.Args()...)
+	return m.main(EnvFromOS(), flag.Args()...)
 }
 
 // WaitForEOF returns when a read on its io.Reader parameter returns io.EOF
diff --git a/test/modules/shell.go b/test/modules/shell.go
index ca4c53d..34401c8 100644
--- a/test/modules/shell.go
+++ b/test/modules/shell.go
@@ -4,17 +4,17 @@
 
 // Package modules implements a mechanism for running commonly used services as
 // subprocesses, and client functionality for accessing those services.  Such
-// services and functions are collectively called 'commands' and are managed by
+// services and functions are collectively called 'programs' and are managed by
 // a 'Registry'. The Shell is analagous to the UNIX shell and maintains a key,
 // value store of environment variables and config settings that are accessible
-// to the commands that it hosts. Simple variable expansion is supported.
+// to the programs that it hosts. Simple variable expansion is supported.
 //
-// Three types of 'commands' may be invoked via a Shell:
+// Three types of 'programs' may be invoked via a Shell:
 //
-//   1) Functions of type Shell.Main as subprocesses via fork/exec.
-//   2) Arbitrary non-Vanadium commands available on the underlying operating
+//   1) Functions of type Main as subprocesses via fork/exec.
+//   2) Arbitrary non-Vanadium programs available on the underlying operating
 //      system such as '/bin/cp', 'bash' etc.
-//   3) Arbitrary Vanadium commands available on the underlying operating system
+//   3) Arbitrary Vanadium programs available on the underlying operating system
 //      such as precompiled Vanadium services.
 //
 // The first type requires that the function to be executed is compiled into the
@@ -22,8 +22,8 @@
 // a single, per-process, registry.
 //
 // The second two types allow for arbitrary binaries to be executed. The
-// distinction between a Vanadium and non-Vanadium command is that the Vanadium
-// command implements the protocol used by v.io/x/ref/lib/exec package to
+// distinction between a Vanadium and non-Vanadium program is that the Vanadium
+// program implements the protocol used by v.io/x/ref/lib/exec package to
 // synchronise between the parent and child processes and to share information
 // such as the ConfigKey key,value store supported by the Shell, a shared
 // secret, shared file descriptors etc.
@@ -36,23 +36,20 @@
 //
 // The registry provides the following functions:
 //
-//   RegisterChild: generally called from an init function to register a
-//     shell.Main to be executed in a subprocess by fork/exec'ing the calling
-//     process.
-//   Dispatch: which must be called in the child process to lookup the requested
-//     function in the registry and to invoke it. This will typically be called
-//     from a TestMain. modules.IsModulesChildProcess can be used to determine
-//     if the calling process is a child started via this package.
+//   Register: registers a Main function to be executed in a subprocess,
+//     the returned Program is typically assigned to a global variable.
+//   Dispatch: must be called in the child process to lookup and invoke the
+//     requested function.  Typically called from TestMain.
 //
-// The v23 tool can automate generation of TestMain and calls to RegisterChild.
-// Adding the comment below to a test file will generate the appropriate code.
+// The v23 tool can automate generation of TestMain.  Adding the comment below
+// to a test file will generate the appropriate code.
 //
 //   //go:generate v23 test generate .
 //
 // Use 'v23 test generate --help' to get a complete explanation.
 //
-// In all cases commands are started by invoking the StartWithOpts method on the
-// Shell with the name of the command to run. An instance of the Handle
+// In all cases programs are started by invoking the StartWithOpts method on the
+// Shell with the name of the program to run. An instance of the Handle
 // interface is returned which can be used to interact with the function or
 // subprocess, and in particular to read/write data from/to it using io channels
 // that follow the stdin, stdout, stderr convention. The StartOpts struct is
@@ -61,20 +58,19 @@
 // StartOpts and for common uses of StartWithOpts.
 //
 // Each successful call to StartWithOpts returns a handle representing the
-// running command. This handle can be used to gain access to that command's
+// running program. This handle can be used to gain access to that program's
 // stdin, stdout, stderr and to request or synchronize with its termination via
 // the Shutdown method. The Shutdown method can optionally be used to read any
-// remaining output from the commands stdout and stderr.  The Shell maintains a
+// remaining output from the programs stdout and stderr.  The Shell maintains a
 // record of all such handles and will call Shutdown on them in LIFO order when
 // the Shell's Cleanup method is called.
 //
-// A simple protocol must be followed by all modules.Main commands, in
-// particular, they should wait for their stdin stream to be closed before
-// exiting. The caller can then coordinate with any command by writing to that
-// stdin stream and reading responses from the stdout stream, and it can close
-// stdin when it's ready for the command to exit using the CloseStdin method on
-// the command's handle. Any binary or script that follows this protocol can be
-// used as well.
+// A simple protocol must be followed by all programs, in particular, they
+// should wait for their stdin stream to be closed before exiting. The caller
+// can then coordinate with any program by writing to that stdin stream and
+// reading responses from the stdout stream, and it can close stdin when it's
+// ready for the program to exit using the CloseStdin method on the program's
+// handle. Any binary or script that follows this protocol can be used as well.
 //
 // By default, every Shell created by NewShell starts a security agent to manage
 // principals for child processes. These default credentials can be overridden
@@ -82,12 +78,12 @@
 // environment provided as a parameter to the StartWithOpts method. It is also
 // possible to specify custom credentials via StartOpts.
 //
-// Interacting with Commands
+// Interacting with Programs
 //
 // Handle.Stdout(), Stdin(), Stderr():
 //
 // StartWithOpts returns a Handle which can be used to interact with the running
-// command. In particular, its Stdin() and Stdout() methods give access to the
+// program. In particular, its Stdin() and Stdout() methods give access to the
 // running process' corresponding stdin and stdout and hence can be used to
 // communicate with it. Stderr is handled differently and is configured so that
 // the child's stderr is written to a log file rather than a pipe. This is in
@@ -96,9 +92,9 @@
 //
 // Handle.Shutdown(stdout, stderr io.Writer):
 //
-// The Shutdown method is used to gracefully shutdown a command and to
+// The Shutdown method is used to gracefully shutdown a program and to
 // synchronise with its termination. In particular, Shutdown can be used to read
-// any unread output from the command's stdout and stderr. Note that since
+// any unread output from the program's stdout and stderr. Note that since
 // Stderr is buffered to a file, Shutdown is able to return the entire contents
 // of that file. This is useful for debugging misbehaving/crashing child
 // processes.
@@ -107,32 +103,32 @@
 //
 // The Shell keeps track of all Handles that it has issued and in particular if
 // Shutdown (or Forget) have not been called, it will call Shutdown for each
-// such Handle in LIFO order. This ensures that all commands will be Shutdown
+// such Handle in LIFO order. This ensures that all programs will be Shutdown
 // even if the developer does not explicitly take care to do so for every
 // invocation.
 //
 // Pipes:
 //
-// StartWithOpts allows the caller to pass an io.Reader to the command
+// StartWithOpts allows the caller to pass an io.Reader to the program
 // (StartOpts.Stdin) for it to read from, rather than creating a new pipe
-// internally. This makes it possible to connect the output of one command to
+// internally. This makes it possible to connect the output of one program to
 // the input of another directly.
 //
 // Command Line Arguments:
 //
 // The arguments passed in calls to Start are appended to any system required
 // ones (e.g. for propagating test timeouts, verbosity etc) and the child
-// process will call the command with the result of flag.Args(). In this way the
+// process will call the program with the result of flag.Args(). In this way the
 // caller can provide flags used by libraries in the child process as well as
-// those specific to the command and the command will only receive the args
+// those specific to the program and the program will only receive the args
 // specific to it. The usual "--" convention can be used to override this
 // default behaviour.
 //
 // Caveats:
 //
-// Handle.Shutdown assumes that the child command/process will terminate when
+// Handle.Shutdown assumes that the child program/process will terminate when
 // its stdin stream is closed. This assumption is unlikely to be valid for
-// 'external' commands (e.g. /bin/cp) and in these cases Kill or some other
+// 'external' programs (e.g. /bin/cp) and in these cases Kill or some other
 // application specific mechanism will need to be used.
 package modules
 
@@ -172,7 +168,7 @@
 	ExecProtocol:    true,
 }
 
-// Shell represents the context within which commands are run.
+// Shell represents the context within which programs are run.
 type Shell struct {
 	mu               sync.Mutex
 	env              map[string]string
@@ -350,22 +346,30 @@
 	return creds, nil
 }
 
-type Main func(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error
-
-// String returns a string representation of the Shell, which is a
-// list of the commands currently available in the shell.
-func (sh *Shell) String() string {
-	return registry.help("")
+// Env represents the environment for Main functions.
+type Env struct {
+	Stdin  io.Reader
+	Stdout io.Writer
+	Stderr io.Writer
+	Vars   map[string]string // Environment variables
 }
 
-// Help returns the help message for the specified command.
-func (sh *Shell) Help(command string) string {
-	return registry.help(command)
+// EnvFromOS returns a new Env based on the underlying OS.
+func EnvFromOS() *Env {
+	return &Env{
+		Stdin:  os.Stdin,
+		Stdout: os.Stdout,
+		Stderr: os.Stderr,
+		Vars:   envvar.SliceToMap(os.Environ()),
+	}
 }
 
+// Main describes the entry-point function type for registered programs.
+type Main func(env *Env, args ...string) error
+
 // Start is shorthand for StartWithOpts(sh.DefaultStartOpts(), ...)
-func (sh *Shell) Start(name string, env []string, args ...string) (Handle, error) {
-	return sh.StartWithOpts(sh.DefaultStartOpts(), env, name, args...)
+func (sh *Shell) Start(env []string, prog Program, args ...string) (Handle, error) {
+	return sh.StartWithOpts(sh.DefaultStartOpts(), env, prog, args...)
 }
 
 // StartOpts represents the options that can be passed to the
@@ -397,11 +401,11 @@
 	Credentials *CustomCredentials
 	// ExecProtocol indicates whether the child process is expected to
 	// implement the v.io/x.ref/lib/exec parent/child protocol.
-	// It should be set to false when running non-vanadium commands
+	// It should be set to false when running non-vanadium programs
 	// (e.g. /bin/cp).
 	ExecProtocol bool
-	// External indicates if the command is an external process rather than
-	// a Shell.Main function.
+	// External indicates if the program is an external process rather than
+	// a Main function.
 	External bool
 	// StartTimeout specifies the amount of time to wait for the
 	// child process to signal its correct intialization for Vanadium
@@ -451,37 +455,37 @@
 	return opts
 }
 
-// NoExecCommand returns a copy of opts with the External option
+// NoExecProgram returns a copy of opts with the External option
 // enabled and ExecProtocol disabled.
-func (opts StartOpts) NoExecCommand() StartOpts {
+func (opts StartOpts) NoExecProgram() StartOpts {
 	opts.External = true
 	opts.ExecProtocol = false
 	return opts
 }
 
-// ExternalCommand returns a copy of StartOpts with the
+// ExternalProgram returns a copy of StartOpts with the
 // External option enabled.
-func (opts StartOpts) ExternalCommand() StartOpts {
+func (opts StartOpts) ExternalProgram() StartOpts {
 	opts.External = true
 	return opts
 }
 
 var (
-	ErrNotRegistered        = errors.New("command not registered")
+	ErrNotRegistered        = errors.New("program not registered")
 	ErrNoExecAndCustomCreds = errors.New("ExecProtocol set to false but this invocation is attempting to use custome credentials")
 )
 
-// StartWithOpts starts the specified command according to the supplied
+// StartWithOpts starts the specified program according to the supplied
 // StartOpts and returns a Handle which can be used for interacting with
-// that command.
+// that program.
 //
-// The environment variables for the command are set by merging variables
+// The environment variables for the program are set by merging variables
 // from the OS environment, those in this Shell and those provided as a
 // parameter to it. In general, it prefers values from its parameter over
 // those from the Shell, over those from the OS. However, the VeyronCredentials
 // and agent FdEnvVar variables will never use the value from the Shell or OS.
 //
-// If the shell is managing principals, the command is configured to
+// If the shell is managing principals, the program is configured to
 // connect to the shell's agent. Custom credentials may be specified
 // via StartOpts. If the shell is not managing principals, set
 // the VeyronCredentials environment variable in the 'env' parameter.
@@ -489,7 +493,7 @@
 // The Shell tracks all of the Handles that it creates so that it can shut
 // them down when asked to. The returned Handle may be non-nil even when an
 // error is returned, in which case it may be used to retrieve any output
-// from the failed command.
+// from the failed program.
 //
 // StartWithOpts will return a valid handle for errors that occur during the
 // child processes startup process. It is thus possible to call Shutdown
@@ -504,16 +508,16 @@
 //        }
 //        t.Fatal(err)
 //    }
-func (sh *Shell) StartWithOpts(opts StartOpts, env []string, name string, args ...string) (Handle, error) {
+func (sh *Shell) StartWithOpts(opts StartOpts, env []string, prog Program, args ...string) (Handle, error) {
 	var err error
 	if opts.Error != nil {
 		return nil, opts.Error
 	}
 
-	var desc *commandDesc
+	var info *programInfo
 	if opts.External {
-		desc = registry.getExternalCommand(name)
-	} else if desc = registry.getCommand(name); desc == nil {
+		info = registry.getExternalProgram(prog)
+	} else if info = registry.getProgram(prog); info == nil {
 		return nil, ErrNotRegistered
 	}
 
@@ -536,8 +540,8 @@
 		}
 	}
 
-	handle := desc.factory()
-	h, err := handle.start(sh, p, &opts, sh.setupCommandEnv(env), sh.expand(args))
+	handle := info.factory()
+	h, err := handle.start(sh, p, &opts, sh.setupProgramEnv(env), sh.expand(args))
 	if err != nil {
 		return h, err
 	}
@@ -548,19 +552,19 @@
 	return h, nil
 }
 
-// CommandEnvelope returns the command line and environment that would be used
+// ProgramEnvelope returns the command line and environment that would be used
 // for running the subprocess if it were started with the specifed arguments.
-func (sh *Shell) CommandEnvelope(name string, env []string, args ...string) ([]string, []string) {
-	desc := registry.getCommand(name)
-	if desc == nil {
+func (sh *Shell) ProgramEnvelope(env []string, prog Program, args ...string) ([]string, []string) {
+	info := registry.getProgram(prog)
+	if info == nil {
 		return []string{}, []string{}
 	}
-	return desc.factory().envelope(sh, sh.setupCommandEnv(env), sh.expand(args))
+	return info.factory().envelope(sh, sh.setupProgramEnv(env), sh.expand(args))
 }
 
 // Forget tells the Shell to stop tracking the supplied Handle. This is
 // generally used when the application wants to control the order that
-// commands are shutdown in.
+// programs are shutdown in.
 func (sh *Shell) Forget(h Handle) {
 	sh.mu.Lock()
 	if handle, ok := h.(*execHandle); ok {
@@ -670,7 +674,7 @@
 	var err error
 	for i := len(handles); i > 0; i-- {
 		h := handles[i-1]
-		writeMsg("---- Cleanup calling Shutdown on command %q\n", h.name)
+		writeMsg("---- Cleanup calling Shutdown on program %q\n", h.desc)
 		cerr := h.Shutdown(stdout, stderr)
 		if cerr != nil {
 			err = cerr
@@ -682,7 +686,7 @@
 				return ": error: " + err.Error()
 			}
 		}
-		writeMsg("---- Shutdown on command %q%s\n", h.name, fn())
+		writeMsg("---- Shutdown on program %q%s\n", h.desc, fn())
 	}
 
 	if sh.cancelCtx != nil {
@@ -697,7 +701,7 @@
 	return err
 }
 
-func (sh *Shell) setupCommandEnv(env []string) []string {
+func (sh *Shell) setupProgramEnv(env []string) []string {
 	osmap := envvar.SliceToMap(os.Environ())
 	evmap := envvar.SliceToMap(env)
 
@@ -731,34 +735,34 @@
 	Error() error
 }
 
-// Handle represents a running command.
+// Handle represents a running program.
 type Handle interface {
 	ExpectSession
 
-	// Stdout returns a reader to the running command's stdout stream.
+	// Stdout returns a reader to the running program's stdout stream.
 	Stdout() io.Reader
 
-	// Stderr returns a reader to the running command's stderr
+	// Stderr returns a reader to the running program's stderr
 	// stream.
 	Stderr() io.Reader
 
-	// Stdin returns a writer to the running command's stdin. The
-	// convention is for commands to wait for stdin to be closed before
+	// Stdin returns a writer to the running program's stdin. The
+	// convention is for programs to wait for stdin to be closed before
 	// they exit, thus the caller should close stdin when it wants the
-	// command to exit cleanly.
+	// program to exit cleanly.
 	Stdin() io.Writer
 
 	// CloseStdin closes stdin in a manner that avoids a data race
 	// between any current readers on it.
 	CloseStdin()
 
-	// Shutdown closes the Stdin for the command and then reads output
-	// from the command's stdout until it encounters EOF, waits for
-	// the command to complete and then reads all of its stderr output.
+	// Shutdown closes the Stdin for the program and then reads output
+	// from the program's stdout until it encounters EOF, waits for
+	// the program to complete and then reads all of its stderr output.
 	// The stdout and stderr contents are written to the corresponding
 	// io.Writers if they are non-nil, otherwise the content is discarded.
 	Shutdown(stdout, stderr io.Writer) error
 
-	// Pid returns the pid of the process running the command
+	// Pid returns the pid of the process running the program
 	Pid() int
 }
diff --git a/test/modules/util.go b/test/modules/util.go
index db463a1..642abfe 100644
--- a/test/modules/util.go
+++ b/test/modules/util.go
@@ -5,13 +5,11 @@
 package modules
 
 import (
-	"flag"
 	"fmt"
 	"hash/adler32"
 	"io"
 	"io/ioutil"
 	"os"
-	"strings"
 
 	"v.io/v23/security"
 	"v.io/x/lib/vlog"
@@ -60,19 +58,3 @@
 	}
 	return p, nil
 }
-
-// Usage generates a usage string based on the flags in a flagset.
-func Usage(fs *flag.FlagSet) string {
-	res := []string{}
-	fs.VisitAll(func(f *flag.Flag) {
-		format := "  -%s=%s: %s"
-		if getter, ok := f.Value.(flag.Getter); ok {
-			if _, ok := getter.Get().(string); ok {
-				// put quotes on the value
-				format = "  -%s=%q: %s"
-			}
-		}
-		res = append(res, fmt.Sprintf(format, f.Name, f.DefValue, f.Usage))
-	})
-	return strings.Join(res, "\n") + "\n"
-}
diff --git a/test/testutil/util.go b/test/testutil/util.go
index cd80aa6..05492e2 100644
--- a/test/testutil/util.go
+++ b/test/testutil/util.go
@@ -10,24 +10,6 @@
 	"runtime"
 )
 
-// DepthToExternalCaller determines the number of stack frames to the first
-// enclosing caller that is external to the package that this function is
-// called from. Drectory name is used as a proxy for package name,
-// that is, the directory component of the file return runtime.Caller is
-// compared to that of the lowest level caller until a different one is
-// encountered as the stack is walked upwards.
-func DepthToExternalCaller() int {
-	_, file, _, _ := runtime.Caller(1)
-	cwd := filepath.Dir(file)
-	for d := 2; d < 10; d++ {
-		_, file, _, _ := runtime.Caller(d)
-		if cwd != filepath.Dir(file) {
-			return d
-		}
-	}
-	return 1
-}
-
 // FormatLogLine will prepend the file and line number of the caller
 // at the specificied depth (as per runtime.Caller) to the supplied
 // format and args and return a formatted string. It is useful when
diff --git a/test/testutil/util_test.go b/test/testutil/util_test.go
index 86a8d24..0a44dab 100644
--- a/test/testutil/util_test.go
+++ b/test/testutil/util_test.go
@@ -10,14 +10,8 @@
 )
 
 func TestFormatLogline(t *testing.T) {
-	depth, want := DepthToExternalCaller(), 2
-	if depth != want {
-		t.Errorf("got %v, want %v", depth, want)
-	}
-	{
-		line, want := FormatLogLine(depth, "test"), "testing.go:.*"
-		if ok, err := regexp.MatchString(want, line); !ok || err != nil {
-			t.Errorf("got %v, want %v", line, want)
-		}
+	line, want := FormatLogLine(2, "test"), "testing.go:.*"
+	if ok, err := regexp.MatchString(want, line); !ok || err != nil {
+		t.Errorf("got %v, want %v", line, want)
 	}
 }
diff --git a/test/v23tests/binary.go b/test/v23tests/binary.go
index c4dd0cc..ec770e3 100644
--- a/test/v23tests/binary.go
+++ b/test/v23tests/binary.go
@@ -11,7 +11,6 @@
 	"strings"
 
 	"v.io/x/lib/vlog"
-
 	"v.io/x/ref/test/modules"
 )
 
@@ -65,7 +64,7 @@
 		opts.Credentials, opts.Error = b.env.shell.NewChildCredentials("child")
 	}
 	opts.ExpectTesting = b.env.TB
-	handle, err := b.env.shell.StartWithOpts(opts, b.envVars, b.Path(), args...)
+	handle, err := b.env.shell.StartWithOpts(opts, b.envVars, modules.Program(b.Path()), args...)
 	if err != nil {
 		if handle != nil {
 			vlog.Infof("%s: start failed", Caller(skip+1))
diff --git a/test/v23tests/internal/v23_test.go b/test/v23tests/internal/v23_test.go
index 87514d1..aad6a7a 100644
--- a/test/v23tests/internal/v23_test.go
+++ b/test/v23tests/internal/v23_test.go
@@ -4,11 +4,14 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
+
 package internal_test
 
-import "testing"
+import (
+	"testing"
 
-import "v.io/x/ref/test/v23tests"
+	"v.io/x/ref/test/v23tests"
+)
 
 func TestV23One(t *testing.T) {
 	v23tests.RunTest(t, V23TestOne)
diff --git a/test/v23tests/invocation.go b/test/v23tests/invocation.go
index 8d5f3cf..63ddc75 100644
--- a/test/v23tests/invocation.go
+++ b/test/v23tests/invocation.go
@@ -11,7 +11,6 @@
 	"syscall"
 
 	"v.io/x/lib/vlog"
-
 	"v.io/x/ref/test/modules"
 )
 
diff --git a/test/v23tests/v23tests.go b/test/v23tests/v23tests.go
index 4976dd1..91e1afa 100644
--- a/test/v23tests/v23tests.go
+++ b/test/v23tests/v23tests.go
@@ -419,7 +419,7 @@
 		env:     t,
 		envVars: nil,
 		path:    path,
-		opts:    t.shell.DefaultStartOpts().NoExecCommand(),
+		opts:    t.shell.DefaultStartOpts().NoExecProgram(),
 	}
 }
 
@@ -449,7 +449,7 @@
 // be used via BuildV23Pkg.
 func (t *T) BuildV23Pkg(pkg string, flags ...string) *Binary {
 	b := t.buildPkg(pkg, flags...)
-	b.opts = t.shell.DefaultStartOpts().ExternalCommand()
+	b.opts = t.shell.DefaultStartOpts().ExternalProgram()
 	return b
 }
 
@@ -474,7 +474,7 @@
 		env:     t,
 		envVars: nil,
 		path:    built_path,
-		opts:    t.shell.DefaultStartOpts().NoExecCommand(),
+		opts:    t.shell.DefaultStartOpts().NoExecProgram(),
 	}
 	t.builtBinaries[pkg] = binary
 	return binary
diff --git a/test/v23tests/v23tests_test.go b/test/v23tests/v23tests_test.go
index 76e16be..63efeb9 100644
--- a/test/v23tests/v23tests_test.go
+++ b/test/v23tests/v23tests_test.go
@@ -8,7 +8,6 @@
 	"bytes"
 	"crypto/sha1"
 	"fmt"
-	"io"
 	"os"
 	"regexp"
 	"strings"
@@ -101,23 +100,26 @@
 var globalT *testing.T
 
 func TestHelperProcess(t *testing.T) {
-	globalT = t
-	modules.Dispatch()
+	if modules.IsChildProcess() {
+		globalT = t
+		if err := modules.Dispatch(); err != nil {
+			t.Errorf("modules.Dispatch failed: %v", err)
+		}
+	}
 }
 
-func RunIntegrationTestInChild(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+var RunIntegrationTestInChild = modules.Register(func(env *modules.Env, args ...string) error {
 	v23tests.RunTest(globalT, IntegrationTestInChild)
 	return nil
-}
+}, "RunIntegrationTestInChild")
 
 func init() {
 	test.Init()
-	modules.RegisterChild("RunIntegrationTestInChild", "", RunIntegrationTestInChild)
 }
 
 func TestDeferHandling(t *testing.T) {
 	sh, _ := modules.NewShell(nil, nil, testing.Verbose(), t)
-	child, err := sh.Start("RunIntegrationTestInChild", nil, "--test.run=TestHelperProcess", "--v23.tests")
+	child, err := sh.Start(nil, RunIntegrationTestInChild, "--test.run=TestHelperProcess", "--v23.tests")
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -294,7 +296,7 @@
 		msg := recover().(string)
 		// this, and the tests below are intended to ensure that line #s
 		// are captured and reported correctly.
-		if got, want := msg, "v23tests_test.go:304"; !strings.Contains(got, want) {
+		if got, want := msg, "v23tests_test.go:306"; !strings.Contains(got, want) {
 			t.Fatalf("%q does not contain %q", got, want)
 		}
 		if got, want := msg, "fork/exec /bin/echox: no such file or directory"; !strings.Contains(got, want) {
@@ -316,7 +318,7 @@
 	sh.SetDefaultStartOpts(opts)
 	defer func() {
 		msg := recover().(string)
-		if got, want := msg, "v23tests_test.go:326"; !strings.Contains(got, want) {
+		if got, want := msg, "v23tests_test.go:328"; !strings.Contains(got, want) {
 			t.Fatalf("%q does not contain %q", got, want)
 		}
 		if got, want := msg, "StartWithOpts"; !strings.Contains(got, want) {
@@ -340,7 +342,7 @@
 		if iterations == 0 {
 			t.Fatalf("our sleeper didn't get to run")
 		}
-		if got, want := recover().(string), "v23tests_test.go:347: timed out"; !strings.Contains(got, want) {
+		if got, want := recover().(string), "v23tests_test.go:349: timed out"; !strings.Contains(got, want) {
 			t.Fatalf("%q does not contain %q", got, want)
 		}
 	}()
@@ -362,7 +364,7 @@
 		if iterations != 0 {
 			t.Fatalf("our sleeper got to run")
 		}
-		if got, want := recover().(string), "v23tests_test.go:369: timed out"; !strings.Contains(got, want) {
+		if got, want := recover().(string), "v23tests_test.go:371: timed out"; !strings.Contains(got, want) {
 			t.Fatalf("%q does not contain %q", got, want)
 		}
 	}()