Merge "examples/hello:  Move hello to the test directory."
diff --git a/cmd/gclogs/doc.go b/cmd/gclogs/doc.go
index 95cbf6c..c4aabd1 100644
--- a/cmd/gclogs/doc.go
+++ b/cmd/gclogs/doc.go
@@ -33,50 +33,7 @@
    If true, each deleted file is shown on stdout.
 
 The global flags are:
- -alsologtostderr=true
-   log to standard error as well as files
- -log_backtrace_at=:0
-   when logging hits line file:N, emit a stack trace
- -log_dir=
-   if non-empty, write log files to this directory
- -logtostderr=false
-   log to standard error instead of files
- -max_stack_buf_size=4292608
-   max size in bytes of the buffer to use for logging stack traces
- -stderrthreshold=2
-   logs at or above this threshold go to stderr
- -v=0
-   log level for V logs
- -v23.credentials=
-   directory to use for storing security credentials
- -v23.i18n-catalogue=
-   18n catalogue files to load, comma separated
  -v23.metadata=<just specify -v23.metadata to activate>
    Displays metadata for the program and exits.
- -v23.namespace.root=[/(dev.v.io/role/vprod/service/mounttabled)@ns.dev.v.io:8101]
-   local namespace root; can be repeated to provided multiple roots
- -v23.permissions.file=map[]
-   specify a perms file as <name>:<permsfile>
- -v23.permissions.literal=
-   explicitly specify the runtime perms as a JSON-encoded access.Permissions.
-   Overrides all --v23.permissions.file flags.
- -v23.proxy=
-   object name of proxy service to use to export services across network
-   boundaries
- -v23.tcp.address=
-   address to listen on
- -v23.tcp.protocol=wsh
-   protocol to listen with
- -v23.vtrace.cache-size=1024
-   The number of vtrace traces to store in memory.
- -v23.vtrace.collect-regexp=
-   Spans and annotations that match this regular expression will trigger trace
-   collection.
- -v23.vtrace.dump-on-shutdown=true
-   If true, dump all stored traces on runtime shutdown.
- -v23.vtrace.sample-rate=0
-   Rate (from 0.0 to 1.0) to sample vtrace traces.
- -vmodule=
-   comma-separated list of pattern=N settings for file-filtered logging
 */
 package main
diff --git a/cmd/gclogs/gclogs.go b/cmd/gclogs/gclogs.go
index 76cb946..4ba69d5 100644
--- a/cmd/gclogs/gclogs.go
+++ b/cmd/gclogs/gclogs.go
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// The following enables go generate to generate the doc.go file.
+//go:generate go run $V23_ROOT/release/go/src/v.io/x/lib/cmdline/testdata/gendoc.go . -help
+
 package main
 
 import (
@@ -13,9 +16,7 @@
 	"regexp"
 	"time"
 
-	"v.io/v23"
-	"v.io/x/lib/cmdline"
-	_ "v.io/x/ref/profiles/static"
+	"v.io/x/lib/cmdline2"
 )
 
 var (
@@ -24,10 +25,10 @@
 	flagVerbose  bool
 	flagDryrun   bool
 
-	cmdGCLogs = &cmdline.Command{
-		Run:   garbageCollectLogs,
-		Name:  "gclogs",
-		Short: "safely deletes old log files",
+	cmdGCLogs = &cmdline2.Command{
+		Runner: cmdline2.RunnerFunc(garbageCollectLogs),
+		Name:   "gclogs",
+		Short:  "safely deletes old log files",
 		Long: `
 Command gclogs safely deletes old log files.
 
@@ -44,19 +45,19 @@
 )
 
 func init() {
-	cmdline.HideGlobalFlagsExcept()
 	cmdGCLogs.Flags.DurationVar(&flagCutoff, "cutoff", 24*time.Hour, "The age cut-off for a log file to be considered for garbage collection.")
 	cmdGCLogs.Flags.StringVar(&flagProgname, "program", ".*", `A regular expression to apply to the program part of the log file name, e.g ".*test".`)
 	cmdGCLogs.Flags.BoolVar(&flagVerbose, "verbose", false, "If true, each deleted file is shown on stdout.")
 	cmdGCLogs.Flags.BoolVar(&flagDryrun, "n", false, "If true, log files that would be deleted are shown on stdout, but not actually deleted.")
 }
 
-func garbageCollectLogs(cmd *cmdline.Command, args []string) error {
-	_, shutdown := v23.Init()
-	defer shutdown()
+func main() {
+	cmdline2.Main(cmdGCLogs)
+}
 
+func garbageCollectLogs(env *cmdline2.Env, args []string) error {
 	if len(args) == 0 {
-		cmd.UsageErrorf("gclogs requires at least one argument")
+		env.UsageErrorf("gclogs requires at least one argument")
 	}
 	timeCutoff := time.Now().Add(-flagCutoff)
 	currentUser, err := user.Current()
@@ -69,15 +70,15 @@
 	}
 	var lastErr error
 	for _, logdir := range args {
-		if err := processDirectory(cmd, logdir, timeCutoff, programRE, currentUser.Username); err != nil {
+		if err := processDirectory(env, logdir, timeCutoff, programRE, currentUser.Username); err != nil {
 			lastErr = err
 		}
 	}
 	return lastErr
 }
 
-func processDirectory(cmd *cmdline.Command, logdir string, timeCutoff time.Time, programRE *regexp.Regexp, username string) error {
-	fmt.Fprintf(cmd.Stdout(), "Processing: %q\n", logdir)
+func processDirectory(env *cmdline2.Env, logdir string, timeCutoff time.Time, programRE *regexp.Regexp, username string) error {
+	fmt.Fprintf(env.Stdout, "Processing: %q\n", logdir)
 
 	f, err := os.Open(logdir)
 	if err != nil {
@@ -101,26 +102,26 @@
 			fullname := filepath.Join(logdir, file.Name())
 			if file.IsDir() {
 				if flagVerbose {
-					fmt.Fprintf(cmd.Stdout(), "Skipped directory: %q\n", fullname)
+					fmt.Fprintf(env.Stdout, "Skipped directory: %q\n", fullname)
 				}
 				continue
 			}
 			lf, err := parseFileInfo(logdir, file)
 			if err != nil {
 				if flagVerbose {
-					fmt.Fprintf(cmd.Stdout(), "Not a log file: %q\n", fullname)
+					fmt.Fprintf(env.Stdout, "Not a log file: %q\n", fullname)
 				}
 				continue
 			}
 			if lf.user != username {
 				if flagVerbose {
-					fmt.Fprintf(cmd.Stdout(), "Skipped log file created by other user: %q\n", fullname)
+					fmt.Fprintf(env.Stdout, "Skipped log file created by other user: %q\n", fullname)
 				}
 				continue
 			}
 			if !programRE.MatchString(lf.program) {
 				if flagVerbose {
-					fmt.Fprintf(cmd.Stdout(), "Skipped log file doesn't match %q: %q\n", flagProgname, fullname)
+					fmt.Fprintf(env.Stdout, "Skipped log file doesn't match %q: %q\n", flagProgname, fullname)
 				}
 				continue
 			}
@@ -130,11 +131,11 @@
 			}
 			if file.ModTime().Before(timeCutoff) {
 				if flagDryrun {
-					fmt.Fprintf(cmd.Stdout(), "Would delete %q\n", fullname)
+					fmt.Fprintf(env.Stdout, "Would delete %q\n", fullname)
 					continue
 				}
 				if flagVerbose {
-					fmt.Fprintf(cmd.Stdout(), "Deleting %q\n", fullname)
+					fmt.Fprintf(env.Stdout, "Deleting %q\n", fullname)
 				}
 				if err := os.Remove(fullname); err != nil {
 					lastErr = err
@@ -148,11 +149,11 @@
 	for _, sl := range symlinks {
 		if _, err := os.Stat(sl); err != nil && os.IsNotExist(err) {
 			if flagDryrun {
-				fmt.Fprintf(cmd.Stdout(), "Would delete symlink %q\n", sl)
+				fmt.Fprintf(env.Stdout, "Would delete symlink %q\n", sl)
 				continue
 			}
 			if flagVerbose {
-				fmt.Fprintf(cmd.Stdout(), "Deleting symlink %q\n", sl)
+				fmt.Fprintf(env.Stdout, "Deleting symlink %q\n", sl)
 			}
 			if err := os.Remove(sl); err != nil {
 				lastErr = err
@@ -162,6 +163,6 @@
 		}
 
 	}
-	fmt.Fprintf(cmd.Stdout(), "Number of files deleted: %d\n", deleted)
+	fmt.Fprintf(env.Stdout, "Number of files deleted: %d\n", deleted)
 	return lastErr
 }
diff --git a/cmd/gclogs/gclogs_test.go b/cmd/gclogs/gclogs_test.go
index 480b9d6..da7b0ea 100644
--- a/cmd/gclogs/gclogs_test.go
+++ b/cmd/gclogs/gclogs_test.go
@@ -15,6 +15,8 @@
 	"strings"
 	"testing"
 	"time"
+
+	"v.io/x/lib/cmdline2"
 )
 
 func setup(t *testing.T, workdir, username string) (tmpdir string) {
@@ -70,10 +72,6 @@
 		t.Fatalf("user.Current failed: %v", err)
 	}
 
-	cmd := cmdGCLogs
-	var stdout, stderr bytes.Buffer
-	cmd.Init(nil, &stdout, &stderr)
-
 	testcases := []struct {
 		cutoff   time.Duration
 		verbose  bool
@@ -162,7 +160,9 @@
 		cutoff := fmt.Sprintf("--cutoff=%s", tc.cutoff)
 		verbose := fmt.Sprintf("--verbose=%v", tc.verbose)
 		dryrun := fmt.Sprintf("--n=%v", tc.dryrun)
-		if err := cmd.Execute([]string{cutoff, verbose, dryrun, testdir}); err != nil {
+		var stdout, stderr bytes.Buffer
+		env := &cmdline2.Env{Stdout: &stdout, Stderr: &stderr}
+		if err := cmdline2.ParseAndRun(cmdGCLogs, env, []string{cutoff, verbose, dryrun, testdir}); err != nil {
 			t.Fatalf("%v: %v", stderr.String(), err)
 		}
 		gotsl := strings.Split(stdout.String(), "\n")
@@ -176,6 +176,5 @@
 		if got != expected {
 			t.Errorf("Unexpected result for (%v, %v): got %q, expected %q", tc.cutoff, tc.verbose, got, expected)
 		}
-		stdout.Reset()
 	}
 }
diff --git a/cmd/gclogs/main.go b/cmd/gclogs/main.go
deleted file mode 100644
index 9c62ff3..0000000
--- a/cmd/gclogs/main.go
+++ /dev/null
@@ -1,16 +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 following enables go generate to generate the doc.go file.
-//go:generate go run $V23_ROOT/release/go/src/v.io/x/lib/cmdline/testdata/gendoc.go . -help
-
-package main
-
-import (
-	"os"
-)
-
-func main() {
-	os.Exit(cmdGCLogs.Main())
-}
diff --git a/cmd/mounttable/doc.go b/cmd/mounttable/doc.go
index 7763255..0dd113d 100644
--- a/cmd/mounttable/doc.go
+++ b/cmd/mounttable/doc.go
@@ -116,11 +116,6 @@
 
 "help ..." recursively displays help for all commands and topics.
 
-Output is formatted to a target width in runes, determined by checking the
-CMDLINE_WIDTH environment variable, falling back on the terminal width, falling
-back on 80 chars.  By setting CMDLINE_WIDTH=x, if x > 0 the width is x, if x < 0
-the width is unlimited, and if x == 0 or is unset one of the fallbacks is used.
-
 Usage:
    mounttable help [flags] [command/topic ...]
 
@@ -133,5 +128,9 @@
       full    - Good for cmdline output, shows all global flags.
       godoc   - Good for godoc processing.
    Override the default by setting the CMDLINE_STYLE environment variable.
+ -width=<terminal width>
+   Format output to this target width in runes, or unlimited if width < 0.
+   Defaults to the terminal width if available.  Override the default by setting
+   the CMDLINE_WIDTH environment variable.
 */
 package main
diff --git a/cmd/mounttable/impl.go b/cmd/mounttable/impl.go
index 8be675e..04fcc9e 100644
--- a/cmd/mounttable/impl.go
+++ b/cmd/mounttable/impl.go
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// The following enables go generate to generate the doc.go file.
+//go:generate go run $V23_ROOT/release/go/src/v.io/x/lib/cmdline/testdata/gendoc.go .
+
 package main
 
 import (
@@ -16,16 +19,19 @@
 	"v.io/v23/options"
 	"v.io/v23/rpc"
 	"v.io/v23/security"
-	"v.io/x/lib/cmdline"
+	"v.io/x/lib/cmdline2"
 	"v.io/x/lib/vlog"
+	"v.io/x/ref/lib/v23cmd"
+	_ "v.io/x/ref/profiles"
 )
 
-func init() {
-	cmdline.HideGlobalFlagsExcept(regexp.MustCompile(`^v23\.namespace\.root$`))
+func main() {
+	cmdline2.HideGlobalFlagsExcept(regexp.MustCompile(`^v23\.namespace\.root$`))
+	cmdline2.Main(cmdRoot)
 }
 
-var cmdGlob = &cmdline.Command{
-	Run:      runGlob,
+var cmdGlob = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runGlob),
 	Name:     "glob",
 	Short:    "returns all matching entries in the mount table",
 	Long:     "returns all matching entries in the mount table",
@@ -37,8 +43,8 @@
 `,
 }
 
-func runGlob(cmd *cmdline.Command, args []string) error {
-	ctx, cancel := context.WithTimeout(gctx, time.Minute)
+func runGlob(ctx *context.T, env *cmdline2.Env, args []string) error {
+	ctx, cancel := context.WithTimeout(ctx, time.Minute)
 	defer cancel()
 
 	if len(args) == 1 {
@@ -49,7 +55,7 @@
 		args = append([]string{roots[0]}, args...)
 	}
 	if expected, got := 2, len(args); expected != got {
-		return cmd.UsageErrorf("glob: incorrect number of arguments, expected %d, got %d", expected, got)
+		return env.UsageErrorf("glob: incorrect number of arguments, expected %d, got %d", expected, got)
 	}
 
 	name, pattern := args[0], args[1]
@@ -65,11 +71,11 @@
 		}
 		switch v := gr.(type) {
 		case naming.GlobReplyEntry:
-			fmt.Fprint(cmd.Stdout(), v.Value.Name)
+			fmt.Fprint(env.Stdout, v.Value.Name)
 			for _, s := range v.Value.Servers {
-				fmt.Fprintf(cmd.Stdout(), " %s (Deadline %s)", s.Server, s.Deadline.Time)
+				fmt.Fprintf(env.Stdout, " %s (Deadline %s)", s.Server, s.Deadline.Time)
 			}
-			fmt.Fprintln(cmd.Stdout())
+			fmt.Fprintln(env.Stdout)
 		}
 	}
 	if err := call.Finish(); err != nil {
@@ -78,8 +84,8 @@
 	return nil
 }
 
-var cmdMount = &cmdline.Command{
-	Run:      runMount,
+var cmdMount = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runMount),
 	Name:     "mount",
 	Short:    "Mounts a server <name> onto a mount table",
 	Long:     "Mounts a server <name> onto a mount table",
@@ -97,10 +103,10 @@
 `,
 }
 
-func runMount(cmd *cmdline.Command, args []string) error {
+func runMount(ctx *context.T, env *cmdline2.Env, args []string) error {
 	got := len(args)
 	if got < 2 || got > 4 {
-		return cmd.UsageErrorf("mount: incorrect number of arguments, expected 2, 3, or 4, got %d", got)
+		return env.UsageErrorf("mount: incorrect number of arguments, expected 2, 3, or 4, got %d", got)
 	}
 	name := args[0]
 	server := args[1]
@@ -124,18 +130,18 @@
 			}
 		}
 	}
-	ctx, cancel := context.WithTimeout(gctx, time.Minute)
+	ctx, cancel := context.WithTimeout(ctx, time.Minute)
 	defer cancel()
 	client := v23.GetClient(ctx)
 	if err := client.Call(ctx, name, "Mount", []interface{}{server, seconds, flags}, nil, options.NoResolve{}); err != nil {
 		return err
 	}
-	fmt.Fprintln(cmd.Stdout(), "Name mounted successfully.")
+	fmt.Fprintln(env.Stdout, "Name mounted successfully.")
 	return nil
 }
 
-var cmdUnmount = &cmdline.Command{
-	Run:      runUnmount,
+var cmdUnmount = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runUnmount),
 	Name:     "unmount",
 	Short:    "removes server <name> from the mount table",
 	Long:     "removes server <name> from the mount table",
@@ -146,22 +152,22 @@
 `,
 }
 
-func runUnmount(cmd *cmdline.Command, args []string) error {
+func runUnmount(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, got := 2, len(args); expected != got {
-		return cmd.UsageErrorf("unmount: incorrect number of arguments, expected %d, got %d", expected, got)
+		return env.UsageErrorf("unmount: incorrect number of arguments, expected %d, got %d", expected, got)
 	}
-	ctx, cancel := context.WithTimeout(gctx, time.Minute)
+	ctx, cancel := context.WithTimeout(ctx, time.Minute)
 	defer cancel()
 	client := v23.GetClient(ctx)
 	if err := client.Call(ctx, args[0], "Unmount", []interface{}{args[1]}, nil, options.NoResolve{}); err != nil {
 		return err
 	}
-	fmt.Fprintln(cmd.Stdout(), "Unmount successful or name not mounted.")
+	fmt.Fprintln(env.Stdout, "Unmount successful or name not mounted.")
 	return nil
 }
 
-var cmdResolveStep = &cmdline.Command{
-	Run:      runResolveStep,
+var cmdResolveStep = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runResolveStep),
 	Name:     "resolvestep",
 	Short:    "takes the next step in resolving a name.",
 	Long:     "takes the next step in resolving a name.",
@@ -171,30 +177,28 @@
 `,
 }
 
-func runResolveStep(cmd *cmdline.Command, args []string) error {
+func runResolveStep(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, got := 1, len(args); expected != got {
-		return cmd.UsageErrorf("mount: incorrect number of arguments, expected %d, got %d", expected, got)
+		return env.UsageErrorf("mount: incorrect number of arguments, expected %d, got %d", expected, got)
 	}
-	ctx, cancel := context.WithTimeout(gctx, time.Minute)
+	ctx, cancel := context.WithTimeout(ctx, time.Minute)
 	defer cancel()
 	client := v23.GetClient(ctx)
 	var entry naming.MountEntry
 	if err := client.Call(ctx, args[0], "ResolveStep", nil, []interface{}{&entry}, options.NoResolve{}); err != nil {
 		return err
 	}
-	fmt.Fprintf(cmd.Stdout(), "Servers: %v Suffix: %q MT: %v\n", entry.Servers, entry.Name, entry.ServesMountTable)
+	fmt.Fprintf(env.Stdout, "Servers: %v Suffix: %q MT: %v\n", entry.Servers, entry.Name, entry.ServesMountTable)
 	return nil
 }
 
-func root() *cmdline.Command {
-	return &cmdline.Command{
-		Name:  "mounttable",
-		Short: "sends commands to Vanadium mounttable services",
-		Long: `
+var cmdRoot = &cmdline2.Command{
+	Name:  "mounttable",
+	Short: "sends commands to Vanadium mounttable services",
+	Long: `
 Command mounttable sends commands to Vanadium mounttable services.
 `,
-		Children: []*cmdline.Command{cmdGlob, cmdMount, cmdUnmount, cmdResolveStep},
-	}
+	Children: []*cmdline2.Command{cmdGlob, cmdMount, cmdUnmount, cmdResolveStep},
 }
 
 func blessingPatternsFromServer(ctx *context.T, server string) ([]security.BlessingPattern, error) {
diff --git a/cmd/mounttable/impl_test.go b/cmd/mounttable/impl_test.go
index 84de715..66495a9 100644
--- a/cmd/mounttable/impl_test.go
+++ b/cmd/mounttable/impl_test.go
@@ -19,8 +19,9 @@
 	"v.io/v23/security/access"
 	"v.io/v23/services/mounttable"
 	vdltime "v.io/v23/vdlroot/time"
+	"v.io/x/lib/cmdline2"
 	"v.io/x/lib/vlog"
-
+	"v.io/x/ref/lib/v23cmd"
 	_ "v.io/x/ref/profiles"
 	"v.io/x/ref/test"
 )
@@ -112,11 +113,10 @@
 }
 
 func TestMountTableClient(t *testing.T) {
-	var shutdown v23.Shutdown
-	gctx, shutdown = test.InitForTest()
+	ctx, shutdown := test.InitForTest()
 	defer shutdown()
 
-	server, endpoint, err := startServer(t, gctx)
+	server, endpoint, err := startServer(t, ctx)
 	if err != nil {
 		return
 	}
@@ -124,15 +124,14 @@
 
 	// Make sure to use our newly created mounttable rather than the
 	// default.
-	v23.GetNamespace(gctx).SetRoots(endpoint.Name())
+	v23.GetNamespace(ctx).SetRoots(endpoint.Name())
 
 	// Setup the command-line.
-	cmd := root()
 	var stdout, stderr bytes.Buffer
-	cmd.Init(nil, &stdout, &stderr)
+	env := &cmdline2.Env{Stdout: &stdout, Stderr: &stderr}
 
 	// Test the 'glob' command.
-	if err := cmd.Execute([]string{"glob", naming.JoinAddressName(endpoint.String(), ""), "*"}); err != nil {
+	if err := v23cmd.ParseAndRun(cmdRoot, ctx, env, []string{"glob", naming.JoinAddressName(endpoint.String(), ""), "*"}); err != nil {
 		t.Fatalf("%v", err)
 	}
 	const deadRE = `\(Deadline ([^)]+)\)`
@@ -142,7 +141,7 @@
 	stdout.Reset()
 
 	// Test the 'mount' command.
-	if err := cmd.Execute([]string{"mount", "server", endpoint.Name(), "123s"}); err != nil {
+	if err := v23cmd.ParseAndRun(cmdRoot, ctx, env, []string{"mount", "server", endpoint.Name(), "123s"}); err != nil {
 		t.Fatalf("%v", err)
 	}
 	if got, want := strings.TrimSpace(stdout.String()), "Name mounted successfully."; got != want {
@@ -151,7 +150,7 @@
 	stdout.Reset()
 
 	// Test the 'unmount' command.
-	if err := cmd.Execute([]string{"unmount", "server", endpoint.Name()}); err != nil {
+	if err := v23cmd.ParseAndRun(cmdRoot, ctx, env, []string{"unmount", "server", endpoint.Name()}); err != nil {
 		t.Fatalf("%v", err)
 	}
 	if got, want := strings.TrimSpace(stdout.String()), "Unmount successful or name not mounted."; got != want {
@@ -161,7 +160,7 @@
 
 	// Test the 'resolvestep' command.
 	vlog.Infof("resovestep %s", naming.JoinAddressName(endpoint.String(), "name"))
-	if err := cmd.Execute([]string{"resolvestep", naming.JoinAddressName(endpoint.String(), "name")}); err != nil {
+	if err := v23cmd.ParseAndRun(cmdRoot, ctx, env, []string{"resolvestep", naming.JoinAddressName(endpoint.String(), "name")}); err != nil {
 		t.Fatalf("%v", err)
 	}
 	if got, wantRE := strings.TrimSpace(stdout.String()), regexp.MustCompile(`Servers: \[\{server1 [^}]+\}\] Suffix: "name" MT: false`); !wantRE.MatchString(got) {
diff --git a/cmd/mounttable/main.go b/cmd/mounttable/main.go
deleted file mode 100644
index e33dc99..0000000
--- a/cmd/mounttable/main.go
+++ /dev/null
@@ -1,27 +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 following enables go generate to generate the doc.go file.
-//go:generate go run $V23_ROOT/release/go/src/v.io/x/lib/cmdline/testdata/gendoc.go .
-
-package main
-
-import (
-	"os"
-
-	"v.io/v23"
-	"v.io/v23/context"
-
-	_ "v.io/x/ref/profiles"
-)
-
-var gctx *context.T
-
-func main() {
-	var shutdown v23.Shutdown
-	gctx, shutdown = v23.Init()
-	exitCode := root().Main()
-	shutdown()
-	os.Exit(exitCode)
-}
diff --git a/cmd/namespace/doc.go b/cmd/namespace/doc.go
index b6e2957..d873fb4 100644
--- a/cmd/namespace/doc.go
+++ b/cmd/namespace/doc.go
@@ -193,11 +193,6 @@
 
 "help ..." recursively displays help for all commands and topics.
 
-Output is formatted to a target width in runes, determined by checking the
-CMDLINE_WIDTH environment variable, falling back on the terminal width, falling
-back on 80 chars.  By setting CMDLINE_WIDTH=x, if x > 0 the width is x, if x < 0
-the width is unlimited, and if x == 0 or is unset one of the fallbacks is used.
-
 Usage:
    namespace help [flags] [command/topic ...]
 
@@ -210,5 +205,9 @@
       full    - Good for cmdline output, shows all global flags.
       godoc   - Good for godoc processing.
    Override the default by setting the CMDLINE_STYLE environment variable.
+ -width=<terminal width>
+   Format output to this target width in runes, or unlimited if width < 0.
+   Defaults to the terminal width if available.  Override the default by setting
+   the CMDLINE_WIDTH environment variable.
 */
 package main
diff --git a/cmd/namespace/impl.go b/cmd/namespace/impl.go
index 9abaee2..5e4f1b6 100644
--- a/cmd/namespace/impl.go
+++ b/cmd/namespace/impl.go
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// The following enables go generate to generate the doc.go file.
+//go:generate go run $V23_ROOT/release/go/src/v.io/x/lib/cmdline/testdata/gendoc.go .
+
 package main
 
 import (
@@ -18,12 +21,15 @@
 	"v.io/v23/options"
 	"v.io/v23/security/access"
 	"v.io/v23/verror"
-	"v.io/x/lib/cmdline"
+	"v.io/x/lib/cmdline2"
 	"v.io/x/lib/vlog"
+	"v.io/x/ref/lib/v23cmd"
+	_ "v.io/x/ref/profiles"
 )
 
-func init() {
-	cmdline.HideGlobalFlagsExcept(regexp.MustCompile(`^v23\.namespace\.root$`))
+func main() {
+	cmdline2.HideGlobalFlagsExcept(regexp.MustCompile(`^v23\.namespace\.root$`))
+	cmdline2.Main(cmdRoot)
 }
 
 var (
@@ -33,8 +39,15 @@
 	flagDeleteSubtree       bool
 )
 
-var cmdGlob = &cmdline.Command{
-	Run:      runGlob,
+func init() {
+	cmdGlob.Flags.BoolVar(&flagLongGlob, "l", false, "Long listing format.")
+	cmdResolve.Flags.BoolVar(&flagInsecureResolve, "insecure", false, "Insecure mode: May return results from untrusted servers and invoke Resolve on untrusted mounttables")
+	cmdResolveToMT.Flags.BoolVar(&flagInsecureResolveToMT, "insecure", false, "Insecure mode: May return results from untrusted servers and invoke Resolve on untrusted mounttables")
+	cmdDelete.Flags.BoolVar(&flagDeleteSubtree, "r", false, "Delete all children of the name in addition to the name itself.")
+}
+
+var cmdGlob = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runGlob),
 	Name:     "glob",
 	Short:    "Returns all matching entries from the namespace",
 	Long:     "Returns all matching entries from the namespace.",
@@ -45,13 +58,13 @@
 `,
 }
 
-func runGlob(cmd *cmdline.Command, args []string) error {
+func runGlob(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, got := 1, len(args); expected != got {
-		return cmd.UsageErrorf("glob: incorrect number of arguments, expected %d, got %d", expected, got)
+		return env.UsageErrorf("glob: incorrect number of arguments, expected %d, got %d", expected, got)
 	}
 	pattern := args[0]
 
-	ctx, cancel := context.WithTimeout(gctx, time.Minute)
+	ctx, cancel := context.WithTimeout(ctx, time.Minute)
 	defer cancel()
 
 	ns := v23.GetNamespace(ctx)
@@ -66,14 +79,14 @@
 		for res := range c {
 			switch v := res.(type) {
 			case *naming.GlobReplyEntry:
-				fmt.Fprint(cmd.Stdout(), v.Value.Name)
+				fmt.Fprint(env.Stdout, v.Value.Name)
 				for _, s := range v.Value.Servers {
 					delta := s.Deadline.Time.Sub(time.Now())
-					fmt.Fprintf(cmd.Stdout(), " %s (Expires in %d sec)", s.Server, int(delta.Seconds()))
+					fmt.Fprintf(env.Stdout, " %s (Expires in %d sec)", s.Server, int(delta.Seconds()))
 				}
-				fmt.Fprintln(cmd.Stdout())
+				fmt.Fprintln(env.Stdout)
 			case *naming.GlobReplyError:
-				fmt.Fprintf(cmd.Stderr(), "Error: %s: %v\n", v.Value.Name, v.Value.Error)
+				fmt.Fprintf(env.Stderr, "Error: %s: %v\n", v.Value.Name, v.Value.Error)
 			}
 		}
 		return nil
@@ -97,16 +110,16 @@
 	}
 	sort.Strings(results)
 	for _, result := range results {
-		fmt.Fprintln(cmd.Stdout(), result)
+		fmt.Fprintln(env.Stdout, result)
 	}
 	for _, err := range errors {
-		fmt.Fprintf(cmd.Stderr(), "Error: %s: %v\n", err.Name, err.Error)
+		fmt.Fprintf(env.Stderr, "Error: %s: %v\n", err.Name, err.Error)
 	}
 	return nil
 }
 
-var cmdMount = &cmdline.Command{
-	Run:      runMount,
+var cmdMount = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runMount),
 	Name:     "mount",
 	Short:    "Adds a server to the namespace",
 	Long:     "Adds server <server> to the namespace with name <name>.",
@@ -119,9 +132,9 @@
 `,
 }
 
-func runMount(cmd *cmdline.Command, args []string) error {
+func runMount(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, got := 3, len(args); expected != got {
-		return cmd.UsageErrorf("mount: incorrect number of arguments, expected %d, got %d", expected, got)
+		return env.UsageErrorf("mount: incorrect number of arguments, expected %d, got %d", expected, got)
 	}
 	name := args[0]
 	server := args[1]
@@ -132,7 +145,7 @@
 		return fmt.Errorf("TTL parse error: %v", err)
 	}
 
-	ctx, cancel := context.WithTimeout(gctx, time.Minute)
+	ctx, cancel := context.WithTimeout(ctx, time.Minute)
 	defer cancel()
 
 	ns := v23.GetNamespace(ctx)
@@ -140,12 +153,12 @@
 		vlog.Infof("ns.Mount(%q, %q, %s) failed: %v", name, server, ttl, err)
 		return err
 	}
-	fmt.Fprintln(cmd.Stdout(), "Server mounted successfully.")
+	fmt.Fprintln(env.Stdout, "Server mounted successfully.")
 	return nil
 }
 
-var cmdUnmount = &cmdline.Command{
-	Run:      runUnmount,
+var cmdUnmount = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runUnmount),
 	Name:     "unmount",
 	Short:    "Removes a server from the namespace",
 	Long:     "Removes server <server> with name <name> from the namespace.",
@@ -156,14 +169,14 @@
 `,
 }
 
-func runUnmount(cmd *cmdline.Command, args []string) error {
+func runUnmount(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, got := 2, len(args); expected != got {
-		return cmd.UsageErrorf("unmount: incorrect number of arguments, expected %d, got %d", expected, got)
+		return env.UsageErrorf("unmount: incorrect number of arguments, expected %d, got %d", expected, got)
 	}
 	name := args[0]
 	server := args[1]
 
-	ctx, cancel := context.WithTimeout(gctx, time.Minute)
+	ctx, cancel := context.WithTimeout(ctx, time.Minute)
 	defer cancel()
 
 	ns := v23.GetNamespace(ctx)
@@ -172,12 +185,12 @@
 		vlog.Infof("ns.Unmount(%q, %q) failed: %v", name, server, err)
 		return err
 	}
-	fmt.Fprintln(cmd.Stdout(), "Server unmounted successfully.")
+	fmt.Fprintln(env.Stdout, "Server unmounted successfully.")
 	return nil
 }
 
-var cmdResolve = &cmdline.Command{
-	Run:      runResolve,
+var cmdResolve = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runResolve),
 	Name:     "resolve",
 	Short:    "Translates a object name to its object address(es)",
 	Long:     "Translates a object name to its object address(es).",
@@ -185,13 +198,13 @@
 	ArgsLong: "<name> is the name to resolve.",
 }
 
-func runResolve(cmd *cmdline.Command, args []string) error {
+func runResolve(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, got := 1, len(args); expected != got {
-		return cmd.UsageErrorf("resolve: incorrect number of arguments, expected %d, got %d", expected, got)
+		return env.UsageErrorf("resolve: incorrect number of arguments, expected %d, got %d", expected, got)
 	}
 	name := args[0]
 
-	ctx, cancel := context.WithTimeout(gctx, time.Minute)
+	ctx, cancel := context.WithTimeout(ctx, time.Minute)
 	defer cancel()
 
 	ns := v23.GetNamespace(ctx)
@@ -206,13 +219,13 @@
 		return err
 	}
 	for _, n := range me.Names() {
-		fmt.Fprintln(cmd.Stdout(), n)
+		fmt.Fprintln(env.Stdout, n)
 	}
 	return nil
 }
 
-var cmdResolveToMT = &cmdline.Command{
-	Run:      runResolveToMT,
+var cmdResolveToMT = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runResolveToMT),
 	Name:     "resolvetomt",
 	Short:    "Finds the address of the mounttable that holds an object name",
 	Long:     "Finds the address of the mounttable that holds an object name.",
@@ -220,13 +233,13 @@
 	ArgsLong: "<name> is the name to resolve.",
 }
 
-func runResolveToMT(cmd *cmdline.Command, args []string) error {
+func runResolveToMT(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, got := 1, len(args); expected != got {
-		return cmd.UsageErrorf("resolvetomt: incorrect number of arguments, expected %d, got %d", expected, got)
+		return env.UsageErrorf("resolvetomt: incorrect number of arguments, expected %d, got %d", expected, got)
 	}
 	name := args[0]
 
-	ctx, cancel := context.WithTimeout(gctx, time.Minute)
+	ctx, cancel := context.WithTimeout(ctx, time.Minute)
 	defer cancel()
 
 	ns := v23.GetNamespace(ctx)
@@ -240,12 +253,12 @@
 		return err
 	}
 	for _, s := range e.Servers {
-		fmt.Fprintln(cmd.Stdout(), naming.JoinAddressName(s.Server, e.Name))
+		fmt.Fprintln(env.Stdout, naming.JoinAddressName(s.Server, e.Name))
 	}
 	return nil
 }
 
-var cmdPermissions = &cmdline.Command{
+var cmdPermissions = &cmdline2.Command{
 	Name:  "permissions",
 	Short: "Manipulates permissions on an entry in the namespace",
 	Long: `
@@ -255,13 +268,13 @@
 The permissions are provided as an JSON-encoded version of the Permissions type
 defined in v.io/v23/security/access/types.vdl.
 `,
-	Children: []*cmdline.Command{cmdPermissionsGet, cmdPermissionsSet},
+	Children: []*cmdline2.Command{cmdPermissionsGet, cmdPermissionsSet},
 }
 
-var cmdPermissionsSet = &cmdline.Command{
-	Run:   runPermissionsSet,
-	Name:  "set",
-	Short: "Sets permissions on a mount name",
+var cmdPermissionsSet = &cmdline2.Command{
+	Runner: v23cmd.RunnerFunc(runPermissionsSet),
+	Name:   "set",
+	Short:  "Sets permissions on a mount name",
 	Long: `
 Set replaces the permissions controlling usage of a mount name.
 `,
@@ -274,9 +287,9 @@
 `,
 }
 
-func runPermissionsSet(cmd *cmdline.Command, args []string) error {
+func runPermissionsSet(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, got := 2, len(args); expected != got {
-		return cmd.UsageErrorf("set: incorrect number of arguments, expected %d, got %d", expected, got)
+		return env.UsageErrorf("set: incorrect number of arguments, expected %d, got %d", expected, got)
 	}
 	name := args[0]
 	var perms access.Permissions
@@ -291,7 +304,7 @@
 	if err := json.NewDecoder(file).Decode(&perms); err != nil {
 		return err
 	}
-	ctx, cancel := context.WithTimeout(gctx, time.Minute)
+	ctx, cancel := context.WithTimeout(ctx, time.Minute)
 	defer cancel()
 	ns := v23.GetNamespace(ctx)
 	for {
@@ -307,8 +320,8 @@
 	}
 }
 
-var cmdPermissionsGet = &cmdline.Command{
-	Run:      runPermissionsGet,
+var cmdPermissionsGet = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runPermissionsGet),
 	Name:     "get",
 	Short:    "Gets permissions on a mount name",
 	ArgsName: "<name>",
@@ -323,23 +336,23 @@
 `,
 }
 
-func runPermissionsGet(cmd *cmdline.Command, args []string) error {
+func runPermissionsGet(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, got := 1, len(args); expected != got {
-		return cmd.UsageErrorf("get: incorrect number of arguments, expected %d, got %d", expected, got)
+		return env.UsageErrorf("get: incorrect number of arguments, expected %d, got %d", expected, got)
 	}
 	name := args[0]
-	ctx, cancel := context.WithTimeout(gctx, time.Minute)
+	ctx, cancel := context.WithTimeout(ctx, time.Minute)
 	defer cancel()
 
 	perms, _, err := v23.GetNamespace(ctx).GetPermissions(ctx, name)
 	if err != nil {
 		return err
 	}
-	return json.NewEncoder(cmd.Stdout()).Encode(perms)
+	return json.NewEncoder(env.Stdout).Encode(perms)
 }
 
-var cmdDelete = &cmdline.Command{
-	Run:      runDelete,
+var cmdDelete = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runDelete),
 	Name:     "delete",
 	Short:    "Deletes a name from the namespace",
 	ArgsName: "<name>",
@@ -347,26 +360,21 @@
 	Long:     "Deletes a name from the namespace.",
 }
 
-func runDelete(cmd *cmdline.Command, args []string) error {
+func runDelete(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, got := 1, len(args); expected != got {
-		return cmd.UsageErrorf("delete: incorrect number of arguments, expected %d, got %d", expected, got)
+		return env.UsageErrorf("delete: incorrect number of arguments, expected %d, got %d", expected, got)
 	}
 	name := args[0]
-	ctx, cancel := context.WithTimeout(gctx, time.Minute)
+	ctx, cancel := context.WithTimeout(ctx, time.Minute)
 	defer cancel()
 
 	return v23.GetNamespace(ctx).Delete(ctx, name, flagDeleteSubtree)
 }
 
-func root() *cmdline.Command {
-	cmdGlob.Flags.BoolVar(&flagLongGlob, "l", false, "Long listing format.")
-	cmdResolve.Flags.BoolVar(&flagInsecureResolve, "insecure", false, "Insecure mode: May return results from untrusted servers and invoke Resolve on untrusted mounttables")
-	cmdResolveToMT.Flags.BoolVar(&flagInsecureResolveToMT, "insecure", false, "Insecure mode: May return results from untrusted servers and invoke Resolve on untrusted mounttables")
-	cmdDelete.Flags.BoolVar(&flagDeleteSubtree, "r", false, "Delete all children of the name in addition to the name itself.")
-	return &cmdline.Command{
-		Name:  "namespace",
-		Short: "resolves and manages names in the Vanadium namespace",
-		Long: `
+var cmdRoot = &cmdline2.Command{
+	Name:  "namespace",
+	Short: "resolves and manages names in the Vanadium namespace",
+	Long: `
 Command namespace resolves and manages names in the Vanadium namespace.
 
 The namespace roots are set from the command line via --v23.namespace.root
@@ -374,6 +382,5 @@
 with V23_NAMESPACE, e.g.  V23_NAMESPACE, V23_NAMESPACE_2, V23_NAMESPACE_GOOGLE,
 etc.  The command line options override the environment.
 `,
-		Children: []*cmdline.Command{cmdGlob, cmdMount, cmdUnmount, cmdResolve, cmdResolveToMT, cmdPermissions, cmdDelete},
-	}
+	Children: []*cmdline2.Command{cmdGlob, cmdMount, cmdUnmount, cmdResolve, cmdResolveToMT, cmdPermissions, cmdDelete},
 }
diff --git a/cmd/namespace/main.go b/cmd/namespace/main.go
deleted file mode 100644
index e33dc99..0000000
--- a/cmd/namespace/main.go
+++ /dev/null
@@ -1,27 +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 following enables go generate to generate the doc.go file.
-//go:generate go run $V23_ROOT/release/go/src/v.io/x/lib/cmdline/testdata/gendoc.go .
-
-package main
-
-import (
-	"os"
-
-	"v.io/v23"
-	"v.io/v23/context"
-
-	_ "v.io/x/ref/profiles"
-)
-
-var gctx *context.T
-
-func main() {
-	var shutdown v23.Shutdown
-	gctx, shutdown = v23.Init()
-	exitCode := root().Main()
-	shutdown()
-	os.Exit(exitCode)
-}
diff --git a/cmd/principal/bless.go b/cmd/principal/bless.go
index 3ed3ed9..80db361 100644
--- a/cmd/principal/bless.go
+++ b/cmd/principal/bless.go
@@ -59,7 +59,7 @@
 	return service, macaroon, root, nil
 }
 
-func getMacaroonForBlessRPC(blessServerURL string, blessedChan <-chan string, browser bool) (<-chan string, error) {
+func getMacaroonForBlessRPC(key security.PublicKey, blessServerURL string, blessedChan <-chan string, browser bool) (<-chan string, error) {
 	// Setup a HTTP server to recieve a blessing macaroon from the identity server.
 	// Steps:
 	// 1. Generate a state token to be included in the HTTP request
@@ -118,7 +118,7 @@
 	go http.Serve(ln, nil)
 
 	// Print the link to start the flow.
-	url, err := seekBlessingsURL(blessServerURL, redirectURL, state)
+	url, err := seekBlessingsURL(key, blessServerURL, redirectURL, state)
 	if err != nil {
 		return nil, fmt.Errorf("failed to create seekBlessingsURL: %s", err)
 	}
@@ -135,14 +135,19 @@
 	return result, nil
 }
 
-func seekBlessingsURL(blessServerURL, redirectURL, state string) (string, error) {
+func seekBlessingsURL(key security.PublicKey, blessServerURL, redirectURL, state string) (string, error) {
 	baseURL, err := url.Parse(joinURL(blessServerURL, identity.SeekBlessingsRoute))
 	if err != nil {
 		return "", fmt.Errorf("failed to parse url: %v", err)
 	}
+	keyBytes, err := key.MarshalBinary()
+	if err != nil {
+		return "", fmt.Errorf("failed to marshal public key: %v", err)
+	}
 	params := url.Values{}
 	params.Add("redirect_url", redirectURL)
 	params.Add("state", state)
+	params.Add("public_key", base64.URLEncoding.EncodeToString(keyBytes))
 	baseURL.RawQuery = params.Encode()
 	return baseURL.String(), nil
 }
diff --git a/cmd/principal/caveat.go b/cmd/principal/caveat.go
index 2f98264..aa77faa 100644
--- a/cmd/principal/caveat.go
+++ b/cmd/principal/caveat.go
@@ -121,7 +121,7 @@
 	for _, p := range pkgs {
 		build.BuildPackage(p, env)
 		if !env.Errors.IsEmpty() {
-			return fmt.Errorf("failed to build package(%s): %s", p, env.Errors)
+			return fmt.Errorf("failed to build package(%v): %s", p, env.Errors)
 		}
 	}
 	return nil
diff --git a/cmd/principal/doc.go b/cmd/principal/doc.go
index ae4f429..4910fb7 100644
--- a/cmd/principal/doc.go
+++ b/cmd/principal/doc.go
@@ -90,8 +90,10 @@
 Usage:
    principal create [flags] <directory> <blessing>
 
-	<directory> is the directory to which the new principal will be persisted.
-	<blessing> is the self-blessed blessing that the principal will be setup to use by default.
+<directory> is the directory to which the new principal will be persisted.
+
+<blessing> is the self-blessed blessing that the principal will be setup to use
+by default.
 
 The principal create flags are:
  -overwrite=false
@@ -114,8 +116,9 @@
 Usage:
    principal fork [flags] <directory> <extension>
 
-	<directory> is the directory to which the forked principal will be persisted.
-	<extension> is the extension under which the forked principal is blessed.
+<directory> is the directory to which the forked principal will be persisted.
+
+<extension> is the extension under which the forked principal is blessed.
 
 The principal fork flags are:
  -caveat=[]
@@ -515,11 +518,6 @@
 
 "help ..." recursively displays help for all commands and topics.
 
-Output is formatted to a target width in runes, determined by checking the
-CMDLINE_WIDTH environment variable, falling back on the terminal width, falling
-back on 80 chars.  By setting CMDLINE_WIDTH=x, if x > 0 the width is x, if x < 0
-the width is unlimited, and if x == 0 or is unset one of the fallbacks is used.
-
 Usage:
    principal help [flags] [command/topic ...]
 
@@ -532,5 +530,9 @@
       full    - Good for cmdline output, shows all global flags.
       godoc   - Good for godoc processing.
    Override the default by setting the CMDLINE_STYLE environment variable.
+ -width=<terminal width>
+   Format output to this target width in runes, or unlimited if width < 0.
+   Defaults to the terminal width if available.  Override the default by setting
+   the CMDLINE_WIDTH environment variable.
 */
 package main
diff --git a/cmd/principal/main.go b/cmd/principal/main.go
index 2c76e3a..714ec5b 100644
--- a/cmd/principal/main.go
+++ b/cmd/principal/main.go
@@ -27,9 +27,10 @@
 	"v.io/v23/rpc"
 	"v.io/v23/security"
 	"v.io/v23/vom"
-	"v.io/x/lib/cmdline"
+	"v.io/x/lib/cmdline2"
 	"v.io/x/ref/envvar"
 	vsecurity "v.io/x/ref/lib/security"
+	"v.io/x/ref/lib/v23cmd"
 	_ "v.io/x/ref/profiles/static"
 )
 
@@ -80,17 +81,14 @@
 
 	errNoCaveats = fmt.Errorf("no caveats provided: it is generally dangerous to bless another principal without any caveats as that gives them almost unrestricted access to the blesser's credentials. If you really want to do this, set --require-caveats=false")
 
-	cmdDump = &cmdline.Command{
+	cmdDump = &cmdline2.Command{
 		Name:  "dump",
 		Short: "Dump out information about the principal",
 		Long: `
 Prints out information about the principal specified by the environment
 that this tool is running in.
 `,
-		Run: func(cmd *cmdline.Command, args []string) error {
-			ctx, shutdown := v23.Init()
-			defer shutdown()
-
+		Runner: v23cmd.RunnerFunc(func(ctx *context.T, env *cmdline2.Env, args []string) error {
 			p := v23.GetPrincipal(ctx)
 			if flagDumpShort {
 				fmt.Printf("%v\n", p.BlessingStore().Default())
@@ -102,10 +100,10 @@
 			fmt.Println("---------------- BlessingRoots ----------------")
 			fmt.Printf("%v", p.Roots().DebugString())
 			return nil
-		},
+		}),
 	}
 
-	cmdDumpBlessings = &cmdline.Command{
+	cmdDumpBlessings = &cmdline2.Command{
 		Name:  "dumpblessings",
 		Short: "Dump out information about the provided blessings",
 		Long: `
@@ -117,7 +115,7 @@
 <file> is the path to a file containing blessings typically obtained from
 this tool. - is used for STDIN.
 `,
-		Run: func(cmd *cmdline.Command, args []string) error {
+		Runner: cmdline2.RunnerFunc(func(env *cmdline2.Env, args []string) error {
 			if len(args) != 1 {
 				return fmt.Errorf("requires exactly one argument, <file>, provided %d", len(args))
 			}
@@ -149,10 +147,10 @@
 				}
 			}
 			return nil
-		},
+		}),
 	}
 
-	cmdBlessSelf = &cmdline.Command{
+	cmdBlessSelf = &cmdline2.Command{
 		Name:  "blessself",
 		Short: "Generate a self-signed blessing",
 		Long: `
@@ -167,7 +165,7 @@
 specified, a name will be generated based on the hostname of the
 machine and the name of the user running this command.
 `,
-		Run: func(cmd *cmdline.Command, args []string) error {
+		Runner: v23cmd.RunnerFunc(func(ctx *context.T, env *cmdline2.Env, args []string) error {
 			var name string
 			switch len(args) {
 			case 0:
@@ -177,13 +175,10 @@
 			default:
 				return fmt.Errorf("requires at most one argument, provided %d", len(args))
 			}
-
 			caveats, err := caveatsFromFlags(flagBlessSelfFor, &flagBlessSelfCaveats)
 			if err != nil {
 				return err
 			}
-			ctx, shutdown := v23.Init()
-			defer shutdown()
 			principal := v23.GetPrincipal(ctx)
 			blessing, err := principal.BlessSelf(name, caveats...)
 			if err != nil {
@@ -191,10 +186,10 @@
 			}
 
 			return dumpBlessings(blessing)
-		},
+		}),
 	}
 
-	cmdBless = &cmdline.Command{
+	cmdBless = &cmdline2.Command{
 		Name:  "bless",
 		Short: "Bless another principal",
 		Long: `
@@ -238,16 +233,12 @@
 blessing.
 
 	`,
-		Run: func(cmd *cmdline.Command, args []string) error {
+		Runner: v23cmd.RunnerFunc(func(ctx *context.T, env *cmdline2.Env, args []string) error {
 			if len(flagRemoteArgFile) > 0 && len(args) != 1 {
 				return fmt.Errorf("when --remote-arg-file is provided, only <extension> is expected, provided %d", len(args))
 			} else if len(flagRemoteArgFile) == 0 && len(args) != 2 {
 				return fmt.Errorf("require exactly two arguments when --remote-arg-file is not provided, provided %d", len(args))
 			}
-
-			ctx, shutdown := v23.Init()
-			defer shutdown()
-
 			p := v23.GetPrincipal(ctx)
 
 			var (
@@ -290,10 +281,10 @@
 				return err
 			}
 			return dumpBlessings(blessings)
-		},
+		}),
 	}
 
-	cmdGetPublicKey = &cmdline.Command{
+	cmdGetPublicKey = &cmdline2.Command{
 		Name:  "publickey",
 		Short: "Prints the public key of the principal.",
 		Long: `
@@ -308,9 +299,7 @@
 for humans to read and is used in output of other commands in this program, but
 is not suitable as an argument to the 'recognize' command.
 `,
-		Run: func(cmd *cmdline.Command, args []string) error {
-			ctx, shutdown := v23.Init()
-			defer shutdown()
+		Runner: v23cmd.RunnerFunc(func(ctx *context.T, env *cmdline2.Env, args []string) error {
 			key := v23.GetPrincipal(ctx).PublicKey()
 			if flagGetPublicKeyPretty {
 				fmt.Println(key)
@@ -322,10 +311,10 @@
 			}
 			fmt.Println(base64.URLEncoding.EncodeToString(der))
 			return nil
-		},
+		}),
 	}
 
-	cmdGetTrustedRoots = &cmdline.Command{
+	cmdGetTrustedRoots = &cmdline2.Command{
 		Name:  "recognizedroots",
 		Short: "Return recognized blessings, and their associated public key.",
 		Long: `
@@ -334,15 +323,13 @@
 appear on this list. If the principal is operating as a server, clients must
 present blessings derived from this list.
 `,
-		Run: func(cmd *cmdline.Command, args []string) error {
-			ctx, shutdown := v23.Init()
-			defer shutdown()
+		Runner: v23cmd.RunnerFunc(func(ctx *context.T, env *cmdline2.Env, args []string) error {
 			fmt.Printf(v23.GetPrincipal(ctx).Roots().DebugString())
 			return nil
-		},
+		}),
 	}
 
-	cmdGetPeerMap = &cmdline.Command{
+	cmdGetPeerMap = &cmdline2.Command{
 		Name:  "peermap",
 		Short: "Shows the map from peer pattern to which blessing name to present.",
 		Long: `
@@ -351,15 +338,13 @@
 If the principal operates as a client, it presents the map value associated with
 the peer it contacts.
 `,
-		Run: func(cmd *cmdline.Command, args []string) error {
-			ctx, shutdown := v23.Init()
-			defer shutdown()
+		Runner: v23cmd.RunnerFunc(func(ctx *context.T, env *cmdline2.Env, args []string) error {
 			fmt.Printf(v23.GetPrincipal(ctx).BlessingStore().DebugString())
 			return nil
-		},
+		}),
 	}
 
-	cmdGetForPeer = &cmdline.Command{
+	cmdGetForPeer = &cmdline2.Command{
 		Name:  "forpeer",
 		Short: "Return blessings marked for the provided peer",
 		Long: `
@@ -380,14 +365,12 @@
 store.forpeer returns the blessings that are marked for all peers (i.e.,
 blessings set on the store with the "..." pattern).
 `,
-		Run: func(cmd *cmdline.Command, args []string) error {
-			ctx, shutdown := v23.Init()
-			defer shutdown()
+		Runner: v23cmd.RunnerFunc(func(ctx *context.T, env *cmdline2.Env, args []string) error {
 			return printBlessingsInfo(v23.GetPrincipal(ctx).BlessingStore().ForPeer(args...))
-		},
+		}),
 	}
 
-	cmdGetDefault = &cmdline.Command{
+	cmdGetDefault = &cmdline2.Command{
 		Name:  "default",
 		Short: "Return blessings marked as default",
 		Long: `
@@ -399,14 +382,12 @@
 Providing --caveats <chain_name> will print the caveats on the certificate chain
 with chain_name.
 `,
-		Run: func(cmd *cmdline.Command, args []string) error {
-			ctx, shutdown := v23.Init()
-			defer shutdown()
+		Runner: v23cmd.RunnerFunc(func(ctx *context.T, env *cmdline2.Env, args []string) error {
 			return printBlessingsInfo(v23.GetPrincipal(ctx).BlessingStore().Default())
-		},
+		}),
 	}
 
-	cmdSetForPeer = &cmdline.Command{
+	cmdSetForPeer = &cmdline2.Command{
 		Name:  "forpeer",
 		Short: "Set provided blessings for peer",
 		Long: `
@@ -431,7 +412,7 @@
 <pattern> is the BlessingPattern used to identify peers with whom this
 blessing can be shared with.
 `,
-		Run: func(cmd *cmdline.Command, args []string) error {
+		Runner: v23cmd.RunnerFunc(func(ctx *context.T, env *cmdline2.Env, args []string) error {
 			if len(args) != 2 {
 				return fmt.Errorf("requires exactly two arguments <file>, <pattern>, provided %d", len(args))
 			}
@@ -441,9 +422,6 @@
 			}
 			pattern := security.BlessingPattern(args[1])
 
-			ctx, shutdown := v23.Init()
-			defer shutdown()
-
 			p := v23.GetPrincipal(ctx)
 			if _, err := p.BlessingStore().Set(blessings, pattern); err != nil {
 				return fmt.Errorf("failed to set blessings %v for peers %v: %v", blessings, pattern, err)
@@ -454,10 +432,10 @@
 				}
 			}
 			return nil
-		},
+		}),
 	}
 
-	cmdRecognize = &cmdline.Command{
+	cmdRecognize = &cmdline2.Command{
 		Name:  "recognize",
 		Short: "Add to the set of identity providers recognized by this principal",
 		Long: `
@@ -485,13 +463,10 @@
 
 <blessing pattern> is the blessing pattern for which <key> should be recognized.
 `,
-		Run: func(cmd *cmdline.Command, args []string) error {
+		Runner: v23cmd.RunnerFunc(func(ctx *context.T, env *cmdline2.Env, args []string) error {
 			if len(args) != 1 && len(args) != 2 {
 				return fmt.Errorf("requires either one argument <file>, or two arguments <key> <blessing pattern>, provided %d", len(args))
 			}
-			ctx, shutdown := v23.Init()
-			defer shutdown()
-
 			p := v23.GetPrincipal(ctx)
 			if len(args) == 1 {
 				blessings, err := decodeBlessings(args[0])
@@ -513,10 +488,10 @@
 				return fmt.Errorf("invalid DER encoding of public key: %v", err)
 			}
 			return p.Roots().Add(key, security.BlessingPattern(args[0]))
-		},
+		}),
 	}
 
-	cmdSetDefault = &cmdline.Command{
+	cmdSetDefault = &cmdline2.Command{
 		Name:  "default",
 		Short: "Set provided blessings as default",
 		Long: `
@@ -531,7 +506,7 @@
 <file> is the path to a file containing a blessing typically obtained from
 this tool. - is used for STDIN.
 `,
-		Run: func(cmd *cmdline.Command, args []string) error {
+		Runner: v23cmd.RunnerFunc(func(ctx *context.T, env *cmdline2.Env, args []string) error {
 			if len(args) != 1 {
 				return fmt.Errorf("requires exactly one argument, <file>, provided %d", len(args))
 			}
@@ -540,9 +515,6 @@
 				return fmt.Errorf("failed to decode provided blessings: %v", err)
 			}
 
-			ctx, shutdown := v23.Init()
-			defer shutdown()
-
 			p := v23.GetPrincipal(ctx)
 			if err := p.BlessingStore().SetDefault(blessings); err != nil {
 				return fmt.Errorf("failed to set blessings %v as default: %v", blessings, err)
@@ -553,10 +525,10 @@
 				}
 			}
 			return nil
-		},
+		}),
 	}
 
-	cmdCreate = &cmdline.Command{
+	cmdCreate = &cmdline2.Command{
 		Name:  "create",
 		Short: "Create a new principal and persist it into a directory",
 		Long: `
@@ -570,10 +542,11 @@
 `,
 		ArgsName: "<directory> <blessing>",
 		ArgsLong: `
-	<directory> is the directory to which the new principal will be persisted.
-	<blessing> is the self-blessed blessing that the principal will be setup to use by default.
+<directory> is the directory to which the new principal will be persisted.
+
+<blessing> is the self-blessed blessing that the principal will be setup to use by default.
 	`,
-		Run: func(cmd *cmdline.Command, args []string) error {
+		Runner: cmdline2.RunnerFunc(func(env *cmdline2.Env, args []string) error {
 			if len(args) != 2 {
 				return fmt.Errorf("requires exactly two arguments: <directory> and <blessing>, provided %d", len(args))
 			}
@@ -595,10 +568,10 @@
 				return fmt.Errorf("could not set blessings %v as default: %v", blessings, err)
 			}
 			return nil
-		},
+		}),
 	}
 
-	cmdFork = &cmdline.Command{
+	cmdFork = &cmdline2.Command{
 		Name:  "fork",
 		Short: "Fork a new principal from the principal that this tool is running as and persist it into a directory",
 		Long: `
@@ -616,18 +589,15 @@
 `,
 		ArgsName: "<directory> <extension>",
 		ArgsLong: `
-	<directory> is the directory to which the forked principal will be persisted.
-	<extension> is the extension under which the forked principal is blessed.
+<directory> is the directory to which the forked principal will be persisted.
+
+<extension> is the extension under which the forked principal is blessed.
 	`,
-		Run: func(cmd *cmdline.Command, args []string) error {
+		Runner: v23cmd.RunnerFunc(func(ctx *context.T, env *cmdline2.Env, args []string) error {
 			if len(args) != 2 {
 				return fmt.Errorf("requires exactly two arguments: <directory> and <extension>, provided %d", len(args))
 			}
 			dir, extension := args[0], args[1]
-
-			ctx, shutdown := v23.Init()
-			defer shutdown()
-
 			caveats, err := caveatsFromFlags(flagForkFor, &flagForkCaveats)
 			if err != nil {
 				return err
@@ -667,10 +637,10 @@
 				return fmt.Errorf("could not set blessings %v as default: %v", blessings, err)
 			}
 			return nil
-		},
+		}),
 	}
 
-	cmdSeekBlessings = &cmdline.Command{
+	cmdSeekBlessings = &cmdline2.Command{
 		Name:  "seekblessings",
 		Short: "Seek blessings from a web-based Vanadium blessing service",
 		Long: `
@@ -685,15 +655,12 @@
 set to true, and are also set for sharing with all peers, unless a more
 specific peer pattern is provided using the --for-peer flag.
 `,
-		Run: func(cmd *cmdline.Command, args []string) error {
-			// Initialize the runtime first so that any local errors are reported
-			// before the HTTP roundtrips for obtaining the macaroon begin.
-			ctx, shutdown := v23.Init()
-			defer shutdown()
+		Runner: v23cmd.RunnerFunc(func(ctx *context.T, env *cmdline2.Env, args []string) error {
+			p := v23.GetPrincipal(ctx)
 
 			blessedChan := make(chan string)
 			defer close(blessedChan)
-			macaroonChan, err := getMacaroonForBlessRPC(flagSeekBlessingsFrom, blessedChan, flagSeekBlessingsBrowser)
+			macaroonChan, err := getMacaroonForBlessRPC(p.PublicKey(), flagSeekBlessingsFrom, blessedChan, flagSeekBlessingsBrowser)
 			if err != nil {
 				return fmt.Errorf("failed to get macaroon from Vanadium blesser: %v", err)
 			}
@@ -706,8 +673,6 @@
 			// Wait for getTokenForBlessRPC to clean up:
 			<-macaroonChan
 
-			p := v23.GetPrincipal(ctx)
-
 			if flagSeekBlessingsSetDefault {
 				if err := p.BlessingStore().SetDefault(blessings); err != nil {
 					return fmt.Errorf("failed to set blessings %v as default: %v", blessings, err)
@@ -723,12 +688,12 @@
 					return fmt.Errorf("AddToRoots failed: %v", err)
 				}
 			}
-			fmt.Fprintf(cmd.Stdout(), "Received blessings: %v\n", blessings)
+			fmt.Fprintf(env.Stdout, "Received blessings: %v\n", blessings)
 			return nil
-		},
+		}),
 	}
 
-	cmdRecvBlessings = &cmdline.Command{
+	cmdRecvBlessings = &cmdline2.Command{
 		Name:  "recvblessings",
 		Short: "Receive blessings sent by another principal and use them as the default",
 		Long: `
@@ -768,14 +733,10 @@
 		principal bless --remote-arg-file FILE EXTENSION
 
 `,
-		Run: func(cmd *cmdline.Command, args []string) error {
+		Runner: v23cmd.RunnerFunc(func(ctx *context.T, env *cmdline2.Env, args []string) error {
 			if len(args) != 0 {
 				return fmt.Errorf("command accepts no arguments")
 			}
-
-			ctx, shutdown := v23.Init()
-			defer shutdown()
-
 			server, err := v23.NewServer(ctx)
 			if err != nil {
 				return fmt.Errorf("failed to create server to listen for blessings: %v", err)
@@ -817,7 +778,7 @@
 			fmt.Println()
 			fmt.Println("...waiting for sender..")
 			return <-service.notify
-		},
+		}),
 	}
 )
 
@@ -859,9 +820,9 @@
 }
 
 type recvBlessingsInfo struct {
-	RemoteKey   string `json:remote_key`
-	RemoteToken string `json:remote_token`
-	Name        string `json:name`
+	RemoteKey   string `json:"remote_key"`
+	RemoteToken string `json:"remote_token"`
+	Name        string `json:"name"`
 }
 
 func writeRecvBlessingsInfo(fname string, remoteKey, remoteToken, name string) error {
@@ -892,7 +853,7 @@
 }
 
 func main() {
-	cmdline.HideGlobalFlagsExcept()
+	cmdline2.HideGlobalFlagsExcept()
 	cmdBlessSelf.Flags.Var(&flagBlessSelfCaveats, "caveat", flagBlessSelfCaveats.usage())
 	cmdBlessSelf.Flags.DurationVar(&flagBlessSelfFor, "for", 0, "Duration of blessing validity (zero implies no expiration)")
 
@@ -942,7 +903,7 @@
 
 	cmdGetPublicKey.Flags.BoolVar(&flagGetPublicKeyPretty, "pretty", false, "If true, print the key out in a more human-readable but lossy representation.")
 
-	cmdSet := &cmdline.Command{
+	cmdSet := &cmdline2.Command{
 		Name:  "set",
 		Short: "Mutate the principal's blessings.",
 		Long: `
@@ -951,10 +912,10 @@
 All input blessings are expected to be serialized using base64-VOM-encoding.
 See 'principal get'.
 `,
-		Children: []*cmdline.Command{cmdSetDefault, cmdSetForPeer},
+		Children: []*cmdline2.Command{cmdSetDefault, cmdSetForPeer},
 	}
 
-	cmdGet := &cmdline.Command{
+	cmdGet := &cmdline2.Command{
 		Name:  "get",
 		Short: "Read the principal's blessings.",
 		Long: `
@@ -962,10 +923,10 @@
 
 All blessings are printed to stdout using base64-VOM-encoding.
 `,
-		Children: []*cmdline.Command{cmdGetDefault, cmdGetForPeer, cmdGetPublicKey, cmdGetTrustedRoots, cmdGetPeerMap},
+		Children: []*cmdline2.Command{cmdGetDefault, cmdGetForPeer, cmdGetPublicKey, cmdGetTrustedRoots, cmdGetPeerMap},
 	}
 
-	root := &cmdline.Command{
+	root := &cmdline2.Command{
 		Name:  "principal",
 		Short: "creates and manages Vanadium principals and blessings",
 		Long: `
@@ -973,9 +934,9 @@
 
 All objects are printed using base64-VOM-encoding.
 `,
-		Children: []*cmdline.Command{cmdCreate, cmdFork, cmdSeekBlessings, cmdRecvBlessings, cmdDump, cmdDumpBlessings, cmdBlessSelf, cmdBless, cmdSet, cmdGet, cmdRecognize},
+		Children: []*cmdline2.Command{cmdCreate, cmdFork, cmdSeekBlessings, cmdRecvBlessings, cmdDump, cmdDumpBlessings, cmdBlessSelf, cmdBless, cmdSet, cmdGet, cmdRecognize},
 	}
-	os.Exit(root.Main())
+	cmdline2.Main(root)
 }
 
 func decodeBlessings(fname string) (security.Blessings, error) {
diff --git a/cmd/uniqueid/doc.go b/cmd/uniqueid/doc.go
index ce5859d..08b1405 100644
--- a/cmd/uniqueid/doc.go
+++ b/cmd/uniqueid/doc.go
@@ -18,51 +18,8 @@
    help        Display help for commands or topics
 
 The global flags are:
- -alsologtostderr=true
-   log to standard error as well as files
- -log_backtrace_at=:0
-   when logging hits line file:N, emit a stack trace
- -log_dir=
-   if non-empty, write log files to this directory
- -logtostderr=false
-   log to standard error instead of files
- -max_stack_buf_size=4292608
-   max size in bytes of the buffer to use for logging stack traces
- -stderrthreshold=2
-   logs at or above this threshold go to stderr
- -v=0
-   log level for V logs
- -v23.credentials=
-   directory to use for storing security credentials
- -v23.i18n-catalogue=
-   18n catalogue files to load, comma separated
  -v23.metadata=<just specify -v23.metadata to activate>
    Displays metadata for the program and exits.
- -v23.namespace.root=[/(dev.v.io/role/vprod/service/mounttabled)@ns.dev.v.io:8101]
-   local namespace root; can be repeated to provided multiple roots
- -v23.permissions.file=map[]
-   specify a perms file as <name>:<permsfile>
- -v23.permissions.literal=
-   explicitly specify the runtime perms as a JSON-encoded access.Permissions.
-   Overrides all --v23.permissions.file flags.
- -v23.proxy=
-   object name of proxy service to use to export services across network
-   boundaries
- -v23.tcp.address=
-   address to listen on
- -v23.tcp.protocol=wsh
-   protocol to listen with
- -v23.vtrace.cache-size=1024
-   The number of vtrace traces to store in memory.
- -v23.vtrace.collect-regexp=
-   Spans and annotations that match this regular expression will trigger trace
-   collection.
- -v23.vtrace.dump-on-shutdown=true
-   If true, dump all stored traces on runtime shutdown.
- -v23.vtrace.sample-rate=0
-   Rate (from 0.0 to 1.0) to sample vtrace traces.
- -vmodule=
-   comma-separated list of pattern=N settings for file-filtered logging
 
 Uniqueid generate
 
@@ -89,11 +46,6 @@
 
 "help ..." recursively displays help for all commands and topics.
 
-Output is formatted to a target width in runes, determined by checking the
-CMDLINE_WIDTH environment variable, falling back on the terminal width, falling
-back on 80 chars.  By setting CMDLINE_WIDTH=x, if x > 0 the width is x, if x < 0
-the width is unlimited, and if x == 0 or is unset one of the fallbacks is used.
-
 Usage:
    uniqueid help [flags] [command/topic ...]
 
@@ -106,5 +58,9 @@
       full    - Good for cmdline output, shows all global flags.
       godoc   - Good for godoc processing.
    Override the default by setting the CMDLINE_STYLE environment variable.
+ -width=<terminal width>
+   Format output to this target width in runes, or unlimited if width < 0.
+   Defaults to the terminal width if available.  Override the default by setting
+   the CMDLINE_WIDTH environment variable.
 */
 package main
diff --git a/cmd/uniqueid/main.go b/cmd/uniqueid/main.go
index 9cd2737..3d27018 100644
--- a/cmd/uniqueid/main.go
+++ b/cmd/uniqueid/main.go
@@ -11,43 +11,31 @@
 	"bytes"
 	"fmt"
 	"io/ioutil"
-	"os"
 	"regexp"
 
-	"v.io/v23"
 	"v.io/v23/uniqueid"
-	"v.io/x/lib/cmdline"
-	_ "v.io/x/ref/profiles/static"
+	"v.io/x/lib/cmdline2"
 )
 
 func main() {
-	cmdline.HideGlobalFlagsExcept()
-	os.Exit(cmdUniqueId.Main())
+	cmdline2.Main(cmdUniqueId)
 }
 
-func runHelper(run cmdline.Runner) cmdline.Runner {
-	return func(cmd *cmdline.Command, args []string) error {
-		_, shutdown := v23.Init()
-		defer shutdown()
-		return run(cmd, args)
-	}
-}
-
-var cmdUniqueId = &cmdline.Command{
+var cmdUniqueId = &cmdline2.Command{
 	Name:  "uniqueid",
 	Short: "generates unique identifiers",
 	Long: `
 Command uniqueid generates unique identifiers.
 It also has an option of automatically substituting unique ids with placeholders in files.
 `,
-	Children: []*cmdline.Command{cmdGenerate, cmdInject},
-	Topics:   []cmdline.Topic{},
+	Children: []*cmdline2.Command{cmdGenerate, cmdInject},
+	Topics:   []cmdline2.Topic{},
 }
 
-var cmdGenerate = &cmdline.Command{
-	Run:   runHelper(runGenerate),
-	Name:  "generate",
-	Short: "Generates UniqueIds",
+var cmdGenerate = &cmdline2.Command{
+	Runner: cmdline2.RunnerFunc(runGenerate),
+	Name:   "generate",
+	Short:  "Generates UniqueIds",
 	Long: `
 Generates unique ids and outputs them to standard out.
 `,
@@ -55,10 +43,10 @@
 	ArgsLong: "",
 }
 
-var cmdInject = &cmdline.Command{
-	Run:   runHelper(runInject),
-	Name:  "inject",
-	Short: "Injects UniqueIds into existing files",
+var cmdInject = &cmdline2.Command{
+	Runner: cmdline2.RunnerFunc(runInject),
+	Name:   "inject",
+	Short:  "Injects UniqueIds into existing files",
 	Long: `
 Injects UniqueIds into existing files.
 Strings of the form "$UNIQUEID$" will be replaced with generated ids.
@@ -68,9 +56,9 @@
 }
 
 // runGenerate implements the generate command which outputs generated ids to stdout.
-func runGenerate(command *cmdline.Command, args []string) error {
+func runGenerate(env *cmdline2.Env, args []string) error {
 	if len(args) > 0 {
-		return command.UsageErrorf("expected 0 args, got %d", len(args))
+		return env.UsageErrorf("expected 0 args, got %d", len(args))
 	}
 	id, err := uniqueid.Random()
 	if err != nil {
@@ -81,9 +69,9 @@
 }
 
 // runInject implements the inject command which replaces $UNIQUEID$ strings with generated ids.
-func runInject(command *cmdline.Command, args []string) error {
+func runInject(env *cmdline2.Env, args []string) error {
 	if len(args) == 0 {
-		return command.UsageErrorf("expected at least one file arg, got 0")
+		return env.UsageErrorf("expected at least one file arg, got 0")
 	}
 	for _, arg := range args {
 		if err := injectIntoFile(arg); err != nil {
diff --git a/cmd/vdl/doc.go b/cmd/vdl/doc.go
index 4130b33..f06e0e8 100644
--- a/cmd/vdl/doc.go
+++ b/cmd/vdl/doc.go
@@ -76,7 +76,7 @@
    before the package path, and dst is the replacement for src.  Use commas to
    separate multiple rules; the first rule matching src is used.  The special
    dst SKIP indicates matching packages are skipped.
- -java-out-dir=go/src->java/src/vdl/java
+ -java-out-dir=release/go/src->roadmap/java/lib/generated-src/vdl,roadmap/go/src->roadmap/java/lib/generated-src/vdl
    Same semantics as --go-out-dir but applies to java code generation.
  -java-out-pkg=v.io->io/v
    Java output package translation rules.  Must be of the form:
@@ -146,7 +146,7 @@
    before the package path, and dst is the replacement for src.  Use commas to
    separate multiple rules; the first rule matching src is used.  The special
    dst SKIP indicates matching packages are skipped.
- -java-out-dir=go/src->java/src/vdl/java
+ -java-out-dir=release/go/src->roadmap/java/lib/generated-src/vdl,roadmap/go/src->roadmap/java/lib/generated-src/vdl
    Same semantics as --go-out-dir but applies to java code generation.
  -java-out-pkg=v.io->io/v
    Java output package translation rules.  Must be of the form:
@@ -192,11 +192,6 @@
 
 "help ..." recursively displays help for all commands and topics.
 
-Output is formatted to a target width in runes, determined by checking the
-CMDLINE_WIDTH environment variable, falling back on the terminal width, falling
-back on 80 chars.  By setting CMDLINE_WIDTH=x, if x > 0 the width is x, if x < 0
-the width is unlimited, and if x == 0 or is unset one of the fallbacks is used.
-
 Usage:
    vdl help [flags] [command/topic ...]
 
@@ -209,6 +204,10 @@
       full    - Good for cmdline output, shows all global flags.
       godoc   - Good for godoc processing.
    Override the default by setting the CMDLINE_STYLE environment variable.
+ -width=<terminal width>
+   Format output to this target width in runes, or unlimited if width < 0.
+   Defaults to the terminal width if available.  Override the default by setting
+   the CMDLINE_WIDTH environment variable.
 
 Vdl packages - help topic
 
diff --git a/cmd/vdl/main.go b/cmd/vdl/main.go
index 6caf904..4c8c557 100644
--- a/cmd/vdl/main.go
+++ b/cmd/vdl/main.go
@@ -17,7 +17,7 @@
 	"strings"
 
 	"v.io/v23/vdlroot/vdltool"
-	"v.io/x/lib/cmdline"
+	"v.io/x/lib/cmdline2"
 	"v.io/x/lib/textutil"
 	"v.io/x/ref/lib/vdl/build"
 	"v.io/x/ref/lib/vdl/codegen/golang"
@@ -32,7 +32,7 @@
 }
 
 func main() {
-	os.Exit(cmdVDL.Main())
+	cmdline2.Main(cmdVDL)
 }
 
 func checkErrors(errs *vdlutil.Errors) error {
@@ -45,8 +45,8 @@
 
 // runHelper returns a function that generates a sorted list of transitive
 // targets, and calls the supplied run function.
-func runHelper(run func(targets []*build.Package, env *compile.Env)) func(cmd *cmdline.Command, args []string) error {
-	return func(cmd *cmdline.Command, args []string) error {
+func runHelper(run func(targets []*build.Package, env *compile.Env)) cmdline2.Runner {
+	return cmdline2.RunnerFunc(func(_ *cmdline2.Env, args []string) error {
 		if flagVerbose {
 			vdlutil.SetVerbose()
 		}
@@ -69,10 +69,10 @@
 		}
 		run(targets, env)
 		return checkErrors(env.Errors)
-	}
+	})
 }
 
-var topicPackages = cmdline.Topic{
+var topicPackages = cmdline2.Topic{
 	Name:  "packages",
 	Short: "Description of package lists",
 	Long: `
@@ -105,7 +105,7 @@
 `,
 }
 
-var topicVdlPath = cmdline.Topic{
+var topicVdlPath = cmdline2.Topic{
 	Name:  "vdlpath",
 	Short: "Description of VDLPATH environment variable",
 	Long: `
@@ -135,7 +135,7 @@
 `,
 }
 
-var topicVdlRoot = cmdline.Topic{
+var topicVdlRoot = cmdline2.Topic{
 	Name:  "vdlroot",
 	Short: "Description of VDLROOT environment variable",
 	Long: `
@@ -150,7 +150,7 @@
 `,
 }
 
-var topicVdlConfig = cmdline.Topic{
+var topicVdlConfig = cmdline2.Topic{
 	Name:  "vdl.config",
 	Short: "Description of vdl.config files",
 	Long: `
@@ -170,10 +170,10 @@
 For more information, run "vdl help packages".
 `
 
-var cmdCompile = &cmdline.Command{
-	Run:   runHelper(runCompile),
-	Name:  "compile",
-	Short: "Compile packages and dependencies, but don't generate code",
+var cmdCompile = &cmdline2.Command{
+	Runner: runHelper(runCompile),
+	Name:   "compile",
+	Short:  "Compile packages and dependencies, but don't generate code",
 	Long: `
 Compile compiles packages and their transitive dependencies, but does not
 generate code.  This is useful to sanity-check that your VDL files are valid.
@@ -182,10 +182,10 @@
 	ArgsLong: pkgArgLong,
 }
 
-var cmdGenerate = &cmdline.Command{
-	Run:   runHelper(runGenerate),
-	Name:  "generate",
-	Short: "Compile packages and dependencies, and generate code",
+var cmdGenerate = &cmdline2.Command{
+	Runner: runHelper(runGenerate),
+	Name:   "generate",
+	Short:  "Compile packages and dependencies, and generate code",
 	Long: `
 Generate compiles packages and their transitive dependencies, and generates code
 in the specified languages.
@@ -194,10 +194,10 @@
 	ArgsLong: pkgArgLong,
 }
 
-var cmdAudit = &cmdline.Command{
-	Run:   runHelper(runAudit),
-	Name:  "audit",
-	Short: "Check if any packages are stale and need generation",
+var cmdAudit = &cmdline2.Command{
+	Runner: runHelper(runAudit),
+	Name:   "audit",
+	Short:  "Check if any packages are stale and need generation",
 	Long: `
 Audit runs the same logic as generate, but doesn't write out generated files.
 Returns a 0 exit code if all packages are up-to-date, otherwise returns a
@@ -207,10 +207,10 @@
 	ArgsLong: pkgArgLong,
 }
 
-var cmdList = &cmdline.Command{
-	Run:   runHelper(runList),
-	Name:  "list",
-	Short: "List package and dependency info in transitive order",
+var cmdList = &cmdline2.Command{
+	Runner: runHelper(runList),
+	Name:   "list",
+	Short:  "List package and dependency info in transitive order",
 	Long: `
 List returns information about packages and their transitive dependencies, in
 transitive order.  This is the same order the generate and compile commands use
@@ -243,7 +243,7 @@
 }
 
 func (gls *genLangs) Set(value string) error {
-	// If the flag is repeated on the cmdline it is overridden.  Duplicates within
+	// If the flag is repeated on the cmdline2 it is overridden.  Duplicates within
 	// the comma separated list are ignored, and retain their original ordering.
 	*gls = genLangs{}
 	seen := make(map[vdltool.GenLanguage]bool)
@@ -331,7 +331,8 @@
 	optGenGoOutDir   = genOutDir{}
 	optGenJavaOutDir = genOutDir{
 		rules: xlateRules{
-			{"go/src", "java/src/vdl/java"},
+			{"release/go/src", "roadmap/java/lib/generated-src/vdl"},
+			{"roadmap/go/src", "roadmap/java/lib/generated-src/vdl"},
 		},
 	}
 	optGenJavascriptOutDir = genOutDir{
@@ -353,15 +354,15 @@
 )
 
 // Root returns the root command for the VDL tool.
-var cmdVDL = &cmdline.Command{
+var cmdVDL = &cmdline2.Command{
 	Name:  "vdl",
 	Short: "manages Vanadium Definition Language source code",
 	Long: `
 Command vdl manages Vanadium Definition Language source code.  It's similar to
 the go tool used for managing Go source code.
 `,
-	Children: []*cmdline.Command{cmdGenerate, cmdCompile, cmdAudit, cmdList},
-	Topics:   []cmdline.Topic{topicPackages, topicVdlPath, topicVdlRoot, topicVdlConfig},
+	Children: []*cmdline2.Command{cmdGenerate, cmdCompile, cmdAudit, cmdList},
+	Topics:   []cmdline2.Topic{topicPackages, topicVdlPath, topicVdlRoot, topicVdlConfig},
 }
 
 func init() {
diff --git a/cmd/vdl/vdl_test.go b/cmd/vdl/vdl_test.go
index 2e4de41..12848ab 100644
--- a/cmd/vdl/vdl_test.go
+++ b/cmd/vdl/vdl_test.go
@@ -12,6 +12,8 @@
 	"path/filepath"
 	"strings"
 	"testing"
+
+	"v.io/x/lib/cmdline2"
 )
 
 const (
@@ -23,8 +25,6 @@
 
 // Compares generated VDL files against the copy in the repo.
 func TestVDLGenerator(t *testing.T) {
-	// Setup the vdl command-line.
-	cmdVDL.Init(nil, os.Stdout, os.Stderr)
 	// Use vdl to generate Go code from input, into a temporary directory.
 	outDir, err := ioutil.TempDir("", "vdltest")
 	if err != nil {
@@ -33,7 +33,8 @@
 	defer os.RemoveAll(outDir)
 	// TODO(toddw): test the generated java and javascript files too.
 	outOpt := fmt.Sprintf("--go-out-dir=%s", outDir)
-	if err := cmdVDL.Execute([]string{"generate", "--lang=go", outOpt, testDir}); err != nil {
+	env := cmdline2.NewEnv()
+	if err := cmdline2.ParseAndRun(cmdVDL, env, []string{"generate", "--lang=go", outOpt, testDir}); err != nil {
 		t.Fatalf("Execute() failed: %v", err)
 	}
 	// Check that each *.vdl.go file in the testDir matches the generated output.
diff --git a/cmd/vom/doc.go b/cmd/vom/doc.go
index 09aebc3..e34f185 100644
--- a/cmd/vom/doc.go
+++ b/cmd/vom/doc.go
@@ -17,51 +17,8 @@
    help        Display help for commands or topics
 
 The global flags are:
- -alsologtostderr=true
-   log to standard error as well as files
- -log_backtrace_at=:0
-   when logging hits line file:N, emit a stack trace
- -log_dir=
-   if non-empty, write log files to this directory
- -logtostderr=false
-   log to standard error instead of files
- -max_stack_buf_size=4292608
-   max size in bytes of the buffer to use for logging stack traces
- -stderrthreshold=2
-   logs at or above this threshold go to stderr
- -v=0
-   log level for V logs
- -v23.credentials=
-   directory to use for storing security credentials
- -v23.i18n-catalogue=
-   18n catalogue files to load, comma separated
  -v23.metadata=<just specify -v23.metadata to activate>
    Displays metadata for the program and exits.
- -v23.namespace.root=[/(dev.v.io/role/vprod/service/mounttabled)@ns.dev.v.io:8101]
-   local namespace root; can be repeated to provided multiple roots
- -v23.permissions.file=map[]
-   specify a perms file as <name>:<permsfile>
- -v23.permissions.literal=
-   explicitly specify the runtime perms as a JSON-encoded access.Permissions.
-   Overrides all --v23.permissions.file flags.
- -v23.proxy=
-   object name of proxy service to use to export services across network
-   boundaries
- -v23.tcp.address=
-   address to listen on
- -v23.tcp.protocol=wsh
-   protocol to listen with
- -v23.vtrace.cache-size=1024
-   The number of vtrace traces to store in memory.
- -v23.vtrace.collect-regexp=
-   Spans and annotations that match this regular expression will trigger trace
-   collection.
- -v23.vtrace.dump-on-shutdown=true
-   If true, dump all stored traces on runtime shutdown.
- -v23.vtrace.sample-rate=0
-   Rate (from 0.0 to 1.0) to sample vtrace traces.
- -vmodule=
-   comma-separated list of pattern=N settings for file-filtered logging
 
 Vom decode
 
@@ -119,11 +76,6 @@
 
 "help ..." recursively displays help for all commands and topics.
 
-Output is formatted to a target width in runes, determined by checking the
-CMDLINE_WIDTH environment variable, falling back on the terminal width, falling
-back on 80 chars.  By setting CMDLINE_WIDTH=x, if x > 0 the width is x, if x < 0
-the width is unlimited, and if x == 0 or is unset one of the fallbacks is used.
-
 Usage:
    vom help [flags] [command/topic ...]
 
@@ -136,5 +88,9 @@
       full    - Good for cmdline output, shows all global flags.
       godoc   - Good for godoc processing.
    Override the default by setting the CMDLINE_STYLE environment variable.
+ -width=<terminal width>
+   Format output to this target width in runes, or unlimited if width < 0.
+   Defaults to the terminal width if available.  Override the default by setting
+   the CMDLINE_WIDTH environment variable.
 */
 package main
diff --git a/cmd/vom/types.vdl.go b/cmd/vom/types.vdl.go
index 0f1b276..1111b2d 100644
--- a/cmd/vom/types.vdl.go
+++ b/cmd/vom/types.vdl.go
@@ -55,7 +55,7 @@
 }
 
 func (dataRep) __VDLReflect(struct {
-	Name string "v.io/x/ref/cmd/vom.dataRep"
+	Name string `vdl:"v.io/x/ref/cmd/vom.dataRep"`
 	Enum struct{ Hex, Binary string }
 }) {
 }
diff --git a/cmd/vom/vom.go b/cmd/vom/vom.go
index 1c7398f..f387762 100644
--- a/cmd/vom/vom.go
+++ b/cmd/vom/vom.go
@@ -17,39 +17,28 @@
 	"strings"
 	"unicode"
 
-	"v.io/v23"
 	"v.io/v23/vdl"
 	"v.io/v23/vom"
-	"v.io/x/lib/cmdline"
-	_ "v.io/x/ref/profiles/static"
+	"v.io/x/lib/cmdline2"
 )
 
 func main() {
-	cmdline.HideGlobalFlagsExcept()
-	os.Exit(cmdVom.Main())
+	cmdline2.Main(cmdVom)
 }
 
-func runHelper(run cmdline.Runner) cmdline.Runner {
-	return func(cmd *cmdline.Command, args []string) error {
-		_, shutdown := v23.Init()
-		defer shutdown()
-		return run(cmd, args)
-	}
-}
-
-var cmdVom = &cmdline.Command{
+var cmdVom = &cmdline2.Command{
 	Name:  "vom",
 	Short: "helps debug the Vanadium Object Marshaling wire protocol",
 	Long: `
 Command vom helps debug the Vanadium Object Marshaling wire protocol.
 `,
-	Children: []*cmdline.Command{cmdDecode, cmdDump},
+	Children: []*cmdline2.Command{cmdDecode, cmdDump},
 }
 
-var cmdDecode = &cmdline.Command{
-	Run:   runHelper(runDecode),
-	Name:  "decode",
-	Short: "Decode data encoded in the vom format",
+var cmdDecode = &cmdline2.Command{
+	Runner: cmdline2.RunnerFunc(runDecode),
+	Name:   "decode",
+	Short:  "Decode data encoded in the vom format",
 	Long: `
 Decode decodes data encoded in the vom format.  If no arguments are provided,
 decode reads the data from stdin, otherwise the argument is the data.
@@ -63,10 +52,10 @@
 	ArgsLong: "[data] is the data to decode; if not specified, reads from stdin",
 }
 
-var cmdDump = &cmdline.Command{
-	Run:   runHelper(runDump),
-	Name:  "dump",
-	Short: "Dump data encoded in the vom format into formatted output",
+var cmdDump = &cmdline2.Command{
+	Runner: cmdline2.RunnerFunc(runDump),
+	Name:   "dump",
+	Short:  "Dump data encoded in the vom format into formatted output",
 	Long: `
 Dump dumps data encoded in the vom format, generating formatted output
 describing each portion of the encoding.  If no arguments are provided, dump
@@ -102,12 +91,12 @@
 		"Data representation, one of "+fmt.Sprint(dataRepAll))
 }
 
-func runDecode(cmd *cmdline.Command, args []string) error {
+func runDecode(env *cmdline2.Env, args []string) error {
 	// Convert all inputs into a reader over binary bytes.
 	var data string
 	switch {
 	case len(args) > 1:
-		return cmd.UsageErrorf("too many args")
+		return env.UsageErrorf("too many args")
 	case len(args) == 1:
 		data = args[0]
 	default:
@@ -129,29 +118,29 @@
 	if err := decoder.Decode(&result); err != nil {
 		return err
 	}
-	fmt.Fprintln(cmd.Stdout(), result)
+	fmt.Fprintln(env.Stdout, result)
 	if reader.Len() != 0 {
 		return fmt.Errorf("%d leftover bytes: % x", reader.Len(), reader.String())
 	}
 	return nil
 }
 
-func runDump(cmd *cmdline.Command, args []string) error {
+func runDump(env *cmdline2.Env, args []string) error {
 	// Handle non-streaming cases.
 	switch {
 	case len(args) > 1:
-		return cmd.UsageErrorf("too many args")
+		return env.UsageErrorf("too many args")
 	case len(args) == 1:
 		binbytes, err := dataToBinaryBytes(args[0])
 		if err != nil {
 			return err
 		}
-		fmt.Fprintln(cmd.Stdout(), vom.Dump(binbytes))
+		fmt.Fprintln(env.Stdout, vom.Dump(binbytes))
 		return nil
 	}
 	// Handle streaming from stdin.
 	// TODO(toddw): Add a flag to configure stdout/stderr dumping.
-	dumper := vom.NewDumper(dumpWriter{cmd.Stdout(), cmd.Stdout()})
+	dumper := vom.NewDumper(dumpWriter{env.Stdout, env.Stdout})
 	defer dumper.Close()
 	// Handle simple non-hex cases.
 	switch flagDataRep {
diff --git a/cmd/vomtestgen/generate.go b/cmd/vomtestgen/generate.go
index e6382b3..34420c2 100644
--- a/cmd/vomtestgen/generate.go
+++ b/cmd/vomtestgen/generate.go
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// The following enables go generate to generate the doc.go file.
+//go:generate go run $V23_ROOT/release/go/src/v.io/x/lib/cmdline/testdata/gendoc.go . -help
+
 package main
 
 import (
@@ -13,10 +16,9 @@
 	"path/filepath"
 	"strings"
 
-	"v.io/v23"
 	"v.io/v23/vdl"
 	"v.io/v23/vom"
-	"v.io/x/lib/cmdline"
+	"v.io/x/lib/cmdline2"
 	"v.io/x/ref/lib/vdl/build"
 	"v.io/x/ref/lib/vdl/codegen"
 	"v.io/x/ref/lib/vdl/codegen/vdlgen"
@@ -30,10 +32,10 @@
 	vomdataConfig    = "vomdata.vdl.config"
 )
 
-var cmdGenerate = &cmdline.Command{
-	Run:   runGenerate,
-	Name:  "vomtestgen",
-	Short: "generates test data for the vom wire protocol implementation",
+var cmdGenerate = &cmdline2.Command{
+	Runner: cmdline2.RunnerFunc(runGenerate),
+	Name:   "vomtestgen",
+	Short:  "generates test data for the vom wire protocol implementation",
 	Long: `
 Command vomtestgen generates test data for the vom wire protocol implementation.
 It takes as input a vdl config file, and outputs a vdl package with test cases.
@@ -62,38 +64,38 @@
 )
 
 func init() {
-	cmdline.HideGlobalFlagsExcept()
 	cmdGenerate.Flags.IntVar(&optGenMaxErrors, "max-errors", -1, "Stop processing after this many errors, or -1 for unlimited.")
 	cmdGenerate.Flags.StringVar(&optGenExts, "exts", ".vdl", "Comma-separated list of valid VDL file name extensions.")
 }
 
-func runGenerate(cmd *cmdline.Command, args []string) error {
-	_, shutdown := v23.Init()
-	defer shutdown()
+func main() {
+	cmdline2.Main(cmdGenerate)
+}
 
+func runGenerate(env *cmdline2.Env, args []string) error {
 	debug := new(bytes.Buffer)
-	defer dumpDebug(cmd.Stderr(), debug)
-	env := compile.NewEnv(optGenMaxErrors)
+	defer dumpDebug(env.Stderr, debug)
+	compileEnv := compile.NewEnv(optGenMaxErrors)
 	// Get the input datafile path.
 	var path string
 	switch len(args) {
 	case 0:
-		srcDirs := build.SrcDirs(env.Errors)
-		if err := env.Errors.ToError(); err != nil {
-			return cmd.UsageErrorf("%v", err)
+		srcDirs := build.SrcDirs(compileEnv.Errors)
+		if err := compileEnv.Errors.ToError(); err != nil {
+			return env.UsageErrorf("%v", err)
 		}
 		path = guessDataFilePath(debug, srcDirs)
 		if path == "" {
-			return cmd.UsageErrorf("couldn't find vomdata file in src dirs: %v", srcDirs)
+			return env.UsageErrorf("couldn't find vomdata file in src dirs: %v", srcDirs)
 		}
 	case 1:
 		path = args[0]
 	default:
-		return cmd.UsageErrorf("too many args (expecting exactly 1 vomdata file)")
+		return env.UsageErrorf("too many args (expecting exactly 1 vomdata file)")
 	}
 	inName := filepath.Clean(path)
 	if !strings.HasSuffix(inName, ".vdl.config") {
-		return cmd.UsageErrorf(`vomdata file doesn't end in ".vdl.config": %s`, inName)
+		return env.UsageErrorf(`vomdata file doesn't end in ".vdl.config": %s`, inName)
 	}
 	outName := inName[:len(inName)-len(".config")]
 	// Remove the generated file, so that it doesn't interfere with compiling the
@@ -101,7 +103,7 @@
 	if err := os.Remove(outName); err == nil {
 		fmt.Fprintf(debug, "Removed output file %v\n", outName)
 	}
-	config, err := compileConfig(debug, inName, env)
+	config, err := compileConfig(debug, inName, compileEnv)
 	if err != nil {
 		return err
 	}
@@ -113,7 +115,7 @@
 		return err
 	}
 	debug.Reset() // Don't dump debugging information on success
-	fmt.Fprintf(cmd.Stdout(), "Wrote output file %v\n", outName)
+	fmt.Fprintf(env.Stdout, "Wrote output file %v\n", outName)
 	return nil
 }
 
diff --git a/cmd/vomtestgen/main.go b/cmd/vomtestgen/main.go
deleted file mode 100644
index 6305464..0000000
--- a/cmd/vomtestgen/main.go
+++ /dev/null
@@ -1,16 +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 following enables go generate to generate the doc.go file.
-//go:generate go run $V23_ROOT/release/go/src/v.io/x/lib/cmdline/testdata/gendoc.go . -help
-
-package main
-
-import (
-	"os"
-)
-
-func main() {
-	os.Exit(cmdGenerate.Main())
-}
diff --git a/cmd/vrpc/doc.go b/cmd/vrpc/doc.go
index 0311b7a..fc183f9 100644
--- a/cmd/vrpc/doc.go
+++ b/cmd/vrpc/doc.go
@@ -139,11 +139,6 @@
 
 "help ..." recursively displays help for all commands and topics.
 
-Output is formatted to a target width in runes, determined by checking the
-CMDLINE_WIDTH environment variable, falling back on the terminal width, falling
-back on 80 chars.  By setting CMDLINE_WIDTH=x, if x > 0 the width is x, if x < 0
-the width is unlimited, and if x == 0 or is unset one of the fallbacks is used.
-
 Usage:
    vrpc help [flags] [command/topic ...]
 
@@ -156,5 +151,9 @@
       full    - Good for cmdline output, shows all global flags.
       godoc   - Good for godoc processing.
    Override the default by setting the CMDLINE_STYLE environment variable.
+ -width=<terminal width>
+   Format output to this target width in runes, or unlimited if width < 0.
+   Defaults to the terminal width if available.  Override the default by setting
+   the CMDLINE_WIDTH environment variable.
 */
 package main
diff --git a/cmd/vrpc/internal/test_base.vdl.go b/cmd/vrpc/internal/test_base.vdl.go
index 103fcdc..c1dc0dc 100644
--- a/cmd/vrpc/internal/test_base.vdl.go
+++ b/cmd/vrpc/internal/test_base.vdl.go
@@ -22,14 +22,14 @@
 }
 
 func (Struct) __VDLReflect(struct {
-	Name string "v.io/x/ref/cmd/vrpc/internal.Struct"
+	Name string `vdl:"v.io/x/ref/cmd/vrpc/internal.Struct"`
 }) {
 }
 
 type Array2Int [2]int32
 
 func (Array2Int) __VDLReflect(struct {
-	Name string "v.io/x/ref/cmd/vrpc/internal.Array2Int"
+	Name string `vdl:"v.io/x/ref/cmd/vrpc/internal.Array2Int"`
 }) {
 }
 
diff --git a/cmd/vrpc/vrpc.go b/cmd/vrpc/vrpc.go
index e540707..0e25698 100644
--- a/cmd/vrpc/vrpc.go
+++ b/cmd/vrpc/vrpc.go
@@ -10,7 +10,6 @@
 import (
 	"fmt"
 	"io"
-	"os"
 	"regexp"
 	"strings"
 	"time"
@@ -23,7 +22,8 @@
 	"v.io/v23/rpc/reserved"
 	"v.io/v23/vdl"
 	"v.io/v23/vdlroot/signature"
-	"v.io/x/lib/cmdline"
+	"v.io/x/lib/cmdline2"
+	"v.io/x/ref/lib/v23cmd"
 	"v.io/x/ref/lib/vdl/build"
 	"v.io/x/ref/lib/vdl/codegen/vdlgen"
 	"v.io/x/ref/lib/vdl/compile"
@@ -32,18 +32,13 @@
 )
 
 var (
-	gctx             *context.T
 	flagInsecure     bool
 	flagShowReserved bool
 )
 
 func main() {
-	cmdline.HideGlobalFlagsExcept(regexp.MustCompile(`^v23\.namespace\.root$`))
-	var shutdown v23.Shutdown
-	gctx, shutdown = v23.Init()
-	exitCode := cmdVRPC.Main()
-	shutdown()
-	os.Exit(exitCode)
+	cmdline2.HideGlobalFlagsExcept(regexp.MustCompile(`^v23\.namespace\.root$`))
+	cmdline2.Main(cmdVRPC)
 }
 
 func init() {
@@ -58,7 +53,7 @@
 	cmdSignature.Flags.BoolVar(&flagShowReserved, "show-reserved", false, "if true, also show the signatures of reserved methods")
 }
 
-var cmdVRPC = &cmdline.Command{
+var cmdVRPC = &cmdline2.Command{
 	Name:  "vrpc",
 	Short: "sends and receives Vanadium remote procedure calls",
 	Long: `
@@ -68,7 +63,7 @@
 	// TODO(toddw): Add cmdServe, which will take an interface as input, and set
 	// up a server capable of handling the given methods.  When a request is
 	// received, it'll allow the user to respond via stdin.
-	Children: []*cmdline.Command{cmdSignature, cmdCall, cmdIdentify},
+	Children: []*cmdline2.Command{cmdSignature, cmdCall, cmdIdentify},
 }
 
 const serverDesc = `
@@ -76,10 +71,10 @@
 the server, or an object name that will be resolved to an end-point.
 `
 
-var cmdSignature = &cmdline.Command{
-	Run:   runSignature,
-	Name:  "signature",
-	Short: "Describe the interfaces of a Vanadium server",
+var cmdSignature = &cmdline2.Command{
+	Runner: v23cmd.RunnerFunc(runSignature),
+	Name:   "signature",
+	Short:  "Describe the interfaces of a Vanadium server",
 	Long: `
 Signature connects to the Vanadium server identified by <server>.
 
@@ -93,10 +88,10 @@
 `,
 }
 
-var cmdCall = &cmdline.Command{
-	Run:   runCall,
-	Name:  "call",
-	Short: "Call a method of a Vanadium server",
+var cmdCall = &cmdline2.Command{
+	Runner: v23cmd.RunnerFunc(runCall),
+	Name:   "call",
+	Short:  "Call a method of a Vanadium server",
 	Long: `
 Call connects to the Vanadium server identified by <server> and calls the
 <method> with the given positional [args...], returning results on stdout.
@@ -124,10 +119,10 @@
 `,
 }
 
-var cmdIdentify = &cmdline.Command{
-	Run:   runIdentify,
-	Name:  "identify",
-	Short: "Reveal blessings presented by a Vanadium server",
+var cmdIdentify = &cmdline2.Command{
+	Runner: v23cmd.RunnerFunc(runIdentify),
+	Name:   "identify",
+	Short:  "Reveal blessings presented by a Vanadium server",
 	Long: `
 Identify connects to the Vanadium server identified by <server> and dumps out
 the blessings presented by that server (and the subset of those that are
@@ -137,7 +132,7 @@
 	ArgsLong: serverDesc,
 }
 
-func runSignature(cmd *cmdline.Command, args []string) error {
+func runSignature(ctx *context.T, env *cmdline2.Env, args []string) error {
 	// Error-check args.
 	var server, method string
 	switch len(args) {
@@ -146,11 +141,11 @@
 	case 2:
 		server, method = args[0], args[1]
 	default:
-		return cmd.UsageErrorf("wrong number of arguments")
+		return env.UsageErrorf("wrong number of arguments")
 	}
 	// Get the interface or method signature, and pretty-print.  We print the
 	// named types after the signatures, to aid in readability.
-	ctx, cancel := context.WithTimeout(gctx, time.Minute)
+	ctx, cancel := context.WithTimeout(ctx, time.Minute)
 	defer cancel()
 	var types vdlgen.NamedTypes
 	var opts []rpc.CallOpt
@@ -162,9 +157,9 @@
 		if err != nil {
 			return fmt.Errorf("MethodSignature failed: %v", err)
 		}
-		vdlgen.PrintMethod(cmd.Stdout(), methodSig, &types)
-		fmt.Fprintln(cmd.Stdout())
-		types.Print(cmd.Stdout())
+		vdlgen.PrintMethod(env.Stdout, methodSig, &types)
+		fmt.Fprintln(env.Stdout)
+		types.Print(env.Stdout)
 		return nil
 	}
 	ifacesSig, err := reserved.Signature(ctx, server, opts...)
@@ -176,22 +171,22 @@
 			continue
 		}
 		if i > 0 {
-			fmt.Fprintln(cmd.Stdout())
+			fmt.Fprintln(env.Stdout)
 		}
-		vdlgen.PrintInterface(cmd.Stdout(), iface, &types)
-		fmt.Fprintln(cmd.Stdout())
+		vdlgen.PrintInterface(env.Stdout, iface, &types)
+		fmt.Fprintln(env.Stdout)
 	}
-	types.Print(cmd.Stdout())
+	types.Print(env.Stdout)
 	return nil
 }
 
-func runCall(cmd *cmdline.Command, args []string) error {
+func runCall(ctx *context.T, env *cmdline2.Env, args []string) error {
 	// Error-check args, and set up argsdata with a comma-separated list of
 	// arguments, allowing each individual arg to already be comma-separated.
 	//
 	// TODO(toddw): Should we just space-separate the args instead?
 	if len(args) < 2 {
-		return cmd.UsageErrorf("must specify <server> and <method>")
+		return env.UsageErrorf("must specify <server> and <method>")
 	}
 	server, method := args[0], args[1]
 	var argsdata string
@@ -204,7 +199,7 @@
 		}
 	}
 	// Get the method signature and parse args.
-	ctx, cancel := context.WithTimeout(gctx, time.Minute)
+	ctx, cancel := context.WithTimeout(ctx, time.Minute)
 	defer cancel()
 	methodSig, err := reserved.MethodSignature(ctx, server, method)
 	if err != nil {
@@ -231,7 +226,7 @@
 		case err != nil:
 			return fmt.Errorf("call.Recv failed: %v", err)
 		}
-		fmt.Fprintf(cmd.Stdout(), "<< %v\n", vdlgen.TypedConst(item, "", nil))
+		fmt.Fprintf(env.Stdout, "<< %v\n", vdlgen.TypedConst(item, "", nil))
 	}
 	// Finish the method call.
 	outargs := make([]*vdl.Value, len(methodSig.OutArgs))
@@ -245,11 +240,11 @@
 	// Pretty-print results.
 	for i, arg := range outargs {
 		if i > 0 {
-			fmt.Fprint(cmd.Stdout(), " ")
+			fmt.Fprint(env.Stdout, " ")
 		}
-		fmt.Fprint(cmd.Stdout(), vdlgen.TypedConst(arg, "", nil))
+		fmt.Fprint(env.Stdout, vdlgen.TypedConst(arg, "", nil))
 	}
-	fmt.Fprintln(cmd.Stdout())
+	fmt.Fprintln(env.Stdout)
 	return nil
 }
 
@@ -277,12 +272,12 @@
 	return ret, nil
 }
 
-func runIdentify(cmd *cmdline.Command, args []string) error {
+func runIdentify(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if len(args) != 1 {
-		return cmd.UsageErrorf("wrong number of arguments")
+		return env.UsageErrorf("wrong number of arguments")
 	}
 	server := args[0]
-	ctx, cancel := context.WithTimeout(gctx, time.Minute)
+	ctx, cancel := context.WithTimeout(ctx, time.Minute)
 	defer cancel()
 	var opts []rpc.CallOpt
 	if flagInsecure {
@@ -295,6 +290,6 @@
 		return fmt.Errorf(`client.StartCall(%q, "", nil) failed with %v`, server, err)
 	}
 	valid, presented := call.RemoteBlessings()
-	fmt.Fprintf(cmd.Stdout(), "PRESENTED: %v\nVALID:     %v\nPUBLICKEY: %v\n", presented, valid, presented.PublicKey())
+	fmt.Fprintf(env.Stdout, "PRESENTED: %v\nVALID:     %v\nPUBLICKEY: %v\n", presented, valid, presented.PublicKey())
 	return nil
 }
diff --git a/cmd/vrpc/vrpc_test.go b/cmd/vrpc/vrpc_test.go
index d6aebaa..d8ef7dc 100644
--- a/cmd/vrpc/vrpc_test.go
+++ b/cmd/vrpc/vrpc_test.go
@@ -6,15 +6,17 @@
 
 import (
 	"bytes"
+	"fmt"
 	"strings"
 	"testing"
 
 	"v.io/v23"
 	"v.io/v23/context"
 	"v.io/v23/rpc"
+	"v.io/x/lib/cmdline2"
 	"v.io/x/lib/vlog"
-
 	"v.io/x/ref/cmd/vrpc/internal"
+	"v.io/x/ref/lib/v23cmd"
 	_ "v.io/x/ref/profiles"
 	"v.io/x/ref/test"
 )
@@ -114,16 +116,15 @@
 	return nil
 }
 
-func initTest(t *testing.T) (name string, shutdown v23.Shutdown) {
-	// The gctx initialized here is the global context defined in vrpc.go.
-	gctx, shutdown = test.InitForTest()
+func initTest(t *testing.T) (ctx *context.T, name string, shutdown v23.Shutdown) {
+	ctx, shutdown = test.InitForTest()
 
-	rpcServer, err := v23.NewServer(gctx)
+	rpcServer, err := v23.NewServer(ctx)
 	if err != nil {
 		t.Fatalf("NewServer failed: %v", err)
 		return
 	}
-	endpoints, err := rpcServer.Listen(v23.GetListenSpec(gctx))
+	endpoints, err := rpcServer.Listen(v23.GetListenSpec(ctx))
 	if err != nil {
 		t.Fatalf("Listen failed: %v", err)
 		return
@@ -132,22 +133,18 @@
 	obj := internal.TypeTesterServer(&server{})
 	if err := rpcServer.Serve("", obj, nil); err != nil {
 		t.Fatalf("Serve failed: %v", err)
-		return name, shutdown
+		return
 	}
-	return name, shutdown
+	return
 }
 
 func testSignature(t *testing.T, showReserved bool, wantSig string) {
-	name, shutdown := initTest(t)
+	ctx, name, shutdown := initTest(t)
 	defer shutdown()
 	var stdout, stderr bytes.Buffer
-	cmdVRPC.Init(nil, &stdout, &stderr)
-
-	args := []string{"signature", name}
-	// The cmdline package won't reparse the flags sent to Execute, so
-	// instead, set the flag variable directly from here.
-	flagShowReserved = showReserved
-	if err := cmdVRPC.Execute(args); err != nil {
+	env := &cmdline2.Env{Stdout: &stdout, Stderr: &stderr}
+	args := []string{"signature", fmt.Sprintf("-show-reserved=%v", showReserved), name}
+	if err := v23cmd.ParseAndRun(cmdVRPC, ctx, env, args); err != nil {
 		t.Fatalf("%s: %v", args, err)
 	}
 
@@ -274,7 +271,7 @@
 }
 
 func TestMethodSignature(t *testing.T) {
-	name, shutdown := initTest(t)
+	ctx, name, shutdown := initTest(t)
 	defer shutdown()
 
 	tests := []struct {
@@ -294,8 +291,8 @@
 	}
 	for _, test := range tests {
 		var stdout, stderr bytes.Buffer
-		cmdVRPC.Init(nil, &stdout, &stderr)
-		if err := cmdVRPC.Execute([]string{"signature", name, test.Method}); err != nil {
+		env := &cmdline2.Env{Stdout: &stdout, Stderr: &stderr}
+		if err := v23cmd.ParseAndRun(cmdVRPC, ctx, env, []string{"signature", name, test.Method}); err != nil {
 			t.Errorf("%q failed: %v", test.Method, err)
 			continue
 		}
@@ -309,7 +306,7 @@
 }
 
 func TestCall(t *testing.T) {
-	name, shutdown := initTest(t)
+	ctx, name, shutdown := initTest(t)
 	defer shutdown()
 
 	tests := []struct {
@@ -337,8 +334,8 @@
 	}
 	for _, test := range tests {
 		var stdout, stderr bytes.Buffer
-		cmdVRPC.Init(nil, &stdout, &stderr)
-		if err := cmdVRPC.Execute([]string{"call", name, test.Method, test.InArgs}); err != nil {
+		env := &cmdline2.Env{Stdout: &stdout, Stderr: &stderr}
+		if err := v23cmd.ParseAndRun(cmdVRPC, ctx, env, []string{"call", name, test.Method, test.InArgs}); err != nil {
 			t.Errorf("%q(%s) failed: %v", test.Method, test.InArgs, err)
 			continue
 		}
diff --git a/cmd/vrun/vrun.go b/cmd/vrun/vrun.go
index 390d0f1..dbcc8df 100644
--- a/cmd/vrun/vrun.go
+++ b/cmd/vrun/vrun.go
@@ -17,9 +17,10 @@
 	"v.io/v23"
 	"v.io/v23/context"
 	"v.io/v23/security"
-	"v.io/x/lib/cmdline"
+	"v.io/x/lib/cmdline2"
 	"v.io/x/lib/vlog"
 	"v.io/x/ref/envvar"
+	"v.io/x/ref/lib/v23cmd"
 	"v.io/x/ref/services/agent/agentlib"
 	"v.io/x/ref/services/agent/keymgr"
 	"v.io/x/ref/services/role"
@@ -33,8 +34,8 @@
 	roleFlag     string
 )
 
-var cmdVrun = &cmdline.Command{
-	Run:      vrun,
+var cmdVrun = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(vrun),
 	Name:     "vrun",
 	Short:    "executes commands with a derived Vanadium principal",
 	Long:     "Command vrun executes commands with a derived Vanadium principal.",
@@ -42,7 +43,7 @@
 }
 
 func main() {
-	cmdline.HideGlobalFlagsExcept()
+	cmdline2.HideGlobalFlagsExcept()
 	syscall.CloseOnExec(3)
 	syscall.CloseOnExec(4)
 
@@ -50,19 +51,16 @@
 	cmdVrun.Flags.StringVar(&nameFlag, "name", "", "Name to use for the blessing. Uses the command name if unset.")
 	cmdVrun.Flags.StringVar(&roleFlag, "role", "", "Role object from which to request the blessing. If set, the blessings from this role server are used and --name is ignored. If not set, the default blessings of the calling principal are extended with --name.")
 
-	os.Exit(cmdVrun.Main())
+	cmdline2.Main(cmdVrun)
 }
 
-func vrun(cmd *cmdline.Command, args []string) error {
-	ctx, shutdown := v23.Init()
-	defer shutdown()
-
+func vrun(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if len(args) == 0 {
 		args = []string{"bash", "--norc"}
 	}
 	principal, conn, err := createPrincipal(ctx)
 	if err != nil {
-		return err
+		return env.UsageErrorf("%v", err)
 	}
 	if len(roleFlag) == 0 {
 		if len(nameFlag) == 0 {
diff --git a/examples/rps/service.vdl.go b/examples/rps/service.vdl.go
index 163b87d..3f86372 100644
--- a/examples/rps/service.vdl.go
+++ b/examples/rps/service.vdl.go
@@ -43,7 +43,7 @@
 }
 
 func (GameId) __VDLReflect(struct {
-	Name string "v.io/x/ref/examples/rps.GameId"
+	Name string `vdl:"v.io/x/ref/examples/rps.GameId"`
 }) {
 }
 
@@ -54,14 +54,14 @@
 }
 
 func (GameOptions) __VDLReflect(struct {
-	Name string "v.io/x/ref/examples/rps.GameOptions"
+	Name string `vdl:"v.io/x/ref/examples/rps.GameOptions"`
 }) {
 }
 
 type GameTypeTag byte
 
 func (GameTypeTag) __VDLReflect(struct {
-	Name string "v.io/x/ref/examples/rps.GameTypeTag"
+	Name string `vdl:"v.io/x/ref/examples/rps.GameTypeTag"`
 }) {
 }
 
@@ -83,7 +83,7 @@
 	PlayerActionQuit struct{ Value unused } // Indicates that the player is quitting the game.
 	// __PlayerActionReflect describes the PlayerAction union type.
 	__PlayerActionReflect struct {
-		Name  string "v.io/x/ref/examples/rps.PlayerAction"
+		Name  string `vdl:"v.io/x/ref/examples/rps.PlayerAction"`
 		Type  PlayerAction
 		Union struct {
 			Move PlayerActionMove
@@ -106,7 +106,7 @@
 }
 
 func (unused) __VDLReflect(struct {
-	Name string "v.io/x/ref/examples/rps.unused"
+	Name string `vdl:"v.io/x/ref/examples/rps.unused"`
 }) {
 }
 
@@ -134,7 +134,7 @@
 	JudgeActionScore struct{ Value ScoreCard } // The result of the game.
 	// __JudgeActionReflect describes the JudgeAction union type.
 	__JudgeActionReflect struct {
-		Name  string "v.io/x/ref/examples/rps.JudgeAction"
+		Name  string `vdl:"v.io/x/ref/examples/rps.JudgeAction"`
 		Type  JudgeAction
 		Union struct {
 			PlayerNum    JudgeActionPlayerNum
@@ -174,7 +174,7 @@
 type PlayersMoves [2]string
 
 func (PlayersMoves) __VDLReflect(struct {
-	Name string "v.io/x/ref/examples/rps.PlayersMoves"
+	Name string `vdl:"v.io/x/ref/examples/rps.PlayersMoves"`
 }) {
 }
 
@@ -188,7 +188,7 @@
 }
 
 func (Round) __VDLReflect(struct {
-	Name string "v.io/x/ref/examples/rps.Round"
+	Name string `vdl:"v.io/x/ref/examples/rps.Round"`
 }) {
 }
 
@@ -197,7 +197,7 @@
 type WinnerTag byte
 
 func (WinnerTag) __VDLReflect(struct {
-	Name string "v.io/x/ref/examples/rps.WinnerTag"
+	Name string `vdl:"v.io/x/ref/examples/rps.WinnerTag"`
 }) {
 }
 
@@ -207,7 +207,7 @@
 }
 
 func (PlayResult) __VDLReflect(struct {
-	Name string "v.io/x/ref/examples/rps.PlayResult"
+	Name string `vdl:"v.io/x/ref/examples/rps.PlayResult"`
 }) {
 }
 
@@ -222,7 +222,7 @@
 }
 
 func (ScoreCard) __VDLReflect(struct {
-	Name string "v.io/x/ref/examples/rps.ScoreCard"
+	Name string `vdl:"v.io/x/ref/examples/rps.ScoreCard"`
 }) {
 }
 
diff --git a/examples/tunnel/tunnel.vdl.go b/examples/tunnel/tunnel.vdl.go
index 07eb5a2..51973c2 100644
--- a/examples/tunnel/tunnel.vdl.go
+++ b/examples/tunnel/tunnel.vdl.go
@@ -28,7 +28,7 @@
 }
 
 func (ShellOpts) __VDLReflect(struct {
-	Name string "v.io/x/ref/examples/tunnel.ShellOpts"
+	Name string `vdl:"v.io/x/ref/examples/tunnel.ShellOpts"`
 }) {
 }
 
@@ -38,7 +38,7 @@
 }
 
 func (WindowSize) __VDLReflect(struct {
-	Name string "v.io/x/ref/examples/tunnel.WindowSize"
+	Name string `vdl:"v.io/x/ref/examples/tunnel.WindowSize"`
 }) {
 }
 
@@ -69,7 +69,7 @@
 	ClientShellPacketWinSize struct{ Value WindowSize }
 	// __ClientShellPacketReflect describes the ClientShellPacket union type.
 	__ClientShellPacketReflect struct {
-		Name  string "v.io/x/ref/examples/tunnel.ClientShellPacket"
+		Name  string `vdl:"v.io/x/ref/examples/tunnel.ClientShellPacket"`
 		Type  ClientShellPacket
 		Union struct {
 			Stdin     ClientShellPacketStdin
@@ -98,7 +98,7 @@
 }
 
 func (unused) __VDLReflect(struct {
-	Name string "v.io/x/ref/examples/tunnel.unused"
+	Name string `vdl:"v.io/x/ref/examples/tunnel.unused"`
 }) {
 }
 
@@ -124,7 +124,7 @@
 	ServerShellPacketStderr struct{ Value []byte }
 	// __ServerShellPacketReflect describes the ServerShellPacket union type.
 	__ServerShellPacketReflect struct {
-		Name  string "v.io/x/ref/examples/tunnel.ServerShellPacket"
+		Name  string `vdl:"v.io/x/ref/examples/tunnel.ServerShellPacket"`
 		Type  ServerShellPacket
 		Union struct {
 			Stdout ServerShellPacketStdout
diff --git a/examples/tunnel/tunneld/impl.go b/examples/tunnel/tunneld/impl.go
index f7b5902..7389974 100644
--- a/examples/tunnel/tunneld/impl.go
+++ b/examples/tunnel/tunneld/impl.go
@@ -131,7 +131,7 @@
 		return nonShellErrorCode, err
 	}
 	if status.Signaled() {
-		return int32(status), fmt.Errorf("process killed by signal %u (%v)", status.Signal(), status.Signal())
+		return int32(status), fmt.Errorf("process killed by signal %d (%v)", status.Signal(), status.Signal())
 	}
 	if status.Exited() {
 		if status.ExitStatus() == 0 {
diff --git a/lib/exec/exec_test.go b/lib/exec/exec_test.go
index 10cdb50..259b4a7 100644
--- a/lib/exec/exec_test.go
+++ b/lib/exec/exec_test.go
@@ -112,7 +112,6 @@
 		log.Printf("expectMessage: timeout\n")
 		return false
 	}
-	panic("unreachable")
 }
 
 func TestConfigExchange(t *testing.T) {
diff --git a/lib/exec/parent.go b/lib/exec/parent.go
index 8431820..e8747a1 100644
--- a/lib/exec/parent.go
+++ b/lib/exec/parent.go
@@ -291,7 +291,6 @@
 		<-c
 		return verror.New(ErrTimeout, nil)
 	}
-	panic("unreachable")
 }
 
 // wait performs the Wait on the underlying command under lock, and only once
@@ -330,7 +329,6 @@
 	} else {
 		return <-c
 	}
-	panic("unreachable")
 }
 
 // Pid returns the pid of the child, 0 if the child process doesn't exist
diff --git a/lib/exec/util_test.go b/lib/exec/util_test.go
index 3fb073a..dd97eb0 100644
--- a/lib/exec/util_test.go
+++ b/lib/exec/util_test.go
@@ -15,25 +15,25 @@
 		t.Fatalf("Unexpected length of environment variable slice: expected %d, got %d", expected, got)
 	}
 	if expected, got := "NAME=VALUE1", env[0]; expected != got {
-		t.Fatalf("Unexpected element in the environment variable slice: expected %d, got %d", expected, got)
+		t.Fatalf("Unexpected element in the environment variable slice: expected %q, got %q", expected, got)
 	}
 	env = Setenv(env, "NAME", "VALUE2")
 	if expected, got := 1, len(env); expected != got {
 		t.Fatalf("Unexpected length of environment variable slice: expected %d, got %d", expected, got)
 	}
 	if expected, got := "NAME=VALUE2", env[0]; expected != got {
-		t.Fatalf("Unexpected element in the environment variable slice: expected %d, got %d", expected, got)
+		t.Fatalf("Unexpected element in the environment variable slice: expected %q, got %q", expected, got)
 	}
 	value, err := Getenv(env, "NAME")
 	if err != nil {
 		t.Fatalf("Unexpected error when looking up environment variable value: %v", err)
 	}
 	if expected, got := "VALUE2", value; expected != got {
-		t.Fatalf("Unexpected value of an environment variable: expected %d, got %d", expected, got)
+		t.Fatalf("Unexpected value of an environment variable: expected %q, got %q", expected, got)
 	}
 	value, err = Getenv(env, "NONAME")
 	if err == nil {
-		t.Fatalf("Expected error when looking up environment variable value, got none", value)
+		t.Fatalf("Expected error when looking up environment variable value, got none (value: %q)", value)
 	}
 }
 
diff --git a/lib/flags/flags_test.go b/lib/flags/flags_test.go
index b3706f6..37508db 100644
--- a/lib/flags/flags_test.go
+++ b/lib/flags/flags_test.go
@@ -54,16 +54,16 @@
 	permsf := fl.PermissionsFlags()
 
 	if got, want := permsf.PermissionsFile("runtime"), "foo.json"; got != want {
-		t.Errorf("got %t, want %t", got, want)
+		t.Errorf("got %q, want %q", got, want)
 	}
 	if got, want := permsf.PermissionsFile("bar"), "bar.json"; got != want {
-		t.Errorf("got %t, want %t", got, want)
+		t.Errorf("got %q, want %q", got, want)
 	}
 	if got, want := permsf.PermissionsFile("wombat"), ""; got != want {
-		t.Errorf("got %t, want %t", got, want)
+		t.Errorf("got %q, want %q", got, want)
 	}
 	if got, want := permsf.PermissionsFile("baz"), "bar:baz.json"; got != want {
-		t.Errorf("got %t, want %t", got, want)
+		t.Errorf("got %q, want %q", got, want)
 	}
 }
 
@@ -75,10 +75,10 @@
 	permsf := fl.PermissionsFlags()
 
 	if got, want := permsf.PermissionsFile("runtime"), ""; got != want {
-		t.Errorf("got %t, want %t", got, want)
+		t.Errorf("got %q, want %q", got, want)
 	}
 	if got, want := permsf.PermissionsLiteral(), "hedgehog"; got != want {
-		t.Errorf("got %t, want %t, ok %t", got, want)
+		t.Errorf("got %q, want %q", got, want)
 	}
 }
 
@@ -90,10 +90,10 @@
 	permsf := fl.PermissionsFlags()
 
 	if got, want := permsf.PermissionsFile("runtime"), "foo.json"; got != want {
-		t.Errorf("got %t, want %t", got, want)
+		t.Errorf("got %q, want %q", got, want)
 	}
 	if got, want := permsf.PermissionsLiteral(), "hedgehog"; got != want {
-		t.Errorf("got %t, want %t, ok %t", got, want)
+		t.Errorf("got %q, want %q", got, want)
 	}
 }
 
diff --git a/lib/security/audit/principal_test.go b/lib/security/audit/principal_test.go
index ac0eecd..7094739 100644
--- a/lib/security/audit/principal_test.go
+++ b/lib/security/audit/principal_test.go
@@ -115,7 +115,7 @@
 				return fmt.Errorf("result #%d: Got error %v, want %v", i, gerr, werr)
 			}
 		} else if !reflect.DeepEqual(got, want) {
-			return fmt.Errorf("result #%d: Got  %v, want %v", got, want)
+			return fmt.Errorf("result #%d: Got  %v, want %v", i, got, want)
 		}
 	}
 	return nil
diff --git a/lib/security/blessingroots.go b/lib/security/blessingroots.go
index e5748bf..5c0a7b2 100644
--- a/lib/security/blessingroots.go
+++ b/lib/security/blessingroots.go
@@ -16,6 +16,8 @@
 	"v.io/x/ref/lib/security/serialization"
 )
 
+var errRootsAddPattern = verror.Register(pkgPath+".errRootsAddPattern", verror.NoRetry, "{1:}{2:} a root cannot be recognized for all blessing names (i.e., the pattern '...')")
+
 // blessingRoots implements security.BlessingRoots.
 type blessingRoots struct {
 	persistedData SerializerReaderWriter
@@ -33,6 +35,9 @@
 }
 
 func (br *blessingRoots) Add(root security.PublicKey, pattern security.BlessingPattern) error {
+	if pattern == security.AllPrincipals {
+		return verror.New(errRootsAddPattern, nil)
+	}
 	key, err := stateMapKey(root)
 	if err != nil {
 		return err
diff --git a/lib/security/blessingroots_test.go b/lib/security/blessingroots_test.go
index a8ef94f..ad6afd5 100644
--- a/lib/security/blessingroots_test.go
+++ b/lib/security/blessingroots_test.go
@@ -30,6 +30,9 @@
 }
 
 func (t *rootsTester) add(br security.BlessingRoots) error {
+	if err := br.Add(t[0], security.AllPrincipals); err == nil {
+		return fmt.Errorf("Add( , %v) succeeded, expected it to fail", security.AllPrincipals)
+	}
 	testdata := []struct {
 		root    security.PublicKey
 		pattern security.BlessingPattern
diff --git a/lib/security/blessingstore_test.go b/lib/security/blessingstore_test.go
index 8177624..339cd59 100644
--- a/lib/security/blessingstore_test.go
+++ b/lib/security/blessingstore_test.go
@@ -252,7 +252,7 @@
 	)
 
 	if old, err := s.Set(alice, security.AllPrincipals); !reflect.DeepEqual(old, empty) || err != nil {
-		t.Errorf("Got (%v, %v), want (%v, nil)", old, err)
+		t.Errorf("Got (%v, %v), want (%v, nil)", old, err, empty)
 	}
 	if old, err := s.Set(alice, security.AllPrincipals); !reflect.DeepEqual(old, alice) || err != nil {
 		t.Errorf("Got (%v, %v) want (%v, nil)", old, err, alice)
diff --git a/lib/security/serialization/serialization_test.go b/lib/security/serialization/serialization_test.go
index 06f018f..9f7fc96 100644
--- a/lib/security/serialization/serialization_test.go
+++ b/lib/security/serialization/serialization_test.go
@@ -174,10 +174,10 @@
 		}
 		matchErr := "cannot be nil"
 		if _, err := serialization.NewSigningWriteCloser(d, s, signer, nil); !matchesErrorPattern(err, matchErr) {
-			t.Errorf("NewSigningWriter(%p, %p, %p, ...) returned: %v, want to match: %v", d, s, signer, err, matchErr)
+			t.Errorf("NewSigningWriter(%v, %v, %v, ...) returned: %v, want to match: %v", d, s, signer, err, matchErr)
 		}
 		if _, err := serialization.NewVerifyingReader(d, s, key); !matchesErrorPattern(err, matchErr) {
-			t.Errorf("NewVerifyingReader(%p, %p, %p) returned: %v, want to match: %v", d, s, key, err, matchErr)
+			t.Errorf("NewVerifyingReader(%v, %v, %v) returned: %v, want to match: %v", d, s, key, err, matchErr)
 		}
 	}
 }
diff --git a/lib/security/serialization/types.vdl.go b/lib/security/serialization/types.vdl.go
index 096ae05..3c361cd 100644
--- a/lib/security/serialization/types.vdl.go
+++ b/lib/security/serialization/types.vdl.go
@@ -20,14 +20,14 @@
 }
 
 func (SignedHeader) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/security/serialization.SignedHeader"
+	Name string `vdl:"v.io/x/ref/lib/security/serialization.SignedHeader"`
 }) {
 }
 
 type HashCode [32]byte
 
 func (HashCode) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/security/serialization.HashCode"
+	Name string `vdl:"v.io/x/ref/lib/security/serialization.HashCode"`
 }) {
 }
 
@@ -51,7 +51,7 @@
 	SignedDataHash struct{ Value HashCode }
 	// __SignedDataReflect describes the SignedData union type.
 	__SignedDataReflect struct {
-		Name  string "v.io/x/ref/lib/security/serialization.SignedData"
+		Name  string `vdl:"v.io/x/ref/lib/security/serialization.SignedData"`
 		Type  SignedData
 		Union struct {
 			Signature SignedDataSignature
diff --git a/lib/security/type.vdl.go b/lib/security/type.vdl.go
index 127698b..3fabed7 100644
--- a/lib/security/type.vdl.go
+++ b/lib/security/type.vdl.go
@@ -18,7 +18,7 @@
 type blessingRootsState map[string][]security.BlessingPattern
 
 func (blessingRootsState) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/security.blessingRootsState"
+	Name string `vdl:"v.io/x/ref/lib/security.blessingRootsState"`
 }) {
 }
 
@@ -35,7 +35,7 @@
 }
 
 func (blessingStoreState) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/security.blessingStoreState"
+	Name string `vdl:"v.io/x/ref/lib/security.blessingStoreState"`
 }) {
 }
 
diff --git a/lib/stats/stats_test.go b/lib/stats/stats_test.go
index 605f7c6..d95a660 100644
--- a/lib/stats/stats_test.go
+++ b/lib/stats/stats_test.go
@@ -321,7 +321,7 @@
 	}
 	for i, tc := range testcases {
 		if got := m.Incr(tc.key, tc.incr); got != tc.expected {
-			t.Errorf("unexpected result for #%d. Got %v, expected %v", got, tc.expected)
+			t.Errorf("unexpected result for #%d. Got %v, expected %v", i, got, tc.expected)
 		}
 		got, err := libstats.Value("testing/foo/" + tc.key)
 		if err != nil {
diff --git a/lib/v23cmd/v23cmd.go b/lib/v23cmd/v23cmd.go
new file mode 100644
index 0000000..cdca697
--- /dev/null
+++ b/lib/v23cmd/v23cmd.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 v23cmd implements utilities for running v23 cmdline programs.
+//
+// The cmdline package requires a Runner that implements Run(env, args), but for
+// v23 programs we'd like to implement Run(ctx, env, args) instead.  The
+// initialization of the ctx is also tricky, since v23.Init() calls flag.Parse,
+// but the cmdline package needs to call flag.Parse first.
+//
+// The RunnerFunc package-level function allows us to write run functions of the
+// form Run(ctx, env, args), retaining static type-safety, and also getting the
+// flag.Parse ordering right.  In addition the Run and ParseAndRun functions may
+// be called in tests, to pass your own ctx into your command runners.
+package v23cmd
+
+import (
+	"errors"
+
+	"v.io/v23"
+	"v.io/v23/context"
+	"v.io/x/lib/cmdline2"
+)
+
+var (
+	ErrUnknownRunner = errors.New("v23cmd: unknown runner")
+
+	// The strategy behind this package is this slice, which only grows and never
+	// shrinks.  Here we maintain a mapping between an index number and the
+	// originally registered function, so that we can look the function up in a
+	// typesafe manner.
+	funcs  []func(*context.T, *cmdline2.Env, []string) error
+	initFn = v23.Init
+)
+
+// indexRunner implements cmdline.Runner by looking up the index in the funcs
+// slice to retrieve the originally registered function.
+type indexRunner uint
+
+func (ix indexRunner) Run(env *cmdline2.Env, args []string) error {
+	if int(ix) < len(funcs) {
+		ctx, shutdown := initFn()
+		err := funcs[ix](ctx, env, args)
+		shutdown()
+		return err
+	}
+	return ErrUnknownRunner
+}
+
+// RunnerFunc behaves similarly to cmdline.RunnerFunc, but takes a run function
+// fn that includes a context as the first arg.  The context is created via
+// v23.Init when Run is called on the returned Runner.
+func RunnerFunc(fn func(*context.T, *cmdline2.Env, []string) error) cmdline2.Runner {
+	ix := indexRunner(len(funcs))
+	funcs = append(funcs, fn)
+	return ix
+}
+
+// Lookup returns the function registered via RunnerFunc corresponding to
+// runner, or nil if it doesn't exist.
+func Lookup(runner cmdline2.Runner) func(*context.T, *cmdline2.Env, []string) error {
+	if ix, ok := runner.(indexRunner); ok && int(ix) < len(funcs) {
+		return funcs[ix]
+	}
+	return nil
+}
+
+// Run performs Lookup, and then runs the function with the given ctx, env and
+// args.
+//
+// Doesn't call v23.Init; the context initialization is up to you.
+func Run(runner cmdline2.Runner, ctx *context.T, env *cmdline2.Env, args []string) error {
+	if fn := Lookup(runner); fn != nil {
+		return fn(ctx, env, args)
+	}
+	return ErrUnknownRunner
+}
+
+// ParseAndRun parses the cmd with the given env and args, and calls Run to run
+// the returned runner with the ctx, env and args.
+//
+// Doesn't call v23.Init; the context initialization is up to you.
+func ParseAndRun(cmd *cmdline2.Command, ctx *context.T, env *cmdline2.Env, args []string) error {
+	runner, args, err := cmdline2.Parse(cmd, env, args)
+	if err != nil {
+		return err
+	}
+	return Run(runner, ctx, env, args)
+}
diff --git a/lib/vdl/build/build_test.go b/lib/vdl/build/build_test.go
index 2c9052e..e437bdb 100644
--- a/lib/vdl/build/build_test.go
+++ b/lib/vdl/build/build_test.go
@@ -203,10 +203,10 @@
 	}
 	for _, test := range tests {
 		if got, want := build.IsDirPath(test.Path), test.IsDir; got != want {
-			t.Errorf("IsDirPath(%q) want %v", want)
+			t.Errorf("IsDirPath(%q) want %v", test.Path, want)
 		}
 		if got, want := build.IsImportPath(test.Path), !test.IsDir; got != want {
-			t.Errorf("IsImportPath(%q) want %v", want)
+			t.Errorf("IsImportPath(%q) want %v", test.Path, want)
 		}
 	}
 }
diff --git a/lib/vdl/codegen/golang/type.go b/lib/vdl/codegen/golang/type.go
index c99dafb..7c6d612 100644
--- a/lib/vdl/codegen/golang/type.go
+++ b/lib/vdl/codegen/golang/type.go
@@ -137,7 +137,7 @@
 			"\n\treturn \"\""+
 			"\n}"+
 			"\n\nfunc (%[1]s) __VDLReflect(struct{"+
-			"\n\tName string %[3]q"+
+			"\n\tName string `vdl:%[3]q`"+
 			"\n\tEnum struct{ %[2]s string }"+
 			"\n}) {"+
 			"\n}",
@@ -153,7 +153,7 @@
 		s += "\n}" + def.DocSuffix
 		s += fmt.Sprintf("\n"+
 			"\nfunc (%[1]s) __VDLReflect(struct{"+
-			"\n\tName string %[2]q"+
+			"\n\tName string `vdl:%[2]q`"+
 			"\n}) {"+
 			"\n}",
 			def.Name, qualifiedIdent(def.File, def.Name))
@@ -180,7 +180,7 @@
 		}
 		s += fmt.Sprintf("\n\t// __%[1]sReflect describes the %[1]s union type."+
 			"\n\t__%[1]sReflect struct {"+
-			"\n\t\tName string %[2]q"+
+			"\n\t\tName string `vdl:%[2]q`"+
 			"\n\t\tType %[1]s"+
 			"\n\t\tUnion struct {", def.Name, qualifiedIdent(def.File, def.Name))
 		for ix := 0; ix < t.NumField(); ix++ {
@@ -199,7 +199,7 @@
 		s += typeGo(data, def.BaseType) + def.DocSuffix
 		s += fmt.Sprintf("\n"+
 			"\nfunc (%[1]s) __VDLReflect(struct{"+
-			"\n\tName string %[2]q"+
+			"\n\tName string `vdl:%[2]q`"+
 			"\n}) {"+
 			"\n}",
 			def.Name, qualifiedIdent(def.File, def.Name))
diff --git a/lib/vdl/codegen/golang/type_test.go b/lib/vdl/codegen/golang/type_test.go
index 495638e..4a650c8 100644
--- a/lib/vdl/codegen/golang/type_test.go
+++ b/lib/vdl/codegen/golang/type_test.go
@@ -101,7 +101,7 @@
 }
 
 func (TestEnum) __VDLReflect(struct{
-	Name string "TestEnum"
+	Name string ` + "`vdl:\"TestEnum\"`" + `
 	Enum struct{ A, B, C string }
 }) {
 }`},
@@ -111,7 +111,7 @@
 }
 
 func (TestStruct) __VDLReflect(struct{
-	Name string "TestStruct"
+	Name string ` + "`vdl:\"TestStruct\"`" + `
 }) {
 }`},
 		{tUnion, `type (
@@ -132,7 +132,7 @@
 	TestUnionB struct{ Value int64 }
 	// __TestUnionReflect describes the TestUnion union type.
 	__TestUnionReflect struct {
-		Name string "TestUnion"
+		Name string ` + "`vdl:\"TestUnion\"`" + `
 		Type TestUnion
 		Union struct {
 			A TestUnionA
diff --git a/lib/vdl/codegen/java/file_array.go b/lib/vdl/codegen/java/file_array.go
index 0501a75..af38b90 100644
--- a/lib/vdl/codegen/java/file_array.go
+++ b/lib/vdl/codegen/java/file_array.go
@@ -11,7 +11,6 @@
 	"strings"
 
 	"v.io/x/ref/lib/vdl/compile"
-	"v.io/x/ref/lib/vdl/vdlutil"
 )
 
 const arrayTmpl = header + `
@@ -56,7 +55,7 @@
 
 // genJavaArrayFile generates the Java class file for the provided named array type.
 func genJavaArrayFile(tdef *compile.TypeDef, env *compile.Env) JavaFileInfo {
-	javaTypeName := vdlutil.FirstRuneToUpper(tdef.Name)
+	name, access := javaTypeName(tdef, env)
 	elemType := javaType(tdef.Type.Elem(), true, env)
 	elems := strings.TrimSuffix(strings.Repeat(javaZeroValue(tdef.Type.Elem(), env)+", ", tdef.Type.Len()), ", ")
 	zeroValue := fmt.Sprintf("new %s[] {%s}", elemType, elems)
@@ -76,13 +75,13 @@
 		ZeroValue         string
 	}{
 		FileDoc:           tdef.File.Package.FileDoc,
-		AccessModifier:    accessModifierForName(tdef.Name),
+		AccessModifier:    access,
 		Doc:               javaDocInComment(tdef.Doc),
 		ElemType:          elemType,
 		ElemIsPrimitive:   !isClass(tdef.Type.Elem(), env),
 		ElemPrimitiveType: javaType(tdef.Type.Elem(), false, env),
 		Length:            tdef.Type.Len(),
-		Name:              javaTypeName,
+		Name:              name,
 		Package:           javaPath(javaGenPkgPath(tdef.File.Package.GenPath)),
 		SourceFile:        tdef.File.BaseName,
 		VdlTypeName:       tdef.Type.Name(),
@@ -95,7 +94,7 @@
 		log.Fatalf("vdl: couldn't execute array template: %v", err)
 	}
 	return JavaFileInfo{
-		Name: javaTypeName + ".java",
+		Name: name + ".java",
 		Data: buf.Bytes(),
 	}
 }
diff --git a/lib/vdl/codegen/java/file_client_interface.go b/lib/vdl/codegen/java/file_client_interface.go
index db4366f..ffc17c8 100644
--- a/lib/vdl/codegen/java/file_client_interface.go
+++ b/lib/vdl/codegen/java/file_client_interface.go
@@ -23,6 +23,7 @@
 {{ range $method := .Methods }}
     {{/* If this method has multiple return arguments, generate the class. */}}
     {{ if $method.IsMultipleRet }}
+    @io.v.v23.vdl.MultiReturn
     public static class {{ $method.UppercaseMethodName }}Out {
         {{ range $retArg := $method.RetArgs }}
         public {{ $retArg.Type }} {{ $retArg.Name }};
@@ -47,8 +48,8 @@
 	AccessModifier      string
 	Args                string
 	Doc                 string
-	IsMultipleRet       bool
 	Name                string
+	IsMultipleRet       bool
 	RetArgs             []clientInterfaceArg
 	RetType             string
 	UppercaseMethodName string
@@ -66,8 +67,8 @@
 	}
 }
 
-func clientInterfaceOutArg(iface *compile.Interface, method *compile.Method, isService bool, env *compile.Env) string {
-	if isStreamingMethod(method) && !isService {
+func clientInterfaceOutArg(iface *compile.Interface, method *compile.Method, env *compile.Env) string {
+	if isStreamingMethod(method) {
 		return fmt.Sprintf("io.v.v23.vdl.ClientStream<%s, %s, %s>", javaType(method.InStream, true, env), javaType(method.OutStream, true, env), clientInterfaceNonStreamingOutArg(iface, method, true, env))
 	}
 	return clientInterfaceNonStreamingOutArg(iface, method, false, env)
@@ -87,10 +88,10 @@
 		AccessModifier:      accessModifierForName(method.Name),
 		Args:                javaDeclarationArgStr(method.InArgs, env, true),
 		Doc:                 method.Doc,
-		IsMultipleRet:       len(retArgs) > 1,
 		Name:                vdlutil.FirstRuneToLower(method.Name),
+		IsMultipleRet:       len(retArgs) > 1,
 		RetArgs:             retArgs,
-		RetType:             clientInterfaceOutArg(iface, method, false, env),
+		RetType:             clientInterfaceOutArg(iface, method, env),
 		UppercaseMethodName: method.Name,
 	}
 }
diff --git a/lib/vdl/codegen/java/file_client_stub.go b/lib/vdl/codegen/java/file_client_stub.go
index 4f8c3dc..fd28e07 100644
--- a/lib/vdl/codegen/java/file_client_stub.go
+++ b/lib/vdl/codegen/java/file_client_stub.go
@@ -43,24 +43,6 @@
 
     }
 
-    // Methods from interface UniversalServiceMethods.
-    @Override
-    public io.v.v23.vdlroot.signature.Interface getSignature(io.v.v23.context.VContext context) throws io.v.v23.verror.VException {
-        return getSignature(context, null);
-    }
-    @Override
-    public io.v.v23.vdlroot.signature.Interface getSignature(io.v.v23.context.VContext context, io.v.v23.Options vOpts) throws io.v.v23.verror.VException {
-        // Start the call.
-        final io.v.v23.rpc.Client.Call _call = getClient(context).startCall(context, this.vName, "signature", new java.lang.Object[0], new java.lang.reflect.Type[0], vOpts);
-
-        // Finish the call.
-        final java.lang.reflect.Type[] _resultTypes = new java.lang.reflect.Type[]{
-            new com.google.common.reflect.TypeToken<io.v.v23.vdlroot.signature.Interface>() {}.getType(),
-        };
-        final java.lang.Object[] _results = _call.finish(_resultTypes);
-        return (io.v.v23.vdlroot.signature.Interface)_results[0];
-    }
-
     // Methods from interface {{ .ServiceName }}Client.
 {{/* Iterate over methods defined directly in the body of this service */}}
 {{ range $method := .Methods }}
@@ -218,7 +200,7 @@
 		NotStreaming:            !isStreamingMethod(method),
 		OutArgs:                 outArgs,
 		RecvType:                javaType(method.OutStream, true, env),
-		RetType:                 clientInterfaceOutArg(iface, method, false, env),
+		RetType:                 clientInterfaceOutArg(iface, method, env),
 		Returns:                 len(method.OutArgs) >= 1 || isStreamingMethod(method),
 		SendType:                javaType(method.InStream, true, env),
 		ServiceName:             vdlutil.FirstRuneToUpper(iface.Name),
@@ -232,7 +214,7 @@
 		DeclarationArgs:         javaDeclarationArgStr(embedMethod.InArgs, env, true),
 		LocalStubVarName:        vdlutil.FirstRuneToLower(iface.Name) + "ClientStub",
 		Name:                    vdlutil.FirstRuneToLower(embedMethod.Name),
-		RetType:                 clientInterfaceOutArg(iface, embedMethod, false, env),
+		RetType:                 clientInterfaceOutArg(iface, embedMethod, env),
 		Returns:                 len(embedMethod.OutArgs) >= 1 || isStreamingMethod(embedMethod),
 	}
 }
diff --git a/lib/vdl/codegen/java/file_complex.go b/lib/vdl/codegen/java/file_complex.go
index 1f4fffc..14b1b89 100644
--- a/lib/vdl/codegen/java/file_complex.go
+++ b/lib/vdl/codegen/java/file_complex.go
@@ -11,7 +11,6 @@
 
 	"v.io/v23/vdl"
 	"v.io/x/ref/lib/vdl/compile"
-	"v.io/x/ref/lib/vdl/vdlutil"
 )
 
 const complexTmpl = header + `
@@ -53,7 +52,7 @@
 	default:
 		panic(fmt.Errorf("val: unhandled kind: %v", kind))
 	}
-	javaTypeName := vdlutil.FirstRuneToUpper(tdef.Name)
+	name, access := javaTypeName(tdef, env)
 	data := struct {
 		FileDoc        string
 		AccessModifier string
@@ -67,9 +66,9 @@
 		VdlTypeString  string
 	}{
 		FileDoc:        tdef.File.Package.FileDoc,
-		AccessModifier: accessModifierForName(tdef.Name),
+		AccessModifier: access,
 		Doc:            javaDocInComment(tdef.Doc),
-		Name:           javaTypeName,
+		Name:           name,
 		PackagePath:    javaPath(javaGenPkgPath(tdef.File.Package.GenPath)),
 		Source:         tdef.File.BaseName,
 		ValueType:      ValueType,
@@ -83,7 +82,7 @@
 		log.Fatalf("vdl: couldn't execute VDL complex template: %v", err)
 	}
 	return JavaFileInfo{
-		Name: javaTypeName + ".java",
+		Name: name + ".java",
 		Data: buf.Bytes(),
 	}
 }
diff --git a/lib/vdl/codegen/java/file_enum.go b/lib/vdl/codegen/java/file_enum.go
index f24484d..f69bacb 100644
--- a/lib/vdl/codegen/java/file_enum.go
+++ b/lib/vdl/codegen/java/file_enum.go
@@ -9,7 +9,6 @@
 	"log"
 
 	"v.io/x/ref/lib/vdl/compile"
-	"v.io/x/ref/lib/vdl/vdlutil"
 )
 
 const enumTmpl = header + `
@@ -56,7 +55,7 @@
 	for i := 0; i < tdef.Type.NumEnumLabel(); i++ {
 		labels[i] = tdef.Type.EnumLabel(i)
 	}
-	javaTypeName := vdlutil.FirstRuneToUpper(tdef.Name)
+	name, access := javaTypeName(tdef, env)
 	data := struct {
 		FileDoc        string
 		AccessModifier string
@@ -69,10 +68,10 @@
 		VdlTypeString  string
 	}{
 		FileDoc:        tdef.File.Package.FileDoc,
-		AccessModifier: accessModifierForName(tdef.Name),
+		AccessModifier: access,
 		EnumLabels:     labels,
 		Doc:            javaDocInComment(tdef.Doc),
-		Name:           javaTypeName,
+		Name:           name,
 		PackagePath:    javaPath(javaGenPkgPath(tdef.File.Package.GenPath)),
 		Source:         tdef.File.BaseName,
 		VdlTypeName:    tdef.Type.Name(),
@@ -84,7 +83,7 @@
 		log.Fatalf("vdl: couldn't execute enum template: %v", err)
 	}
 	return JavaFileInfo{
-		Name: javaTypeName + ".java",
+		Name: name + ".java",
 		Data: buf.Bytes(),
 	}
 }
diff --git a/lib/vdl/codegen/java/file_list.go b/lib/vdl/codegen/java/file_list.go
index 84e8d78..327469f 100644
--- a/lib/vdl/codegen/java/file_list.go
+++ b/lib/vdl/codegen/java/file_list.go
@@ -9,7 +9,6 @@
 	"log"
 
 	"v.io/x/ref/lib/vdl/compile"
-	"v.io/x/ref/lib/vdl/vdlutil"
 )
 
 const listTmpl = header + `
@@ -38,7 +37,7 @@
 
 // genJavaListFile generates the Java class file for the provided named list type.
 func genJavaListFile(tdef *compile.TypeDef, env *compile.Env) JavaFileInfo {
-	javaTypeName := vdlutil.FirstRuneToUpper(tdef.Name)
+	name, access := javaTypeName(tdef, env)
 	data := struct {
 		FileDoc        string
 		AccessModifier string
@@ -51,10 +50,10 @@
 		VdlTypeString  string
 	}{
 		FileDoc:        tdef.File.Package.FileDoc,
-		AccessModifier: accessModifierForName(tdef.Name),
+		AccessModifier: access,
 		Doc:            javaDocInComment(tdef.Doc),
 		ElemType:       javaType(tdef.Type.Elem(), true, env),
-		Name:           javaTypeName,
+		Name:           name,
 		Package:        javaPath(javaGenPkgPath(tdef.File.Package.GenPath)),
 		SourceFile:     tdef.File.BaseName,
 		VdlTypeName:    tdef.Type.Name(),
@@ -66,7 +65,7 @@
 		log.Fatalf("vdl: couldn't execute list template: %v", err)
 	}
 	return JavaFileInfo{
-		Name: javaTypeName + ".java",
+		Name: name + ".java",
 		Data: buf.Bytes(),
 	}
 }
diff --git a/lib/vdl/codegen/java/file_map.go b/lib/vdl/codegen/java/file_map.go
index d32461b..255f4bb 100644
--- a/lib/vdl/codegen/java/file_map.go
+++ b/lib/vdl/codegen/java/file_map.go
@@ -9,7 +9,6 @@
 	"log"
 
 	"v.io/x/ref/lib/vdl/compile"
-	"v.io/x/ref/lib/vdl/vdlutil"
 )
 
 const mapTmpl = header + `
@@ -39,7 +38,7 @@
 
 // genJavaMapFile generates the Java class file for the provided named map type.
 func genJavaMapFile(tdef *compile.TypeDef, env *compile.Env) JavaFileInfo {
-	javaTypeName := vdlutil.FirstRuneToUpper(tdef.Name)
+	name, access := javaTypeName(tdef, env)
 	data := struct {
 		FileDoc        string
 		AccessModifier string
@@ -53,11 +52,11 @@
 		VdlTypeString  string
 	}{
 		FileDoc:        tdef.File.Package.FileDoc,
-		AccessModifier: accessModifierForName(tdef.Name),
+		AccessModifier: access,
 		Doc:            javaDocInComment(tdef.Doc),
 		ElemType:       javaType(tdef.Type.Elem(), true, env),
 		KeyType:        javaType(tdef.Type.Key(), true, env),
-		Name:           javaTypeName,
+		Name:           name,
 		Package:        javaPath(javaGenPkgPath(tdef.File.Package.GenPath)),
 		SourceFile:     tdef.File.BaseName,
 		VdlTypeName:    tdef.Type.Name(),
@@ -69,7 +68,7 @@
 		log.Fatalf("vdl: couldn't execute map template: %v", err)
 	}
 	return JavaFileInfo{
-		Name: javaTypeName + ".java",
+		Name: name + ".java",
 		Data: buf.Bytes(),
 	}
 }
diff --git a/lib/vdl/codegen/java/file_primitive.go b/lib/vdl/codegen/java/file_primitive.go
index d2db2ff..c94760e 100644
--- a/lib/vdl/codegen/java/file_primitive.go
+++ b/lib/vdl/codegen/java/file_primitive.go
@@ -10,7 +10,6 @@
 
 	"v.io/v23/vdl"
 	"v.io/x/ref/lib/vdl/compile"
-	"v.io/x/ref/lib/vdl/vdlutil"
 )
 
 const primitiveTmpl = header + `
@@ -71,7 +70,7 @@
 
 // genJavaPrimitiveFile generates the Java class file for the provided user-defined type.
 func genJavaPrimitiveFile(tdef *compile.TypeDef, env *compile.Env) JavaFileInfo {
-	javaTypeName := vdlutil.FirstRuneToUpper(tdef.Name)
+	name, access := javaTypeName(tdef, env)
 	data := struct {
 		FileDoc                  string
 		AccessModifier           string
@@ -86,9 +85,9 @@
 		VdlTypeString            string
 	}{
 		FileDoc:                  tdef.File.Package.FileDoc,
-		AccessModifier:           accessModifierForName(tdef.Name),
+		AccessModifier:           access,
 		Doc:                      javaDocInComment(tdef.Doc),
-		Name:                     javaTypeName,
+		Name:                     name,
 		PackagePath:              javaPath(javaGenPkgPath(tdef.File.Package.GenPath)),
 		Source:                   tdef.File.BaseName,
 		ConstructorType:          javaConstructorType(tdef.Type),
@@ -103,7 +102,7 @@
 		log.Fatalf("vdl: couldn't execute primitive template: %v", err)
 	}
 	return JavaFileInfo{
-		Name: javaTypeName + ".java",
+		Name: name + ".java",
 		Data: buf.Bytes(),
 	}
 }
diff --git a/lib/vdl/codegen/java/file_server_interface.go b/lib/vdl/codegen/java/file_server_interface.go
index 947fa66..d3d8891 100644
--- a/lib/vdl/codegen/java/file_server_interface.go
+++ b/lib/vdl/codegen/java/file_server_interface.go
@@ -24,6 +24,16 @@
 )
 {{ .AccessModifier }} interface {{ .ServiceName }}Server {{ .Extends }} {
 {{ range $method := .Methods }}
+    {{/* If this method has multiple return arguments, generate the class. */}}
+    {{ if $method.IsMultipleRet }}
+    @io.v.v23.vdl.MultiReturn
+    public static class {{ $method.UppercaseMethodName }}Out {
+        {{ range $retArg := $method.RetArgs }}
+        public {{ $retArg.Type }} {{ $retArg.Name }};
+        {{ end }}
+    }
+    {{ end }}
+
     {{/* Generate the method signature. */}}
     {{ $method.Doc }}
     {{ $method.AccessModifier }} {{ $method.RetType }} {{ $method.Name }}(final io.v.v23.context.VContext ctx, final io.v.v23.rpc.ServerCall call{{ $method.Args }}) throws io.v.v23.verror.VException;
@@ -31,23 +41,31 @@
 }
 `
 
-func serverInterfaceOutArg(method *compile.Method, iface *compile.Interface, env *compile.Env) string {
+func serverInterfaceOutArg(iface *compile.Interface, method *compile.Method, env *compile.Env) string {
 	switch len(method.OutArgs) {
 	case 0:
 		return "void"
 	case 1:
 		return javaType(method.OutArgs[0].Type, false, env)
 	default:
-		return javaPath(path.Join(interfaceFullyQualifiedName(iface)+"Client", method.Name+"Out"))
+		return javaPath(path.Join(interfaceFullyQualifiedName(iface)+"Server", method.Name+"Out"))
 	}
 }
 
+type serverInterfaceArg struct {
+	Type string
+	Name string
+}
+
 type serverInterfaceMethod struct {
-	AccessModifier string
-	Args           string
-	Doc            string
-	Name           string
-	RetType        string
+	AccessModifier      string
+	Args                string
+	Doc                 string
+	Name                string
+	IsMultipleRet       bool
+	RetArgs             []serverInterfaceArg
+	RetType             string
+	UppercaseMethodName string
 }
 
 func processServerInterfaceMethod(method *compile.Method, iface *compile.Interface, env *compile.Env) serverInterfaceMethod {
@@ -55,12 +73,25 @@
 	if isStreamingMethod(method) {
 		args += fmt.Sprintf(", io.v.v23.vdl.Stream<%s, %s> stream", javaType(method.OutStream, true, env), javaType(method.InStream, true, env))
 	}
+	retArgs := make([]serverInterfaceArg, len(method.OutArgs))
+	for i := 0; i < len(method.OutArgs); i++ {
+		if method.OutArgs[i].Name != "" {
+			retArgs[i].Name = vdlutil.FirstRuneToLower(method.OutArgs[i].Name)
+		} else {
+			retArgs[i].Name = fmt.Sprintf("ret%d", i+1)
+		}
+		retArgs[i].Type = javaType(method.OutArgs[i].Type, false, env)
+	}
+
 	return serverInterfaceMethod{
-		AccessModifier: accessModifierForName(method.Name),
-		Args:           args,
-		Doc:            method.Doc,
-		Name:           vdlutil.FirstRuneToLower(method.Name),
-		RetType:        serverInterfaceOutArg(method, iface, env),
+		AccessModifier:      accessModifierForName(method.Name),
+		Args:                args,
+		Doc:                 method.Doc,
+		Name:                vdlutil.FirstRuneToLower(method.Name),
+		IsMultipleRet:       len(retArgs) > 1,
+		RetArgs:             retArgs,
+		RetType:             serverInterfaceOutArg(iface, method, env),
+		UppercaseMethodName: method.Name,
 	}
 }
 
diff --git a/lib/vdl/codegen/java/file_server_wrapper.go b/lib/vdl/codegen/java/file_server_wrapper.go
index d7b69f9..5043d6d 100644
--- a/lib/vdl/codegen/java/file_server_wrapper.go
+++ b/lib/vdl/codegen/java/file_server_wrapper.go
@@ -39,7 +39,7 @@
     /**
      * Returns a description of this server.
      */
-    public io.v.v23.vdlroot.signature.Interface signature(final io.v.v23.context.VContext ctx, final io.v.v23.rpc.ServerCall call) throws io.v.v23.verror.VException {
+    public io.v.v23.vdlroot.signature.Interface signature() {
         java.util.List<io.v.v23.vdlroot.signature.Embed> embeds = new java.util.ArrayList<io.v.v23.vdlroot.signature.Embed>();
         java.util.List<io.v.v23.vdlroot.signature.Method> methods = new java.util.ArrayList<io.v.v23.vdlroot.signature.Method>();
         {{ range $method := .Methods }}
@@ -75,7 +75,7 @@
      * by this server.
      */
     @SuppressWarnings("unused")
-    public io.v.v23.vdl.VdlValue[] getMethodTags(final io.v.v23.rpc.StreamServerCall call, final java.lang.String method) throws io.v.v23.verror.VException {
+    public io.v.v23.vdl.VdlValue[] getMethodTags(final java.lang.String method) throws io.v.v23.verror.VException {
         {{ range $methodName, $tags := .MethodTags }}
         if ("{{ $methodName }}".equals(method)) {
             try {
@@ -89,7 +89,7 @@
         {{ end }}
         {{ range $embed := .Embeds }}
         {
-            final io.v.v23.vdl.VdlValue[] tags = this.{{ $embed.LocalWrapperVarName }}.getMethodTags(call, method);
+            final io.v.v23.vdl.VdlValue[] tags = this.{{ $embed.LocalWrapperVarName }}.getMethodTags(method);
             if (tags != null) {
                 return tags;
             }
@@ -195,7 +195,7 @@
 		IsStreaming:     isStreamingMethod(method),
 		Name:            vdlutil.FirstRuneToLower(method.Name),
 		RecvType:        javaType(method.InStream, true, env),
-		RetType:         clientInterfaceOutArg(iface, method, true, env),
+		RetType:         serverInterfaceOutArg(iface, method, env),
 		RetJavaTypes:    retArgTypes,
 		Returns:         len(method.OutArgs) >= 1,
 		SendType:        javaType(method.OutStream, true, env),
@@ -211,7 +211,7 @@
 		DeclarationArgs:     javaDeclarationArgStr(embedMethod.InArgs, env, true),
 		LocalWrapperVarName: vdlutil.FirstRuneToLower(iface.Name) + "Wrapper",
 		Name:                vdlutil.FirstRuneToLower(embedMethod.Name),
-		RetType:             clientInterfaceOutArg(iface, embedMethod, true, env),
+		RetType:             serverInterfaceOutArg(iface, embedMethod, env),
 		Returns:             len(embedMethod.OutArgs) >= 1,
 	}
 }
@@ -227,9 +227,6 @@
 		})
 	}
 	methodTags := make(map[string][]methodTag)
-	// Add generated methods to the tag map:
-	methodTags["signature"] = []methodTag{}
-	methodTags["getMethodTags"] = []methodTag{}
 	// Copy method tags off of the interface.
 	methods := make([]serverWrapperMethod, len(iface.Methods))
 	for i, method := range iface.Methods {
diff --git a/lib/vdl/codegen/java/file_set.go b/lib/vdl/codegen/java/file_set.go
index 06c074b..cd703c1 100644
--- a/lib/vdl/codegen/java/file_set.go
+++ b/lib/vdl/codegen/java/file_set.go
@@ -9,7 +9,6 @@
 	"log"
 
 	"v.io/x/ref/lib/vdl/compile"
-	"v.io/x/ref/lib/vdl/vdlutil"
 )
 
 const setTmpl = header + `
@@ -39,7 +38,7 @@
 
 // genJavaSetFile generates the Java class file for the provided named set type.
 func genJavaSetFile(tdef *compile.TypeDef, env *compile.Env) JavaFileInfo {
-	javaTypeName := vdlutil.FirstRuneToUpper(tdef.Name)
+	name, access := javaTypeName(tdef, env)
 	data := struct {
 		FileDoc        string
 		AccessModifier string
@@ -52,10 +51,10 @@
 		VdlTypeString  string
 	}{
 		FileDoc:        tdef.File.Package.FileDoc,
-		AccessModifier: accessModifierForName(tdef.Name),
+		AccessModifier: access,
 		Doc:            javaDocInComment(tdef.Doc),
 		KeyType:        javaType(tdef.Type.Key(), true, env),
-		Name:           javaTypeName,
+		Name:           name,
 		Package:        javaPath(javaGenPkgPath(tdef.File.Package.GenPath)),
 		SourceFile:     tdef.File.BaseName,
 		VdlTypeName:    tdef.Type.Name(),
@@ -67,7 +66,7 @@
 		log.Fatalf("vdl: couldn't execute set template: %v", err)
 	}
 	return JavaFileInfo{
-		Name: javaTypeName + ".java",
+		Name: name + ".java",
 		Data: buf.Bytes(),
 	}
 }
diff --git a/lib/vdl/codegen/java/file_struct.go b/lib/vdl/codegen/java/file_struct.go
index 4db4d39..f36c7ef 100644
--- a/lib/vdl/codegen/java/file_struct.go
+++ b/lib/vdl/codegen/java/file_struct.go
@@ -164,7 +164,7 @@
 		}
 	}
 
-	javaTypeName := vdlutil.FirstRuneToUpper(tdef.Name)
+	name, access := javaTypeName(tdef, env)
 	data := struct {
 		FileDoc        string
 		AccessModifier string
@@ -178,11 +178,11 @@
 		VdlTypeString  string
 	}{
 		FileDoc:        tdef.File.Package.FileDoc,
-		AccessModifier: accessModifierForName(tdef.Name),
+		AccessModifier: access,
 		Doc:            javaDocInComment(tdef.Doc),
 		Fields:         fields,
 		FieldsAsArgs:   javaFieldArgStr(tdef.Type, env),
-		Name:           javaTypeName,
+		Name:           name,
 		PackagePath:    javaPath(javaGenPkgPath(tdef.File.Package.GenPath)),
 		Source:         tdef.File.BaseName,
 		VdlTypeName:    tdef.Type.Name(),
@@ -194,7 +194,7 @@
 		log.Fatalf("vdl: couldn't execute struct template: %v", err)
 	}
 	return JavaFileInfo{
-		Name: javaTypeName + ".java",
+		Name: name + ".java",
 		Data: buf.Bytes(),
 	}
 }
diff --git a/lib/vdl/codegen/java/file_union.go b/lib/vdl/codegen/java/file_union.go
index 14b6d45..f8a0ac2 100644
--- a/lib/vdl/codegen/java/file_union.go
+++ b/lib/vdl/codegen/java/file_union.go
@@ -9,7 +9,6 @@
 	"log"
 
 	"v.io/x/ref/lib/vdl/compile"
-	"v.io/x/ref/lib/vdl/vdlutil"
 )
 
 const unionTmpl = header + `
@@ -80,7 +79,7 @@
 			ZeroValue:           javaZeroValue(fld.Type, env),
 		}
 	}
-	javaTypeName := vdlutil.FirstRuneToUpper(tdef.Name)
+	name, access := javaTypeName(tdef, env)
 	data := struct {
 		FileDoc        string
 		AccessModifier string
@@ -93,10 +92,10 @@
 		VdlTypeString  string
 	}{
 		FileDoc:        tdef.File.Package.FileDoc,
-		AccessModifier: accessModifierForName(tdef.Name),
+		AccessModifier: access,
 		Doc:            javaDocInComment(tdef.Doc),
 		Fields:         fields,
-		Name:           javaTypeName,
+		Name:           name,
 		PackagePath:    javaPath(javaGenPkgPath(tdef.File.Package.GenPath)),
 		Source:         tdef.File.BaseName,
 		VdlTypeName:    tdef.Type.Name(),
@@ -108,7 +107,7 @@
 		log.Fatalf("vdl: couldn't execute union template: %v", err)
 	}
 	return JavaFileInfo{
-		Name: javaTypeName + ".java",
+		Name: name + ".java",
 		Data: buf.Bytes(),
 	}
 }
diff --git a/lib/vdl/codegen/java/generate.go b/lib/vdl/codegen/java/generate.go
index bb301f8..008e2c1 100644
--- a/lib/vdl/codegen/java/generate.go
+++ b/lib/vdl/codegen/java/generate.go
@@ -107,6 +107,7 @@
 var nativeTypePackageWhitelist = map[string]bool{
 	"time":                                   true,
 	"v.io/v23/security":                      true,
+	"v.io/v23/security/access":               true,
 	"v.io/x/ref/lib/vdl/testdata/nativetest": true,
 }
 
@@ -117,10 +118,4 @@
 	if len(pkg.Config.Java.WireToNativeTypes) > 0 && !nativeTypePackageWhitelist[pkg.Path] {
 		env.Errors.Errorf("%s: Java.WireToNativeTypes is restricted to whitelisted VDL packages", vdlconfig)
 	}
-	// Make sure each wire type is actually defined in the package.
-	for wire, _ := range pkg.Config.Java.WireToNativeTypes {
-		if def := pkg.ResolveType(wire); def == nil {
-			env.Errors.Errorf("%s: type %s specified in Java.WireToNativeTypes undefined", vdlconfig, wire)
-		}
-	}
 }
diff --git a/lib/vdl/codegen/java/util_interface.go b/lib/vdl/codegen/java/util_interface.go
index 73bc317..9a35327 100644
--- a/lib/vdl/codegen/java/util_interface.go
+++ b/lib/vdl/codegen/java/util_interface.go
@@ -38,14 +38,18 @@
 // javaClientExtendsStr creates an extends clause for a client interface
 // e.g. "extends com.a.B, com.d.E"
 func javaClientExtendsStr(embeds []*compile.Interface) string {
+	if len(embeds) == 0 {
+		return ""
+	}
 	var buf bytes.Buffer
 	buf.WriteString("extends ")
-	for _, embed := range embeds {
+	for i, embed := range embeds {
+		if i > 0 {
+			buf.WriteString(", ")
+		}
 		buf.WriteString(javaPath(interfaceFullyQualifiedName(embed)))
 		buf.WriteString("Client")
-		buf.WriteString(", ")
 	}
-	buf.WriteString("io.v.v23.rpc.UniversalServiceMethods")
 	return buf.String()
 }
 
diff --git a/lib/vdl/codegen/java/util_type.go b/lib/vdl/codegen/java/util_type.go
index 2aaf468..b369b2a 100644
--- a/lib/vdl/codegen/java/util_type.go
+++ b/lib/vdl/codegen/java/util_type.go
@@ -20,7 +20,8 @@
 		name, _ := javaBuiltInType(def.Type, forceClass)
 		return name
 	}
-	return javaPath(path.Join(javaGenPkgPath(def.File.Package.GenPath), vdlutil.FirstRuneToUpper(def.Name)))
+	name, _ := javaTypeName(def, env)
+	return javaPath(path.Join(javaGenPkgPath(def.File.Package.GenPath), name))
 }
 
 // javaReflectType returns java.reflect.Type string for provided VDL type.
@@ -104,13 +105,21 @@
 	}
 }
 
+func javaTypeName(def *compile.TypeDef, env *compile.Env) (string, string) {
+	if native, ok := def.File.Package.Config.Java.WireTypeRenames[def.Name]; ok {
+		return native, accessModifierForName(native)
+	}
+	return vdlutil.FirstRuneToUpper(def.Name), accessModifierForName(def.Name)
+}
+
 func javaNativeType(t *vdl.Type, env *compile.Env) (string, bool) {
 	if t == vdl.ErrorType {
 		return "io.v.v23.verror.VException", true
 	}
 	if def := env.FindTypeDef(t); def != nil {
 		pkg := def.File.Package
-		if native, ok := pkg.Config.Java.WireToNativeTypes[def.Name]; ok {
+		name, _ := javaTypeName(def, env)
+		if native, ok := pkg.Config.Java.WireToNativeTypes[name]; ok {
 			// There is a Java native type configured for this defined type.
 			return native, true
 		}
diff --git a/lib/vdl/compile/compile_test.go b/lib/vdl/compile/compile_test.go
index 9192a77..63bfe8e 100644
--- a/lib/vdl/compile/compile_test.go
+++ b/lib/vdl/compile/compile_test.go
@@ -36,10 +36,10 @@
 			continue
 		}
 		if got, want := pkg.Name, test.name; got != want {
-			t.Errorf("%s got package name %s, want %s", got, want)
+			t.Errorf("%v got package name %s, want %s", buildPkg, got, want)
 		}
 		if got, want := pkg.Path, path; got != want {
-			t.Errorf("%s got package path %s, want %s", got, want)
+			t.Errorf("%v got package path %s, want %s", buildPkg, got, want)
 		}
 		test.expect(t, test.name, pkg)
 	}
diff --git a/lib/vdl/testdata/base/base.vdl.go b/lib/vdl/testdata/base/base.vdl.go
index c1c5a00..c68af45 100644
--- a/lib/vdl/testdata/base/base.vdl.go
+++ b/lib/vdl/testdata/base/base.vdl.go
@@ -23,91 +23,91 @@
 type NamedBool bool
 
 func (NamedBool) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/vdl/testdata/base.NamedBool"
+	Name string `vdl:"v.io/x/ref/lib/vdl/testdata/base.NamedBool"`
 }) {
 }
 
 type NamedByte byte
 
 func (NamedByte) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/vdl/testdata/base.NamedByte"
+	Name string `vdl:"v.io/x/ref/lib/vdl/testdata/base.NamedByte"`
 }) {
 }
 
 type NamedUint16 uint16
 
 func (NamedUint16) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/vdl/testdata/base.NamedUint16"
+	Name string `vdl:"v.io/x/ref/lib/vdl/testdata/base.NamedUint16"`
 }) {
 }
 
 type NamedUint32 uint32
 
 func (NamedUint32) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/vdl/testdata/base.NamedUint32"
+	Name string `vdl:"v.io/x/ref/lib/vdl/testdata/base.NamedUint32"`
 }) {
 }
 
 type NamedUint64 uint64
 
 func (NamedUint64) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/vdl/testdata/base.NamedUint64"
+	Name string `vdl:"v.io/x/ref/lib/vdl/testdata/base.NamedUint64"`
 }) {
 }
 
 type NamedInt16 int16
 
 func (NamedInt16) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/vdl/testdata/base.NamedInt16"
+	Name string `vdl:"v.io/x/ref/lib/vdl/testdata/base.NamedInt16"`
 }) {
 }
 
 type NamedInt32 int32
 
 func (NamedInt32) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/vdl/testdata/base.NamedInt32"
+	Name string `vdl:"v.io/x/ref/lib/vdl/testdata/base.NamedInt32"`
 }) {
 }
 
 type NamedInt64 int64
 
 func (NamedInt64) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/vdl/testdata/base.NamedInt64"
+	Name string `vdl:"v.io/x/ref/lib/vdl/testdata/base.NamedInt64"`
 }) {
 }
 
 type NamedFloat32 float32
 
 func (NamedFloat32) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/vdl/testdata/base.NamedFloat32"
+	Name string `vdl:"v.io/x/ref/lib/vdl/testdata/base.NamedFloat32"`
 }) {
 }
 
 type NamedFloat64 float64
 
 func (NamedFloat64) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/vdl/testdata/base.NamedFloat64"
+	Name string `vdl:"v.io/x/ref/lib/vdl/testdata/base.NamedFloat64"`
 }) {
 }
 
 type NamedComplex64 complex64
 
 func (NamedComplex64) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/vdl/testdata/base.NamedComplex64"
+	Name string `vdl:"v.io/x/ref/lib/vdl/testdata/base.NamedComplex64"`
 }) {
 }
 
 type NamedComplex128 complex128
 
 func (NamedComplex128) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/vdl/testdata/base.NamedComplex128"
+	Name string `vdl:"v.io/x/ref/lib/vdl/testdata/base.NamedComplex128"`
 }) {
 }
 
 type NamedString string
 
 func (NamedString) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/vdl/testdata/base.NamedString"
+	Name string `vdl:"v.io/x/ref/lib/vdl/testdata/base.NamedString"`
 }) {
 }
 
@@ -159,7 +159,7 @@
 }
 
 func (NamedEnum) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/vdl/testdata/base.NamedEnum"
+	Name string `vdl:"v.io/x/ref/lib/vdl/testdata/base.NamedEnum"`
 	Enum struct{ A, B, C string }
 }) {
 }
@@ -167,28 +167,28 @@
 type NamedArray [2]bool
 
 func (NamedArray) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/vdl/testdata/base.NamedArray"
+	Name string `vdl:"v.io/x/ref/lib/vdl/testdata/base.NamedArray"`
 }) {
 }
 
 type NamedList []uint32
 
 func (NamedList) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/vdl/testdata/base.NamedList"
+	Name string `vdl:"v.io/x/ref/lib/vdl/testdata/base.NamedList"`
 }) {
 }
 
 type NamedSet map[string]struct{}
 
 func (NamedSet) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/vdl/testdata/base.NamedSet"
+	Name string `vdl:"v.io/x/ref/lib/vdl/testdata/base.NamedSet"`
 }) {
 }
 
 type NamedMap map[string]float32
 
 func (NamedMap) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/vdl/testdata/base.NamedMap"
+	Name string `vdl:"v.io/x/ref/lib/vdl/testdata/base.NamedMap"`
 }) {
 }
 
@@ -199,7 +199,7 @@
 }
 
 func (NamedStruct) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/vdl/testdata/base.NamedStruct"
+	Name string `vdl:"v.io/x/ref/lib/vdl/testdata/base.NamedStruct"`
 }) {
 }
 
@@ -223,7 +223,7 @@
 	NamedUnionC struct{ Value int32 }
 	// __NamedUnionReflect describes the NamedUnion union type.
 	__NamedUnionReflect struct {
-		Name  string "v.io/x/ref/lib/vdl/testdata/base.NamedUnion"
+		Name  string `vdl:"v.io/x/ref/lib/vdl/testdata/base.NamedUnion"`
 		Type  NamedUnion
 		Union struct {
 			A NamedUnionA
@@ -283,7 +283,7 @@
 }
 
 func (Scalars) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/vdl/testdata/base.Scalars"
+	Name string `vdl:"v.io/x/ref/lib/vdl/testdata/base.Scalars"`
 }) {
 }
 
@@ -318,14 +318,14 @@
 }
 
 func (KeyScalars) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/vdl/testdata/base.KeyScalars"
+	Name string `vdl:"v.io/x/ref/lib/vdl/testdata/base.KeyScalars"`
 }) {
 }
 
 type ScalarsArray [2]Scalars
 
 func (ScalarsArray) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/vdl/testdata/base.ScalarsArray"
+	Name string `vdl:"v.io/x/ref/lib/vdl/testdata/base.ScalarsArray"`
 }) {
 }
 
@@ -339,14 +339,14 @@
 }
 
 func (Composites) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/vdl/testdata/base.Composites"
+	Name string `vdl:"v.io/x/ref/lib/vdl/testdata/base.Composites"`
 }) {
 }
 
 type CompositesArray [2]Composites
 
 func (CompositesArray) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/vdl/testdata/base.CompositesArray"
+	Name string `vdl:"v.io/x/ref/lib/vdl/testdata/base.CompositesArray"`
 }) {
 }
 
@@ -359,7 +359,7 @@
 }
 
 func (CompComp) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/vdl/testdata/base.CompComp"
+	Name string `vdl:"v.io/x/ref/lib/vdl/testdata/base.CompComp"`
 }) {
 }
 
@@ -371,7 +371,7 @@
 }
 
 func (NestedArgs) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/vdl/testdata/base.NestedArgs"
+	Name string `vdl:"v.io/x/ref/lib/vdl/testdata/base.NestedArgs"`
 }) {
 }
 
@@ -382,7 +382,7 @@
 }
 
 func (Args) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/vdl/testdata/base.Args"
+	Name string `vdl:"v.io/x/ref/lib/vdl/testdata/base.Args"`
 }) {
 }
 
diff --git a/lib/vdl/testdata/nativedep/nativedep.vdl.go b/lib/vdl/testdata/nativedep/nativedep.vdl.go
index 6eac05b..b3b6a50 100644
--- a/lib/vdl/testdata/nativedep/nativedep.vdl.go
+++ b/lib/vdl/testdata/nativedep/nativedep.vdl.go
@@ -26,7 +26,7 @@
 }
 
 func (All) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/vdl/testdata/nativedep.All"
+	Name string `vdl:"v.io/x/ref/lib/vdl/testdata/nativedep.All"`
 }) {
 }
 
diff --git a/lib/vdl/testdata/nativedep2/nativedep2.vdl.go b/lib/vdl/testdata/nativedep2/nativedep2.vdl.go
index 1e2fb81..0fedb68 100644
--- a/lib/vdl/testdata/nativedep2/nativedep2.vdl.go
+++ b/lib/vdl/testdata/nativedep2/nativedep2.vdl.go
@@ -19,7 +19,7 @@
 type MyTime time.Time
 
 func (MyTime) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/vdl/testdata/nativedep2.MyTime"
+	Name string `vdl:"v.io/x/ref/lib/vdl/testdata/nativedep2.MyTime"`
 }) {
 }
 
diff --git a/lib/vdl/testdata/nativetest/nativetest.vdl b/lib/vdl/testdata/nativetest/nativetest.vdl
index 70f9091..93bb309 100644
--- a/lib/vdl/testdata/nativetest/nativetest.vdl
+++ b/lib/vdl/testdata/nativetest/nativetest.vdl
@@ -10,6 +10,7 @@
 type WireTime int32
 type WireSamePkg int32
 type WireMultiImport int32
+type WireRenameMe int32
 
 type WireAll struct {
 	A WireString
@@ -17,4 +18,5 @@
 	C WireTime
 	D WireSamePkg
 	E WireMultiImport
+	F WireRenameMe
 }
diff --git a/lib/vdl/testdata/nativetest/nativetest.vdl.go b/lib/vdl/testdata/nativetest/nativetest.vdl.go
index 7ff315d..983fbd0 100644
--- a/lib/vdl/testdata/nativetest/nativetest.vdl.go
+++ b/lib/vdl/testdata/nativetest/nativetest.vdl.go
@@ -20,35 +20,42 @@
 type WireString int32
 
 func (WireString) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/vdl/testdata/nativetest.WireString"
+	Name string `vdl:"v.io/x/ref/lib/vdl/testdata/nativetest.WireString"`
 }) {
 }
 
 type WireMapStringInt int32
 
 func (WireMapStringInt) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/vdl/testdata/nativetest.WireMapStringInt"
+	Name string `vdl:"v.io/x/ref/lib/vdl/testdata/nativetest.WireMapStringInt"`
 }) {
 }
 
 type WireTime int32
 
 func (WireTime) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/vdl/testdata/nativetest.WireTime"
+	Name string `vdl:"v.io/x/ref/lib/vdl/testdata/nativetest.WireTime"`
 }) {
 }
 
 type WireSamePkg int32
 
 func (WireSamePkg) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/vdl/testdata/nativetest.WireSamePkg"
+	Name string `vdl:"v.io/x/ref/lib/vdl/testdata/nativetest.WireSamePkg"`
 }) {
 }
 
 type WireMultiImport int32
 
 func (WireMultiImport) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/vdl/testdata/nativetest.WireMultiImport"
+	Name string `vdl:"v.io/x/ref/lib/vdl/testdata/nativetest.WireMultiImport"`
+}) {
+}
+
+type WireRenameMe int32
+
+func (WireRenameMe) __VDLReflect(struct {
+	Name string `vdl:"v.io/x/ref/lib/vdl/testdata/nativetest.WireRenameMe"`
 }) {
 }
 
@@ -58,10 +65,11 @@
 	C time.Time
 	D nativetest.NativeSamePkg
 	E map[nativetest.NativeSamePkg]time.Time
+	F WireRenameMe
 }
 
 func (WireAll) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/vdl/testdata/nativetest.WireAll"
+	Name string `vdl:"v.io/x/ref/lib/vdl/testdata/nativetest.WireAll"`
 }) {
 }
 
@@ -76,6 +84,7 @@
 	vdl.Register((*WireTime)(nil))
 	vdl.Register((*WireSamePkg)(nil))
 	vdl.Register((*WireMultiImport)(nil))
+	vdl.Register((*WireRenameMe)(nil))
 	vdl.Register((*WireAll)(nil))
 }
 
diff --git a/lib/vdl/testdata/nativetest/otherfile.vdl.go b/lib/vdl/testdata/nativetest/otherfile.vdl.go
index 76b8bc2..6e4a1bf 100644
--- a/lib/vdl/testdata/nativetest/otherfile.vdl.go
+++ b/lib/vdl/testdata/nativetest/otherfile.vdl.go
@@ -19,7 +19,7 @@
 type ignoreme string
 
 func (ignoreme) __VDLReflect(struct {
-	Name string "v.io/x/ref/lib/vdl/testdata/nativetest.ignoreme"
+	Name string `vdl:"v.io/x/ref/lib/vdl/testdata/nativetest.ignoreme"`
 }) {
 }
 
diff --git a/lib/vdl/testdata/nativetest/vdl.config b/lib/vdl/testdata/nativetest/vdl.config
index babfd9e..7695b14 100644
--- a/lib/vdl/testdata/nativetest/vdl.config
+++ b/lib/vdl/testdata/nativetest/vdl.config
@@ -21,12 +21,17 @@
 		},
 	},
 	Java: {
+		WireTypeRenames: {
+			"WireRenameMe": "WireRenamed",
+		},
+
 		WireToNativeTypes: {
 			"WireString": "java.lang.String",
 			"WireMapStringInt": "java.util.Map<java.lang.String, java.lang.Integer>",
 			"WireTime": "org.joda.time.DateTime",
 			"WireSamePkg": "io.v.v23.vdl.testdata.nativetest.NativeSamePkg",
 			"WireMultiImport": "java.util.Map<io.v.v23.vdl.testdata.nativetest.NativeSamePkg, org.joda.time.DateTime>",
+			"WireRenamed": "java.lang.Long",
 		},
 	},
 }
diff --git a/profiles/chrome/chromeinit.go b/profiles/chrome/chromeinit.go
index ccbfc83..0baedb3 100644
--- a/profiles/chrome/chromeinit.go
+++ b/profiles/chrome/chromeinit.go
@@ -26,7 +26,7 @@
 
 func init() {
 	v23.RegisterProfile(Init)
-	rpc.RegisterUnknownProtocol("wsh", websocket.Dial, websocket.Listener)
+	rpc.RegisterUnknownProtocol("wsh", websocket.Dial, websocket.Resolve, websocket.Listener)
 	commonFlags = flags.CreateAndRegister(flag.CommandLine, flags.Runtime)
 }
 
diff --git a/profiles/fake/fake.go b/profiles/fake/fake.go
index ddd6a0d..279f4c4 100644
--- a/profiles/fake/fake.go
+++ b/profiles/fake/fake.go
@@ -32,7 +32,7 @@
 
 func init() {
 	v23.RegisterProfile(Init)
-	rpc.RegisterUnknownProtocol("wsh", websocket.HybridDial, websocket.HybridListener)
+	rpc.RegisterUnknownProtocol("wsh", websocket.HybridDial, websocket.HybridResolve, websocket.HybridListener)
 }
 
 func Init(ctx *context.T) (v23.Runtime, *context.T, v23.Shutdown, error) {
diff --git a/profiles/fake/runtime.go b/profiles/fake/runtime.go
index d67a646..9a17fa7 100644
--- a/profiles/fake/runtime.go
+++ b/profiles/fake/runtime.go
@@ -71,10 +71,8 @@
 
 func (*Runtime) WithReservedNameDispatcher(ctx *context.T, d rpc.Dispatcher) *context.T {
 	panic("unimplemented")
-	return nil
 }
 
 func (*Runtime) GetReservedNameDispatcher(ctx *context.T) rpc.Dispatcher {
 	panic("unimplmeneted")
-	return nil
 }
diff --git a/profiles/gce/init.go b/profiles/gce/init.go
index d71409d..99651de 100644
--- a/profiles/gce/init.go
+++ b/profiles/gce/init.go
@@ -34,7 +34,7 @@
 
 func init() {
 	v23.RegisterProfile(Init)
-	rpc.RegisterUnknownProtocol("wsh", websocket.HybridDial, websocket.HybridListener)
+	rpc.RegisterUnknownProtocol("wsh", websocket.HybridDial, websocket.HybridResolve, websocket.HybridListener)
 	commonFlags = flags.CreateAndRegister(flag.CommandLine, flags.Runtime, flags.Listen)
 }
 
diff --git a/profiles/genericinit.go b/profiles/genericinit.go
index 484f621..3851947 100644
--- a/profiles/genericinit.go
+++ b/profiles/genericinit.go
@@ -26,7 +26,7 @@
 
 func init() {
 	v23.RegisterProfile(Init)
-	rpc.RegisterUnknownProtocol("wsh", websocket.HybridDial, websocket.HybridListener)
+	rpc.RegisterUnknownProtocol("wsh", websocket.HybridDial, websocket.HybridResolve, websocket.HybridListener)
 	flags.SetDefaultHostPort(":0")
 	commonFlags = flags.CreateAndRegister(flag.CommandLine, flags.Runtime, flags.Listen)
 }
diff --git a/profiles/internal/lib/appcycle/appcycle.go b/profiles/internal/lib/appcycle/appcycle.go
index c0017f4..9e9bc49 100644
--- a/profiles/internal/lib/appcycle/appcycle.go
+++ b/profiles/internal/lib/appcycle/appcycle.go
@@ -68,20 +68,24 @@
 }
 
 func (m *AppCycle) Stop() {
+	defer vlog.LogCall()() // AUTO-GENERATED, 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
 	os.Exit(v23.ForceStopExitCode)
 }
 
 func (m *AppCycle) WaitForStop(ch chan<- string) {
+	defer vlog.LogCallf("ch=")("") // AUTO-GENERATED, 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
 	m.Lock()
 	defer m.Unlock()
 	if m.shutDown {
@@ -109,6 +113,7 @@
 }
 
 func (m *AppCycle) AdvanceGoal(delta int32) {
+	defer vlog.LogCallf("delta=%v", delta)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
 	if delta <= 0 {
 		return
 	}
@@ -116,6 +121,7 @@
 }
 
 func (m *AppCycle) AdvanceProgress(delta int32) {
+	defer vlog.LogCallf("delta=%v", delta)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
 	if delta <= 0 {
 		return
 	}
@@ -123,10 +129,12 @@
 }
 
 func (m *AppCycle) Remote() interface{} {
+	defer vlog.LogCall()() // AUTO-GENERATED, 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
 	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
@@ -150,6 +158,7 @@
 }
 
 func (d *invoker) ForceStop(*context.T, rpc.ServerCall) error {
+	defer vlog.LogCall()() // AUTO-GENERATED, 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/profiles/internal/lib/websocket/conn_nacl.go b/profiles/internal/lib/websocket/conn_nacl.go
index c9467cb..479d38f 100644
--- a/profiles/internal/lib/websocket/conn_nacl.go
+++ b/profiles/internal/lib/websocket/conn_nacl.go
@@ -46,6 +46,10 @@
 	return WebsocketConn(address, ws), nil
 }
 
+func Resolve(protocol, address string) (string, string, error) {
+	return "ws", address, nil
+}
+
 func (c *wrappedConn) Read(b []byte) (int, error) {
 	c.readLock.Lock()
 	defer c.readLock.Unlock()
diff --git a/profiles/internal/lib/websocket/conn_test.go b/profiles/internal/lib/websocket/conn_test.go
index 07a6246..1415afc 100644
--- a/profiles/internal/lib/websocket/conn_test.go
+++ b/profiles/internal/lib/websocket/conn_test.go
@@ -3,6 +3,7 @@
 // license that can be found in the LICENSE file.
 
 // +build !nacl
+
 package websocket
 
 import (
diff --git a/profiles/internal/lib/websocket/hybrid.go b/profiles/internal/lib/websocket/hybrid.go
index 9e4e52a..4d8e3a2 100644
--- a/profiles/internal/lib/websocket/hybrid.go
+++ b/profiles/internal/lib/websocket/hybrid.go
@@ -30,6 +30,17 @@
 	return conn, nil
 }
 
+// HybridResolve performs a DNS resolution on the network, address and always
+// returns tcp as its Network.
+func HybridResolve(network, address string) (string, string, error) {
+	tcp := mapWebSocketToTCP[network]
+	tcpAddr, err := net.ResolveTCPAddr(tcp, address)
+	if err != nil {
+		return "", "", err
+	}
+	return tcp, tcpAddr.String(), nil
+}
+
 // HybridListener returns a net.Listener that supports both tcp and
 // websockets over the same, single, port. A listen address of
 // --v23.tcp.protocol=wsh --v23.tcp.address=127.0.0.1:8101 means
diff --git a/profiles/internal/lib/websocket/resolver.go b/profiles/internal/lib/websocket/resolver.go
new file mode 100644
index 0000000..1286cf6
--- /dev/null
+++ b/profiles/internal/lib/websocket/resolver.go
@@ -0,0 +1,21 @@
+// 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.
+
+// +build !nacl
+
+package websocket
+
+import (
+	"net"
+)
+
+// Resolve performs a DNS resolution on the provided protocol and address.
+func Resolve(protocol, address string) (string, string, error) {
+	tcp := mapWebSocketToTCP[protocol]
+	tcpAddr, err := net.ResolveTCPAddr(tcp, address)
+	if err != nil {
+		return "", "", err
+	}
+	return "ws", tcpAddr.String(), nil
+}
diff --git a/profiles/internal/naming/endpoint.go b/profiles/internal/naming/endpoint.go
index 6f70248..d1b238f 100644
--- a/profiles/internal/naming/endpoint.go
+++ b/profiles/internal/naming/endpoint.go
@@ -14,6 +14,7 @@
 
 	"v.io/v23/naming"
 	"v.io/x/lib/metadata"
+	"v.io/x/lib/vlog"
 )
 
 const (
@@ -156,6 +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
 	switch version {
 	default:
 		return ep.VersionedString(defaultVersion)
diff --git a/profiles/internal/naming/namespace/all_test.go b/profiles/internal/naming/namespace/all_test.go
index 59749d3..07f540a 100644
--- a/profiles/internal/naming/namespace/all_test.go
+++ b/profiles/internal/naming/namespace/all_test.go
@@ -61,7 +61,7 @@
 
 // N squared but who cares, this is a little test.
 // Ignores dups.
-func contains(container, contained []string) bool {
+func contains(container, contained []string) (string, bool) {
 L:
 	for _, d := range contained {
 		for _, r := range container {
@@ -69,21 +69,31 @@
 				continue L
 			}
 		}
-		return false
+		return d, false
 	}
-	return true
+	return "", true
 }
 
 func compare(t *testing.T, caller, name string, got, want []string) {
 	// Compare ignoring dups.
-	if !contains(got, want) || !contains(want, got) {
-		boom(t, "%s: %q: got %v, want %v", caller, name, got, want)
+	a, foundA := contains(got, want)
+	b, foundB := contains(want, got)
+	if !foundA {
+		vlog.Infof("%s: %q: failed to find %q: got %v, want %v", caller, name, a, got, want)
+		boom(t, "%s: %q: failed to find %q: got %v, want %v", caller, name, a, got, want)
+	}
+	if !foundB {
+		vlog.Infof("%s: %q: failed to find %q: got %v, want %v", caller, name, a, got, want)
+		boom(t, "%s: %q: failed to find %q: got %v, want %v", caller, name, b, got, want)
 	}
 }
 
 func doGlob(t *testing.T, ctx *context.T, ns namespace.T, pattern string, limit int) []string {
 	var replies []string
-	rc, err := ns.Glob(ctx, pattern)
+
+	sctx, done := context.WithTimeout(ctx, 2*time.Minute)
+	defer done()
+	rc, err := ns.Glob(sctx, pattern)
 	if err != nil {
 		boom(t, "Glob(%s): %s", pattern, err)
 	}
@@ -94,6 +104,8 @@
 			if limit > 0 && len(replies) > limit {
 				boom(t, "Glob returns too many results, perhaps not limiting recursion")
 			}
+		case *naming.GlobReplyError:
+			boom(t, "Glob failed at %q: %v", v.Value.Name, v.Value.Error)
 		}
 	}
 	return replies
@@ -141,11 +153,23 @@
 }
 
 func doResolveTest(t *testing.T, fname string, f func(*context.T, string, ...naming.NamespaceOpt) (*naming.MountEntry, error), ctx *context.T, name string, want []string, opts ...naming.NamespaceOpt) {
-	me, err := f(ctx, name, opts...)
-	if err != nil {
-		boom(t, "Failed to %s %s: %s", fname, name, err)
+	maxretries := 5
+	var lastErr error
+	for i := 0; i < maxretries; i++ {
+		me, err := f(ctx, name, opts...)
+		if err == nil {
+			if i > 0 {
+				t.Logf("doResolveTest: retried %d times", i)
+			}
+			compare(t, fname, name, me.Names(), want)
+			return
+		}
+		if err != nil && verror.Action(err).RetryAction() != 0 {
+			boom(t, "Failed to %s %s: %s, attempt %d", fname, name, err, i)
+		}
+		lastErr = err
 	}
-	compare(t, fname, name, me.Names(), want)
+	boom(t, "Failed to %s %s: %s after %d attempts", fname, name, lastErr, maxretries)
 }
 
 func testResolveToMountTable(t *testing.T, ctx *context.T, ns namespace.T, name string, want ...string) {
@@ -197,6 +221,7 @@
 	if err := s.ServeDispatcher(mountPoint, disp); err != nil {
 		boom(t, "Failed to serve mount table at %s: %s", mountPoint, err)
 	}
+	t.Logf("server %q -> %s", eps[0].Name(), mountPoint)
 	return &serverEntry{mountPoint: mountPoint, server: s, endpoint: eps[0], name: eps[0].Name()}
 }
 
@@ -267,7 +292,9 @@
 	// We directly mount baz into the mt4/foo mount table.
 	globalMP := naming.JoinAddressName(mts["mt4/foo"].name, "baz")
 	mts["baz"] = runMT(t, ctx, "baz")
-	if err := ns.Mount(ctx, globalMP, mts["baz"].name, ttl); err != nil {
+	sctx, done := context.WithTimeout(ctx, 2*time.Minute)
+	defer done()
+	if err := ns.Mount(sctx, globalMP, mts["baz"].name, ttl); err != nil {
 		boom(t, "Failed to Mount %s: %s", globalMP, err)
 	}
 }
@@ -570,7 +597,7 @@
 	// should win.
 	r := doGlob(t, c, ns, "c1/...", 1000)
 	if len(r) != 6 {
-		t.Fatal("expected 6 replies, got %v", r)
+		t.Fatalf("expected 6 replies, got %v", r)
 	}
 }
 
@@ -645,6 +672,9 @@
 
 	// 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 {
 		t.Error(err)
@@ -736,6 +766,8 @@
 	_, ctx, cleanup := createContexts(t)
 	defer cleanup()
 	root := runMT(t, ctx, "")
+	defer func() { root.server.Stop() }()
+
 	ns := v23.GetNamespace(ctx)
 	ns.SetRoots(root.name)
 
@@ -750,6 +782,7 @@
 	if err := server.Serve("leaf", &leafObject{}, nil); err != nil {
 		boom(t, "server.Serve failed: %s", err)
 	}
+	defer server.Stop()
 
 	mountEntry, err := ns.Resolve(ctx, "leaf")
 	if err != nil {
diff --git a/profiles/internal/naming/namespace/glob.go b/profiles/internal/naming/namespace/glob.go
index e81d052..732b48c 100644
--- a/profiles/internal/naming/namespace/glob.go
+++ b/profiles/internal/naming/namespace/glob.go
@@ -9,14 +9,15 @@
 	"strings"
 	"sync"
 
-	"v.io/x/ref/lib/glob"
+	"v.io/x/lib/vlog"
 
 	"v.io/v23"
 	"v.io/v23/context"
 	"v.io/v23/naming"
 	"v.io/v23/rpc"
 	"v.io/v23/verror"
-	"v.io/x/lib/vlog"
+
+	"v.io/x/ref/lib/glob"
 )
 
 type tracks struct {
@@ -181,7 +182,7 @@
 		// If no tasks are running, return.
 		if t.error != nil {
 			if !notAnMT(t.error) {
-				reply <- naming.GlobReplyError{naming.GlobError{Name: naming.Join(prefix, t.me.Name), Error: t.error}}
+				reply <- &naming.GlobReplyError{naming.GlobError{Name: naming.Join(prefix, t.me.Name), Error: t.error}}
 			}
 			inFlight--
 			continue
@@ -230,7 +231,7 @@
 
 // Glob implements naming.MountTable.Glob.
 func (ns *namespace) Glob(ctx *context.T, pattern string, opts ...naming.NamespaceOpt) (chan naming.GlobReply, error) {
-	defer vlog.LogCall()()
+	defer vlog.LogCallf("ctx=,pattern=%.10s...,opts...=%v", pattern, opts)("") // AUTO-GENERATED, 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/profiles/internal/naming/namespace/mount.go b/profiles/internal/naming/namespace/mount.go
index b42ddc4..ccdc7fb 100644
--- a/profiles/internal/naming/namespace/mount.go
+++ b/profiles/internal/naming/namespace/mount.go
@@ -19,15 +19,14 @@
 // mountIntoMountTable mounts a single server into a single mount table.
 func mountIntoMountTable(ctx *context.T, client rpc.Client, name, server string, ttl time.Duration, flags naming.MountFlag, id string, opts ...rpc.CallOpt) (s status) {
 	s.id = id
-	ctx, _ = context.WithTimeout(ctx, callTimeout)
+	ctx = withTimeout(ctx)
 	s.err = client.Call(ctx, name, "Mount", []interface{}{server, uint32(ttl.Seconds()), flags}, nil, append(opts, options.NoResolve{})...)
 	return
 }
 
 // Mount implements Namespace.Mount.
 func (ns *namespace) Mount(ctx *context.T, name, server string, ttl time.Duration, opts ...naming.NamespaceOpt) error {
-	defer vlog.LogCall()()
-
+	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
 	var flags naming.MountFlag
 	for _, o := range opts {
 		// NB: used a switch since we'll be adding more options.
@@ -60,14 +59,14 @@
 // unmountFromMountTable removes a single mounted server from a single mount table.
 func unmountFromMountTable(ctx *context.T, client rpc.Client, name, server string, id string, opts ...rpc.CallOpt) (s status) {
 	s.id = id
-	ctx, _ = context.WithTimeout(ctx, callTimeout)
+	ctx = withTimeout(ctx)
 	s.err = client.Call(ctx, name, "Unmount", []interface{}{server}, nil, append(opts, options.NoResolve{})...)
 	return
 }
 
 // Unmount implements Namespace.Unmount.
 func (ns *namespace) Unmount(ctx *context.T, name, server string, opts ...naming.NamespaceOpt) error {
-	defer vlog.LogCall()()
+	defer vlog.LogCallf("ctx=,name=%.10s...,server=%.10s...,opts...=%v", name, server, opts)("") // AUTO-GENERATED, 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 {
@@ -82,14 +81,14 @@
 // and deleteSubtree isn't true, nothing is deleted.
 func deleteFromMountTable(ctx *context.T, client rpc.Client, name string, deleteSubtree bool, id string, opts ...rpc.CallOpt) (s status) {
 	s.id = id
-	ctx, _ = context.WithTimeout(ctx, callTimeout)
+	ctx = withTimeout(ctx)
 	s.err = client.Call(ctx, name, "Delete", []interface{}{deleteSubtree}, nil, append(opts, options.NoResolve{})...)
 	return
 }
 
 // RDeleteemove implements Namespace.Delete.
 func (ns *namespace) Delete(ctx *context.T, name string, deleteSubtree bool, opts ...naming.NamespaceOpt) error {
-	defer vlog.LogCall()()
+	defer vlog.LogCallf("ctx=,name=%.10s...,deleteSubtree=%v,opts...=%v", name, deleteSubtree, opts)("") // AUTO-GENERATED, 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/profiles/internal/naming/namespace/namespace.go b/profiles/internal/naming/namespace/namespace.go
index bda109e..96cd1d9 100644
--- a/profiles/internal/naming/namespace/namespace.go
+++ b/profiles/internal/naming/namespace/namespace.go
@@ -10,6 +10,7 @@
 
 	inaming "v.io/x/ref/profiles/internal/naming"
 
+	"v.io/v23/context"
 	"v.io/v23/naming"
 	"v.io/v23/rpc"
 	"v.io/v23/security"
@@ -71,7 +72,7 @@
 
 // SetRoots implements naming.Namespace.SetRoots
 func (ns *namespace) SetRoots(roots ...string) error {
-	defer vlog.LogCall()()
+	defer vlog.LogCallf("roots...=%v", roots)("") // AUTO-GENERATED, 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)
@@ -169,13 +170,30 @@
 	return false
 }
 
-// all operations against the mount table service use this fixed timeout for the
-// time being.
+// All operations against the mount table service use this fixed timeout unless overridden.
 const callTimeout = 30 * time.Second
 
+// withTimeout returns a new context if the orinal has no timeout set.
+func withTimeout(ctx *context.T) *context.T {
+	if _, ok := ctx.Deadline(); !ok {
+		ctx, _ = context.WithTimeout(ctx, callTimeout)
+	}
+	return ctx
+}
+
+// withTimeoutAndCancel returns a new context with a deadline and a cancellation function.
+func withTimeoutAndCancel(ctx *context.T) (nctx *context.T, cancel context.CancelFunc) {
+	if _, ok := ctx.Deadline(); !ok {
+		nctx, cancel = context.WithTimeout(ctx, callTimeout)
+	} else {
+		nctx, cancel = context.WithCancel(ctx)
+	}
+	return
+}
+
 // CacheCtl implements naming.Namespace.CacheCtl
 func (ns *namespace) CacheCtl(ctls ...naming.CacheCtl) []naming.CacheCtl {
-	defer vlog.LogCall()()
+	defer vlog.LogCallf("ctls...=%v", ctls)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
 	for _, c := range ctls {
 		switch v := c.(type) {
 		case naming.DisableCache:
diff --git a/profiles/internal/naming/namespace/parallelstartcall.go b/profiles/internal/naming/namespace/parallelstartcall.go
index f753e4e..ff0fda8 100644
--- a/profiles/internal/naming/namespace/parallelstartcall.go
+++ b/profiles/internal/naming/namespace/parallelstartcall.go
@@ -34,7 +34,7 @@
 	c := make(chan startStatus, len(servers))
 	cancelFuncs := make([]context.CancelFunc, len(servers))
 	for index, server := range servers {
-		callCtx, cancel := context.WithTimeout(ctx, callTimeout)
+		callCtx, cancel := withTimeoutAndCancel(ctx)
 		cancelFuncs[index] = cancel
 		go tryStartCall(callCtx, client, server, method, args, c, index, opts...)
 	}
diff --git a/profiles/internal/naming/namespace/perms.go b/profiles/internal/naming/namespace/perms.go
index ed97813..6e12398 100644
--- a/profiles/internal/naming/namespace/perms.go
+++ b/profiles/internal/naming/namespace/perms.go
@@ -17,13 +17,13 @@
 // setPermsInMountTable sets the Permissions in a single server.
 func setPermsInMountTable(ctx *context.T, client rpc.Client, name string, perms access.Permissions, version, id string, opts []rpc.CallOpt) (s status) {
 	s.id = id
-	ctx, _ = context.WithTimeout(ctx, callTimeout)
+	ctx = withTimeout(ctx)
 	s.err = client.Call(ctx, name, "SetPermissions", []interface{}{perms, version}, nil, append(opts, options.NoResolve{})...)
 	return
 }
 
 func (ns *namespace) SetPermissions(ctx *context.T, name string, perms access.Permissions, version string, opts ...naming.NamespaceOpt) error {
-	defer vlog.LogCall()()
+	defer vlog.LogCallf("ctx=,name=%.10s...,perms=,version=%.10s...,opts...=%v", name, version, opts)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
 	client := v23.GetClient(ctx)
 
 	// Apply to all mount tables implementing the name.
@@ -37,7 +37,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.LogCall()()
+	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
 	client := v23.GetClient(ctx)
 
 	// Resolve to all the mount tables implementing name.
diff --git a/profiles/internal/naming/namespace/perms_test.go b/profiles/internal/naming/namespace/perms_test.go
index 93a30e5..0719656 100644
--- a/profiles/internal/naming/namespace/perms_test.go
+++ b/profiles/internal/naming/namespace/perms_test.go
@@ -239,6 +239,7 @@
 	if err != nil {
 		t.Fatalf("v23.NewServer failed: %v", err)
 	}
+	defer server.Stop()
 	if _, err := server.Listen(v23.GetListenSpec(rootCtx)); err != nil {
 		t.Fatalf("Failed to Listen: %s", err)
 	}
diff --git a/profiles/internal/naming/namespace/resolve.go b/profiles/internal/naming/namespace/resolve.go
index 96b08f8..7539d40 100644
--- a/profiles/internal/naming/namespace/resolve.go
+++ b/profiles/internal/naming/namespace/resolve.go
@@ -33,7 +33,7 @@
 		if _, hasDeadline := ctx.Deadline(); !hasDeadline {
 			// Only set a per-call timeout if a deadline has not already
 			// been set.
-			callCtx, _ = context.WithTimeout(ctx, callTimeout)
+			callCtx = withTimeout(ctx)
 		}
 		entry := new(naming.MountEntry)
 		if err := client.Call(callCtx, name, "ResolveStep", nil, []interface{}{entry}, opts...); err != nil {
@@ -61,7 +61,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.LogCall()()
+	defer vlog.LogCallf("ctx=,name=%.10s...,opts...=%v", name, opts)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
 	e, _ := ns.rootMountEntry(name, opts...)
 	if vlog.V(2) {
 		_, file, line, _ := runtime.Caller(1)
@@ -105,7 +105,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.LogCall()()
+	defer vlog.LogCallf("ctx=,name=%.10s...,opts...=%v", name, opts)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
 	e, _ := ns.rootMountEntry(name, opts...)
 	if vlog.V(2) {
 		_, file, line, _ := runtime.Caller(1)
@@ -156,7 +156,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.LogCall()()
+	defer vlog.LogCallf("name=%.10s...", name)("") // AUTO-GENERATED, 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/profiles/internal/rpc/client.go b/profiles/internal/rpc/client.go
index f00c4ba..a032018 100644
--- a/profiles/internal/rpc/client.go
+++ b/profiles/internal/rpc/client.go
@@ -185,13 +185,12 @@
 }
 
 func (c *client) StartCall(ctx *context.T, name, method string, args []interface{}, opts ...rpc.CallOpt) (rpc.ClientCall, error) {
-	defer vlog.LogCall()()
+	defer vlog.LogCallf("ctx=,name=%.10s...,method=%.10s...,args=,opts...=%v", name, method, opts)("") // AUTO-GENERATED, 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.LogCall()()
-
+	defer vlog.LogCallf("ctx=,name=%.10s...,method=%.10s...,inArgs=,outArgs=,opts...=%v", name, method, opts)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
 	deadline := getDeadline(ctx, opts)
 
 	var lastErr error
@@ -566,7 +565,7 @@
 				go func() {
 					select {
 					case <-doneChan:
-						vtrace.GetSpan(fc.ctx).Annotate("Cancelled")
+						vtrace.GetSpan(fc.ctx).Annotate("Canceled")
 						fc.flow.Cancel()
 					case <-fc.flow.Closed():
 					}
@@ -729,7 +728,7 @@
 }
 
 func (c *client) Close() {
-	defer vlog.LogCall()()
+	defer vlog.LogCall()() // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
 	for _, v := range c.vcCache.Close() {
 		c.streamMgr.ShutdownEndpoint(v.RemoteEndpoint())
 	}
@@ -852,7 +851,7 @@
 }
 
 func (fc *flowClient) Send(item interface{}) error {
-	defer vlog.LogCall()()
+	defer vlog.LogCallf("item=")("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
 	if fc.sendClosed {
 		return verror.New(verror.ErrAborted, fc.ctx)
 	}
@@ -896,11 +895,9 @@
 }
 
 func (fc *flowClient) Recv(itemptr interface{}) error {
-	defer vlog.LogCall()()
+	defer vlog.LogCallf("itemptr=")("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
 	switch {
 	case fc.response.Error != nil:
-		// TODO(cnicolaou): this will become a verror.E when we convert the
-		// server.
 		return verror.New(verror.ErrBadProtocol, fc.ctx, fc.response.Error)
 	case fc.response.EndStreamResults:
 		return io.EOF
@@ -913,8 +910,6 @@
 		return fc.close(berr)
 	}
 	if fc.response.Error != nil {
-		// TODO(cnicolaou): this will become a verror.E when we convert the
-		// server.
 		return verror.New(verror.ErrBadProtocol, fc.ctx, fc.response.Error)
 	}
 	if fc.response.EndStreamResults {
@@ -935,7 +930,7 @@
 }
 
 func (fc *flowClient) CloseSend() error {
-	defer vlog.LogCall()()
+	defer vlog.LogCall()() // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
 	return fc.closeSend()
 }
 
@@ -967,7 +962,7 @@
 }
 
 func (fc *flowClient) Finish(resultptrs ...interface{}) error {
-	defer vlog.LogCall()()
+	defer vlog.LogCallf("resultptrs...=%v", resultptrs)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
 	err := fc.finish(resultptrs...)
 	vtrace.GetSpan(fc.ctx).Finish()
 	return err
@@ -1044,6 +1039,7 @@
 }
 
 func (fc *flowClient) RemoteBlessings() ([]string, security.Blessings) {
+	defer vlog.LogCall()() // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
 	return fc.server, fc.flow.RemoteBlessings()
 }
 
diff --git a/profiles/internal/rpc/discharges.go b/profiles/internal/rpc/discharges.go
index 9417172..a5dd1d9 100644
--- a/profiles/internal/rpc/discharges.go
+++ b/profiles/internal/rpc/discharges.go
@@ -23,8 +23,12 @@
 // NoDischarges specifies that the RPC call should not fetch discharges.
 type NoDischarges struct{}
 
-func (NoDischarges) RPCCallOpt() {}
-func (NoDischarges) NSOpt()      {}
+func (NoDischarges) RPCCallOpt() {
+	defer vlog.LogCall()() // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+}
+func (NoDischarges) NSOpt() {
+	defer vlog.LogCall()() // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+}
 
 // discharger implements vc.DischargeClient.
 type dischargeClient struct {
diff --git a/profiles/internal/rpc/full_test.go b/profiles/internal/rpc/full_test.go
index 12445ee..9b5c277 100644
--- a/profiles/internal/rpc/full_test.go
+++ b/profiles/internal/rpc/full_test.go
@@ -1507,7 +1507,7 @@
 	ctx, _ = v23.WithPrincipal(ctx, pclient)
 	_, err := b.client.StartCall(ctx, "incompatible/suffix", "Echo", []interface{}{"foo"}, options.NoRetry{})
 	if verror.ErrorID(err) != verror.ErrNoServers.ID {
-		t.Errorf("Expected error %s, found: %v", verror.ErrNoServers, err)
+		t.Errorf("Expected error %v, found: %v", verror.ErrNoServers, err)
 	}
 
 	// Now add a server with a compatible endpoint and try again.
@@ -2077,7 +2077,7 @@
 }
 
 func init() {
-	rpc.RegisterUnknownProtocol("wsh", websocket.HybridDial, websocket.HybridListener)
+	rpc.RegisterUnknownProtocol("wsh", websocket.HybridDial, websocket.HybridResolve, websocket.HybridListener)
 	security.RegisterCaveatValidator(fakeTimeCaveat, func(_ *context.T, _ security.Call, t int64) error {
 		if now := clock.Now(); now > t {
 			return fmt.Errorf("fakeTimeCaveat expired: now=%d > then=%d", now, t)
diff --git a/profiles/internal/rpc/options.go b/profiles/internal/rpc/options.go
index 5a61261..f5ab563 100644
--- a/profiles/internal/rpc/options.go
+++ b/profiles/internal/rpc/options.go
@@ -11,6 +11,7 @@
 	"v.io/v23/options"
 	"v.io/v23/rpc"
 
+	"v.io/x/lib/vlog"
 	"v.io/x/ref/profiles/internal/rpc/stream"
 )
 
@@ -19,13 +20,17 @@
 // and to order them in the specified order.
 type PreferredProtocols []string
 
-func (PreferredProtocols) RPCClientOpt() {}
+func (PreferredProtocols) RPCClientOpt() {
+	defer vlog.LogCall()() // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+}
 
 // This option is used to sort and filter the endpoints when resolving the
 // proxy name from a mounttable.
 type PreferredServerResolveProtocols []string
 
-func (PreferredServerResolveProtocols) RPCServerOpt() {}
+func (PreferredServerResolveProtocols) RPCServerOpt() {
+	defer vlog.LogCall()() // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+}
 
 // ReservedNameDispatcher specifies the dispatcher that controls access
 // to framework managed portion of the namespace.
@@ -33,7 +38,9 @@
 	Dispatcher rpc.Dispatcher
 }
 
-func (ReservedNameDispatcher) RPCServerOpt() {}
+func (ReservedNameDispatcher) RPCServerOpt() {
+	defer vlog.LogCall()() // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+}
 
 func getRetryTimeoutOpt(opts []rpc.CallOpt) (time.Duration, bool) {
 	for _, o := range opts {
diff --git a/profiles/internal/rpc/protocols/tcp/init.go b/profiles/internal/rpc/protocols/tcp/init.go
index a6067b9..da259a7 100644
--- a/profiles/internal/rpc/protocols/tcp/init.go
+++ b/profiles/internal/rpc/protocols/tcp/init.go
@@ -16,9 +16,9 @@
 )
 
 func init() {
-	rpc.RegisterProtocol("tcp", tcpDial, tcpListen, "tcp4", "tcp6")
-	rpc.RegisterProtocol("tcp4", tcpDial, tcpListen)
-	rpc.RegisterProtocol("tcp6", tcpDial, tcpListen)
+	rpc.RegisterProtocol("tcp", tcpDial, tcpResolve, tcpListen, "tcp4", "tcp6")
+	rpc.RegisterProtocol("tcp4", tcpDial, tcpResolve, tcpListen)
+	rpc.RegisterProtocol("tcp6", tcpDial, tcpResolve, tcpListen)
 }
 
 func tcpDial(network, address string, timeout time.Duration) (net.Conn, error) {
@@ -32,6 +32,15 @@
 	return conn, nil
 }
 
+// tcpResolve performs a DNS resolution on the provided network and address.
+func tcpResolve(network, address string) (string, string, error) {
+	tcpAddr, err := net.ResolveTCPAddr(network, address)
+	if err != nil {
+		return "", "", err
+	}
+	return tcpAddr.Network(), tcpAddr.String(), nil
+}
+
 // tcpListen returns a listener that sets KeepAlive on all accepted connections.
 func tcpListen(network, laddr string) (net.Listener, error) {
 	ln, err := net.Listen(network, laddr)
diff --git a/profiles/internal/rpc/protocols/ws/init.go b/profiles/internal/rpc/protocols/ws/init.go
index dde1c5b..6167bbf 100644
--- a/profiles/internal/rpc/protocols/ws/init.go
+++ b/profiles/internal/rpc/protocols/ws/init.go
@@ -12,7 +12,7 @@
 
 func init() {
 	// ws, ws4, ws6 represent websocket protocol instances.
-	rpc.RegisterProtocol("ws", websocket.Dial, websocket.Listener, "ws4", "ws6")
-	rpc.RegisterProtocol("ws4", websocket.Dial, websocket.Listener)
-	rpc.RegisterProtocol("ws6", websocket.Dial, websocket.Listener)
+	rpc.RegisterProtocol("ws", websocket.Dial, websocket.Resolve, websocket.Listener, "ws4", "ws6")
+	rpc.RegisterProtocol("ws4", websocket.Dial, websocket.Resolve, websocket.Listener)
+	rpc.RegisterProtocol("ws6", websocket.Dial, websocket.Resolve, websocket.Listener)
 }
diff --git a/profiles/internal/rpc/protocols/wsh/init.go b/profiles/internal/rpc/protocols/wsh/init.go
index 159cfd2..ef706d4 100644
--- a/profiles/internal/rpc/protocols/wsh/init.go
+++ b/profiles/internal/rpc/protocols/wsh/init.go
@@ -13,7 +13,7 @@
 )
 
 func init() {
-	rpc.RegisterProtocol("wsh", websocket.HybridDial, websocket.HybridListener, "tcp4", "tcp6", "ws4", "ws6")
-	rpc.RegisterProtocol("wsh4", websocket.HybridDial, websocket.HybridListener, "tcp4", "ws4")
-	rpc.RegisterProtocol("wsh6", websocket.HybridDial, websocket.HybridListener, "tcp6", "ws6")
+	rpc.RegisterProtocol("wsh", websocket.HybridDial, websocket.HybridResolve, websocket.HybridListener, "tcp4", "tcp6", "ws4", "ws6")
+	rpc.RegisterProtocol("wsh4", websocket.HybridDial, websocket.HybridResolve, websocket.HybridListener, "tcp4", "ws4")
+	rpc.RegisterProtocol("wsh6", websocket.HybridDial, websocket.HybridResolve, websocket.HybridListener, "tcp6", "ws6")
 }
diff --git a/profiles/internal/rpc/protocols/wsh_nacl/init.go b/profiles/internal/rpc/protocols/wsh_nacl/init.go
index 8c72fd3..6540b75 100644
--- a/profiles/internal/rpc/protocols/wsh_nacl/init.go
+++ b/profiles/internal/rpc/protocols/wsh_nacl/init.go
@@ -15,7 +15,7 @@
 func init() {
 	// We limit wsh to ws since in general nacl does not allow direct access
 	// to TCP/UDP networking.
-	rpc.RegisterProtocol("wsh", websocket.Dial, websocket.Listener, "ws4", "ws6")
-	rpc.RegisterProtocol("wsh4", websocket.Dial, websocket.Listener, "ws4")
-	rpc.RegisterProtocol("wsh6", websocket.Dial, websocket.Listener, "ws6")
+	rpc.RegisterProtocol("wsh", websocket.Dial, websocket.Resolve, websocket.Listener, "ws4", "ws6")
+	rpc.RegisterProtocol("wsh4", websocket.Dial, websocket.Resolve, websocket.Listener, "ws4")
+	rpc.RegisterProtocol("wsh6", websocket.Dial, websocket.Resolve, websocket.Listener, "ws6")
 }
diff --git a/profiles/internal/rpc/reserved.go b/profiles/internal/rpc/reserved.go
index 249a043..03a63fc 100644
--- a/profiles/internal/rpc/reserved.go
+++ b/profiles/internal/rpc/reserved.go
@@ -44,6 +44,7 @@
 }
 
 func (r *reservedMethods) Describe__() []rpc.InterfaceDesc {
+	defer vlog.LogCall()() // AUTO-GENERATED, 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 "__".`,
@@ -354,8 +355,14 @@
 	return &derivedServerCall{src, src.Suffix(), sec}
 }
 
-func (c *derivedServerCall) Suffix() string          { return c.suffix }
-func (c *derivedServerCall) Security() security.Call { return c.security }
+func (c *derivedServerCall) Suffix() string {
+	defer vlog.LogCall()() // AUTO-GENERATED, 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
+	return c.security
+}
 
 type derivedSecurityCall struct {
 	security.Call
@@ -371,5 +378,11 @@
 	return &derivedSecurityCall{src, src.Suffix(), tags}
 }
 
-func (c *derivedSecurityCall) Suffix() string           { return c.suffix }
-func (c *derivedSecurityCall) MethodTags() []*vdl.Value { return c.methodTags }
+func (c *derivedSecurityCall) Suffix() string {
+	defer vlog.LogCall()() // AUTO-GENERATED, 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
+	return c.methodTags
+}
diff --git a/profiles/internal/rpc/server.go b/profiles/internal/rpc/server.go
index d289516..e1ac0da 100644
--- a/profiles/internal/rpc/server.go
+++ b/profiles/internal/rpc/server.go
@@ -253,6 +253,7 @@
 }
 
 func (s *server) Status() rpc.ServerStatus {
+	defer vlog.LogCall()() // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
 	status := rpc.ServerStatus{}
 	defer vlog.LogCall()()
 	s.Lock()
@@ -280,7 +281,7 @@
 }
 
 func (s *server) WatchNetwork(ch chan<- rpc.NetworkChange) {
-	defer vlog.LogCall()()
+	defer vlog.LogCallf("ch=")("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
 	s.Lock()
 	defer s.Unlock()
 	if s.dhcpState != nil {
@@ -289,7 +290,7 @@
 }
 
 func (s *server) UnwatchNetwork(ch chan<- rpc.NetworkChange) {
-	defer vlog.LogCall()()
+	defer vlog.LogCallf("ch=")("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
 	s.Lock()
 	defer s.Unlock()
 	if s.dhcpState != nil {
@@ -363,7 +364,7 @@
 }
 
 func (s *server) Listen(listenSpec rpc.ListenSpec) ([]naming.Endpoint, error) {
-	defer vlog.LogCall()()
+	defer vlog.LogCallf("listenSpec=")("") // AUTO-GENERATED, 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")
@@ -743,6 +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
 	if suffix != "" {
 		return nil, nil, verror.New(verror.ErrUnknownSuffix, nil, suffix)
 	}
@@ -750,7 +752,7 @@
 }
 
 func (s *server) Serve(name string, obj interface{}, authorizer security.Authorizer) error {
-	defer vlog.LogCall()()
+	defer vlog.LogCallf("name=%.10s...,obj=,authorizer=", name)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
 	if obj == nil {
 		return verror.New(verror.ErrBadArg, s.ctx, "nil object")
 	}
@@ -774,7 +776,7 @@
 }
 
 func (s *server) ServeDispatcher(name string, disp rpc.Dispatcher) error {
-	defer vlog.LogCall()()
+	defer vlog.LogCallf("name=%.10s...,disp=", name)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
 	if disp == nil {
 		return verror.New(verror.ErrBadArg, s.ctx, "nil dispatcher")
 	}
@@ -797,7 +799,7 @@
 }
 
 func (s *server) AddName(name string) error {
-	defer vlog.LogCall()()
+	defer vlog.LogCallf("name=%.10s...", name)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
 	if len(name) == 0 {
 		return verror.New(verror.ErrBadArg, s.ctx, "name is empty")
 	}
@@ -812,7 +814,7 @@
 }
 
 func (s *server) RemoveName(name string) {
-	defer vlog.LogCall()()
+	defer vlog.LogCallf("name=%.10s...", name)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
 	s.Lock()
 	defer s.Unlock()
 	if err := s.allowed(publishing, "RemoveName"); err != nil {
@@ -823,6 +825,7 @@
 }
 
 func (s *server) Stop() error {
+	defer vlog.LogCall()() // AUTO-GENERATED, 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)
@@ -911,7 +914,7 @@
 		vlog.Errorf("%s: Listener Close Error: %v", serverDebug, firstErr)
 		vlog.Errorf("%s: Timedout waiting for goroutines to stop: listeners: %d (currently: %d)", serverDebug, nListeners, len(s.listeners))
 		for ln, _ := range s.listeners {
-			vlog.Errorf("%s: Listener: %p", serverDebug, ln)
+			vlog.Errorf("%s: Listener: %v", serverDebug, ln)
 		}
 		for ls, _ := range s.listenState {
 			vlog.Errorf("%s: ListenState: %v", serverDebug, ls)
@@ -1269,7 +1272,7 @@
 
 // Send implements the rpc.Stream method.
 func (fs *flowServer) Send(item interface{}) error {
-	defer vlog.LogCall()()
+	defer vlog.LogCallf("item=")("") // AUTO-GENERATED, 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
@@ -1279,7 +1282,7 @@
 
 // Recv implements the rpc.Stream method.
 func (fs *flowServer) Recv(itemptr interface{}) error {
-	defer vlog.LogCall()()
+	defer vlog.LogCallf("itemptr=")("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
 	var req rpc.Request
 	if err := fs.dec.Decode(&req); err != nil {
 		return err
diff --git a/profiles/internal/rpc/server_authorizer.go b/profiles/internal/rpc/server_authorizer.go
index 5478b29..cda735d 100644
--- a/profiles/internal/rpc/server_authorizer.go
+++ b/profiles/internal/rpc/server_authorizer.go
@@ -12,6 +12,7 @@
 	"v.io/v23/rpc"
 	"v.io/v23/security"
 	"v.io/v23/verror"
+	"v.io/x/lib/vlog"
 )
 
 // TODO(ribrdb): Flip this to true once everything is updated and also update
@@ -66,6 +67,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
 	if call.RemoteBlessings().IsZero() {
 		return verror.New(errNoBlessingsFromServer, ctx)
 	}
diff --git a/profiles/internal/rpc/server_test.go b/profiles/internal/rpc/server_test.go
index 59d3658..0b6f03f 100644
--- a/profiles/internal/rpc/server_test.go
+++ b/profiles/internal/rpc/server_test.go
@@ -117,7 +117,7 @@
 	}
 	status := server.Status()
 	if got, want := len(status.Errors), 1; got != want {
-		t.Fatalf("got %s, want %s", got, want)
+		t.Fatalf("got %v, want %v", got, want)
 	}
 	_, err = server.Listen(rpc.ListenSpec{Addrs: rpc.ListenAddrs{{"tcp", "*:0"}}})
 	if verror.ErrorID(err) != verror.ErrBadArg.ID {
@@ -125,7 +125,7 @@
 	}
 	status = server.Status()
 	if got, want := len(status.Errors), 1; got != want {
-		t.Fatalf("got %s, want %s", got, want)
+		t.Fatalf("got %v, want %v", got, want)
 	}
 }
 
@@ -475,7 +475,7 @@
 
 	status := server.Status()
 	if got, want := status.Endpoints, eps; !cmpEndpoints(got, want) {
-		t.Fatalf("got %d, want %d", got, want)
+		t.Fatalf("got %v, want %v", got, want)
 	}
 
 	if got, want := len(status.Mounts), len(eps)*2; got != want {
diff --git a/profiles/internal/rpc/stream/errors.go b/profiles/internal/rpc/stream/errors.go
index 4aae55c..2a54607 100644
--- a/profiles/internal/rpc/stream/errors.go
+++ b/profiles/internal/rpc/stream/errors.go
@@ -8,6 +8,7 @@
 	"net"
 
 	"v.io/v23/verror"
+	"v.io/x/lib/vlog"
 )
 
 const pkgPath = "v.io/x/ref/profiles/internal/rpc/stream"
@@ -19,14 +20,15 @@
 // of their errors are intended to be used as arguments to higher level errors.
 var (
 	// TODO(cnicolaou): rename ErrSecurity to ErrAuth
-	ErrSecurity   = verror.Register(pkgPath+".errSecurity", verror.NoRetry, "{:3}")
-	ErrNotTrusted = verror.Register(pkgPath+".errNotTrusted", verror.NoRetry, "{:3}")
-	ErrNetwork    = verror.Register(pkgPath+".errNetwork", verror.NoRetry, "{:3}")
-	ErrDialFailed = verror.Register(pkgPath+".errDialFailed", verror.NoRetry, "{:3}")
-	ErrProxy      = verror.Register(pkgPath+".errProxy", verror.NoRetry, "{:3}")
-	ErrBadArg     = verror.Register(pkgPath+".errBadArg", verror.NoRetry, "{:3}")
-	ErrBadState   = verror.Register(pkgPath+".errBadState", verror.NoRetry, "{:3}")
-	ErrAborted    = verror.Register(pkgPath+".errAborted", verror.NoRetry, "{:3}")
+	ErrSecurity      = verror.Register(pkgPath+".errSecurity", verror.NoRetry, "{:3}")
+	ErrNotTrusted    = verror.Register(pkgPath+".errNotTrusted", verror.NoRetry, "{:3}")
+	ErrNetwork       = verror.Register(pkgPath+".errNetwork", verror.NoRetry, "{:3}")
+	ErrDialFailed    = verror.Register(pkgPath+".errDialFailed", verror.NoRetry, "{:3}")
+	ErrResolveFailed = verror.Register(pkgPath+".errResolveFailed", verror.NoRetry, "{:3}")
+	ErrProxy         = verror.Register(pkgPath+".errProxy", verror.NoRetry, "{:3}")
+	ErrBadArg        = verror.Register(pkgPath+".errBadArg", verror.NoRetry, "{:3}")
+	ErrBadState      = verror.Register(pkgPath+".errBadState", verror.NoRetry, "{:3}")
+	ErrAborted       = verror.Register(pkgPath+".errAborted", verror.NoRetry, "{:3}")
 )
 
 // NetError implements net.Error
@@ -48,7 +50,10 @@
 	return &NetError{err, timeout, temporary}
 }
 
-func (t NetError) Err() error      { return t.err }
-func (t NetError) Error() string   { return t.err.Error() }
+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
+	return t.err.Error()
+}
 func (t NetError) Timeout() bool   { return t.timeout }
 func (t NetError) Temporary() bool { return t.temp }
diff --git a/profiles/internal/rpc/stream/manager/error_test.go b/profiles/internal/rpc/stream/manager/error_test.go
index ff51fe6..545d051 100644
--- a/profiles/internal/rpc/stream/manager/error_test.go
+++ b/profiles/internal/rpc/stream/manager/error_test.go
@@ -89,6 +89,10 @@
 	return mocknet.DialerWithOpts(opts, network, address, timeout)
 }
 
+func simpleResolver(network, address string) (string, string, error) {
+	return network, address, nil
+}
+
 func TestDialErrors(t *testing.T) {
 	_, shutdown := test.InitForTest()
 	defer shutdown()
@@ -100,8 +104,9 @@
 	// bad protocol
 	ep, _ := inaming.NewEndpoint(naming.FormatEndpoint("x", "127.0.0.1:2"))
 	_, err := client.Dial(ep, pclient)
-	if verror.ErrorID(err) != stream.ErrDialFailed.ID {
-		t.Fatalf("wrong error: %s", err)
+	// A bad protocol should result in a Resolve Error.
+	if verror.ErrorID(err) != stream.ErrResolveFailed.ID {
+		t.Errorf("wrong error: %v", err)
 	}
 	t.Log(err)
 
@@ -109,11 +114,11 @@
 	ep, _ = inaming.NewEndpoint(naming.FormatEndpoint("tcp", "127.0.0.1:2"))
 	_, err = client.Dial(ep, pclient)
 	if verror.ErrorID(err) != stream.ErrDialFailed.ID {
-		t.Fatalf("wrong error: %s", err)
+		t.Errorf("wrong error: %v", err)
 	}
 	t.Log(err)
 
-	rpc.RegisterProtocol("dropData", dropDataDialer, net.Listen)
+	rpc.RegisterProtocol("dropData", dropDataDialer, simpleResolver, net.Listen)
 
 	ln, sep, err := server.Listen("tcp", "127.0.0.1:0", pserver, pserver.BlessingStore().Default())
 	if err != nil {
@@ -129,7 +134,7 @@
 	}
 	_, err = client.Dial(cep, pclient)
 	if verror.ErrorID(err) != stream.ErrNetwork.ID {
-		t.Fatalf("wrong error: %s", err)
+		t.Errorf("wrong error: %v", err)
 	}
 	t.Log(err)
 }
diff --git a/profiles/internal/rpc/stream/manager/listener.go b/profiles/internal/rpc/stream/manager/listener.go
index 869e50f..3e0ec5d 100644
--- a/profiles/internal/rpc/stream/manager/listener.go
+++ b/profiles/internal/rpc/stream/manager/listener.go
@@ -209,8 +209,8 @@
 				conn.Close()
 				return
 			}
-			ln.vifs.Insert(vf)
-			ln.manager.vifs.Insert(vf)
+			ln.vifs.Insert(vf, conn.RemoteAddr().Network(), conn.RemoteAddr().String())
+			ln.manager.vifs.Insert(vf, conn.RemoteAddr().Network(), conn.RemoteAddr().String())
 
 			ln.vifLoops.Add(1)
 			vifLoop(vf, ln.q, func() {
@@ -223,6 +223,12 @@
 	}
 }
 
+func (ln *netListener) deleteVIF(vf *vif.VIF) {
+	vlog.VI(2).Infof("VIF %v is closed, removing from cache", vf)
+	ln.vifs.Delete(vf)
+	ln.manager.vifs.Delete(vf)
+}
+
 func (ln *netListener) Accept() (stream.Flow, error) {
 	item, err := ln.q.Get(nil)
 	switch {
@@ -252,12 +258,6 @@
 	return nil
 }
 
-func (ln *netListener) deleteVIF(vf *vif.VIF) {
-	vlog.VI(2).Infof("VIF %v is closed, removing from cache", vf)
-	ln.vifs.Delete(vf)
-	ln.manager.vifs.Delete(vf)
-}
-
 func (ln *netListener) String() string {
 	return fmt.Sprintf("%T: (%v, %v)", ln, ln.netLn.Addr().Network(), ln.netLn.Addr())
 }
diff --git a/profiles/internal/rpc/stream/manager/manager.go b/profiles/internal/rpc/stream/manager/manager.go
index 85c081f..afa3afa 100644
--- a/profiles/internal/rpc/stream/manager/manager.go
+++ b/profiles/internal/rpc/stream/manager/manager.go
@@ -83,10 +83,12 @@
 type DialTimeout time.Duration
 
 func (DialTimeout) RPCStreamVCOpt() {}
-func (DialTimeout) RPCClientOpt()   {}
+func (DialTimeout) RPCClientOpt() {
+	defer vlog.LogCall()() // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+}
 
-func dial(network, address string, timeout time.Duration) (net.Conn, error) {
-	if d, _, _ := rpc.RegisteredProtocol(network); d != nil {
+func dial(d rpc.DialerFunc, network, address string, timeout time.Duration) (net.Conn, error) {
+	if d != nil {
 		conn, err := d(network, address, timeout)
 		if err != nil {
 			return nil, verror.New(stream.ErrDialFailed, nil, err)
@@ -96,6 +98,17 @@
 	return nil, verror.New(stream.ErrDialFailed, nil, verror.New(errUnknownNetwork, nil, network))
 }
 
+func resolve(r rpc.ResolverFunc, network, address string) (string, string, error) {
+	if r != nil {
+		net, addr, err := r(network, address)
+		if err != nil {
+			return "", "", verror.New(stream.ErrResolveFailed, nil, err)
+		}
+		return net, addr, nil
+	}
+	return "", "", verror.New(stream.ErrResolveFailed, nil, verror.New(errUnknownNetwork, nil, network))
+}
+
 // FindOrDialVIF returns the network connection (VIF) to the provided address
 // from the cache in the manager. If not already present in the cache, a new
 // connection will be created using net.Dial.
@@ -109,37 +122,38 @@
 		}
 	}
 	addr := remote.Addr()
-	network, address := addr.Network(), addr.String()
-	if vf := m.vifs.Find(network, address); vf != nil {
-		return vf, nil
-	}
-	vlog.VI(1).Infof("(%q, %q) not in VIF cache. Dialing", network, address)
-	conn, err := dial(network, address, timeout)
-	if err != nil {
-		return nil, err
-	}
+	d, r, _, _ := rpc.RegisteredProtocol(addr.Network())
 	// (network, address) in the endpoint might not always match up
 	// with the key used in the vifs. For example:
 	// - conn, err := net.Dial("tcp", "www.google.com:80")
 	//   fmt.Println(conn.RemoteAddr()) // Might yield the corresponding IP address
 	// - Similarly, an unspecified IP address (net.IP.IsUnspecified) like "[::]:80"
 	//   might yield "[::1]:80" (loopback interface) in conn.RemoteAddr().
-	// Thus, look for VIFs with the resolved address as well.
-	resNetwork, resAddress := conn.RemoteAddr().Network(), conn.RemoteAddr().String()
-	if vf := m.vifs.BlockingFind(resNetwork, resAddress); vf != nil {
-		vlog.VI(1).Infof("(%q, %q) resolved to (%q, %q) which exists in the VIF cache. Closing newly Dialed connection", network, address, resNetwork, resAddress)
-		conn.Close()
+	// Thus, look for VIFs with the resolved address.
+	network, address, err := resolve(r, addr.Network(), addr.String())
+	if err != nil {
+		return nil, err
+	}
+	vf, unblock := m.vifs.BlockingFind(network, address)
+	if vf != nil {
+		vlog.VI(1).Infof("(%q, %q) resolved to (%q, %q) which exists in the VIF cache.", addr.Network(), addr.String(), network, address)
 		return vf, nil
 	}
-	defer m.vifs.Unblock(resNetwork, resAddress)
+	defer unblock()
+
+	vlog.VI(1).Infof("(%q, %q) not in VIF cache. Dialing", network, address)
+	conn, err := dial(d, network, address, timeout)
+	if err != nil {
+		return nil, err
+	}
 
 	opts = append([]stream.VCOpt{vc.StartTimeout{defaultStartTimeout}}, opts...)
-	vf, err := vif.InternalNewDialedVIF(conn, m.rid, principal, nil, m.deleteVIF, opts...)
+	vf, err = vif.InternalNewDialedVIF(conn, m.rid, principal, nil, m.deleteVIF, opts...)
 	if err != nil {
 		conn.Close()
 		return nil, err
 	}
-	m.vifs.Insert(vf)
+	m.vifs.Insert(vf, network, address)
 	return vf, nil
 }
 
@@ -161,8 +175,13 @@
 	return nil, verror.NewErrInternal(nil) // Not reached
 }
 
+func (m *manager) deleteVIF(vf *vif.VIF) {
+	vlog.VI(2).Infof("%p: VIF %v is closed, removing from cache", m, vf)
+	m.vifs.Delete(vf)
+}
+
 func listen(protocol, address string) (net.Listener, error) {
-	if _, l, _ := rpc.RegisteredProtocol(protocol); l != nil {
+	if _, _, l, _ := rpc.RegisteredProtocol(protocol); l != nil {
 		ln, err := l(protocol, address)
 		if err != nil {
 			return nil, verror.New(stream.ErrNetwork, nil, err)
@@ -239,11 +258,6 @@
 	return ln, ep, nil
 }
 
-func (m *manager) deleteVIF(vf *vif.VIF) {
-	vlog.VI(2).Infof("%p: VIF %v is closed, removing from cache", m, vf)
-	m.vifs.Delete(vf)
-}
-
 func (m *manager) ShutdownEndpoint(remote naming.Endpoint) {
 	vifs := m.vifs.List()
 	total := 0
diff --git a/profiles/internal/rpc/stream/manager/manager_test.go b/profiles/internal/rpc/stream/manager/manager_test.go
index ff74b71..5042471 100644
--- a/profiles/internal/rpc/stream/manager/manager_test.go
+++ b/profiles/internal/rpc/stream/manager/manager_test.go
@@ -628,14 +628,12 @@
 	// We'd like an endpoint that contains an address that's different than the
 	// one used for the connection. In practice this is awkward to achieve since
 	// we don't want to listen on ":0" since that will annoy firewalls. Instead we
-	// listen on 127.0.0.1 and we fabricate an endpoint that doesn't contain
-	// 127.0.0.1 by using ":0" to create it. This leads to an endpoint such that
-	// the address encoded in the endpoint (e.g. "0.0.0.0:55324") is different
-	// from the address of the connection (e.g. "127.0.0.1:55324").
+	// create a endpoint with "localhost", which will result in an endpoint that
+	// doesn't contain 127.0.0.1.
 	_, port, _ := net.SplitHostPort(ep.Addr().String())
 	nep := &inaming.Endpoint{
 		Protocol: ep.Addr().Network(),
-		Address:  net.JoinHostPort("", port),
+		Address:  net.JoinHostPort("localhost", port),
 		RID:      ep.RoutingID(),
 	}
 
@@ -769,10 +767,13 @@
 	dialer := func(_, _ string, _ time.Duration) (net.Conn, error) {
 		return nil, fmt.Errorf("tn.Dial")
 	}
+	resolver := func(_, _ string) (string, string, error) {
+		return "", "", fmt.Errorf("tn.Resolve")
+	}
 	listener := func(_, _ string) (net.Listener, error) {
 		return nil, fmt.Errorf("tn.Listen")
 	}
-	rpc.RegisterProtocol("tn", dialer, listener)
+	rpc.RegisterProtocol("tn", dialer, resolver, listener)
 
 	_, _, err := server.Listen("tnx", "127.0.0.1:0", principal, blessings)
 	if err == nil || !strings.Contains(err.Error(), "unknown network: tnx") {
@@ -789,7 +790,7 @@
 		return net.Listen("tcp", addr)
 	}
 
-	if got, want := rpc.RegisterProtocol("tn", dialer, listener), true; got != want {
+	if got, want := rpc.RegisterProtocol("tn", dialer, resolver, listener), true; got != want {
 		t.Errorf("got %t, want %t", got, want)
 	}
 
@@ -799,8 +800,8 @@
 	}
 
 	_, err = client.Dial(ep, testutil.NewPrincipal("client"))
-	if err == nil || !strings.Contains(err.Error(), "tn.Dial") {
-		t.Fatal("expected error is missing (%v)", err)
+	if err == nil || !strings.Contains(err.Error(), "tn.Resolve") {
+		t.Fatalf("expected error is missing (%v)", err)
 	}
 }
 
@@ -942,17 +943,9 @@
 	}
 	go acceptLoop(ln)
 
-	// We'd like an endpoint that contains an address that's different than the
-	// one used for the connection. In practice this is awkward to achieve since
-	// we don't want to listen on ":0" since that will annoy firewalls. Instead we
-	// listen on 127.0.0.1 and we fabricate an endpoint that doesn't contain
-	// 127.0.0.1 by using ":0" to create it. This leads to an endpoint such that
-	// the address encoded in the endpoint (e.g. "0.0.0.0:55324") is different
-	// from the address of the connection (e.g. "127.0.0.1:55324").
-	_, port, _ := net.SplitHostPort(ep.Addr().String())
 	nep := &inaming.Endpoint{
 		Protocol: ep.Addr().Network(),
-		Address:  net.JoinHostPort("", port),
+		Address:  ep.Addr().String(),
 		RID:      ep.RoutingID(),
 	}
 
diff --git a/profiles/internal/rpc/stream/message/message.go b/profiles/internal/rpc/stream/message/message.go
index c729949..ea425c0 100644
--- a/profiles/internal/rpc/stream/message/message.go
+++ b/profiles/internal/rpc/stream/message/message.go
@@ -219,7 +219,6 @@
 	default:
 		return verror.New(errInvalidMessageType, nil, fmt.Sprintf("%T", m))
 	}
-	return nil
 }
 
 // EncryptMessage encrypts the message's control data in place.
diff --git a/profiles/internal/rpc/stream/proxy/protocol.vdl.go b/profiles/internal/rpc/stream/proxy/protocol.vdl.go
index e02fe6b..7dae3d9 100644
--- a/profiles/internal/rpc/stream/proxy/protocol.vdl.go
+++ b/profiles/internal/rpc/stream/proxy/protocol.vdl.go
@@ -27,7 +27,7 @@
 }
 
 func (Request) __VDLReflect(struct {
-	Name string "v.io/x/ref/profiles/internal/rpc/stream/proxy.Request"
+	Name string `vdl:"v.io/x/ref/profiles/internal/rpc/stream/proxy.Request"`
 }) {
 }
 
@@ -42,7 +42,7 @@
 }
 
 func (Response) __VDLReflect(struct {
-	Name string "v.io/x/ref/profiles/internal/rpc/stream/proxy.Response"
+	Name string `vdl:"v.io/x/ref/profiles/internal/rpc/stream/proxy.Response"`
 }) {
 }
 
diff --git a/profiles/internal/rpc/stream/proxy/proxy.go b/profiles/internal/rpc/stream/proxy/proxy.go
index 1022b10..4959491 100644
--- a/profiles/internal/rpc/stream/proxy/proxy.go
+++ b/profiles/internal/rpc/stream/proxy/proxy.go
@@ -229,7 +229,7 @@
 	laddr := spec.Addrs[0]
 	network := laddr.Protocol
 	address := laddr.Address
-	_, listenFn, _ := rpc.RegisteredProtocol(network)
+	_, _, listenFn, _ := rpc.RegisteredProtocol(network)
 	if listenFn == nil {
 		return nil, verror.New(stream.ErrProxy, nil, verror.New(errUnknownNetwork, nil, network))
 	}
diff --git a/profiles/internal/rpc/stream/proxy/proxy_test.go b/profiles/internal/rpc/stream/proxy/proxy_test.go
index cffa224..44ef293 100644
--- a/profiles/internal/rpc/stream/proxy/proxy_test.go
+++ b/profiles/internal/rpc/stream/proxy/proxy_test.go
@@ -335,10 +335,10 @@
 	rchan = make(chan string)
 	go readFlow(t, lnC, rchan)
 	if err := writeFlow(client2, epC, "daffy duck"); err != nil {
-		t.Fatal("client2 failed to chat with client1: %v", err)
+		t.Fatalf("client2 failed to chat with client1: %v", err)
 	}
 	if got, want := <-rchan, "daffy duck"; got != want {
-		t.Fatal("client2->client1 got %q want %q", got, want)
+		t.Fatalf("client2->client1 got %q want %q", got, want)
 	}
 }
 
diff --git a/profiles/internal/rpc/stream/vc/listener_test.go b/profiles/internal/rpc/stream/vc/listener_test.go
index 1aa4899..28d9706 100644
--- a/profiles/internal/rpc/stream/vc/listener_test.go
+++ b/profiles/internal/rpc/stream/vc/listener_test.go
@@ -47,10 +47,10 @@
 		t.Error(err)
 	}
 	if f, err := ln.Accept(); f != f1 || err != nil {
-		t.Errorf("Got (%p, %v) want (%p, nil)", f, err, f1)
+		t.Errorf("Got (%v, %v) want (%v, nil)", f, err, f1)
 	}
 	if f, err := ln.Accept(); f != f2 || err != nil {
-		t.Errorf("Got (%p, %v) want (%p, nil)", f, err, f2)
+		t.Errorf("Got (%v, %v) want (%v, nil)", f, err, f2)
 	}
 	if err := ln.Close(); err != nil {
 		t.Error(err)
@@ -63,6 +63,6 @@
 		t.Error(err)
 	}
 	if f, err := ln.Accept(); f != nil || verror.ErrorID(err) != stream.ErrBadState.ID || !strings.Contains(err.Error(), "closed") {
-		t.Errorf("Accept returned (%p, %v) wanted (nil, %v)", f, err, errListenerClosed)
+		t.Errorf("Accept returned (%v, %v) wanted (nil, %v)", f, err, errListenerClosed)
 	}
 }
diff --git a/profiles/internal/rpc/stream/vc/vc.go b/profiles/internal/rpc/stream/vc/vc.go
index 7846d4e..46d807f 100644
--- a/profiles/internal/rpc/stream/vc/vc.go
+++ b/profiles/internal/rpc/stream/vc/vc.go
@@ -72,7 +72,9 @@
 type DischargeExpiryBuffer time.Duration
 
 func (DischargeExpiryBuffer) RPCStreamListenerOpt() {}
-func (DischargeExpiryBuffer) RPCServerOpt()         {}
+func (DischargeExpiryBuffer) RPCServerOpt() {
+	defer vlog.LogCall()() // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
+}
 
 const DefaultServerDischargeExpiryBuffer = 20 * time.Second
 
diff --git a/profiles/internal/rpc/stream/vc/writer_test.go b/profiles/internal/rpc/stream/vc/writer_test.go
index eb7018a..a7c84fe 100644
--- a/profiles/internal/rpc/stream/vc/writer_test.go
+++ b/profiles/internal/rpc/stream/vc/writer_test.go
@@ -147,7 +147,7 @@
 		t.Fatal(err)
 	}
 	if gbw != bw {
-		t.Fatalf("Got %p want %p", gbw, bw)
+		t.Fatalf("Got %v want %v", gbw, bw)
 	}
 	if len(bufs) != 1 {
 		t.Fatalf("Got %d bufs, want 1", len(bufs))
diff --git a/profiles/internal/rpc/stream/vif/set.go b/profiles/internal/rpc/stream/vif/set.go
index 497e43b..3032dfc 100644
--- a/profiles/internal/rpc/stream/vif/set.go
+++ b/profiles/internal/rpc/stream/vif/set.go
@@ -20,6 +20,7 @@
 	mu      sync.RWMutex
 	set     map[string][]*VIF // GUARDED_BY(mu)
 	started map[string]bool   // GUARDED_BY(mu)
+	keys    map[*VIF]string   // GUARDED_BY(mu)
 	cond    *sync.Cond
 }
 
@@ -28,6 +29,7 @@
 	s := &Set{
 		set:     make(map[string][]*VIF),
 		started: make(map[string]bool),
+		keys:    make(map[*VIF]string),
 	}
 	s.cond = sync.NewCond(&s.mu)
 	return s
@@ -37,36 +39,49 @@
 // is identified by the provided (network, address). Returns nil if there is no
 // such VIF.
 //
-// If BlockingFind returns nil, the caller is required to call Unblock, to avoid deadlock.
-// The network and address in Unblock must be the same as used in the BlockingFind call.
-// During this time, all new BlockingFind calls for this network and address will Block until
-// the corresponding Unblock call is made.
-func (s *Set) BlockingFind(network, address string) *VIF {
-	return s.find(network, address, true)
+// The caller is required to call the returned unblock function, to avoid deadlock.
+// Until the returned function is called, all new BlockingFind calls for this
+// network and address will block.
+func (s *Set) BlockingFind(network, address string) (*VIF, func()) {
+	if isNonDistinctConn(network, address) {
+		return nil, func() {}
+	}
+
+	k := key(network, address)
+
+	s.mu.Lock()
+	defer s.mu.Unlock()
+
+	for s.started[k] {
+		s.cond.Wait()
+	}
+
+	_, _, _, p := rpc.RegisteredProtocol(network)
+	for _, n := range p {
+		if vifs := s.set[key(n, address)]; len(vifs) > 0 {
+			return vifs[rand.Intn(len(vifs))], func() {}
+		}
+	}
+
+	s.started[k] = true
+	return nil, func() { s.unblock(network, address) }
 }
 
-// Unblock marks the status of the network, address as no longer started, and
+// unblock marks the status of the network, address as no longer started, and
 // broadcasts waiting threads.
-func (s *Set) Unblock(network, address string) {
+func (s *Set) unblock(network, address string) {
 	s.mu.Lock()
 	delete(s.started, key(network, address))
 	s.cond.Broadcast()
 	s.mu.Unlock()
 }
 
-// Find returns a VIF where the remote end of the underlying network connection
-// is identified by the provided (network, address). Returns nil if there is no
-// such VIF.
-func (s *Set) Find(network, address string) *VIF {
-	return s.find(network, address, false)
-}
-
 // Insert adds a VIF to the set.
-func (s *Set) Insert(vif *VIF) {
-	addr := vif.conn.RemoteAddr()
-	k := key(addr.Network(), addr.String())
+func (s *Set) Insert(vif *VIF, network, address string) {
+	k := key(network, address)
 	s.mu.Lock()
 	defer s.mu.Unlock()
+	s.keys[vif] = k
 	vifs := s.set[k]
 	for _, v := range vifs {
 		if v == vif {
@@ -74,16 +89,13 @@
 		}
 	}
 	s.set[k] = append(vifs, vif)
-	vif.addSet(s)
 }
 
 // Delete removes a VIF from the set.
 func (s *Set) Delete(vif *VIF) {
-	vif.removeSet(s)
-	addr := vif.conn.RemoteAddr()
-	k := key(addr.Network(), addr.String())
 	s.mu.Lock()
 	defer s.mu.Unlock()
+	k := s.keys[vif]
 	vifs := s.set[k]
 	for i, v := range vifs {
 		if v == vif {
@@ -92,6 +104,7 @@
 			} else {
 				s.set[k] = append(vifs[:i], vifs[i+1:]...)
 			}
+			delete(s.keys, vif)
 			return
 		}
 	}
@@ -108,33 +121,6 @@
 	return l
 }
 
-func (s *Set) find(network, address string, blocking bool) *VIF {
-	if isNonDistinctConn(network, address) {
-		return nil
-	}
-
-	k := key(network, address)
-
-	s.mu.Lock()
-	defer s.mu.Unlock()
-
-	for blocking && s.started[k] {
-		s.cond.Wait()
-	}
-
-	_, _, p := rpc.RegisteredProtocol(network)
-	for _, n := range p {
-		if vifs := s.set[key(n, address)]; len(vifs) > 0 {
-			return vifs[rand.Intn(len(vifs))]
-		}
-	}
-
-	if blocking {
-		s.started[k] = true
-	}
-	return nil
-}
-
 func key(network, address string) string {
 	if network == "tcp" || network == "ws" {
 		host, _, _ := net.SplitHostPort(address)
diff --git a/profiles/internal/rpc/stream/vif/set_test.go b/profiles/internal/rpc/stream/vif/set_test.go
index bc95fa5..e9e597d 100644
--- a/profiles/internal/rpc/stream/vif/set_test.go
+++ b/profiles/internal/rpc/stream/vif/set_test.go
@@ -24,7 +24,10 @@
 var supportsIPv6 bool
 
 func init() {
-	rpc.RegisterProtocol("unix", net.DialTimeout, net.Listen)
+	simpleResolver := func(network, address string) (string, string, error) {
+		return network, address, nil
+	}
+	rpc.RegisterProtocol("unix", net.DialTimeout, simpleResolver, net.Listen)
 
 	// Check whether the platform supports IPv6.
 	ln, err := net.Listen("tcp6", "[::1]:0")
@@ -35,7 +38,7 @@
 }
 
 func newConn(network, address string) (net.Conn, net.Conn, error) {
-	dfunc, lfunc, _ := rpc.RegisteredProtocol(network)
+	dfunc, _, lfunc, _ := rpc.RegisteredProtocol(network)
 	ln, err := lfunc(network, address)
 	if err != nil {
 		return nil, nil, err
@@ -94,6 +97,12 @@
 	return d
 }
 
+func find(set *vif.Set, n, a string) *vif.VIF {
+	found, unblock := set.BlockingFind(n, a)
+	unblock()
+	return found
+}
+
 func TestSetBasic(t *testing.T) {
 	sockdir, err := ioutil.TempDir("", "TestSetBasic")
 	if err != nil {
@@ -143,23 +152,23 @@
 		}
 		a := c.RemoteAddr()
 
-		set.Insert(vf)
+		set.Insert(vf, a.Network(), a.String())
 		for _, n := range test.compatibles {
-			if found := set.Find(n, a.String()); found == nil {
-				t.Fatalf("%s: Got nil, but want [%v] on Find(%q, %q))", name, vf, n, a)
+			if found := find(set, n, a.String()); found == nil {
+				t.Fatalf("%s: Got nil, but want [%v] on find(%q, %q))", name, vf, n, a)
 			}
 		}
 
 		for _, n := range diff(all, test.compatibles) {
-			if v := set.Find(n, a.String()); v != nil {
-				t.Fatalf("%s: Got [%v], but want nil on Find(%q, %q))", name, v, n, a)
+			if v := find(set, n, a.String()); v != nil {
+				t.Fatalf("%s: Got [%v], but want nil on find(%q, %q))", name, v, n, a)
 			}
 		}
 
 		set.Delete(vf)
 		for _, n := range all {
-			if v := set.Find(n, a.String()); v != nil {
-				t.Fatalf("%s: Got [%v], but want nil on Find(%q, %q))", name, v, n, a)
+			if v := find(set, n, a.String()); v != nil {
+				t.Fatalf("%s: Got [%v], but want nil on find(%q, %q))", name, v, n, a)
 			}
 		}
 	}
@@ -186,17 +195,17 @@
 	}
 
 	set := vif.NewSet()
-	set.Insert(vf1)
-	if v := set.Find(a1.Network(), a1.String()); v != nil {
-		t.Fatalf("Got [%v], but want nil on Find(%q, %q))", v, a1.Network(), a1)
+	set.Insert(vf1, a1.Network(), a1.String())
+	if v := find(set, a1.Network(), a1.String()); v != nil {
+		t.Fatalf("Got [%v], but want nil on find(%q, %q))", v, a1.Network(), a1)
 	}
 	if l := set.List(); len(l) != 1 || l[0] != vf1 {
 		t.Errorf("Unexpected list of VIFs: %v", l)
 	}
 
-	set.Insert(vf2)
-	if v := set.Find(a2.Network(), a2.String()); v != nil {
-		t.Fatalf("Got [%v], but want nil on Find(%q, %q))", v, a2.Network(), a2)
+	set.Insert(vf2, a2.Network(), a2.String())
+	if v := find(set, a2.Network(), a2.String()); v != nil {
+		t.Fatalf("Got [%v], but want nil on find(%q, %q))", v, a2.Network(), a2)
 	}
 	if l := set.List(); len(l) != 2 || l[0] != vf1 || l[1] != vf2 {
 		t.Errorf("Unexpected list of VIFs: %v", l)
@@ -247,17 +256,17 @@
 	}
 
 	set := vif.NewSet()
-	set.Insert(vf1)
-	if v := set.Find(a1.Network(), a1.String()); v != nil {
-		t.Fatalf("Got [%v], but want nil on Find(%q, %q))", v, a1.Network(), a1)
+	set.Insert(vf1, a1.Network(), a1.String())
+	if v := find(set, a1.Network(), a1.String()); v != nil {
+		t.Fatalf("Got [%v], but want nil on find(%q, %q))", v, a1.Network(), a1)
 	}
 	if l := set.List(); len(l) != 1 || l[0] != vf1 {
 		t.Errorf("Unexpected list of VIFs: %v", l)
 	}
 
-	set.Insert(vf2)
-	if v := set.Find(a2.Network(), a2.String()); v != nil {
-		t.Fatalf("Got [%v], but want nil on Find(%q, %q))", v, a2.Network(), a2)
+	set.Insert(vf2, a2.Network(), a2.String())
+	if v := find(set, a2.Network(), a2.String()); v != nil {
+		t.Fatalf("Got [%v], but want nil on find(%q, %q))", v, a2.Network(), a2)
 	}
 	if l := set.List(); len(l) != 2 || l[0] != vf1 || l[1] != vf2 {
 		t.Errorf("Unexpected list of VIFs: %v", l)
@@ -275,68 +284,38 @@
 
 func TestSetInsertDelete(t *testing.T) {
 	c1, s1 := net.Pipe()
-	c2, s2 := net.Pipe()
 	vf1, _, err := newVIF(c1, s1)
 	if err != nil {
 		t.Fatal(err)
 	}
-	vf2, _, err := newVIF(c2, s2)
-	if err != nil {
-		t.Fatal(err)
-	}
 
 	set1 := vif.NewSet()
-	set2 := vif.NewSet()
 
-	set1.Insert(vf1)
+	n1, a1 := c1.RemoteAddr().Network(), c1.RemoteAddr().String()
+	set1.Insert(vf1, n1, a1)
 	if l := set1.List(); len(l) != 1 || l[0] != vf1 {
 		t.Errorf("Unexpected list of VIFs: %v", l)
 	}
-	set1.Insert(vf2)
-	if l := set1.List(); len(l) != 2 || l[0] != vf1 || l[1] != vf2 {
-		t.Errorf("Unexpected list of VIFs: %v", l)
-	}
-
-	set2.Insert(vf1)
-	set2.Insert(vf2)
 
 	set1.Delete(vf1)
-	if l := set1.List(); len(l) != 1 || l[0] != vf2 {
-		t.Errorf("Unexpected list of VIFs: %v", l)
-	}
-	if l := set2.List(); len(l) != 2 || l[0] != vf1 || l[1] != vf2 {
-		t.Errorf("Unexpected list of VIFs: %v", l)
-	}
-
-	vf1.Close()
-	if l := set1.List(); len(l) != 1 || l[0] != vf2 {
-		t.Errorf("Unexpected list of VIFs: %v", l)
-	}
-	if l := set2.List(); len(l) != 1 || l[0] != vf2 {
-		t.Errorf("Unexpected list of VIFs: %v", l)
-	}
-
-	vf2.Close()
 	if l := set1.List(); len(l) != 0 {
 		t.Errorf("Unexpected list of VIFs: %v", l)
 	}
-	if l := set2.List(); len(l) != 0 {
-		t.Errorf("Unexpected list of VIFs: %v", l)
-	}
 }
 
 func TestBlockingFind(t *testing.T) {
 	network, address := "tcp", "127.0.0.1:1234"
 	set := vif.NewSet()
 
-	set.BlockingFind(network, address)
+	_, unblock := set.BlockingFind(network, address)
 
 	ch := make(chan *vif.VIF, 1)
 
 	// set.BlockingFind should block until set.Unblock is called with the corresponding VIF,
 	// since set.BlockingFind was called earlier.
 	go func(ch chan *vif.VIF) {
-		ch <- set.BlockingFind(network, address)
+		vf, _ := set.BlockingFind(network, address)
+		ch <- vf
 	}(ch)
 
 	// set.BlockingFind for a different network and address should not block.
@@ -351,8 +330,8 @@
 	if err != nil {
 		t.Fatal(err)
 	}
-	set.Insert(vf)
-	set.Unblock(network, address)
+	set.Insert(vf, network, address)
+	unblock()
 
 	// Now the set.BlockingFind should have returned the correct vif.
 	if cachedVif := <-ch; cachedVif != vf {
diff --git a/profiles/internal/rpc/stream/vif/vif.go b/profiles/internal/rpc/stream/vif/vif.go
index 2342ddd..fb206b7 100644
--- a/profiles/internal/rpc/stream/vif/vif.go
+++ b/profiles/internal/rpc/stream/vif/vif.go
@@ -120,10 +120,6 @@
 	isClosed   bool // GUARDED_BY(isClosedMu)
 	onClose    func(*VIF)
 
-	// All sets that this VIF is in.
-	muSets sync.Mutex
-	sets   []*Set // GUARDED_BY(muSets)
-
 	// These counters track the number of messages sent and received by
 	// this VIF.
 	muMsgCounters sync.Mutex
@@ -345,27 +341,6 @@
 	return vc, nil
 }
 
-// addSet adds a set to the list of sets this VIF is in. This method is called
-// by Set.Insert().
-func (vif *VIF) addSet(s *Set) {
-	vif.muSets.Lock()
-	defer vif.muSets.Unlock()
-	vif.sets = append(vif.sets, s)
-}
-
-// removeSet removes a set from the list of sets this VIF is in. This method is
-// called by Set.Delete().
-func (vif *VIF) removeSet(s *Set) {
-	vif.muSets.Lock()
-	defer vif.muSets.Unlock()
-	for ix, vs := range vif.sets {
-		if vs == s {
-			vif.sets = append(vif.sets[:ix], vif.sets[ix+1:]...)
-			return
-		}
-	}
-}
-
 // Close closes all VCs (and thereby Flows) over the VIF and then closes the
 // underlying network connection after draining all pending writes on those
 // VCs.
@@ -378,14 +353,6 @@
 	vif.isClosed = true
 	vif.isClosedMu.Unlock()
 
-	vif.muSets.Lock()
-	sets := vif.sets
-	vif.sets = nil
-	vif.muSets.Unlock()
-	for _, s := range sets {
-		s.Delete(vif)
-	}
-
 	vlog.VI(1).Infof("Closing VIF %s", vif)
 	// Stop accepting new VCs.
 	vif.StopAccepting()
diff --git a/profiles/internal/rpc/stress/mtstress/doc.go b/profiles/internal/rpc/stress/mtstress/doc.go
index 82fe778..f3ade96 100644
--- a/profiles/internal/rpc/stress/mtstress/doc.go
+++ b/profiles/internal/rpc/stress/mtstress/doc.go
@@ -93,11 +93,6 @@
 
 "help ..." recursively displays help for all commands and topics.
 
-Output is formatted to a target width in runes, determined by checking the
-CMDLINE_WIDTH environment variable, falling back on the terminal width, falling
-back on 80 chars.  By setting CMDLINE_WIDTH=x, if x > 0 the width is x, if x < 0
-the width is unlimited, and if x == 0 or is unset one of the fallbacks is used.
-
 Usage:
    mtstress help [flags] [command/topic ...]
 
@@ -110,5 +105,9 @@
       full    - Good for cmdline output, shows all global flags.
       godoc   - Good for godoc processing.
    Override the default by setting the CMDLINE_STYLE environment variable.
+ -width=<terminal width>
+   Format output to this target width in runes, or unlimited if width < 0.
+   Defaults to the terminal width if available.  Override the default by setting
+   the CMDLINE_WIDTH environment variable.
 */
 package main
diff --git a/profiles/internal/rpc/stress/mtstress/main.go b/profiles/internal/rpc/stress/mtstress/main.go
index 5c0a8b9..24ebf44 100644
--- a/profiles/internal/rpc/stress/mtstress/main.go
+++ b/profiles/internal/rpc/stress/mtstress/main.go
@@ -10,7 +10,6 @@
 import (
 	"flag"
 	"net"
-	"os"
 	"regexp"
 	"time"
 
@@ -19,22 +18,21 @@
 	"v.io/v23/naming"
 	"v.io/v23/options"
 	"v.io/v23/verror"
-	"v.io/x/lib/cmdline"
+	"v.io/x/lib/cmdline2"
+	"v.io/x/ref/lib/v23cmd"
 	_ "v.io/x/ref/profiles"
 )
 
 func init() {
-	cmdline.HideGlobalFlagsExcept(regexp.MustCompile(`^(rate)|(duration)|(reauthenticate)$`))
+	cmdline2.HideGlobalFlagsExcept(regexp.MustCompile(`^((rate)|(duration)|(reauthenticate))$`))
 }
 
 var (
-	gctx *context.T
-
 	rate     = flag.Float64("rate", 1, "Rate, in RPCs per second, to send to the test server")
 	duration = flag.Duration("duration", 10*time.Second, "Duration for sending test traffic and measuring latency")
 	reauth   = flag.Bool("reauthenticate", false, "If true, establish a new authenticated connection for each RPC, simulating load from a distinct process")
 
-	cmdMount = &cmdline.Command{
+	cmdMount = &cmdline2.Command{
 		Name:  "mount",
 		Short: "Measure latency of the Mount RPC at a fixed request rate",
 		Long: `
@@ -48,14 +46,14 @@
 seconds, 1m for 1 minute etc.
 Valid time units are "ms", "s", "m", "h".
 `,
-		Run: func(cmd *cmdline.Command, args []string) error {
+		Runner: v23cmd.RunnerFunc(func(ctx *context.T, env *cmdline2.Env, args []string) error {
 			if got, want := len(args), 2; got != want {
-				return cmd.UsageErrorf("mount: got %d arguments, want %d", got, want)
+				return env.UsageErrorf("mount: got %d arguments, want %d", got, want)
 			}
 			mountpoint := args[0]
 			ttl, err := time.ParseDuration(args[1])
 			if err != nil {
-				return cmd.UsageErrorf("invalid TTL: %v", err)
+				return env.UsageErrorf("invalid TTL: %v", err)
 			}
 			// Make up a random server to mount
 			ep := naming.FormatEndpoint("tcp", "127.0.0.1:14141")
@@ -69,15 +67,15 @@
 				}
 				return time.Since(start), nil
 			}
-			p, err := paramsFromFlags(mountpoint)
+			p, err := paramsFromFlags(ctx, mountpoint)
 			if err != nil {
 				return err
 			}
 			return run(mount, p)
-		},
+		}),
 	}
 
-	cmdResolve = &cmdline.Command{
+	cmdResolve = &cmdline2.Command{
 		Name:  "resolve",
 		Short: "Measure latency of the Resolve RPC at a fixed request rate",
 		Long: `
@@ -87,9 +85,9 @@
 		ArgsLong: `
 <name> the object name to resolve
 `,
-		Run: func(cmd *cmdline.Command, args []string) error {
+		Runner: v23cmd.RunnerFunc(func(ctx *context.T, env *cmdline2.Env, args []string) error {
 			if got, want := len(args), 1; got != want {
-				return cmd.UsageErrorf("resolve: got %d arguments, want %d", got, want)
+				return env.UsageErrorf("resolve: got %d arguments, want %d", got, want)
 			}
 			name := args[0]
 			resolve := func(ctx *context.T) (time.Duration, error) {
@@ -103,21 +101,21 @@
 				}
 				return time.Since(start), nil
 			}
-			p, err := paramsFromFlags(name)
+			p, err := paramsFromFlags(ctx, name)
 			if err != nil {
 				return err
 			}
 			return run(resolve, p)
-		},
+		}),
 	}
 )
 
-func paramsFromFlags(objectName string) (params, error) {
+func paramsFromFlags(ctx *context.T, objectName string) (params, error) {
 	// Measure network distance to objectName
 	const iters = 5
 	addr, _ := naming.SplitAddressName(objectName)
 	if len(addr) == 0 {
-		addr, _ = naming.SplitAddressName(v23.GetNamespace(gctx).Roots()[0])
+		addr, _ = naming.SplitAddressName(v23.GetNamespace(ctx).Roots()[0])
 	}
 	ep, err := v23.NewEndpoint(addr)
 	if err != nil {
@@ -138,19 +136,16 @@
 		Rate:            *rate,
 		Duration:        *duration,
 		NetworkDistance: time.Duration(total.Nanoseconds() / iters),
-		Context:         gctx,
+		Context:         ctx,
 		Reauthenticate:  *reauth,
 	}, nil
 }
 
 func main() {
-	ctx, shutdown := v23.Init()
-	defer shutdown()
-	gctx = ctx
-	root := &cmdline.Command{
+	root := &cmdline2.Command{
 		Name:     "mtstress",
 		Short:    "Tool to stress test a mounttable service by issuing a fixed rate of requests per second and measuring latency",
-		Children: []*cmdline.Command{cmdMount, cmdResolve},
+		Children: []*cmdline2.Command{cmdMount, cmdResolve},
 	}
-	os.Exit(root.Main())
+	cmdline2.Main(root)
 }
diff --git a/profiles/internal/rpc/stress/stress.vdl.go b/profiles/internal/rpc/stress/stress.vdl.go
index 3ab11a1..0918477 100644
--- a/profiles/internal/rpc/stress/stress.vdl.go
+++ b/profiles/internal/rpc/stress/stress.vdl.go
@@ -26,7 +26,7 @@
 }
 
 func (SumArg) __VDLReflect(struct {
-	Name string "v.io/x/ref/profiles/internal/rpc/stress.SumArg"
+	Name string `vdl:"v.io/x/ref/profiles/internal/rpc/stress.SumArg"`
 }) {
 }
 
@@ -38,7 +38,7 @@
 }
 
 func (SumStats) __VDLReflect(struct {
-	Name string "v.io/x/ref/profiles/internal/rpc/stress.SumStats"
+	Name string `vdl:"v.io/x/ref/profiles/internal/rpc/stress.SumStats"`
 }) {
 }
 
diff --git a/profiles/internal/rpc/stress/stress/load.go b/profiles/internal/rpc/stress/stress/load.go
index 9c9ceeb..e041349 100644
--- a/profiles/internal/rpc/stress/stress/load.go
+++ b/profiles/internal/rpc/stress/stress/load.go
@@ -11,9 +11,9 @@
 	"runtime"
 	"time"
 
-	"v.io/v23"
-
-	"v.io/x/lib/cmdline"
+	"v.io/v23/context"
+	"v.io/x/lib/cmdline2"
+	"v.io/x/ref/lib/v23cmd"
 	"v.io/x/ref/profiles/internal/rpc/stress/internal"
 )
 
@@ -36,8 +36,8 @@
 	QpsPerCore float64
 }
 
-var cmdLoadTest = &cmdline.Command{
-	Run:      runLoadTest,
+var cmdLoadTest = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runLoadTest),
 	Name:     "load",
 	Short:    "Run load test",
 	Long:     "Run load test",
@@ -45,12 +45,12 @@
 	ArgsLong: "<server> ... A list of servers to connect to.",
 }
 
-func runLoadTest(cmd *cmdline.Command, args []string) error {
+func runLoadTest(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if len(args) == 0 {
-		return cmd.UsageErrorf("no server specified")
+		return env.UsageErrorf("no server specified")
 	}
 	if outFormat != "text" && outFormat != "json" {
-		return cmd.UsageErrorf("invalid output format: %s\n", outFormat)
+		return env.UsageErrorf("invalid output format: %s\n", outFormat)
 	}
 
 	cores := cpus
@@ -59,11 +59,8 @@
 	}
 	runtime.GOMAXPROCS(cores)
 
-	ctx, shutdown := v23.Init()
-	defer shutdown()
-
-	fmt.Fprintf(cmd.Stdout(), "starting load test against %d server(s) using %d core(s)...\n", len(args), cores)
-	fmt.Fprintf(cmd.Stdout(), "payloadSize: %d, duration: %v\n", payloadSize, duration)
+	fmt.Fprintf(env.Stdout, "starting load test against %d server(s) using %d core(s)...\n", len(args), cores)
+	fmt.Fprintf(env.Stdout, "payloadSize: %d, duration: %v\n", payloadSize, duration)
 
 	start := time.Now()
 	done := make(chan loadStats)
@@ -91,7 +88,7 @@
 	merged.QpsPerCore = merged.Qps / float64(cores)
 	elapsed := time.Since(start)
 	fmt.Printf("done after %v\n", elapsed)
-	return outLoadStats(cmd.Stdout(), outFormat, "load stats:", &merged)
+	return outLoadStats(env.Stdout, outFormat, "load stats:", &merged)
 }
 
 func outLoadStats(w io.Writer, format, title string, stats *loadStats) error {
diff --git a/profiles/internal/rpc/stress/stress/main.go b/profiles/internal/rpc/stress/stress/main.go
index 561cf60..d269d84 100644
--- a/profiles/internal/rpc/stress/stress/main.go
+++ b/profiles/internal/rpc/stress/stress/main.go
@@ -5,17 +5,15 @@
 package main
 
 import (
-	"os"
-
-	"v.io/v23"
-
-	"v.io/x/lib/cmdline"
+	"v.io/v23/context"
+	"v.io/x/lib/cmdline2"
+	"v.io/x/ref/lib/v23cmd"
 	"v.io/x/ref/profiles/internal/rpc/stress"
 	_ "v.io/x/ref/profiles/static"
 )
 
-var cmdStopServers = &cmdline.Command{
-	Run:      runStopServers,
+var cmdStopServers = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runStopServers),
 	Name:     "stop",
 	Short:    "Stop servers",
 	Long:     "Stop servers",
@@ -23,14 +21,10 @@
 	ArgsLong: "<server> ... A list of servers to stop.",
 }
 
-func runStopServers(cmd *cmdline.Command, args []string) error {
+func runStopServers(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if len(args) == 0 {
-		return cmd.UsageErrorf("no server specified")
+		return env.UsageErrorf("no server specified")
 	}
-
-	ctx, shutdown := v23.Init()
-	defer shutdown()
-
 	for _, server := range args {
 		if err := stress.StressClient(server).Stop(ctx); err != nil {
 			return err
@@ -40,16 +34,16 @@
 }
 
 func main() {
-	cmdRoot := &cmdline.Command{
+	cmdRoot := &cmdline2.Command{
 		Name:  "stress",
 		Short: "Tool to stress/load test RPC",
 		Long:  "Tool to stress/load test RPC by issuing randomly generated requests",
-		Children: []*cmdline.Command{
+		Children: []*cmdline2.Command{
 			cmdStressTest,
 			cmdStressStats,
 			cmdLoadTest,
 			cmdStopServers,
 		},
 	}
-	os.Exit(cmdRoot.Main())
+	cmdline2.Main(cmdRoot)
 }
diff --git a/profiles/internal/rpc/stress/stress/stress.go b/profiles/internal/rpc/stress/stress/stress.go
index 3d363c0..db1306a 100644
--- a/profiles/internal/rpc/stress/stress/stress.go
+++ b/profiles/internal/rpc/stress/stress/stress.go
@@ -12,9 +12,9 @@
 	"runtime"
 	"time"
 
-	"v.io/v23"
-
-	"v.io/x/lib/cmdline"
+	"v.io/v23/context"
+	"v.io/x/lib/cmdline2"
+	"v.io/x/ref/lib/v23cmd"
 	"v.io/x/ref/profiles/internal/rpc/stress"
 	"v.io/x/ref/profiles/internal/rpc/stress/internal"
 )
@@ -39,8 +39,8 @@
 	cmdStressStats.Flags.StringVar(&outFormat, "format", "text", "Stats output format; either text or json")
 }
 
-var cmdStressTest = &cmdline.Command{
-	Run:      runStressTest,
+var cmdStressTest = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runStressTest),
 	Name:     "stress",
 	Short:    "Run stress test",
 	Long:     "Run stress test",
@@ -48,23 +48,19 @@
 	ArgsLong: "<server> ... A list of servers to connect to.",
 }
 
-func runStressTest(cmd *cmdline.Command, args []string) error {
+func runStressTest(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if len(args) == 0 {
-		return cmd.UsageErrorf("no server specified")
+		return env.UsageErrorf("no server specified")
 	}
 	if outFormat != "text" && outFormat != "json" {
-		return cmd.UsageErrorf("invalid output format: %s\n", outFormat)
+		return env.UsageErrorf("invalid output format: %s\n", outFormat)
 	}
 
 	cores := runtime.NumCPU()
 	runtime.GOMAXPROCS(cores)
-
-	ctx, shutdown := v23.Init()
-	defer shutdown()
-
 	rand.Seed(time.Now().UnixNano())
-	fmt.Fprintf(cmd.Stdout(), "starting stress test against %d server(s) using %d core(s)...\n", len(args), cores)
-	fmt.Fprintf(cmd.Stdout(), "workers: %d, maxChunkCnt: %d, maxPayloadSize: %d, duration: %v\n", workers, maxChunkCnt, maxPayloadSize, duration)
+	fmt.Fprintf(env.Stdout, "starting stress test against %d server(s) using %d core(s)...\n", len(args), cores)
+	fmt.Fprintf(env.Stdout, "workers: %d, maxChunkCnt: %d, maxPayloadSize: %d, duration: %v\n", workers, maxChunkCnt, maxPayloadSize, duration)
 
 	start := time.Now()
 	done := make(chan stress.SumStats)
@@ -100,11 +96,11 @@
 	}
 	elapsed := time.Since(start)
 	fmt.Printf("done after %v\n", elapsed)
-	return outSumStats(cmd.Stdout(), outFormat, "client stats:", &merged)
+	return outSumStats(env.Stdout, outFormat, "client stats:", &merged)
 }
 
-var cmdStressStats = &cmdline.Command{
-	Run:      runStressStats,
+var cmdStressStats = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runStressStats),
 	Name:     "stats",
 	Short:    "Print out stress stats of servers",
 	Long:     "Print out stress stats of servers",
@@ -112,24 +108,20 @@
 	ArgsLong: "<server> ... A list of servers to connect to.",
 }
 
-func runStressStats(cmd *cmdline.Command, args []string) error {
+func runStressStats(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if len(args) == 0 {
-		return cmd.UsageErrorf("no server specified")
+		return env.UsageErrorf("no server specified")
 	}
 	if outFormat != "text" && outFormat != "json" {
-		return cmd.UsageErrorf("invalid output format: %s\n", outFormat)
+		return env.UsageErrorf("invalid output format: %s\n", outFormat)
 	}
-
-	ctx, shutdown := v23.Init()
-	defer shutdown()
-
 	for _, server := range args {
 		stats, err := stress.StressClient(server).GetSumStats(ctx)
 		if err != nil {
 			return err
 		}
 		title := fmt.Sprintf("server stats(%s):", server)
-		if err := outSumStats(cmd.Stdout(), outFormat, title, &stats); err != nil {
+		if err := outSumStats(env.Stdout, outFormat, title, &stats); err != nil {
 			return err
 		}
 	}
diff --git a/profiles/internal/rpc/test/client_test.go b/profiles/internal/rpc/test/client_test.go
index c44a635..9665d0d 100644
--- a/profiles/internal/rpc/test/client_test.go
+++ b/profiles/internal/rpc/test/client_test.go
@@ -411,6 +411,10 @@
 	return mocknet.DialerWithOpts(opts, network, address, timeout)
 }
 
+func simpleResolver(network, address string) (string, string, error) {
+	return network, address, nil
+}
+
 func TestStartCallBadProtocol(t *testing.T) {
 	ctx, shutdown := newCtx()
 	defer shutdown()
@@ -423,7 +427,7 @@
 		logErrors(t, msg, true, false, false, err)
 	}
 
-	rpc.RegisterProtocol("dropData", dropDataDialer, net.Listen)
+	rpc.RegisterProtocol("dropData", dropDataDialer, simpleResolver, net.Listen)
 
 	// The following test will fail due to a broken connection.
 	// We need to run mount table and servers with no security to use
diff --git a/profiles/internal/rpc/test/simple_test.go b/profiles/internal/rpc/test/simple_test.go
index 22c3da4..1ce55f8 100644
--- a/profiles/internal/rpc/test/simple_test.go
+++ b/profiles/internal/rpc/test/simple_test.go
@@ -116,7 +116,7 @@
 			t.Fatalf("unexpected error: %s", err)
 		}
 		if want := i + inc; got != want {
-			t.Fatalf("got %d, want %d")
+			t.Fatalf("got %d, want %d", got, want)
 		}
 	}
 	call.CloseSend()
diff --git a/profiles/internal/rt/runtime.go b/profiles/internal/rt/runtime.go
index 88d092c..475be9b 100644
--- a/profiles/internal/rt/runtime.go
+++ b/profiles/internal/rt/runtime.go
@@ -179,6 +179,7 @@
 }
 
 func (r *Runtime) Init(ctx *context.T) error {
+	defer vlog.LogCallf("ctx=")("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
 	return r.initMgmt(ctx)
 }
 
@@ -213,10 +214,12 @@
 }
 
 func (*Runtime) NewEndpoint(ep string) (naming.Endpoint, error) {
+	defer vlog.LogCallf("ep=%.10s...", ep)("") // AUTO-GENERATED, 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
 	// Create a new RoutingID (and StreamManager) for each server.
 	sm, err := newStreamManager()
 	if err != nil {
@@ -295,6 +298,7 @@
 }
 
 func (r *Runtime) WithNewStreamManager(ctx *context.T) (*context.T, error) {
+	defer vlog.LogCallf("ctx=")("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
 	newctx, err := r.setNewStreamManager(ctx)
 	if err != nil {
 		return ctx, err
@@ -320,6 +324,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
 	var err error
 	newctx := ctx
 
@@ -345,11 +350,13 @@
 }
 
 func (*Runtime) GetPrincipal(ctx *context.T) security.Principal {
+	defer vlog.LogCallf("ctx=")("") // AUTO-GENERATED, 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
 	otherOpts := append([]rpc.ClientOpt{}, opts...)
 
 	p, _ := ctx.Value(principalKey).(security.Principal)
@@ -376,6 +383,7 @@
 }
 
 func (*Runtime) GetClient(ctx *context.T) rpc.Client {
+	defer vlog.LogCallf("ctx=")("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
 	cl, _ := ctx.Value(clientKey).(rpc.Client)
 	return cl
 }
@@ -397,6 +405,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
 	newctx, ns, err := r.setNewNamespace(ctx, roots...)
 	if err != nil {
 		return ctx, nil, err
@@ -412,16 +421,19 @@
 }
 
 func (*Runtime) GetNamespace(ctx *context.T) namespace.T {
+	// nologcall
 	ns, _ := ctx.Value(namespaceKey).(namespace.T)
 	return ns
 }
 
 func (*Runtime) GetAppCycle(ctx *context.T) v23.AppCycle {
+	// nologcall
 	id, _ := ctx.Value(initKey).(*initData)
 	return id.appCycle
 }
 
 func (*Runtime) GetListenSpec(ctx *context.T) rpc.ListenSpec {
+	// nologcall
 	if id, _ := ctx.Value(initKey).(*initData); id.listenSpec != nil {
 		return id.listenSpec.Copy()
 	}
@@ -429,6 +441,7 @@
 }
 
 func (*Runtime) WithBackgroundContext(ctx *context.T) *context.T {
+	defer vlog.LogCallf("ctx=")("") // AUTO-GENERATED, 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.
@@ -437,6 +450,7 @@
 }
 
 func (*Runtime) GetBackgroundContext(ctx *context.T) *context.T {
+	// nologcall
 	bctx, _ := ctx.Value(backgroundKey).(*context.T)
 	if bctx == nil {
 		// There should always be a background context.  If we don't find
@@ -449,10 +463,12 @@
 }
 
 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
 	return context.WithValue(ctx, reservedNameKey, d)
 }
 
 func (*Runtime) GetReservedNameDispatcher(ctx *context.T) rpc.Dispatcher {
+	// nologcall
 	if d, ok := ctx.Value(reservedNameKey).(rpc.Dispatcher); ok {
 		return d
 	}
diff --git a/profiles/internal/testing/concurrency/state_test.go b/profiles/internal/testing/concurrency/state_test.go
index f518d74..1305139 100644
--- a/profiles/internal/testing/concurrency/state_test.go
+++ b/profiles/internal/testing/concurrency/state_test.go
@@ -114,7 +114,7 @@
 	}
 	// Check a new exploration seed has been identified.
 	if seeds.Length() != 1 {
-		t.Fatal("Unexpected number of seeds: expected %v, got %v", 1, seeds.Length())
+		t.Fatalf("Unexpected number of seeds: expected %v, got %v", 1, seeds.Length())
 	}
 	if err := root.addBranch(rightBranch, seeds); err != nil {
 		t.Fatalf("addBranch() failed: %v", err)
diff --git a/profiles/internal/testing/mocks/mocknet/mocknet.go b/profiles/internal/testing/mocks/mocknet/mocknet.go
index 0b73e4b..259563c 100644
--- a/profiles/internal/testing/mocks/mocknet/mocknet.go
+++ b/profiles/internal/testing/mocks/mocknet/mocknet.go
@@ -72,7 +72,7 @@
 //  dialer := func(network, address string, timeout time.Duration) (net.Conn, error) {
 //	    return mocknet.DialerWithOpts(mocknet.Opts{UnderlyingProtocol:"tcp"}, network, address, timeout)
 //  }
-// rpc.RegisterProtocol("brkDial", dialer, net.Listen)
+// rpc.RegisterProtocol("brkDial", dialer, resolver, net.Listen)
 //
 func DialerWithOpts(opts Opts, network, address string, timeout time.Duration) (net.Conn, error) {
 	protocol := opts.UnderlyingProtocol
diff --git a/profiles/internal/testing/mocks/mocknet/mocknet_test.go b/profiles/internal/testing/mocks/mocknet/mocknet_test.go
index f65ca4b..91c445e 100644
--- a/profiles/internal/testing/mocks/mocknet/mocknet_test.go
+++ b/profiles/internal/testing/mocks/mocknet/mocknet_test.go
@@ -272,7 +272,11 @@
 		return mocknet.DialerWithOpts(opts, network, address, timeout)
 	}
 
-	rpc.RegisterProtocol("dropControl", dropControlDialer, net.Listen)
+	simpleResolver := func(network, address string) (string, string, error) {
+		return network, address, nil
+	}
+
+	rpc.RegisterProtocol("dropControl", dropControlDialer, simpleResolver, net.Listen)
 
 	server, fn := initServer(t, ctx)
 	defer fn()
diff --git a/profiles/internal/testing/mocks/naming/namespace.go b/profiles/internal/testing/mocks/naming/namespace.go
index cdfe4b7..d38a499 100644
--- a/profiles/internal/testing/mocks/naming/namespace.go
+++ b/profiles/internal/testing/mocks/naming/namespace.go
@@ -40,7 +40,7 @@
 }
 
 func (ns *namespaceMock) Mount(ctx *context.T, name, server string, _ time.Duration, opts ...naming.NamespaceOpt) error {
-	defer vlog.LogCall()()
+	defer vlog.LogCallf("ctx=,name=%.10s...,server=%.10s...,opts...=%v", name, server, opts)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
 	ns.Lock()
 	defer ns.Unlock()
 	for n, _ := range ns.mounts {
@@ -58,7 +58,7 @@
 }
 
 func (ns *namespaceMock) Unmount(ctx *context.T, name, server string, opts ...naming.NamespaceOpt) error {
-	defer vlog.LogCall()()
+	defer vlog.LogCallf("ctx=,name=%.10s...,server=%.10s...,opts...=%v", name, server, opts)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
 	ns.Lock()
 	defer ns.Unlock()
 	e := ns.mounts[name]
@@ -84,7 +84,7 @@
 }
 
 func (ns *namespaceMock) Delete(ctx *context.T, name string, removeSubtree bool, opts ...naming.NamespaceOpt) error {
-	defer vlog.LogCall()()
+	defer vlog.LogCallf("ctx=,name=%.10s...,removeSubtree=%v,opts...=%v", name, removeSubtree, opts)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
 	ns.Lock()
 	defer ns.Unlock()
 	e := ns.mounts[name]
@@ -104,7 +104,7 @@
 }
 
 func (ns *namespaceMock) Resolve(ctx *context.T, name string, opts ...naming.NamespaceOpt) (*naming.MountEntry, error) {
-	defer vlog.LogCall()()
+	defer vlog.LogCallf("ctx=,name=%.10s...,opts...=%v", name, opts)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
 	_, name = security.SplitPatternName(name)
 	if address, suffix := naming.SplitAddressName(name); len(address) > 0 {
 		return &naming.MountEntry{
@@ -125,49 +125,43 @@
 }
 
 func (ns *namespaceMock) ResolveToMountTable(ctx *context.T, name string, opts ...naming.NamespaceOpt) (*naming.MountEntry, error) {
-	defer vlog.LogCall()()
+	defer vlog.LogCallf("ctx=,name=%.10s...,opts...=%v", name, opts)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
 	// TODO(mattr): Implement this method for tests that might need it.
 	panic("ResolveToMountTable not implemented")
-	return nil, nil
 }
 
 func (ns *namespaceMock) FlushCacheEntry(name string) bool {
-	defer vlog.LogCall()()
+	defer vlog.LogCallf("name=%.10s...", name)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
 	return false
 }
 
 func (ns *namespaceMock) CacheCtl(ctls ...naming.CacheCtl) []naming.CacheCtl {
-	defer vlog.LogCall()()
+	defer vlog.LogCallf("ctls...=%v", ctls)("") // AUTO-GENERATED, 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.LogCall()()
+	defer vlog.LogCallf("ctx=,pattern=%.10s...,opts...=%v", pattern, opts)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
 	// TODO(mattr): Implement this method for tests that might need it.
 	panic("Glob not implemented")
-	return nil, nil
 }
 
 func (ns *namespaceMock) SetRoots(...string) error {
-	defer vlog.LogCall()()
+	defer vlog.LogCall()() // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
 	panic("Calling SetRoots on a mock namespace.  This is not supported.")
-	return nil
 }
 
 func (ns *namespaceMock) Roots() []string {
-	defer vlog.LogCall()()
+	defer vlog.LogCall()() // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
 	panic("Calling Roots on a mock namespace.  This is not supported.")
-	return nil
 }
 
 func (ns *namespaceMock) GetPermissions(ctx *context.T, name string, opts ...naming.NamespaceOpt) (perms access.Permissions, version string, err error) {
-	defer vlog.LogCall()()
+	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
 	panic("Calling GetPermissions on a mock namespace.  This is not supported.")
-	return nil, "", nil
 }
 
 func (ns *namespaceMock) SetPermissions(ctx *context.T, name string, perms access.Permissions, version string, opts ...naming.NamespaceOpt) error {
-	defer vlog.LogCall()()
+	defer vlog.LogCallf("ctx=,name=%.10s...,perms=,version=%.10s...,opts...=%v", name, version, opts)("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
 	panic("Calling SetPermissions on a mock namespace.  This is not supported.")
-	return nil
 }
diff --git a/profiles/internal/util.go b/profiles/internal/util.go
index a530087..58894d7 100644
--- a/profiles/internal/util.go
+++ b/profiles/internal/util.go
@@ -39,8 +39,7 @@
 	if handle != nil {
 		config = handle.Config.Dump()
 	}
-	f.Parse(os.Args[1:], config)
-	return nil
+	return f.Parse(os.Args[1:], config)
 }
 
 // IPAddressChooser returns the preferred IP address, which is,
diff --git a/profiles/internal/vtrace/store.go b/profiles/internal/vtrace/store.go
index 583ae13..9c0d77b 100644
--- a/profiles/internal/vtrace/store.go
+++ b/profiles/internal/vtrace/store.go
@@ -13,6 +13,7 @@
 	"v.io/v23/uniqueid"
 	"v.io/v23/vtrace"
 
+	"v.io/x/lib/vlog"
 	"v.io/x/ref/lib/flags"
 )
 
@@ -60,6 +61,7 @@
 }
 
 func (s *Store) ForceCollect(id uniqueid.Id) {
+	defer vlog.LogCallf("id=")("") // AUTO-GENERATED, DO NOT EDIT, MUST BE FIRST STATEMENT
 	s.mu.Lock()
 	s.forceCollectLocked(id)
 	s.mu.Unlock()
@@ -83,6 +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
 	s.mu.Lock()
 	defer s.mu.Unlock()
 
@@ -158,6 +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
 	s.mu.Lock()
 	defer s.mu.Unlock()
 
@@ -173,6 +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
 	s.mu.Lock()
 	defer s.mu.Unlock()
 	out := &vtrace.TraceRecord{}
diff --git a/profiles/internal/vtrace/vtrace.go b/profiles/internal/vtrace/vtrace.go
index ed55e70..2dfc987 100644
--- a/profiles/internal/vtrace/vtrace.go
+++ b/profiles/internal/vtrace/vtrace.go
@@ -46,17 +46,32 @@
 	return s
 }
 
-func (s *span) ID() uniqueid.Id     { return s.id }
-func (s *span) Parent() uniqueid.Id { return s.parent }
-func (s *span) Name() string        { return s.name }
-func (s *span) Trace() uniqueid.Id  { return s.trace }
+func (s *span) ID() uniqueid.Id {
+	// nologcall
+	return s.id
+}
+func (s *span) Parent() uniqueid.Id {
+	// nologcall
+	return s.parent
+}
+func (s *span) Name() string {
+	// nologcall
+	return s.name
+}
+func (s *span) Trace() uniqueid.Id {
+	// nologcall
+	return s.trace
+}
 func (s *span) Annotate(msg string) {
+	// nologcall
 	s.store.annotate(s, msg)
 }
 func (s *span) Annotatef(format string, a ...interface{}) {
+	// nologcall
 	s.store.annotate(s, fmt.Sprintf(format, a...))
 }
 func (s *span) Finish() {
+	// nologcall
 	s.store.finish(s)
 }
 func (s *span) flags() vtrace.TraceFlags {
@@ -79,6 +94,7 @@
 // disconnected from the activity ctx is performing.  For example
 // this might be used to start background tasks.
 func (m manager) WithNewTrace(ctx *context.T) (*context.T, vtrace.Span) {
+	// nologcall
 	id, err := uniqueid.Random()
 	if err != nil {
 		vlog.Errorf("vtrace: Couldn't generate Trace Id, debug data may be lost: %v", err)
@@ -93,6 +109,7 @@
 // req contains the parameters needed to connect this span with it's
 // trace.
 func (m manager) WithContinuedTrace(ctx *context.T, name string, req vtrace.Request) (*context.T, vtrace.Span) {
+	// nologcall
 	st := getStore(ctx)
 	if req.Flags&vtrace.CollectInMemory != 0 {
 		st.ForceCollect(req.TraceId)
@@ -104,6 +121,7 @@
 // WithNewSpan derives a context with a new Span that can be used to
 // trace and annotate operations across process boundaries.
 func (m manager) WithNewSpan(ctx *context.T, name string) (*context.T, vtrace.Span) {
+	// nologcall
 	if curSpan := getSpan(ctx); curSpan != nil {
 		if curSpan.store == nil {
 			panic("nil store")
@@ -118,6 +136,7 @@
 
 // Span finds the currently active span.
 func (m manager) GetSpan(ctx *context.T) vtrace.Span {
+	// nologcall
 	if span := getSpan(ctx); span != nil {
 		return span
 	}
@@ -126,6 +145,7 @@
 
 // Request generates a vtrace.Request from the active Span.
 func (m manager) GetRequest(ctx *context.T) vtrace.Request {
+	// nologcall
 	if span := getSpan(ctx); span != nil {
 		return vtrace.Request{
 			SpanId:  span.id,
@@ -138,6 +158,7 @@
 
 // Response captures the vtrace.Response for the active Span.
 func (m manager) GetResponse(ctx *context.T) vtrace.Response {
+	// nologcall
 	if span := getSpan(ctx); span != nil {
 		return vtrace.Response{
 			Flags: span.flags(),
@@ -149,6 +170,7 @@
 
 // Store returns the current vtrace.Store.
 func (m manager) GetStore(ctx *context.T) vtrace.Store {
+	// nologcall
 	if store := getStore(ctx); store != nil {
 		return store
 	}
diff --git a/profiles/roaming/roaminginit.go b/profiles/roaming/roaminginit.go
index c6b37f6..bff4202 100644
--- a/profiles/roaming/roaminginit.go
+++ b/profiles/roaming/roaminginit.go
@@ -48,7 +48,7 @@
 
 func init() {
 	v23.RegisterProfile(Init)
-	rpc.RegisterUnknownProtocol("wsh", websocket.HybridDial, websocket.HybridListener)
+	rpc.RegisterUnknownProtocol("wsh", websocket.HybridDial, websocket.HybridResolve, websocket.HybridListener)
 	commonFlags = flags.CreateAndRegister(flag.CommandLine, flags.Runtime, flags.Listen)
 }
 
diff --git a/profiles/static/staticinit.go b/profiles/static/staticinit.go
index dc93eae..c18a028 100644
--- a/profiles/static/staticinit.go
+++ b/profiles/static/staticinit.go
@@ -31,7 +31,7 @@
 
 func init() {
 	v23.RegisterProfile(Init)
-	rpc.RegisterUnknownProtocol("wsh", websocket.HybridDial, websocket.HybridListener)
+	rpc.RegisterUnknownProtocol("wsh", websocket.HybridDial, websocket.HybridResolve, websocket.HybridListener)
 	commonFlags = flags.CreateAndRegister(flag.CommandLine, flags.Runtime, flags.Listen)
 }
 
diff --git a/services/agent/agentd/main.go b/services/agent/agentd/main.go
index afb1f70..654e4e5 100644
--- a/services/agent/agentd/main.go
+++ b/services/agent/agentd/main.go
@@ -109,7 +109,7 @@
 	defer shutdown()
 
 	if ctx, err = v23.WithPrincipal(ctx, p); err != nil {
-		vlog.Panic("failed to set principal for ctx: %v", err)
+		vlog.Panicf("failed to set principal for ctx: %v", err)
 	}
 
 	if *keypath == "" && passphrase != nil {
diff --git a/services/agent/internal/cache/cache_test.go b/services/agent/internal/cache/cache_test.go
index cd89a5d0..5729063 100644
--- a/services/agent/internal/cache/cache_test.go
+++ b/services/agent/internal/cache/cache_test.go
@@ -37,7 +37,7 @@
 func expectRecognized(roots security.BlessingRoots, key security.PublicKey, blessing string) string {
 	err := roots.Recognized(key, blessing)
 	if err != nil {
-		return fmt.Sprintf("Key (%s, %b) not matched by roots:\n%s, Recognized returns error: %v", key, blessing, roots.DebugString(), err)
+		return fmt.Sprintf("Key (%s, %v) not matched by roots:\n%s, Recognized returns error: %v", key, blessing, roots.DebugString(), err)
 	}
 	return ""
 }
diff --git a/services/agent/internal/unixfd/unixfd.go b/services/agent/internal/unixfd/unixfd.go
index 6d4328d..6497af7 100644
--- a/services/agent/internal/unixfd/unixfd.go
+++ b/services/agent/internal/unixfd/unixfd.go
@@ -35,7 +35,7 @@
 const Network string = "unixfd"
 
 func init() {
-	rpc.RegisterProtocol(Network, unixFDConn, unixFDListen)
+	rpc.RegisterProtocol(Network, unixFDConn, unixFDResolve, unixFDListen)
 }
 
 // singleConnListener implements net.Listener for an already-connected socket.
@@ -136,6 +136,10 @@
 	return c.addr
 }
 
+func unixFDResolve(_, address string) (string, string, error) {
+	return Network, address, nil
+}
+
 func unixFDListen(protocol, address string) (net.Listener, error) {
 	conn, err := unixFDConn(protocol, address, 0)
 	if err != nil {
diff --git a/services/agent/vbecome/doc.go b/services/agent/vbecome/doc.go
new file mode 100644
index 0000000..02e0b6d
--- /dev/null
+++ b/services/agent/vbecome/doc.go
@@ -0,0 +1,66 @@
+// 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 via go generate.
+// DO NOT UPDATE MANUALLY
+
+/*
+Command vbecome executes commands with a derived Vanadium principal.
+
+Usage:
+   vbecome [flags] <command> [command args...]
+
+The vbecome flags are:
+ -duration=1h0m0s
+   Duration for the blessing.
+ -name=
+   Name to use for the blessing.
+ -role=
+   Role object from which to request the blessing. If set, the blessings from
+   this role server are used and --name is ignored. If not set, the default
+   blessings of the calling principal are extended with --name.
+
+The global flags are:
+ -alsologtostderr=true
+   log to standard error as well as files
+ -log_backtrace_at=:0
+   when logging hits line file:N, emit a stack trace
+ -log_dir=
+   if non-empty, write log files to this directory
+ -logtostderr=false
+   log to standard error instead of files
+ -max_stack_buf_size=4292608
+   max size in bytes of the buffer to use for logging stack traces
+ -stderrthreshold=2
+   logs at or above this threshold go to stderr
+ -v=0
+   log level for V logs
+ -v23.credentials=
+   directory to use for storing security credentials
+ -v23.i18n-catalogue=
+   18n catalogue files to load, comma separated
+ -v23.metadata=<just specify -v23.metadata to activate>
+   Displays metadata for the program and exits.
+ -v23.namespace.root=[/(dev.v.io/role/vprod/service/mounttabled)@ns.dev.v.io:8101]
+   local namespace root; can be repeated to provided multiple roots
+ -v23.proxy=
+   object name of proxy service to use to export services across network
+   boundaries
+ -v23.tcp.address=
+   address to listen on
+ -v23.tcp.protocol=wsh
+   protocol to listen with
+ -v23.vtrace.cache-size=1024
+   The number of vtrace traces to store in memory.
+ -v23.vtrace.collect-regexp=
+   Spans and annotations that match this regular expression will trigger trace
+   collection.
+ -v23.vtrace.dump-on-shutdown=true
+   If true, dump all stored traces on runtime shutdown.
+ -v23.vtrace.sample-rate=0
+   Rate (from 0.0 to 1.0) to sample vtrace traces.
+ -vmodule=
+   comma-separated list of pattern=N settings for file-filtered logging
+*/
+package main
diff --git a/services/agent/vbecome/v23_test.go b/services/agent/vbecome/v23_test.go
new file mode 100644
index 0000000..35ff567
--- /dev/null
+++ b/services/agent/vbecome/v23_test.go
@@ -0,0 +1,29 @@
+// 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 via go generate.
+// DO NOT UPDATE MANUALLY
+package main_test
+
+import "testing"
+import "os"
+
+import "v.io/x/ref/test"
+import "v.io/x/ref/test/v23tests"
+
+func TestMain(m *testing.M) {
+	test.Init()
+	cleanup := v23tests.UseSharedBinDir()
+	r := m.Run()
+	cleanup()
+	os.Exit(r)
+}
+
+func TestV23BecomeRole(t *testing.T) {
+	v23tests.RunTest(t, V23TestBecomeRole)
+}
+
+func TestV23BecomeName(t *testing.T) {
+	v23tests.RunTest(t, V23TestBecomeName)
+}
diff --git a/services/agent/vbecome/vbecome.go b/services/agent/vbecome/vbecome.go
new file mode 100644
index 0000000..d4c224b
--- /dev/null
+++ b/services/agent/vbecome/vbecome.go
@@ -0,0 +1,177 @@
+// 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 following enables go generate to generate the doc.go file.
+//go:generate go run $V23_ROOT/release/go/src/v.io/x/lib/cmdline/testdata/gendoc.go . -help
+
+package main
+
+import (
+	"crypto/ecdsa"
+	"crypto/elliptic"
+	"crypto/rand"
+	"fmt"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"syscall"
+	"time"
+
+	"v.io/v23"
+	"v.io/v23/context"
+	"v.io/v23/security"
+	"v.io/x/lib/cmdline2"
+	"v.io/x/lib/vlog"
+	"v.io/x/ref/envvar"
+	vsecurity "v.io/x/ref/lib/security"
+	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/services/agent/internal/server"
+	"v.io/x/ref/services/role"
+
+	_ "v.io/x/ref/profiles"
+)
+
+var (
+	durationFlag time.Duration
+	nameFlag     string
+	roleFlag     string
+)
+
+var cmdVbecome = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(vbecome),
+	Name:     "vbecome",
+	Short:    "executes commands with a derived Vanadium principal",
+	Long:     "Command vbecome executes commands with a derived Vanadium principal.",
+	ArgsName: "<command> [command args...]",
+}
+
+const childAgentFd = 3
+const keyServerFd = 4
+
+func main() {
+	cmdline2.HideGlobalFlagsExcept()
+	syscall.CloseOnExec(childAgentFd)
+	syscall.CloseOnExec(keyServerFd)
+
+	cmdVbecome.Flags.DurationVar(&durationFlag, "duration", 1*time.Hour, "Duration for the blessing.")
+	cmdVbecome.Flags.StringVar(&nameFlag, "name", "", "Name to use for the blessing.")
+	cmdVbecome.Flags.StringVar(&roleFlag, "role", "", "Role object from which to request the blessing. If set, the blessings from this role server are used and --name is ignored. If not set, the default blessings of the calling principal are extended with --name.")
+
+	cmdline2.Main(cmdVbecome)
+}
+
+func vbecome(ctx *context.T, env *cmdline2.Env, args []string) error {
+	if len(args) == 0 {
+		if shell := os.Getenv("SHELL"); shell != "" {
+			args = []string{shell}
+		} else {
+			return fmt.Errorf("You must specify a command to run.")
+		}
+	}
+
+	key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+	if err != nil {
+		return err
+	}
+	signer := security.NewInMemoryECDSASigner(key)
+	principal, err := vsecurity.NewPrincipalFromSigner(signer, nil)
+	if err != nil {
+		return err
+	}
+
+	if len(roleFlag) == 0 {
+		if len(nameFlag) == 0 {
+			nameFlag = filepath.Base(args[0])
+		}
+		if err := bless(ctx, principal, nameFlag); err != nil {
+			return err
+		}
+		ctx, err = v23.WithPrincipal(ctx, principal)
+		if err != nil {
+			return err
+		}
+	} else {
+		// The role server expects the client's blessing name to end
+		// with RoleSuffix. This is to avoid accidentally granting role
+		// access to anything else that might have been blessed by the
+		// same principal.
+		if err := bless(ctx, principal, role.RoleSuffix); err != nil {
+			return err
+		}
+		ctx, err = v23.WithPrincipal(ctx, principal)
+		if err != nil {
+			return err
+		}
+		if err = setupRoleBlessings(ctx, roleFlag); err != nil {
+			return err
+		}
+	}
+
+	// Start an agent server.
+	sock, endpoint, err := server.RunAnonymousAgent(ctx, principal, childAgentFd)
+	if err != nil {
+		return err
+	}
+	if err = os.Setenv(envvar.AgentEndpoint, endpoint); err != nil {
+		vlog.Fatalf("setenv: %v", err)
+	}
+
+	return doExec(args, sock)
+}
+
+func bless(ctx *context.T, p security.Principal, name string) error {
+	caveat, err := security.NewExpiryCaveat(time.Now().Add(durationFlag))
+	if err != nil {
+		vlog.Errorf("Couldn't create caveat")
+		return err
+	}
+
+	rp := v23.GetPrincipal(ctx)
+	blessing, err := rp.Bless(p.PublicKey(), rp.BlessingStore().Default(), name, caveat)
+	if err != nil {
+		vlog.Errorf("Couldn't bless")
+		return err
+	}
+
+	if err = p.BlessingStore().SetDefault(blessing); err != nil {
+		vlog.Errorf("Couldn't set default blessing")
+		return err
+	}
+	if _, err = p.BlessingStore().Set(blessing, security.AllPrincipals); err != nil {
+		vlog.Errorf("Couldn't set default client blessing")
+		return err
+	}
+	if err = p.AddToRoots(blessing); err != nil {
+		vlog.Errorf("Couldn't set trusted roots")
+		return err
+	}
+	return nil
+}
+
+func doExec(args []string, sock *os.File) error {
+	cmd := exec.Command(args[0], args[1:]...)
+	cmd.Stdin = os.Stdin
+	cmd.Stdout = os.Stdout
+	cmd.Stderr = os.Stderr
+	cmd.ExtraFiles = []*os.File{sock}
+	return cmd.Run()
+}
+
+func setupRoleBlessings(ctx *context.T, roleStr string) error {
+	b, err := role.RoleClient(roleStr).SeekBlessings(ctx)
+	if err != nil {
+		return err
+	}
+	p := v23.GetPrincipal(ctx)
+	// TODO(rthellend,ashankar): Revisit this configuration.
+	// SetDefault: Should we expect users to want to act as a server on behalf of the role (by default?)
+	// AllPrincipals: Do we not want to be discriminating about which services we use the role blessing at.
+	if err := p.BlessingStore().SetDefault(b); err != nil {
+		return err
+	}
+	if _, err := p.BlessingStore().Set(b, security.AllPrincipals); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/services/agent/vbecome/vbecome_v23_test.go b/services/agent/vbecome/vbecome_v23_test.go
new file mode 100644
index 0000000..298af90
--- /dev/null
+++ b/services/agent/vbecome/vbecome_v23_test.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_test
+
+import (
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"regexp"
+
+	_ "v.io/x/ref/profiles"
+	"v.io/x/ref/test/v23tests"
+)
+
+//go:generate v23 test generate .
+
+func writeRoledConfig() (path string, shutdown func(), err error) {
+	dir, err := ioutil.TempDir("", "")
+	if err != nil {
+		return dir, nil, err
+	}
+	err = ioutil.WriteFile(filepath.Join(dir, "therole.conf"), []byte(`
+{
+  "Members": ["root/child"],
+  "Extend": true
+}
+`), 0644)
+	return dir, func() { os.RemoveAll(dir) }, err
+}
+
+func V23TestBecomeRole(t *v23tests.T) {
+	vbecome := t.BuildV23Pkg("v.io/x/ref/services/agent/vbecome")
+	principal := t.BuildV23Pkg("v.io/x/ref/cmd/principal")
+
+	roled := t.BuildV23Pkg("v.io/x/ref/services/role/roled")
+	roledCreds, _ := t.Shell().NewChildCredentials("master")
+	roled = roled.WithStartOpts(roled.StartOpts().WithCustomCredentials(roledCreds))
+
+	v23tests.RunRootMT(t, "--v23.tcp.address=127.0.0.1:0")
+
+	dir, shutdown, err := writeRoledConfig()
+	if err != nil {
+		t.Fatalf("Couldn't write roled config: %v", err)
+	}
+	defer shutdown()
+	roled.Start("--v23.tcp.address=127.0.0.1:0", "--config-dir", dir, "--name", "roled")
+
+	output := vbecome.Run("--role=roled/therole", principal.Path(), "dump")
+	want := regexp.MustCompile(`Default Blessings\s+root/master/therole/root/child`)
+	if !want.MatchString(output) {
+		t.Errorf("Principal didn't have the role blessing:\n %s", output)
+	}
+}
+
+func V23TestBecomeName(t *v23tests.T) {
+	vbecome := t.BuildV23Pkg("v.io/x/ref/services/agent/vbecome")
+	principal := t.BuildV23Pkg("v.io/x/ref/cmd/principal")
+
+	v23tests.RunRootMT(t, "--v23.tcp.address=127.0.0.1:0")
+	output := vbecome.Run("--name=bob", principal.Path(), "dump")
+	want := regexp.MustCompile(`Default Blessings\s+root/child/bob`)
+	if !want.MatchString(output) {
+		t.Errorf("Principal didn't have the expected blessing:\n %s", output)
+	}
+}
diff --git a/services/application/application/doc.go b/services/application/application/doc.go
index 9ec0e14..2f6d774 100644
--- a/services/application/application/doc.go
+++ b/services/application/application/doc.go
@@ -109,11 +109,6 @@
 
 "help ..." recursively displays help for all commands and topics.
 
-Output is formatted to a target width in runes, determined by checking the
-CMDLINE_WIDTH environment variable, falling back on the terminal width, falling
-back on 80 chars.  By setting CMDLINE_WIDTH=x, if x > 0 the width is x, if x < 0
-the width is unlimited, and if x == 0 or is unset one of the fallbacks is used.
-
 Usage:
    application help [flags] [command/topic ...]
 
@@ -126,5 +121,9 @@
       full    - Good for cmdline output, shows all global flags.
       godoc   - Good for godoc processing.
    Override the default by setting the CMDLINE_STYLE environment variable.
+ -width=<terminal width>
+   Format output to this target width in runes, or unlimited if width < 0.
+   Defaults to the terminal width if available.  Override the default by setting
+   the CMDLINE_WIDTH environment variable.
 */
 package main
diff --git a/services/application/application/impl.go b/services/application/application/impl.go
index 448661e..605eb6b 100644
--- a/services/application/application/impl.go
+++ b/services/application/application/impl.go
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// The following enables go generate to generate the doc.go file.
+//go:generate go run $V23_ROOT/release/go/src/v.io/x/lib/cmdline/testdata/gendoc.go .
+
 package main
 
 import (
@@ -17,16 +20,19 @@
 
 	"v.io/v23/context"
 	"v.io/v23/services/application"
-	"v.io/x/lib/cmdline"
+	"v.io/x/lib/cmdline2"
+	"v.io/x/ref/lib/v23cmd"
+	_ "v.io/x/ref/profiles"
 	"v.io/x/ref/services/repository"
 )
 
-func init() {
-	cmdline.HideGlobalFlagsExcept()
+func main() {
+	cmdline2.HideGlobalFlagsExcept()
+	cmdline2.Main(cmdRoot)
 }
 
-func getEnvelopeJSON(app repository.ApplicationClientMethods, profiles string) ([]byte, error) {
-	ctx, cancel := context.WithTimeout(gctx, time.Minute)
+func getEnvelopeJSON(ctx *context.T, app repository.ApplicationClientMethods, profiles string) ([]byte, error) {
+	ctx, cancel := context.WithTimeout(ctx, time.Minute)
 	defer cancel()
 	env, err := app.Match(ctx, strings.Split(profiles, ","))
 	if err != nil {
@@ -39,12 +45,12 @@
 	return j, nil
 }
 
-func putEnvelopeJSON(app repository.ApplicationClientMethods, profiles string, j []byte) error {
+func putEnvelopeJSON(ctx *context.T, app repository.ApplicationClientMethods, profiles string, j []byte) error {
 	var env application.Envelope
 	if err := json.Unmarshal(j, &env); err != nil {
 		return fmt.Errorf("Unmarshal(%v) failed: %v", string(j), err)
 	}
-	ctx, cancel := context.WithTimeout(gctx, time.Minute)
+	ctx, cancel := context.WithTimeout(ctx, time.Minute)
 	defer cancel()
 	if err := app.Put(ctx, strings.Split(profiles, ","), env); err != nil {
 		return err
@@ -52,8 +58,8 @@
 	return nil
 }
 
-func promptUser(cmd *cmdline.Command, msg string) string {
-	fmt.Fprint(cmd.Stdout(), msg)
+func promptUser(env *cmdline2.Env, msg string) string {
+	fmt.Fprint(env.Stdout, msg)
 	var answer string
 	if _, err := fmt.Scanf("%s", &answer); err != nil {
 		return ""
@@ -61,8 +67,8 @@
 	return answer
 }
 
-var cmdMatch = &cmdline.Command{
-	Run:      runMatch,
+var cmdMatch = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runMatch),
 	Name:     "match",
 	Short:    "Shows the first matching envelope that matches the given profiles.",
 	Long:     "Shows the first matching envelope that matches the given profiles.",
@@ -72,22 +78,22 @@
 <profiles> is a comma-separated list of profiles.`,
 }
 
-func runMatch(cmd *cmdline.Command, args []string) error {
+func runMatch(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, got := 2, len(args); expected != got {
-		return cmd.UsageErrorf("match: incorrect number of arguments, expected %d, got %d", expected, got)
+		return env.UsageErrorf("match: incorrect number of arguments, expected %d, got %d", expected, got)
 	}
 	name, profiles := args[0], args[1]
 	app := repository.ApplicationClient(name)
-	j, err := getEnvelopeJSON(app, profiles)
+	j, err := getEnvelopeJSON(ctx, app, profiles)
 	if err != nil {
 		return err
 	}
-	fmt.Fprintln(cmd.Stdout(), string(j))
+	fmt.Fprintln(env.Stdout, string(j))
 	return nil
 }
 
-var cmdPut = &cmdline.Command{
-	Run:      runPut,
+var cmdPut = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runPut),
 	Name:     "put",
 	Short:    "Add the given envelope to the application for the given profiles.",
 	Long:     "Add the given envelope to the application for the given profiles.",
@@ -99,9 +105,9 @@
 not provided, the user will be prompted to enter the data manually.`,
 }
 
-func runPut(cmd *cmdline.Command, args []string) error {
+func runPut(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if got := len(args); got != 2 && got != 3 {
-		return cmd.UsageErrorf("put: incorrect number of arguments, expected 2 or 3, got %d", got)
+		return env.UsageErrorf("put: incorrect number of arguments, expected 2 or 3, got %d", got)
 	}
 	name, profiles := args[0], args[1]
 	app := repository.ApplicationClient(name)
@@ -111,25 +117,25 @@
 		if err != nil {
 			return fmt.Errorf("ReadFile(%v): %v", envelope, err)
 		}
-		if err = putEnvelopeJSON(app, profiles, j); err != nil {
+		if err = putEnvelopeJSON(ctx, app, profiles, j); err != nil {
 			return err
 		}
-		fmt.Fprintln(cmd.Stdout(), "Application envelope added successfully.")
+		fmt.Fprintln(env.Stdout, "Application envelope added successfully.")
 		return nil
 	}
-	env := application.Envelope{Args: []string{}, Env: []string{}, Packages: application.Packages{}}
-	j, err := json.MarshalIndent(env, "", "  ")
+	envelope := application.Envelope{Args: []string{}, Env: []string{}, Packages: application.Packages{}}
+	j, err := json.MarshalIndent(envelope, "", "  ")
 	if err != nil {
 		return fmt.Errorf("MarshalIndent() failed: %v", err)
 	}
-	if err := editAndPutEnvelopeJSON(cmd, app, profiles, j); err != nil {
+	if err := editAndPutEnvelopeJSON(ctx, env, app, profiles, j); err != nil {
 		return err
 	}
 	return nil
 }
 
-var cmdRemove = &cmdline.Command{
-	Run:      runRemove,
+var cmdRemove = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runRemove),
 	Name:     "remove",
 	Short:    "removes the application envelope for the given profile.",
 	Long:     "removes the application envelope for the given profile.",
@@ -139,23 +145,23 @@
 <profile> is a profile.`,
 }
 
-func runRemove(cmd *cmdline.Command, args []string) error {
+func runRemove(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, got := 2, len(args); expected != got {
-		return cmd.UsageErrorf("remove: incorrect number of arguments, expected %d, got %d", expected, got)
+		return env.UsageErrorf("remove: incorrect number of arguments, expected %d, got %d", expected, got)
 	}
 	name, profile := args[0], args[1]
 	app := repository.ApplicationClient(name)
-	ctx, cancel := context.WithTimeout(gctx, time.Minute)
+	ctx, cancel := context.WithTimeout(ctx, time.Minute)
 	defer cancel()
 	if err := app.Remove(ctx, profile); err != nil {
 		return err
 	}
-	fmt.Fprintln(cmd.Stdout(), "Application envelope removed successfully.")
+	fmt.Fprintln(env.Stdout, "Application envelope removed successfully.")
 	return nil
 }
 
-var cmdEdit = &cmdline.Command{
-	Run:      runEdit,
+var cmdEdit = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runEdit),
 	Name:     "edit",
 	Short:    "edits the application envelope for the given profile.",
 	Long:     "edits the application envelope for the given profile.",
@@ -165,24 +171,24 @@
 <profile> is a profile.`,
 }
 
-func runEdit(cmd *cmdline.Command, args []string) error {
+func runEdit(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, got := 2, len(args); expected != got {
-		return cmd.UsageErrorf("edit: incorrect number of arguments, expected %d, got %d", expected, got)
+		return env.UsageErrorf("edit: incorrect number of arguments, expected %d, got %d", expected, got)
 	}
 	name, profile := args[0], args[1]
 	app := repository.ApplicationClient(name)
 
-	envData, err := getEnvelopeJSON(app, profile)
+	envData, err := getEnvelopeJSON(ctx, app, profile)
 	if err != nil {
 		return err
 	}
-	if err := editAndPutEnvelopeJSON(cmd, app, profile, envData); err != nil {
+	if err := editAndPutEnvelopeJSON(ctx, env, app, profile, envData); err != nil {
 		return err
 	}
 	return nil
 }
 
-func editAndPutEnvelopeJSON(cmd *cmdline.Command, app repository.ApplicationClientMethods, profile string, envData []byte) error {
+func editAndPutEnvelopeJSON(ctx *context.T, env *cmdline2.Env, app repository.ApplicationClientMethods, profile string, envData []byte) error {
 	f, err := ioutil.TempFile("", "application-edit-")
 	if err != nil {
 		return fmt.Errorf("TempFile() failed: %v", err)
@@ -207,36 +213,34 @@
 		}
 		newData, err := ioutil.ReadFile(fileName)
 		if err != nil {
-			fmt.Fprintf(cmd.Stdout(), "Error: %v\n", err)
-			if ans := promptUser(cmd, "Try again? [y/N] "); strings.ToUpper(ans) == "Y" {
+			fmt.Fprintf(env.Stdout, "Error: %v\n", err)
+			if ans := promptUser(env, "Try again? [y/N] "); strings.ToUpper(ans) == "Y" {
 				continue
 			}
 			return errors.New("aborted")
 		}
 		if bytes.Compare(envData, newData) == 0 {
-			fmt.Fprintln(cmd.Stdout(), "Nothing changed")
+			fmt.Fprintln(env.Stdout, "Nothing changed")
 			return nil
 		}
-		if err = putEnvelopeJSON(app, profile, newData); err != nil {
-			fmt.Fprintf(cmd.Stdout(), "Error: %v\n", err)
-			if ans := promptUser(cmd, "Try again? [y/N] "); strings.ToUpper(ans) == "Y" {
+		if err = putEnvelopeJSON(ctx, app, profile, newData); err != nil {
+			fmt.Fprintf(env.Stdout, "Error: %v\n", err)
+			if ans := promptUser(env, "Try again? [y/N] "); strings.ToUpper(ans) == "Y" {
 				continue
 			}
 			return errors.New("aborted")
 		}
 		break
 	}
-	fmt.Fprintln(cmd.Stdout(), "Application envelope updated successfully.")
+	fmt.Fprintln(env.Stdout, "Application envelope updated successfully.")
 	return nil
 }
 
-func root() *cmdline.Command {
-	return &cmdline.Command{
-		Name:  "application",
-		Short: "manages the Vanadium application repository",
-		Long: `
+var cmdRoot = &cmdline2.Command{
+	Name:  "application",
+	Short: "manages the Vanadium application repository",
+	Long: `
 Command application manages the Vanadium application repository.
 `,
-		Children: []*cmdline.Command{cmdMatch, cmdPut, cmdRemove, cmdEdit},
-	}
+	Children: []*cmdline2.Command{cmdMatch, cmdPut, cmdRemove, cmdEdit},
 }
diff --git a/services/application/application/impl_test.go b/services/application/application/impl_test.go
index 8c8f0fe..7683569 100644
--- a/services/application/application/impl_test.go
+++ b/services/application/application/impl_test.go
@@ -18,8 +18,9 @@
 	"v.io/v23/security"
 	"v.io/v23/security/access"
 	"v.io/v23/services/application"
+	"v.io/x/lib/cmdline2"
 	"v.io/x/lib/vlog"
-
+	"v.io/x/ref/lib/v23cmd"
 	_ "v.io/x/ref/profiles"
 	"v.io/x/ref/services/repository"
 	"v.io/x/ref/test"
@@ -141,24 +142,22 @@
 }
 
 func TestApplicationClient(t *testing.T) {
-	var shutdown v23.Shutdown
-	gctx, shutdown = test.InitForTest()
+	ctx, shutdown := test.InitForTest()
 	defer shutdown()
 
-	server, endpoint, err := startServer(t, gctx)
+	server, endpoint, err := startServer(t, ctx)
 	if err != nil {
 		return
 	}
 	defer stopServer(t, server)
 	// Setup the command-line.
-	cmd := root()
 	var stdout, stderr bytes.Buffer
-	cmd.Init(nil, &stdout, &stderr)
+	env := &cmdline2.Env{Stdout: &stdout, Stderr: &stderr}
 	appName := naming.JoinAddressName(endpoint.String(), "myapp/1")
 	profile := "myprofile"
 
 	// Test the 'Match' command.
-	if err := cmd.Execute([]string{"match", appName, profile}); err != nil {
+	if err := v23cmd.ParseAndRun(cmdRoot, ctx, env, []string{"match", appName, profile}); err != nil {
 		t.Fatalf("%v", err)
 	}
 	if expected, got := jsonEnv, strings.TrimSpace(stdout.String()); got != expected {
@@ -179,7 +178,7 @@
 	if err = f.Close(); err != nil {
 		t.Fatalf("%v", err)
 	}
-	if err := cmd.Execute([]string{"put", appName, profile, fileName}); err != nil {
+	if err := v23cmd.ParseAndRun(cmdRoot, ctx, env, []string{"put", appName, profile, fileName}); err != nil {
 		t.Fatalf("%v", err)
 	}
 	if expected, got := "Application envelope added successfully.", strings.TrimSpace(stdout.String()); got != expected {
@@ -188,7 +187,7 @@
 	stdout.Reset()
 
 	// Test the 'remove' command.
-	if err := cmd.Execute([]string{"remove", appName, profile}); err != nil {
+	if err := v23cmd.ParseAndRun(cmdRoot, ctx, env, []string{"remove", appName, profile}); err != nil {
 		t.Fatalf("%v", err)
 	}
 	if expected, got := "Application envelope removed successfully.", strings.TrimSpace(stdout.String()); got != expected {
@@ -198,7 +197,7 @@
 
 	// Test the 'edit' command. (nothing changed)
 	os.Setenv("EDITOR", "true")
-	if err := cmd.Execute([]string{"edit", appName, profile}); err != nil {
+	if err := v23cmd.ParseAndRun(cmdRoot, ctx, env, []string{"edit", appName, profile}); err != nil {
 		t.Fatalf("%v", err)
 	}
 	if expected, got := "Nothing changed", strings.TrimSpace(stdout.String()); got != expected {
@@ -208,7 +207,7 @@
 
 	// Test the 'edit' command.
 	os.Setenv("EDITOR", "perl -pi -e 's/arg1/arg111/'")
-	if err := cmd.Execute([]string{"edit", appName, profile}); err != nil {
+	if err := v23cmd.ParseAndRun(cmdRoot, ctx, env, []string{"edit", appName, profile}); err != nil {
 		t.Fatalf("%v", err)
 	}
 	if expected, got := "Application envelope updated successfully.", strings.TrimSpace(stdout.String()); got != expected {
diff --git a/services/application/application/main.go b/services/application/application/main.go
deleted file mode 100644
index e33dc99..0000000
--- a/services/application/application/main.go
+++ /dev/null
@@ -1,27 +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 following enables go generate to generate the doc.go file.
-//go:generate go run $V23_ROOT/release/go/src/v.io/x/lib/cmdline/testdata/gendoc.go .
-
-package main
-
-import (
-	"os"
-
-	"v.io/v23"
-	"v.io/v23/context"
-
-	_ "v.io/x/ref/profiles"
-)
-
-var gctx *context.T
-
-func main() {
-	var shutdown v23.Shutdown
-	gctx, shutdown = v23.Init()
-	exitCode := root().Main()
-	shutdown()
-	os.Exit(exitCode)
-}
diff --git a/services/binary/binary/doc.go b/services/binary/binary/doc.go
index 04a4f73..e182c70 100644
--- a/services/binary/binary/doc.go
+++ b/services/binary/binary/doc.go
@@ -110,11 +110,6 @@
 
 "help ..." recursively displays help for all commands and topics.
 
-Output is formatted to a target width in runes, determined by checking the
-CMDLINE_WIDTH environment variable, falling back on the terminal width, falling
-back on 80 chars.  By setting CMDLINE_WIDTH=x, if x > 0 the width is x, if x < 0
-the width is unlimited, and if x == 0 or is unset one of the fallbacks is used.
-
 Usage:
    binary help [flags] [command/topic ...]
 
@@ -127,5 +122,9 @@
       full    - Good for cmdline output, shows all global flags.
       godoc   - Good for godoc processing.
    Override the default by setting the CMDLINE_STYLE environment variable.
+ -width=<terminal width>
+   Format output to this target width in runes, or unlimited if width < 0.
+   Defaults to the terminal width if available.  Override the default by setting
+   the CMDLINE_WIDTH environment variable.
 */
 package main
diff --git a/services/binary/binary/impl.go b/services/binary/binary/impl.go
index a86ede8..5c35b8e 100644
--- a/services/binary/binary/impl.go
+++ b/services/binary/binary/impl.go
@@ -2,22 +2,29 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// The following enables go generate to generate the doc.go file.
+//go:generate go run $V23_ROOT/release/go/src/v.io/x/lib/cmdline/testdata/gendoc.go .
+
 package main
 
 import (
 	"fmt"
 	"os"
 
-	"v.io/x/lib/cmdline"
+	"v.io/v23/context"
+	"v.io/x/lib/cmdline2"
+	"v.io/x/ref/lib/v23cmd"
+	_ "v.io/x/ref/profiles"
 	"v.io/x/ref/services/internal/binarylib"
 )
 
-func init() {
-	cmdline.HideGlobalFlagsExcept()
+func main() {
+	cmdline2.HideGlobalFlagsExcept()
+	cmdline2.Main(cmdRoot)
 }
 
-var cmdDelete = &cmdline.Command{
-	Run:      runDelete,
+var cmdDelete = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runDelete),
 	Name:     "delete",
 	Short:    "Delete a binary",
 	Long:     "Delete connects to the binary repository and deletes the specified binary",
@@ -25,22 +32,22 @@
 	ArgsLong: "<von> is the vanadium object name of the binary to delete",
 }
 
-func runDelete(cmd *cmdline.Command, args []string) error {
+func runDelete(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, got := 1, len(args); expected != got {
-		return cmd.UsageErrorf("delete: incorrect number of arguments, expected %d, got %d", expected, got)
+		return env.UsageErrorf("delete: incorrect number of arguments, expected %d, got %d", expected, got)
 	}
 	von := args[0]
-	if err := binarylib.Delete(gctx, von); err != nil {
+	if err := binarylib.Delete(ctx, von); err != nil {
 		return err
 	}
-	fmt.Fprintf(cmd.Stdout(), "Binary deleted successfully\n")
+	fmt.Fprintf(env.Stdout, "Binary deleted successfully\n")
 	return nil
 }
 
-var cmdDownload = &cmdline.Command{
-	Run:   runDownload,
-	Name:  "download",
-	Short: "Download a binary",
+var cmdDownload = &cmdline2.Command{
+	Runner: v23cmd.RunnerFunc(runDownload),
+	Name:   "download",
+	Short:  "Download a binary",
 	Long: `
 Download connects to the binary repository, downloads the specified binary, and
 writes it to a file.
@@ -52,22 +59,22 @@
 `,
 }
 
-func runDownload(cmd *cmdline.Command, args []string) error {
+func runDownload(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, got := 2, len(args); expected != got {
-		return cmd.UsageErrorf("download: incorrect number of arguments, expected %d, got %d", expected, got)
+		return env.UsageErrorf("download: incorrect number of arguments, expected %d, got %d", expected, got)
 	}
 	von, filename := args[0], args[1]
-	if err := binarylib.DownloadToFile(gctx, von, filename); err != nil {
+	if err := binarylib.DownloadToFile(ctx, von, filename); err != nil {
 		return err
 	}
-	fmt.Fprintf(cmd.Stdout(), "Binary downloaded to file %s\n", filename)
+	fmt.Fprintf(env.Stdout, "Binary downloaded to file %s\n", filename)
 	return nil
 }
 
-var cmdUpload = &cmdline.Command{
-	Run:   runUpload,
-	Name:  "upload",
-	Short: "Upload a binary or directory archive",
+var cmdUpload = &cmdline2.Command{
+	Runner: v23cmd.RunnerFunc(runUpload),
+	Name:   "upload",
+	Short:  "Upload a binary or directory archive",
 	Long: `
 Upload connects to the binary repository and uploads the binary of the specified
 file or archive of the specified directory. When successful, it writes the name of the new binary to stdout.
@@ -79,9 +86,9 @@
 `,
 }
 
-func runUpload(cmd *cmdline.Command, args []string) error {
+func runUpload(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, got := 2, len(args); expected != got {
-		return cmd.UsageErrorf("upload: incorrect number of arguments, expected %d, got %d", expected, got)
+		return env.UsageErrorf("upload: incorrect number of arguments, expected %d, got %d", expected, got)
 	}
 	von, filename := args[0], args[1]
 	fi, err := os.Stat(filename)
@@ -89,23 +96,23 @@
 		return err
 	}
 	if fi.IsDir() {
-		sig, err := binarylib.UploadFromDir(gctx, von, filename)
+		sig, err := binarylib.UploadFromDir(ctx, von, filename)
 		if err != nil {
 			return err
 		}
-		fmt.Fprintf(cmd.Stdout(), "Binary package uploaded from directory %s signature(%v)\n", filename, sig)
+		fmt.Fprintf(env.Stdout, "Binary package uploaded from directory %s signature(%v)\n", filename, sig)
 		return nil
 	}
-	sig, err := binarylib.UploadFromFile(gctx, von, filename)
+	sig, err := binarylib.UploadFromFile(ctx, von, filename)
 	if err != nil {
 		return err
 	}
-	fmt.Fprintf(cmd.Stdout(), "Binary uploaded from file %s signature(%v)\n", filename, sig)
+	fmt.Fprintf(env.Stdout, "Binary uploaded from file %s signature(%v)\n", filename, sig)
 	return nil
 }
 
-var cmdURL = &cmdline.Command{
-	Run:      runURL,
+var cmdURL = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runURL),
 	Name:     "url",
 	Short:    "Fetch a download URL",
 	Long:     "Connect to the binary repository and fetch the download URL for the given vanadium object name.",
@@ -113,26 +120,24 @@
 	ArgsLong: "<von> is the vanadium object name of the binary repository",
 }
 
-func runURL(cmd *cmdline.Command, args []string) error {
+func runURL(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, got := 1, len(args); expected != got {
-		return cmd.UsageErrorf("rooturl: incorrect number of arguments, expected %d, got %d", expected, got)
+		return env.UsageErrorf("rooturl: incorrect number of arguments, expected %d, got %d", expected, got)
 	}
 	von := args[0]
-	url, _, err := binarylib.DownloadUrl(gctx, von)
+	url, _, err := binarylib.DownloadUrl(ctx, von)
 	if err != nil {
 		return err
 	}
-	fmt.Fprintf(cmd.Stdout(), "%v\n", url)
+	fmt.Fprintf(env.Stdout, "%v\n", url)
 	return nil
 }
 
-func root() *cmdline.Command {
-	return &cmdline.Command{
-		Name:  "binary",
-		Short: "manages the Vanadium binary repository",
-		Long: `
+var cmdRoot = &cmdline2.Command{
+	Name:  "binary",
+	Short: "manages the Vanadium binary repository",
+	Long: `
 Command binary manages the Vanadium binary repository.
 `,
-		Children: []*cmdline.Command{cmdDelete, cmdDownload, cmdUpload, cmdURL},
-	}
+	Children: []*cmdline2.Command{cmdDelete, cmdDownload, cmdUpload, cmdURL},
 }
diff --git a/services/binary/binary/impl_test.go b/services/binary/binary/impl_test.go
index 2b6c87d..deab3df 100644
--- a/services/binary/binary/impl_test.go
+++ b/services/binary/binary/impl_test.go
@@ -23,8 +23,9 @@
 	"v.io/v23/security/access"
 	"v.io/v23/services/binary"
 	"v.io/v23/services/repository"
+	"v.io/x/lib/cmdline2"
 	"v.io/x/lib/vlog"
-
+	"v.io/x/ref/lib/v23cmd"
 	_ "v.io/x/ref/profiles"
 	"v.io/x/ref/test"
 )
@@ -126,23 +127,21 @@
 }
 
 func TestBinaryClient(t *testing.T) {
-	var shutdown v23.Shutdown
-	gctx, shutdown = test.InitForTest()
+	ctx, shutdown := test.InitForTest()
 	defer shutdown()
 
-	server, endpoint, err := startServer(t, gctx)
+	server, endpoint, err := startServer(t, ctx)
 	if err != nil {
 		return
 	}
 	defer stopServer(t, server)
 
 	// Setup the command-line.
-	cmd := root()
 	var out bytes.Buffer
-	cmd.Init(nil, &out, &out)
+	env := &cmdline2.Env{Stdout: &out, Stderr: &out}
 
 	// Test the 'delete' command.
-	if err := cmd.Execute([]string{"delete", naming.JoinAddressName(endpoint.String(), "exists")}); err != nil {
+	if err := v23cmd.ParseAndRun(cmdRoot, ctx, env, []string{"delete", naming.JoinAddressName(endpoint.String(), "exists")}); err != nil {
 		t.Fatalf("%v failed: %v\n%v", "delete", err, out.String())
 	}
 	if expected, got := "Binary deleted successfully", strings.TrimSpace(out.String()); got != expected {
@@ -158,7 +157,7 @@
 	defer os.RemoveAll(dir)
 	file := path.Join(dir, "testfile")
 	defer os.Remove(file)
-	if err := cmd.Execute([]string{"download", naming.JoinAddressName(endpoint.String(), "exists"), file}); err != nil {
+	if err := v23cmd.ParseAndRun(cmdRoot, ctx, env, []string{"download", naming.JoinAddressName(endpoint.String(), "exists"), file}); err != nil {
 		t.Fatalf("%v failed: %v\n%v", "download", err, out.String())
 	}
 	if expected, got := "Binary downloaded to file "+file, strings.TrimSpace(out.String()); got != expected {
@@ -174,13 +173,13 @@
 	out.Reset()
 
 	// Test the 'upload' command.
-	if err := cmd.Execute([]string{"upload", naming.JoinAddressName(endpoint.String(), "exists"), file}); err != nil {
+	if err := v23cmd.ParseAndRun(cmdRoot, ctx, env, []string{"upload", naming.JoinAddressName(endpoint.String(), "exists"), file}); err != nil {
 		t.Fatalf("%v failed: %v\n%v", "upload", err, out.String())
 	}
 	out.Reset()
 
 	// Test the 'url' command.
-	if err := cmd.Execute([]string{"url", naming.JoinAddressName(endpoint.String(), "")}); err != nil {
+	if err := v23cmd.ParseAndRun(cmdRoot, ctx, env, []string{"url", naming.JoinAddressName(endpoint.String(), "")}); err != nil {
 		t.Fatalf("%v failed: %v\n%v", "url", err, out.String())
 	}
 	if expected, got := "test-download-url", strings.TrimSpace(out.String()); got != expected {
diff --git a/services/binary/binary/main.go b/services/binary/binary/main.go
deleted file mode 100644
index e33dc99..0000000
--- a/services/binary/binary/main.go
+++ /dev/null
@@ -1,27 +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 following enables go generate to generate the doc.go file.
-//go:generate go run $V23_ROOT/release/go/src/v.io/x/lib/cmdline/testdata/gendoc.go .
-
-package main
-
-import (
-	"os"
-
-	"v.io/v23"
-	"v.io/v23/context"
-
-	_ "v.io/x/ref/profiles"
-)
-
-var gctx *context.T
-
-func main() {
-	var shutdown v23.Shutdown
-	gctx, shutdown = v23.Init()
-	exitCode := root().Main()
-	shutdown()
-	os.Exit(exitCode)
-}
diff --git a/services/build/build/doc.go b/services/build/build/doc.go
index 20bda16..7e1a1c1 100644
--- a/services/build/build/doc.go
+++ b/services/build/build/doc.go
@@ -87,11 +87,6 @@
 
 "help ..." recursively displays help for all commands and topics.
 
-Output is formatted to a target width in runes, determined by checking the
-CMDLINE_WIDTH environment variable, falling back on the terminal width, falling
-back on 80 chars.  By setting CMDLINE_WIDTH=x, if x > 0 the width is x, if x < 0
-the width is unlimited, and if x == 0 or is unset one of the fallbacks is used.
-
 Usage:
    build help [flags] [command/topic ...]
 
@@ -104,5 +99,9 @@
       full    - Good for cmdline output, shows all global flags.
       godoc   - Good for godoc processing.
    Override the default by setting the CMDLINE_STYLE environment variable.
+ -width=<terminal width>
+   Format output to this target width in runes, or unlimited if width < 0.
+   Defaults to the terminal width if available.  Override the default by setting
+   the CMDLINE_WIDTH environment variable.
 */
 package main
diff --git a/services/build/build/impl.go b/services/build/build/impl.go
index f9b6e7f..b47f284 100644
--- a/services/build/build/impl.go
+++ b/services/build/build/impl.go
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// The following enables go generate to generate the doc.go file.
+//go:generate go run $V23_ROOT/release/go/src/v.io/x/lib/cmdline/testdata/gendoc.go .
+
 package main
 
 import (
@@ -16,40 +19,41 @@
 
 	"v.io/v23/context"
 	vbuild "v.io/v23/services/build"
-	"v.io/x/lib/cmdline"
+	"v.io/x/lib/cmdline2"
+	"v.io/x/ref/lib/v23cmd"
+	_ "v.io/x/ref/profiles"
 )
 
+func main() {
+	cmdline2.Main(cmdRoot)
+}
+
 var (
 	flagArch string
 	flagOS   string
 )
 
 func init() {
-	cmdline.HideGlobalFlagsExcept()
+	cmdline2.HideGlobalFlagsExcept()
 	cmdBuild.Flags.StringVar(&flagArch, "arch", runtime.GOARCH, "Target architecture.  The default is the value of runtime.GOARCH.")
 	cmdBuild.Flags.Lookup("arch").DefValue = "<runtime.GOARCH>"
 	cmdBuild.Flags.StringVar(&flagOS, "os", runtime.GOOS, "Target operating system.  The default is the value of runtime.GOOS.")
 	cmdBuild.Flags.Lookup("os").DefValue = "<runtime.GOOS>"
 }
 
-var cmdRoot = &cmdline.Command{
+var cmdRoot = &cmdline2.Command{
 	Name:  "build",
 	Short: "sends commands to a Vanadium build server",
 	Long: `
 Command build sends commands to a Vanadium build server.
 `,
-	Children: []*cmdline.Command{cmdBuild},
+	Children: []*cmdline2.Command{cmdBuild},
 }
 
-// root returns a command that represents the root of the vanadium tool.
-func root() *cmdline.Command {
-	return cmdRoot
-}
-
-var cmdBuild = &cmdline.Command{
-	Run:   runBuild,
-	Name:  "build",
-	Short: "Build vanadium Go packages",
+var cmdBuild = &cmdline2.Command{
+	Runner: v23cmd.RunnerFunc(runBuild),
+	Name:   "build",
+	Short:  "Build vanadium Go packages",
 	Long: `
 Build vanadium Go packages using a remote build server. The command collects all
 source code files that are not part of the Go standard library that the target
@@ -99,7 +103,7 @@
 			pkg := pkgMap[path]
 			fis, err := ioutil.ReadDir(pkg.Dir)
 			if err != nil {
-				return fmt.Errorf("ReadDir(%v) failed: %v", pkg.Dir)
+				return fmt.Errorf("ReadDir(%v) failed: %v", pkg.Dir, err)
 			}
 			for _, fi := range fis {
 				if fi.IsDir() {
@@ -222,7 +226,7 @@
 // concurrently 1) reads the source files, 2) sends them to the build
 // server and receives binaries from the build server, and 3) writes
 // the binaries out to the disk.
-func runBuild(command *cmdline.Command, args []string) error {
+func runBuild(ctx *context.T, env *cmdline2.Env, args []string) error {
 	name, paths := args[0], args[1:]
 	pkgMap := map[string]*build.Package{}
 	if err := importPackages(paths, pkgMap); err != nil {
@@ -231,7 +235,7 @@
 	errchan := make(chan error)
 	defer close(errchan)
 
-	ctx, ctxCancel := context.WithTimeout(gctx, time.Minute)
+	ctx, ctxCancel := context.WithTimeout(ctx, time.Minute)
 	defer ctxCancel()
 
 	// Start all stages of the pipeline.
diff --git a/services/build/build/impl_test.go b/services/build/build/impl_test.go
index a4a71ca..6f35178 100644
--- a/services/build/build/impl_test.go
+++ b/services/build/build/impl_test.go
@@ -16,8 +16,9 @@
 	"v.io/v23/services/binary"
 	"v.io/v23/services/build"
 	"v.io/v23/verror"
+	"v.io/x/lib/cmdline2"
 	"v.io/x/lib/vlog"
-
+	"v.io/x/ref/lib/v23cmd"
 	_ "v.io/x/ref/profiles"
 	"v.io/x/ref/test"
 )
@@ -69,22 +70,19 @@
 }
 
 func TestBuildClient(t *testing.T) {
-	var shutdown v23.Shutdown
-	gctx, shutdown = test.InitForTest()
+	ctx, shutdown := test.InitForTest()
 	defer shutdown()
 
-	server, endpoint := startServer(gctx, t)
+	server, endpoint := startServer(ctx, t)
 	defer stopServer(t, server)
 
-	cmd := root()
 	var stdout, stderr bytes.Buffer
-	cmd.Init(nil, &stdout, &stderr)
-
-	// Test the 'Build' command.
-	if err := cmd.Execute([]string{"build", naming.JoinAddressName(endpoint.String(), ""), "v.io/x/ref/services/build/build"}); err != nil {
-		t.Fatalf("%v", err)
+	env := &cmdline2.Env{Stdout: &stdout, Stderr: &stderr}
+	args := []string{"build", naming.JoinAddressName(endpoint.String(), ""), "v.io/x/ref/services/build/build"}
+	if err := v23cmd.ParseAndRun(cmdRoot, ctx, env, args); err != nil {
+		t.Fatalf("Run failed: %v", err)
 	}
-	if expected, got := "", strings.TrimSpace(stdout.String()); got != expected {
-		t.Errorf("Unexpected output from build: got %q, expected %q", got, expected)
+	if got, want := strings.TrimSpace(stdout.String()), ""; got != want {
+		t.Errorf("got %q, want %q", got, want)
 	}
 }
diff --git a/services/build/build/main.go b/services/build/build/main.go
deleted file mode 100644
index e33dc99..0000000
--- a/services/build/build/main.go
+++ /dev/null
@@ -1,27 +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 following enables go generate to generate the doc.go file.
-//go:generate go run $V23_ROOT/release/go/src/v.io/x/lib/cmdline/testdata/gendoc.go .
-
-package main
-
-import (
-	"os"
-
-	"v.io/v23"
-	"v.io/v23/context"
-
-	_ "v.io/x/ref/profiles"
-)
-
-var gctx *context.T
-
-func main() {
-	var shutdown v23.Shutdown
-	gctx, shutdown = v23.Init()
-	exitCode := root().Main()
-	shutdown()
-	os.Exit(exitCode)
-}
diff --git a/services/debug/debug/debug_v23_test.go b/services/debug/debug/debug_v23_test.go
index 5b0f047..c854906 100644
--- a/services/debug/debug/debug_v23_test.go
+++ b/services/debug/debug/debug_v23_test.go
@@ -108,7 +108,7 @@
 
 	want := fmt.Sprintf("Count: %d", runCount)
 	if !strings.Contains(got, want) {
-		i.Fatalf("expected output to contain %s, but did not\n", want, got)
+		i.Fatalf("expected output %q to contain %q, but did not\n", got, want)
 	}
 }
 
@@ -166,11 +166,11 @@
 	// Grab the ID of the first and only trace.
 	want, traceContent := 1, binary.Start("vtrace", "__debug/vtrace").Output()
 	if count := strings.Count(traceContent, "Trace -"); count != want {
-		i.Fatalf("unexpected trace count, want %d, got %d\n%s", want, count, traceContent)
+		i.Fatalf("unexpected trace count, want %d, got %d: %s", want, count, traceContent)
 	}
 	fields := strings.Split(traceContent, " ")
 	if len(fields) < 3 {
-		i.Fatalf("expected at least 3 space-delimited fields, got %d\n", len(fields), traceContent)
+		i.Fatalf("expected at least 3 space-delimited fields, got %d: %v", len(fields), traceContent)
 	}
 	traceId := fields[2]
 
@@ -196,7 +196,7 @@
 	}
 	fields = strings.Split(got, " ")
 	if len(fields) < 3 {
-		i.Fatalf("expected at least 3 space-delimited fields, got %d\n", len(fields), got)
+		i.Fatalf("expected at least 3 space-delimited fields, got %d: %v", len(fields), got)
 	}
 	got = fields[2]
 	if traceId != got {
diff --git a/services/debug/debug/doc.go b/services/debug/debug/doc.go
index ad9e01e..d291f34 100644
--- a/services/debug/debug/doc.go
+++ b/services/debug/debug/doc.go
@@ -208,11 +208,6 @@
 
 "help ..." recursively displays help for all commands and topics.
 
-Output is formatted to a target width in runes, determined by checking the
-CMDLINE_WIDTH environment variable, falling back on the terminal width, falling
-back on 80 chars.  By setting CMDLINE_WIDTH=x, if x > 0 the width is x, if x < 0
-the width is unlimited, and if x == 0 or is unset one of the fallbacks is used.
-
 Usage:
    debug help [flags] [command/topic ...]
 
@@ -225,5 +220,9 @@
       full    - Good for cmdline output, shows all global flags.
       godoc   - Good for godoc processing.
    Override the default by setting the CMDLINE_STYLE environment variable.
+ -width=<terminal width>
+   Format output to this target width in runes, or unlimited if width < 0.
+   Defaults to the terminal width if available.  Override the default by setting
+   the CMDLINE_WIDTH environment variable.
 */
 package main
diff --git a/services/debug/debug/impl.go b/services/debug/debug/impl.go
index 464faf9..b0b5793 100644
--- a/services/debug/debug/impl.go
+++ b/services/debug/debug/impl.go
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// The following enables go generate to generate the doc.go file.
+//go:generate go run $V23_ROOT/release/go/src/v.io/x/lib/cmdline/testdata/gendoc.go .
+
 package main
 
 import (
@@ -25,12 +28,18 @@
 	"v.io/v23/uniqueid"
 	"v.io/v23/vdl"
 	"v.io/v23/vtrace"
-	"v.io/x/lib/cmdline"
+	"v.io/x/lib/cmdline2"
 	"v.io/x/ref/lib/glob"
 	"v.io/x/ref/lib/signals"
+	"v.io/x/ref/lib/v23cmd"
+	_ "v.io/x/ref/profiles"
 	"v.io/x/ref/services/internal/pproflib"
 )
 
+func main() {
+	cmdline2.Main(cmdRoot)
+}
+
 var (
 	follow     bool
 	verbose    bool
@@ -42,7 +51,7 @@
 )
 
 func init() {
-	cmdline.HideGlobalFlagsExcept()
+	cmdline2.HideGlobalFlagsExcept()
 
 	// logs read flags
 	cmdLogsRead.Flags.BoolVar(&follow, "f", false, "When true, read will wait for new log entries when it reaches the end of the file.")
@@ -62,8 +71,8 @@
 	cmdPProfRun.Flags.StringVar(&pprofCmd, "pprofcmd", "v23 go tool pprof", "The pprof command to use.")
 }
 
-var cmdVtrace = &cmdline.Command{
-	Run:      runVtrace,
+var cmdVtrace = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runVtrace),
 	Name:     "vtrace",
 	Short:    "Returns vtrace traces.",
 	Long:     "Returns matching vtrace traces (or all stored traces if no ids are given).",
@@ -86,16 +95,16 @@
 	}
 }
 
-func runVtrace(cmd *cmdline.Command, args []string) error {
+func runVtrace(ctx *context.T, env *cmdline2.Env, args []string) error {
 	arglen := len(args)
 	if arglen == 0 {
-		return cmd.UsageErrorf("vtrace: incorrect number of arguments, got %d want >= 1", arglen)
+		return env.UsageErrorf("vtrace: incorrect number of arguments, got %d want >= 1", arglen)
 	}
 
 	name := args[0]
 	client := s_vtrace.StoreClient(name)
 	if arglen == 1 {
-		call, err := client.AllTraces(gctx)
+		call, err := client.AllTraces(ctx)
 		if err != nil {
 			return err
 		}
@@ -120,7 +129,7 @@
 		if err != nil {
 			return err
 		}
-		go doFetchTrace(gctx, &wg, client, id, traces, errors)
+		go doFetchTrace(ctx, &wg, client, id, traces, errors)
 	}
 	go func() {
 		wg.Wait()
@@ -136,8 +145,8 @@
 	return <-errors
 }
 
-var cmdGlob = &cmdline.Command{
-	Run:      runGlob,
+var cmdGlob = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runGlob),
 	Name:     "glob",
 	Short:    "Returns all matching entries from the namespace.",
 	Long:     "Returns all matching entries from the namespace.",
@@ -147,32 +156,32 @@
 `,
 }
 
-func runGlob(cmd *cmdline.Command, args []string) error {
+func runGlob(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if min, got := 1, len(args); got < min {
-		return cmd.UsageErrorf("glob: incorrect number of arguments, got %d, want >=%d", got, min)
+		return env.UsageErrorf("glob: incorrect number of arguments, got %d, want >=%d", got, min)
 	}
 	results := make(chan naming.GlobReply)
 	errors := make(chan error)
-	doGlobs(gctx, args, results, errors)
+	doGlobs(ctx, args, results, errors)
 	var lastErr error
 	for {
 		select {
 		case err := <-errors:
 			lastErr = err
-			fmt.Fprintln(cmd.Stderr(), "Error:", err)
+			fmt.Fprintln(env.Stderr, "Error:", err)
 		case me, ok := <-results:
 			if !ok {
 				return lastErr
 			}
 			switch v := me.(type) {
 			case *naming.GlobReplyEntry:
-				fmt.Fprint(cmd.Stdout(), v.Value.Name)
+				fmt.Fprint(env.Stdout, v.Value.Name)
 				for _, s := range v.Value.Servers {
-					fmt.Fprintf(cmd.Stdout(), " %s (Deadline %s)", s.Server, s.Deadline.Time)
+					fmt.Fprintf(env.Stdout, " %s (Deadline %s)", s.Server, s.Deadline.Time)
 				}
-				fmt.Fprintln(cmd.Stdout())
+				fmt.Fprintln(env.Stdout)
 			case *naming.GlobReplyError:
-				fmt.Fprintf(cmd.Stderr(), "Error: %s: %v\n", v.Value.Name, v.Value.Error)
+				fmt.Fprintf(env.Stderr, "Error: %s: %v\n", v.Value.Name, v.Value.Error)
 			}
 		}
 	}
@@ -207,8 +216,8 @@
 	}
 }
 
-var cmdLogsRead = &cmdline.Command{
-	Run:      runLogsRead,
+var cmdLogsRead = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runLogsRead),
 	Name:     "read",
 	Short:    "Reads the content of a log file object.",
 	Long:     "Reads the content of a log file object.",
@@ -218,13 +227,13 @@
 `,
 }
 
-func runLogsRead(cmd *cmdline.Command, args []string) error {
+func runLogsRead(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if want, got := 1, len(args); want != got {
-		return cmd.UsageErrorf("read: incorrect number of arguments, got %d, want %d", got, want)
+		return env.UsageErrorf("read: incorrect number of arguments, got %d, want %d", got, want)
 	}
 	name := args[0]
 	lf := logreader.LogFileClient(name)
-	stream, err := lf.ReadLog(gctx, startPos, int32(numEntries), follow)
+	stream, err := lf.ReadLog(ctx, startPos, int32(numEntries), follow)
 	if err != nil {
 		return err
 	}
@@ -232,9 +241,9 @@
 	for iterator.Advance() {
 		entry := iterator.Value()
 		if verbose {
-			fmt.Fprintf(cmd.Stdout(), "[%d] %s\n", entry.Position, entry.Line)
+			fmt.Fprintf(env.Stdout, "[%d] %s\n", entry.Position, entry.Line)
 		} else {
-			fmt.Fprintf(cmd.Stdout(), "%s\n", entry.Line)
+			fmt.Fprintf(env.Stdout, "%s\n", entry.Line)
 		}
 	}
 	if err = iterator.Err(); err != nil {
@@ -245,13 +254,13 @@
 		return err
 	}
 	if verbose {
-		fmt.Fprintf(cmd.Stdout(), "Offset: %d\n", offset)
+		fmt.Fprintf(env.Stdout, "Offset: %d\n", offset)
 	}
 	return nil
 }
 
-var cmdLogsSize = &cmdline.Command{
-	Run:      runLogsSize,
+var cmdLogsSize = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runLogsSize),
 	Name:     "size",
 	Short:    "Returns the size of a log file object.",
 	Long:     "Returns the size of a log file object.",
@@ -261,22 +270,22 @@
 `,
 }
 
-func runLogsSize(cmd *cmdline.Command, args []string) error {
+func runLogsSize(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if want, got := 1, len(args); want != got {
-		return cmd.UsageErrorf("size: incorrect number of arguments, got %d, want %d", got, want)
+		return env.UsageErrorf("size: incorrect number of arguments, got %d, want %d", got, want)
 	}
 	name := args[0]
 	lf := logreader.LogFileClient(name)
-	size, err := lf.Size(gctx)
+	size, err := lf.Size(ctx)
 	if err != nil {
 		return err
 	}
-	fmt.Fprintln(cmd.Stdout(), size)
+	fmt.Fprintln(env.Stdout, size)
 	return nil
 }
 
-var cmdStatsRead = &cmdline.Command{
-	Run:      runStatsRead,
+var cmdStatsRead = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runStatsRead),
 	Name:     "read",
 	Short:    "Returns the value of stats objects.",
 	Long:     "Returns the value of stats objects.",
@@ -287,13 +296,13 @@
 `,
 }
 
-func runStatsRead(cmd *cmdline.Command, args []string) error {
+func runStatsRead(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if min, got := 1, len(args); got < min {
-		return cmd.UsageErrorf("read: incorrect number of arguments, got %d, want >=%d", got, min)
+		return env.UsageErrorf("read: incorrect number of arguments, got %d, want >=%d", got, min)
 	}
 	globResults := make(chan naming.GlobReply)
 	errors := make(chan error)
-	doGlobs(gctx, args, globResults, errors)
+	doGlobs(ctx, args, globResults, errors)
 
 	output := make(chan string)
 	go func() {
@@ -302,7 +311,7 @@
 			switch v := me.(type) {
 			case *naming.GlobReplyEntry:
 				wg.Add(1)
-				go doValue(gctx, v.Value.Name, output, errors, &wg)
+				go doValue(ctx, v.Value.Name, output, errors, &wg)
 			}
 		}
 		wg.Wait()
@@ -314,12 +323,12 @@
 		select {
 		case err := <-errors:
 			lastErr = err
-			fmt.Fprintln(cmd.Stderr(), err)
+			fmt.Fprintln(env.Stderr, err)
 		case out, ok := <-output:
 			if !ok {
 				return lastErr
 			}
-			fmt.Fprintln(cmd.Stdout(), out)
+			fmt.Fprintln(env.Stdout, out)
 		}
 	}
 }
@@ -341,8 +350,8 @@
 	output <- fmt.Sprintf("%s: %v", name, fv)
 }
 
-var cmdStatsWatch = &cmdline.Command{
-	Run:      runStatsWatch,
+var cmdStatsWatch = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runStatsWatch),
 	Name:     "watch",
 	Short:    "Returns a stream of all matching entries and their values as they change.",
 	Long:     "Returns a stream of all matching entries and their values as they change.",
@@ -352,9 +361,9 @@
 `,
 }
 
-func runStatsWatch(cmd *cmdline.Command, args []string) error {
+func runStatsWatch(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if want, got := 1, len(args); got < want {
-		return cmd.UsageErrorf("watch: incorrect number of arguments, got %d, want >=%d", got, want)
+		return env.UsageErrorf("watch: incorrect number of arguments, got %d, want >=%d", got, want)
 	}
 
 	results := make(chan string)
@@ -362,7 +371,7 @@
 	var wg sync.WaitGroup
 	wg.Add(len(args))
 	for _, arg := range args {
-		go doWatch(gctx, arg, results, errors, &wg)
+		go doWatch(ctx, arg, results, errors, &wg)
 	}
 	go func() {
 		wg.Wait()
@@ -373,12 +382,12 @@
 		select {
 		case err := <-errors:
 			lastErr = err
-			fmt.Fprintln(cmd.Stderr(), "Error:", err)
+			fmt.Fprintln(env.Stderr, "Error:", err)
 		case r, ok := <-results:
 			if !ok {
 				return lastErr
 			}
-			fmt.Fprintln(cmd.Stdout(), r)
+			fmt.Fprintln(env.Stdout, r)
 		}
 	}
 }
@@ -447,8 +456,8 @@
 	return ret + fmt.Sprint(pretty), err
 }
 
-var cmdPProfRun = &cmdline.Command{
-	Run:      runPProf,
+var cmdPProfRun = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runPProf),
 	Name:     "run",
 	Short:    "Runs the pprof tool.",
 	Long:     "Runs the pprof tool.",
@@ -464,16 +473,16 @@
 `,
 }
 
-func runPProf(cmd *cmdline.Command, args []string) error {
+func runPProf(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if min, got := 1, len(args); got < min {
-		return cmd.UsageErrorf("pprof: incorrect number of arguments, got %d, want >=%d", got, min)
+		return env.UsageErrorf("pprof: incorrect number of arguments, got %d, want >=%d", got, min)
 	}
 	name := args[0]
 	if len(args) == 1 {
-		return showPProfProfiles(cmd, name)
+		return showPProfProfiles(ctx, env, name)
 	}
 	profile := args[1]
-	listener, err := pproflib.StartProxy(gctx, name)
+	listener, err := pproflib.StartProxy(ctx, name)
 	if err != nil {
 		return err
 	}
@@ -487,24 +496,24 @@
 	}
 	pargs = append(pargs, shellEscape(fmt.Sprintf("http://%s/pprof/%s", listener.Addr(), profile)))
 	pcmd := strings.Join(pargs, " ")
-	fmt.Fprintf(cmd.Stdout(), "Running: %s\n", pcmd)
+	fmt.Fprintf(env.Stdout, "Running: %s\n", pcmd)
 	c := exec.Command("sh", "-c", pcmd)
 	c.Stdin = os.Stdin
-	c.Stdout = cmd.Stdout()
-	c.Stderr = cmd.Stderr()
+	c.Stdout = env.Stdout
+	c.Stderr = env.Stderr
 	return c.Run()
 }
 
-func showPProfProfiles(cmd *cmdline.Command, name string) error {
-	v, err := pprof.PProfClient(name).Profiles(gctx)
+func showPProfProfiles(ctx *context.T, env *cmdline2.Env, name string) error {
+	v, err := pprof.PProfClient(name).Profiles(ctx)
 	if err != nil {
 		return err
 	}
 	v = append(v, "profile")
 	sort.Strings(v)
-	fmt.Fprintln(cmd.Stdout(), "Available profiles:")
+	fmt.Fprintln(env.Stdout, "Available profiles:")
 	for _, p := range v {
-		fmt.Fprintf(cmd.Stdout(), "  %s\n", p)
+		fmt.Fprintf(env.Stdout, "  %s\n", p)
 	}
 	return nil
 }
@@ -517,8 +526,8 @@
 	return `"` + re.ReplaceAllString(s, "\\$1") + `"`
 }
 
-var cmdPProfRunProxy = &cmdline.Command{
-	Run:      runPProfProxy,
+var cmdPProfRunProxy = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runPProfProxy),
 	Name:     "proxy",
 	Short:    "Runs an http proxy to a pprof object.",
 	Long:     "Runs an http proxy to a pprof object.",
@@ -528,54 +537,50 @@
 `,
 }
 
-func runPProfProxy(cmd *cmdline.Command, args []string) error {
+func runPProfProxy(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if want, got := 1, len(args); got != want {
-		return cmd.UsageErrorf("proxy: incorrect number of arguments, got %d, want %d", got, want)
+		return env.UsageErrorf("proxy: incorrect number of arguments, got %d, want %d", got, want)
 	}
 	name := args[0]
-	listener, err := pproflib.StartProxy(gctx, name)
+	listener, err := pproflib.StartProxy(ctx, name)
 	if err != nil {
 		return err
 	}
 	defer listener.Close()
 
-	fmt.Fprintln(cmd.Stdout())
-	fmt.Fprintf(cmd.Stdout(), "The pprof proxy is listening at http://%s/pprof\n", listener.Addr())
-	fmt.Fprintln(cmd.Stdout())
-	fmt.Fprintln(cmd.Stdout(), "Hit CTRL-C to exit")
+	fmt.Fprintln(env.Stdout)
+	fmt.Fprintf(env.Stdout, "The pprof proxy is listening at http://%s/pprof\n", listener.Addr())
+	fmt.Fprintln(env.Stdout)
+	fmt.Fprintln(env.Stdout, "Hit CTRL-C to exit")
 
-	<-signals.ShutdownOnSignals(gctx)
+	<-signals.ShutdownOnSignals(ctx)
 	return nil
 }
 
-var cmdRoot = cmdline.Command{
+var cmdRoot = &cmdline2.Command{
 	Name:  "debug",
 	Short: "supports debugging Vanadium servers.",
 	Long:  "Command debug supports debugging Vanadium servers.",
-	Children: []*cmdline.Command{
+	Children: []*cmdline2.Command{
 		cmdGlob,
 		cmdVtrace,
-		&cmdline.Command{
+		&cmdline2.Command{
 			Name:     "logs",
 			Short:    "Accesses log files",
 			Long:     "Accesses log files",
-			Children: []*cmdline.Command{cmdLogsRead, cmdLogsSize},
+			Children: []*cmdline2.Command{cmdLogsRead, cmdLogsSize},
 		},
-		&cmdline.Command{
+		&cmdline2.Command{
 			Name:     "stats",
 			Short:    "Accesses stats",
 			Long:     "Accesses stats",
-			Children: []*cmdline.Command{cmdStatsRead, cmdStatsWatch},
+			Children: []*cmdline2.Command{cmdStatsRead, cmdStatsWatch},
 		},
-		&cmdline.Command{
+		&cmdline2.Command{
 			Name:     "pprof",
 			Short:    "Accesses profiling data",
 			Long:     "Accesses profiling data",
-			Children: []*cmdline.Command{cmdPProfRun, cmdPProfRunProxy},
+			Children: []*cmdline2.Command{cmdPProfRun, cmdPProfRunProxy},
 		},
 	},
 }
-
-func root() *cmdline.Command {
-	return &cmdRoot
-}
diff --git a/services/debug/debug/main.go b/services/debug/debug/main.go
deleted file mode 100644
index e33dc99..0000000
--- a/services/debug/debug/main.go
+++ /dev/null
@@ -1,27 +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 following enables go generate to generate the doc.go file.
-//go:generate go run $V23_ROOT/release/go/src/v.io/x/lib/cmdline/testdata/gendoc.go .
-
-package main
-
-import (
-	"os"
-
-	"v.io/v23"
-	"v.io/v23/context"
-
-	_ "v.io/x/ref/profiles"
-)
-
-var gctx *context.T
-
-func main() {
-	var shutdown v23.Shutdown
-	gctx, shutdown = v23.Init()
-	exitCode := root().Main()
-	shutdown()
-	os.Exit(exitCode)
-}
diff --git a/services/device/device/acl_impl.go b/services/device/device/acl_impl.go
index 9e0c940..34b7a40 100644
--- a/services/device/device/acl_impl.go
+++ b/services/device/device/acl_impl.go
@@ -9,15 +9,17 @@
 import (
 	"fmt"
 
+	"v.io/v23/context"
 	"v.io/v23/security"
 	"v.io/v23/security/access"
 	"v.io/v23/services/device"
 	"v.io/v23/verror"
-	"v.io/x/lib/cmdline"
+	"v.io/x/lib/cmdline2"
+	"v.io/x/ref/lib/v23cmd"
 )
 
-var cmdGet = &cmdline.Command{
-	Run:      runGet,
+var cmdGet = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runGet),
 	Name:     "get",
 	Short:    "Get Permissions for the given target.",
 	Long:     "Get Permissions for the given target.",
@@ -27,13 +29,13 @@
 application installation or instance.`,
 }
 
-func runGet(cmd *cmdline.Command, args []string) error {
+func runGet(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, got := 1, len(args); expected != got {
-		return cmd.UsageErrorf("get: incorrect number of arguments, expected %d, got %d", expected, got)
+		return env.UsageErrorf("get: incorrect number of arguments, expected %d, got %d", expected, got)
 	}
 
 	vanaName := args[0]
-	objPerms, _, err := device.ApplicationClient(vanaName).GetPermissions(gctx)
+	objPerms, _, err := device.ApplicationClient(vanaName).GetPermissions(ctx)
 	if err != nil {
 		return fmt.Errorf("GetPermissions on %s failed: %v", vanaName, err)
 	}
@@ -47,15 +49,15 @@
 			entries.Tags(b)[tag] = true
 		}
 	}
-	fmt.Fprintln(cmd.Stdout(), entries)
+	fmt.Fprintln(env.Stdout, entries)
 	return nil
 }
 
 // TODO(caprita): Add unit test logic for 'force set'.
 var forceSet bool
 
-var cmdSet = &cmdline.Command{
-	Run:      runSet,
+var cmdSet = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runSet),
 	Name:     "set",
 	Short:    "Set Permissions for the given target.",
 	Long:     "Set Permissions for the given target",
@@ -87,9 +89,9 @@
 	cmdSet.Flags.BoolVar(&forceSet, "f", false, "Instead of making the AccessLists additive, do a complete replacement based on the specified settings.")
 }
 
-func runSet(cmd *cmdline.Command, args []string) error {
+func runSet(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if got := len(args); !((got%2) == 1 && got >= 3) {
-		return cmd.UsageErrorf("set: incorrect number of arguments %d, must be 1 + 2n", got)
+		return env.UsageErrorf("set: incorrect number of arguments %d, must be 1 + 2n", got)
 	}
 
 	vanaName := args[0]
@@ -100,7 +102,7 @@
 		blessing := pairs[i]
 		tags, err := parseAccessTags(pairs[i+1])
 		if err != nil {
-			return cmd.UsageErrorf("failed to parse access tags for %q: %v", blessing, err)
+			return env.UsageErrorf("failed to parse access tags for %q: %v", blessing, err)
 		}
 		entries[blessing] = tags
 	}
@@ -110,7 +112,7 @@
 		objPerms, version := make(access.Permissions), ""
 		if !forceSet {
 			var err error
-			if objPerms, version, err = device.ApplicationClient(vanaName).GetPermissions(gctx); err != nil {
+			if objPerms, version, err = device.ApplicationClient(vanaName).GetPermissions(ctx); err != nil {
 				return fmt.Errorf("GetPermissions(%s) failed: %v", vanaName, err)
 			}
 		}
@@ -124,24 +126,21 @@
 				}
 			}
 		}
-		switch err := device.ApplicationClient(vanaName).SetPermissions(gctx, objPerms, version); {
+		switch err := device.ApplicationClient(vanaName).SetPermissions(ctx, objPerms, version); {
 		case err != nil && verror.ErrorID(err) != verror.ErrBadVersion.ID:
 			return fmt.Errorf("SetPermissions(%s) failed: %v", vanaName, err)
 		case err == nil:
 			return nil
 		}
-		fmt.Fprintln(cmd.Stderr(), "WARNING: trying again because of asynchronous change")
+		fmt.Fprintln(env.Stderr, "WARNING: trying again because of asynchronous change")
 	}
-	return nil
 }
 
-func aclRoot() *cmdline.Command {
-	return &cmdline.Command{
-		Name:  "acl",
-		Short: "Tool for setting device manager Permissions",
-		Long: `
+var cmdACL = &cmdline2.Command{
+	Name:  "acl",
+	Short: "Tool for setting device manager Permissions",
+	Long: `
 The acl tool manages Permissions on the device manger, installations and instances.
 `,
-		Children: []*cmdline.Command{cmdGet, cmdSet},
-	}
+	Children: []*cmdline2.Command{cmdGet, cmdSet},
 }
diff --git a/services/device/device/acl_test.go b/services/device/device/acl_test.go
index bf51feb..3cc5988 100644
--- a/services/device/device/acl_test.go
+++ b/services/device/device/acl_test.go
@@ -14,6 +14,9 @@
 	"v.io/v23/security"
 	"v.io/v23/security/access"
 	"v.io/v23/verror"
+	"v.io/x/lib/cmdline2"
+	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/test"
 
 	cmd_device "v.io/x/ref/services/device/device"
 )
@@ -25,20 +28,20 @@
 )
 
 func TestAccessListGetCommand(t *testing.T) {
-	shutdown := initTest()
+	ctx, shutdown := test.InitForTest()
 	defer shutdown()
 
 	tapes := newTapeMap()
-	server, endpoint, err := startServer(t, gctx, tapes)
+	server, endpoint, err := startServer(t, ctx, tapes)
 	if err != nil {
 		return
 	}
 	defer stopServer(t, server)
 
 	// Setup the command-line.
-	cmd := cmd_device.Root()
+	cmd := cmd_device.CmdRoot
 	var stdout, stderr bytes.Buffer
-	cmd.Init(nil, &stdout, &stderr)
+	env := &cmdline2.Env{Stdout: &stdout, Stderr: &stderr}
 	deviceName := endpoint.Name()
 
 	// Test the 'get' command.
@@ -57,7 +60,7 @@
 		err:     nil,
 	}})
 
-	if err := cmd.Execute([]string{"acl", "get", deviceName}); err != nil {
+	if err := v23cmd.ParseAndRun(cmd, ctx, env, []string{"acl", "get", deviceName}); err != nil {
 		t.Fatalf("error: %v", err)
 	}
 	if expected, got := strings.TrimSpace(`
@@ -73,24 +76,24 @@
 }
 
 func TestAccessListSetCommand(t *testing.T) {
-	shutdown := initTest()
+	ctx, shutdown := test.InitForTest()
 	defer shutdown()
 
 	tapes := newTapeMap()
-	server, endpoint, err := startServer(t, gctx, tapes)
+	server, endpoint, err := startServer(t, ctx, tapes)
 	if err != nil {
 		return
 	}
 	defer stopServer(t, server)
 
 	// Setup the command-line.
-	cmd := cmd_device.Root()
+	cmd := cmd_device.CmdRoot
 	var stdout, stderr bytes.Buffer
-	cmd.Init(nil, &stdout, &stderr)
+	env := &cmdline2.Env{Stdout: &stdout, Stderr: &stderr}
 	deviceName := endpoint.Name()
 
 	// Some tests to validate parse.
-	if err := cmd.Execute([]string{"acl", "set", deviceName}); err == nil {
+	if err := v23cmd.ParseAndRun(cmd, ctx, env, []string{"acl", "set", deviceName}); err == nil {
 		t.Fatalf("failed to correctly detect insufficient parameters")
 	}
 	if expected, got := "ERROR: set: incorrect number of arguments 1, must be 1 + 2n", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
@@ -99,7 +102,7 @@
 
 	stderr.Reset()
 	stdout.Reset()
-	if err := cmd.Execute([]string{"acl", "set", deviceName, "foo"}); err == nil {
+	if err := v23cmd.ParseAndRun(cmd, ctx, env, []string{"acl", "set", deviceName, "foo"}); err == nil {
 		t.Fatalf("failed to correctly detect insufficient parameters")
 	}
 	if expected, got := "ERROR: set: incorrect number of arguments 2, must be 1 + 2n", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
@@ -108,7 +111,7 @@
 
 	stderr.Reset()
 	stdout.Reset()
-	if err := cmd.Execute([]string{"acl", "set", deviceName, "foo", "bar", "ohno"}); err == nil {
+	if err := v23cmd.ParseAndRun(cmd, ctx, env, []string{"acl", "set", deviceName, "foo", "bar", "ohno"}); err == nil {
 		t.Fatalf("failed to correctly detect insufficient parameters")
 	}
 	if expected, got := "ERROR: set: incorrect number of arguments 4, must be 1 + 2n", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
@@ -117,7 +120,7 @@
 
 	stderr.Reset()
 	stdout.Reset()
-	if err := cmd.Execute([]string{"acl", "set", deviceName, "foo", "!"}); err == nil {
+	if err := v23cmd.ParseAndRun(cmd, ctx, env, []string{"acl", "set", deviceName, "foo", "!"}); err == nil {
 		t.Fatalf("failed to detect invalid parameter")
 	}
 	if expected, got := "ERROR: failed to parse access tags for \"foo\": empty access tag", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
@@ -163,7 +166,7 @@
 	// - Adds a blacklist entry for "friend/alice"  for "Admin"
 	// - Edits existing entry for "self" (adding "Write" access)
 	// - Removes entry for "other/bob/baddevice"
-	if err := cmd.Execute([]string{
+	if err := v23cmd.ParseAndRun(cmd, ctx, env, []string{
 		"acl",
 		"set",
 		deviceName,
@@ -237,7 +240,7 @@
 	},
 	})
 
-	if err := cmd.Execute([]string{"acl", "set", deviceName, "vana/bad", "Read"}); err == nil {
+	if err := v23cmd.ParseAndRun(cmd, ctx, env, []string{"acl", "set", deviceName, "vana/bad", "Read"}); err == nil {
 		t.Fatalf("GetPermissions RPC inside perms set command failed but error wrongly not detected")
 	} else if expected, got := `^GetPermissions\(`+deviceName+`\) failed:.*oops!`, err.Error(); !regexp.MustCompile(expected).MatchString(got) {
 		t.Fatalf("Unexpected output from list. Got %q, regexp %q", got, expected)
@@ -269,7 +272,7 @@
 		verror.New(errOops, nil),
 	})
 
-	if err := cmd.Execute([]string{"acl", "set", deviceName, "friend", "Read"}); err == nil {
+	if err := v23cmd.ParseAndRun(cmd, ctx, env, []string{"acl", "set", deviceName, "friend", "Read"}); err == nil {
 		t.Fatalf("SetPermissions should have failed: %v", err)
 	} else if expected, got := `^SetPermissions\(`+deviceName+`\) failed:.*oops!`, err.Error(); !regexp.MustCompile(expected).MatchString(got) {
 		t.Fatalf("Unexpected output from list. Got %q, regexp %q", got, expected)
diff --git a/services/device/device/associate_impl.go b/services/device/device/associate_impl.go
index 45a8a0c..6c1e634 100644
--- a/services/device/device/associate_impl.go
+++ b/services/device/device/associate_impl.go
@@ -10,11 +10,12 @@
 
 	"v.io/v23/context"
 	"v.io/v23/services/device"
-	"v.io/x/lib/cmdline"
+	"v.io/x/lib/cmdline2"
+	"v.io/x/ref/lib/v23cmd"
 )
 
-var cmdList = &cmdline.Command{
-	Run:      runList,
+var cmdList = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runList),
 	Name:     "list",
 	Short:    "Lists the account associations.",
 	Long:     "Lists all account associations.",
@@ -23,12 +24,12 @@
 <devicemanager> is the name of the device manager to connect to.`,
 }
 
-func runList(cmd *cmdline.Command, args []string) error {
+func runList(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, got := 1, len(args); expected != got {
-		return cmd.UsageErrorf("list: incorrect number of arguments, expected %d, got %d", expected, got)
+		return env.UsageErrorf("list: incorrect number of arguments, expected %d, got %d", expected, got)
 	}
 
-	ctx, cancel := context.WithTimeout(gctx, time.Minute)
+	ctx, cancel := context.WithTimeout(ctx, time.Minute)
 	defer cancel()
 	assocs, err := device.DeviceClient(args[0]).ListAssociations(ctx)
 	if err != nil {
@@ -36,13 +37,13 @@
 	}
 
 	for _, a := range assocs {
-		fmt.Fprintf(cmd.Stdout(), "%s %s\n", a.IdentityName, a.AccountName)
+		fmt.Fprintf(env.Stdout, "%s %s\n", a.IdentityName, a.AccountName)
 	}
 	return nil
 }
 
-var cmdAdd = &cmdline.Command{
-	Run:      runAdd,
+var cmdAdd = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runAdd),
 	Name:     "add",
 	Short:    "Add the listed blessings with the specified system account.",
 	Long:     "Add the listed blessings with the specified system account.",
@@ -53,17 +54,17 @@
 <blessing>.. are the blessings to associate systemAccount with.`,
 }
 
-func runAdd(cmd *cmdline.Command, args []string) error {
+func runAdd(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, got := 3, len(args); got < expected {
-		return cmd.UsageErrorf("add: incorrect number of arguments, expected at least %d, got %d", expected, got)
+		return env.UsageErrorf("add: incorrect number of arguments, expected at least %d, got %d", expected, got)
 	}
-	ctx, cancel := context.WithTimeout(gctx, time.Minute)
+	ctx, cancel := context.WithTimeout(ctx, time.Minute)
 	defer cancel()
 	return device.DeviceClient(args[0]).AssociateAccount(ctx, args[2:], args[1])
 }
 
-var cmdRemove = &cmdline.Command{
-	Run:      runRemove,
+var cmdRemove = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runRemove),
 	Name:     "remove",
 	Short:    "Removes system accounts associated with the listed blessings.",
 	Long:     "Removes system accounts associated with the listed blessings.",
@@ -73,22 +74,20 @@
 <blessing>... is a list of blessings.`,
 }
 
-func runRemove(cmd *cmdline.Command, args []string) error {
+func runRemove(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, got := 2, len(args); got < expected {
-		return cmd.UsageErrorf("remove: incorrect number of arguments, expected at least %d, got %d", expected, got)
+		return env.UsageErrorf("remove: incorrect number of arguments, expected at least %d, got %d", expected, got)
 	}
-	ctx, cancel := context.WithTimeout(gctx, time.Minute)
+	ctx, cancel := context.WithTimeout(ctx, time.Minute)
 	defer cancel()
 	return device.DeviceClient(args[0]).AssociateAccount(ctx, args[1:], "")
 }
 
-func associateRoot() *cmdline.Command {
-	return &cmdline.Command{
-		Name:  "associate",
-		Short: "Tool for creating associations between Vanadium blessings and a system account",
-		Long: `
+var cmdAssociate = &cmdline2.Command{
+	Name:  "associate",
+	Short: "Tool for creating associations between Vanadium blessings and a system account",
+	Long: `
 The associate tool facilitates managing blessing to system account associations.
 `,
-		Children: []*cmdline.Command{cmdList, cmdAdd, cmdRemove},
-	}
+	Children: []*cmdline2.Command{cmdList, cmdAdd, cmdRemove},
 }
diff --git a/services/device/device/doc.go b/services/device/device/doc.go
index 732c29c..8a01b17 100644
--- a/services/device/device/doc.go
+++ b/services/device/device/doc.go
@@ -379,13 +379,14 @@
 Device publish
 
 Publishes the given application(s) to the binary and application servers. The
-binaries should be in $V23_ROOT/release/go/bin/[<GOOS>_<GOARCH>]. The binary is
-published as <binserv>/<binary name>/<GOOS>-<GOARCH>/<TIMESTAMP>. The
+<title> can be optionally specified with @<title> (defaults to the binary name).
+The binaries should be in $V23_ROOT/release/go/bin/[<GOOS>_<GOARCH>]. The binary
+is published as <binserv>/<binary name>/<GOOS>-<GOARCH>/<TIMESTAMP>. The
 application envelope is published as <appserv>/<binary name>/0. Optionally, adds
 blessing patterns to the Read and Resolve AccessLists.
 
 Usage:
-   device publish [flags] <binary name> ...
+   device publish [flags] <binary name>[@<title>] ...
 
 The device publish flags are:
  -appserv=applications
diff --git a/services/device/device/impl.go b/services/device/device/impl.go
index f6be88a..6ebcc63 100644
--- a/services/device/device/impl.go
+++ b/services/device/device/impl.go
@@ -17,7 +17,8 @@
 	"v.io/v23/security"
 	"v.io/v23/services/application"
 	"v.io/v23/services/device"
-	"v.io/x/lib/cmdline"
+	"v.io/x/lib/cmdline2"
+	"v.io/x/ref/lib/v23cmd"
 )
 
 type configFlag device.Config
@@ -55,8 +56,8 @@
 	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{
-	Run:      runInstall,
+var cmdInstall = &cmdline2.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.",
@@ -68,12 +69,12 @@
 `,
 }
 
-func runInstall(cmd *cmdline.Command, args []string) error {
+func runInstall(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, got := 2, len(args); expected != got {
-		return cmd.UsageErrorf("install: incorrect number of arguments, expected %d, got %d", 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(gctx, appName, device.Config(configOverride), application.Packages(packagesOverride))
+	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).
@@ -82,12 +83,12 @@
 	if err != nil {
 		return fmt.Errorf("Install failed: %v", err)
 	}
-	fmt.Fprintf(cmd.Stdout(), "%s\n", naming.Join(deviceName, appID))
+	fmt.Fprintf(env.Stdout, "%s\n", naming.Join(deviceName, appID))
 	return nil
 }
 
-var cmdUninstall = &cmdline.Command{
-	Run:      runUninstall,
+var cmdUninstall = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runUninstall),
 	Name:     "uninstall",
 	Short:    "Uninstall the given application installation.",
 	Long:     "Uninstall the given application installation.",
@@ -98,20 +99,20 @@
 `,
 }
 
-func runUninstall(cmd *cmdline.Command, args []string) error {
+func runUninstall(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, got := 1, len(args); expected != got {
-		return cmd.UsageErrorf("uninstall: incorrect number of arguments, expected %d, got %d", 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(gctx); err != nil {
+	if err := device.ApplicationClient(installName).Uninstall(ctx); err != nil {
 		return fmt.Errorf("Uninstall failed: %v", err)
 	}
-	fmt.Fprintf(cmd.Stdout(), "Successfully uninstalled: %q\n", installName)
+	fmt.Fprintf(env.Stdout, "Successfully uninstalled: %q\n", installName)
 	return nil
 }
 
-var cmdInstantiate = &cmdline.Command{
-	Run:      runInstantiate,
+var cmdInstantiate = &cmdline2.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.",
@@ -134,13 +135,13 @@
 	return p.Bless(call.RemoteBlessings().PublicKey(), p.BlessingStore().Default(), g.extension, security.UnconstrainedUse())
 }
 
-func runInstantiate(cmd *cmdline.Command, args []string) error {
+func runInstantiate(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, got := 2, len(args); expected != got {
-		return cmd.UsageErrorf("instantiate: incorrect number of arguments, expected %d, got %d", 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(gctx)
+	ctx, cancel := context.WithCancel(ctx)
 	defer cancel()
 	principal := v23.GetPrincipal(ctx)
 
@@ -162,19 +163,19 @@
 			}
 			call.SendStream().Send(device.BlessClientMessageAppBlessings{blessings})
 		default:
-			fmt.Fprintf(cmd.Stderr(), "Received unexpected message: %#v\n", msg)
+			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(cmd.Stdout(), "%s\n", naming.Join(appInstallation, instanceID))
+	fmt.Fprintf(env.Stdout, "%s\n", naming.Join(appInstallation, instanceID))
 	return nil
 }
 
-var cmdClaim = &cmdline.Command{
-	Run:      runClaim,
+var cmdClaim = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runClaim),
 	Name:     "claim",
 	Short:    "Claim the device.",
 	Long:     "Claim the device.",
@@ -192,9 +193,9 @@
 are claiming.`,
 }
 
-func runClaim(cmd *cmdline.Command, args []string) error {
+func runClaim(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, max, got := 2, 4, len(args); expected > got || got > max {
-		return cmd.UsageErrorf("claim: incorrect number of arguments, expected atleast %d (max: %d), got %d", expected, max, got)
+		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
@@ -215,15 +216,15 @@
 	}
 	// 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(gctx, pairingToken, &granter{extension: grant}, serverKeyOpts, options.SkipServerEndpointAuthorization{}); err != nil {
+	if err := device.ClaimableClient(deviceName).Claim(ctx, pairingToken, &granter{extension: grant}, serverKeyOpts, options.SkipServerEndpointAuthorization{}); err != nil {
 		return err
 	}
-	fmt.Fprintln(cmd.Stdout(), "Successfully claimed.")
+	fmt.Fprintln(env.Stdout, "Successfully claimed.")
 	return nil
 }
 
-var cmdDescribe = &cmdline.Command{
-	Run:      runDescribe,
+var cmdDescribe = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runDescribe),
 	Name:     "describe",
 	Short:    "Describe the device.",
 	Long:     "Describe the device.",
@@ -232,21 +233,21 @@
 <device> is the vanadium object name of the device manager's device service.`,
 }
 
-func runDescribe(cmd *cmdline.Command, args []string) error {
+func runDescribe(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, got := 1, len(args); expected != got {
-		return cmd.UsageErrorf("describe: incorrect number of arguments, expected %d, got %d", 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(gctx); err != nil {
+	if description, err := device.DeviceClient(deviceName).Describe(ctx); err != nil {
 		return fmt.Errorf("Describe failed: %v", err)
 	} else {
-		fmt.Fprintf(cmd.Stdout(), "%+v\n", description)
+		fmt.Fprintf(env.Stdout, "%+v\n", description)
 	}
 	return nil
 }
 
-var cmdUpdate = &cmdline.Command{
-	Run:      runUpdate,
+var cmdUpdate = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runUpdate),
 	Name:     "update",
 	Short:    "Update the device manager or application",
 	Long:     "Update the device manager or application",
@@ -256,20 +257,20 @@
 installation or instance to update.`,
 }
 
-func runUpdate(cmd *cmdline.Command, args []string) error {
+func runUpdate(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, got := 1, len(args); expected != got {
-		return cmd.UsageErrorf("update: incorrect number of arguments, expected %d, got %d", 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(gctx); err != nil {
+	if err := device.ApplicationClient(name).Update(ctx); err != nil {
 		return err
 	}
-	fmt.Fprintln(cmd.Stdout(), "Update successful.")
+	fmt.Fprintln(env.Stdout, "Update successful.")
 	return nil
 }
 
-var cmdRevert = &cmdline.Command{
-	Run:      runRevert,
+var cmdRevert = &cmdline2.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",
@@ -279,20 +280,20 @@
 installation to revert.`,
 }
 
-func runRevert(cmd *cmdline.Command, args []string) error {
+func runRevert(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, got := 1, len(args); expected != got {
-		return cmd.UsageErrorf("revert: incorrect number of arguments, expected %d, got %d", 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(gctx); err != nil {
+	if err := device.ApplicationClient(deviceName).Revert(ctx); err != nil {
 		return err
 	}
-	fmt.Fprintln(cmd.Stdout(), "Revert successful.")
+	fmt.Fprintln(env.Stdout, "Revert successful.")
 	return nil
 }
 
-var cmdDebug = &cmdline.Command{
-	Run:      runDebug,
+var cmdDebug = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runDebug),
 	Name:     "debug",
 	Short:    "Debug the device.",
 	Long:     "Debug the device.",
@@ -301,21 +302,21 @@
 <app name> is the vanadium object name of an app installation or instance.`,
 }
 
-func runDebug(cmd *cmdline.Command, args []string) error {
+func runDebug(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, got := 1, len(args); expected != got {
-		return cmd.UsageErrorf("debug: incorrect number of arguments, expected %d, got %d", 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(gctx); err != nil {
+	if description, err := device.DeviceClient(appName).Debug(ctx); err != nil {
 		return fmt.Errorf("Debug failed: %v", err)
 	} else {
-		fmt.Fprintf(cmd.Stdout(), "%v\n", description)
+		fmt.Fprintf(env.Stdout, "%v\n", description)
 	}
 	return nil
 }
 
-var cmdStatus = &cmdline.Command{
-	Run:      runStatus,
+var cmdStatus = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runStatus),
 	Name:     "status",
 	Short:    "Get application status.",
 	Long:     "Get the status of an application installation or instance.",
@@ -324,20 +325,20 @@
 <app name> is the vanadium object name of an app installation or instance.`,
 }
 
-func runStatus(cmd *cmdline.Command, args []string) error {
+func runStatus(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, got := 1, len(args); expected != got {
-		return cmd.UsageErrorf("status: incorrect number of arguments, expected %d, got %d", expected, got)
+		return env.UsageErrorf("status: incorrect number of arguments, expected %d, got %d", expected, got)
 	}
 	appName := args[0]
-	status, err := device.DeviceClient(appName).Status(gctx)
+	status, err := device.DeviceClient(appName).Status(ctx)
 	if err != nil {
 		return fmt.Errorf("Status failed: %v", err)
 	}
 	switch s := status.(type) {
 	case device.StatusInstance:
-		fmt.Fprintf(cmd.Stdout(), "Instance [State:%v,Version:%v]\n", s.Value.State, s.Value.Version)
+		fmt.Fprintf(env.Stdout, "Instance [State:%v,Version:%v]\n", s.Value.State, s.Value.Version)
 	case device.StatusInstallation:
-		fmt.Fprintf(cmd.Stdout(), "Installation [State:%v,Version:%v]\n", s.Value.State, s.Value.Version)
+		fmt.Fprintf(env.Stdout, "Installation [State:%v,Version:%v]\n", s.Value.State, s.Value.Version)
 	default:
 		return fmt.Errorf("Status returned unknown type: %T", s)
 	}
diff --git a/services/device/device/impl_test.go b/services/device/device/impl_test.go
index 9ac0b1d..ddd7f3b 100644
--- a/services/device/device/impl_test.go
+++ b/services/device/device/impl_test.go
@@ -18,27 +18,31 @@
 	"v.io/v23/services/application"
 	"v.io/v23/services/device"
 	"v.io/v23/verror"
+	"v.io/x/lib/cmdline2"
 	"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) {
-	shutdown := initTest()
+	ctx, shutdown := test.InitForTest()
 	defer shutdown()
 
 	tapes := newTapeMap()
-	server, endpoint, err := startServer(t, gctx, tapes)
+	server, endpoint, err := startServer(t, ctx, tapes)
 	if err != nil {
 		return
 	}
 	defer stopServer(t, server)
 
 	// Setup the command-line.
-	cmd := cmd_device.Root()
+	cmd := cmd_device.CmdRoot
 	var stdout, stderr bytes.Buffer
-	cmd.Init(nil, &stdout, &stderr)
+	env := &cmdline2.Env{Stdout: &stdout, Stderr: &stderr}
 	deviceName := naming.JoinAddressName(endpoint.String(), "")
 
 	rootTape := tapes.forSuffix("")
@@ -57,7 +61,7 @@
 		err: nil,
 	}})
 
-	if err := cmd.Execute([]string{"associate", "list", deviceName}); err != nil {
+	if err := v23cmd.ParseAndRun(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 {
@@ -70,7 +74,7 @@
 	stdout.Reset()
 
 	// Test list with bad parameters.
-	if err := cmd.Execute([]string{"associate", "list", deviceName, "hello"}); err == nil {
+	if err := v23cmd.ParseAndRun(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 {
@@ -79,23 +83,23 @@
 }
 
 func TestAddCommand(t *testing.T) {
-	shutdown := initTest()
+	ctx, shutdown := test.InitForTest()
 	defer shutdown()
 
 	tapes := newTapeMap()
-	server, endpoint, err := startServer(t, gctx, tapes)
+	server, endpoint, err := startServer(t, ctx, tapes)
 	if err != nil {
 		return
 	}
 	defer stopServer(t, server)
 
 	// Setup the command-line.
-	cmd := cmd_device.Root()
+	cmd := cmd_device.CmdRoot
 	var stdout, stderr bytes.Buffer
-	cmd.Init(nil, &stdout, &stderr)
+	env := &cmdline2.Env{Stdout: &stdout, Stderr: &stderr}
 	deviceName := naming.JoinAddressName(endpoint.String(), "")
 
-	if err := cmd.Execute([]string{"add", "one"}); err == nil {
+	if err := v23cmd.ParseAndRun(cmd, ctx, env, []string{"add", "one"}); err == nil {
 		t.Fatalf("wrongly failed to receive a non-nil error.")
 	}
 	rootTape := tapes.forSuffix("")
@@ -106,7 +110,7 @@
 	stdout.Reset()
 
 	rootTape.SetResponses([]interface{}{nil})
-	if err := cmd.Execute([]string{"associate", "add", deviceName, "alice", "root/self"}); err != nil {
+	if err := v23cmd.ParseAndRun(cmd, ctx, env, []string{"associate", "add", deviceName, "alice", "root/self"}); err != nil {
 		t.Fatalf("%v", err)
 	}
 	expected := []interface{}{
@@ -119,7 +123,7 @@
 	stdout.Reset()
 
 	rootTape.SetResponses([]interface{}{nil})
-	if err := cmd.Execute([]string{"associate", "add", deviceName, "alice", "root/other", "root/self"}); err != nil {
+	if err := v23cmd.ParseAndRun(cmd, ctx, env, []string{"associate", "add", deviceName, "alice", "root/other", "root/self"}); err != nil {
 		t.Fatalf("%v", err)
 	}
 	expected = []interface{}{
@@ -131,23 +135,23 @@
 }
 
 func TestRemoveCommand(t *testing.T) {
-	shutdown := initTest()
+	ctx, shutdown := test.InitForTest()
 	defer shutdown()
 
 	tapes := newTapeMap()
-	server, endpoint, err := startServer(t, gctx, tapes)
+	server, endpoint, err := startServer(t, ctx, tapes)
 	if err != nil {
 		return
 	}
 	defer stopServer(t, server)
 
 	// Setup the command-line.
-	cmd := cmd_device.Root()
+	cmd := cmd_device.CmdRoot
 	var stdout, stderr bytes.Buffer
-	cmd.Init(nil, &stdout, &stderr)
+	env := &cmdline2.Env{Stdout: &stdout, Stderr: &stderr}
 	deviceName := naming.JoinAddressName(endpoint.String(), "")
 
-	if err := cmd.Execute([]string{"remove", "one"}); err == nil {
+	if err := v23cmd.ParseAndRun(cmd, ctx, env, []string{"remove", "one"}); err == nil {
 		t.Fatalf("wrongly failed to receive a non-nil error.")
 	}
 	rootTape := tapes.forSuffix("")
@@ -158,7 +162,7 @@
 	stdout.Reset()
 
 	rootTape.SetResponses([]interface{}{nil})
-	if err := cmd.Execute([]string{"associate", "remove", deviceName, "root/self"}); err != nil {
+	if err := v23cmd.ParseAndRun(cmd, ctx, env, []string{"associate", "remove", deviceName, "root/self"}); err != nil {
 		t.Fatalf("%v", err)
 	}
 	expected := []interface{}{
@@ -170,20 +174,20 @@
 }
 
 func TestInstallCommand(t *testing.T) {
-	shutdown := initTest()
+	ctx, shutdown := test.InitForTest()
 	defer shutdown()
 
 	tapes := newTapeMap()
-	server, endpoint, err := startServer(t, gctx, tapes)
+	server, endpoint, err := startServer(t, ctx, tapes)
 	if err != nil {
 		return
 	}
 	defer stopServer(t, server)
 
 	// Setup the command-line.
-	cmd := cmd_device.Root()
+	cmd := cmd_device.CmdRoot
 	var stdout, stderr bytes.Buffer
-	cmd.Init(nil, &stdout, &stderr)
+	env := &cmdline2.Env{Stdout: &stdout, Stderr: &stderr}
 	deviceName := naming.JoinAddressName(endpoint.String(), "")
 	appId := "myBestAppID"
 	cfg := device.Config{"someflag": "somevalue"}
@@ -254,13 +258,13 @@
 			c.args = append([]string{fmt.Sprintf("--packages=%s", string(jsonPackages))}, c.args...)
 		}
 		c.args = append([]string{"install"}, c.args...)
-		err := cmd.Execute(c.args)
+		err := v23cmd.ParseAndRun(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", got, expected)
+				t.Errorf("test case %d: invalid call sequence. Got %v, want %v", i, got, expected)
 			}
 		} else {
 			if err != nil {
@@ -279,28 +283,28 @@
 }
 
 func TestClaimCommand(t *testing.T) {
-	shutdown := initTest()
+	ctx, shutdown := test.InitForTest()
 	defer shutdown()
 
 	tapes := newTapeMap()
-	server, endpoint, err := startServer(t, gctx, tapes)
+	server, endpoint, err := startServer(t, ctx, tapes)
 	if err != nil {
 		return
 	}
 	defer stopServer(t, server)
 
 	// Setup the command-line.
-	cmd := cmd_device.Root()
+	cmd := cmd_device.CmdRoot
 	var stdout, stderr bytes.Buffer
-	cmd.Init(nil, &stdout, &stderr)
+	env := &cmdline2.Env{Stdout: &stdout, Stderr: &stderr}
 	deviceName := naming.JoinAddressName(endpoint.String(), "")
-	deviceKey, err := v23.GetPrincipal(gctx).PublicKey().MarshalBinary()
+	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 := cmd.Execute([]string{"claim", "nope"}); err == nil {
+	if err := v23cmd.ParseAndRun(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) {
@@ -311,7 +315,7 @@
 	rootTape := tapes.forSuffix("")
 	rootTape.Rewind()
 
-	if err := cmd.Execute([]string{"claim", "nope", "nope", "nope", "nope", "nope"}); err == nil {
+	if err := v23cmd.ParseAndRun(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) {
@@ -331,7 +335,7 @@
 			t.Fatalf("Failed to marshal principal public key: %v", err)
 		}
 	}
-	if err := cmd.Execute([]string{"claim", deviceName, "grant", pairingToken, base64.URLEncoding.EncodeToString(deviceKeyWrong)}); verror.ErrorID(err) != verror.ErrNotTrusted.ID {
+	if err := v23cmd.ParseAndRun(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()
@@ -342,7 +346,7 @@
 	rootTape.SetResponses([]interface{}{
 		nil,
 	})
-	if err := cmd.Execute([]string{"claim", deviceName, "grant", pairingToken, base64.URLEncoding.EncodeToString(deviceKey)}); err != nil {
+	if err := v23cmd.ParseAndRun(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 {
@@ -365,8 +369,8 @@
 	rootTape.SetResponses([]interface{}{
 		verror.New(errOops, nil),
 	})
-	if err := cmd.Execute([]string{"claim", deviceName, "grant", pairingToken}); err == nil {
-		t.Fatalf("claim() failed to detect error", err)
+	if err := v23cmd.ParseAndRun(cmd, ctx, env, []string{"claim", deviceName, "grant", pairingToken}); err == nil {
+		t.Fatal("claim() failed to detect error:", err)
 	}
 	expected = []interface{}{
 		"Claim",
@@ -377,24 +381,24 @@
 }
 
 func TestInstantiateCommand(t *testing.T) {
-	shutdown := initTest()
+	ctx, shutdown := test.InitForTest()
 	defer shutdown()
 
 	tapes := newTapeMap()
-	server, endpoint, err := startServer(t, gctx, tapes)
+	server, endpoint, err := startServer(t, ctx, tapes)
 	if err != nil {
 		return
 	}
 	defer stopServer(t, server)
 
 	// Setup the command-line.
-	cmd := cmd_device.Root()
+	cmd := cmd_device.CmdRoot
 	var stdout, stderr bytes.Buffer
-	cmd.Init(nil, &stdout, &stderr)
+	env := &cmdline2.Env{Stdout: &stdout, Stderr: &stderr}
 	appName := naming.JoinAddressName(endpoint.String(), "")
 
 	// Confirm that we correctly enforce the number of arguments.
-	if err := cmd.Execute([]string{"instantiate", "nope"}); err == nil {
+	if err := v23cmd.ParseAndRun(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) {
@@ -405,7 +409,7 @@
 	rootTape := tapes.forSuffix("")
 	rootTape.Rewind()
 
-	if err := cmd.Execute([]string{"instantiate", "nope", "nope", "nope"}); err == nil {
+	if err := v23cmd.ParseAndRun(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) {
@@ -421,7 +425,7 @@
 		instanceID: "app1",
 	},
 	})
-	if err := cmd.Execute([]string{"instantiate", appName, "grant"}); err != nil {
+	if err := v23cmd.ParseAndRun(cmd, ctx, env, []string{"instantiate", appName, "grant"}); err != nil {
 		t.Fatalf("instantiate %s %s failed: %v", appName, "grant", err)
 	}
 
@@ -446,7 +450,7 @@
 		"",
 	},
 	})
-	if err := cmd.Execute([]string{"instantiate", appName, "grant"}); err == nil {
+	if err := v23cmd.ParseAndRun(cmd, ctx, env, []string{"instantiate", appName, "grant"}); err == nil {
 		t.Fatalf("instantiate failed to detect error")
 	}
 	expected = []interface{}{
@@ -458,24 +462,24 @@
 }
 
 func TestDebugCommand(t *testing.T) {
-	shutdown := initTest()
+	ctx, shutdown := test.InitForTest()
 	defer shutdown()
 	tapes := newTapeMap()
-	server, endpoint, err := startServer(t, gctx, tapes)
+	server, endpoint, err := startServer(t, ctx, tapes)
 	if err != nil {
 		return
 	}
 	defer stopServer(t, server)
 	// Setup the command-line.
-	cmd := cmd_device.Root()
+	cmd := cmd_device.CmdRoot
 	var stdout, stderr bytes.Buffer
-	cmd.Init(nil, &stdout, &stderr)
+	env := &cmdline2.Env{Stdout: &stdout, Stderr: &stderr}
 	appName := naming.JoinAddressName(endpoint.String(), "")
 
 	debugMessage := "the secrets of the universe, revealed"
 	rootTape := tapes.forSuffix("")
 	rootTape.SetResponses([]interface{}{debugMessage})
-	if err := cmd.Execute([]string{"debug", appName}); err != nil {
+	if err := v23cmd.ParseAndRun(cmd, ctx, env, []string{"debug", appName}); err != nil {
 		t.Fatalf("%v", err)
 	}
 	if expected, got := debugMessage, strings.TrimSpace(stdout.String()); got != expected {
@@ -487,18 +491,18 @@
 }
 
 func TestStatusCommand(t *testing.T) {
-	shutdown := initTest()
+	ctx, shutdown := test.InitForTest()
 	defer shutdown()
 	tapes := newTapeMap()
-	server, endpoint, err := startServer(t, gctx, tapes)
+	server, endpoint, err := startServer(t, ctx, tapes)
 	if err != nil {
 		return
 	}
 	defer stopServer(t, server)
 	// Setup the command-line.
-	cmd := cmd_device.Root()
+	cmd := cmd_device.CmdRoot
 	var stdout, stderr bytes.Buffer
-	cmd.Init(nil, &stdout, &stderr)
+	env := &cmdline2.Env{Stdout: &stdout, Stderr: &stderr}
 	appName := naming.JoinAddressName(endpoint.String(), "")
 
 	rootTape := tapes.forSuffix("")
@@ -524,7 +528,7 @@
 		rootTape.Rewind()
 		stdout.Reset()
 		rootTape.SetResponses([]interface{}{c.tapeResponse})
-		if err := cmd.Execute([]string{"status", appName}); err != nil {
+		if err := v23cmd.ParseAndRun(cmd, ctx, env, []string{"status", appName}); err != nil {
 			t.Errorf("%v", err)
 		}
 		if expected, got := c.expected, strings.TrimSpace(stdout.String()); got != expected {
diff --git a/services/device/device/instance_impl.go b/services/device/device/instance_impl.go
index 7a3febd..e1bcd82 100644
--- a/services/device/device/instance_impl.go
+++ b/services/device/device/instance_impl.go
@@ -10,12 +10,14 @@
 	"fmt"
 	"time"
 
+	"v.io/v23/context"
 	"v.io/v23/services/device"
-	"v.io/x/lib/cmdline"
+	"v.io/x/lib/cmdline2"
+	"v.io/x/ref/lib/v23cmd"
 )
 
-var cmdDelete = &cmdline.Command{
-	Run:      runDelete,
+var cmdDelete = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runDelete),
 	Name:     "delete",
 	Short:    "Delete the given application instance.",
 	Long:     "Delete the given application instance.",
@@ -24,23 +26,23 @@
 <app instance> is the vanadium object name of the application instance to delete.`,
 }
 
-func runDelete(cmd *cmdline.Command, args []string) error {
+func runDelete(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, got := 1, len(args); expected != got {
-		return cmd.UsageErrorf("delete: incorrect number of arguments, expected %d, got %d", 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(gctx); err != nil {
+	if err := device.ApplicationClient(appName).Delete(ctx); err != nil {
 		return fmt.Errorf("Delete failed: %v", err)
 	}
-	fmt.Fprintf(cmd.Stdout(), "Delete succeeded\n")
+	fmt.Fprintf(env.Stdout, "Delete succeeded\n")
 	return nil
 }
 
 const killDeadline = 10 * time.Second
 
-var cmdKill = &cmdline.Command{
-	Run:      runKill,
+var cmdKill = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runKill),
 	Name:     "kill",
 	Short:    "Kill the given application instance.",
 	Long:     "Kill the given application instance.",
@@ -49,21 +51,21 @@
 <app instance> is the vanadium object name of the application instance to kill.`,
 }
 
-func runKill(cmd *cmdline.Command, args []string) error {
+func runKill(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, got := 1, len(args); expected != got {
-		return cmd.UsageErrorf("kill: incorrect number of arguments, expected %d, got %d", 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(gctx, killDeadline); err != nil {
+	if err := device.ApplicationClient(appName).Kill(ctx, killDeadline); err != nil {
 		return fmt.Errorf("Kill failed: %v", err)
 	}
-	fmt.Fprintf(cmd.Stdout(), "Kill succeeded\n")
+	fmt.Fprintf(env.Stdout, "Kill succeeded\n")
 	return nil
 }
 
-var cmdRun = &cmdline.Command{
-	Run:      runRun,
+var cmdRun = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runRun),
 	Name:     "run",
 	Short:    "Run the given application instance.",
 	Long:     "Run the given application instance.",
@@ -72,15 +74,15 @@
 <app instance> is the vanadium object name of the application instance to run.`,
 }
 
-func runRun(cmd *cmdline.Command, args []string) error {
+func runRun(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, got := 1, len(args); expected != got {
-		return cmd.UsageErrorf("run: incorrect number of arguments, expected %d, got %d", 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(gctx); err != nil {
+	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(cmd.Stdout(), "Run succeeded\n")
+	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
index fdbe781..d072b04 100644
--- a/services/device/device/instance_impl_test.go
+++ b/services/device/device/instance_impl_test.go
@@ -13,29 +13,32 @@
 
 	"v.io/v23/naming"
 	"v.io/v23/verror"
+	"v.io/x/lib/cmdline2"
+	"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) {
-	shutdown := initTest()
+	ctx, shutdown := test.InitForTest()
 	defer shutdown()
 
 	tapes := newTapeMap()
-	server, endpoint, err := startServer(t, gctx, tapes)
+	server, endpoint, err := startServer(t, ctx, tapes)
 	if err != nil {
 		return
 	}
 	defer stopServer(t, server)
 
 	// Setup the command-line.
-	cmd := cmd_device.Root()
+	cmd := cmd_device.CmdRoot
 	var stdout, stderr bytes.Buffer
-	cmd.Init(nil, &stdout, &stderr)
+	env := &cmdline2.Env{Stdout: &stdout, Stderr: &stderr}
 	appName := naming.JoinAddressName(endpoint.String(), "appname")
 
 	// Confirm that we correctly enforce the number of arguments.
-	if err := cmd.Execute([]string{"kill"}); err == nil {
+	if err := v23cmd.ParseAndRun(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) {
@@ -46,7 +49,7 @@
 	appTape := tapes.forSuffix("appname")
 	appTape.Rewind()
 
-	if err := cmd.Execute([]string{"kill", "nope", "nope"}); err == nil {
+	if err := v23cmd.ParseAndRun(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) {
@@ -61,7 +64,7 @@
 		nil,
 	})
 
-	if err := cmd.Execute([]string{"kill", appName}); err != nil {
+	if err := v23cmd.ParseAndRun(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 {
@@ -81,7 +84,7 @@
 	appTape.SetResponses([]interface{}{
 		verror.New(errOops, nil),
 	})
-	if err := cmd.Execute([]string{"kill", appName}); err == nil {
+	if err := v23cmd.ParseAndRun(cmd, ctx, env, []string{"kill", appName}); err == nil {
 		t.Fatalf("wrongly didn't receive a non-nil error.")
 	}
 	// expected the same.
@@ -91,24 +94,24 @@
 }
 
 func testHelper(t *testing.T, lower, upper string) {
-	shutdown := initTest()
+	ctx, shutdown := test.InitForTest()
 	defer shutdown()
 
 	tapes := newTapeMap()
-	server, endpoint, err := startServer(t, gctx, tapes)
+	server, endpoint, err := startServer(t, ctx, tapes)
 	if err != nil {
 		return
 	}
 	defer stopServer(t, server)
 
 	// Setup the command-line.
-	cmd := cmd_device.Root()
+	cmd := cmd_device.CmdRoot
 	var stdout, stderr bytes.Buffer
-	cmd.Init(nil, &stdout, &stderr)
+	env := &cmdline2.Env{Stdout: &stdout, Stderr: &stderr}
 	appName := naming.JoinAddressName(endpoint.String(), "appname")
 
 	// Confirm that we correctly enforce the number of arguments.
-	if err := cmd.Execute([]string{lower}); err == nil {
+	if err := v23cmd.ParseAndRun(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) {
@@ -119,7 +122,7 @@
 	appTape := tapes.forSuffix("appname")
 	appTape.Rewind()
 
-	if err := cmd.Execute([]string{lower, "nope", "nope"}); err == nil {
+	if err := v23cmd.ParseAndRun(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) {
@@ -133,7 +136,7 @@
 	appTape.SetResponses([]interface{}{
 		nil,
 	})
-	if err := cmd.Execute([]string{lower, appName}); err != nil {
+	if err := v23cmd.ParseAndRun(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 {
@@ -150,7 +153,7 @@
 	appTape.SetResponses([]interface{}{
 		verror.New(errOops, nil),
 	})
-	if err := cmd.Execute([]string{lower, appName}); err == nil {
+	if err := v23cmd.ParseAndRun(cmd, ctx, env, []string{lower, appName}); err == nil {
 		t.Fatalf("wrongly didn't receive a non-nil error.")
 	}
 	// expected the same.
diff --git a/services/device/device/local_install.go b/services/device/device/local_install.go
index c124974..f0ef6c3 100644
--- a/services/device/device/local_install.go
+++ b/services/device/device/local_install.go
@@ -27,12 +27,13 @@
 	"v.io/v23/uniqueid"
 	"v.io/x/lib/vlog"
 
-	"v.io/x/lib/cmdline"
+	"v.io/x/lib/cmdline2"
+	"v.io/x/ref/lib/v23cmd"
 	"v.io/x/ref/services/internal/packages"
 )
 
-var cmdInstallLocal = &cmdline.Command{
-	Run:      runInstallLocal,
+var cmdInstallLocal = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runInstallLocal),
 	Name:     "install-local",
 	Short:    "Install the given application from the local system.",
 	Long:     "Install the given application specified using a local path, and print the name of the new installation.",
@@ -250,9 +251,9 @@
 // TODO(caprita/ashankar): We should use bi-directional streams to get this
 // working over the same connection that the command makes to the device
 // manager.
-func runInstallLocal(cmd *cmdline.Command, args []string) error {
+func runInstallLocal(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expectedMin, got := 2, len(args); got < expectedMin {
-		return cmd.UsageErrorf("install-local: incorrect number of arguments, expected at least %d, got %d", expectedMin, got)
+		return env.UsageErrorf("install-local: incorrect number of arguments, expected at least %d, got %d", expectedMin, got)
 	}
 	deviceName, title := args[0], args[1]
 	args = args[2:]
@@ -268,7 +269,7 @@
 	envelope.Env = args[:firstNonEnv]
 	args = args[firstNonEnv:]
 	if len(args) == 0 {
-		return cmd.UsageErrorf("install-local: missing binary")
+		return env.UsageErrorf("install-local: missing binary")
 	}
 	binary := args[0]
 	args = args[1:]
@@ -285,7 +286,7 @@
 	if _, err := os.Stat(binary); err != nil {
 		return fmt.Errorf("binary %v not found: %v", binary, err)
 	}
-	server, cancel, err := createServer(gctx, cmd.Stderr())
+	server, cancel, err := createServer(ctx, env.Stderr)
 	if err != nil {
 		return fmt.Errorf("failed to create server: %v", err)
 	}
@@ -330,7 +331,7 @@
 		return err
 	}
 	vlog.VI(1).Infof("application serving envelope as %v", appName)
-	appID, err := device.ApplicationClient(deviceName).Install(gctx, appName, device.Config(configOverride), packagesRewritten)
+	appID, err := device.ApplicationClient(deviceName).Install(ctx, appName, device.Config(configOverride), packagesRewritten)
 	// Reset the value for any future invocations of "install" or
 	// "install-local" (we run more than one command per process in unit
 	// tests).
@@ -339,6 +340,6 @@
 	if err != nil {
 		return fmt.Errorf("Install failed: %v", err)
 	}
-	fmt.Fprintf(cmd.Stdout(), "%s\n", naming.Join(deviceName, appID))
+	fmt.Fprintf(env.Stdout, "%s\n", naming.Join(deviceName, appID))
 	return nil
 }
diff --git a/services/device/device/local_install_test.go b/services/device/device/local_install_test.go
index 0c9aeac..b996393 100644
--- a/services/device/device/local_install_test.go
+++ b/services/device/device/local_install_test.go
@@ -19,6 +19,9 @@
 	"v.io/v23/security"
 	"v.io/v23/services/application"
 	"v.io/v23/services/device"
+	"v.io/x/lib/cmdline2"
+	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/test"
 
 	cmd_device "v.io/x/ref/services/device/device"
 )
@@ -30,19 +33,19 @@
 }
 
 func TestInstallLocalCommand(t *testing.T) {
-	shutdown := initTest()
+	ctx, shutdown := test.InitForTest()
 	defer shutdown()
 
 	tapes := newTapeMap()
-	server, endpoint, err := startServer(t, gctx, tapes)
+	server, endpoint, err := startServer(t, ctx, tapes)
 	if err != nil {
 		return
 	}
 	defer stopServer(t, server)
 	// Setup the command-line.
-	cmd := cmd_device.Root()
+	cmd := cmd_device.CmdRoot
 	var stdout, stderr bytes.Buffer
-	cmd.Init(nil, &stdout, &stderr)
+	env := &cmdline2.Env{Stdout: &stdout, Stderr: &stderr}
 	deviceName := naming.JoinAddressName(endpoint.String(), "")
 	const appTitle = "Appo di tutti Appi"
 	binary := os.Args[0]
@@ -73,7 +76,7 @@
 		},
 	} {
 		c.args = append([]string{"install-local"}, c.args...)
-		if err := cmd.Execute(c.args); err == nil {
+		if err := v23cmd.ParseAndRun(cmd, ctx, env, c.args); err == nil {
 			t.Fatalf("test case %d: wrongly failed to receive a non-nil error.", i)
 		} else {
 			fmt.Fprintln(&stderr, "ERROR:", err)
@@ -82,7 +85,7 @@
 			}
 		}
 		if got, expected := len(rootTape.Play()), 0; got != expected {
-			t.Errorf("test case %d: invalid call sequence. Got %v, want %v", got, expected)
+			t.Errorf("test case %d: invalid call sequence. Got %v, want %v", i, got, expected)
 		}
 		rootTape.Rewind()
 		stdout.Reset()
@@ -237,7 +240,7 @@
 			c.args = append([]string{fmt.Sprintf("--packages=%s", string(jsonPackages))}, c.args...)
 		}
 		c.args = append([]string{"install-local"}, c.args...)
-		if err := cmd.Execute(c.args); err != nil {
+		if err := v23cmd.ParseAndRun(cmd, ctx, env, c.args); err != nil {
 			t.Fatalf("test case %d: %v", i, err)
 		}
 		if expected, got := naming.Join(deviceName, appId), strings.TrimSpace(stdout.String()); got != expected {
diff --git a/services/device/device/main.go b/services/device/device/main.go
deleted file mode 100644
index b523dbc..0000000
--- a/services/device/device/main.go
+++ /dev/null
@@ -1,26 +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 following enables go generate to generate the doc.go file.
-//go:generate go run $V23_ROOT/release/go/src/v.io/x/lib/cmdline/testdata/gendoc.go .
-
-package main
-
-import (
-	"os"
-	"regexp"
-
-	"v.io/v23"
-	"v.io/x/lib/cmdline"
-	_ "v.io/x/ref/profiles/static"
-)
-
-func main() {
-	cmdline.HideGlobalFlagsExcept(regexp.MustCompile(`^(v23\.namespace\.root)|(v23\.proxy)$`))
-	gctx, shutdown := v23.Init()
-	SetGlobalContext(gctx)
-	exitCode := Root().Main()
-	shutdown()
-	os.Exit(exitCode)
-}
diff --git a/services/device/device/publish.go b/services/device/device/publish.go
index 7412cda..ae49d3c 100644
--- a/services/device/device/publish.go
+++ b/services/device/device/publish.go
@@ -12,14 +12,15 @@
 	"strings"
 	"time"
 
+	"v.io/v23/context"
 	"v.io/v23/naming"
 	"v.io/v23/security"
 	"v.io/v23/security/access"
 	"v.io/v23/services/application"
 	"v.io/v23/services/permissions"
 	"v.io/v23/verror"
-
-	"v.io/x/lib/cmdline"
+	"v.io/x/lib/cmdline2"
+	"v.io/x/ref/lib/v23cmd"
 	"v.io/x/ref/services/internal/binarylib"
 	"v.io/x/ref/services/repository"
 )
@@ -28,17 +29,19 @@
 
 // TODO(caprita): Extend to include env, args, packages.
 
-var cmdPublish = &cmdline.Command{
-	Run:   runPublish,
-	Name:  "publish",
-	Short: "Publish the given application(s).",
+var cmdPublish = &cmdline2.Command{
+	Runner: v23cmd.RunnerFunc(runPublish),
+	Name:   "publish",
+	Short:  "Publish the given application(s).",
 	Long: `
 Publishes the given application(s) to the binary and application servers.
+The <title> can be optionally specified with @<title> (defaults to the binary
+name).
 The binaries should be in $V23_ROOT/release/go/bin/[<GOOS>_<GOARCH>].
 The binary is published as <binserv>/<binary name>/<GOOS>-<GOARCH>/<TIMESTAMP>.
 The application envelope is published as <appserv>/<binary name>/0.
 Optionally, adds blessing patterns to the Read and Resolve AccessLists.`,
-	ArgsName: "<binary name> ...",
+	ArgsName: "<binary name>[@<title>] ...",
 }
 
 var binaryService, applicationService, readBlessings, goarchFlag, goosFlag string
@@ -53,11 +56,11 @@
 	cmdPublish.Flags.StringVar(&readBlessings, "readers", "dev.v.io", "If non-empty, comma-separated blessing patterns to add to Read and Resolve AccessList.")
 }
 
-func setAccessLists(cmd *cmdline.Command, von string) error {
+func setAccessLists(ctx *context.T, env *cmdline2.Env, von string) error {
 	if readBlessings == "" {
 		return nil
 	}
-	perms, version, err := permissions.ObjectClient(von).GetPermissions(gctx)
+	perms, version, err := permissions.ObjectClient(von).GetPermissions(ctx)
 	if err != nil {
 		// TODO(caprita): This is a workaround until we sort out the
 		// default AccessLists for applicationd (see issue #1317).  At that
@@ -71,14 +74,18 @@
 			perms.Add(security.BlessingPattern(blessing), string(tag))
 		}
 	}
-	if err := permissions.ObjectClient(von).SetPermissions(gctx, perms, version); err != nil {
+	if err := permissions.ObjectClient(von).SetPermissions(ctx, perms, version); err != nil {
 		return err
 	}
-	fmt.Fprintf(cmd.Stdout(), "Added patterns %q to Read,Resolve AccessList for %q\n", readBlessings, von)
+	fmt.Fprintf(env.Stdout, "Added patterns %q to Read,Resolve AccessList for %q\n", readBlessings, von)
 	return nil
 }
 
-func publishOne(cmd *cmdline.Command, binPath, binaryName string) error {
+func publishOne(ctx *context.T, env *cmdline2.Env, binPath, binary string) error {
+	binaryName, title := binary, binary
+	if parts := strings.SplitN(binary, "@", 2); len(parts) == 2 {
+		binaryName, title = parts[0], parts[1]
+	}
 	// Step 1, upload the binary to the binary service.
 
 	// TODO(caprita): Instead of the current timestamp, use each binary's
@@ -87,14 +94,14 @@
 	binaryVON := naming.Join(binaryService, binaryName, fmt.Sprintf("%s-%s", goosFlag, goarchFlag), timestamp)
 	binaryFile := filepath.Join(binPath, binaryName)
 	// TODO(caprita): Take signature of binary and put it in the envelope.
-	if _, err := binarylib.UploadFromFile(gctx, binaryVON, binaryFile); err != nil {
+	if _, err := binarylib.UploadFromFile(ctx, binaryVON, binaryFile); err != nil {
 		return err
 	}
-	fmt.Fprintf(cmd.Stdout(), "Binary %q uploaded from file %s\n", binaryVON, binaryFile)
+	fmt.Fprintf(env.Stdout, "Binary %q uploaded from file %s\n", binaryVON, binaryFile)
 
 	// Step 2, set the perms for the uploaded binary.
 
-	if err := setAccessLists(cmd, binaryVON); err != nil {
+	if err := setAccessLists(ctx, env, binaryVON); err != nil {
 		return err
 	}
 
@@ -110,55 +117,55 @@
 	// NOTE: If profiles contains more than one entry, this will return only
 	// the first match.  But presumably that's ok, since we're going to set
 	// the envelopes for all the profiles to the same envelope anyway below.
-	envelope, err := appClient.Match(gctx, profiles)
+	envelope, err := appClient.Match(ctx, profiles)
 	if verror.ErrorID(err) == verror.ErrNoExist.ID {
 		// There was nothing published yet, create a new envelope.
-		envelope = application.Envelope{Title: binaryName}
+		envelope = application.Envelope{Title: title}
 	} else if err != nil {
 		return err
 	}
 	envelope.Binary.File = binaryVON
-	if err := repository.ApplicationClient(appVON).Put(gctx, profiles, envelope); err != nil {
+	if err := repository.ApplicationClient(appVON).Put(ctx, profiles, envelope); err != nil {
 		return err
 	}
-	fmt.Fprintf(cmd.Stdout(), "Published %q\n", appVON)
+	fmt.Fprintf(env.Stdout, "Published %q\n", appVON)
 
 	// Step 4, set the perms for the uploaded envelope.
 
-	if err := setAccessLists(cmd, appVON); err != nil {
+	if err := setAccessLists(ctx, env, appVON); err != nil {
 		return err
 	}
 	return nil
 }
 
-func runPublish(cmd *cmdline.Command, args []string) error {
+func runPublish(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expectedMin, got := 1, len(args); got < expectedMin {
-		return cmd.UsageErrorf("publish: incorrect number of arguments, expected at least %d, got %d", expectedMin, got)
+		return env.UsageErrorf("publish: incorrect number of arguments, expected at least %d, got %d", expectedMin, got)
 	}
 	binaries := args
 	vroot := os.Getenv("V23_ROOT")
 	if vroot == "" {
-		return cmd.UsageErrorf("publish: $V23_ROOT environment variable should be set")
+		return env.UsageErrorf("publish: $V23_ROOT environment variable should be set")
 	}
 	binPath := filepath.Join(vroot, "release/go/bin")
 	if goosFlag != runtime.GOOS || goarchFlag != runtime.GOARCH {
 		binPath = filepath.Join(binPath, fmt.Sprintf("%s_%s", goosFlag, goarchFlag))
 	}
 	if fi, err := os.Stat(binPath); err != nil {
-		return cmd.UsageErrorf("publish: failed to stat %v: %v", binPath, err)
+		return env.UsageErrorf("publish: failed to stat %v: %v", binPath, err)
 	} else if !fi.IsDir() {
-		return cmd.UsageErrorf("publish: %v is not a directory", binPath)
+		return env.UsageErrorf("publish: %v is not a directory", binPath)
 	}
 	if binaryService == "" {
-		return cmd.UsageErrorf("publish: --binserv must point to a binary service name")
+		return env.UsageErrorf("publish: --binserv must point to a binary service name")
 	}
 	if applicationService == "" {
-		return cmd.UsageErrorf("publish: --appserv must point to an application service name")
+		return env.UsageErrorf("publish: --appserv must point to an application service name")
 	}
 	var lastErr error
 	for _, b := range binaries {
-		if err := publishOne(cmd, binPath, b); err != nil {
-			fmt.Fprintf(cmd.Stderr(), "Failed to publish %q: %v\n", b, err)
+		if err := publishOne(ctx, env, binPath, b); err != nil {
+			fmt.Fprintf(env.Stderr, "Failed to publish %q: %v\n", b, err)
 			lastErr = err
 		}
 	}
diff --git a/services/device/device/root.go b/services/device/device/root.go
index 809b3bb..ec07f48 100644
--- a/services/device/device/root.go
+++ b/services/device/device/root.go
@@ -5,24 +5,22 @@
 package main
 
 import (
-	"v.io/v23/context"
+	"regexp"
 
-	"v.io/x/lib/cmdline"
+	"v.io/x/lib/cmdline2"
+	_ "v.io/x/ref/profiles/static"
 )
 
-var gctx *context.T
-
-func SetGlobalContext(ctx *context.T) {
-	gctx = ctx
-}
-
-func Root() *cmdline.Command {
-	return &cmdline.Command{
-		Name:  "device",
-		Short: "facilitates interaction with the Vanadium device manager",
-		Long: `
+var CmdRoot = &cmdline2.Command{
+	Name:  "device",
+	Short: "facilitates interaction with the Vanadium device manager",
+	Long: `
 Command device facilitates interaction with the Vanadium device manager.
 `,
-		Children: []*cmdline.Command{cmdInstall, cmdInstallLocal, cmdUninstall, associateRoot(), cmdDescribe, cmdClaim, cmdInstantiate, cmdDelete, cmdRun, cmdKill, cmdRevert, cmdUpdate, cmdUpdateAll, cmdStatus, cmdDebug, aclRoot(), cmdPublish},
-	}
+	Children: []*cmdline2.Command{cmdInstall, cmdInstallLocal, cmdUninstall, cmdAssociate, cmdDescribe, cmdClaim, cmdInstantiate, cmdDelete, cmdRun, cmdKill, cmdRevert, cmdUpdate, cmdUpdateAll, cmdStatus, cmdDebug, cmdACL, cmdPublish},
+}
+
+func main() {
+	cmdline2.HideGlobalFlagsExcept(regexp.MustCompile(`^((v23\.namespace\.root)|(v23\.proxy))$`))
+	cmdline2.Main(CmdRoot)
 }
diff --git a/services/device/device/updateall.go b/services/device/device/updateall.go
index c660863..b98b46b 100644
--- a/services/device/device/updateall.go
+++ b/services/device/device/updateall.go
@@ -11,11 +11,12 @@
 	"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/lib/cmdline2"
+	"v.io/x/ref/lib/v23cmd"
 	deviceimpl "v.io/x/ref/services/device/internal/impl"
 )
 
@@ -24,8 +25,8 @@
 
 // TODO(caprita): Add unit test.
 
-var cmdUpdateAll = &cmdline.Command{
-	Run:      runUpdateAll,
+var cmdUpdateAll = &cmdline2.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",
@@ -43,12 +44,12 @@
 `,
 }
 
-type updater func(cmd *cmdline.Command, von string) error
+type updater func(ctx *context.T, env *cmdline2.Env, von string) error
 
-func updateChildren(cmd *cmdline.Command, von string, updateChild updater) error {
-	ns := v23.GetNamespace(gctx)
+func updateChildren(ctx *context.T, env *cmdline2.Env, von string, updateChild updater) error {
+	ns := v23.GetNamespace(ctx)
 	pattern := naming.Join(von, "*")
-	c, err := ns.Glob(gctx, pattern)
+	c, err := ns.Glob(ctx, pattern)
 	if err != nil {
 		return fmt.Errorf("ns.Glob(%q) failed: %v", pattern, err)
 	}
@@ -62,7 +63,7 @@
 		case *naming.GlobReplyEntry:
 			pending.Add(1)
 			go func() {
-				if err := updateChild(cmd, v.Value.Name); err != nil {
+				if err := updateChild(ctx, env, v.Value.Name); err != nil {
 					numErrorsMu.Lock()
 					numErrors++
 					numErrorsMu.Unlock()
@@ -70,7 +71,7 @@
 				pending.Done()
 			}()
 		case *naming.GlobReplyError:
-			fmt.Fprintf(cmd.Stderr(), "Glob error for %q: %v\n", v.Value.Name, v.Value.Error)
+			fmt.Fprintf(env.Stderr, "Glob error for %q: %v\n", v.Value.Name, v.Value.Error)
 			numErrorsMu.Lock()
 			numErrors++
 			numErrorsMu.Unlock()
@@ -83,8 +84,8 @@
 	return nil
 }
 
-func instanceIsRunning(von string) (bool, error) {
-	status, err := device.ApplicationClient(von).Status(gctx)
+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)
 	}
@@ -95,79 +96,79 @@
 	return s.Value.State == device.InstanceStateRunning, nil
 }
 
-func updateInstance(cmd *cmdline.Command, von string) (retErr error) {
+func updateInstance(ctx *context.T, env *cmdline2.Env, von string) (retErr error) {
 	defer func() {
 		if retErr == nil {
-			fmt.Fprintf(cmd.Stdout(), "Successfully updated instance %q.\n", von)
+			fmt.Fprintf(env.Stdout, "Successfully updated instance %q.\n", von)
 		} else {
 			retErr = fmt.Errorf("failed to update instance %q: %v", von, retErr)
-			fmt.Fprintf(cmd.Stderr(), "ERROR: %v.\n", retErr)
+			fmt.Fprintf(env.Stderr, "ERROR: %v.\n", retErr)
 		}
 	}()
-	running, err := instanceIsRunning(von)
+	running, err := instanceIsRunning(ctx, von)
 	if err != nil {
 		return err
 	}
 	if running {
 		// Try killing the app.
-		if err := device.ApplicationClient(von).Kill(gctx, killDeadline); err != nil {
+		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(von)
+			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(cmd.Stderr(), "Kill(%s) returned an error (%s) but app is now not running.\n", 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(gctx); err != nil {
+			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(cmd.Stderr(), "ERROR: %v.\n", err)
+					fmt.Fprintf(env.Stderr, "ERROR: %v.\n", err)
 				}
 			}
 		}()
 	}
 	// Update the instance.
-	switch err := device.ApplicationClient(von).Update(gctx); {
+	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(cmd.Stdout(), "Instance %q already up to date.\n", von)
+		fmt.Fprintf(env.Stdout, "Instance %q already up to date.\n", von)
 		return nil
 	default:
 		return err
 	}
 }
 
-func updateInstallation(cmd *cmdline.Command, von string) (retErr error) {
+func updateInstallation(ctx *context.T, env *cmdline2.Env, von string) (retErr error) {
 	defer func() {
 		if retErr == nil {
-			fmt.Fprintf(cmd.Stdout(), "Successfully updated installation %q.\n", von)
+			fmt.Fprintf(env.Stdout, "Successfully updated installation %q.\n", von)
 		} else {
 			retErr = fmt.Errorf("failed to update installation %q: %v", von, retErr)
-			fmt.Fprintf(cmd.Stderr(), "ERROR: %v.\n", retErr)
+			fmt.Fprintf(env.Stderr, "ERROR: %v.\n", retErr)
 		}
 	}()
 
 	// First, update the installation.
-	switch err := device.ApplicationClient(von).Update(gctx); {
+	switch err := device.ApplicationClient(von).Update(ctx); {
 	case err == nil:
-		fmt.Fprintf(cmd.Stdout(), "Successfully updated version for installation %q.\n", von)
+		fmt.Fprintf(env.Stdout, "Successfully updated version for installation %q.\n", von)
 	case verror.ErrorID(err) == deviceimpl.ErrUpdateNoOp.ID:
-		fmt.Fprintf(cmd.Stdout(), "Installation %q already up to date.\n", von)
+		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.
@@ -175,32 +176,32 @@
 		return err
 	}
 	// Then, update all the instances for the installation.
-	return updateChildren(cmd, von, updateInstance)
+	return updateChildren(ctx, env, von, updateInstance)
 }
 
-func updateApp(cmd *cmdline.Command, von string) error {
-	if err := updateChildren(cmd, von, updateInstallation); err != nil {
+func updateApp(ctx *context.T, env *cmdline2.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(cmd.Stderr(), "ERROR: %v.\n", err)
+		fmt.Fprintf(env.Stderr, "ERROR: %v.\n", err)
 		return err
 	}
-	fmt.Fprintf(cmd.Stdout(), "Successfully updated app %q.\n", von)
+	fmt.Fprintf(env.Stdout, "Successfully updated app %q.\n", von)
 	return nil
 }
 
-func updateAllApps(cmd *cmdline.Command, von string) error {
-	if err := updateChildren(cmd, von, updateApp); err != nil {
+func updateAllApps(ctx *context.T, env *cmdline2.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(cmd.Stderr(), "ERROR: %v.\n", err)
+		fmt.Fprintf(env.Stderr, "ERROR: %v.\n", err)
 		return err
 	}
-	fmt.Fprintf(cmd.Stdout(), "Successfully updated all apps %q.\n", von)
+	fmt.Fprintf(env.Stdout, "Successfully updated all apps %q.\n", von)
 	return nil
 }
 
-func runUpdateAll(cmd *cmdline.Command, args []string) error {
+func runUpdateAll(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, got := 1, len(args); expected != got {
-		return cmd.UsageErrorf("updateall: incorrect number of arguments, expected %d, got %d", expected, got)
+		return env.UsageErrorf("updateall: incorrect number of arguments, expected %d, got %d", expected, got)
 	}
 	appVON := args[0]
 	components := strings.Split(appVON, "/")
@@ -223,13 +224,13 @@
 	fmt.Printf("prefix: %q, components: %q\n", prefix, components)
 	switch len(components) {
 	case 0:
-		return updateAllApps(cmd, appVON)
+		return updateAllApps(ctx, env, appVON)
 	case 1:
-		return updateApp(cmd, appVON)
+		return updateApp(ctx, env, appVON)
 	case 2:
-		return updateInstallation(cmd, appVON)
+		return updateInstallation(ctx, env, appVON)
 	case 3:
-		return updateInstance(cmd, appVON)
+		return updateInstance(ctx, env, appVON)
 	}
-	return cmd.UsageErrorf("updateall: name %q does not refer to a supported app hierarchy object", 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
deleted file mode 100644
index 0be10bc..0000000
--- a/services/device/device/util_test.go
+++ /dev/null
@@ -1,26 +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 (
-	"v.io/v23"
-	"v.io/v23/context"
-
-	cmd_device "v.io/x/ref/services/device/device"
-	"v.io/x/ref/test"
-)
-
-var gctx *context.T
-
-func initTest() v23.Shutdown {
-	var shutdown v23.Shutdown
-	gctx, shutdown = test.InitForTest()
-	cmd_device.SetGlobalContext(gctx)
-	return func() {
-		shutdown()
-		cmd_device.SetGlobalContext(nil)
-		gctx = nil
-	}
-}
diff --git a/services/device/deviced/commands.go b/services/device/deviced/commands.go
index a8bc375..a2eccc2 100644
--- a/services/device/deviced/commands.go
+++ b/services/device/deviced/commands.go
@@ -8,7 +8,7 @@
 	"fmt"
 	"os"
 
-	"v.io/x/lib/cmdline"
+	"v.io/x/lib/cmdline2"
 
 	"v.io/v23"
 	"v.io/x/lib/vlog"
@@ -41,8 +41,8 @@
 	}
 }
 
-var cmdInstall = &cmdline.Command{
-	Run:      runInstall,
+var cmdInstall = &cmdline2.Command{
+	Runner:   cmdline2.RunnerFunc(runInstall),
 	Name:     "install",
 	Short:    "Install the device manager.",
 	Long:     fmt.Sprintf("Performs installation of device manager into %s (if the env var set), or into the current dir otherwise", deviceDirEnv),
@@ -63,7 +63,7 @@
 	cmdInstall.Flags.BoolVar(&initMode, "init_mode", false, "if set, installs the device manager with the system init service manager")
 }
 
-func runInstall(cmd *cmdline.Command, args []string) error {
+func runInstall(env *cmdline2.Env, args []string) error {
 	if installFrom != "" {
 		// TODO(caprita): Also pass args into InstallFrom.
 		if err := impl.InstallFrom(installFrom); err != nil {
@@ -73,94 +73,94 @@
 		return nil
 	}
 	if suidHelper == "" {
-		return cmd.UsageErrorf("--suid_helper must be set")
+		return env.UsageErrorf("--suid_helper must be set")
 	}
 	if agent == "" {
-		return cmd.UsageErrorf("--agent must be set")
+		return env.UsageErrorf("--agent must be set")
 	}
 	if initMode && initHelper == "" {
-		return cmd.UsageErrorf("--init_helper must be set")
+		return env.UsageErrorf("--init_helper must be set")
 	}
-	if err := impl.SelfInstall(installationDir(), suidHelper, agent, initHelper, origin, singleUser, sessionMode, initMode, args, os.Environ(), cmd.Stderr(), cmd.Stdout()); err != nil {
+	if err := impl.SelfInstall(installationDir(), suidHelper, agent, initHelper, origin, singleUser, sessionMode, initMode, args, os.Environ(), env.Stderr, env.Stdout); err != nil {
 		vlog.Errorf("SelfInstall failed: %v", err)
 		return err
 	}
 	return nil
 }
 
-var cmdUninstall = &cmdline.Command{
-	Run:   runUninstall,
-	Name:  "uninstall",
-	Short: "Uninstall the device manager.",
-	Long:  fmt.Sprintf("Removes the device manager installation from %s (if the env var set), or the current dir otherwise", deviceDirEnv),
+var cmdUninstall = &cmdline2.Command{
+	Runner: cmdline2.RunnerFunc(runUninstall),
+	Name:   "uninstall",
+	Short:  "Uninstall the device manager.",
+	Long:   fmt.Sprintf("Removes the device manager installation from %s (if the env var set), or the current dir otherwise", deviceDirEnv),
 }
 
 func init() {
 	cmdUninstall.Flags.StringVar(&suidHelper, "suid_helper", "", "path to suid helper")
 }
 
-func runUninstall(cmd *cmdline.Command, _ []string) error {
+func runUninstall(env *cmdline2.Env, _ []string) error {
 	if suidHelper == "" {
-		return cmd.UsageErrorf("--suid_helper must be set")
+		return env.UsageErrorf("--suid_helper must be set")
 	}
-	if err := impl.Uninstall(installationDir(), suidHelper, cmd.Stderr(), cmd.Stdout()); err != nil {
+	if err := impl.Uninstall(installationDir(), suidHelper, env.Stderr, env.Stdout); err != nil {
 		vlog.Errorf("Uninstall failed: %v", err)
 		return err
 	}
 	return nil
 }
 
-var cmdStart = &cmdline.Command{
-	Run:   runStart,
-	Name:  "start",
-	Short: "Start the device manager.",
-	Long:  fmt.Sprintf("Starts the device manager installed under from %s (if the env var set), or the current dir otherwise", deviceDirEnv),
+var cmdStart = &cmdline2.Command{
+	Runner: cmdline2.RunnerFunc(runStart),
+	Name:   "start",
+	Short:  "Start the device manager.",
+	Long:   fmt.Sprintf("Starts the device manager installed under from %s (if the env var set), or the current dir otherwise", deviceDirEnv),
 }
 
-func runStart(cmd *cmdline.Command, _ []string) error {
-	if err := impl.Start(installationDir(), cmd.Stderr(), cmd.Stdout()); err != nil {
+func runStart(env *cmdline2.Env, _ []string) error {
+	if err := impl.Start(installationDir(), env.Stderr, env.Stdout); err != nil {
 		vlog.Errorf("Start failed: %v", err)
 		return err
 	}
 	return nil
 }
 
-var cmdStop = &cmdline.Command{
-	Run:   runStop,
-	Name:  "stop",
-	Short: "Stop the device manager.",
-	Long:  fmt.Sprintf("Stops the device manager installed under from %s (if the env var set), or the current dir otherwise", deviceDirEnv),
+var cmdStop = &cmdline2.Command{
+	Runner: cmdline2.RunnerFunc(runStop),
+	Name:   "stop",
+	Short:  "Stop the device manager.",
+	Long:   fmt.Sprintf("Stops the device manager installed under from %s (if the env var set), or the current dir otherwise", deviceDirEnv),
 }
 
-func runStop(cmd *cmdline.Command, _ []string) error {
+func runStop(env *cmdline2.Env, _ []string) error {
 	ctx, shutdown := v23.Init()
 	defer shutdown()
-	if err := impl.Stop(ctx, installationDir(), cmd.Stderr(), cmd.Stdout()); err != nil {
+	if err := impl.Stop(ctx, installationDir(), env.Stderr, env.Stdout); err != nil {
 		vlog.Errorf("Stop failed: %v", err)
 		return err
 	}
 	return nil
 }
 
-var cmdProfile = &cmdline.Command{
-	Run:   runProfile,
-	Name:  "profile",
-	Short: "Dumps profile for the device manager.",
-	Long:  "Prints the internal profile description for the device manager.",
+var cmdProfile = &cmdline2.Command{
+	Runner: cmdline2.RunnerFunc(runProfile),
+	Name:   "profile",
+	Short:  "Dumps profile for the device manager.",
+	Long:   "Prints the internal profile description for the device manager.",
 }
 
-func runProfile(cmd *cmdline.Command, _ []string) error {
+func runProfile(env *cmdline2.Env, _ []string) error {
 	spec, err := impl.ComputeDeviceProfile()
 	if err != nil {
 		vlog.Errorf("ComputeDeviceProfile failed: %v", err)
 		return err
 	}
-	fmt.Fprintf(cmd.Stdout(), "Profile: %#v\n", spec)
+	fmt.Fprintf(env.Stdout, "Profile: %#v\n", spec)
 	desc, err := impl.Describe()
 	if err != nil {
 		vlog.Errorf("Describe failed: %v", err)
 		return err
 	}
-	fmt.Fprintf(cmd.Stdout(), "Description: %#v\n", desc)
+	fmt.Fprintf(env.Stdout, "Description: %#v\n", desc)
 	return nil
 }
diff --git a/services/device/deviced/doc.go b/services/device/deviced/doc.go
index cc1c5bb..f9000bc 100644
--- a/services/device/deviced/doc.go
+++ b/services/device/deviced/doc.go
@@ -10,8 +10,8 @@
 which implements the v.io/v23/services/device interfaces.
 
 Usage:
-   deviced <command>
    deviced
+   deviced <command>
 
 The deviced commands are:
    install     Install the device manager.
@@ -184,11 +184,6 @@
 
 "help ..." recursively displays help for all commands and topics.
 
-Output is formatted to a target width in runes, determined by checking the
-CMDLINE_WIDTH environment variable, falling back on the terminal width, falling
-back on 80 chars.  By setting CMDLINE_WIDTH=x, if x > 0 the width is x, if x < 0
-the width is unlimited, and if x == 0 or is unset one of the fallbacks is used.
-
 Usage:
    deviced help [flags] [command/topic ...]
 
@@ -201,5 +196,9 @@
       full    - Good for cmdline output, shows all global flags.
       godoc   - Good for godoc processing.
    Override the default by setting the CMDLINE_STYLE environment variable.
+ -width=<terminal width>
+   Format output to this target width in runes, or unlimited if width < 0.
+   Defaults to the terminal width if available.  Override the default by setting
+   the CMDLINE_WIDTH environment variable.
 */
 package main
diff --git a/services/device/deviced/main.go b/services/device/deviced/main.go
index 2e824b2..8da9320 100644
--- a/services/device/deviced/main.go
+++ b/services/device/deviced/main.go
@@ -11,7 +11,8 @@
 	"os"
 	"runtime"
 
-	"v.io/x/lib/cmdline"
+	"v.io/x/lib/cmdline2"
+	"v.io/x/ref/lib/v23cmd"
 )
 
 func main() {
@@ -21,15 +22,15 @@
 		runtime.GOMAXPROCS(runtime.NumCPU())
 	}
 
-	rootCmd := cmdline.Command{
+	rootCmd := &cmdline2.Command{
 		Name:  "deviced",
 		Short: "launch, configure and manage the deviced daemon",
 		Long: `
 Command deviced is used to launch, configure and manage the deviced daemon,
 which implements the v.io/v23/services/device interfaces.
 `,
-		Children: []*cmdline.Command{cmdInstall, cmdUninstall, cmdStart, cmdStop, cmdProfile},
-		Run:      runServer,
+		Children: []*cmdline2.Command{cmdInstall, cmdUninstall, cmdStart, cmdStop, cmdProfile},
+		Runner:   v23cmd.RunnerFunc(runServer),
 	}
-	os.Exit(rootCmd.Main())
+	cmdline2.Main(rootCmd)
 }
diff --git a/services/device/deviced/server.go b/services/device/deviced/server.go
index 61e8773..d90ae68 100644
--- a/services/device/deviced/server.go
+++ b/services/device/deviced/server.go
@@ -19,7 +19,7 @@
 	"v.io/v23/context"
 	"v.io/v23/rpc"
 	"v.io/v23/verror"
-	"v.io/x/lib/cmdline"
+	"v.io/x/lib/cmdline2"
 	"v.io/x/lib/vlog"
 	vexec "v.io/x/ref/lib/exec"
 	"v.io/x/ref/lib/mgmt"
@@ -47,13 +47,10 @@
 )
 
 func init() {
-	cmdline.HideGlobalFlagsExcept(regexp.MustCompile(`^(name)|(restart-exit-code)|(neighborhood-name)|(deviced-port)|(proxy-port)|(use-pairing-token)$`))
+	cmdline2.HideGlobalFlagsExcept(regexp.MustCompile(`^((name)|(restart-exit-code)|(neighborhood-name)|(deviced-port)|(proxy-port)|(use-pairing-token))$`))
 }
 
-func runServer(*cmdline.Command, []string) error {
-	ctx, shutdown := v23.Init()
-	defer shutdown()
-
+func runServer(ctx *context.T, _ *cmdline2.Env, _ []string) error {
 	var testMode bool
 	// If this device manager was started by another device manager, it must
 	// be part of a self update to test that this binary works. In that
@@ -104,7 +101,7 @@
 	dev := starter.DeviceArgs{
 		ConfigState:     configState,
 		TestMode:        testMode,
-		RestartCallback: func() { exitErr = cmdline.ErrExitCode(*restartExitCode) },
+		RestartCallback: func() { exitErr = cmdline2.ErrExitCode(*restartExitCode) },
 		PairingToken:    pairingToken,
 	}
 	if testMode {
diff --git a/services/device/internal/impl/app_service.go b/services/device/internal/impl/app_service.go
index 066b667..9d6ab22 100644
--- a/services/device/internal/impl/app_service.go
+++ b/services/device/internal/impl/app_service.go
@@ -414,7 +414,7 @@
 	installationID := generateID()
 	installationDir := filepath.Join(i.config.Root, applicationDirName(envelope.Title), installationDirName(installationID))
 	deferrer := func() {
-		cleanupDir(installationDir, "")
+		CleanupDir(installationDir, "")
 	}
 	if err := mkdirPerm(installationDir, 0711); err != nil {
 		return "", verror.New(ErrOperationFailed, nil)
@@ -917,12 +917,12 @@
 	helper := i.config.Helper
 	instanceDir, instanceID, err := i.newInstance(ctx, call)
 	if err != nil {
-		cleanupDir(instanceDir, helper)
+		CleanupDir(instanceDir, helper)
 		return "", err
 	}
 	systemName := suidHelper.usernameForPrincipal(ctx, call.Security(), i.uat)
 	if err := saveSystemNameForInstance(instanceDir, systemName); err != nil {
-		cleanupDir(instanceDir, helper)
+		CleanupDir(instanceDir, helper)
 		return "", err
 	}
 	return instanceID, nil
@@ -1101,7 +1101,7 @@
 	}
 	versionDir, err := newVersion(ctx, installationDir, newEnvelope, oldVersionDir)
 	if err != nil {
-		cleanupDir(versionDir, "")
+		CleanupDir(versionDir, "")
 		return err
 	}
 	return nil
diff --git a/services/device/internal/impl/applife/app_life_test.go b/services/device/internal/impl/applife/app_life_test.go
new file mode 100644
index 0000000..f47c6a3
--- /dev/null
+++ b/services/device/internal/impl/applife/app_life_test.go
@@ -0,0 +1,332 @@
+// 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 applife_test
+
+import (
+	"crypto/md5"
+	"encoding/base64"
+	"fmt"
+	"io/ioutil"
+	"path/filepath"
+	"strings"
+	"syscall"
+	"testing"
+	"time"
+
+	"v.io/v23/naming"
+	"v.io/v23/services/device"
+
+	"v.io/x/ref/envvar"
+	"v.io/x/ref/lib/mgmt"
+	"v.io/x/ref/services/device/internal/impl"
+	"v.io/x/ref/services/device/internal/impl/utiltest"
+	"v.io/x/ref/services/internal/servicetest"
+	"v.io/x/ref/test"
+)
+
+func instanceDirForApp(root, appID, instanceID string) string {
+	applicationDirName := func(title string) string {
+		h := md5.New()
+		h.Write([]byte(title))
+		hash := strings.TrimRight(base64.URLEncoding.EncodeToString(h.Sum(nil)), "=")
+		return "app-" + hash
+	}
+	components := strings.Split(appID, "/")
+	appTitle, installationID := components[0], components[1]
+	return filepath.Join(root, applicationDirName(appTitle), "installation-"+installationID, "instances", "instance-"+instanceID)
+}
+
+func verifyAppWorkspace(t *testing.T, root, appID, instanceID string) {
+	// HACK ALERT: for now, we peek inside the device manager's directory
+	// structure (which ought to be opaque) to check for what the app has
+	// written to its local root.
+	//
+	// TODO(caprita): add support to device manager to browse logs/app local
+	// root.
+	rootDir := filepath.Join(instanceDirForApp(root, appID, instanceID), "root")
+	testFile := filepath.Join(rootDir, "testfile")
+	if read, err := ioutil.ReadFile(testFile); err != nil {
+		t.Fatalf("Failed to read %v: %v", testFile, err)
+	} else if want, got := "goodbye world", string(read); want != got {
+		t.Fatalf("Expected to read %v, got %v instead", want, got)
+	}
+	// END HACK
+}
+
+// TestLifeOfAnApp installs an app, instantiates, runs, kills, and deletes
+// several instances, and performs updates.
+func TestLifeOfAnApp(t *testing.T) {
+	ctx, shutdown := utiltest.InitForTest()
+	defer shutdown()
+
+	sh, deferFn := servicetest.CreateShellAndMountTable(t, ctx, nil)
+	defer deferFn()
+
+	// Set up mock application and binary repositories.
+	envelope, cleanup := utiltest.StartMockRepos(t, ctx)
+	defer cleanup()
+
+	root, cleanup := servicetest.SetupRootDir(t, "devicemanager")
+	defer cleanup()
+	if err := impl.SaveCreatorInfo(root); err != nil {
+		t.Fatal(err)
+	}
+
+	// Create a script wrapping the test target that implements suidhelper.
+	helperPath := utiltest.GenerateSuidHelperScript(t, root)
+
+	// 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")
+	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)
+
+	// Create an envelope for a first version of the app.
+	*envelope = utiltest.EnvelopeFromShell(sh, []string{utiltest.TestEnvVarName + "=env-val-envelope"}, utiltest.AppCmd, "google naps", 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
+	// config-specified value for origin should override the value in the
+	// Install rpc argument.
+	mtName, ok := sh.GetVar(envvar.NamespacePrefix)
+	if !ok {
+		t.Fatalf("failed to get namespace root var from shell")
+	}
+	// This rooted name should be equivalent to the relative name "ar", but
+	// we want to test that the config override for origin works.
+	rootedAppRepoName := naming.Join(mtName, "ar")
+	appID := utiltest.InstallApp(t, ctx, device.Config{utiltest.TestFlagName: "flag-val-install", mgmt.AppOriginConfigKey: rootedAppRepoName})
+	v1 := utiltest.VerifyState(t, ctx, device.InstallationStateActive, appID)
+	installationDebug := utiltest.Debug(t, ctx, appID)
+	// We spot-check a couple pieces of information we expect in the debug
+	// output.
+	// TODO(caprita): Is there a way to verify more without adding brittle
+	// logic that assumes too much about the format?  This may be one
+	// argument in favor of making the output of Debug a struct instead of
+	// free-form string.
+	if !strings.Contains(installationDebug, fmt.Sprintf("Origin: %v", rootedAppRepoName)) {
+		t.Fatalf("debug response doesn't contain expected info: %v", installationDebug)
+	}
+	if !strings.Contains(installationDebug, "Config: map[random_test_flag:flag-val-install]") {
+		t.Fatalf("debug response doesn't contain expected info: %v", installationDebug)
+	}
+
+	// Start requires the caller to bless the app instance.
+	expectedErr := "bless failed"
+	if _, err := utiltest.LaunchAppImpl(t, ctx, appID, ""); err == nil || err.Error() != expectedErr {
+		t.Fatalf("Start(%v) expected to fail with %v, got %v instead", appID, expectedErr, err)
+	}
+
+	// Start an instance of the app.
+	instance1ID := utiltest.LaunchApp(t, ctx, appID)
+	if v := utiltest.VerifyState(t, ctx, device.InstanceStateRunning, appID, instance1ID); v != v1 {
+		t.Fatalf("Instance version expected to be %v, got %v instead", v1, v)
+	}
+
+	instanceDebug := utiltest.Debug(t, ctx, appID, instance1ID)
+	// Verify the apps default blessings.
+	if !strings.Contains(instanceDebug, fmt.Sprintf("Default Blessings                %s/forapp", test.TestBlessing)) {
+		t.Fatalf("debug response doesn't contain expected info: %v", instanceDebug)
+	}
+
+	// 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]
+
+	// Stop the app instance.
+	utiltest.KillApp(t, ctx, appID, instance1ID)
+	utiltest.VerifyState(t, ctx, device.InstanceStateNotRunning, appID, instance1ID)
+	utiltest.ResolveExpectNotFound(t, ctx, "appV1")
+
+	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 {
+		t.Fatalf("Expected a new endpoint for the app after kill/run")
+	}
+
+	// Start a second instance.
+	instance2ID := utiltest.LaunchApp(t, ctx, appID)
+	pingCh.VerifyPingArgs(t, utiltest.UserName(t), "flag-val-install", "env-val-envelope") // Wait until the app pings us that it's ready.
+
+	// There should be two endpoints mounted as "appV1", one for each
+	// instance of the app.
+	endpoints := utiltest.Resolve(t, ctx, "appV1", 2)
+	v1EP2 := endpoints[0]
+	if endpoints[0] == v1EP1 {
+		v1EP2 = endpoints[1]
+		if v1EP2 == v1EP1 {
+			t.Fatalf("Both endpoints are the same")
+		}
+	} else if endpoints[1] != v1EP1 {
+		t.Fatalf("Second endpoint should have been v1EP1: %v, %v", endpoints, v1EP1)
+	}
+
+	// TODO(caprita): verify various non-standard combinations (kill when
+	// canceled; run while still running).
+
+	// 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 {
+		t.Fatalf("Resolve(%v): want: %v, got %v", "appV1", want, got)
+	}
+
+	// Updating the installation to itself is a no-op.
+	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")
+
+	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", "appV2")
+
+	utiltest.UpdateApp(t, ctx, appID)
+
+	v2 := utiltest.VerifyState(t, ctx, device.InstallationStateActive, appID)
+	if v1 == v2 {
+		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 {
+		t.Fatalf("Resolve(%v): want: %v, got %v", "appV1", want, got)
+	}
+	if v := utiltest.VerifyState(t, ctx, device.InstanceStateRunning, appID, instance2ID); v != v1 {
+		t.Fatalf("Instance version expected to be %v, got %v instead", v1, v)
+	}
+
+	// Resume first instance.
+	utiltest.RunApp(t, ctx, appID, instance1ID)
+	if v := utiltest.VerifyState(t, ctx, device.InstanceStateRunning, appID, instance1ID); v != v1 {
+		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.
+	// 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)
+	if endpoints[0] == v1EP2 {
+		if endpoints[1] == v1EP2 {
+			t.Fatalf("Both endpoints are the same")
+		}
+	} else if endpoints[1] != v1EP2 {
+		t.Fatalf("Second endpoint should have been v1EP2: %v, %v", endpoints, v1EP2)
+	}
+
+	// Trying to update first instance while it's running should fail.
+	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 {
+		t.Fatalf("Resolve(%v): want: %v, got %v", "appV1", want, got)
+	}
+	// Update succeeds now.
+	utiltest.UpdateInstance(t, ctx, appID, instance1ID)
+	if v := utiltest.VerifyState(t, ctx, device.InstanceStateNotRunning, appID, instance1ID); v != v2 {
+		t.Fatalf("Instance version expected to be %v, got %v instead", v2, v)
+	}
+	// 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)
+
+	// Stop first instance.
+	utiltest.TerminateApp(t, ctx, appID, instance1ID)
+	verifyAppWorkspace(t, root, appID, instance1ID)
+	utiltest.ResolveExpectNotFound(t, ctx, "appV2")
+
+	// Start a third instance.
+	instance3ID := utiltest.LaunchApp(t, ctx, appID)
+	if v := utiltest.VerifyState(t, ctx, device.InstanceStateRunning, appID, instance3ID); v != v2 {
+		t.Fatalf("Instance version expected to be %v, got %v instead", v2, v)
+	}
+	// 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)
+
+	// Stop second instance.
+	utiltest.TerminateApp(t, ctx, appID, instance2ID)
+	utiltest.ResolveExpectNotFound(t, ctx, "appV1")
+
+	// Stop third instance.
+	utiltest.TerminateApp(t, ctx, appID, instance3ID)
+	utiltest.ResolveExpectNotFound(t, ctx, "appV2")
+
+	// Revert the app.
+	utiltest.RevertApp(t, ctx, appID)
+	if v := utiltest.VerifyState(t, ctx, device.InstallationStateActive, appID); v != v1 {
+		t.Fatalf("Installation version expected to be %v, got %v instead", v1, v)
+	}
+
+	// Start a fourth instance.  It should be running from version 1.
+	instance4ID := utiltest.LaunchApp(t, ctx, appID)
+	if v := utiltest.VerifyState(t, ctx, device.InstanceStateRunning, appID, instance4ID); v != v1 {
+		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.TerminateApp(t, ctx, appID, instance4ID)
+	utiltest.ResolveExpectNotFound(t, ctx, "appV1")
+
+	// We are already on the first version, no further revert possible.
+	utiltest.RevertAppExpectError(t, ctx, appID, impl.ErrUpdateNoOp.ID)
+
+	// Uninstall the app.
+	utiltest.UninstallApp(t, ctx, appID)
+	utiltest.VerifyState(t, ctx, device.InstallationStateUninstalled, appID)
+
+	// Updating the installation should no longer be allowed.
+	utiltest.UpdateAppExpectError(t, ctx, appID, impl.ErrInvalidOperation.ID)
+
+	// Reverting the installation should no longer be allowed.
+	utiltest.RevertAppExpectError(t, ctx, appID, impl.ErrInvalidOperation.ID)
+
+	// Starting new instances should no longer be allowed.
+	utiltest.LaunchAppExpectError(t, ctx, appID, impl.ErrInvalidOperation.ID)
+
+	// Make sure that Kill will actually kill an app that doesn't exit
+	// 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", "hAppV1")
+	hAppID := utiltest.InstallApp(t, ctx)
+	hInstanceID := utiltest.LaunchApp(t, ctx, hAppID)
+	hangingPid := pingCh.WaitForPingArgs(t).Pid
+	if err := syscall.Kill(hangingPid, 0); err != nil && err != syscall.EPERM {
+		t.Fatalf("Pid of hanging app (%v) is not live", hangingPid)
+	}
+	utiltest.KillApp(t, ctx, hAppID, hInstanceID)
+	pidIsAlive := true
+	for i := 0; i < 10 && pidIsAlive; i++ {
+		if err := syscall.Kill(hangingPid, 0); err == nil || err == syscall.EPERM {
+			time.Sleep(time.Second) // pid is still alive
+		} else {
+			pidIsAlive = false
+		}
+	}
+	if pidIsAlive {
+		t.Fatalf("Pid of hanging app (%d) has not exited after Stop() call", hangingPid)
+	}
+
+	// Cleanly shut down the device manager.
+	defer utiltest.VerifyNoRunningProcesses(t)
+	syscall.Kill(dmh.Pid(), syscall.SIGINT)
+	dmh.Expect("dm terminated")
+	dmh.ExpectEOF()
+}
diff --git a/services/device/internal/impl/args_darwin_test.go b/services/device/internal/impl/applife/doc.go
similarity index 65%
copy from services/device/internal/impl/args_darwin_test.go
copy to services/device/internal/impl/applife/doc.go
index c1d0b11..e1b872a 100644
--- a/services/device/internal/impl/args_darwin_test.go
+++ b/services/device/internal/impl/applife/doc.go
@@ -2,9 +2,6 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package impl_test
+package applife
 
-const (
-	testUserName        = "_uucp"
-	anotherTestUserName = "_lp"
-)
+// Test code for application life cycle and reap reconcilliation.
diff --git a/services/device/internal/impl/applife/impl_test.go b/services/device/internal/impl/applife/impl_test.go
new file mode 100644
index 0000000..bceee06
--- /dev/null
+++ b/services/device/internal/impl/applife/impl_test.go
@@ -0,0 +1,19 @@
+// 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 applife_test
+
+import (
+	"testing"
+
+	"v.io/x/ref/services/device/internal/impl/utiltest"
+)
+
+func TestMain(m *testing.M) {
+	utiltest.TestMainImpl(m)
+}
+
+func TestSuidHelper(t *testing.T) {
+	utiltest.TestSuidHelperImpl(t)
+}
diff --git a/services/device/internal/impl/instance_reaping_test.go b/services/device/internal/impl/applife/instance_reaping_test.go
similarity index 98%
rename from services/device/internal/impl/instance_reaping_test.go
rename to services/device/internal/impl/applife/instance_reaping_test.go
index 39e74e8..ef023ca 100644
--- a/services/device/internal/impl/instance_reaping_test.go
+++ b/services/device/internal/impl/applife/instance_reaping_test.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package impl_test
+package applife_test
 
 import (
 	"syscall"
diff --git a/services/device/internal/impl/device_service.go b/services/device/internal/impl/device_service.go
index 1678fb8..4b612d1 100644
--- a/services/device/internal/impl/device_service.go
+++ b/services/device/internal/impl/device_service.go
@@ -212,7 +212,7 @@
 func savePersistentArgs(root string, args []string) error {
 	dir := filepath.Join(root, "device-manager", "device-data")
 	if err := os.MkdirAll(dir, 0700); err != nil {
-		return fmt.Errorf("MkdirAll(%q) failed: %v", dir)
+		return fmt.Errorf("MkdirAll(%q) failed: %v", dir, err)
 	}
 	data, err := json.Marshal(args)
 	if err != nil {
@@ -322,14 +322,14 @@
 		defer os.Remove(fName)
 		*v = file
 
-		defer func() {
+		defer func(k string) {
 			if f, err := os.Open(fName); err == nil {
 				scanner := bufio.NewScanner(f)
 				for scanner.Scan() {
 					vlog.Infof("[testDeviceManager %s] %s", k, scanner.Text())
 				}
 			}
-		}()
+		}(k)
 	}
 
 	// Setup up the child process callback.
@@ -504,7 +504,7 @@
 	}
 
 	deferrer := func() {
-		cleanupDir(workspace, "")
+		CleanupDir(workspace, "")
 	}
 	defer func() {
 		if deferrer != nil {
diff --git a/services/device/internal/impl/args_darwin_test.go b/services/device/internal/impl/globsuid/args_darwin_test.go
similarity index 91%
rename from services/device/internal/impl/args_darwin_test.go
rename to services/device/internal/impl/globsuid/args_darwin_test.go
index c1d0b11..8925295 100644
--- a/services/device/internal/impl/args_darwin_test.go
+++ b/services/device/internal/impl/globsuid/args_darwin_test.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package impl_test
+package globsuid_test
 
 const (
 	testUserName        = "_uucp"
diff --git a/services/device/internal/impl/args_linux_test.go b/services/device/internal/impl/globsuid/args_linux_test.go
similarity index 91%
rename from services/device/internal/impl/args_linux_test.go
rename to services/device/internal/impl/globsuid/args_linux_test.go
index dc9a365..e7e4aa6 100644
--- a/services/device/internal/impl/args_linux_test.go
+++ b/services/device/internal/impl/globsuid/args_linux_test.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package impl_test
+package globsuid_test
 
 const (
 	testUserName        = "uucp"
diff --git a/services/device/internal/impl/globsuid/doc.go b/services/device/internal/impl/globsuid/doc.go
new file mode 100644
index 0000000..4e210e6
--- /dev/null
+++ b/services/device/internal/impl/globsuid/doc.go
@@ -0,0 +1,8 @@
+// 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 globsuid
+
+// Test code for the device manager's globbing, suidhelper and
+// signature matching functionality.
diff --git a/services/device/internal/impl/globsuid/glob_test.go b/services/device/internal/impl/globsuid/glob_test.go
new file mode 100644
index 0000000..f9becde
--- /dev/null
+++ b/services/device/internal/impl/globsuid/glob_test.go
@@ -0,0 +1,112 @@
+// 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 globsuid_test
+
+import (
+	"path"
+	"syscall"
+	"testing"
+
+	"v.io/x/ref/services/device/internal/impl"
+	"v.io/x/ref/services/device/internal/impl/utiltest"
+	"v.io/x/ref/services/internal/servicetest"
+)
+
+func TestDeviceManagerGlobAndDebug(t *testing.T) {
+	ctx, shutdown := utiltest.InitForTest()
+	defer shutdown()
+
+	sh, deferFn := servicetest.CreateShellAndMountTable(t, ctx, nil)
+	defer deferFn()
+
+	// Set up mock application and binary repositories.
+	envelope, cleanup := utiltest.StartMockRepos(t, ctx)
+	defer cleanup()
+
+	root, cleanup := servicetest.SetupRootDir(t, "devicemanager")
+	defer cleanup()
+	if err := impl.SaveCreatorInfo(root); err != nil {
+		t.Fatal(err)
+	}
+
+	// Create a script wrapping the test target that implements suidhelper.
+	helperPath := utiltest.GenerateSuidHelperScript(t, root)
+
+	// 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")
+	pid := servicetest.ReadPID(t, dmh)
+	defer syscall.Kill(pid, syscall.SIGINT)
+
+	// Create the local server that the app uses to let us know it's ready.
+	pingCh, cleanup := utiltest.SetupPingServer(t, ctx)
+	defer cleanup()
+
+	// Create the envelope for the first version of the app.
+	*envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.AppCmd, "google naps", "appV1")
+
+	// Device must be claimed before applications can be installed.
+	utiltest.ClaimDevice(t, ctx, "claimable", "dm", "mydevice", utiltest.NoPairingToken)
+	// Install the app.
+	appID := utiltest.InstallApp(t, ctx)
+	install1ID := path.Base(appID)
+
+	// Start an instance of the app.
+	instance1ID := utiltest.LaunchApp(t, ctx, appID)
+	defer utiltest.TerminateApp(t, ctx, appID, instance1ID)
+
+	// Wait until the app pings us that it's ready.
+	pingCh.WaitForPingArgs(t)
+
+	app2ID := utiltest.InstallApp(t, ctx)
+	install2ID := path.Base(app2ID)
+
+	// Base name of argv[0] that the app should have when it executes
+	// It will be path.Base(envelope.Title + "@" + envelope.Binary.File + "/app").
+	// Note the suffix, which ensures that the result is always "app" at the moment.
+	// Someday in future we may remove that and have binary names that reflect the app name.
+	const appName = "app"
+
+	testcases := []utiltest.GlobTestVector{
+		{"dm", "...", []string{
+			"",
+			"apps",
+			"apps/google naps",
+			"apps/google naps/" + install1ID,
+			"apps/google naps/" + install1ID + "/" + instance1ID,
+			"apps/google naps/" + install1ID + "/" + instance1ID + "/logs",
+			"apps/google naps/" + install1ID + "/" + instance1ID + "/logs/STDERR-<timestamp>",
+			"apps/google naps/" + install1ID + "/" + instance1ID + "/logs/STDOUT-<timestamp>",
+			"apps/google naps/" + install1ID + "/" + instance1ID + "/logs/" + appName + ".INFO",
+			"apps/google naps/" + install1ID + "/" + instance1ID + "/logs/" + appName + ".<*>.INFO.<timestamp>",
+			"apps/google naps/" + install1ID + "/" + instance1ID + "/pprof",
+			"apps/google naps/" + install1ID + "/" + instance1ID + "/stats",
+			"apps/google naps/" + install1ID + "/" + instance1ID + "/stats/rpc",
+			"apps/google naps/" + install1ID + "/" + instance1ID + "/stats/system",
+			"apps/google naps/" + install1ID + "/" + instance1ID + "/stats/system/start-time-rfc1123",
+			"apps/google naps/" + install1ID + "/" + instance1ID + "/stats/system/start-time-unix",
+			"apps/google naps/" + install2ID,
+			"device",
+		}},
+		{"dm/apps", "*", []string{"google naps"}},
+		{"dm/apps/google naps", "*", []string{install1ID, install2ID}},
+		{"dm/apps/google naps/" + install1ID, "*", []string{instance1ID}},
+		{"dm/apps/google naps/" + install1ID + "/" + instance1ID, "*", []string{"logs", "pprof", "stats"}},
+		{"dm/apps/google naps/" + install1ID + "/" + instance1ID + "/logs", "*", []string{
+			"STDERR-<timestamp>",
+			"STDOUT-<timestamp>",
+			appName + ".INFO",
+			appName + ".<*>.INFO.<timestamp>",
+		}},
+		{"dm/apps/google naps/" + install1ID + "/" + instance1ID + "/stats/system", "start-time*", []string{"start-time-rfc1123", "start-time-unix"}},
+	}
+
+	res := utiltest.NewGlobTestRegexHelper(appName)
+
+	utiltest.VerifyGlob(t, ctx, appName, testcases, res)
+	utiltest.VerifyLog(t, ctx, "dm", "apps/google naps", install1ID, instance1ID, "logs", "*")
+	utiltest.VerifyStatsValues(t, ctx, "dm", "apps/google naps", install1ID, instance1ID, "stats/system/start-time*")
+	utiltest.VerifyPProfCmdLine(t, ctx, appName, "dm", "apps/google naps", install1ID, instance1ID, "pprof")
+}
diff --git a/services/device/internal/impl/globsuid/impl_test.go b/services/device/internal/impl/globsuid/impl_test.go
new file mode 100644
index 0000000..820a15e
--- /dev/null
+++ b/services/device/internal/impl/globsuid/impl_test.go
@@ -0,0 +1,19 @@
+// 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 globsuid_test
+
+import (
+	"testing"
+
+	"v.io/x/ref/services/device/internal/impl/utiltest"
+)
+
+func TestMain(m *testing.M) {
+	utiltest.TestMainImpl(m)
+}
+
+func TestSuidHelper(t *testing.T) {
+	utiltest.TestSuidHelperImpl(t)
+}
diff --git a/services/device/internal/impl/globsuid/signature_match_test.go b/services/device/internal/impl/globsuid/signature_match_test.go
new file mode 100644
index 0000000..ded825f
--- /dev/null
+++ b/services/device/internal/impl/globsuid/signature_match_test.go
@@ -0,0 +1,143 @@
+// 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 globsuid_test
+
+import (
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"syscall"
+	"testing"
+
+	"v.io/v23"
+	"v.io/v23/naming"
+	"v.io/v23/services/application"
+	"v.io/v23/services/device"
+	"v.io/v23/services/repository"
+	"v.io/v23/verror"
+
+	"v.io/x/ref/services/device/internal/impl"
+	"v.io/x/ref/services/device/internal/impl/utiltest"
+	"v.io/x/ref/services/internal/binarylib"
+	"v.io/x/ref/services/internal/servicetest"
+	"v.io/x/ref/test/testutil"
+)
+
+func TestDownloadSignatureMatch(t *testing.T) {
+	ctx, shutdown := utiltest.InitForTest()
+	defer shutdown()
+
+	sh, deferFn := servicetest.CreateShellAndMountTable(t, ctx, nil)
+	defer deferFn()
+
+	binaryVON := "binary"
+	pkgVON := naming.Join(binaryVON, "testpkg")
+	defer utiltest.StartRealBinaryRepository(t, ctx, binaryVON)()
+
+	up := testutil.RandomBytes(testutil.Intn(5 << 20))
+	mediaInfo := repository.MediaInfo{Type: "application/octet-stream"}
+	sig, err := binarylib.Upload(ctx, naming.Join(binaryVON, "testbinary"), up, mediaInfo)
+	if err != nil {
+		t.Fatalf("Upload(%v) failed:%v", binaryVON, err)
+	}
+
+	// Upload packages for this application
+	tmpdir, err := ioutil.TempDir("", "test-package-")
+	if err != nil {
+		t.Fatalf("ioutil.TempDir failed: %v", err)
+	}
+	defer os.RemoveAll(tmpdir)
+	pkgContents := testutil.RandomBytes(testutil.Intn(5 << 20))
+	if err := ioutil.WriteFile(filepath.Join(tmpdir, "pkg.txt"), pkgContents, 0600); err != nil {
+		t.Fatalf("ioutil.WriteFile failed: %v", err)
+	}
+	pkgSig, err := binarylib.UploadFromDir(ctx, pkgVON, tmpdir)
+	if err != nil {
+		t.Fatalf("binarylib.UploadFromDir failed: %v", err)
+	}
+
+	// Start the application repository
+	envelope, serverStop := utiltest.StartApplicationRepository(ctx)
+	defer serverStop()
+
+	root, cleanup := servicetest.SetupRootDir(t, "devicemanager")
+	defer cleanup()
+	if err := impl.SaveCreatorInfo(root); err != nil {
+		t.Fatal(err)
+	}
+
+	// Create a script wrapping the test target that implements suidhelper.
+	helperPath := utiltest.GenerateSuidHelperScript(t, root)
+
+	// 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")
+	pid := servicetest.ReadPID(t, dmh)
+	defer syscall.Kill(pid, syscall.SIGINT)
+	utiltest.ClaimDevice(t, ctx, "claimable", "dm", "mydevice", utiltest.NoPairingToken)
+
+	publisher, err := v23.GetPrincipal(ctx).BlessSelf("publisher")
+	if err != nil {
+		t.Fatalf("Failed to generate publisher blessings:%v", err)
+	}
+	*envelope = application.Envelope{
+		Binary: application.SignedFile{
+			File:      naming.Join(binaryVON, "testbinary"),
+			Signature: *sig,
+		},
+		Publisher: publisher,
+		Packages: map[string]application.SignedFile{
+			"pkg": application.SignedFile{
+				File:      pkgVON,
+				Signature: *pkgSig,
+			},
+		},
+	}
+	if _, err := utiltest.AppStub().Install(ctx, utiltest.MockApplicationRepoName, device.Config{}, nil); err != nil {
+		t.Fatalf("Failed to Install app:%v", err)
+	}
+
+	// Verify that when the binary is corrupted, signature verification fails.
+	up[0] = up[0] ^ 0xFF
+	if err := binarylib.Delete(ctx, naming.Join(binaryVON, "testbinary")); err != nil {
+		t.Fatalf("Delete(%v) failed:%v", binaryVON, err)
+	}
+	if _, err := binarylib.Upload(ctx, naming.Join(binaryVON, "testbinary"), up, mediaInfo); err != nil {
+		t.Fatalf("Upload(%v) failed:%v", binaryVON, err)
+	}
+	if _, err := utiltest.AppStub().Install(ctx, utiltest.MockApplicationRepoName, device.Config{}, nil); verror.ErrorID(err) != impl.ErrOperationFailed.ID {
+		t.Fatalf("Failed to verify signature mismatch for binary:%v. Got errorid=%v[%v], want errorid=%v", binaryVON, verror.ErrorID(err), err, impl.ErrOperationFailed.ID)
+	}
+
+	// Restore the binary and verify that installation succeeds.
+	up[0] = up[0] ^ 0xFF
+	if err := binarylib.Delete(ctx, naming.Join(binaryVON, "testbinary")); err != nil {
+		t.Fatalf("Delete(%v) failed:%v", binaryVON, err)
+	}
+	if _, err := binarylib.Upload(ctx, naming.Join(binaryVON, "testbinary"), up, mediaInfo); err != nil {
+		t.Fatalf("Upload(%v) failed:%v", binaryVON, err)
+	}
+	if _, err := utiltest.AppStub().Install(ctx, utiltest.MockApplicationRepoName, device.Config{}, nil); err != nil {
+		t.Fatalf("Failed to Install app:%v", err)
+	}
+
+	// Verify that when the package contents are corrupted, signature verification fails.
+	pkgContents[0] = pkgContents[0] ^ 0xFF
+	if err := binarylib.Delete(ctx, pkgVON); err != nil {
+		t.Fatalf("Delete(%v) failed:%v", pkgVON, err)
+	}
+	if err := os.Remove(filepath.Join(tmpdir, "pkg.txt")); err != nil {
+		t.Fatalf("Remove(%v) failed:%v", filepath.Join(tmpdir, "pkg.txt"), err)
+	}
+	if err := ioutil.WriteFile(filepath.Join(tmpdir, "pkg.txt"), pkgContents, 0600); err != nil {
+		t.Fatalf("ioutil.WriteFile failed: %v", err)
+	}
+	if _, err = binarylib.UploadFromDir(ctx, pkgVON, tmpdir); err != nil {
+		t.Fatalf("binarylib.UploadFromDir failed: %v", err)
+	}
+	if _, err := utiltest.AppStub().Install(ctx, utiltest.MockApplicationRepoName, device.Config{}, nil); verror.ErrorID(err) != impl.ErrOperationFailed.ID {
+		t.Fatalf("Failed to verify signature mismatch for package:%v", pkgVON)
+	}
+}
diff --git a/services/device/internal/impl/globsuid/suid_test.go b/services/device/internal/impl/globsuid/suid_test.go
new file mode 100644
index 0000000..83ae185
--- /dev/null
+++ b/services/device/internal/impl/globsuid/suid_test.go
@@ -0,0 +1,203 @@
+// 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 globsuid_test
+
+import (
+	"flag"
+	"fmt"
+	"os"
+	"reflect"
+	"syscall"
+	"testing"
+
+	"v.io/x/lib/vlog"
+
+	"v.io/v23"
+	"v.io/v23/security"
+	"v.io/v23/security/access"
+	"v.io/v23/services/device"
+	"v.io/v23/verror"
+
+	"v.io/x/ref/services/device/internal/impl"
+	"v.io/x/ref/services/device/internal/impl/utiltest"
+	"v.io/x/ref/services/internal/servicetest"
+	"v.io/x/ref/test/testutil"
+)
+
+var mockIsSetuid = flag.Bool("mocksetuid", false, "set flag to pretend to have a helper with setuid permissions")
+
+func possiblyMockIsSetuid(fileStat os.FileInfo) bool {
+	vlog.VI(2).Infof("Mock isSetuid is reporting: %v", *mockIsSetuid)
+	return *mockIsSetuid
+}
+
+func init() {
+	impl.IsSetuid = possiblyMockIsSetuid
+}
+
+func TestAppWithSuidHelper(t *testing.T) {
+	ctx, shutdown := utiltest.InitForTest()
+	defer shutdown()
+
+	// Identity provider used to ensure that all processes recognize each
+	// others' blessings.
+	idp := testutil.NewIDProvider("root")
+	if err := idp.Bless(v23.GetPrincipal(ctx), "self"); err != nil {
+		t.Fatal(err)
+	}
+
+	sh, deferFn := servicetest.CreateShellAndMountTable(t, ctx, nil)
+	defer deferFn()
+
+	// Set up mock application and binary repositories.
+	envelope, cleanup := utiltest.StartMockRepos(t, ctx)
+	defer cleanup()
+
+	root, cleanup := servicetest.SetupRootDir(t, "devicemanager")
+	defer cleanup()
+	if err := impl.SaveCreatorInfo(root); err != nil {
+		t.Fatal(err)
+	}
+
+	selfCtx := ctx
+	otherCtx := utiltest.CtxWithNewPrincipal(t, selfCtx, idp, "other")
+
+	// 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")
+	pid := servicetest.ReadPID(t, dmh)
+	defer syscall.Kill(pid, syscall.SIGINT)
+	defer utiltest.VerifyNoRunningProcesses(t)
+	// Claim the devicemanager with selfCtx as root/self/alice
+	utiltest.ClaimDevice(t, selfCtx, "claimable", "dm", "alice", utiltest.NoPairingToken)
+
+	deviceStub := device.DeviceClient("dm/device")
+
+	// Create the local server that the app uses to tell us which system
+	// name the device manager wished to run it as.
+	pingCh, cleanup := utiltest.SetupPingServer(t, ctx)
+	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", fmt.Sprintf("--%s=flag-val-envelope", utiltest.TestFlagName), "appV1")
+
+	// Install and start the app as root/self.
+	appID := utiltest.InstallApp(t, selfCtx)
+
+	vlog.VI(2).Infof("Validate that the created app has the right permission lists.")
+	perms, _, err := utiltest.AppStub(appID).GetPermissions(selfCtx)
+	if err != nil {
+		t.Fatalf("GetPermissions on appID: %v failed %v", appID, err)
+	}
+	expected := make(access.Permissions)
+	for _, tag := range access.AllTypicalTags() {
+		expected[string(tag)] = access.AccessList{In: []security.BlessingPattern{"root/self/$"}}
+	}
+	if got, want := perms.Normalize(), expected.Normalize(); !reflect.DeepEqual(got, want) {
+		t.Errorf("got %#v, expected %#v", got, want)
+	}
+
+	// Start an instance of the app but this time it should fail: we do not
+	// have an associated uname for the invoking identity.
+	utiltest.LaunchAppExpectError(t, selfCtx, appID, verror.ErrNoAccess.ID)
+
+	// Create an association for selfCtx
+	if err := deviceStub.AssociateAccount(selfCtx, []string{"root/self"}, testUserName); err != nil {
+		t.Fatalf("AssociateAccount failed %v", err)
+	}
+
+	instance1ID := utiltest.LaunchApp(t, selfCtx, appID)
+	pingCh.VerifyPingArgs(t, testUserName, "flag-val-envelope", "env-var") // Wait until the app pings us that it's ready.
+	utiltest.TerminateApp(t, selfCtx, appID, instance1ID)
+
+	vlog.VI(2).Infof("other attempting to run an app without access. Should fail.")
+	utiltest.LaunchAppExpectError(t, otherCtx, appID, verror.ErrNoAccess.ID)
+
+	// Self will now let other also install apps.
+	if err := deviceStub.AssociateAccount(selfCtx, []string{"root/other"}, testUserName); err != nil {
+		t.Fatalf("AssociateAccount failed %v", err)
+	}
+	// Add Start to the AccessList list for root/other.
+	newAccessList, _, err := deviceStub.GetPermissions(selfCtx)
+	if err != nil {
+		t.Fatalf("GetPermissions failed %v", err)
+	}
+	newAccessList.Add("root/other", string(access.Write))
+	if err := deviceStub.SetPermissions(selfCtx, newAccessList, ""); err != nil {
+		t.Fatalf("SetPermissions failed %v", err)
+	}
+
+	// With the introduction of per installation and per instance AccessLists,
+	// while other now has administrator permissions on the device manager,
+	// other doesn't have execution permissions for the app. So this will
+	// fail.
+	vlog.VI(2).Infof("other attempting to run an app still without access. Should fail.")
+	utiltest.LaunchAppExpectError(t, otherCtx, appID, verror.ErrNoAccess.ID)
+
+	// But self can give other permissions  to start applications.
+	vlog.VI(2).Infof("self attempting to give other permission to start %s", appID)
+	newAccessList, _, err = utiltest.AppStub(appID).GetPermissions(selfCtx)
+	if err != nil {
+		t.Fatalf("GetPermissions on appID: %v failed %v", appID, err)
+	}
+	newAccessList.Add("root/other", string(access.Read))
+	if err = utiltest.AppStub(appID).SetPermissions(selfCtx, newAccessList, ""); err != nil {
+		t.Fatalf("SetPermissions on appID: %v failed: %v", appID, err)
+	}
+
+	vlog.VI(2).Infof("other attempting to run an app with access. Should succeed.")
+	instance2ID := utiltest.LaunchApp(t, otherCtx, appID)
+	pingCh.VerifyPingArgs(t, testUserName, "flag-val-envelope", "env-var") // Wait until the app pings us that it's ready.
+
+	vlog.VI(2).Infof("Validate that created instance has the right permissions.")
+	expected = make(access.Permissions)
+	for _, tag := range access.AllTypicalTags() {
+		expected[string(tag)] = access.AccessList{In: []security.BlessingPattern{"root/other/$"}}
+	}
+	perms, _, err = utiltest.AppStub(appID, instance2ID).GetPermissions(selfCtx)
+	if err != nil {
+		t.Fatalf("GetPermissions on instance %v/%v failed: %v", appID, instance2ID, err)
+	}
+	if got, want := perms.Normalize(), expected.Normalize(); !reflect.DeepEqual(got, want) {
+		t.Errorf("got %#v, expected %#v ", got, want)
+	}
+
+	// Shutdown the app.
+	utiltest.KillApp(t, otherCtx, appID, instance2ID)
+
+	vlog.VI(2).Infof("Verify that Run with the same systemName works.")
+	utiltest.RunApp(t, otherCtx, appID, instance2ID)
+	pingCh.VerifyPingArgs(t, testUserName, "flag-val-envelope", "env-var") // Wait until the app pings us that it's ready.
+	utiltest.KillApp(t, otherCtx, appID, instance2ID)
+
+	vlog.VI(2).Infof("Verify that other can install and run applications.")
+	otherAppID := utiltest.InstallApp(t, otherCtx)
+
+	vlog.VI(2).Infof("other attempting to run an app that other installed. Should succeed.")
+	instance4ID := utiltest.LaunchApp(t, otherCtx, otherAppID)
+	pingCh.VerifyPingArgs(t, testUserName, "flag-val-envelope", "env-var") // Wait until the app pings us that it's ready.
+
+	// Clean up.
+	utiltest.TerminateApp(t, otherCtx, otherAppID, instance4ID)
+
+	// Change the associated system name.
+	if err := deviceStub.AssociateAccount(selfCtx, []string{"root/other"}, anotherTestUserName); err != nil {
+		t.Fatalf("AssociateAccount failed %v", err)
+	}
+
+	vlog.VI(2).Infof("Show that Run with a different systemName fails.")
+	utiltest.RunAppExpectError(t, otherCtx, appID, instance2ID, verror.ErrNoAccess.ID)
+
+	// Clean up.
+	utiltest.DeleteApp(t, otherCtx, appID, instance2ID)
+
+	vlog.VI(2).Infof("Show that Start with different systemName works.")
+	instance3ID := utiltest.LaunchApp(t, otherCtx, appID)
+	pingCh.VerifyPingArgs(t, anotherTestUserName, "flag-val-envelope", "env-var") // Wait until the app pings us that it's ready.
+
+	// Clean up.
+	utiltest.TerminateApp(t, otherCtx, appID, instance3ID)
+}
diff --git a/services/device/internal/impl/helper_manager.go b/services/device/internal/impl/helper_manager.go
index 280db2b..a0528e0 100644
--- a/services/device/internal/impl/helper_manager.go
+++ b/services/device/internal/impl/helper_manager.go
@@ -135,9 +135,9 @@
 	return nil
 }
 
-// isSetuid is defined like this so we can override its
+// IsSetuid is defined like this so we can override its
 // implementation for tests.
-var isSetuid = func(fileStat os.FileInfo) bool {
+var IsSetuid = func(fileStat os.FileInfo) bool {
 	vlog.VI(2).Infof("running the original isSetuid")
 	return fileStat.Mode()&os.ModeSetuid == os.ModeSetuid
 }
@@ -152,7 +152,7 @@
 	if err != nil {
 		return false, verror.New(ErrOperationFailed, nil, fmt.Sprintf("Stat(%v) failed: %v. helper is required.", s.helperPath, err))
 	}
-	haveHelper := isSetuid(helperStat)
+	haveHelper := IsSetuid(helperStat)
 
 	switch {
 	case haveHelper && s.dmUser != targetUser:
@@ -162,8 +162,6 @@
 	default:
 		return false, nil
 	}
-	// Keep the compiler happy.
-	return false, nil
 }
 
 // usernameForPrincipal returns the system name that the
diff --git a/services/device/internal/impl/impl_helper_test.go b/services/device/internal/impl/impl_helper_test.go
index 4eed0cb..1ee97bf 100644
--- a/services/device/internal/impl/impl_helper_test.go
+++ b/services/device/internal/impl/impl_helper_test.go
@@ -38,12 +38,12 @@
 	// Setup a helper.
 	helper := utiltest.GenerateSuidHelperScript(t, dir)
 
-	impl.WrapBaseCleanupDir(helperTarget, helper)
+	impl.BaseCleanupDir(helperTarget, helper)
 	if _, err := os.Stat(helperTarget); err == nil || os.IsExist(err) {
 		t.Fatalf("%s should be missing but isn't", helperTarget)
 	}
 
-	impl.WrapBaseCleanupDir(nohelperTarget, "")
+	impl.BaseCleanupDir(nohelperTarget, "")
 	if _, err := os.Stat(nohelperTarget); err == nil || os.IsExist(err) {
 		t.Fatalf("%s should be missing but isn't", nohelperTarget)
 	}
diff --git a/services/device/internal/impl/impl_test.go b/services/device/internal/impl/impl_test.go
index babe024..87b4cd9 100644
--- a/services/device/internal/impl/impl_test.go
+++ b/services/device/internal/impl/impl_test.go
@@ -2,48 +2,33 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// TODO(caprita): This file is becoming unmanageable; split into several test
-// files.
 // TODO(rjkroege): Add a more extensive unit test case to exercise AccessList logic.
 
 package impl_test
 
 import (
-	"bytes"
-	"crypto/md5"
-	"encoding/base64"
-	"encoding/hex"
 	"fmt"
 	"io/ioutil"
 	"os"
-	"path"
 	"path/filepath"
-	"reflect"
 	"strings"
 	"syscall"
 	"testing"
-	"time"
 
 	"v.io/x/lib/vlog"
 
 	"v.io/v23"
 	"v.io/v23/context"
 	"v.io/v23/naming"
-	"v.io/v23/security"
-	"v.io/v23/security/access"
 	"v.io/v23/services/application"
 	"v.io/v23/services/device"
-	"v.io/v23/services/repository"
-	"v.io/v23/verror"
 
 	"v.io/x/ref/envvar"
-	"v.io/x/ref/lib/mgmt"
 	"v.io/x/ref/services/device/internal/config"
 	"v.io/x/ref/services/device/internal/impl"
 	"v.io/x/ref/services/device/internal/impl/utiltest"
 	"v.io/x/ref/services/internal/binarylib"
 	"v.io/x/ref/services/internal/servicetest"
-	"v.io/x/ref/test"
 	"v.io/x/ref/test/expect"
 	"v.io/x/ref/test/testutil"
 )
@@ -83,16 +68,6 @@
 	return path
 }
 
-func initForTest() (*context.T, v23.Shutdown) {
-	roots, _ := envvar.NamespaceRoots()
-	for key, _ := range roots {
-		os.Unsetenv(key)
-	}
-	ctx, shutdown := test.InitForTest()
-	v23.GetNamespace(ctx).CacheCtl(naming.DisableCache(true))
-	return ctx, shutdown
-}
-
 // TestDeviceManagerUpdateAndRevert makes the device manager go through the
 // motions of updating itself to newer versions (twice), and reverting itself
 // back (twice). It also checks that update and revert fail when they're
@@ -100,7 +75,7 @@
 // command. Further versions are running through the soft link that the device
 // manager itself updates.
 func TestDeviceManagerUpdateAndRevert(t *testing.T) {
-	ctx, shutdown := initForTest()
+	ctx, shutdown := utiltest.InitForTest()
 	defer shutdown()
 
 	sh, deferFn := servicetest.CreateShellAndMountTable(t, ctx, v23.GetPrincipal(ctx))
@@ -292,505 +267,6 @@
 	dmh.ExpectEOF()
 }
 
-func instanceDirForApp(root, appID, instanceID string) string {
-	applicationDirName := func(title string) string {
-		h := md5.New()
-		h.Write([]byte(title))
-		hash := strings.TrimRight(base64.URLEncoding.EncodeToString(h.Sum(nil)), "=")
-		return "app-" + hash
-	}
-	components := strings.Split(appID, "/")
-	appTitle, installationID := components[0], components[1]
-	return filepath.Join(root, applicationDirName(appTitle), "installation-"+installationID, "instances", "instance-"+instanceID)
-}
-
-func verifyAppWorkspace(t *testing.T, root, appID, instanceID string) {
-	// HACK ALERT: for now, we peek inside the device manager's directory
-	// structure (which ought to be opaque) to check for what the app has
-	// written to its local root.
-	//
-	// TODO(caprita): add support to device manager to browse logs/app local
-	// root.
-	rootDir := filepath.Join(instanceDirForApp(root, appID, instanceID), "root")
-	testFile := filepath.Join(rootDir, "testfile")
-	if read, err := ioutil.ReadFile(testFile); err != nil {
-		t.Fatalf("Failed to read %v: %v", testFile, err)
-	} else if want, got := "goodbye world", string(read); want != got {
-		t.Fatalf("Expected to read %v, got %v instead", want, got)
-	}
-	// END HACK
-}
-
-// TestLifeOfAnApp installs an app, instantiates, runs, kills, and deletes
-// several instances, and performs updates.
-func TestLifeOfAnApp(t *testing.T) {
-	ctx, shutdown := initForTest()
-	defer shutdown()
-
-	sh, deferFn := servicetest.CreateShellAndMountTable(t, ctx, nil)
-	defer deferFn()
-
-	// Set up mock application and binary repositories.
-	envelope, cleanup := utiltest.StartMockRepos(t, ctx)
-	defer cleanup()
-
-	root, cleanup := servicetest.SetupRootDir(t, "devicemanager")
-	defer cleanup()
-	if err := impl.SaveCreatorInfo(root); err != nil {
-		t.Fatal(err)
-	}
-
-	// Create a script wrapping the test target that implements suidhelper.
-	helperPath := utiltest.GenerateSuidHelperScript(t, root)
-
-	// 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")
-	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)
-
-	// Create an envelope for a first version of the app.
-	*envelope = utiltest.EnvelopeFromShell(sh, []string{utiltest.TestEnvVarName + "=env-val-envelope"}, utiltest.AppCmd, "google naps", 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
-	// config-specified value for origin should override the value in the
-	// Install rpc argument.
-	mtName, ok := sh.GetVar(envvar.NamespacePrefix)
-	if !ok {
-		t.Fatalf("failed to get namespace root var from shell")
-	}
-	// This rooted name should be equivalent to the relative name "ar", but
-	// we want to test that the config override for origin works.
-	rootedAppRepoName := naming.Join(mtName, "ar")
-	appID := utiltest.InstallApp(t, ctx, device.Config{utiltest.TestFlagName: "flag-val-install", mgmt.AppOriginConfigKey: rootedAppRepoName})
-	v1 := utiltest.VerifyState(t, ctx, device.InstallationStateActive, appID)
-	installationDebug := utiltest.Debug(t, ctx, appID)
-	// We spot-check a couple pieces of information we expect in the debug
-	// output.
-	// TODO(caprita): Is there a way to verify more without adding brittle
-	// logic that assumes too much about the format?  This may be one
-	// argument in favor of making the output of Debug a struct instead of
-	// free-form string.
-	if !strings.Contains(installationDebug, fmt.Sprintf("Origin: %v", rootedAppRepoName)) {
-		t.Fatalf("debug response doesn't contain expected info: %v", installationDebug)
-	}
-	if !strings.Contains(installationDebug, "Config: map[random_test_flag:flag-val-install]") {
-		t.Fatalf("debug response doesn't contain expected info: %v", installationDebug)
-	}
-
-	// Start requires the caller to bless the app instance.
-	expectedErr := "bless failed"
-	if _, err := utiltest.LaunchAppImpl(t, ctx, appID, ""); err == nil || err.Error() != expectedErr {
-		t.Fatalf("Start(%v) expected to fail with %v, got %v instead", appID, expectedErr, err)
-	}
-
-	// Start an instance of the app.
-	instance1ID := utiltest.LaunchApp(t, ctx, appID)
-	if v := utiltest.VerifyState(t, ctx, device.InstanceStateRunning, appID, instance1ID); v != v1 {
-		t.Fatalf("Instance version expected to be %v, got %v instead", v1, v)
-	}
-
-	instanceDebug := utiltest.Debug(t, ctx, appID, instance1ID)
-	// Verify the apps default blessings.
-	if !strings.Contains(instanceDebug, fmt.Sprintf("Default Blessings                %s/forapp", test.TestBlessing)) {
-		t.Fatalf("debug response doesn't contain expected info: %v", instanceDebug)
-	}
-
-	// 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]
-
-	// Stop the app instance.
-	utiltest.KillApp(t, ctx, appID, instance1ID)
-	utiltest.VerifyState(t, ctx, device.InstanceStateNotRunning, appID, instance1ID)
-	utiltest.ResolveExpectNotFound(t, ctx, "appV1")
-
-	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 {
-		t.Fatalf("Expected a new endpoint for the app after kill/run")
-	}
-
-	// Start a second instance.
-	instance2ID := utiltest.LaunchApp(t, ctx, appID)
-	pingCh.VerifyPingArgs(t, utiltest.UserName(t), "flag-val-install", "env-val-envelope") // Wait until the app pings us that it's ready.
-
-	// There should be two endpoints mounted as "appV1", one for each
-	// instance of the app.
-	endpoints := utiltest.Resolve(t, ctx, "appV1", 2)
-	v1EP2 := endpoints[0]
-	if endpoints[0] == v1EP1 {
-		v1EP2 = endpoints[1]
-		if v1EP2 == v1EP1 {
-			t.Fatalf("Both endpoints are the same")
-		}
-	} else if endpoints[1] != v1EP1 {
-		t.Fatalf("Second endpoint should have been v1EP1: %v, %v", endpoints, v1EP1)
-	}
-
-	// TODO(caprita): verify various non-standard combinations (kill when
-	// canceled; run while still running).
-
-	// 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 {
-		t.Fatalf("Resolve(%v): want: %v, got %v", "appV1", want, got)
-	}
-
-	// Updating the installation to itself is a no-op.
-	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")
-
-	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", "appV2")
-
-	utiltest.UpdateApp(t, ctx, appID)
-
-	v2 := utiltest.VerifyState(t, ctx, device.InstallationStateActive, appID)
-	if v1 == v2 {
-		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 {
-		t.Fatalf("Resolve(%v): want: %v, got %v", "appV1", want, got)
-	}
-	if v := utiltest.VerifyState(t, ctx, device.InstanceStateRunning, appID, instance2ID); v != v1 {
-		t.Fatalf("Instance version expected to be %v, got %v instead", v1, v)
-	}
-
-	// Resume first instance.
-	utiltest.RunApp(t, ctx, appID, instance1ID)
-	if v := utiltest.VerifyState(t, ctx, device.InstanceStateRunning, appID, instance1ID); v != v1 {
-		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.
-	// 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)
-	if endpoints[0] == v1EP2 {
-		if endpoints[1] == v1EP2 {
-			t.Fatalf("Both endpoints are the same")
-		}
-	} else if endpoints[1] != v1EP2 {
-		t.Fatalf("Second endpoint should have been v1EP2: %v, %v", endpoints, v1EP2)
-	}
-
-	// Trying to update first instance while it's running should fail.
-	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 {
-		t.Fatalf("Resolve(%v): want: %v, got %v", "appV1", want, got)
-	}
-	// Update succeeds now.
-	utiltest.UpdateInstance(t, ctx, appID, instance1ID)
-	if v := utiltest.VerifyState(t, ctx, device.InstanceStateNotRunning, appID, instance1ID); v != v2 {
-		t.Fatalf("Instance version expected to be %v, got %v instead", v2, v)
-	}
-	// 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)
-
-	// Stop first instance.
-	utiltest.TerminateApp(t, ctx, appID, instance1ID)
-	verifyAppWorkspace(t, root, appID, instance1ID)
-	utiltest.ResolveExpectNotFound(t, ctx, "appV2")
-
-	// Start a third instance.
-	instance3ID := utiltest.LaunchApp(t, ctx, appID)
-	if v := utiltest.VerifyState(t, ctx, device.InstanceStateRunning, appID, instance3ID); v != v2 {
-		t.Fatalf("Instance version expected to be %v, got %v instead", v2, v)
-	}
-	// 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)
-
-	// Stop second instance.
-	utiltest.TerminateApp(t, ctx, appID, instance2ID)
-	utiltest.ResolveExpectNotFound(t, ctx, "appV1")
-
-	// Stop third instance.
-	utiltest.TerminateApp(t, ctx, appID, instance3ID)
-	utiltest.ResolveExpectNotFound(t, ctx, "appV2")
-
-	// Revert the app.
-	utiltest.RevertApp(t, ctx, appID)
-	if v := utiltest.VerifyState(t, ctx, device.InstallationStateActive, appID); v != v1 {
-		t.Fatalf("Installation version expected to be %v, got %v instead", v1, v)
-	}
-
-	// Start a fourth instance.  It should be running from version 1.
-	instance4ID := utiltest.LaunchApp(t, ctx, appID)
-	if v := utiltest.VerifyState(t, ctx, device.InstanceStateRunning, appID, instance4ID); v != v1 {
-		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.TerminateApp(t, ctx, appID, instance4ID)
-	utiltest.ResolveExpectNotFound(t, ctx, "appV1")
-
-	// We are already on the first version, no further revert possible.
-	utiltest.RevertAppExpectError(t, ctx, appID, impl.ErrUpdateNoOp.ID)
-
-	// Uninstall the app.
-	utiltest.UninstallApp(t, ctx, appID)
-	utiltest.VerifyState(t, ctx, device.InstallationStateUninstalled, appID)
-
-	// Updating the installation should no longer be allowed.
-	utiltest.UpdateAppExpectError(t, ctx, appID, impl.ErrInvalidOperation.ID)
-
-	// Reverting the installation should no longer be allowed.
-	utiltest.RevertAppExpectError(t, ctx, appID, impl.ErrInvalidOperation.ID)
-
-	// Starting new instances should no longer be allowed.
-	utiltest.LaunchAppExpectError(t, ctx, appID, impl.ErrInvalidOperation.ID)
-
-	// Make sure that Kill will actually kill an app that doesn't exit
-	// 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", "hAppV1")
-	hAppID := utiltest.InstallApp(t, ctx)
-	hInstanceID := utiltest.LaunchApp(t, ctx, hAppID)
-	hangingPid := pingCh.WaitForPingArgs(t).Pid
-	if err := syscall.Kill(hangingPid, 0); err != nil && err != syscall.EPERM {
-		t.Fatalf("Pid of hanging app (%v) is not live", hangingPid)
-	}
-	utiltest.KillApp(t, ctx, hAppID, hInstanceID)
-	pidIsAlive := true
-	for i := 0; i < 10 && pidIsAlive; i++ {
-		if err := syscall.Kill(hangingPid, 0); err == nil || err == syscall.EPERM {
-			time.Sleep(time.Second) // pid is still alive
-		} else {
-			pidIsAlive = false
-		}
-	}
-	if pidIsAlive {
-		t.Fatalf("Pid of hanging app (%d) has not exited after Stop() call", hangingPid)
-	}
-
-	// Cleanly shut down the device manager.
-	defer utiltest.VerifyNoRunningProcesses(t)
-	syscall.Kill(dmh.Pid(), syscall.SIGINT)
-	dmh.Expect("dm terminated")
-	dmh.ExpectEOF()
-}
-
-func startRealBinaryRepository(t *testing.T, ctx *context.T, von string) func() {
-	rootDir, err := binarylib.SetupRootDir("")
-	if err != nil {
-		t.Fatalf("binarylib.SetupRootDir failed: %v", err)
-	}
-	state, err := binarylib.NewState(rootDir, "", 3)
-	if err != nil {
-		t.Fatalf("binarylib.NewState failed: %v", err)
-	}
-	server, _ := servicetest.NewServer(ctx)
-	d, err := binarylib.NewDispatcher(v23.GetPrincipal(ctx), state)
-	if err != nil {
-		t.Fatalf("server.NewDispatcher failed: %v", err)
-	}
-	if err := server.ServeDispatcher(von, d); err != nil {
-		t.Fatalf("server.ServeDispatcher failed: %v", err)
-	}
-	return func() {
-		if err := server.Stop(); err != nil {
-			t.Fatalf("server.Stop failed: %v", err)
-		}
-		if err := os.RemoveAll(rootDir); err != nil {
-			t.Fatalf("os.RemoveAll(%q) failed: %v", rootDir, err)
-		}
-	}
-}
-
-// TestDeviceManagerClaim claims a devicemanager and tests AccessList permissions on
-// its methods.
-func TestDeviceManagerClaim(t *testing.T) {
-	ctx, shutdown := initForTest()
-	defer shutdown()
-
-	// root blessing provider so that the principals of all the contexts
-	// recognize each other.
-	idp := testutil.NewIDProvider("root")
-	if err := idp.Bless(v23.GetPrincipal(ctx), "ctx"); err != nil {
-		t.Fatal(err)
-	}
-
-	sh, deferFn := servicetest.CreateShellAndMountTable(t, ctx, nil)
-	defer deferFn()
-
-	// Set up mock application and binary repositories.
-	envelope, cleanup := utiltest.StartMockRepos(t, ctx)
-	defer cleanup()
-
-	root, cleanup := servicetest.SetupRootDir(t, "devicemanager")
-	defer cleanup()
-	if err := impl.SaveCreatorInfo(root); err != nil {
-		t.Fatal(err)
-	}
-
-	// Create a script wrapping the test target that implements suidhelper.
-	helperPath := utiltest.GenerateSuidHelperScript(t, root)
-
-	// 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)
-	pid := servicetest.ReadPID(t, dmh)
-	defer syscall.Kill(pid, syscall.SIGINT)
-
-	*envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.AppCmd, "google naps", "trapp")
-
-	claimantCtx := utiltest.CtxWithNewPrincipal(t, ctx, idp, "claimant")
-	octx, err := v23.WithPrincipal(ctx, testutil.NewPrincipal("other"))
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	// Unclaimed devices cannot do anything but be claimed.
-	// TODO(ashankar,caprita): The line below will currently fail with
-	// ErrUnclaimedDevice != NotTrusted. NotTrusted can be avoided by
-	// passing options.SkipServerEndpointAuthorization{} to the "Install" RPC.
-	// Refactor the helper function to make this possible.
-	//installAppExpectError(t, octx, impl.ErrUnclaimedDevice.ID)
-
-	// Claim the device with an incorrect pairing token should fail.
-	utiltest.ClaimDeviceExpectError(t, claimantCtx, "claimable", "mydevice", "badtoken", impl.ErrInvalidPairingToken.ID)
-	// But succeed with a valid pairing token
-	utiltest.ClaimDevice(t, claimantCtx, "claimable", "dm", "mydevice", pairingToken)
-
-	// Installation should succeed since claimantRT is now the "owner" of
-	// the devicemanager.
-	appID := utiltest.InstallApp(t, claimantCtx)
-
-	// octx will not install the app now since it doesn't recognize the
-	// device's blessings. The error returned will be ErrNoServers as that
-	// is what the IPC stack does when there are no authorized servers.
-	utiltest.InstallAppExpectError(t, octx, verror.ErrNoServers.ID)
-	// Even if it does recognize the device (by virtue of recognizing the
-	// claimant), the device will not allow it to install.
-	if err := v23.GetPrincipal(octx).AddToRoots(v23.GetPrincipal(claimantCtx).BlessingStore().Default()); err != nil {
-		t.Fatal(err)
-	}
-	utiltest.InstallAppExpectError(t, octx, verror.ErrNoAccess.ID)
-
-	// Create the local server that the app uses to let us know it's ready.
-	pingCh, cleanup := utiltest.SetupPingServer(t, claimantCtx)
-	defer cleanup()
-
-	// Start an instance of the app.
-	instanceID := utiltest.LaunchApp(t, claimantCtx, appID)
-
-	// Wait until the app pings us that it's ready.
-	pingCh.WaitForPingArgs(t)
-	utiltest.Resolve(t, ctx, "trapp", 1)
-	utiltest.KillApp(t, claimantCtx, appID, instanceID)
-
-	// TODO(gauthamt): Test that AccessLists persist across devicemanager restarts
-}
-
-func TestDeviceManagerUpdateAccessList(t *testing.T) {
-	ctx, shutdown := initForTest()
-	defer shutdown()
-
-	// Identity provider to ensure that all processes recognize each
-	// others' blessings.
-	idp := testutil.NewIDProvider("root")
-	ctx = utiltest.CtxWithNewPrincipal(t, ctx, idp, "self")
-
-	sh, deferFn := servicetest.CreateShellAndMountTable(t, ctx, nil)
-	defer deferFn()
-
-	// Set up mock application and binary repositories.
-	envelope, cleanup := utiltest.StartMockRepos(t, ctx)
-	defer cleanup()
-
-	root, cleanup := servicetest.SetupRootDir(t, "devicemanager")
-	defer cleanup()
-	if err := impl.SaveCreatorInfo(root); err != nil {
-		t.Fatal(err)
-	}
-
-	selfCtx := ctx
-	octx := utiltest.CtxWithNewPrincipal(t, selfCtx, idp, "other")
-
-	// 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")
-	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")
-
-	// On an unclaimed device manager, there will be no AccessLists.
-	if _, _, err := device.DeviceClient("claimable").GetPermissions(selfCtx); err == nil {
-		t.Fatalf("GetPermissions should have failed but didn't.")
-	}
-
-	// Claim the devicemanager as "root/self/mydevice"
-	utiltest.ClaimDevice(t, selfCtx, "claimable", "dm", "mydevice", utiltest.NoPairingToken)
-	expectedAccessList := make(access.Permissions)
-	for _, tag := range access.AllTypicalTags() {
-		expectedAccessList[string(tag)] = access.AccessList{In: []security.BlessingPattern{"root/$", "root/self/$", "root/self/mydevice/$"}}
-	}
-	var b bytes.Buffer
-	if err := expectedAccessList.WriteTo(&b); err != nil {
-		t.Fatalf("Failed to save AccessList:%v", err)
-	}
-	// Note, "version" below refers to the Permissions version, not the device
-	// manager version.
-	md5hash := md5.Sum(b.Bytes())
-	expectedVersion := hex.EncodeToString(md5hash[:])
-	deviceStub := device.DeviceClient("dm/device")
-	perms, version, err := deviceStub.GetPermissions(selfCtx)
-	if err != nil {
-		t.Fatal(err)
-	}
-	if version != expectedVersion {
-		t.Fatalf("getAccessList expected:%v(%v), got:%v(%v)", expectedAccessList, expectedVersion, perms, version)
-	}
-	// Install from octx should fail, since it does not match the AccessList.
-	utiltest.InstallAppExpectError(t, octx, verror.ErrNoAccess.ID)
-
-	newAccessList := make(access.Permissions)
-	for _, tag := range access.AllTypicalTags() {
-		newAccessList.Add("root/other", string(tag))
-	}
-	if err := deviceStub.SetPermissions(selfCtx, newAccessList, "invalid"); err == nil {
-		t.Fatalf("SetPermissions should have failed with invalid version")
-	}
-	if err := deviceStub.SetPermissions(selfCtx, newAccessList, version); err != nil {
-		t.Fatal(err)
-	}
-	// Install should now fail with selfCtx, which no longer matches the
-	// AccessLists but succeed with octx, which does.
-	utiltest.InstallAppExpectError(t, selfCtx, verror.ErrNoAccess.ID)
-	utiltest.InstallApp(t, octx)
-}
-
 type simpleRW chan []byte
 
 func (s simpleRW) Write(p []byte) (n int, err error) {
@@ -807,7 +283,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 := initForTest()
+	ctx, shutdown := utiltest.InitForTest()
 	defer shutdown()
 
 	sh, deferFn := servicetest.CreateShellAndMountTable(t, ctx, nil)
@@ -866,107 +342,10 @@
 	}
 }
 
-func TestDeviceManagerGlobAndDebug(t *testing.T) {
-	ctx, shutdown := initForTest()
-	defer shutdown()
-
-	sh, deferFn := servicetest.CreateShellAndMountTable(t, ctx, nil)
-	defer deferFn()
-
-	// Set up mock application and binary repositories.
-	envelope, cleanup := utiltest.StartMockRepos(t, ctx)
-	defer cleanup()
-
-	root, cleanup := servicetest.SetupRootDir(t, "devicemanager")
-	defer cleanup()
-	if err := impl.SaveCreatorInfo(root); err != nil {
-		t.Fatal(err)
-	}
-
-	// Create a script wrapping the test target that implements suidhelper.
-	helperPath := utiltest.GenerateSuidHelperScript(t, root)
-
-	// 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")
-	pid := servicetest.ReadPID(t, dmh)
-	defer syscall.Kill(pid, syscall.SIGINT)
-
-	// Create the local server that the app uses to let us know it's ready.
-	pingCh, cleanup := utiltest.SetupPingServer(t, ctx)
-	defer cleanup()
-
-	// Create the envelope for the first version of the app.
-	*envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.AppCmd, "google naps", "appV1")
-
-	// Device must be claimed before applications can be installed.
-	utiltest.ClaimDevice(t, ctx, "claimable", "dm", "mydevice", utiltest.NoPairingToken)
-	// Install the app.
-	appID := utiltest.InstallApp(t, ctx)
-	install1ID := path.Base(appID)
-
-	// Start an instance of the app.
-	instance1ID := utiltest.LaunchApp(t, ctx, appID)
-	defer utiltest.TerminateApp(t, ctx, appID, instance1ID)
-
-	// Wait until the app pings us that it's ready.
-	pingCh.WaitForPingArgs(t)
-
-	app2ID := utiltest.InstallApp(t, ctx)
-	install2ID := path.Base(app2ID)
-
-	// Base name of argv[0] that the app should have when it executes
-	// It will be path.Base(envelope.Title + "@" + envelope.Binary.File + "/app").
-	// Note the suffix, which ensures that the result is always "app" at the moment.
-	// Someday in future we may remove that and have binary names that reflect the app name.
-	const appName = "app"
-
-	testcases := []utiltest.GlobTestVector{
-		{"dm", "...", []string{
-			"",
-			"apps",
-			"apps/google naps",
-			"apps/google naps/" + install1ID,
-			"apps/google naps/" + install1ID + "/" + instance1ID,
-			"apps/google naps/" + install1ID + "/" + instance1ID + "/logs",
-			"apps/google naps/" + install1ID + "/" + instance1ID + "/logs/STDERR-<timestamp>",
-			"apps/google naps/" + install1ID + "/" + instance1ID + "/logs/STDOUT-<timestamp>",
-			"apps/google naps/" + install1ID + "/" + instance1ID + "/logs/" + appName + ".INFO",
-			"apps/google naps/" + install1ID + "/" + instance1ID + "/logs/" + appName + ".<*>.INFO.<timestamp>",
-			"apps/google naps/" + install1ID + "/" + instance1ID + "/pprof",
-			"apps/google naps/" + install1ID + "/" + instance1ID + "/stats",
-			"apps/google naps/" + install1ID + "/" + instance1ID + "/stats/rpc",
-			"apps/google naps/" + install1ID + "/" + instance1ID + "/stats/system",
-			"apps/google naps/" + install1ID + "/" + instance1ID + "/stats/system/start-time-rfc1123",
-			"apps/google naps/" + install1ID + "/" + instance1ID + "/stats/system/start-time-unix",
-			"apps/google naps/" + install2ID,
-			"device",
-		}},
-		{"dm/apps", "*", []string{"google naps"}},
-		{"dm/apps/google naps", "*", []string{install1ID, install2ID}},
-		{"dm/apps/google naps/" + install1ID, "*", []string{instance1ID}},
-		{"dm/apps/google naps/" + install1ID + "/" + instance1ID, "*", []string{"logs", "pprof", "stats"}},
-		{"dm/apps/google naps/" + install1ID + "/" + instance1ID + "/logs", "*", []string{
-			"STDERR-<timestamp>",
-			"STDOUT-<timestamp>",
-			appName + ".INFO",
-			appName + ".<*>.INFO.<timestamp>",
-		}},
-		{"dm/apps/google naps/" + install1ID + "/" + instance1ID + "/stats/system", "start-time*", []string{"start-time-rfc1123", "start-time-unix"}},
-	}
-
-	res := utiltest.NewGlobTestRegexHelper(appName)
-
-	utiltest.VerifyGlob(t, ctx, appName, testcases, res)
-	utiltest.VerifyLog(t, ctx, "dm", "apps/google naps", install1ID, instance1ID, "logs", "*")
-	utiltest.VerifyStatsValues(t, ctx, "dm", "apps/google naps", install1ID, instance1ID, "stats/system/start-time*")
-	utiltest.VerifyPProfCmdLine(t, ctx, appName, "dm", "apps/google naps", install1ID, instance1ID, "pprof")
-}
-
 // 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 := initForTest()
+	ctx, shutdown := utiltest.InitForTest()
 	defer shutdown()
 
 	sh, deferFn := servicetest.CreateShellAndMountTable(t, ctx, nil)
@@ -977,7 +356,7 @@
 	defer cleanup()
 
 	binaryVON := "realbin"
-	defer startRealBinaryRepository(t, ctx, binaryVON)()
+	defer utiltest.StartRealBinaryRepository(t, ctx, binaryVON)()
 
 	// upload package to binary repository
 	tmpdir, err := ioutil.TempDir("", "test-package-")
@@ -1106,7 +485,7 @@
 // TODO(rjkroege): Verify that associations persist across restarts once
 // permanent storage is added.
 func TestAccountAssociation(t *testing.T) {
-	ctx, shutdown := initForTest()
+	ctx, shutdown := utiltest.InitForTest()
 	defer shutdown()
 
 	sh, deferFn := servicetest.CreateShellAndMountTable(t, ctx, nil)
@@ -1190,285 +569,3 @@
 		},
 	})
 }
-
-func TestAppWithSuidHelper(t *testing.T) {
-	ctx, shutdown := initForTest()
-	defer shutdown()
-
-	// Identity provider used to ensure that all processes recognize each
-	// others' blessings.
-	idp := testutil.NewIDProvider("root")
-	if err := idp.Bless(v23.GetPrincipal(ctx), "self"); err != nil {
-		t.Fatal(err)
-	}
-
-	sh, deferFn := servicetest.CreateShellAndMountTable(t, ctx, nil)
-	defer deferFn()
-
-	// Set up mock application and binary repositories.
-	envelope, cleanup := utiltest.StartMockRepos(t, ctx)
-	defer cleanup()
-
-	root, cleanup := servicetest.SetupRootDir(t, "devicemanager")
-	defer cleanup()
-	if err := impl.SaveCreatorInfo(root); err != nil {
-		t.Fatal(err)
-	}
-
-	selfCtx := ctx
-	otherCtx := utiltest.CtxWithNewPrincipal(t, selfCtx, idp, "other")
-
-	// 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")
-	pid := servicetest.ReadPID(t, dmh)
-	defer syscall.Kill(pid, syscall.SIGINT)
-	defer utiltest.VerifyNoRunningProcesses(t)
-	// Claim the devicemanager with selfCtx as root/self/alice
-	utiltest.ClaimDevice(t, selfCtx, "claimable", "dm", "alice", utiltest.NoPairingToken)
-
-	deviceStub := device.DeviceClient("dm/device")
-
-	// Create the local server that the app uses to tell us which system
-	// name the device manager wished to run it as.
-	pingCh, cleanup := utiltest.SetupPingServer(t, ctx)
-	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", fmt.Sprintf("--%s=flag-val-envelope", utiltest.TestFlagName), "appV1")
-
-	// Install and start the app as root/self.
-	appID := utiltest.InstallApp(t, selfCtx)
-
-	vlog.VI(2).Infof("Validate that the created app has the right permission lists.")
-	perms, _, err := utiltest.AppStub(appID).GetPermissions(selfCtx)
-	if err != nil {
-		t.Fatalf("GetPermissions on appID: %v failed %v", appID, err)
-	}
-	expected := make(access.Permissions)
-	for _, tag := range access.AllTypicalTags() {
-		expected[string(tag)] = access.AccessList{In: []security.BlessingPattern{"root/self/$"}}
-	}
-	if got, want := perms.Normalize(), expected.Normalize(); !reflect.DeepEqual(got, want) {
-		t.Errorf("got %#v, expected %#v", got, want)
-	}
-
-	// Start an instance of the app but this time it should fail: we do not
-	// have an associated uname for the invoking identity.
-	utiltest.LaunchAppExpectError(t, selfCtx, appID, verror.ErrNoAccess.ID)
-
-	// Create an association for selfCtx
-	if err := deviceStub.AssociateAccount(selfCtx, []string{"root/self"}, testUserName); err != nil {
-		t.Fatalf("AssociateAccount failed %v", err)
-	}
-
-	instance1ID := utiltest.LaunchApp(t, selfCtx, appID)
-	pingCh.VerifyPingArgs(t, testUserName, "flag-val-envelope", "env-var") // Wait until the app pings us that it's ready.
-	utiltest.TerminateApp(t, selfCtx, appID, instance1ID)
-
-	vlog.VI(2).Infof("other attempting to run an app without access. Should fail.")
-	utiltest.LaunchAppExpectError(t, otherCtx, appID, verror.ErrNoAccess.ID)
-
-	// Self will now let other also install apps.
-	if err := deviceStub.AssociateAccount(selfCtx, []string{"root/other"}, testUserName); err != nil {
-		t.Fatalf("AssociateAccount failed %v", err)
-	}
-	// Add Start to the AccessList list for root/other.
-	newAccessList, _, err := deviceStub.GetPermissions(selfCtx)
-	if err != nil {
-		t.Fatalf("GetPermissions failed %v", err)
-	}
-	newAccessList.Add("root/other", string(access.Write))
-	if err := deviceStub.SetPermissions(selfCtx, newAccessList, ""); err != nil {
-		t.Fatalf("SetPermissions failed %v", err)
-	}
-
-	// With the introduction of per installation and per instance AccessLists,
-	// while other now has administrator permissions on the device manager,
-	// other doesn't have execution permissions for the app. So this will
-	// fail.
-	vlog.VI(2).Infof("other attempting to run an app still without access. Should fail.")
-	utiltest.LaunchAppExpectError(t, otherCtx, appID, verror.ErrNoAccess.ID)
-
-	// But self can give other permissions  to start applications.
-	vlog.VI(2).Infof("self attempting to give other permission to start %s", appID)
-	newAccessList, _, err = utiltest.AppStub(appID).GetPermissions(selfCtx)
-	if err != nil {
-		t.Fatalf("GetPermissions on appID: %v failed %v", appID, err)
-	}
-	newAccessList.Add("root/other", string(access.Read))
-	if err = utiltest.AppStub(appID).SetPermissions(selfCtx, newAccessList, ""); err != nil {
-		t.Fatalf("SetPermissions on appID: %v failed: %v", appID, err)
-	}
-
-	vlog.VI(2).Infof("other attempting to run an app with access. Should succeed.")
-	instance2ID := utiltest.LaunchApp(t, otherCtx, appID)
-	pingCh.VerifyPingArgs(t, testUserName, "flag-val-envelope", "env-var") // Wait until the app pings us that it's ready.
-
-	vlog.VI(2).Infof("Validate that created instance has the right permissions.")
-	expected = make(access.Permissions)
-	for _, tag := range access.AllTypicalTags() {
-		expected[string(tag)] = access.AccessList{In: []security.BlessingPattern{"root/other/$"}}
-	}
-	perms, _, err = utiltest.AppStub(appID, instance2ID).GetPermissions(selfCtx)
-	if err != nil {
-		t.Fatalf("GetPermissions on instance %v/%v failed: %v", appID, instance2ID, err)
-	}
-	if got, want := perms.Normalize(), expected.Normalize(); !reflect.DeepEqual(got, want) {
-		t.Errorf("got %#v, expected %#v ", got, want)
-	}
-
-	// Shutdown the app.
-	utiltest.KillApp(t, otherCtx, appID, instance2ID)
-
-	vlog.VI(2).Infof("Verify that Run with the same systemName works.")
-	utiltest.RunApp(t, otherCtx, appID, instance2ID)
-	pingCh.VerifyPingArgs(t, testUserName, "flag-val-envelope", "env-var") // Wait until the app pings us that it's ready.
-	utiltest.KillApp(t, otherCtx, appID, instance2ID)
-
-	vlog.VI(2).Infof("Verify that other can install and run applications.")
-	otherAppID := utiltest.InstallApp(t, otherCtx)
-
-	vlog.VI(2).Infof("other attempting to run an app that other installed. Should succeed.")
-	instance4ID := utiltest.LaunchApp(t, otherCtx, otherAppID)
-	pingCh.VerifyPingArgs(t, testUserName, "flag-val-envelope", "env-var") // Wait until the app pings us that it's ready.
-
-	// Clean up.
-	utiltest.TerminateApp(t, otherCtx, otherAppID, instance4ID)
-
-	// Change the associated system name.
-	if err := deviceStub.AssociateAccount(selfCtx, []string{"root/other"}, anotherTestUserName); err != nil {
-		t.Fatalf("AssociateAccount failed %v", err)
-	}
-
-	vlog.VI(2).Infof("Show that Run with a different systemName fails.")
-	utiltest.RunAppExpectError(t, otherCtx, appID, instance2ID, verror.ErrNoAccess.ID)
-
-	// Clean up.
-	utiltest.DeleteApp(t, otherCtx, appID, instance2ID)
-
-	vlog.VI(2).Infof("Show that Start with different systemName works.")
-	instance3ID := utiltest.LaunchApp(t, otherCtx, appID)
-	pingCh.VerifyPingArgs(t, anotherTestUserName, "flag-val-envelope", "env-var") // Wait until the app pings us that it's ready.
-
-	// Clean up.
-	utiltest.TerminateApp(t, otherCtx, appID, instance3ID)
-}
-
-func TestDownloadSignatureMatch(t *testing.T) {
-	ctx, shutdown := initForTest()
-	defer shutdown()
-
-	sh, deferFn := servicetest.CreateShellAndMountTable(t, ctx, nil)
-	defer deferFn()
-
-	binaryVON := "binary"
-	pkgVON := naming.Join(binaryVON, "testpkg")
-	defer startRealBinaryRepository(t, ctx, binaryVON)()
-
-	up := testutil.RandomBytes(testutil.Intn(5 << 20))
-	mediaInfo := repository.MediaInfo{Type: "application/octet-stream"}
-	sig, err := binarylib.Upload(ctx, naming.Join(binaryVON, "testbinary"), up, mediaInfo)
-	if err != nil {
-		t.Fatalf("Upload(%v) failed:%v", binaryVON, err)
-	}
-
-	// Upload packages for this application
-	tmpdir, err := ioutil.TempDir("", "test-package-")
-	if err != nil {
-		t.Fatalf("ioutil.TempDir failed: %v", err)
-	}
-	defer os.RemoveAll(tmpdir)
-	pkgContents := testutil.RandomBytes(testutil.Intn(5 << 20))
-	if err := ioutil.WriteFile(filepath.Join(tmpdir, "pkg.txt"), pkgContents, 0600); err != nil {
-		t.Fatalf("ioutil.WriteFile failed: %v", err)
-	}
-	pkgSig, err := binarylib.UploadFromDir(ctx, pkgVON, tmpdir)
-	if err != nil {
-		t.Fatalf("binarylib.UploadFromDir failed: %v", err)
-	}
-
-	// Start the application repository
-	envelope, serverStop := utiltest.StartApplicationRepository(ctx)
-	defer serverStop()
-
-	root, cleanup := servicetest.SetupRootDir(t, "devicemanager")
-	defer cleanup()
-	if err := impl.SaveCreatorInfo(root); err != nil {
-		t.Fatal(err)
-	}
-
-	// Create a script wrapping the test target that implements suidhelper.
-	helperPath := utiltest.GenerateSuidHelperScript(t, root)
-
-	// 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")
-	pid := servicetest.ReadPID(t, dmh)
-	defer syscall.Kill(pid, syscall.SIGINT)
-	utiltest.ClaimDevice(t, ctx, "claimable", "dm", "mydevice", utiltest.NoPairingToken)
-
-	publisher, err := v23.GetPrincipal(ctx).BlessSelf("publisher")
-	if err != nil {
-		t.Fatalf("Failed to generate publisher blessings:%v", err)
-	}
-	*envelope = application.Envelope{
-		Binary: application.SignedFile{
-			File:      naming.Join(binaryVON, "testbinary"),
-			Signature: *sig,
-		},
-		Publisher: publisher,
-		Packages: map[string]application.SignedFile{
-			"pkg": application.SignedFile{
-				File:      pkgVON,
-				Signature: *pkgSig,
-			},
-		},
-	}
-	if _, err := utiltest.AppStub().Install(ctx, utiltest.MockApplicationRepoName, device.Config{}, nil); err != nil {
-		t.Fatalf("Failed to Install app:%v", err)
-	}
-
-	// Verify that when the binary is corrupted, signature verification fails.
-	up[0] = up[0] ^ 0xFF
-	if err := binarylib.Delete(ctx, naming.Join(binaryVON, "testbinary")); err != nil {
-		t.Fatalf("Delete(%v) failed:%v", binaryVON, err)
-	}
-	if _, err := binarylib.Upload(ctx, naming.Join(binaryVON, "testbinary"), up, mediaInfo); err != nil {
-		t.Fatalf("Upload(%v) failed:%v", binaryVON, err)
-	}
-	if _, err := utiltest.AppStub().Install(ctx, utiltest.MockApplicationRepoName, device.Config{}, nil); verror.ErrorID(err) != impl.ErrOperationFailed.ID {
-		t.Fatalf("Failed to verify signature mismatch for binary:%v. Got errorid=%v[%v], want errorid=%v", binaryVON, verror.ErrorID(err), err, impl.ErrOperationFailed.ID)
-	}
-
-	// Restore the binary and verify that installation succeeds.
-	up[0] = up[0] ^ 0xFF
-	if err := binarylib.Delete(ctx, naming.Join(binaryVON, "testbinary")); err != nil {
-		t.Fatalf("Delete(%v) failed:%v", binaryVON, err)
-	}
-	if _, err := binarylib.Upload(ctx, naming.Join(binaryVON, "testbinary"), up, mediaInfo); err != nil {
-		t.Fatalf("Upload(%v) failed:%v", binaryVON, err)
-	}
-	if _, err := utiltest.AppStub().Install(ctx, utiltest.MockApplicationRepoName, device.Config{}, nil); err != nil {
-		t.Fatalf("Failed to Install app:%v", err)
-	}
-
-	// Verify that when the package contents are corrupted, signature verification fails.
-	pkgContents[0] = pkgContents[0] ^ 0xFF
-	if err := binarylib.Delete(ctx, pkgVON); err != nil {
-		t.Fatalf("Delete(%v) failed:%v", pkgVON, err)
-	}
-	if err := os.Remove(filepath.Join(tmpdir, "pkg.txt")); err != nil {
-		t.Fatalf("Remove(%v) failed:%v", filepath.Join(tmpdir, "pkg.txt"), err)
-	}
-	if err := ioutil.WriteFile(filepath.Join(tmpdir, "pkg.txt"), pkgContents, 0600); err != nil {
-		t.Fatalf("ioutil.WriteFile failed: %v", err)
-	}
-	if _, err = binarylib.UploadFromDir(ctx, pkgVON, tmpdir); err != nil {
-		t.Fatalf("binarylib.UploadFromDir failed: %v", err)
-	}
-	if _, err := utiltest.AppStub().Install(ctx, utiltest.MockApplicationRepoName, device.Config{}, nil); verror.ErrorID(err) != impl.ErrOperationFailed.ID {
-		t.Fatalf("Failed to verify signature mismatch for package:%v", pkgVON)
-	}
-}
diff --git a/services/device/internal/impl/instance_reaping.go b/services/device/internal/impl/instance_reaping.go
index 3560db2..9dc2b3e 100644
--- a/services/device/internal/impl/instance_reaping.go
+++ b/services/device/internal/impl/instance_reaping.go
@@ -223,7 +223,7 @@
 	// (in case the device restarted while the instance was in one of the
 	// transitional states like launching, dying, etc).
 	if err := transitionInstance(instancePath, state, device.InstanceStateRunning); err != nil {
-		vlog.Errorf("transitionInstance(%s,%s) failed: %v", instancePath, state, device.InstanceStateRunning, err)
+		vlog.Errorf("transitionInstance(%s,%v,%s) failed: %v", instancePath, state, device.InstanceStateRunning, err)
 	}
 
 	vlog.VI(0).Infof("perInstance go routine for %v ending", instancePath)
diff --git a/services/device/internal/impl/only_for_test.go b/services/device/internal/impl/only_for_test.go
index 0cec861..9b02bb7 100644
--- a/services/device/internal/impl/only_for_test.go
+++ b/services/device/internal/impl/only_for_test.go
@@ -5,20 +5,14 @@
 package impl
 
 import (
-	"flag"
 	"fmt"
-	"os"
-	"path/filepath"
 
 	"v.io/v23/rpc"
-	"v.io/x/lib/vlog"
 )
 
 // This file contains code in the impl package that we only want built for tests
 // (it exposes public API methods that we don't want to normally expose).
 
-var mockIsSetuid = flag.Bool("mocksetuid", false, "set flag to pretend to have a helper with setuid permissions")
-
 func (c *callbackState) leaking() bool {
 	c.Lock()
 	defer c.Unlock()
@@ -35,31 +29,3 @@
 		panic(fmt.Sprintf("unexpected type: %T", d))
 	}
 }
-
-func init() {
-	cleanupDir = func(dir, helper string) {
-		if dir == "" {
-			return
-		}
-		parentDir, base := filepath.Dir(dir), filepath.Base(dir)
-		var renamed string
-		if helper != "" {
-			renamed = filepath.Join(parentDir, "helper_deleted_"+base)
-		} else {
-			renamed = filepath.Join(parentDir, "deleted_"+base)
-		}
-		if err := os.Rename(dir, renamed); err != nil {
-			vlog.Errorf("Rename(%v, %v) failed: %v", dir, renamed, err)
-		}
-	}
-	isSetuid = possiblyMockIsSetuid
-}
-
-func possiblyMockIsSetuid(fileStat os.FileInfo) bool {
-	vlog.VI(2).Infof("Mock isSetuid is reporting: %v", *mockIsSetuid)
-	return *mockIsSetuid
-}
-
-func WrapBaseCleanupDir(path, helper string) {
-	baseCleanupDir(path, helper)
-}
diff --git a/services/device/internal/impl/debug_perms_test.go b/services/device/internal/impl/perms/debug_perms_test.go
similarity index 96%
rename from services/device/internal/impl/debug_perms_test.go
rename to services/device/internal/impl/perms/debug_perms_test.go
index 306cebe..9c6e757 100644
--- a/services/device/internal/impl/debug_perms_test.go
+++ b/services/device/internal/impl/perms/debug_perms_test.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package impl_test
+package perms_test
 
 import (
 	"io/ioutil"
@@ -226,15 +226,15 @@
 			[]string{
 				// STDERR and STDOUT are not handled through the log package so
 				// are not included here.
-				"impl.test.INFO",
-				"impl.test.<*>.INFO.<timestamp>",
+				"perms.test.INFO",
+				"perms.test.<*>.INFO.<timestamp>",
 			},
 		},
 	}
-	res := utiltest.NewGlobTestRegexHelper(`impl\.test`)
+	res := utiltest.NewGlobTestRegexHelper(`perms\.test`)
 
 	// Bob claimed the DM so can access it.
-	utiltest.VerifyGlob(t, bobCtx, "impl.test", dmGlobtests, res)
+	utiltest.VerifyGlob(t, bobCtx, "perms.test", dmGlobtests, res)
 	utiltest.VerifyStatsValues(t, bobCtx, "dm", "__debug", "stats/system/start-time*")
 
 	// Without permissions, hackerjoe can't access the device manager.
@@ -247,14 +247,14 @@
 
 	// Alice is an adminstrator and so can can access device manager __debug
 	// values.
-	utiltest.VerifyGlob(t, aliceCtx, "impl.test", dmGlobtests, res)
+	utiltest.VerifyGlob(t, aliceCtx, "perms.test", dmGlobtests, res)
 	utiltest.VerifyStatsValues(t, aliceCtx, "dm", "__debug", "stats/system/start-time*")
 
 	// Bob gives debug access to the device manager to hackerjoe
 	updateAccessList(t, bobCtx, "root/hackerjoe/$", string(access.Debug), "dm", "device")
 
 	// hackerjoe can now access the device manager
-	utiltest.VerifyGlob(t, hjCtx, "impl.test", dmGlobtests, res)
+	utiltest.VerifyGlob(t, hjCtx, "perms.test", dmGlobtests, res)
 	utiltest.VerifyStatsValues(t, hjCtx, "dm", "__debug", "stats/system/start-time*")
 
 	// Cleanly shut down the device manager.
diff --git a/services/device/internal/impl/args_darwin_test.go b/services/device/internal/impl/perms/doc.go
similarity index 65%
copy from services/device/internal/impl/args_darwin_test.go
copy to services/device/internal/impl/perms/doc.go
index c1d0b11..afd8566 100644
--- a/services/device/internal/impl/args_darwin_test.go
+++ b/services/device/internal/impl/perms/doc.go
@@ -2,9 +2,6 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package impl_test
+package perms
 
-const (
-	testUserName        = "_uucp"
-	anotherTestUserName = "_lp"
-)
+// Test code for claiming and permission list code in the device manager.
diff --git a/services/device/internal/impl/perms/impl_test.go b/services/device/internal/impl/perms/impl_test.go
new file mode 100644
index 0000000..37a5102
--- /dev/null
+++ b/services/device/internal/impl/perms/impl_test.go
@@ -0,0 +1,19 @@
+// 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 perms_test
+
+import (
+	"testing"
+
+	"v.io/x/ref/services/device/internal/impl/utiltest"
+)
+
+func TestMain(m *testing.M) {
+	utiltest.TestMainImpl(m)
+}
+
+func TestSuidHelper(t *testing.T) {
+	utiltest.TestSuidHelperImpl(t)
+}
diff --git a/services/device/internal/impl/perms/perms_test.go b/services/device/internal/impl/perms/perms_test.go
new file mode 100644
index 0000000..83bd086
--- /dev/null
+++ b/services/device/internal/impl/perms/perms_test.go
@@ -0,0 +1,191 @@
+// 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 perms_test
+
+import (
+	"bytes"
+	"crypto/md5"
+	"encoding/hex"
+	"syscall"
+	"testing"
+
+	"v.io/v23"
+	"v.io/v23/security"
+	"v.io/v23/security/access"
+	"v.io/v23/services/device"
+	"v.io/v23/verror"
+
+	"v.io/x/ref/services/device/internal/impl"
+	"v.io/x/ref/services/device/internal/impl/utiltest"
+	"v.io/x/ref/services/internal/servicetest"
+	"v.io/x/ref/test/testutil"
+)
+
+// TestDeviceManagerClaim claims a devicemanager and tests AccessList permissions on
+// its methods.
+func TestDeviceManagerClaim(t *testing.T) {
+	ctx, shutdown := utiltest.InitForTest()
+	defer shutdown()
+
+	// root blessing provider so that the principals of all the contexts
+	// recognize each other.
+	idp := testutil.NewIDProvider("root")
+	if err := idp.Bless(v23.GetPrincipal(ctx), "ctx"); err != nil {
+		t.Fatal(err)
+	}
+
+	sh, deferFn := servicetest.CreateShellAndMountTable(t, ctx, nil)
+	defer deferFn()
+
+	// Set up mock application and binary repositories.
+	envelope, cleanup := utiltest.StartMockRepos(t, ctx)
+	defer cleanup()
+
+	root, cleanup := servicetest.SetupRootDir(t, "devicemanager")
+	defer cleanup()
+	if err := impl.SaveCreatorInfo(root); err != nil {
+		t.Fatal(err)
+	}
+
+	// Create a script wrapping the test target that implements suidhelper.
+	helperPath := utiltest.GenerateSuidHelperScript(t, root)
+
+	// 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)
+	pid := servicetest.ReadPID(t, dmh)
+	defer syscall.Kill(pid, syscall.SIGINT)
+
+	*envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.AppCmd, "google naps", "trapp")
+
+	claimantCtx := utiltest.CtxWithNewPrincipal(t, ctx, idp, "claimant")
+	octx, err := v23.WithPrincipal(ctx, testutil.NewPrincipal("other"))
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// Unclaimed devices cannot do anything but be claimed.
+	// TODO(ashankar,caprita): The line below will currently fail with
+	// ErrUnclaimedDevice != NotTrusted. NotTrusted can be avoided by
+	// passing options.SkipServerEndpointAuthorization{} to the "Install" RPC.
+	// Refactor the helper function to make this possible.
+	//installAppExpectError(t, octx, impl.ErrUnclaimedDevice.ID)
+
+	// Claim the device with an incorrect pairing token should fail.
+	utiltest.ClaimDeviceExpectError(t, claimantCtx, "claimable", "mydevice", "badtoken", impl.ErrInvalidPairingToken.ID)
+	// But succeed with a valid pairing token
+	utiltest.ClaimDevice(t, claimantCtx, "claimable", "dm", "mydevice", pairingToken)
+
+	// Installation should succeed since claimantRT is now the "owner" of
+	// the devicemanager.
+	appID := utiltest.InstallApp(t, claimantCtx)
+
+	// octx will not install the app now since it doesn't recognize the
+	// device's blessings. The error returned will be ErrNoServers as that
+	// is what the IPC stack does when there are no authorized servers.
+	utiltest.InstallAppExpectError(t, octx, verror.ErrNoServers.ID)
+	// Even if it does recognize the device (by virtue of recognizing the
+	// claimant), the device will not allow it to install.
+	if err := v23.GetPrincipal(octx).AddToRoots(v23.GetPrincipal(claimantCtx).BlessingStore().Default()); err != nil {
+		t.Fatal(err)
+	}
+	utiltest.InstallAppExpectError(t, octx, verror.ErrNoAccess.ID)
+
+	// Create the local server that the app uses to let us know it's ready.
+	pingCh, cleanup := utiltest.SetupPingServer(t, claimantCtx)
+	defer cleanup()
+
+	// Start an instance of the app.
+	instanceID := utiltest.LaunchApp(t, claimantCtx, appID)
+
+	// Wait until the app pings us that it's ready.
+	pingCh.WaitForPingArgs(t)
+	utiltest.Resolve(t, ctx, "trapp", 1)
+	utiltest.KillApp(t, claimantCtx, appID, instanceID)
+
+	// TODO(gauthamt): Test that AccessLists persist across devicemanager restarts
+}
+
+func TestDeviceManagerUpdateAccessList(t *testing.T) {
+	ctx, shutdown := utiltest.InitForTest()
+	defer shutdown()
+
+	// Identity provider to ensure that all processes recognize each
+	// others' blessings.
+	idp := testutil.NewIDProvider("root")
+	ctx = utiltest.CtxWithNewPrincipal(t, ctx, idp, "self")
+
+	sh, deferFn := servicetest.CreateShellAndMountTable(t, ctx, nil)
+	defer deferFn()
+
+	// Set up mock application and binary repositories.
+	envelope, cleanup := utiltest.StartMockRepos(t, ctx)
+	defer cleanup()
+
+	root, cleanup := servicetest.SetupRootDir(t, "devicemanager")
+	defer cleanup()
+	if err := impl.SaveCreatorInfo(root); err != nil {
+		t.Fatal(err)
+	}
+
+	selfCtx := ctx
+	octx := utiltest.CtxWithNewPrincipal(t, selfCtx, idp, "other")
+
+	// 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")
+	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")
+
+	// On an unclaimed device manager, there will be no AccessLists.
+	if _, _, err := device.DeviceClient("claimable").GetPermissions(selfCtx); err == nil {
+		t.Fatalf("GetPermissions should have failed but didn't.")
+	}
+
+	// Claim the devicemanager as "root/self/mydevice"
+	utiltest.ClaimDevice(t, selfCtx, "claimable", "dm", "mydevice", utiltest.NoPairingToken)
+	expectedAccessList := make(access.Permissions)
+	for _, tag := range access.AllTypicalTags() {
+		expectedAccessList[string(tag)] = access.AccessList{In: []security.BlessingPattern{"root/$", "root/self/$", "root/self/mydevice/$"}}
+	}
+	var b bytes.Buffer
+	if err := expectedAccessList.WriteTo(&b); err != nil {
+		t.Fatalf("Failed to save AccessList:%v", err)
+	}
+	// Note, "version" below refers to the Permissions version, not the device
+	// manager version.
+	md5hash := md5.Sum(b.Bytes())
+	expectedVersion := hex.EncodeToString(md5hash[:])
+	deviceStub := device.DeviceClient("dm/device")
+	perms, version, err := deviceStub.GetPermissions(selfCtx)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if version != expectedVersion {
+		t.Fatalf("getAccessList expected:%v(%v), got:%v(%v)", expectedAccessList, expectedVersion, perms, version)
+	}
+	// Install from octx should fail, since it does not match the AccessList.
+	utiltest.InstallAppExpectError(t, octx, verror.ErrNoAccess.ID)
+
+	newAccessList := make(access.Permissions)
+	for _, tag := range access.AllTypicalTags() {
+		newAccessList.Add("root/other", string(tag))
+	}
+	if err := deviceStub.SetPermissions(selfCtx, newAccessList, "invalid"); err == nil {
+		t.Fatalf("SetPermissions should have failed with invalid version")
+	}
+	if err := deviceStub.SetPermissions(selfCtx, newAccessList, version); err != nil {
+		t.Fatal(err)
+	}
+	// Install should now fail with selfCtx, which no longer matches the
+	// AccessLists but succeed with octx, which does.
+	utiltest.InstallAppExpectError(t, selfCtx, verror.ErrNoAccess.ID)
+	utiltest.InstallApp(t, octx)
+}
diff --git a/services/device/internal/impl/reaping/impl_test.go b/services/device/internal/impl/reaping/impl_test.go
index abc7f7a..66d1c4e 100644
--- a/services/device/internal/impl/reaping/impl_test.go
+++ b/services/device/internal/impl/reaping/impl_test.go
@@ -14,8 +14,6 @@
 	utiltest.TestMainImpl(m)
 }
 
-// TestSuidHelper is testing boilerplate for suidhelper that does not
-// create a runtime because the suidhelper is not a Vanadium application.
 func TestSuidHelper(t *testing.T) {
 	utiltest.TestSuidHelperImpl(t)
 }
diff --git a/services/device/internal/impl/util.go b/services/device/internal/impl/util.go
index dd22307..d7aa4f2 100644
--- a/services/device/internal/impl/util.go
+++ b/services/device/internal/impl/util.go
@@ -129,7 +129,7 @@
 	return nil
 }
 
-func baseCleanupDir(path, helper string) {
+func BaseCleanupDir(path, helper string) {
 	if helper != "" {
 		out, err := exec.Command(helper, "--rm", path).CombinedOutput()
 		if err != nil {
@@ -150,7 +150,7 @@
 	return filepath.Join(c.Root, "device-manager", "device-data", "acls")
 }
 
-// cleanupDir is defined like this so we can override its implementation for
-// tests. cleanupDir will use the helper to delete application state possibly
+// CleanupDir is defined like this so we can override its implementation for
+// tests. CleanupDir will use the helper to delete application state possibly
 // owned by different accounts if helper is provided.
-var cleanupDir = baseCleanupDir
+var CleanupDir = BaseCleanupDir
diff --git a/services/device/internal/impl/utiltest/helpers.go b/services/device/internal/impl/utiltest/helpers.go
index 5b753c0..6469493 100644
--- a/services/device/internal/impl/utiltest/helpers.go
+++ b/services/device/internal/impl/utiltest/helpers.go
@@ -33,8 +33,10 @@
 	"v.io/v23/services/stats"
 	"v.io/v23/verror"
 
+	"v.io/x/ref/envvar"
 	_ "v.io/x/ref/profiles/roaming"
 	"v.io/x/ref/services/device/internal/impl"
+	"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"
@@ -50,6 +52,22 @@
 	impl.Describe = func() (descr device.Description, err error) {
 		return device.Description{Profiles: map[string]struct{}{"test-profile": struct{}{}}}, nil
 	}
+
+	impl.CleanupDir = func(dir, helper string) {
+		if dir == "" {
+			return
+		}
+		parentDir, base := filepath.Dir(dir), filepath.Base(dir)
+		var renamed string
+		if helper != "" {
+			renamed = filepath.Join(parentDir, "helper_deleted_"+base)
+		} else {
+			renamed = filepath.Join(parentDir, "deleted_"+base)
+		}
+		if err := os.Rename(dir, renamed); err != nil {
+			vlog.Errorf("Rename(%v, %v) failed: %v", dir, renamed, err)
+		}
+	}
 }
 
 func EnvelopeFromShell(sh *modules.Shell, env []string, cmd, title string, args ...string) application.Envelope {
@@ -665,3 +683,40 @@
 	}
 	return u.Username
 }
+
+func InitForTest() (*context.T, v23.Shutdown) {
+	roots, _ := envvar.NamespaceRoots()
+	for key, _ := range roots {
+		os.Unsetenv(key)
+	}
+	ctx, shutdown := test.InitForTest()
+	v23.GetNamespace(ctx).CacheCtl(naming.DisableCache(true))
+	return ctx, shutdown
+}
+
+func StartRealBinaryRepository(t *testing.T, ctx *context.T, von string) func() {
+	rootDir, err := binarylib.SetupRootDir("")
+	if err != nil {
+		t.Fatalf("binarylib.SetupRootDir failed: %v", err)
+	}
+	state, err := binarylib.NewState(rootDir, "", 3)
+	if err != nil {
+		t.Fatalf("binarylib.NewState failed: %v", err)
+	}
+	server, _ := servicetest.NewServer(ctx)
+	d, err := binarylib.NewDispatcher(v23.GetPrincipal(ctx), state)
+	if err != nil {
+		t.Fatalf("server.NewDispatcher failed: %v", err)
+	}
+	if err := server.ServeDispatcher(von, d); err != nil {
+		t.Fatalf("server.ServeDispatcher failed: %v", err)
+	}
+	return func() {
+		if err := server.Stop(); err != nil {
+			t.Fatalf("server.Stop failed: %v", err)
+		}
+		if err := os.RemoveAll(rootDir); err != nil {
+			t.Fatalf("os.RemoveAll(%q) failed: %v", rootDir, err)
+		}
+	}
+}
diff --git a/services/device/internal/suid/system.go b/services/device/internal/suid/system.go
index 285c4e3..3e76d82 100644
--- a/services/device/internal/suid/system.go
+++ b/services/device/internal/suid/system.go
@@ -58,11 +58,12 @@
 func (hw *WorkParameters) Exec() error {
 	attr := new(syscall.ProcAttr)
 
-	if dir, err := os.Getwd(); err != nil {
+	dir, err := os.Getwd()
+	if err != nil {
 		log.Printf("error Getwd(): %v", err)
 		return verror.New(errGetwdFailed, nil, err)
-		attr.Dir = dir
 	}
+	attr.Dir = dir
 	attr.Env = hw.envv
 	attr.Files = []uintptr{
 		uintptr(syscall.Stdin),
diff --git a/services/device/internal/sysinit/init_darwin.go b/services/device/internal/sysinit/init_darwin.go
index c6305d3..b1ae822 100644
--- a/services/device/internal/sysinit/init_darwin.go
+++ b/services/device/internal/sysinit/init_darwin.go
@@ -6,10 +6,8 @@
 
 func InitSystem() string {
 	panic("Darwin not supported yet")
-	return ""
 }
 
 func New(system string, sd *ServiceDescription) InstallSystemInit {
 	panic("Darwin not supported yet")
-	return nil
 }
diff --git a/services/identity/identity.vdl.go b/services/identity/identity.vdl.go
index 41b991c..fc7b6d2 100644
--- a/services/identity/identity.vdl.go
+++ b/services/identity/identity.vdl.go
@@ -29,7 +29,7 @@
 }
 
 func (BlessingRootResponse) __VDLReflect(struct {
-	Name string "v.io/x/ref/services/identity.BlessingRootResponse"
+	Name string `vdl:"v.io/x/ref/services/identity.BlessingRootResponse"`
 }) {
 }
 
diff --git a/services/identity/identityd/main.go b/services/identity/identityd/main.go
index 1639b9d..1c57547 100644
--- a/services/identity/identityd/main.go
+++ b/services/identity/identityd/main.go
@@ -73,11 +73,6 @@
 		vlog.Fatalf("Failed to start RevocationManager: %v", err)
 	}
 
-	bname, _, err := util.RootCertificateDetails(v23.GetPrincipal(ctx).BlessingStore().Default())
-	if err != nil {
-		vlog.Fatalf("Failed to get root Blessings name: %v", err)
-	}
-
 	listenSpec := v23.GetListenSpec(ctx)
 	s := server.NewIdentityServer(
 		googleoauth,
@@ -85,7 +80,7 @@
 		reader,
 		revocationManager,
 		googleOAuthBlesserParams(googleoauth, revocationManager),
-		caveats.NewBrowserCaveatSelector(*assetsPrefix, bname),
+		caveats.NewBrowserCaveatSelector(*assetsPrefix),
 		&emailClassifier,
 		*assetsPrefix,
 		*mountPrefix)
diff --git a/services/identity/identityd/sql.go b/services/identity/identityd/sql.go
index b01e47a..fc5c127 100644
--- a/services/identity/identityd/sql.go
+++ b/services/identity/identityd/sql.go
@@ -33,15 +33,15 @@
 // needed to encrypt the information sent over the wire.
 type sqlConfig struct {
 	// DataSourceName is the connection string required by go-sql-driver: "[username[:password]@][protocol[(address)]]/dbname".
-	DataSourceName string `json:dataSourceName`
+	DataSourceName string
 	// RootCertPath is the root certificate of the sql server for ssl.
-	RootCertPath string `json:rootCertPath`
+	RootCertPath string
 	// TLSServerName is the domain name of the sql server for ssl.
-	TLSServerName string `json:tlsServerName`
+	TLSServerName string
 	// ClientCertPath is the client certificate for ssl.
-	ClientCertPath string `json:clientCertPath`
+	ClientCertPath string
 	// ClientKeyPath is the client private key for ssl.
-	ClientKeyPath string `json:clientKeyPath`
+	ClientKeyPath string
 }
 
 func dbFromConfigFile(file string) (*sql.DB, error) {
diff --git a/services/identity/identitylib/test_identityd.go b/services/identity/identitylib/test_identityd.go
index 53c8587..1559091 100644
--- a/services/identity/identitylib/test_identityd.go
+++ b/services/identity/identitylib/test_identityd.go
@@ -55,7 +55,7 @@
 		}
 		host, _, err := net.SplitHostPort(addr)
 		if err != nil {
-			return fmt.Errorf("Failed to parse %q: %v", httpAddr, err)
+			return fmt.Errorf("Failed to parse %q: %v", addr, err)
 		}
 		certFile, keyFile, err := util.WriteCertAndKey(host, duration)
 		if err != nil {
@@ -79,7 +79,7 @@
 
 	auditor, reader := auditor.NewMockBlessingAuditor()
 	revocationManager := revocation.NewMockRevocationManager()
-	oauthProvider := oauth.NewMockOAuth()
+	oauthProvider := oauth.NewMockOAuth("testemail@example.com")
 
 	params := blesser.OAuthBlesserParams{
 		OAuthProvider:     oauthProvider,
diff --git a/services/identity/internal/blesser/macaroon_test.go b/services/identity/internal/blesser/macaroon_test.go
index 95ebbd1..7e537f6 100644
--- a/services/identity/internal/blesser/macaroon_test.go
+++ b/services/identity/internal/blesser/macaroon_test.go
@@ -58,8 +58,8 @@
 		t.Errorf("Got blessings with %d names, want exactly one name", num)
 	}
 	wantName := "provider/user"
-	if cavs := binfo[wantName]; !reflect.DeepEqual(cavs, []security.Caveat{cOnlyMethodFoo}) {
-		t.Errorf("BlessingsInfo %v does not have name %s for the caveat %v", binfo, wantName)
+	if got, want := binfo[wantName], []security.Caveat{cOnlyMethodFoo}; !reflect.DeepEqual(got, want) {
+		t.Errorf("binfo[%q]: Got %v, want %v", wantName, got, want)
 	}
 }
 
diff --git a/services/identity/internal/blesser/oauth_test.go b/services/identity/internal/blesser/oauth_test.go
index 48b569f..91c5a23 100644
--- a/services/identity/internal/blesser/oauth_test.go
+++ b/services/identity/internal/blesser/oauth_test.go
@@ -6,6 +6,7 @@
 
 import (
 	"reflect"
+	"strings"
 	"testing"
 	"time"
 
@@ -15,13 +16,18 @@
 	"v.io/v23/security"
 )
 
+func join(elements ...string) string {
+	return strings.Join(elements, security.ChainSeparator)
+}
+
 func TestOAuthBlesser(t *testing.T) {
 	var (
 		provider, user = testutil.NewPrincipal(), testutil.NewPrincipal()
 		ctx, call      = fakeContextAndCall(provider, user)
 	)
+	mockEmail := "testemail@example.com"
 	blesser := NewOAuthBlesserServer(OAuthBlesserParams{
-		OAuthProvider:    oauth.NewMockOAuth(),
+		OAuthProvider:    oauth.NewMockOAuth(mockEmail),
 		BlessingDuration: time.Hour,
 	})
 
@@ -30,7 +36,7 @@
 		t.Errorf("BlessUsingAccessToken failed: %v", err)
 	}
 
-	wantExtension := "users" + security.ChainSeparator + oauth.MockEmail + security.ChainSeparator + oauth.MockClient
+	wantExtension := join("users", mockEmail, oauth.MockClient)
 	if extension != wantExtension {
 		t.Errorf("got extension: %s, want: %s", extension, wantExtension)
 	}
@@ -45,13 +51,13 @@
 		t.Errorf("Got blessing with info %v, want nil", got)
 	}
 	// But once it recognizes the provider, it should see exactly the name
-	// "provider/testemail@google.com/test-client".
+	// "provider/testemail@example.com/test-client".
 	user.AddToRoots(b)
 	binfo := user.BlessingsInfo(b)
 	if num := len(binfo); num != 1 {
 		t.Errorf("Got blessings with %d names, want exactly one name", num)
 	}
-	if _, ok := binfo["provider"+security.ChainSeparator+wantExtension]; !ok {
+	if _, ok := binfo[join("provider", wantExtension)]; !ok {
 		t.Errorf("BlessingsInfo %v does not have name %s", binfo, wantExtension)
 	}
 }
diff --git a/services/identity/internal/caveats/browser_caveat_selector.go b/services/identity/internal/caveats/browser_caveat_selector.go
index 37e1079..cbb0400 100644
--- a/services/identity/internal/caveats/browser_caveat_selector.go
+++ b/services/identity/internal/caveats/browser_caveat_selector.go
@@ -17,19 +17,18 @@
 
 type browserCaveatSelector struct {
 	assetsPrefix string
-	blessingName string
 }
 
 // NewBrowserCaveatSelector returns a caveat selector that renders a form in the
 // to accept user caveat selections.
-func NewBrowserCaveatSelector(assetsPrefix, blessingName string) CaveatSelector {
-	return &browserCaveatSelector{assetsPrefix, blessingName}
+func NewBrowserCaveatSelector(assetsPrefix string) CaveatSelector {
+	return &browserCaveatSelector{assetsPrefix}
 }
 
-func (s *browserCaveatSelector) Render(blessingExtension, state, redirectURL string, w http.ResponseWriter, r *http.Request) error {
+func (s *browserCaveatSelector) Render(blessingName, state, redirectURL string, w http.ResponseWriter, r *http.Request) error {
 	tmplargs := struct {
-		Email, Macaroon, MacaroonURL, AssetsPrefix, BlessingName string
-	}{blessingExtension, state, redirectURL, s.assetsPrefix, s.blessingName}
+		BlessingName, Macaroon, MacaroonURL, AssetsPrefix string
+	}{blessingName, state, redirectURL, s.assetsPrefix}
 	w.Header().Set("Context-Type", "text/html")
 	if err := templates.SelectCaveats.Execute(w, tmplargs); err != nil {
 		return err
diff --git a/services/identity/internal/caveats/caveat_selector.go b/services/identity/internal/caveats/caveat_selector.go
index f0746b1..487c5a6 100644
--- a/services/identity/internal/caveats/caveat_selector.go
+++ b/services/identity/internal/caveats/caveat_selector.go
@@ -13,10 +13,10 @@
 type CaveatSelector interface {
 	// Render renders the caveat input form. When the user has completed inputing caveats,
 	// Render should redirect to the specified redirect route.
-	// blessingExtension is the extension used for the blessings that is being caveated.
+	// blessingName is the name used for the blessings that is being caveated.
 	// state is any state passed by the caller (e.g., for CSRF mitigation) and is returned by ParseSelections.
 	// redirectRoute is the route to be returned to.
-	Render(blessingExtension, state, redirectURL string, w http.ResponseWriter, r *http.Request) error
+	Render(blessingName, state, redirectURL string, w http.ResponseWriter, r *http.Request) error
 	// ParseSelections parse the users choices of Caveats, and returns the information needed to create them,
 	// the state passed to Render, and any additionalExtension selected by the user to further extend the blessing.
 	ParseSelections(r *http.Request) (caveats []CaveatInfo, state string, additionalExtension string, err error)
diff --git a/services/identity/internal/identityd_test/main.go b/services/identity/internal/identityd_test/main.go
index ca59165..f7977de 100644
--- a/services/identity/internal/identityd_test/main.go
+++ b/services/identity/internal/identityd_test/main.go
@@ -33,11 +33,13 @@
 	assetsPrefix     = flag.String("assets-prefix", "", "host serving the web assets for the identity server")
 	mountPrefix      = flag.String("mount-prefix", "identity", "mount name prefix to use. May be rooted.")
 	browser          = flag.Bool("browser", false, "whether to open a browser caveat selector")
+	oauthEmail       = flag.String("oauth-email", "testemail@example.com", "Username for the mock oauth to put in the returned blessings")
 )
 
 func main() {
 	flag.Usage = usage
-	flag.Parse()
+	ctx, shutdown := v23.Init()
+	defer shutdown()
 
 	// Duration to use for tls cert and blessing duration.
 	duration := 365 * 24 * time.Hour
@@ -50,7 +52,10 @@
 		}
 		host, _, err := net.SplitHostPort(addr)
 		if err != nil {
-			vlog.Fatalf("Failed to parse %q: %v", httpAddr, err)
+			// NOTE(caprita): The (non-test) identityd binary
+			// accepts an address with no port.  Should this test
+			// binary do the same instead?
+			vlog.Fatalf("Failed to parse %q: %v", addr, err)
 		}
 		certFile, keyFile, err := util.WriteCertAndKey(host, duration)
 		if err != nil {
@@ -63,7 +68,7 @@
 
 	auditor, reader := auditor.NewMockBlessingAuditor()
 	revocationManager := revocation.NewMockRevocationManager()
-	oauthProvider := oauth.NewMockOAuth()
+	oauthProvider := oauth.NewMockOAuth(*oauthEmail)
 
 	params := blesser.OAuthBlesserParams{
 		OAuthProvider:     oauthProvider,
@@ -71,16 +76,9 @@
 		RevocationManager: revocationManager,
 	}
 
-	ctx, shutdown := v23.Init()
-	defer shutdown()
-
 	caveatSelector := caveats.NewMockCaveatSelector()
 	if *browser {
-		bname, _, err := util.RootCertificateDetails(v23.GetPrincipal(ctx).BlessingStore().Default())
-		if err != nil {
-			vlog.Fatalf("Failed to get root Blessings name: %v", err)
-		}
-		caveatSelector = caveats.NewBrowserCaveatSelector(*assetsPrefix, bname)
+		caveatSelector = caveats.NewBrowserCaveatSelector(*assetsPrefix)
 	}
 
 	listenSpec := v23.GetListenSpec(ctx)
diff --git a/services/identity/internal/oauth/handler.go b/services/identity/internal/oauth/handler.go
index fcb6c41..c2de6ab 100644
--- a/services/identity/internal/oauth/handler.go
+++ b/services/identity/internal/oauth/handler.go
@@ -381,7 +381,19 @@
 		util.HTTPServerError(w, fmt.Errorf("failed to create new token: %v", err))
 		return
 	}
-	if err := h.args.CaveatSelector.Render(email, outputMacaroon, redirectURL(h.args.Addr, sendMacaroonRoute), w, r); err != nil {
+	localBlessings := security.DefaultBlessingPatterns(h.args.Principal)
+	if len(localBlessings) == 0 {
+		vlog.Infof("server principal has no blessings: %v", h.args.Principal)
+		util.HTTPServerError(w, fmt.Errorf("failed to get server blessings"))
+		return
+	}
+	parts := []string{
+		string(localBlessings[0]),
+		h.args.EmailClassifier.Classify(email),
+		email,
+	}
+	fullBlessingName := strings.Join(parts, security.ChainSeparator)
+	if err := h.args.CaveatSelector.Render(fullBlessingName, outputMacaroon, redirectURL(h.args.Addr, sendMacaroonRoute), w, r); err != nil {
 		vlog.Errorf("Unable to invoke render caveat selector: %v", err)
 		util.HTTPServerError(w, err)
 	}
diff --git a/services/identity/internal/oauth/mockoauth.go b/services/identity/internal/oauth/mockoauth.go
index c122af7..3a0e828 100644
--- a/services/identity/internal/oauth/mockoauth.go
+++ b/services/identity/internal/oauth/mockoauth.go
@@ -4,16 +4,15 @@
 
 package oauth
 
-const (
-	MockEmail  = "testemail@google.com"
-	MockClient = "test-client"
-)
+const MockClient = "test-client"
 
 // mockOAuth is a mock OAuthProvider for use in tests.
-type mockOAuth struct{}
+type mockOAuth struct {
+	email string
+}
 
-func NewMockOAuth() OAuthProvider {
-	return &mockOAuth{}
+func NewMockOAuth(mockEmail string) OAuthProvider {
+	return &mockOAuth{email: mockEmail}
 }
 
 func (m *mockOAuth) AuthURL(redirectUrl string, state string, _ AuthURLApproval) string {
@@ -21,9 +20,9 @@
 }
 
 func (m *mockOAuth) ExchangeAuthCodeForEmail(string, string) (string, error) {
-	return MockEmail, nil
+	return m.email, nil
 }
 
 func (m *mockOAuth) GetEmailAndClientName(string, []AccessTokenClient) (string, string, error) {
-	return MockEmail, MockClient, nil
+	return m.email, MockClient, nil
 }
diff --git a/services/identity/internal/templates/caveats.go b/services/identity/internal/templates/caveats.go
index 72caced..eef93ad 100644
--- a/services/identity/internal/templates/caveats.go
+++ b/services/identity/internal/templates/caveats.go
@@ -25,14 +25,14 @@
       <span class="service-name">Identity Provider</span>
     </nav>
     <nav class="right">
-      <a href="#">{{.Email}}</a>
+      <a href="#">{{.BlessingName}}</a>
     </nav>
   </header>
 
   <main class="add-blessing">
 
     <form method="POST" id="caveats-form" name="input"
-    action="{{.MacaroonURL}}" role="form">
+    action="{{.MacaroonURL}}" role="form" novalidate>
       <input type="text" class="hidden" name="macaroon" value="{{.Macaroon}}">
 
       <h1 class="page-head">Add blessing</h1>
@@ -59,7 +59,7 @@
 
       <label for="blessingExtension">Blessing name</label>
       <div class="value">
-        {{.BlessingName}}/{{.Email}}/
+        {{.BlessingName}}/
         <input name="blessingExtension" type="text" placeholder="extension">
         <input type="hidden" id="timezoneOffset" name="timezoneOffset">
       </div>
@@ -226,6 +226,8 @@
 
     // Set the datetime picker to have a default value of one day from now.
     $('.expiry').val(moment().add(1, 'd').format('YYYY-MM-DDTHH:mm'));
+    // Remove the clear button from the date input.
+    $('.expiry').attr('required', 'required');
 
     // Activate the cancel button.
     $('#cancel').click(function(){
diff --git a/services/identity/internal/util/certs.go b/services/identity/internal/util/certs.go
index 3c6e7b8..c6aeba6 100644
--- a/services/identity/internal/util/certs.go
+++ b/services/identity/internal/util/certs.go
@@ -26,7 +26,8 @@
 	generateCertFile := filepath.Join(strings.TrimSpace(string(output)), "generate_cert.go")
 	generateCertCmd := exec.Command("go", "run", generateCertFile, "--host", host, "--duration", duration.String())
 	generateCertCmd.Dir = tmpDir
-	if err := generateCertCmd.Run(); err != nil {
+	if output, err := generateCertCmd.CombinedOutput(); err != nil {
+		fmt.Fprintf(os.Stderr, "%v failed:\n%s\n", generateCertCmd.Args, output)
 		return "", "", fmt.Errorf("Could not generate key and cert: %v", err)
 	}
 	return filepath.Join(tmpDir, "cert.pem"), filepath.Join(tmpDir, "key.pem"), nil
diff --git a/services/identity/internal/util/write.go b/services/identity/internal/util/write.go
index 0dbf8e6..3e6833e 100644
--- a/services/identity/internal/util/write.go
+++ b/services/identity/internal/util/write.go
@@ -17,7 +17,7 @@
 func HTTPBadRequest(w http.ResponseWriter, req *http.Request, err error) {
 	w.WriteHeader(http.StatusBadRequest)
 	if e := tmplBadRequest.Execute(w, badRequestData{Request: requestString(req), Error: err}); e != nil {
-		vlog.Errorf("Failed to execute Bad Request Template:", e)
+		vlog.Errorf("Failed to execute Bad Request Template: %v", e)
 	}
 }
 
@@ -26,7 +26,7 @@
 func HTTPServerError(w http.ResponseWriter, err error) {
 	w.WriteHeader(http.StatusInternalServerError)
 	if e := tmplServerError.Execute(w, err); e != nil {
-		vlog.Errorf("Failed to execute Server Error template:", e)
+		vlog.Errorf("Failed to execute Server Error template: %v", e)
 	}
 }
 
diff --git a/services/internal/binarylib/client.go b/services/internal/binarylib/client.go
index 3420c0c..e8330c3 100644
--- a/services/internal/binarylib/client.go
+++ b/services/internal/binarylib/client.go
@@ -318,11 +318,11 @@
 
 func UploadFromFile(ctx *context.T, von, path string) (*security.Signature, error) {
 	file, err := os.Open(path)
-	defer file.Close()
 	if err != nil {
-		vlog.Errorf("Open(%v) failed: %v", err)
+		vlog.Errorf("Open(%v) failed: %v", path, err)
 		return nil, verror.New(errOperationFailed, ctx)
 	}
+	defer file.Close()
 	mediaInfo, err := packages.LoadMediaInfo(path)
 	if err != nil {
 		mediaInfo = packages.MediaInfoForFileName(path)
diff --git a/services/internal/binarylib/impl_test.go b/services/internal/binarylib/impl_test.go
index 9d72fae..e746e3a 100644
--- a/services/internal/binarylib/impl_test.go
+++ b/services/internal/binarylib/impl_test.go
@@ -47,7 +47,7 @@
 	}
 	state, err := binarylib.NewState(rootDir, listener.Addr().String(), depth)
 	if err != nil {
-		t.Fatalf("NewState(%v, %v) failed: %v", rootDir, listener.Addr().String(), depth, err)
+		t.Fatalf("NewState(%v, %v, %v) failed: %v", rootDir, listener.Addr().String(), depth, err)
 	}
 	go func() {
 		if err := http.Serve(listener, http.FileServer(binarylib.NewHTTPRoot(state))); err != nil {
diff --git a/services/internal/binarylib/service.go b/services/internal/binarylib/service.go
index dd8ad2c..75e6718 100644
--- a/services/internal/binarylib/service.go
+++ b/services/internal/binarylib/service.go
@@ -111,7 +111,7 @@
 	}
 	nameFile := filepath.Join(tmpDir, nameFileName)
 	if err := ioutil.WriteFile(nameFile, []byte(i.suffix), os.FileMode(0600)); err != nil {
-		vlog.Errorf("WriteFile(%q) failed: %v", nameFile)
+		vlog.Errorf("WriteFile(%q) failed: %v", nameFile, err)
 		return verror.New(ErrOperationFailed, ctx)
 	}
 
@@ -272,7 +272,7 @@
 	infoFile := filepath.Join(i.path, mediaInfoFileName)
 	jInfo, err := ioutil.ReadFile(infoFile)
 	if err != nil {
-		vlog.Errorf("ReadFile(%q) failed: %v", infoFile)
+		vlog.Errorf("ReadFile(%q) failed: %v", infoFile, err)
 		return []binary.PartInfo{}, repository.MediaInfo{}, verror.New(ErrOperationFailed, ctx)
 	}
 	var mediaInfo repository.MediaInfo
diff --git a/services/internal/fs/simplestore_test.go b/services/internal/fs/simplestore_test.go
index f439c3f..d4a7e55 100644
--- a/services/internal/fs/simplestore_test.go
+++ b/services/internal/fs/simplestore_test.go
@@ -94,7 +94,7 @@
 	for _, p := range pvs {
 		v, err := ts.BindObject(p.Path).Get(nil)
 		if err != nil {
-			return fmt.Errorf("Get(%s) expected to succeed but failed", p, err)
+			return fmt.Errorf("Get(%s) expected to succeed but failed: %v", p, err)
 		}
 		if !reflect.DeepEqual(p.Expected, v.Value) {
 			return fmt.Errorf("Unexpected non-equality for %s: got %v, expected %v", p.Path, v.Value, p.Expected)
@@ -558,7 +558,7 @@
 
 	// Write the original memstore to a GOB file.
 	if err := gobPersist(t, originalMemstore); err != nil {
-		t.Fatalf("gobPersist() failed: %v")
+		t.Fatalf("gobPersist() failed: %v", err)
 	}
 
 	// Open the GOB format file.
diff --git a/services/internal/pathperms/permsaccess.go b/services/internal/pathperms/permsaccess.go
index 6e463ac..81c6b76 100644
--- a/services/internal/pathperms/permsaccess.go
+++ b/services/internal/pathperms/permsaccess.go
@@ -49,7 +49,7 @@
 }
 
 // Get returns the Permissions from the data file in dir.
-func (store PathStore) Get(dir string) (access.Permissions, string, error) {
+func (store *PathStore) Get(dir string) (access.Permissions, string, error) {
 	permspath := filepath.Join(dir, permsName)
 	sigpath := filepath.Join(dir, sigName)
 	defer store.lockPath(dir)()
@@ -57,7 +57,7 @@
 }
 
 // TODO(rjkroege): Improve lock handling.
-func (store PathStore) lockPath(dir string) func() {
+func (store *PathStore) lockPath(dir string) func() {
 	store.lk.Lock()
 	lck, contains := store.pthlks[dir]
 	if !contains {
@@ -107,7 +107,7 @@
 
 // Set writes the specified Permissions to the provided directory with
 // enforcement of version synchronization mechanism and locking.
-func (store PathStore) Set(dir string, perms access.Permissions, version string) error {
+func (store *PathStore) Set(dir string, perms access.Permissions, version string) error {
 	return store.SetShareable(dir, perms, version, false)
 }
 
@@ -115,7 +115,7 @@
 // directory with enforcement of version synchronization mechanism and
 // locking with file modes that will give the application read-only
 // access to the permissions file.
-func (store PathStore) SetShareable(dir string, perms access.Permissions, version string, shareable bool) error {
+func (store *PathStore) SetShareable(dir string, perms access.Permissions, version string, shareable bool) error {
 	permspath := filepath.Join(dir, permsName)
 	sigpath := filepath.Join(dir, sigName)
 	defer store.lockPath(dir)()
@@ -187,7 +187,7 @@
 	return nil
 }
 
-func (store PathStore) PermsForPath(path string) (access.Permissions, bool, error) {
+func (store *PathStore) PermsForPath(path string) (access.Permissions, bool, error) {
 	perms, _, err := store.Get(path)
 	if os.IsNotExist(err) {
 		return nil, true, nil
diff --git a/services/mounttable/mounttablelib/mounttable.go b/services/mounttable/mounttablelib/mounttable.go
index 7ba7f4e..4fb98d6 100644
--- a/services/mounttable/mounttablelib/mounttable.go
+++ b/services/mounttable/mounttablelib/mounttable.go
@@ -28,6 +28,7 @@
 )
 
 const pkgPath = "v.io/x/ref/services/mounttable/mounttablelib"
+const defaultMaxNodesPerUser = 1000
 
 var (
 	errMalformedAddress = verror.Register(pkgPath+".errMalformedAddress", verror.NoRetry, "{1:}{2:} malformed address {3} for mounted server {4}{:_}")
@@ -36,6 +37,8 @@
 	errCantDeleteRoot   = verror.Register(pkgPath+".errCantDeleteRoot", verror.NoRetry, "{1:}{2:} cannot delete root node{:_}")
 	errNotEmpty         = verror.Register(pkgPath+".errNotEmpty", verror.NoRetry, "{1:}{2:} cannot delete {3}: has children{:_}")
 	errNamingLoop       = verror.Register(pkgPath+".errNamingLoop", verror.NoRetry, "{1:}{2:} Loop in namespace{:_}")
+	errTooManyNodes     = verror.Register(pkgPath+".errTooManyNodes", verror.NoRetry, "{1:}{2:} User has exceeded his node limit {:_}")
+	errNoSharedRoot     = verror.Register(pkgPath+".errNoSharedRoot", verror.NoRetry, "{1:}{2:} Server and User share no blessing root {:_}")
 )
 
 var (
@@ -51,19 +54,23 @@
 )
 
 type persistence interface {
-	persistPerms(name string, perm *VersionedPermissions) error
+	persistPerms(name, creator string, perm *VersionedPermissions) error
 	persistDelete(name string) error
 	close()
 }
 
 // mountTable represents a namespace.  One exists per server instance.
 type mountTable struct {
-	root          *node
-	superUsers    access.AccessList
-	persisting    bool
-	persist       persistence
-	nodeCounter   *stats.Integer
-	serverCounter *stats.Integer
+	sync.Mutex
+	root               *node
+	superUsers         access.AccessList
+	persisting         bool
+	persist            persistence
+	nodeCounter        *stats.Integer
+	serverCounter      *stats.Integer
+	perUserNodeCounter *stats.Map
+	maxNodesPerUser    int64
+	userPrefixes       []string
 }
 
 var _ rpc.Dispatcher = (*mountTable)(nil)
@@ -93,6 +100,7 @@
 	vPerms              *VersionedPermissions
 	permsTemplate       access.Permissions
 	explicitPermissions bool
+	creator             string
 }
 
 const templateVar = "%%"
@@ -109,9 +117,11 @@
 // statsPrefix is the prefix for for exported statistics objects.
 func NewMountTableDispatcher(permsFile, persistDir, statsPrefix string) (rpc.Dispatcher, error) {
 	mt := &mountTable{
-		root:          new(node),
-		nodeCounter:   stats.NewInteger(naming.Join(statsPrefix, "num-nodes")),
-		serverCounter: stats.NewInteger(naming.Join(statsPrefix, "num-mounted-servers")),
+		root:               new(node),
+		nodeCounter:        stats.NewInteger(naming.Join(statsPrefix, "num-nodes")),
+		serverCounter:      stats.NewInteger(naming.Join(statsPrefix, "num-mounted-servers")),
+		perUserNodeCounter: stats.NewMap(naming.Join(statsPrefix, "num-nodes-per-user")),
+		maxNodesPerUser:    defaultMaxNodesPerUser,
 	}
 	mt.root.parent = mt.newNode() // just for its lock
 	if persistDir != "" {
@@ -140,6 +150,7 @@
 	if n == nil {
 		return
 	}
+	mt.credit(n)
 	nodeCount := int64(0)
 	serverCount := int64(0)
 	queue := []*node{n}
@@ -151,6 +162,7 @@
 		for _, ch := range n.children {
 			ch.Lock() // Keep locked until it is deleted.
 			queue = append(queue, ch)
+			mt.credit(ch)
 		}
 	}
 
@@ -315,8 +327,14 @@
 				return nil, nil, err
 			}
 		}
+		// Obey account limits.
+		creator, err := mt.debit(ctx, call)
+		if err != nil {
+			return nil, nil, err
+		}
 		// At this point cur is still locked, OK to use and change it.
 		next := mt.newNode()
+		next.creator = creator
 		next.parent = cur
 		if cur.permsTemplate != nil {
 			next.vPerms = createVersionedPermissionsFromTemplate(cur.permsTemplate, e)
@@ -534,7 +552,7 @@
 // removeUselessRecursive removes any useless nodes on the tail of the path.
 func (mt *mountTable) removeUselessRecursive(elems []string) {
 	for i := len(elems); i > 0; i-- {
-		n, nelems, _ := mt.traverse(nil, nil, elems[:i-1], false)
+		n, nelems, _ := mt.traverse(nil, nil, elems[:i], false)
 		if n == nil {
 			break
 		}
@@ -805,7 +823,7 @@
 	n.vPerms, err = n.vPerms.Set(ctx, version, perms)
 	if err == nil {
 		if mt.persisting {
-			mt.persist.persistPerms(ms.name, n.vPerms)
+			mt.persist.persistPerms(ms.name, n.creator, n.vPerms)
 		}
 		n.explicitPermissions = true
 	}
@@ -829,3 +847,23 @@
 	version, perms := n.vPerms.Get()
 	return perms, version, nil
 }
+
+// credit user for node deletion.
+func (mt *mountTable) credit(n *node) {
+	mt.perUserNodeCounter.Incr(n.creator, -1)
+}
+
+// debit user for node creation.
+func (mt *mountTable) debit(ctx *context.T, call security.Call) (string, error) {
+	creator := mt.pickCreator(ctx, call)
+	count, ok := mt.perUserNodeCounter.Incr(creator, 1).(int64)
+	if !ok {
+		return "", verror.New(errTooManyNodes, ctx)
+	}
+	// If we have no prefixes defining users, don't bother with checking per user limits.
+	if len(mt.userPrefixes) != 0 && count > mt.maxNodesPerUser {
+		mt.perUserNodeCounter.Incr(creator, -1)
+		return "", verror.New(errTooManyNodes, ctx)
+	}
+	return creator, nil
+}
diff --git a/services/mounttable/mounttablelib/mounttable_test.go b/services/mounttable/mounttablelib/mounttable_test.go
index 5704ef1..6d27b94 100644
--- a/services/mounttable/mounttablelib/mounttable_test.go
+++ b/services/mounttable/mounttablelib/mounttable_test.go
@@ -5,6 +5,7 @@
 package mounttablelib
 
 import (
+	"encoding/json"
 	"errors"
 	"fmt"
 	"io"
@@ -25,6 +26,7 @@
 	"v.io/v23/vdl"
 	"v.io/x/lib/vlog"
 
+	libstats "v.io/x/ref/lib/stats"
 	"v.io/x/ref/services/debug/debuglib"
 	"v.io/x/ref/test"
 	"v.io/x/ref/test/testutil"
@@ -177,7 +179,7 @@
 	}
 }
 
-func newMT(t *testing.T, permsFile, persistDir string, rootCtx *context.T) (rpc.Server, string) {
+func newMT(t *testing.T, permsFile, persistDir, statsDir string, rootCtx *context.T) (rpc.Server, string) {
 	reservedDisp := debuglib.NewDispatcher(vlog.Log.LogDir, nil)
 	ctx := v23.WithReservedNameDispatcher(rootCtx, reservedDisp)
 	server, err := v23.NewServer(ctx, options.ServesMountTable(true))
@@ -185,7 +187,7 @@
 		boom(t, "r.NewServer: %s", err)
 	}
 	// Add mount table service.
-	mt, err := NewMountTableDispatcher(permsFile, persistDir, "mounttable")
+	mt, err := NewMountTableDispatcher(permsFile, persistDir, statsDir)
 	if err != nil {
 		boom(t, "NewMountTableDispatcher: %v", err)
 	}
@@ -227,7 +229,7 @@
 	rootCtx, aliceCtx, bobCtx, shutdown := initTest()
 	defer shutdown()
 
-	mt, mtAddr := newMT(t, "testdata/test.perms", "", rootCtx)
+	mt, mtAddr := newMT(t, "testdata/test.perms", "", "testMountTable", rootCtx)
 	defer mt.Stop()
 	collection, collectionAddr := newCollection(t, rootCtx)
 	defer collection.Stop()
@@ -397,7 +399,7 @@
 	rootCtx, shutdown := test.InitForTest()
 	defer shutdown()
 
-	server, estr := newMT(t, "", "", rootCtx)
+	server, estr := newMT(t, "", "", "testGlob", rootCtx)
 	defer server.Stop()
 
 	// set up a mount space
@@ -444,7 +446,7 @@
 	rootCtx, aliceCtx, bobCtx, shutdown := initTest()
 	defer shutdown()
 
-	server, estr := newMT(t, "testdata/test.perms", "", rootCtx)
+	server, estr := newMT(t, "testdata/test.perms", "", "testAccessListTemplate", rootCtx)
 	defer server.Stop()
 	fakeServer := naming.JoinAddressName(estr, "quux")
 
@@ -457,13 +459,67 @@
 	doMount(t, aliceCtx, estr, "users/alice", fakeServer, true)
 	doMount(t, bobCtx, estr, "users/bob", fakeServer, true)
 	doMount(t, rootCtx, estr, "users/root", fakeServer, true)
+
+	// Make sure the counter works.
+	doUnmount(t, aliceCtx, estr, "users/alice", "", true)
+	doUnmount(t, bobCtx, estr, "users/bob", "", true)
+	doUnmount(t, rootCtx, estr, "users/root", "", true)
+	perms := access.Permissions{"Admin": access.AccessList{In: []security.BlessingPattern{security.AllPrincipals}}}
+	doSetPermissions(t, aliceCtx, estr, "users/alice/a/b/c/d", perms, "", true)
+	doSetPermissions(t, aliceCtx, estr, "users/alice/a/b/c/d", perms, "", true)
+
+	// Do we obey limits?
+	for i := 0; i < defaultMaxNodesPerUser-5; i++ {
+		node := fmt.Sprintf("users/alice/a/b/c/d/%d", i)
+		doSetPermissions(t, aliceCtx, estr, node, perms, "", true)
+	}
+	doSetPermissions(t, aliceCtx, estr, "users/alice/a/b/c/d/straw", perms, "", false)
+
+	// See if the stats numbers are correct.
+	testcases := []struct {
+		key      string
+		expected interface{}
+	}{
+		{"alice", int64(defaultMaxNodesPerUser)},
+		{"bob", int64(0)},
+		{"root", int64(0)},
+		{localUser, int64(3)},
+	}
+	for _, tc := range testcases {
+		name := "testAccessListTemplate/num-nodes-per-user/" + tc.key
+		got, err := libstats.Value(name)
+		if err != nil {
+			t.Errorf("unexpected error getting map entry for %s: %s", name, err)
+		}
+		if got != tc.expected {
+			t.Errorf("unexpected getting map entry for %s. Got %v, want %v", name, got, tc.expected)
+		}
+	}
+}
+
+func getUserNodeCounts(t *testing.T) (counts map[string]int32) {
+	s, err := libstats.Value("mounttable/num-nodes-per-user")
+	if err != nil {
+		boom(t, "Can't get mounttable statistics")
+	}
+	// This string is a json encoded map.  Decode.
+	switch v := s.(type) {
+	default:
+		boom(t, "Wrong type for mounttable statistics")
+	case string:
+		err = json.Unmarshal([]byte(v), &counts)
+		if err != nil {
+			boom(t, "Can't unmarshal mounttable statistics")
+		}
+	}
+	return
 }
 
 func TestGlobAccessLists(t *testing.T) {
 	rootCtx, aliceCtx, bobCtx, shutdown := initTest()
 	defer shutdown()
 
-	server, estr := newMT(t, "testdata/test.perms", "", rootCtx)
+	server, estr := newMT(t, "testdata/test.perms", "", "testGlobAccessLists", rootCtx)
 	defer server.Stop()
 
 	// set up a mount space
@@ -496,7 +552,7 @@
 	rootCtx, shutdown := test.InitForTest()
 	defer shutdown()
 
-	server, estr := newMT(t, "", "", rootCtx)
+	server, estr := newMT(t, "", "", "testCleanup", rootCtx)
 	defer server.Stop()
 
 	// Set up one mount.
@@ -506,6 +562,7 @@
 
 	// After the unmount nothing should be left
 	doUnmount(t, rootCtx, estr, "one/bright/day", "", true)
+	checkMatch(t, nil, doGlob(t, rootCtx, estr, "", "one"))
 	checkMatch(t, nil, doGlob(t, rootCtx, estr, "", "*/..."))
 
 	// Set up a mount, then set the AccessList.
@@ -523,7 +580,7 @@
 	rootCtx, aliceCtx, bobCtx, shutdown := initTest()
 	defer shutdown()
 
-	server, estr := newMT(t, "testdata/test.perms", "", rootCtx)
+	server, estr := newMT(t, "testdata/test.perms", "", "testDelete", rootCtx)
 	defer server.Stop()
 
 	// set up a mount space
@@ -550,7 +607,7 @@
 	rootCtx, shutdown := test.InitForTest()
 	defer shutdown()
 
-	server, estr := newMT(t, "", "", rootCtx)
+	server, estr := newMT(t, "", "", "testerverFormat", rootCtx)
 	defer server.Stop()
 
 	doMount(t, rootCtx, estr, "endpoint", naming.JoinAddressName(estr, "life/on/the/mississippi"), true)
@@ -564,7 +621,7 @@
 	rootCtx, shutdown := test.InitForTest()
 	defer shutdown()
 
-	server, estr := newMT(t, "", "", rootCtx)
+	server, estr := newMT(t, "", "", "testExpiry", rootCtx)
 	defer server.Stop()
 	collection, collectionAddr := newCollection(t, rootCtx)
 	defer collection.Stop()
@@ -632,7 +689,7 @@
 	ft := NewFakeTimeClock()
 	setServerListClock(ft)
 
-	server, estr := newMT(t, "", "", rootCtx)
+	server, estr := newMT(t, "", "", "mounttable", rootCtx)
 	defer server.Stop()
 
 	// Test flat tree
diff --git a/services/mounttable/mounttablelib/persist_test.go b/services/mounttable/mounttablelib/persist_test.go
index a6b936b..c6b7aca 100644
--- a/services/mounttable/mounttablelib/persist_test.go
+++ b/services/mounttable/mounttablelib/persist_test.go
@@ -25,7 +25,7 @@
 	}
 	defer os.RemoveAll(td)
 	fmt.Printf("temp persist dir %s\n", td)
-	mt, mtAddr := newMT(t, "", td, rootCtx)
+	mt, mtAddr := newMT(t, "", td, "testPersistence", rootCtx)
 
 	perms1 := access.Permissions{
 		"Read":    access.AccessList{In: []security.BlessingPattern{security.AllPrincipals}},
@@ -53,7 +53,7 @@
 	mt.Stop()
 
 	// Restart with the persisted data.
-	mt, mtAddr = newMT(t, "", td, rootCtx)
+	mt, mtAddr = newMT(t, "", td, "testPersistence", rootCtx)
 
 	// Add root as Admin to each of the perms since the mounttable itself will.
 	perms1["Admin"] = access.AccessList{In: []security.BlessingPattern{"root"}}
diff --git a/services/mounttable/mounttablelib/persistentstore.go b/services/mounttable/mounttablelib/persistentstore.go
index 1dfd2f8..0cb614d 100644
--- a/services/mounttable/mounttablelib/persistentstore.go
+++ b/services/mounttable/mounttablelib/persistentstore.go
@@ -26,7 +26,8 @@
 type storeElement struct {
 	N string // Name of affected node
 	V VersionedPermissions
-	D bool // True if the subtree at N has been deleted
+	D bool   // True if the subtree at N has been deleted
+	C string // Creator
 }
 
 // newPersistentStore will read the permissions log from the directory and apply them to the
@@ -132,6 +133,7 @@
 		elems := strings.Split(e.N, "/")
 		n, err := mt.findNode(nil, nil, elems, true, nil)
 		if n != nil || err == nil {
+			n.creator = e.C
 			if e.D {
 				mt.deleteNode(n.parent, elems[len(elems)-1])
 				vlog.VI(2).Infof("deleted %s", e.N)
@@ -152,7 +154,7 @@
 // any duplicate or deleted entries disappear.
 func (s *store) depthFirstPersist(n *node, name string) {
 	if n.explicitPermissions {
-		s.persistPerms(name, n.vPerms)
+		s.persistPerms(name, n.creator, n.vPerms)
 	}
 	for nodeName, c := range n.children {
 		s.depthFirstPersist(c, path.Join(name, nodeName))
@@ -160,10 +162,10 @@
 }
 
 // persistPerms appends a changed permission to the log.
-func (s *store) persistPerms(name string, vPerms *VersionedPermissions) error {
+func (s *store) persistPerms(name, creator string, vPerms *VersionedPermissions) error {
 	s.l.Lock()
 	defer s.l.Unlock()
-	e := storeElement{N: name, V: *vPerms}
+	e := storeElement{N: name, V: *vPerms, C: creator}
 	return s.enc.Encode(&e)
 }
 
diff --git a/services/mounttable/mounttablelib/versionedpermissions.go b/services/mounttable/mounttablelib/versionedpermissions.go
index f68401f..867be02 100644
--- a/services/mounttable/mounttablelib/versionedpermissions.go
+++ b/services/mounttable/mounttablelib/versionedpermissions.go
@@ -8,6 +8,7 @@
 	"encoding/json"
 	"io"
 	"os"
+	"reflect"
 	"strconv"
 	"strings"
 
@@ -19,6 +20,12 @@
 	"v.io/x/lib/vlog"
 )
 
+// Blessings can't include a comma so we use them in made up user ids.  The following distinctions are
+// made so that we can account for them differently.
+const localUser = ",LOCAL,"     // a client that has our public key but no blessing from which we can extract a user name
+const blessedUser = ",BLESSED," // a client with blessings we trust but from which we can't extract a user name
+const unknownUser = ",UNKNOWN," // a client which presents no blessing we trust
+
 // VersionedPermissions associates a Version with a Permissions
 type VersionedPermissions struct {
 	V int32
@@ -45,8 +52,8 @@
 		}
 	}
 	b.P = perm
+	// Increment with possible wrap.
 	b.V++
-	// Protect against wrap.
 	if b.V < 0 {
 		b.V = 0
 	}
@@ -132,6 +139,12 @@
 				vlog.VI(2).Infof("added perms %v to %s", perms, name)
 				if isPattern {
 					n.permsTemplate = perms
+					// Save the pattern prefix as a prefix describing a user.
+					prefix := strings.Join(elems[:len(elems)-1], "/")
+					if prefix != "" {
+						prefix += "/"
+					}
+					mt.userPrefixes = append(mt.userPrefixes, prefix)
 				} else {
 					n.vPerms, _ = n.vPerms.Set(nil, "", perms)
 					n.explicitPermissions = true
@@ -143,3 +156,38 @@
 	}
 	return nil
 }
+
+// pickCreator returns a string matching the blessing of the user performing the creation.  We do this using
+// the user prefixes found when parsing the config.  Eventually we may need a better way to define user
+// prefixes.
+//
+// TODO(p): readdress this argument after we have some experience with real users.
+func (mt *mountTable) pickCreator(ctx *context.T, call security.Call) string {
+	// For each blessing, look for one that has a matching user prefix.  The creator is the perfix
+	// plus one more element.
+	//
+	// The prefixes themselves come from the templates in the config that constrain node names to
+	// match the user.
+	blessings, _ := security.RemoteBlessingNames(ctx, call)
+	for _, b := range blessings {
+		for _, p := range mt.userPrefixes {
+			sb := string(b)
+			if !strings.HasPrefix(sb, p) {
+				continue
+			}
+			suffix := strings.TrimPrefix(sb, p)
+			elems := strings.Split(suffix, "/")
+			return p + elems[0]
+		}
+	}
+	if ctx == nil || call == nil {
+		return localUser
+	}
+	if l, r := call.LocalBlessings().PublicKey(), call.RemoteBlessings().PublicKey(); l != nil && reflect.DeepEqual(l, r) {
+		return localUser
+	}
+	if len(blessings) > 0 {
+		return blessedUser
+	}
+	return unknownUser
+}
diff --git a/services/profile/profile.vdl.go b/services/profile/profile.vdl.go
index f5e27f9..ba9db3a 100644
--- a/services/profile/profile.vdl.go
+++ b/services/profile/profile.vdl.go
@@ -27,7 +27,7 @@
 }
 
 func (Library) __VDLReflect(struct {
-	Name string "v.io/x/ref/services/profile.Library"
+	Name string `vdl:"v.io/x/ref/services/profile.Library"`
 }) {
 }
 
@@ -50,7 +50,7 @@
 }
 
 func (Specification) __VDLReflect(struct {
-	Name string "v.io/x/ref/services/profile.Specification"
+	Name string `vdl:"v.io/x/ref/services/profile.Specification"`
 }) {
 }
 
diff --git a/services/profile/profile/doc.go b/services/profile/profile/doc.go
index 51ea9ca..d5281d8 100644
--- a/services/profile/profile/doc.go
+++ b/services/profile/profile/doc.go
@@ -114,11 +114,6 @@
 
 "help ..." recursively displays help for all commands and topics.
 
-Output is formatted to a target width in runes, determined by checking the
-CMDLINE_WIDTH environment variable, falling back on the terminal width, falling
-back on 80 chars.  By setting CMDLINE_WIDTH=x, if x > 0 the width is x, if x < 0
-the width is unlimited, and if x == 0 or is unset one of the fallbacks is used.
-
 Usage:
    profile help [flags] [command/topic ...]
 
@@ -131,5 +126,9 @@
       full    - Good for cmdline output, shows all global flags.
       godoc   - Good for godoc processing.
    Override the default by setting the CMDLINE_STYLE environment variable.
+ -width=<terminal width>
+   Format output to this target width in runes, or unlimited if width < 0.
+   Defaults to the terminal width if available.  Override the default by setting
+   the CMDLINE_WIDTH environment variable.
 */
 package main
diff --git a/services/profile/profile/impl.go b/services/profile/profile/impl.go
index 1bed93e..e4cb9f3 100644
--- a/services/profile/profile/impl.go
+++ b/services/profile/profile/impl.go
@@ -2,6 +2,9 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// The following enables go generate to generate the doc.go file.
+//go:generate go run $V23_ROOT/release/go/src/v.io/x/lib/cmdline/testdata/gendoc.go .
+
 package main
 
 import (
@@ -10,17 +13,23 @@
 
 	"v.io/v23/context"
 	"v.io/v23/services/build"
-	"v.io/x/lib/cmdline"
+	"v.io/x/lib/cmdline2"
+	"v.io/x/ref/lib/v23cmd"
+	_ "v.io/x/ref/profiles"
 	"v.io/x/ref/services/profile"
 	"v.io/x/ref/services/repository"
 )
 
-func init() {
-	cmdline.HideGlobalFlagsExcept()
+func main() {
+	cmdline2.Main(cmdRoot)
 }
 
-var cmdLabel = &cmdline.Command{
-	Run:      runLabel,
+func init() {
+	cmdline2.HideGlobalFlagsExcept()
+}
+
+var cmdLabel = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runLabel),
 	Name:     "label",
 	Short:    "Shows a human-readable profile key for the profile.",
 	Long:     "Shows a human-readable profile key for the profile.",
@@ -28,24 +37,24 @@
 	ArgsLong: "<profile> is the full name of the profile.",
 }
 
-func runLabel(cmd *cmdline.Command, args []string) error {
+func runLabel(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, got := 1, len(args); expected != got {
-		return cmd.UsageErrorf("label: incorrect number of arguments, expected %d, got %d", expected, got)
+		return env.UsageErrorf("label: incorrect number of arguments, expected %d, got %d", expected, got)
 	}
 	name := args[0]
 	p := repository.ProfileClient(name)
-	ctx, cancel := context.WithTimeout(gctx, time.Minute)
+	ctx, cancel := context.WithTimeout(ctx, time.Minute)
 	defer cancel()
 	label, err := p.Label(ctx)
 	if err != nil {
 		return err
 	}
-	fmt.Fprintln(cmd.Stdout(), label)
+	fmt.Fprintln(env.Stdout, label)
 	return nil
 }
 
-var cmdDescription = &cmdline.Command{
-	Run:      runDescription,
+var cmdDescription = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runDescription),
 	Name:     "description",
 	Short:    "Shows a human-readable profile description for the profile.",
 	Long:     "Shows a human-readable profile description for the profile.",
@@ -53,24 +62,24 @@
 	ArgsLong: "<profile> is the full name of the profile.",
 }
 
-func runDescription(cmd *cmdline.Command, args []string) error {
+func runDescription(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, got := 1, len(args); expected != got {
-		return cmd.UsageErrorf("description: incorrect number of arguments, expected %d, got %d", expected, got)
+		return env.UsageErrorf("description: incorrect number of arguments, expected %d, got %d", expected, got)
 	}
 	name := args[0]
 	p := repository.ProfileClient(name)
-	ctx, cancel := context.WithTimeout(gctx, time.Minute)
+	ctx, cancel := context.WithTimeout(ctx, time.Minute)
 	defer cancel()
 	desc, err := p.Description(ctx)
 	if err != nil {
 		return err
 	}
-	fmt.Fprintln(cmd.Stdout(), desc)
+	fmt.Fprintln(env.Stdout, desc)
 	return nil
 }
 
-var cmdSpecification = &cmdline.Command{
-	Run:      runSpecification,
+var cmdSpecification = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runSpecification),
 	Name:     "specification",
 	Short:    "Shows the specification of the profile.",
 	Long:     "Shows the specification of the profile.",
@@ -78,24 +87,24 @@
 	ArgsLong: "<profile> is the full name of the profile.",
 }
 
-func runSpecification(cmd *cmdline.Command, args []string) error {
+func runSpecification(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, got := 1, len(args); expected != got {
-		return cmd.UsageErrorf("specification: incorrect number of arguments, expected %d, got %d", expected, got)
+		return env.UsageErrorf("specification: incorrect number of arguments, expected %d, got %d", expected, got)
 	}
 	name := args[0]
 	p := repository.ProfileClient(name)
-	ctx, cancel := context.WithTimeout(gctx, time.Minute)
+	ctx, cancel := context.WithTimeout(ctx, time.Minute)
 	defer cancel()
 	spec, err := p.Specification(ctx)
 	if err != nil {
 		return err
 	}
-	fmt.Fprintf(cmd.Stdout(), "%#v\n", spec)
+	fmt.Fprintf(env.Stdout, "%#v\n", spec)
 	return nil
 }
 
-var cmdPut = &cmdline.Command{
-	Run:      runPut,
+var cmdPut = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runPut),
 	Name:     "put",
 	Short:    "Sets a placeholder specification for the profile.",
 	Long:     "Sets a placeholder specification for the profile.",
@@ -103,9 +112,9 @@
 	ArgsLong: "<profile> is the full name of the profile.",
 }
 
-func runPut(cmd *cmdline.Command, args []string) error {
+func runPut(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, got := 1, len(args); expected != got {
-		return cmd.UsageErrorf("put: incorrect number of arguments, expected %d, got %d", expected, got)
+		return env.UsageErrorf("put: incorrect number of arguments, expected %d, got %d", expected, got)
 	}
 	name := args[0]
 	p := repository.ProfileClient(name)
@@ -119,17 +128,17 @@
 		Label:       "example",
 		Os:          build.OperatingSystemLinux,
 	}
-	ctx, cancel := context.WithTimeout(gctx, time.Minute)
+	ctx, cancel := context.WithTimeout(ctx, time.Minute)
 	defer cancel()
 	if err := p.Put(ctx, spec); err != nil {
 		return err
 	}
-	fmt.Fprintln(cmd.Stdout(), "Profile added successfully.")
+	fmt.Fprintln(env.Stdout, "Profile added successfully.")
 	return nil
 }
 
-var cmdRemove = &cmdline.Command{
-	Run:      runRemove,
+var cmdRemove = &cmdline2.Command{
+	Runner:   v23cmd.RunnerFunc(runRemove),
 	Name:     "remove",
 	Short:    "removes the profile specification for the profile.",
 	Long:     "removes the profile specification for the profile.",
@@ -137,28 +146,26 @@
 	ArgsLong: "<profile> is the full name of the profile.",
 }
 
-func runRemove(cmd *cmdline.Command, args []string) error {
+func runRemove(ctx *context.T, env *cmdline2.Env, args []string) error {
 	if expected, got := 1, len(args); expected != got {
-		return cmd.UsageErrorf("remove: incorrect number of arguments, expected %d, got %d", expected, got)
+		return env.UsageErrorf("remove: incorrect number of arguments, expected %d, got %d", expected, got)
 	}
 	name := args[0]
 	p := repository.ProfileClient(name)
-	ctx, cancel := context.WithTimeout(gctx, time.Minute)
+	ctx, cancel := context.WithTimeout(ctx, time.Minute)
 	defer cancel()
 	if err := p.Remove(ctx); err != nil {
 		return err
 	}
-	fmt.Fprintln(cmd.Stdout(), "Profile removed successfully.")
+	fmt.Fprintln(env.Stdout, "Profile removed successfully.")
 	return nil
 }
 
-func root() *cmdline.Command {
-	return &cmdline.Command{
-		Name:  "profile",
-		Short: "manages the Vanadium profile repository",
-		Long: `
+var cmdRoot = &cmdline2.Command{
+	Name:  "profile",
+	Short: "manages the Vanadium profile repository",
+	Long: `
 Command profile manages the Vanadium profile repository.
 `,
-		Children: []*cmdline.Command{cmdLabel, cmdDescription, cmdSpecification, cmdPut, cmdRemove},
-	}
+	Children: []*cmdline2.Command{cmdLabel, cmdDescription, cmdSpecification, cmdPut, cmdRemove},
 }
diff --git a/services/profile/profile/impl_test.go b/services/profile/profile/impl_test.go
index d40090f..9a13e99 100644
--- a/services/profile/profile/impl_test.go
+++ b/services/profile/profile/impl_test.go
@@ -17,7 +17,9 @@
 	"v.io/v23/security"
 	"v.io/v23/services/build"
 
+	"v.io/x/lib/cmdline2"
 	"v.io/x/lib/vlog"
+	"v.io/x/ref/lib/v23cmd"
 	_ "v.io/x/ref/profiles"
 	"v.io/x/ref/services/profile"
 	"v.io/x/ref/services/repository"
@@ -115,23 +117,21 @@
 }
 
 func TestProfileClient(t *testing.T) {
-	var shutdown v23.Shutdown
-	gctx, shutdown = test.InitForTest()
+	ctx, shutdown := test.InitForTest()
 	defer shutdown()
 
-	server, endpoint, err := startServer(t, gctx)
+	server, endpoint, err := startServer(t, ctx)
 	if err != nil {
 		return
 	}
 	defer stopServer(t, server)
 	// Setup the command-line.
-	cmd := root()
 	var stdout, stderr bytes.Buffer
-	cmd.Init(nil, &stdout, &stderr)
+	env := &cmdline2.Env{Stdout: &stdout, Stderr: &stderr}
 	exists := naming.JoinAddressName(endpoint.String(), "exists")
 
 	// Test the 'label' command.
-	if err := cmd.Execute([]string{"label", exists}); err != nil {
+	if err := v23cmd.ParseAndRun(cmdRoot, ctx, env, []string{"label", exists}); err != nil {
 		t.Fatalf("%v", err)
 	}
 	if expected, got := spec.Label, strings.TrimSpace(stdout.String()); got != expected {
@@ -140,7 +140,7 @@
 	stdout.Reset()
 
 	// Test the 'description' command.
-	if err := cmd.Execute([]string{"description", exists}); err != nil {
+	if err := v23cmd.ParseAndRun(cmdRoot, ctx, env, []string{"description", exists}); err != nil {
 		t.Fatalf("%v", err)
 	}
 	if expected, got := spec.Description, strings.TrimSpace(stdout.String()); got != expected {
@@ -149,7 +149,7 @@
 	stdout.Reset()
 
 	// Test the 'spec' command.
-	if err := cmd.Execute([]string{"specification", exists}); err != nil {
+	if err := v23cmd.ParseAndRun(cmdRoot, ctx, env, []string{"specification", exists}); err != nil {
 		t.Fatalf("%v", err)
 	}
 	if expected, got := fmt.Sprintf("%#v", spec), strings.TrimSpace(stdout.String()); got != expected {
@@ -158,7 +158,7 @@
 	stdout.Reset()
 
 	// Test the 'put' command.
-	if err := cmd.Execute([]string{"put", exists}); err != nil {
+	if err := v23cmd.ParseAndRun(cmdRoot, ctx, env, []string{"put", exists}); err != nil {
 		t.Fatalf("%v", err)
 	}
 	if expected, got := "Profile added successfully.", strings.TrimSpace(stdout.String()); got != expected {
@@ -167,7 +167,7 @@
 	stdout.Reset()
 
 	// Test the 'remove' command.
-	if err := cmd.Execute([]string{"remove", exists}); err != nil {
+	if err := v23cmd.ParseAndRun(cmdRoot, ctx, env, []string{"remove", exists}); err != nil {
 		t.Fatalf("%v", err)
 	}
 	if expected, got := "Profile removed successfully.", strings.TrimSpace(stdout.String()); got != expected {
diff --git a/services/profile/profile/main.go b/services/profile/profile/main.go
deleted file mode 100644
index e33dc99..0000000
--- a/services/profile/profile/main.go
+++ /dev/null
@@ -1,27 +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 following enables go generate to generate the doc.go file.
-//go:generate go run $V23_ROOT/release/go/src/v.io/x/lib/cmdline/testdata/gendoc.go .
-
-package main
-
-import (
-	"os"
-
-	"v.io/v23"
-	"v.io/v23/context"
-
-	_ "v.io/x/ref/profiles"
-)
-
-var gctx *context.T
-
-func main() {
-	var shutdown v23.Shutdown
-	gctx, shutdown = v23.Init()
-	exitCode := root().Main()
-	shutdown()
-	os.Exit(exitCode)
-}
diff --git a/services/role/roled/internal/config.vdl.go b/services/role/roled/internal/config.vdl.go
index 7e23661..1349dcc 100644
--- a/services/role/roled/internal/config.vdl.go
+++ b/services/role/roled/internal/config.vdl.go
@@ -44,7 +44,7 @@
 }
 
 func (Config) __VDLReflect(struct {
-	Name string "v.io/x/ref/services/role/roled/internal.Config"
+	Name string `vdl:"v.io/x/ref/services/role/roled/internal.Config"`
 }) {
 }
 
diff --git a/services/role/roled/internal/role_internal_test.go b/services/role/roled/internal/role_internal_test.go
index 578feff..b575f69 100644
--- a/services/role/roled/internal/role_internal_test.go
+++ b/services/role/roled/internal/role_internal_test.go
@@ -19,7 +19,7 @@
 func TestImportMembers(t *testing.T) {
 	workdir, err := ioutil.TempDir("", "test-role-server-")
 	if err != nil {
-		t.Fatal("ioutil.TempDir failed: %v", err)
+		t.Fatalf("ioutil.TempDir failed: %v", err)
 	}
 	defer os.RemoveAll(workdir)
 	os.Mkdir(filepath.Join(workdir, "sub"), 0700)
@@ -69,9 +69,9 @@
 func WriteConfig(t *testing.T, config Config, fileName string) {
 	mConf, err := json.Marshal(config)
 	if err != nil {
-		t.Fatal("json.MarshalIndent failed: %v", err)
+		t.Fatalf("json.MarshalIndent failed: %v", err)
 	}
 	if err := ioutil.WriteFile(fileName, mConf, 0644); err != nil {
-		t.Fatal("ioutil.WriteFile(%q, %q) failed: %v", fileName, string(mConf), err)
+		t.Fatalf("ioutil.WriteFile(%q, %q) failed: %v", fileName, string(mConf), err)
 	}
 }
diff --git a/services/role/roled/internal/role_test.go b/services/role/roled/internal/role_test.go
index 28cce94..ea40dbb 100644
--- a/services/role/roled/internal/role_test.go
+++ b/services/role/roled/internal/role_test.go
@@ -31,7 +31,7 @@
 
 	workdir, err := ioutil.TempDir("", "test-role-server-")
 	if err != nil {
-		t.Fatal("ioutil.TempDir failed: %v", err)
+		t.Fatalf("ioutil.TempDir failed: %v", err)
 	}
 	defer os.RemoveAll(workdir)
 
@@ -130,7 +130,7 @@
 
 	workdir, err := ioutil.TempDir("", "test-role-server-")
 	if err != nil {
-		t.Fatal("ioutil.TempDir failed: %v", err)
+		t.Fatalf("ioutil.TempDir failed: %v", err)
 	}
 	defer os.RemoveAll(workdir)
 
@@ -170,7 +170,7 @@
 	c := role.RoleClient(naming.Join(roleAddr, "role"))
 	blessings, err := c.SeekBlessings(user)
 	if err != nil {
-		t.Errorf("unexpected erro:", err)
+		t.Error("unexpected error:", err)
 	}
 	v23.GetPrincipal(user).BlessingStore().Set(blessings, security.AllPrincipals)
 
@@ -204,7 +204,7 @@
 
 	workdir, err := ioutil.TempDir("", "test-role-server-")
 	if err != nil {
-		t.Fatal("ioutil.TempDir failed: %v", err)
+		t.Fatalf("ioutil.TempDir failed: %v", err)
 	}
 	defer os.RemoveAll(workdir)
 	os.Mkdir(filepath.Join(workdir, "sub1"), 0700)
@@ -257,18 +257,18 @@
 	for _, n := range names {
 		blessing, err := root.NewBlessings(principal, n)
 		if err != nil {
-			t.Fatal("root.Bless failed for %q: %v", n, err)
+			t.Fatalf("root.Bless failed for %q: %v", n, err)
 		}
 		blessings = append(blessings, blessing)
 	}
 	bUnion, err := security.UnionOfBlessings(blessings...)
 	if err != nil {
-		t.Fatal("security.UnionOfBlessings failed: %v", err)
+		t.Fatalf("security.UnionOfBlessings failed: %v", err)
 	}
 	vsecurity.SetDefaultBlessings(principal, bUnion)
 	ctx, err = v23.WithPrincipal(ctx, principal)
 	if err != nil {
-		t.Fatal("v23.WithPrincipal failed: %v", err)
+		t.Fatalf("v23.WithPrincipal failed: %v", err)
 	}
 	return ctx
 }
diff --git a/services/stats/types.vdl.go b/services/stats/types.vdl.go
index 8d54f98..5028b34 100644
--- a/services/stats/types.vdl.go
+++ b/services/stats/types.vdl.go
@@ -28,7 +28,7 @@
 }
 
 func (HistogramValue) __VDLReflect(struct {
-	Name string "v.io/x/ref/services/stats.HistogramValue"
+	Name string `vdl:"v.io/x/ref/services/stats.HistogramValue"`
 }) {
 }
 
@@ -41,7 +41,7 @@
 }
 
 func (HistogramBucket) __VDLReflect(struct {
-	Name string "v.io/x/ref/services/stats.HistogramBucket"
+	Name string `vdl:"v.io/x/ref/services/stats.HistogramBucket"`
 }) {
 }
 
diff --git a/services/wspr/internal/account/account.vdl.go b/services/wspr/internal/account/account.vdl.go
index cc306d0..eac5bd3 100644
--- a/services/wspr/internal/account/account.vdl.go
+++ b/services/wspr/internal/account/account.vdl.go
@@ -20,7 +20,7 @@
 }
 
 func (Caveat) __VDLReflect(struct {
-	Name string "v.io/x/ref/services/wspr/internal/account.Caveat"
+	Name string `vdl:"v.io/x/ref/services/wspr/internal/account.Caveat"`
 }) {
 }
 
diff --git a/services/wspr/internal/app/app.vdl.go b/services/wspr/internal/app/app.vdl.go
index b3dce5d..deae50d 100644
--- a/services/wspr/internal/app/app.vdl.go
+++ b/services/wspr/internal/app/app.vdl.go
@@ -35,7 +35,7 @@
 }
 
 func (RpcRequest) __VDLReflect(struct {
-	Name string "v.io/x/ref/services/wspr/internal/app.RpcRequest"
+	Name string `vdl:"v.io/x/ref/services/wspr/internal/app.RpcRequest"`
 }) {
 }
 
@@ -59,7 +59,7 @@
 	RpcCallOptionGranter struct{ Value GranterHandle }
 	// __RpcCallOptionReflect describes the RpcCallOption union type.
 	__RpcCallOptionReflect struct {
-		Name  string "v.io/x/ref/services/wspr/internal/app.RpcCallOption"
+		Name  string `vdl:"v.io/x/ref/services/wspr/internal/app.RpcCallOption"`
 		Type  RpcCallOption
 		Union struct {
 			AllowedServersPolicy RpcCallOptionAllowedServersPolicy
@@ -102,7 +102,7 @@
 	RpcServerOptionServesMountTable struct{ Value bool }
 	// __RpcServerOptionReflect describes the RpcServerOption union type.
 	__RpcServerOptionReflect struct {
-		Name  string "v.io/x/ref/services/wspr/internal/app.RpcServerOption"
+		Name  string `vdl:"v.io/x/ref/services/wspr/internal/app.RpcServerOption"`
 		Type  RpcServerOption
 		Union struct {
 			IsLeaf           RpcServerOptionIsLeaf
@@ -127,14 +127,14 @@
 }
 
 func (RpcResponse) __VDLReflect(struct {
-	Name string "v.io/x/ref/services/wspr/internal/app.RpcResponse"
+	Name string `vdl:"v.io/x/ref/services/wspr/internal/app.RpcResponse"`
 }) {
 }
 
 type GranterHandle int32
 
 func (GranterHandle) __VDLReflect(struct {
-	Name string "v.io/x/ref/services/wspr/internal/app.GranterHandle"
+	Name string `vdl:"v.io/x/ref/services/wspr/internal/app.GranterHandle"`
 }) {
 }
 
@@ -144,7 +144,7 @@
 }
 
 func (GranterRequest) __VDLReflect(struct {
-	Name string "v.io/x/ref/services/wspr/internal/app.GranterRequest"
+	Name string `vdl:"v.io/x/ref/services/wspr/internal/app.GranterRequest"`
 }) {
 }
 
@@ -154,7 +154,7 @@
 }
 
 func (GranterResponse) __VDLReflect(struct {
-	Name string "v.io/x/ref/services/wspr/internal/app.GranterResponse"
+	Name string `vdl:"v.io/x/ref/services/wspr/internal/app.GranterResponse"`
 }) {
 }
 
diff --git a/services/wspr/internal/app/messaging.go b/services/wspr/internal/app/messaging.go
index 4fab9a9..315fa04 100644
--- a/services/wspr/internal/app/messaging.go
+++ b/services/wspr/internal/app/messaging.go
@@ -164,7 +164,7 @@
 		pc := verrStack[i]
 		fnc := runtime.FuncForPC(pc)
 		file, line := fnc.FileLine(pc)
-		logErr = fmt.Sprintf("%s:%d: %s", file, line)
+		logErr = fmt.Sprintf("%s:%d: %s", file, line, logErr)
 	}
 
 	// We want to look at the stack three frames up to find where the error actually
diff --git a/services/wspr/internal/app/mock_jsServer_test.go b/services/wspr/internal/app/mock_jsServer_test.go
index 8fef5ee..1113022 100644
--- a/services/wspr/internal/app/mock_jsServer_test.go
+++ b/services/wspr/internal/app/mock_jsServer_test.go
@@ -158,7 +158,7 @@
 	}
 
 	if msg.Handle != 0 {
-		m.controller.HandleAuthResponse(m.flowCount, internalErrJSON(fmt.Sprintf("unexpected handled: %f", msg.Handle)))
+		m.controller.HandleAuthResponse(m.flowCount, internalErrJSON(fmt.Sprintf("unexpected handled: %v", msg.Handle)))
 		return nil
 	}
 
diff --git a/services/wspr/internal/browspr/browspr.vdl.go b/services/wspr/internal/browspr/browspr.vdl.go
index e4a4600..4cfd019 100644
--- a/services/wspr/internal/browspr/browspr.vdl.go
+++ b/services/wspr/internal/browspr/browspr.vdl.go
@@ -26,7 +26,7 @@
 }
 
 func (StartMessage) __VDLReflect(struct {
-	Name string "v.io/x/ref/services/wspr/internal/browspr.StartMessage"
+	Name string `vdl:"v.io/x/ref/services/wspr/internal/browspr.StartMessage"`
 }) {
 }
 
@@ -37,7 +37,7 @@
 }
 
 func (AssociateAccountMessage) __VDLReflect(struct {
-	Name string "v.io/x/ref/services/wspr/internal/browspr.AssociateAccountMessage"
+	Name string `vdl:"v.io/x/ref/services/wspr/internal/browspr.AssociateAccountMessage"`
 }) {
 }
 
@@ -46,7 +46,7 @@
 }
 
 func (CreateAccountMessage) __VDLReflect(struct {
-	Name string "v.io/x/ref/services/wspr/internal/browspr.CreateAccountMessage"
+	Name string `vdl:"v.io/x/ref/services/wspr/internal/browspr.CreateAccountMessage"`
 }) {
 }
 
@@ -55,7 +55,7 @@
 }
 
 func (CleanupMessage) __VDLReflect(struct {
-	Name string "v.io/x/ref/services/wspr/internal/browspr.CleanupMessage"
+	Name string `vdl:"v.io/x/ref/services/wspr/internal/browspr.CleanupMessage"`
 }) {
 }
 
@@ -64,7 +64,7 @@
 }
 
 func (OriginHasAccountMessage) __VDLReflect(struct {
-	Name string "v.io/x/ref/services/wspr/internal/browspr.OriginHasAccountMessage"
+	Name string `vdl:"v.io/x/ref/services/wspr/internal/browspr.OriginHasAccountMessage"`
 }) {
 }
 
@@ -72,7 +72,7 @@
 }
 
 func (GetAccountsMessage) __VDLReflect(struct {
-	Name string "v.io/x/ref/services/wspr/internal/browspr.GetAccountsMessage"
+	Name string `vdl:"v.io/x/ref/services/wspr/internal/browspr.GetAccountsMessage"`
 }) {
 }
 
@@ -84,7 +84,7 @@
 }
 
 func (CreateInstanceMessage) __VDLReflect(struct {
-	Name string "v.io/x/ref/services/wspr/internal/browspr.CreateInstanceMessage"
+	Name string `vdl:"v.io/x/ref/services/wspr/internal/browspr.CreateInstanceMessage"`
 }) {
 }
 
diff --git a/services/wspr/internal/browspr/browspr_account_test.go b/services/wspr/internal/browspr/browspr_account_test.go
index a5390f5..0155a26 100644
--- a/services/wspr/internal/browspr/browspr_account_test.go
+++ b/services/wspr/internal/browspr/browspr_account_test.go
@@ -66,7 +66,7 @@
 	nilValue := vdl.ValueOf(GetAccountsMessage{})
 	a, err := browspr.HandleAuthGetAccountsRpc(nilValue)
 	if err != nil {
-		t.Fatal("browspr.HandleAuthGetAccountsRpc(%v) failed: %v", nilValue, err)
+		t.Fatalf("browspr.HandleAuthGetAccountsRpc(%v) failed: %v", nilValue, err)
 	}
 	if a.Len() > 0 {
 		t.Fatalf("Expected accounts to be empty array but got %v", a)
@@ -89,7 +89,7 @@
 	// Verify that HandleAuthGetAccountsRpc returns the new account.
 	gotAccounts1, err := browspr.HandleAuthGetAccountsRpc(nilValue)
 	if err != nil {
-		t.Fatal("browspr.HandleAuthGetAccountsRpc(%v) failed: %v", nilValue, err)
+		t.Fatalf("browspr.HandleAuthGetAccountsRpc(%v) failed: %v", nilValue, err)
 	}
 	if want := vdl.ValueOf([]string{account1.RawString()}); !vdl.EqualValue(want, gotAccounts1) {
 		t.Fatalf("Expected account to be %v but got empty but got %v", want, gotAccounts1)
@@ -107,7 +107,7 @@
 	// Verify that HandleAuthGetAccountsRpc returns the new account.
 	gotAccounts2, err := browspr.HandleAuthGetAccountsRpc(nilValue)
 	if err != nil {
-		t.Fatal("browspr.HandleAuthGetAccountsRpc(%v) failed: %v", nilValue, err)
+		t.Fatalf("browspr.HandleAuthGetAccountsRpc(%v) failed: %v", nilValue, err)
 	}
 	if want := vdl.ValueOf([]string{account1.RawString(), account2.RawString()}); !vdl.EqualValue(want, gotAccounts2) {
 		t.Fatalf("Expected account to be %v but got empty but got %v", want, gotAccounts2)
@@ -148,7 +148,7 @@
 		t.Fatal(err)
 	}
 	if hasAccount.Bool() {
-		t.Fatal("Expected browspr.HandleAuthOriginHasAccountRpc(%v) to be false but was true", hasAccountMessage)
+		t.Fatalf("Expected browspr.HandleAuthOriginHasAccountRpc(%v) to be false but was true", hasAccountMessage)
 	}
 
 	assocAccountMessage := vdl.ValueOf(AssociateAccountMessage{
@@ -166,7 +166,7 @@
 		t.Fatal(err)
 	}
 	if !hasAccount.Bool() {
-		t.Fatal("Expected browspr.HandleAuthOriginHasAccountRpc(%v) to be true but was false", hasAccountMessage)
+		t.Fatalf("Expected browspr.HandleAuthOriginHasAccountRpc(%v) to be true but was false", hasAccountMessage)
 	}
 
 	// Verify that principalManager has the correct principal for the origin
@@ -192,7 +192,7 @@
 	})
 
 	if _, err := browspr.HandleAuthAssociateAccountRpc(message); err == nil {
-		t.Fatalf("browspr.HandleAuthAssociateAccountRpc(%v) should have failed but did not.")
+		t.Fatalf("browspr.HandleAuthAssociateAccountRpc(%v) should have failed but did not.", message)
 	}
 
 	// Verify that principalManager creates no principal for the origin
@@ -214,6 +214,6 @@
 		t.Fatal(err)
 	}
 	if hasAccount.Bool() {
-		t.Fatal("Expected browspr.HandleAuthOriginHasAccountRpc(%v) to be false but was true", hasAccountMessage)
+		t.Fatalf("Expected browspr.HandleAuthOriginHasAccountRpc(%v) to be false but was true", hasAccountMessage)
 	}
 }
diff --git a/services/wspr/internal/browspr/browspr_test.go b/services/wspr/internal/browspr/browspr_test.go
index 0debf32..3fdf59c 100644
--- a/services/wspr/internal/browspr/browspr_test.go
+++ b/services/wspr/internal/browspr/browspr_test.go
@@ -170,10 +170,10 @@
 	accountName := "test-account"
 	bp := v23.GetPrincipal(browspr.ctx)
 	if err := browspr.principalManager.AddAccount(accountName, bp.BlessingStore().Default()); err != nil {
-		t.Fatalf("Failed to add account: %v")
+		t.Fatalf("Failed to add account: %v", err)
 	}
 	if err := browspr.accountManager.AssociateAccount(msgOrigin, accountName, nil); err != nil {
-		t.Fatalf("Failed to associate account: %v")
+		t.Fatalf("Failed to associate account: %v", err)
 	}
 
 	rpc := app.RpcRequest{
diff --git a/services/wspr/internal/channel/channel.vdl.go b/services/wspr/internal/channel/channel.vdl.go
index 34c163f..c411dbb 100644
--- a/services/wspr/internal/channel/channel.vdl.go
+++ b/services/wspr/internal/channel/channel.vdl.go
@@ -19,7 +19,7 @@
 }
 
 func (Request) __VDLReflect(struct {
-	Name string "v.io/x/ref/services/wspr/internal/channel.Request"
+	Name string `vdl:"v.io/x/ref/services/wspr/internal/channel.Request"`
 }) {
 }
 
@@ -30,7 +30,7 @@
 }
 
 func (Response) __VDLReflect(struct {
-	Name string "v.io/x/ref/services/wspr/internal/channel.Response"
+	Name string `vdl:"v.io/x/ref/services/wspr/internal/channel.Response"`
 }) {
 }
 
@@ -52,7 +52,7 @@
 	MessageResponse struct{ Value Response }
 	// __MessageReflect describes the Message union type.
 	__MessageReflect struct {
-		Name  string "v.io/x/ref/services/wspr/internal/channel.Message"
+		Name  string `vdl:"v.io/x/ref/services/wspr/internal/channel.Message"`
 		Type  Message
 		Union struct {
 			Request  MessageRequest
diff --git a/services/wspr/internal/lib/simple_client.go b/services/wspr/internal/lib/simple_client.go
index a50fc67..a46fd82 100644
--- a/services/wspr/internal/lib/simple_client.go
+++ b/services/wspr/internal/lib/simple_client.go
@@ -14,7 +14,6 @@
 	"v.io/v23/security"
 	"v.io/v23/vdl"
 	"v.io/v23/vom"
-	"v.io/x/lib/vlog"
 )
 
 type clientWithTimesCalled interface {
@@ -50,7 +49,6 @@
 
 // StartCall Implements rpc.Client
 func (c *simpleMockClient) StartCall(ctx *context.T, name, method string, args []interface{}, opts ...rpc.CallOpt) (rpc.ClientCall, error) {
-	defer vlog.LogCall()()
 	results, ok := c.results[method]
 	if !ok {
 		return nil, fmt.Errorf("method %s not found", method)
@@ -90,7 +88,6 @@
 
 // Close implements rpc.Client
 func (*simpleMockClient) Close() {
-	defer vlog.LogCall()()
 }
 
 // mockCall implements rpc.ClientCall
@@ -101,18 +98,15 @@
 
 // Cancel implements rpc.ClientCall
 func (*mockCall) Cancel() {
-	defer vlog.LogCall()()
 }
 
 // CloseSend implements rpc.ClientCall
 func (*mockCall) CloseSend() error {
-	defer vlog.LogCall()()
 	return nil
 }
 
 // Finish implements rpc.ClientCall
 func (mc *mockCall) Finish(resultptrs ...interface{}) error {
-	defer vlog.LogCall()()
 	if got, want := len(resultptrs), len(mc.results); got != want {
 		return errors.New(fmt.Sprintf("wrong number of output results; expected resultptrs of size %d but got %d", want, got))
 	}
@@ -136,12 +130,10 @@
 
 //Send implements rpc.Stream
 func (*mockStream) Send(interface{}) error {
-	defer vlog.LogCall()()
 	return nil
 }
 
 //Recv implements rpc.Stream
 func (*mockStream) Recv(interface{}) error {
-	defer vlog.LogCall()()
 	return nil
 }
diff --git a/services/wspr/internal/lib/writer.vdl.go b/services/wspr/internal/lib/writer.vdl.go
index 1930733..56bb125 100644
--- a/services/wspr/internal/lib/writer.vdl.go
+++ b/services/wspr/internal/lib/writer.vdl.go
@@ -24,7 +24,7 @@
 }
 
 func (ServerRpcReply) __VDLReflect(struct {
-	Name string "v.io/x/ref/services/wspr/internal/lib.ServerRpcReply"
+	Name string `vdl:"v.io/x/ref/services/wspr/internal/lib.ServerRpcReply"`
 }) {
 }
 
@@ -70,7 +70,7 @@
 }
 
 func (LogLevel) __VDLReflect(struct {
-	Name string "v.io/x/ref/services/wspr/internal/lib.LogLevel"
+	Name string `vdl:"v.io/x/ref/services/wspr/internal/lib.LogLevel"`
 	Enum struct{ Info, Error string }
 }) {
 }
@@ -81,7 +81,7 @@
 }
 
 func (LogMessage) __VDLReflect(struct {
-	Name string "v.io/x/ref/services/wspr/internal/lib.LogMessage"
+	Name string `vdl:"v.io/x/ref/services/wspr/internal/lib.LogMessage"`
 }) {
 }
 
diff --git a/services/wspr/internal/principal/blessings.vdl.go b/services/wspr/internal/principal/blessings.vdl.go
index dc078be..27d8c75 100644
--- a/services/wspr/internal/principal/blessings.vdl.go
+++ b/services/wspr/internal/principal/blessings.vdl.go
@@ -15,7 +15,7 @@
 type BlessingsHandle int32
 
 func (BlessingsHandle) __VDLReflect(struct {
-	Name string "v.io/x/ref/services/wspr/internal/principal.BlessingsHandle"
+	Name string `vdl:"v.io/x/ref/services/wspr/internal/principal.BlessingsHandle"`
 }) {
 }
 
@@ -25,7 +25,7 @@
 }
 
 func (JsBlessings) __VDLReflect(struct {
-	Name string "v.io/x/ref/services/wspr/internal/principal.JsBlessings"
+	Name string `vdl:"v.io/x/ref/services/wspr/internal/principal.JsBlessings"`
 }) {
 }
 
diff --git a/services/wspr/internal/principal/principal_test.go b/services/wspr/internal/principal/principal_test.go
index 638727c..858d2cc 100644
--- a/services/wspr/internal/principal/principal_test.go
+++ b/services/wspr/internal/principal/principal_test.go
@@ -101,7 +101,7 @@
 	unknownOrigin := "http://unknown.com:80"
 	_, err = m.Principal(unknownOrigin)
 	if merr := matchesErrorID(err, verror.ErrNoExist.ID); merr != nil {
-		return fmt.Errorf("Principal(%v): %v, errorid=%v", unknownOrigin, merr)
+		return fmt.Errorf("Principal(%v): %v", unknownOrigin, merr)
 	}
 
 	// Test BlessingsForAccount.
diff --git a/services/wspr/internal/rpc/server/dispatcher_test.go b/services/wspr/internal/rpc/server/dispatcher_test.go
index b376790..b869c6a 100644
--- a/services/wspr/internal/rpc/server/dispatcher_test.go
+++ b/services/wspr/internal/rpc/server/dispatcher_test.go
@@ -182,7 +182,7 @@
 	_, _, err := d.Lookup("a/b")
 
 	if err == nil {
-		t.Errorf("expected error, but got none", err)
+		t.Error("expected error, but got none")
 	}
 
 	expectedResponses := []lib.Response{
diff --git a/services/wspr/internal/rpc/server/server.vdl.go b/services/wspr/internal/rpc/server/server.vdl.go
index 3df8ccf..4956bca 100644
--- a/services/wspr/internal/rpc/server/server.vdl.go
+++ b/services/wspr/internal/rpc/server/server.vdl.go
@@ -26,7 +26,7 @@
 }
 
 func (Context) __VDLReflect(struct {
-	Name string "v.io/x/ref/services/wspr/internal/rpc/server.Context"
+	Name string `vdl:"v.io/x/ref/services/wspr/internal/rpc/server.Context"`
 }) {
 }
 
@@ -43,7 +43,7 @@
 }
 
 func (SecurityCall) __VDLReflect(struct {
-	Name string "v.io/x/ref/services/wspr/internal/rpc/server.SecurityCall"
+	Name string `vdl:"v.io/x/ref/services/wspr/internal/rpc/server.SecurityCall"`
 }) {
 }
 
@@ -54,7 +54,7 @@
 }
 
 func (CaveatValidationRequest) __VDLReflect(struct {
-	Name string "v.io/x/ref/services/wspr/internal/rpc/server.CaveatValidationRequest"
+	Name string `vdl:"v.io/x/ref/services/wspr/internal/rpc/server.CaveatValidationRequest"`
 }) {
 }
 
@@ -63,7 +63,7 @@
 }
 
 func (CaveatValidationResponse) __VDLReflect(struct {
-	Name string "v.io/x/ref/services/wspr/internal/rpc/server.CaveatValidationResponse"
+	Name string `vdl:"v.io/x/ref/services/wspr/internal/rpc/server.CaveatValidationResponse"`
 }) {
 }
 
@@ -76,7 +76,7 @@
 }
 
 func (ServerRpcRequestCall) __VDLReflect(struct {
-	Name string "v.io/x/ref/services/wspr/internal/rpc/server.ServerRpcRequestCall"
+	Name string `vdl:"v.io/x/ref/services/wspr/internal/rpc/server.ServerRpcRequestCall"`
 }) {
 }
 
@@ -90,7 +90,7 @@
 }
 
 func (ServerRpcRequest) __VDLReflect(struct {
-	Name string "v.io/x/ref/services/wspr/internal/rpc/server.ServerRpcRequest"
+	Name string `vdl:"v.io/x/ref/services/wspr/internal/rpc/server.ServerRpcRequest"`
 }) {
 }
 
diff --git a/services/wspr/wsprlib/writer.go b/services/wspr/wsprlib/writer.go
index ef4be93..ea65970 100644
--- a/services/wspr/wsprlib/writer.go
+++ b/services/wspr/wsprlib/writer.go
@@ -55,7 +55,7 @@
 		pc := verrStack[i]
 		fnc := runtime.FuncForPC(pc)
 		file, line := fnc.FileLine(pc)
-		logErr = fmt.Sprintf("%s:%d: %s", file, line)
+		logErr = fmt.Sprintf("%s:%d: %s", file, line, logErr)
 	}
 
 	// We want to look at the stack three frames up to find where the error actually
diff --git a/test/modules/modules_test.go b/test/modules/modules_test.go
index 4e7f012..2cf0949 100644
--- a/test/modules/modules_test.go
+++ b/test/modules/modules_test.go
@@ -656,7 +656,7 @@
 	}
 	sort.Strings(nenv)
 	if got, want := nenv, []string{"V23_SHELL_HELPER_PROCESS_ENTRY_POINT=eg2", "a=a", "b=b"}; !reflect.DeepEqual(got, want) {
-		t.Errorf("got %d, want %d", got, want)
+		t.Errorf("got %v, want %v", got, want)
 	}
 }
 
diff --git a/test/v23tests/binary.go b/test/v23tests/binary.go
index 9cbee4f..c4dd0cc 100644
--- a/test/v23tests/binary.go
+++ b/test/v23tests/binary.go
@@ -35,6 +35,9 @@
 	// Environment variables that will be used when creating invocations
 	// via Start.
 	envVars []string
+
+	// Optional prefix arguments are added to each invocation.
+	prefixArgs []string
 }
 
 // StartOpts returns the current the StartOpts
@@ -52,7 +55,10 @@
 	return b.start(1, args...)
 }
 
-func (b *Binary) start(skip int, args ...string) *Invocation {
+func (b *Binary) start(skip int, oargs ...string) *Invocation {
+	args := make([]string, len(b.prefixArgs), len(oargs)+len(b.prefixArgs))
+	copy(args, b.prefixArgs)
+	args = append(args, oargs...)
 	vlog.Infof("%s: starting %s %s", Caller(skip+1), b.Path(), strings.Join(args, " "))
 	opts := b.opts
 	if opts.ExecProtocol && opts.Credentials == nil {
@@ -134,3 +140,14 @@
 	newBin.opts = opts
 	return &newBin
 }
+
+// WithPrefixArgs returns a copy of this binary that, when Start or Run
+// is called, will use the given additional arguments. For example: given
+// a Binary b built from "git", then b2 := WithPrefixArgs("checkout")
+// will let one run git checkout a; git checkout b with b2.Run("a"),
+// b2.Run("b").
+func (b *Binary) WithPrefixArgs(prefixArgs ...string) *Binary {
+	newBin := *b
+	newBin.prefixArgs = prefixArgs
+	return &newBin
+}
diff --git a/test/v23tests/internal/cached_test.go b/test/v23tests/internal/cached_test.go
index f3277fa..f13f7f9 100644
--- a/test/v23tests/internal/cached_test.go
+++ b/test/v23tests/internal/cached_test.go
@@ -38,7 +38,7 @@
 	nsBin := i.BuildGoPkg("v.io/x/ref/cmd/namespace")
 	fi, err := os.Stat(nsBin.Path())
 	if err != nil {
-		i.Fatal()
+		i.Fatal(err)
 	}
 	modTimes = append(modTimes, fi.ModTime())
 }
diff --git a/test/v23tests/v23tests.go b/test/v23tests/v23tests.go
index 5c55cc0..e792d08 100644
--- a/test/v23tests/v23tests.go
+++ b/test/v23tests/v23tests.go
@@ -362,7 +362,7 @@
 	}
 	// Set up agent for Child.
 	attr.Files = append(attr.Files, agentFile)
-	attr.Env = append(attr.Env, fmt.Sprintf("%s=%d", envvar.AgentEndpoint, agentlib.AgentEndpoint(len(attr.Files)-1)))
+	attr.Env = append(attr.Env, fmt.Sprintf("%s=%v", envvar.AgentEndpoint, agentlib.AgentEndpoint(len(attr.Files)-1)))
 
 	// Set up environment for Child.
 	for _, v := range t.shell.Env() {
diff --git a/test/v23tests/v23tests_test.go b/test/v23tests/v23tests_test.go
index f3c5382..4987ff3 100644
--- a/test/v23tests/v23tests_test.go
+++ b/test/v23tests/v23tests_test.go
@@ -234,6 +234,16 @@
 	if got, want := echo.Run("hello", "world"), "hello world"; got != want {
 		t.Fatalf("got %v, want %v", got, want)
 	}
+
+	sadEcho := echo.WithPrefixArgs("sad")
+	if got, want := sadEcho.Run("hello", "world"), "sad hello world"; got != want {
+		t.Fatalf("got %v, want %v", got, want)
+	}
+
+	happyEcho := echo.WithPrefixArgs("happy")
+	if got, want := happyEcho.Run("hello", "world"), "happy hello world"; got != want {
+		t.Fatalf("got %v, want %v", got, want)
+	}
 }
 
 type mockT struct {
@@ -286,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:296"; !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) {
@@ -308,7 +318,7 @@
 	sh.SetDefaultStartOpts(opts)
 	defer func() {
 		msg := recover().(string)
-		if got, want := msg, "v23tests_test.go:318"; !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) {
@@ -332,7 +342,7 @@
 		if iterations == 0 {
 			t.Fatalf("our sleeper didn't get to run")
 		}
-		if got, want := recover().(string), "v23tests_test.go:339: 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)
 		}
 	}()
@@ -354,7 +364,7 @@
 		if iterations != 0 {
 			t.Fatalf("our sleeper got to run")
 		}
-		if got, want := recover().(string), "v23tests_test.go:361: 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)
 		}
 	}()