Merge "veyron/services/mgmt/binary: Add object names to namespace"
diff --git a/lib/cmdline/cmdline.go b/lib/cmdline/cmdline.go
deleted file mode 100644
index 5c71607..0000000
--- a/lib/cmdline/cmdline.go
+++ /dev/null
@@ -1,506 +0,0 @@
-// Package cmdline provides a data-driven framework to simplify writing
-// command-line programs.  It includes built-in support for formatted help.
-//
-// Commands may be linked together to form a command tree.  Since commands may
-// be arbitrarily nested within other commands, it's easy to create wrapper
-// programs that invoke existing commands.
-//
-// The syntax for each command-line program is:
-//
-//   command [flags] [subcommand [flags]]* [args]
-//
-// Each sequence of flags on the command-line is associated with the command
-// that immediately precedes them.  Global flags registered with the standard
-// flags package are allowed anywhere a command-specific flag is allowed.
-package cmdline
-
-import (
-	"flag"
-	"fmt"
-	"io"
-	"io/ioutil"
-	"os"
-	"strconv"
-	"strings"
-
-	"veyron.io/veyron/veyron/lib/textutil"
-)
-
-// ErrExitCode may be returned by the Run function of a Command to cause the
-// program to exit with a specific error code.
-type ErrExitCode int
-
-func (x ErrExitCode) Error() string {
-	return fmt.Sprintf("exit code %d", x)
-}
-
-// ErrUsage is returned to indicate an error in command usage; e.g. unknown
-// flags, subcommands or args.  It corresponds to exit code 1.
-const ErrUsage = ErrExitCode(1)
-
-// Command represents a single command in a command-line program.  A program
-// with subcommands is represented as a root Command with children representing
-// each subcommand.  The command graph must be a tree; each command may either
-// have exactly one parent (a sub-command), or no parent (the root), and cycles
-// are not allowed.  This makes it easier to display the usage for subcommands.
-type Command struct {
-	Name     string       // Name of the command.
-	Short    string       // Short description, shown in help called on parent.
-	Long     string       // Long description, shown in help called on itself.
-	Flags    flag.FlagSet // Flags for the command.
-	ArgsName string       // Name of the args, shown in usage line.
-	ArgsLong string       // Long description of the args, shown in help.
-
-	// Children of the command.  The framework will match args[0] against each
-	// child's name, and call Run on the first matching child.
-	Children []*Command
-
-	// Topics that provide additional info via the default help command.
-	Topics []Topic
-
-	// Run is a function that runs cmd with args.  If both Children and Run are
-	// specified, Run will only be called if none of the children match.  It is an
-	// error if neither is specified.  The special ErrExitCode error may be
-	// returned to indicate the command should exit with a specific exit code.
-	Run func(cmd *Command, args []string) error
-
-	// parent holds the parent of this Command, or nil if this is the root.
-	parent *Command
-
-	// stdout and stderr are set through Init.
-	stdout, stderr io.Writer
-
-	// parseFlags holds the merged flags used for parsing.  Each command starts
-	// with its own Flags, and we merge in all global flags.  If the same flag is
-	// specified in both sets, the command's own flag wins.
-	parseFlags *flag.FlagSet
-
-	// isDefaultHelp indicates whether this is the the default help command
-	// provided by the framework.
-	isDefaultHelp bool
-
-	// TODO(toddw): If necessary we can add alias support, e.g. for abbreviations.
-	//   Alias map[string]string
-}
-
-// Topic represents an additional help topic that is accessed via the default
-// help command.
-type Topic struct {
-	Name  string // Name of the topic.
-	Short string // Short description, shown in help for the command.
-	Long  string // Long description, shown in help for this topic.
-}
-
-// style describes the formatting style for usage descriptions.
-type style int
-
-const (
-	styleText  style = iota // Default style, good for cmdline output.
-	styleGoDoc              // Style good for godoc processing.
-)
-
-// String returns the human-readable representation of the style.
-func (s *style) String() string {
-	switch *s {
-	case styleText:
-		return "text"
-	case styleGoDoc:
-		return "godoc"
-	default:
-		panic(fmt.Errorf("Unhandled style %d", *s))
-	}
-}
-
-// Set implements the flag.Value interface method.
-func (s *style) Set(value string) error {
-	switch value {
-	case "text":
-		*s = styleText
-	case "godoc":
-		*s = styleGoDoc
-	default:
-		return fmt.Errorf("Unknown style %q", value)
-	}
-	return nil
-}
-
-// Stdout is where output goes.  Typically os.Stdout.
-func (cmd *Command) Stdout() io.Writer {
-	return cmd.stdout
-}
-
-// Stderr is where error messages go.  Typically os.Stderr
-func (cmd *Command) Stderr() io.Writer {
-	return cmd.stderr
-}
-
-// UsageErrorf prints the error message represented by the printf-style format
-// string and args, followed by the usage description of cmd.  Returns ErrUsage
-// to make it easy to use from within the cmd.Run function.
-func (cmd *Command) UsageErrorf(format string, v ...interface{}) error {
-	fmt.Fprint(cmd.stderr, "ERROR: ")
-	fmt.Fprintf(cmd.stderr, format, v...)
-	fmt.Fprint(cmd.stderr, "\n\n")
-	cmd.writeUsage(cmd.stderr)
-	return ErrUsage
-}
-
-// Have a reasonable default for the output width in runes.
-const defaultWidth = 80
-
-func outputWidth() int {
-	if width, err := strconv.Atoi(os.Getenv("CMDLINE_WIDTH")); err == nil && width != 0 {
-		return width
-	}
-	if _, width, err := textutil.TerminalSize(); err == nil && width != 0 {
-		return width
-	}
-	return defaultWidth
-}
-
-func (cmd *Command) writeUsage(w io.Writer) {
-	lineWriter := textutil.NewUTF8LineWriter(w, outputWidth())
-	cmd.usage(lineWriter, true)
-	lineWriter.Flush()
-}
-
-// usage prints the usage of cmd to the writer.  The firstCall boolean is set to
-// false when printing usage for multiple commands, and is used to avoid
-// printing redundant information (e.g. help command, global flags).
-func (cmd *Command) usage(w *textutil.LineWriter, firstCall bool) {
-	fmt.Fprintln(w, cmd.Long)
-	fmt.Fprintln(w)
-	// Usage line.
-	hasFlags := numFlags(&cmd.Flags) > 0
-	fmt.Fprintln(w, "Usage:")
-	path := cmd.namePath()
-	pathf := "   " + path
-	if hasFlags {
-		pathf += " [flags]"
-	}
-	if len(cmd.Children) > 0 {
-		fmt.Fprintln(w, pathf, "<command>")
-	}
-	if cmd.Run != nil {
-		if cmd.ArgsName != "" {
-			fmt.Fprintln(w, pathf, cmd.ArgsName)
-		} else {
-			fmt.Fprintln(w, pathf)
-		}
-	}
-	if len(cmd.Children) == 0 && cmd.Run == nil {
-		// This is a specification error.
-		fmt.Fprintln(w, pathf, "[ERROR: neither Children nor Run is specified]")
-	}
-	// Commands.
-	const minNameWidth = 11
-	if len(cmd.Children) > 0 {
-		fmt.Fprintln(w)
-		fmt.Fprintln(w, "The", path, "commands are:")
-		nameWidth := minNameWidth
-		for _, child := range cmd.Children {
-			if len(child.Name) > nameWidth {
-				nameWidth = len(child.Name)
-			}
-		}
-		// Print as a table with aligned columns Name and Short.
-		w.SetIndents(spaces(3), spaces(3+nameWidth+1))
-		for _, child := range cmd.Children {
-			// Don't repeatedly list default help command.
-			if !child.isDefaultHelp || firstCall {
-				fmt.Fprintf(w, "%-[1]*[2]s %[3]s", nameWidth, child.Name, child.Short)
-				w.Flush()
-			}
-		}
-		w.SetIndents()
-		if firstCall {
-			fmt.Fprintf(w, "Run \"%s help [command]\" for command usage.\n", path)
-		}
-	}
-	// Args.
-	if cmd.Run != nil && cmd.ArgsLong != "" {
-		fmt.Fprintln(w)
-		fmt.Fprintln(w, cmd.ArgsLong)
-	}
-	// Help topics.
-	if len(cmd.Topics) > 0 {
-		fmt.Fprintln(w)
-		fmt.Fprintln(w, "The", path, "additional help topics are:")
-		nameWidth := minNameWidth
-		for _, topic := range cmd.Topics {
-			if len(topic.Name) > nameWidth {
-				nameWidth = len(topic.Name)
-			}
-		}
-		// Print as a table with aligned columns Name and Short.
-		w.SetIndents(spaces(3), spaces(3+nameWidth+1))
-		for _, topic := range cmd.Topics {
-			fmt.Fprintf(w, "%-[1]*[2]s %[3]s", nameWidth, topic.Name, topic.Short)
-			w.Flush()
-		}
-		w.SetIndents()
-		if firstCall {
-			fmt.Fprintf(w, "Run \"%s help [topic]\" for topic details.\n", path)
-		}
-	}
-	// Flags.
-	if hasFlags {
-		fmt.Fprintln(w)
-		fmt.Fprintln(w, "The", path, "flags are:")
-		printFlags(w, &cmd.Flags)
-	}
-	// Global flags.
-	if numFlags(flag.CommandLine) > 0 && firstCall {
-		fmt.Fprintln(w)
-		fmt.Fprintln(w, "The global flags are:")
-		printFlags(w, flag.CommandLine)
-	}
-}
-
-// namePath returns the path of command names up to cmd.
-func (cmd *Command) namePath() string {
-	var path []string
-	for ; cmd != nil; cmd = cmd.parent {
-		path = append([]string{cmd.Name}, path...)
-	}
-	return strings.Join(path, " ")
-}
-
-func numFlags(set *flag.FlagSet) (num int) {
-	set.VisitAll(func(*flag.Flag) {
-		num++
-	})
-	return
-}
-
-func printFlags(w *textutil.LineWriter, set *flag.FlagSet) {
-	set.VisitAll(func(f *flag.Flag) {
-		fmt.Fprintf(w, " -%s=%s", f.Name, f.DefValue)
-		w.SetIndents(spaces(3))
-		fmt.Fprintln(w, f.Usage)
-		w.SetIndents()
-	})
-}
-
-func spaces(count int) string {
-	return strings.Repeat(" ", count)
-}
-
-// newDefaultHelp creates a new default help command.  We need to create new
-// instances since the parent for each help command is different.
-func newDefaultHelp() *Command {
-	helpStyle := styleText
-	help := &Command{
-		Name:  helpName,
-		Short: "Display help for commands or topics",
-		Long: `
-Help with no args displays the usage of the parent command.
-
-Help with args displays the usage of the specified sub-command or help topic.
-
-"help ..." recursively displays help for all commands and topics.
-
-The output is formatted to a target width in runes.  The target width is
-determined by checking the environment variable CMDLINE_WIDTH, falling back on
-the terminal width from the OS, 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.
-`,
-		ArgsName: "[command/topic ...]",
-		ArgsLong: `
-[command/topic ...] optionally identifies a specific sub-command or help topic.
-`,
-		Run: func(cmd *Command, args []string) error {
-			// Help applies to its parent - e.g. "foo help" applies to the foo command.
-			lineWriter := textutil.NewUTF8LineWriter(cmd.stdout, outputWidth())
-			defer lineWriter.Flush()
-			return runHelp(lineWriter, cmd.parent, args, helpStyle)
-		},
-		isDefaultHelp: true,
-	}
-	help.Flags.Var(&helpStyle, "style", `The formatting style for help output, either "text" or "godoc".`)
-	return help
-}
-
-const helpName = "help"
-
-// runHelp runs the "help" command.
-func runHelp(w *textutil.LineWriter, cmd *Command, args []string, style style) error {
-	if len(args) == 0 {
-		cmd.usage(w, true)
-		return nil
-	}
-	if args[0] == "..." {
-		recursiveHelp(w, cmd, style, true)
-		return nil
-	}
-	// Try to display help for the subcommand.
-	subName, subArgs := args[0], args[1:]
-	for _, child := range cmd.Children {
-		if child.Name == subName {
-			return runHelp(w, child, subArgs, style)
-		}
-	}
-	// Try to display help for the help topic.
-	for _, topic := range cmd.Topics {
-		if topic.Name == subName {
-			fmt.Fprintln(w, topic.Long)
-			return nil
-		}
-	}
-	return cmd.UsageErrorf("%s: unknown command or topic %q", cmd.namePath(), subName)
-}
-
-// recursiveHelp prints help recursively via DFS from this cmd onward.
-func recursiveHelp(w *textutil.LineWriter, cmd *Command, style style, firstCall bool) {
-	if !firstCall {
-		lineBreak(w, style)
-		// Title-case required for godoc to recognize this as a section header.
-		header := strings.Title(cmd.namePath())
-		fmt.Fprintln(w, header)
-		fmt.Fprintln(w)
-	}
-	cmd.usage(w, firstCall)
-	for _, child := range cmd.Children {
-		// Don't repeatedly print default help command.
-		if !child.isDefaultHelp || firstCall {
-			recursiveHelp(w, child, style, false)
-		}
-	}
-	for _, topic := range cmd.Topics {
-		lineBreak(w, style)
-		// Title-case required for godoc to recognize this as a section header.
-		header := strings.Title(cmd.namePath()+" "+topic.Name) + " - help topic"
-		fmt.Fprintln(w, header)
-		fmt.Fprintln(w)
-		fmt.Fprintln(w, topic.Long)
-	}
-}
-
-func lineBreak(w *textutil.LineWriter, style style) {
-	w.Flush()
-	switch style {
-	case styleText:
-		width := w.Width()
-		if width < 0 {
-			// If the user has chosen an "unlimited" word-wrapping width, we still
-			// need a reasonable width for our visual line break.
-			width = defaultWidth
-		}
-		fmt.Fprintln(w, strings.Repeat("=", width))
-	case styleGoDoc:
-		fmt.Fprintln(w)
-	}
-	w.Flush()
-}
-
-func trimNewlines(s *string) { *s = strings.Trim(*s, "\n") }
-
-// Init initializes all nodes in the command tree rooted at cmd.  Init must be
-// called before Execute.
-func (cmd *Command) Init(parent *Command, stdout, stderr io.Writer) {
-	cmd.parent = parent
-	cmd.stdout = stdout
-	cmd.stderr = stderr
-	trimNewlines(&cmd.Short)
-	trimNewlines(&cmd.Long)
-	trimNewlines(&cmd.ArgsLong)
-	for tx := range cmd.Topics {
-		trimNewlines(&cmd.Topics[tx].Short)
-		trimNewlines(&cmd.Topics[tx].Long)
-	}
-	// Add help command, if it doesn't already exist.
-	hasHelp := false
-	for _, child := range cmd.Children {
-		if child.Name == helpName {
-			hasHelp = true
-			break
-		}
-	}
-	if !hasHelp && cmd.Name != helpName && len(cmd.Children) > 0 {
-		cmd.Children = append(cmd.Children, newDefaultHelp())
-	}
-	// Merge command-specific and global flags into parseFlags.  We want to handle
-	// all error output ourselves, so we:
-	//   1) Set flag.ContinueOnError so that Parse() doesn't exit or panic.
-	//   2) Discard all output (can't be nil, that means stderr).
-	//   3) Set an empty Usage function (can't be nil, that means default).
-	cmd.parseFlags = flag.NewFlagSet(cmd.Name, flag.ContinueOnError)
-	cmd.parseFlags.SetOutput(ioutil.Discard)
-	cmd.parseFlags.Usage = emptyUsage
-	mergeFlags(cmd.parseFlags, &cmd.Flags)
-	mergeFlags(cmd.parseFlags, flag.CommandLine)
-	// Call children recursively.
-	for _, child := range cmd.Children {
-		child.Init(cmd, stdout, stderr)
-	}
-}
-
-func mergeFlags(dst, src *flag.FlagSet) {
-	src.VisitAll(func(f *flag.Flag) {
-		trimNewlines(&f.Usage)
-		if dst.Lookup(f.Name) == nil {
-			dst.Var(f.Value, f.Name, f.Usage)
-		}
-	})
-}
-
-func emptyUsage() {}
-
-// Execute the command with the given args.  The returned error is ErrUsage if
-// there are usage errors, otherwise it is whatever the leaf command returns
-// from its Run function.
-func (cmd *Command) Execute(args []string) error {
-	path := cmd.namePath()
-	// Parse the merged flags.
-	if err := cmd.parseFlags.Parse(args); err != nil {
-		if err == flag.ErrHelp {
-			cmd.writeUsage(cmd.stdout)
-			return nil
-		}
-		return cmd.UsageErrorf("%s: %v", path, err)
-	}
-	args = cmd.parseFlags.Args()
-	// Look for matching children.
-	if len(args) > 0 {
-		subName, subArgs := args[0], args[1:]
-		for _, child := range cmd.Children {
-			if child.Name == subName {
-				return child.Execute(subArgs)
-			}
-		}
-	}
-	// No matching children, try Run.
-	if cmd.Run != nil {
-		if cmd.ArgsName == "" && len(args) > 0 {
-			if len(cmd.Children) > 0 {
-				return cmd.UsageErrorf("%s: unknown command %q", path, args[0])
-			}
-			return cmd.UsageErrorf("%s doesn't take any arguments", path)
-		}
-		return cmd.Run(cmd, args)
-	}
-	switch {
-	case len(cmd.Children) == 0:
-		return cmd.UsageErrorf("%s: neither Children nor Run is specified", path)
-	case len(args) > 0:
-		return cmd.UsageErrorf("%s: unknown command %q", path, args[0])
-	default:
-		return cmd.UsageErrorf("%s: no command specified", path)
-	}
-}
-
-// Main executes the command tree rooted at cmd, writing output to os.Stdout,
-// writing errors to os.Stderr, and getting args from os.Args.  We'll call
-// os.Exit with a non-zero exit code on errors.  It's meant as a simple
-// one-liner for the main function of command-line tools.
-func (cmd *Command) Main() {
-	cmd.Init(nil, os.Stdout, os.Stderr)
-	if err := cmd.Execute(os.Args[1:]); err != nil {
-		if code, ok := err.(ErrExitCode); ok {
-			os.Exit(int(code))
-		}
-		fmt.Fprintln(os.Stderr, "ERROR:", err)
-		os.Exit(2)
-	}
-}
diff --git a/lib/cmdline/cmdline_test.go b/lib/cmdline/cmdline_test.go
deleted file mode 100644
index 6cf6b0e..0000000
--- a/lib/cmdline/cmdline_test.go
+++ /dev/null
@@ -1,2238 +0,0 @@
-package cmdline
-
-import (
-	"bytes"
-	"errors"
-	"flag"
-	"fmt"
-	"os"
-	"regexp"
-	"strings"
-	"testing"
-)
-
-var (
-	errEcho           = errors.New("echo error")
-	flagExtra         bool
-	optNoNewline      bool
-	flagTopLevelExtra bool
-	globalFlag1       string
-	globalFlag2       *int64
-)
-
-// runEcho is used to implement commands for our tests.
-func runEcho(cmd *Command, args []string) error {
-	if len(args) == 1 {
-		if args[0] == "error" {
-			return errEcho
-		} else if args[0] == "bad_arg" {
-			return cmd.UsageErrorf("Invalid argument %v", args[0])
-		}
-	}
-	if flagExtra {
-		args = append(args, "extra")
-	}
-	if flagTopLevelExtra {
-		args = append(args, "tlextra")
-	}
-	if optNoNewline {
-		fmt.Fprint(cmd.Stdout(), args)
-	} else {
-		fmt.Fprintln(cmd.Stdout(), args)
-	}
-	return nil
-}
-
-// runHello is another function for test commands.
-func runHello(cmd *Command, args []string) error {
-	if flagTopLevelExtra {
-		args = append(args, "tlextra")
-	}
-	fmt.Fprintln(cmd.Stdout(), strings.Join(append([]string{"Hello"}, args...), " "))
-	return nil
-}
-
-type testCase struct {
-	Args        []string
-	Err         error
-	Stdout      string
-	Stderr      string
-	GlobalFlag1 string
-	GlobalFlag2 int64
-}
-
-func init() {
-	os.Setenv("CMDLINE_WIDTH", "80") // make sure the formatting stays the same.
-	flag.StringVar(&globalFlag1, "global1", "", "global test flag 1")
-	globalFlag2 = flag.Int64("global2", 0, "global test flag 2")
-}
-
-func stripOutput(got string) string {
-	// The global flags include the flags from the testing package, so strip them
-	// out before the comparison.
-	re := regexp.MustCompile(" -test[^\n]+\n(?:   [^\n]+\n)+")
-	return re.ReplaceAllLiteralString(got, "")
-}
-
-func runTestCases(t *testing.T, cmd *Command, tests []testCase) {
-	for _, test := range tests {
-		// Reset global variables before running each test case.
-		var stdout bytes.Buffer
-		var stderr bytes.Buffer
-		flagExtra = false
-		flagTopLevelExtra = false
-		optNoNewline = false
-		globalFlag1 = ""
-		*globalFlag2 = 0
-
-		// Run the execute function and check against expected results.
-		cmd.Init(nil, &stdout, &stderr)
-		if err := cmd.Execute(test.Args); err != test.Err {
-			t.Errorf("Ran with args %q\n GOT error:\n%q\nWANT error:\n%q", test.Args, err, test.Err)
-		}
-		if got, want := stripOutput(stdout.String()), test.Stdout; got != want {
-			t.Errorf("Ran with args %q\n GOT stdout:\n%q\nWANT stdout:\n%q", test.Args, got, want)
-		}
-		if got, want := stripOutput(stderr.String()), test.Stderr; got != want {
-			t.Errorf("Ran with args %q\n GOT stderr:\n%q\nWANT stderr:\n%q", test.Args, got, want)
-		}
-		if got, want := globalFlag1, test.GlobalFlag1; got != want {
-			t.Errorf("global1 flag got %q, want %q", got, want)
-		}
-		if got, want := *globalFlag2, test.GlobalFlag2; got != want {
-			t.Errorf("global2 flag got %q, want %q", got, want)
-		}
-	}
-}
-
-func TestNoCommands(t *testing.T) {
-	cmd := &Command{
-		Name:  "nocmds",
-		Short: "Nocmds is invalid.",
-		Long:  "Nocmds has no commands and no run function.",
-	}
-
-	var tests = []testCase{
-		{
-			Args: []string{},
-			Err:  ErrUsage,
-			Stderr: `ERROR: nocmds: neither Children nor Run is specified
-
-Nocmds has no commands and no run function.
-
-Usage:
-   nocmds [ERROR: neither Children nor Run is specified]
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-`,
-		},
-		{
-			Args: []string{"foo"},
-			Err:  ErrUsage,
-			Stderr: `ERROR: nocmds: neither Children nor Run is specified
-
-Nocmds has no commands and no run function.
-
-Usage:
-   nocmds [ERROR: neither Children nor Run is specified]
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-`,
-		},
-	}
-	runTestCases(t, cmd, tests)
-}
-
-func TestOneCommand(t *testing.T) {
-	cmdEcho := &Command{
-		Name:  "echo",
-		Short: "Print strings on stdout",
-		Long: `
-Echo prints any strings passed in to stdout.
-`,
-		Run:      runEcho,
-		ArgsName: "[strings]",
-		ArgsLong: "[strings] are arbitrary strings that will be echoed.",
-	}
-
-	prog := &Command{
-		Name:     "onecmd",
-		Short:    "Onecmd program.",
-		Long:     "Onecmd only has the echo command.",
-		Children: []*Command{cmdEcho},
-	}
-
-	var tests = []testCase{
-		{
-			Args: []string{},
-			Err:  ErrUsage,
-			Stderr: `ERROR: onecmd: no command specified
-
-Onecmd only has the echo command.
-
-Usage:
-   onecmd <command>
-
-The onecmd commands are:
-   echo        Print strings on stdout
-   help        Display help for commands or topics
-Run "onecmd help [command]" for command usage.
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-`,
-		},
-		{
-			Args: []string{"foo"},
-			Err:  ErrUsage,
-			Stderr: `ERROR: onecmd: unknown command "foo"
-
-Onecmd only has the echo command.
-
-Usage:
-   onecmd <command>
-
-The onecmd commands are:
-   echo        Print strings on stdout
-   help        Display help for commands or topics
-Run "onecmd help [command]" for command usage.
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-`,
-		},
-		{
-			Args: []string{"help"},
-			Stdout: `Onecmd only has the echo command.
-
-Usage:
-   onecmd <command>
-
-The onecmd commands are:
-   echo        Print strings on stdout
-   help        Display help for commands or topics
-Run "onecmd help [command]" for command usage.
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-`,
-		},
-		{
-			Args: []string{"help", "echo"},
-			Stdout: `Echo prints any strings passed in to stdout.
-
-Usage:
-   onecmd echo [strings]
-
-[strings] are arbitrary strings that will be echoed.
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-`,
-		},
-		{
-			Args: []string{"help", "help"},
-			Stdout: `Help with no args displays the usage of the parent command.
-
-Help with args displays the usage of the specified sub-command or help topic.
-
-"help ..." recursively displays help for all commands and topics.
-
-The output is formatted to a target width in runes.  The target width is
-determined by checking the environment variable CMDLINE_WIDTH, falling back on
-the terminal width from the OS, 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:
-   onecmd help [flags] [command/topic ...]
-
-[command/topic ...] optionally identifies a specific sub-command or help topic.
-
-The onecmd help flags are:
- -style=text
-   The formatting style for help output, either "text" or "godoc".
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-`,
-		},
-		{
-			Args: []string{"help", "..."},
-			Stdout: `Onecmd only has the echo command.
-
-Usage:
-   onecmd <command>
-
-The onecmd commands are:
-   echo        Print strings on stdout
-   help        Display help for commands or topics
-Run "onecmd help [command]" for command usage.
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-================================================================================
-Onecmd Echo
-
-Echo prints any strings passed in to stdout.
-
-Usage:
-   onecmd echo [strings]
-
-[strings] are arbitrary strings that will be echoed.
-================================================================================
-Onecmd Help
-
-Help with no args displays the usage of the parent command.
-
-Help with args displays the usage of the specified sub-command or help topic.
-
-"help ..." recursively displays help for all commands and topics.
-
-The output is formatted to a target width in runes.  The target width is
-determined by checking the environment variable CMDLINE_WIDTH, falling back on
-the terminal width from the OS, 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:
-   onecmd help [flags] [command/topic ...]
-
-[command/topic ...] optionally identifies a specific sub-command or help topic.
-
-The onecmd help flags are:
- -style=text
-   The formatting style for help output, either "text" or "godoc".
-`,
-		},
-		{
-			Args: []string{"help", "foo"},
-			Err:  ErrUsage,
-			Stderr: `ERROR: onecmd: unknown command or topic "foo"
-
-Onecmd only has the echo command.
-
-Usage:
-   onecmd <command>
-
-The onecmd commands are:
-   echo        Print strings on stdout
-   help        Display help for commands or topics
-Run "onecmd help [command]" for command usage.
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-`,
-		},
-		{
-			Args:   []string{"echo", "foo", "bar"},
-			Stdout: "[foo bar]\n",
-		},
-		{
-			Args: []string{"echo", "error"},
-			Err:  errEcho,
-		},
-		{
-			Args: []string{"echo", "bad_arg"},
-			Err:  ErrUsage,
-			Stderr: `ERROR: Invalid argument bad_arg
-
-Echo prints any strings passed in to stdout.
-
-Usage:
-   onecmd echo [strings]
-
-[strings] are arbitrary strings that will be echoed.
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-`,
-		},
-	}
-	runTestCases(t, prog, tests)
-}
-
-func TestMultiCommands(t *testing.T) {
-	cmdEcho := &Command{
-		Run:   runEcho,
-		Name:  "echo",
-		Short: "Print strings on stdout",
-		Long: `
-Echo prints any strings passed in to stdout.
-`,
-		ArgsName: "[strings]",
-		ArgsLong: "[strings] are arbitrary strings that will be echoed.",
-	}
-	var cmdEchoOpt = &Command{
-		Run:   runEcho,
-		Name:  "echoopt",
-		Short: "Print strings on stdout, with opts",
-		// Try varying number of header/trailer newlines around the long description.
-		Long: `Echoopt prints any args passed in to stdout.
-
-
-`,
-		ArgsName: "[args]",
-		ArgsLong: "[args] are arbitrary strings that will be echoed.",
-	}
-	cmdEchoOpt.Flags.BoolVar(&optNoNewline, "n", false, "Do not output trailing newline")
-
-	prog := &Command{
-		Name:     "multi",
-		Short:    "Multi test command",
-		Long:     "Multi has two variants of echo.",
-		Children: []*Command{cmdEcho, cmdEchoOpt},
-	}
-	prog.Flags.BoolVar(&flagExtra, "extra", false, "Print an extra arg")
-
-	var tests = []testCase{
-		{
-			Args: []string{},
-			Err:  ErrUsage,
-			Stderr: `ERROR: multi: no command specified
-
-Multi has two variants of echo.
-
-Usage:
-   multi [flags] <command>
-
-The multi commands are:
-   echo        Print strings on stdout
-   echoopt     Print strings on stdout, with opts
-   help        Display help for commands or topics
-Run "multi help [command]" for command usage.
-
-The multi flags are:
- -extra=false
-   Print an extra arg
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-`,
-		},
-		{
-			Args: []string{"help"},
-			Stdout: `Multi has two variants of echo.
-
-Usage:
-   multi [flags] <command>
-
-The multi commands are:
-   echo        Print strings on stdout
-   echoopt     Print strings on stdout, with opts
-   help        Display help for commands or topics
-Run "multi help [command]" for command usage.
-
-The multi flags are:
- -extra=false
-   Print an extra arg
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-`,
-		},
-		{
-			Args: []string{"help", "..."},
-			Stdout: `Multi has two variants of echo.
-
-Usage:
-   multi [flags] <command>
-
-The multi commands are:
-   echo        Print strings on stdout
-   echoopt     Print strings on stdout, with opts
-   help        Display help for commands or topics
-Run "multi help [command]" for command usage.
-
-The multi flags are:
- -extra=false
-   Print an extra arg
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-================================================================================
-Multi Echo
-
-Echo prints any strings passed in to stdout.
-
-Usage:
-   multi echo [strings]
-
-[strings] are arbitrary strings that will be echoed.
-================================================================================
-Multi Echoopt
-
-Echoopt prints any args passed in to stdout.
-
-Usage:
-   multi echoopt [flags] [args]
-
-[args] are arbitrary strings that will be echoed.
-
-The multi echoopt flags are:
- -n=false
-   Do not output trailing newline
-================================================================================
-Multi Help
-
-Help with no args displays the usage of the parent command.
-
-Help with args displays the usage of the specified sub-command or help topic.
-
-"help ..." recursively displays help for all commands and topics.
-
-The output is formatted to a target width in runes.  The target width is
-determined by checking the environment variable CMDLINE_WIDTH, falling back on
-the terminal width from the OS, 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:
-   multi help [flags] [command/topic ...]
-
-[command/topic ...] optionally identifies a specific sub-command or help topic.
-
-The multi help flags are:
- -style=text
-   The formatting style for help output, either "text" or "godoc".
-`,
-		},
-		{
-			Args: []string{"help", "echo"},
-			Stdout: `Echo prints any strings passed in to stdout.
-
-Usage:
-   multi echo [strings]
-
-[strings] are arbitrary strings that will be echoed.
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-`,
-		},
-		{
-			Args: []string{"help", "echoopt"},
-			Stdout: `Echoopt prints any args passed in to stdout.
-
-Usage:
-   multi echoopt [flags] [args]
-
-[args] are arbitrary strings that will be echoed.
-
-The multi echoopt flags are:
- -n=false
-   Do not output trailing newline
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-`,
-		},
-		{
-			Args: []string{"help", "foo"},
-			Err:  ErrUsage,
-			Stderr: `ERROR: multi: unknown command or topic "foo"
-
-Multi has two variants of echo.
-
-Usage:
-   multi [flags] <command>
-
-The multi commands are:
-   echo        Print strings on stdout
-   echoopt     Print strings on stdout, with opts
-   help        Display help for commands or topics
-Run "multi help [command]" for command usage.
-
-The multi flags are:
- -extra=false
-   Print an extra arg
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-`,
-		},
-		{
-			Args:   []string{"echo", "foo", "bar"},
-			Stdout: "[foo bar]\n",
-		},
-		{
-			Args:   []string{"-extra", "echo", "foo", "bar"},
-			Stdout: "[foo bar extra]\n",
-		},
-		{
-			Args: []string{"echo", "error"},
-			Err:  errEcho,
-		},
-		{
-			Args:   []string{"echoopt", "foo", "bar"},
-			Stdout: "[foo bar]\n",
-		},
-		{
-			Args:   []string{"-extra", "echoopt", "foo", "bar"},
-			Stdout: "[foo bar extra]\n",
-		},
-		{
-			Args:   []string{"echoopt", "-n", "foo", "bar"},
-			Stdout: "[foo bar]",
-		},
-		{
-			Args:   []string{"-extra", "echoopt", "-n", "foo", "bar"},
-			Stdout: "[foo bar extra]",
-		},
-		{
-			Args:        []string{"-global1=globalStringValue", "-extra", "echoopt", "-n", "foo", "bar"},
-			Stdout:      "[foo bar extra]",
-			GlobalFlag1: "globalStringValue",
-		},
-		{
-			Args:        []string{"-global2=42", "echoopt", "-n", "foo", "bar"},
-			Stdout:      "[foo bar]",
-			GlobalFlag2: 42,
-		},
-		{
-			Args:        []string{"-global1=globalStringOtherValue", "-global2=43", "-extra", "echoopt", "-n", "foo", "bar"},
-			Stdout:      "[foo bar extra]",
-			GlobalFlag1: "globalStringOtherValue",
-			GlobalFlag2: 43,
-		},
-		{
-			Args: []string{"echoopt", "error"},
-			Err:  errEcho,
-		},
-		{
-			Args: []string{"echo", "-n", "foo", "bar"},
-			Err:  ErrUsage,
-			Stderr: `ERROR: multi echo: flag provided but not defined: -n
-
-Echo prints any strings passed in to stdout.
-
-Usage:
-   multi echo [strings]
-
-[strings] are arbitrary strings that will be echoed.
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-`,
-		},
-		{
-			Args: []string{"-nosuchflag", "echo", "foo", "bar"},
-			Err:  ErrUsage,
-			Stderr: `ERROR: multi: flag provided but not defined: -nosuchflag
-
-Multi has two variants of echo.
-
-Usage:
-   multi [flags] <command>
-
-The multi commands are:
-   echo        Print strings on stdout
-   echoopt     Print strings on stdout, with opts
-   help        Display help for commands or topics
-Run "multi help [command]" for command usage.
-
-The multi flags are:
- -extra=false
-   Print an extra arg
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-`,
-		},
-	}
-	runTestCases(t, prog, tests)
-}
-
-func TestMultiLevelCommands(t *testing.T) {
-	cmdEcho := &Command{
-		Run:   runEcho,
-		Name:  "echo",
-		Short: "Print strings on stdout",
-		Long: `
-Echo prints any strings passed in to stdout.
-`,
-		ArgsName: "[strings]",
-		ArgsLong: "[strings] are arbitrary strings that will be echoed.",
-	}
-	cmdEchoOpt := &Command{
-		Run:   runEcho,
-		Name:  "echoopt",
-		Short: "Print strings on stdout, with opts",
-		// Try varying number of header/trailer newlines around the long description.
-		Long: `Echoopt prints any args passed in to stdout.
-
-
-`,
-		ArgsName: "[args]",
-		ArgsLong: "[args] are arbitrary strings that will be echoed.",
-	}
-	cmdEchoOpt.Flags.BoolVar(&optNoNewline, "n", false, "Do not output trailing newline")
-	cmdHello := &Command{
-		Run:   runHello,
-		Name:  "hello",
-		Short: "Print strings on stdout preceded by \"Hello\"",
-		Long: `
-Hello prints any strings passed in to stdout preceded by "Hello".
-`,
-		ArgsName: "[strings]",
-		ArgsLong: "[strings] are arbitrary strings that will be printed.",
-	}
-	echoProg := &Command{
-		Name:     "echoprog",
-		Short:    "Set of echo commands",
-		Long:     "Echoprog has two variants of echo.",
-		Children: []*Command{cmdEcho, cmdEchoOpt},
-		Topics: []Topic{
-			{Name: "topic3", Short: "Help topic 3 short", Long: "Help topic 3 long."},
-		},
-	}
-	echoProg.Flags.BoolVar(&flagExtra, "extra", false, "Print an extra arg")
-	prog := &Command{
-		Name:     "toplevelprog",
-		Short:    "Top level prog",
-		Long:     "Toplevelprog has the echo subprogram and the hello command.",
-		Children: []*Command{echoProg, cmdHello},
-		Topics: []Topic{
-			{Name: "topic1", Short: "Help topic 1 short", Long: "Help topic 1 long."},
-			{Name: "topic2", Short: "Help topic 2 short", Long: "Help topic 2 long."},
-		},
-	}
-	prog.Flags.BoolVar(&flagTopLevelExtra, "tlextra", false, "Print an extra arg for all commands")
-
-	var tests = []testCase{
-		{
-			Args: []string{},
-			Err:  ErrUsage,
-			Stderr: `ERROR: toplevelprog: no command specified
-
-Toplevelprog has the echo subprogram and the hello command.
-
-Usage:
-   toplevelprog [flags] <command>
-
-The toplevelprog commands are:
-   echoprog    Set of echo commands
-   hello       Print strings on stdout preceded by "Hello"
-   help        Display help for commands or topics
-Run "toplevelprog help [command]" for command usage.
-
-The toplevelprog additional help topics are:
-   topic1      Help topic 1 short
-   topic2      Help topic 2 short
-Run "toplevelprog help [topic]" for topic details.
-
-The toplevelprog flags are:
- -tlextra=false
-   Print an extra arg for all commands
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-`,
-		},
-		{
-			Args: []string{"help"},
-			Stdout: `Toplevelprog has the echo subprogram and the hello command.
-
-Usage:
-   toplevelprog [flags] <command>
-
-The toplevelprog commands are:
-   echoprog    Set of echo commands
-   hello       Print strings on stdout preceded by "Hello"
-   help        Display help for commands or topics
-Run "toplevelprog help [command]" for command usage.
-
-The toplevelprog additional help topics are:
-   topic1      Help topic 1 short
-   topic2      Help topic 2 short
-Run "toplevelprog help [topic]" for topic details.
-
-The toplevelprog flags are:
- -tlextra=false
-   Print an extra arg for all commands
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-`,
-		},
-		{
-			Args: []string{"help", "..."},
-			Stdout: `Toplevelprog has the echo subprogram and the hello command.
-
-Usage:
-   toplevelprog [flags] <command>
-
-The toplevelprog commands are:
-   echoprog    Set of echo commands
-   hello       Print strings on stdout preceded by "Hello"
-   help        Display help for commands or topics
-Run "toplevelprog help [command]" for command usage.
-
-The toplevelprog additional help topics are:
-   topic1      Help topic 1 short
-   topic2      Help topic 2 short
-Run "toplevelprog help [topic]" for topic details.
-
-The toplevelprog flags are:
- -tlextra=false
-   Print an extra arg for all commands
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-================================================================================
-Toplevelprog Echoprog
-
-Echoprog has two variants of echo.
-
-Usage:
-   toplevelprog echoprog [flags] <command>
-
-The toplevelprog echoprog commands are:
-   echo        Print strings on stdout
-   echoopt     Print strings on stdout, with opts
-
-The toplevelprog echoprog additional help topics are:
-   topic3      Help topic 3 short
-
-The toplevelprog echoprog flags are:
- -extra=false
-   Print an extra arg
-================================================================================
-Toplevelprog Echoprog Echo
-
-Echo prints any strings passed in to stdout.
-
-Usage:
-   toplevelprog echoprog echo [strings]
-
-[strings] are arbitrary strings that will be echoed.
-================================================================================
-Toplevelprog Echoprog Echoopt
-
-Echoopt prints any args passed in to stdout.
-
-Usage:
-   toplevelprog echoprog echoopt [flags] [args]
-
-[args] are arbitrary strings that will be echoed.
-
-The toplevelprog echoprog echoopt flags are:
- -n=false
-   Do not output trailing newline
-================================================================================
-Toplevelprog Echoprog Topic3 - help topic
-
-Help topic 3 long.
-================================================================================
-Toplevelprog Hello
-
-Hello prints any strings passed in to stdout preceded by "Hello".
-
-Usage:
-   toplevelprog hello [strings]
-
-[strings] are arbitrary strings that will be printed.
-================================================================================
-Toplevelprog Help
-
-Help with no args displays the usage of the parent command.
-
-Help with args displays the usage of the specified sub-command or help topic.
-
-"help ..." recursively displays help for all commands and topics.
-
-The output is formatted to a target width in runes.  The target width is
-determined by checking the environment variable CMDLINE_WIDTH, falling back on
-the terminal width from the OS, 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:
-   toplevelprog help [flags] [command/topic ...]
-
-[command/topic ...] optionally identifies a specific sub-command or help topic.
-
-The toplevelprog help flags are:
- -style=text
-   The formatting style for help output, either "text" or "godoc".
-================================================================================
-Toplevelprog Topic1 - help topic
-
-Help topic 1 long.
-================================================================================
-Toplevelprog Topic2 - help topic
-
-Help topic 2 long.
-`,
-		},
-		{
-			Args: []string{"help", "echoprog"},
-			Stdout: `Echoprog has two variants of echo.
-
-Usage:
-   toplevelprog echoprog [flags] <command>
-
-The toplevelprog echoprog commands are:
-   echo        Print strings on stdout
-   echoopt     Print strings on stdout, with opts
-   help        Display help for commands or topics
-Run "toplevelprog echoprog help [command]" for command usage.
-
-The toplevelprog echoprog additional help topics are:
-   topic3      Help topic 3 short
-Run "toplevelprog echoprog help [topic]" for topic details.
-
-The toplevelprog echoprog flags are:
- -extra=false
-   Print an extra arg
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-`,
-		},
-		{
-			Args: []string{"help", "topic1"},
-			Stdout: `Help topic 1 long.
-`,
-		},
-		{
-			Args: []string{"help", "topic2"},
-			Stdout: `Help topic 2 long.
-`,
-		},
-		{
-			Args: []string{"echoprog", "help", "..."},
-			Stdout: `Echoprog has two variants of echo.
-
-Usage:
-   toplevelprog echoprog [flags] <command>
-
-The toplevelprog echoprog commands are:
-   echo        Print strings on stdout
-   echoopt     Print strings on stdout, with opts
-   help        Display help for commands or topics
-Run "toplevelprog echoprog help [command]" for command usage.
-
-The toplevelprog echoprog additional help topics are:
-   topic3      Help topic 3 short
-Run "toplevelprog echoprog help [topic]" for topic details.
-
-The toplevelprog echoprog flags are:
- -extra=false
-   Print an extra arg
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-================================================================================
-Toplevelprog Echoprog Echo
-
-Echo prints any strings passed in to stdout.
-
-Usage:
-   toplevelprog echoprog echo [strings]
-
-[strings] are arbitrary strings that will be echoed.
-================================================================================
-Toplevelprog Echoprog Echoopt
-
-Echoopt prints any args passed in to stdout.
-
-Usage:
-   toplevelprog echoprog echoopt [flags] [args]
-
-[args] are arbitrary strings that will be echoed.
-
-The toplevelprog echoprog echoopt flags are:
- -n=false
-   Do not output trailing newline
-================================================================================
-Toplevelprog Echoprog Help
-
-Help with no args displays the usage of the parent command.
-
-Help with args displays the usage of the specified sub-command or help topic.
-
-"help ..." recursively displays help for all commands and topics.
-
-The output is formatted to a target width in runes.  The target width is
-determined by checking the environment variable CMDLINE_WIDTH, falling back on
-the terminal width from the OS, 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:
-   toplevelprog echoprog help [flags] [command/topic ...]
-
-[command/topic ...] optionally identifies a specific sub-command or help topic.
-
-The toplevelprog echoprog help flags are:
- -style=text
-   The formatting style for help output, either "text" or "godoc".
-================================================================================
-Toplevelprog Echoprog Topic3 - help topic
-
-Help topic 3 long.
-`,
-		},
-		{
-			Args: []string{"echoprog", "help", "echoopt"},
-			Stdout: `Echoopt prints any args passed in to stdout.
-
-Usage:
-   toplevelprog echoprog echoopt [flags] [args]
-
-[args] are arbitrary strings that will be echoed.
-
-The toplevelprog echoprog echoopt flags are:
- -n=false
-   Do not output trailing newline
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-`,
-		},
-		{
-			Args: []string{"help", "echoprog", "topic3"},
-			Stdout: `Help topic 3 long.
-`,
-		},
-		{
-			Args: []string{"echoprog", "help", "topic3"},
-			Stdout: `Help topic 3 long.
-`,
-		},
-		{
-			Args: []string{"help", "hello"},
-			Stdout: `Hello prints any strings passed in to stdout preceded by "Hello".
-
-Usage:
-   toplevelprog hello [strings]
-
-[strings] are arbitrary strings that will be printed.
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-`,
-		},
-		{
-			Args: []string{"help", "foo"},
-			Err:  ErrUsage,
-			Stderr: `ERROR: toplevelprog: unknown command or topic "foo"
-
-Toplevelprog has the echo subprogram and the hello command.
-
-Usage:
-   toplevelprog [flags] <command>
-
-The toplevelprog commands are:
-   echoprog    Set of echo commands
-   hello       Print strings on stdout preceded by "Hello"
-   help        Display help for commands or topics
-Run "toplevelprog help [command]" for command usage.
-
-The toplevelprog additional help topics are:
-   topic1      Help topic 1 short
-   topic2      Help topic 2 short
-Run "toplevelprog help [topic]" for topic details.
-
-The toplevelprog flags are:
- -tlextra=false
-   Print an extra arg for all commands
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-`,
-		},
-		{
-			Args:   []string{"echoprog", "echo", "foo", "bar"},
-			Stdout: "[foo bar]\n",
-		},
-		{
-			Args:   []string{"echoprog", "-extra", "echo", "foo", "bar"},
-			Stdout: "[foo bar extra]\n",
-		},
-		{
-			Args: []string{"echoprog", "echo", "error"},
-			Err:  errEcho,
-		},
-		{
-			Args:   []string{"echoprog", "echoopt", "foo", "bar"},
-			Stdout: "[foo bar]\n",
-		},
-		{
-			Args:   []string{"echoprog", "-extra", "echoopt", "foo", "bar"},
-			Stdout: "[foo bar extra]\n",
-		},
-		{
-			Args:   []string{"echoprog", "echoopt", "-n", "foo", "bar"},
-			Stdout: "[foo bar]",
-		},
-		{
-			Args:   []string{"echoprog", "-extra", "echoopt", "-n", "foo", "bar"},
-			Stdout: "[foo bar extra]",
-		},
-		{
-			Args: []string{"echoprog", "echoopt", "error"},
-			Err:  errEcho,
-		},
-		{
-			Args:   []string{"--tlextra", "echoprog", "-extra", "echoopt", "foo", "bar"},
-			Stdout: "[foo bar extra tlextra]\n",
-		},
-		{
-			Args:   []string{"hello", "foo", "bar"},
-			Stdout: "Hello foo bar\n",
-		},
-		{
-			Args:   []string{"--tlextra", "hello", "foo", "bar"},
-			Stdout: "Hello foo bar tlextra\n",
-		},
-		{
-			Args: []string{"hello", "--extra", "foo", "bar"},
-			Err:  ErrUsage,
-			Stderr: `ERROR: toplevelprog hello: flag provided but not defined: -extra
-
-Hello prints any strings passed in to stdout preceded by "Hello".
-
-Usage:
-   toplevelprog hello [strings]
-
-[strings] are arbitrary strings that will be printed.
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-`,
-		},
-		{
-			Args: []string{"-extra", "echoprog", "echoopt", "foo", "bar"},
-			Err:  ErrUsage,
-			Stderr: `ERROR: toplevelprog: flag provided but not defined: -extra
-
-Toplevelprog has the echo subprogram and the hello command.
-
-Usage:
-   toplevelprog [flags] <command>
-
-The toplevelprog commands are:
-   echoprog    Set of echo commands
-   hello       Print strings on stdout preceded by "Hello"
-   help        Display help for commands or topics
-Run "toplevelprog help [command]" for command usage.
-
-The toplevelprog additional help topics are:
-   topic1      Help topic 1 short
-   topic2      Help topic 2 short
-Run "toplevelprog help [topic]" for topic details.
-
-The toplevelprog flags are:
- -tlextra=false
-   Print an extra arg for all commands
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-`,
-		},
-	}
-	runTestCases(t, prog, tests)
-}
-
-func TestMultiLevelCommandsOrdering(t *testing.T) {
-	cmdHello11 := &Command{
-		Name:  "hello11",
-		Short: "Print strings on stdout preceded by \"Hello\"",
-		Long: `
-Hello prints any strings passed in to stdout preceded by "Hello".
-`,
-		ArgsName: "[strings]",
-		ArgsLong: "[strings] are arbitrary strings that will be printed.",
-		Run:      runHello,
-	}
-	cmdHello12 := &Command{
-		Name:  "hello12",
-		Short: "Print strings on stdout preceded by \"Hello\"",
-		Long: `
-Hello prints any strings passed in to stdout preceded by "Hello".
-`,
-		ArgsName: "[strings]",
-		ArgsLong: "[strings] are arbitrary strings that will be printed.",
-		Run:      runHello,
-	}
-	cmdHello21 := &Command{
-		Name:  "hello21",
-		Short: "Print strings on stdout preceded by \"Hello\"",
-		Long: `
-Hello prints any strings passed in to stdout preceded by "Hello".
-`,
-		ArgsName: "[strings]",
-		ArgsLong: "[strings] are arbitrary strings that will be printed.",
-		Run:      runHello,
-	}
-	cmdHello22 := &Command{
-		Name:  "hello22",
-		Short: "Print strings on stdout preceded by \"Hello\"",
-		Long: `
-Hello prints any strings passed in to stdout preceded by "Hello".
-`,
-		ArgsName: "[strings]",
-		ArgsLong: "[strings] are arbitrary strings that will be printed.",
-		Run:      runHello,
-	}
-	cmdHello31 := &Command{
-		Name:  "hello31",
-		Short: "Print strings on stdout preceded by \"Hello\"",
-		Long: `
-Hello prints any strings passed in to stdout preceded by "Hello".
-`,
-		ArgsName: "[strings]",
-		ArgsLong: "[strings] are arbitrary strings that will be printed.",
-		Run:      runHello,
-	}
-	cmdHello32 := &Command{
-		Name:  "hello32",
-		Short: "Print strings on stdout preceded by \"Hello\"",
-		Long: `
-Hello prints any strings passed in to stdout preceded by "Hello".
-`,
-		ArgsName: "[strings]",
-		ArgsLong: "[strings] are arbitrary strings that will be printed.",
-		Run:      runHello,
-	}
-	progHello3 := &Command{
-		Name:     "prog3",
-		Short:    "Set of hello commands",
-		Long:     "Prog3 has two variants of hello.",
-		Children: []*Command{cmdHello31, cmdHello32},
-	}
-	progHello2 := &Command{
-		Name:     "prog2",
-		Short:    "Set of hello commands",
-		Long:     "Prog2 has two variants of hello and a subprogram prog3.",
-		Children: []*Command{cmdHello21, progHello3, cmdHello22},
-	}
-	progHello1 := &Command{
-		Name:     "prog1",
-		Short:    "Set of hello commands",
-		Long:     "Prog1 has two variants of hello and a subprogram prog2.",
-		Children: []*Command{cmdHello11, cmdHello12, progHello2},
-	}
-
-	var tests = []testCase{
-		{
-			Args: []string{},
-			Err:  ErrUsage,
-			Stderr: `ERROR: prog1: no command specified
-
-Prog1 has two variants of hello and a subprogram prog2.
-
-Usage:
-   prog1 <command>
-
-The prog1 commands are:
-   hello11     Print strings on stdout preceded by "Hello"
-   hello12     Print strings on stdout preceded by "Hello"
-   prog2       Set of hello commands
-   help        Display help for commands or topics
-Run "prog1 help [command]" for command usage.
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-`,
-		},
-		{
-			Args: []string{"help"},
-			Stdout: `Prog1 has two variants of hello and a subprogram prog2.
-
-Usage:
-   prog1 <command>
-
-The prog1 commands are:
-   hello11     Print strings on stdout preceded by "Hello"
-   hello12     Print strings on stdout preceded by "Hello"
-   prog2       Set of hello commands
-   help        Display help for commands or topics
-Run "prog1 help [command]" for command usage.
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-`,
-		},
-		{
-			Args: []string{"help", "..."},
-			Stdout: `Prog1 has two variants of hello and a subprogram prog2.
-
-Usage:
-   prog1 <command>
-
-The prog1 commands are:
-   hello11     Print strings on stdout preceded by "Hello"
-   hello12     Print strings on stdout preceded by "Hello"
-   prog2       Set of hello commands
-   help        Display help for commands or topics
-Run "prog1 help [command]" for command usage.
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-================================================================================
-Prog1 Hello11
-
-Hello prints any strings passed in to stdout preceded by "Hello".
-
-Usage:
-   prog1 hello11 [strings]
-
-[strings] are arbitrary strings that will be printed.
-================================================================================
-Prog1 Hello12
-
-Hello prints any strings passed in to stdout preceded by "Hello".
-
-Usage:
-   prog1 hello12 [strings]
-
-[strings] are arbitrary strings that will be printed.
-================================================================================
-Prog1 Prog2
-
-Prog2 has two variants of hello and a subprogram prog3.
-
-Usage:
-   prog1 prog2 <command>
-
-The prog1 prog2 commands are:
-   hello21     Print strings on stdout preceded by "Hello"
-   prog3       Set of hello commands
-   hello22     Print strings on stdout preceded by "Hello"
-================================================================================
-Prog1 Prog2 Hello21
-
-Hello prints any strings passed in to stdout preceded by "Hello".
-
-Usage:
-   prog1 prog2 hello21 [strings]
-
-[strings] are arbitrary strings that will be printed.
-================================================================================
-Prog1 Prog2 Prog3
-
-Prog3 has two variants of hello.
-
-Usage:
-   prog1 prog2 prog3 <command>
-
-The prog1 prog2 prog3 commands are:
-   hello31     Print strings on stdout preceded by "Hello"
-   hello32     Print strings on stdout preceded by "Hello"
-================================================================================
-Prog1 Prog2 Prog3 Hello31
-
-Hello prints any strings passed in to stdout preceded by "Hello".
-
-Usage:
-   prog1 prog2 prog3 hello31 [strings]
-
-[strings] are arbitrary strings that will be printed.
-================================================================================
-Prog1 Prog2 Prog3 Hello32
-
-Hello prints any strings passed in to stdout preceded by "Hello".
-
-Usage:
-   prog1 prog2 prog3 hello32 [strings]
-
-[strings] are arbitrary strings that will be printed.
-================================================================================
-Prog1 Prog2 Hello22
-
-Hello prints any strings passed in to stdout preceded by "Hello".
-
-Usage:
-   prog1 prog2 hello22 [strings]
-
-[strings] are arbitrary strings that will be printed.
-================================================================================
-Prog1 Help
-
-Help with no args displays the usage of the parent command.
-
-Help with args displays the usage of the specified sub-command or help topic.
-
-"help ..." recursively displays help for all commands and topics.
-
-The output is formatted to a target width in runes.  The target width is
-determined by checking the environment variable CMDLINE_WIDTH, falling back on
-the terminal width from the OS, 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:
-   prog1 help [flags] [command/topic ...]
-
-[command/topic ...] optionally identifies a specific sub-command or help topic.
-
-The prog1 help flags are:
- -style=text
-   The formatting style for help output, either "text" or "godoc".
-`,
-		},
-		{
-			Args: []string{"prog2", "help", "..."},
-			Stdout: `Prog2 has two variants of hello and a subprogram prog3.
-
-Usage:
-   prog1 prog2 <command>
-
-The prog1 prog2 commands are:
-   hello21     Print strings on stdout preceded by "Hello"
-   prog3       Set of hello commands
-   hello22     Print strings on stdout preceded by "Hello"
-   help        Display help for commands or topics
-Run "prog1 prog2 help [command]" for command usage.
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-================================================================================
-Prog1 Prog2 Hello21
-
-Hello prints any strings passed in to stdout preceded by "Hello".
-
-Usage:
-   prog1 prog2 hello21 [strings]
-
-[strings] are arbitrary strings that will be printed.
-================================================================================
-Prog1 Prog2 Prog3
-
-Prog3 has two variants of hello.
-
-Usage:
-   prog1 prog2 prog3 <command>
-
-The prog1 prog2 prog3 commands are:
-   hello31     Print strings on stdout preceded by "Hello"
-   hello32     Print strings on stdout preceded by "Hello"
-================================================================================
-Prog1 Prog2 Prog3 Hello31
-
-Hello prints any strings passed in to stdout preceded by "Hello".
-
-Usage:
-   prog1 prog2 prog3 hello31 [strings]
-
-[strings] are arbitrary strings that will be printed.
-================================================================================
-Prog1 Prog2 Prog3 Hello32
-
-Hello prints any strings passed in to stdout preceded by "Hello".
-
-Usage:
-   prog1 prog2 prog3 hello32 [strings]
-
-[strings] are arbitrary strings that will be printed.
-================================================================================
-Prog1 Prog2 Hello22
-
-Hello prints any strings passed in to stdout preceded by "Hello".
-
-Usage:
-   prog1 prog2 hello22 [strings]
-
-[strings] are arbitrary strings that will be printed.
-================================================================================
-Prog1 Prog2 Help
-
-Help with no args displays the usage of the parent command.
-
-Help with args displays the usage of the specified sub-command or help topic.
-
-"help ..." recursively displays help for all commands and topics.
-
-The output is formatted to a target width in runes.  The target width is
-determined by checking the environment variable CMDLINE_WIDTH, falling back on
-the terminal width from the OS, 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:
-   prog1 prog2 help [flags] [command/topic ...]
-
-[command/topic ...] optionally identifies a specific sub-command or help topic.
-
-The prog1 prog2 help flags are:
- -style=text
-   The formatting style for help output, either "text" or "godoc".
-`,
-		},
-		{
-			Args: []string{"prog2", "prog3", "help", "..."},
-			Stdout: `Prog3 has two variants of hello.
-
-Usage:
-   prog1 prog2 prog3 <command>
-
-The prog1 prog2 prog3 commands are:
-   hello31     Print strings on stdout preceded by "Hello"
-   hello32     Print strings on stdout preceded by "Hello"
-   help        Display help for commands or topics
-Run "prog1 prog2 prog3 help [command]" for command usage.
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-================================================================================
-Prog1 Prog2 Prog3 Hello31
-
-Hello prints any strings passed in to stdout preceded by "Hello".
-
-Usage:
-   prog1 prog2 prog3 hello31 [strings]
-
-[strings] are arbitrary strings that will be printed.
-================================================================================
-Prog1 Prog2 Prog3 Hello32
-
-Hello prints any strings passed in to stdout preceded by "Hello".
-
-Usage:
-   prog1 prog2 prog3 hello32 [strings]
-
-[strings] are arbitrary strings that will be printed.
-================================================================================
-Prog1 Prog2 Prog3 Help
-
-Help with no args displays the usage of the parent command.
-
-Help with args displays the usage of the specified sub-command or help topic.
-
-"help ..." recursively displays help for all commands and topics.
-
-The output is formatted to a target width in runes.  The target width is
-determined by checking the environment variable CMDLINE_WIDTH, falling back on
-the terminal width from the OS, 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:
-   prog1 prog2 prog3 help [flags] [command/topic ...]
-
-[command/topic ...] optionally identifies a specific sub-command or help topic.
-
-The prog1 prog2 prog3 help flags are:
- -style=text
-   The formatting style for help output, either "text" or "godoc".
-`,
-		},
-		{
-			Args: []string{"help", "prog2", "prog3", "..."},
-			Stdout: `Prog3 has two variants of hello.
-
-Usage:
-   prog1 prog2 prog3 <command>
-
-The prog1 prog2 prog3 commands are:
-   hello31     Print strings on stdout preceded by "Hello"
-   hello32     Print strings on stdout preceded by "Hello"
-   help        Display help for commands or topics
-Run "prog1 prog2 prog3 help [command]" for command usage.
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-================================================================================
-Prog1 Prog2 Prog3 Hello31
-
-Hello prints any strings passed in to stdout preceded by "Hello".
-
-Usage:
-   prog1 prog2 prog3 hello31 [strings]
-
-[strings] are arbitrary strings that will be printed.
-================================================================================
-Prog1 Prog2 Prog3 Hello32
-
-Hello prints any strings passed in to stdout preceded by "Hello".
-
-Usage:
-   prog1 prog2 prog3 hello32 [strings]
-
-[strings] are arbitrary strings that will be printed.
-================================================================================
-Prog1 Prog2 Prog3 Help
-
-Help with no args displays the usage of the parent command.
-
-Help with args displays the usage of the specified sub-command or help topic.
-
-"help ..." recursively displays help for all commands and topics.
-
-The output is formatted to a target width in runes.  The target width is
-determined by checking the environment variable CMDLINE_WIDTH, falling back on
-the terminal width from the OS, 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:
-   prog1 prog2 prog3 help [flags] [command/topic ...]
-
-[command/topic ...] optionally identifies a specific sub-command or help topic.
-
-The prog1 prog2 prog3 help flags are:
- -style=text
-   The formatting style for help output, either "text" or "godoc".
-`,
-		},
-		{
-			Args: []string{"help", "-style=godoc", "..."},
-			Stdout: `Prog1 has two variants of hello and a subprogram prog2.
-
-Usage:
-   prog1 <command>
-
-The prog1 commands are:
-   hello11     Print strings on stdout preceded by "Hello"
-   hello12     Print strings on stdout preceded by "Hello"
-   prog2       Set of hello commands
-   help        Display help for commands or topics
-Run "prog1 help [command]" for command usage.
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-
-Prog1 Hello11
-
-Hello prints any strings passed in to stdout preceded by "Hello".
-
-Usage:
-   prog1 hello11 [strings]
-
-[strings] are arbitrary strings that will be printed.
-
-Prog1 Hello12
-
-Hello prints any strings passed in to stdout preceded by "Hello".
-
-Usage:
-   prog1 hello12 [strings]
-
-[strings] are arbitrary strings that will be printed.
-
-Prog1 Prog2
-
-Prog2 has two variants of hello and a subprogram prog3.
-
-Usage:
-   prog1 prog2 <command>
-
-The prog1 prog2 commands are:
-   hello21     Print strings on stdout preceded by "Hello"
-   prog3       Set of hello commands
-   hello22     Print strings on stdout preceded by "Hello"
-
-Prog1 Prog2 Hello21
-
-Hello prints any strings passed in to stdout preceded by "Hello".
-
-Usage:
-   prog1 prog2 hello21 [strings]
-
-[strings] are arbitrary strings that will be printed.
-
-Prog1 Prog2 Prog3
-
-Prog3 has two variants of hello.
-
-Usage:
-   prog1 prog2 prog3 <command>
-
-The prog1 prog2 prog3 commands are:
-   hello31     Print strings on stdout preceded by "Hello"
-   hello32     Print strings on stdout preceded by "Hello"
-
-Prog1 Prog2 Prog3 Hello31
-
-Hello prints any strings passed in to stdout preceded by "Hello".
-
-Usage:
-   prog1 prog2 prog3 hello31 [strings]
-
-[strings] are arbitrary strings that will be printed.
-
-Prog1 Prog2 Prog3 Hello32
-
-Hello prints any strings passed in to stdout preceded by "Hello".
-
-Usage:
-   prog1 prog2 prog3 hello32 [strings]
-
-[strings] are arbitrary strings that will be printed.
-
-Prog1 Prog2 Hello22
-
-Hello prints any strings passed in to stdout preceded by "Hello".
-
-Usage:
-   prog1 prog2 hello22 [strings]
-
-[strings] are arbitrary strings that will be printed.
-
-Prog1 Help
-
-Help with no args displays the usage of the parent command.
-
-Help with args displays the usage of the specified sub-command or help topic.
-
-"help ..." recursively displays help for all commands and topics.
-
-The output is formatted to a target width in runes.  The target width is
-determined by checking the environment variable CMDLINE_WIDTH, falling back on
-the terminal width from the OS, 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:
-   prog1 help [flags] [command/topic ...]
-
-[command/topic ...] optionally identifies a specific sub-command or help topic.
-
-The prog1 help flags are:
- -style=text
-   The formatting style for help output, either "text" or "godoc".
-`,
-		},
-	}
-
-	runTestCases(t, progHello1, tests)
-}
-
-func TestCommandAndArgs(t *testing.T) {
-	cmdEcho := &Command{
-		Name:  "echo",
-		Short: "Print strings on stdout",
-		Long: `
-Echo prints any strings passed in to stdout.
-`,
-		Run:      runEcho,
-		ArgsName: "[strings]",
-		ArgsLong: "[strings] are arbitrary strings that will be echoed.",
-	}
-
-	prog := &Command{
-		Name:     "cmdargs",
-		Short:    "Cmdargs program.",
-		Long:     "Cmdargs has the echo command and a Run function with args.",
-		Children: []*Command{cmdEcho},
-		Run:      runHello,
-		ArgsName: "[strings]",
-		ArgsLong: "[strings] are arbitrary strings that will be printed.",
-	}
-
-	var tests = []testCase{
-		{
-			Args:   []string{},
-			Stdout: "Hello\n",
-		},
-		{
-			Args:   []string{"foo"},
-			Stdout: "Hello foo\n",
-		},
-		{
-			Args: []string{"help"},
-			Stdout: `Cmdargs has the echo command and a Run function with args.
-
-Usage:
-   cmdargs <command>
-   cmdargs [strings]
-
-The cmdargs commands are:
-   echo        Print strings on stdout
-   help        Display help for commands or topics
-Run "cmdargs help [command]" for command usage.
-
-[strings] are arbitrary strings that will be printed.
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-`,
-		},
-		{
-			Args: []string{"help", "echo"},
-			Stdout: `Echo prints any strings passed in to stdout.
-
-Usage:
-   cmdargs echo [strings]
-
-[strings] are arbitrary strings that will be echoed.
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-`,
-		},
-		{
-			Args: []string{"help", "..."},
-			Stdout: `Cmdargs has the echo command and a Run function with args.
-
-Usage:
-   cmdargs <command>
-   cmdargs [strings]
-
-The cmdargs commands are:
-   echo        Print strings on stdout
-   help        Display help for commands or topics
-Run "cmdargs help [command]" for command usage.
-
-[strings] are arbitrary strings that will be printed.
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-================================================================================
-Cmdargs Echo
-
-Echo prints any strings passed in to stdout.
-
-Usage:
-   cmdargs echo [strings]
-
-[strings] are arbitrary strings that will be echoed.
-================================================================================
-Cmdargs Help
-
-Help with no args displays the usage of the parent command.
-
-Help with args displays the usage of the specified sub-command or help topic.
-
-"help ..." recursively displays help for all commands and topics.
-
-The output is formatted to a target width in runes.  The target width is
-determined by checking the environment variable CMDLINE_WIDTH, falling back on
-the terminal width from the OS, 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:
-   cmdargs help [flags] [command/topic ...]
-
-[command/topic ...] optionally identifies a specific sub-command or help topic.
-
-The cmdargs help flags are:
- -style=text
-   The formatting style for help output, either "text" or "godoc".
-`,
-		},
-		{
-			Args: []string{"help", "foo"},
-			Err:  ErrUsage,
-			Stderr: `ERROR: cmdargs: unknown command or topic "foo"
-
-Cmdargs has the echo command and a Run function with args.
-
-Usage:
-   cmdargs <command>
-   cmdargs [strings]
-
-The cmdargs commands are:
-   echo        Print strings on stdout
-   help        Display help for commands or topics
-Run "cmdargs help [command]" for command usage.
-
-[strings] are arbitrary strings that will be printed.
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-`,
-		},
-		{
-			Args:   []string{"echo", "foo", "bar"},
-			Stdout: "[foo bar]\n",
-		},
-		{
-			Args: []string{"echo", "error"},
-			Err:  errEcho,
-		},
-		{
-			Args: []string{"echo", "bad_arg"},
-			Err:  ErrUsage,
-			Stderr: `ERROR: Invalid argument bad_arg
-
-Echo prints any strings passed in to stdout.
-
-Usage:
-   cmdargs echo [strings]
-
-[strings] are arbitrary strings that will be echoed.
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-`,
-		},
-	}
-	runTestCases(t, prog, tests)
-}
-
-func TestCommandAndRunNoArgs(t *testing.T) {
-	cmdEcho := &Command{
-		Name:  "echo",
-		Short: "Print strings on stdout",
-		Long: `
-Echo prints any strings passed in to stdout.
-`,
-		Run:      runEcho,
-		ArgsName: "[strings]",
-		ArgsLong: "[strings] are arbitrary strings that will be echoed.",
-	}
-
-	prog := &Command{
-		Name:     "cmdrun",
-		Short:    "Cmdrun program.",
-		Long:     "Cmdrun has the echo command and a Run function with no args.",
-		Children: []*Command{cmdEcho},
-		Run:      runHello,
-	}
-
-	var tests = []testCase{
-		{
-			Args:   []string{},
-			Stdout: "Hello\n",
-		},
-		{
-			Args: []string{"foo"},
-			Err:  ErrUsage,
-			Stderr: `ERROR: cmdrun: unknown command "foo"
-
-Cmdrun has the echo command and a Run function with no args.
-
-Usage:
-   cmdrun <command>
-   cmdrun
-
-The cmdrun commands are:
-   echo        Print strings on stdout
-   help        Display help for commands or topics
-Run "cmdrun help [command]" for command usage.
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-`,
-		},
-		{
-			Args: []string{"help"},
-			Stdout: `Cmdrun has the echo command and a Run function with no args.
-
-Usage:
-   cmdrun <command>
-   cmdrun
-
-The cmdrun commands are:
-   echo        Print strings on stdout
-   help        Display help for commands or topics
-Run "cmdrun help [command]" for command usage.
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-`,
-		},
-		{
-			Args: []string{"help", "echo"},
-			Stdout: `Echo prints any strings passed in to stdout.
-
-Usage:
-   cmdrun echo [strings]
-
-[strings] are arbitrary strings that will be echoed.
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-`,
-		},
-		{
-			Args: []string{"help", "..."},
-			Stdout: `Cmdrun has the echo command and a Run function with no args.
-
-Usage:
-   cmdrun <command>
-   cmdrun
-
-The cmdrun commands are:
-   echo        Print strings on stdout
-   help        Display help for commands or topics
-Run "cmdrun help [command]" for command usage.
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-================================================================================
-Cmdrun Echo
-
-Echo prints any strings passed in to stdout.
-
-Usage:
-   cmdrun echo [strings]
-
-[strings] are arbitrary strings that will be echoed.
-================================================================================
-Cmdrun Help
-
-Help with no args displays the usage of the parent command.
-
-Help with args displays the usage of the specified sub-command or help topic.
-
-"help ..." recursively displays help for all commands and topics.
-
-The output is formatted to a target width in runes.  The target width is
-determined by checking the environment variable CMDLINE_WIDTH, falling back on
-the terminal width from the OS, 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:
-   cmdrun help [flags] [command/topic ...]
-
-[command/topic ...] optionally identifies a specific sub-command or help topic.
-
-The cmdrun help flags are:
- -style=text
-   The formatting style for help output, either "text" or "godoc".
-`,
-		},
-		{
-			Args: []string{"help", "foo"},
-			Err:  ErrUsage,
-			Stderr: `ERROR: cmdrun: unknown command or topic "foo"
-
-Cmdrun has the echo command and a Run function with no args.
-
-Usage:
-   cmdrun <command>
-   cmdrun
-
-The cmdrun commands are:
-   echo        Print strings on stdout
-   help        Display help for commands or topics
-Run "cmdrun help [command]" for command usage.
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-`,
-		},
-		{
-			Args:   []string{"echo", "foo", "bar"},
-			Stdout: "[foo bar]\n",
-		},
-		{
-			Args: []string{"echo", "error"},
-			Err:  errEcho,
-		},
-		{
-			Args: []string{"echo", "bad_arg"},
-			Err:  ErrUsage,
-			Stderr: `ERROR: Invalid argument bad_arg
-
-Echo prints any strings passed in to stdout.
-
-Usage:
-   cmdrun echo [strings]
-
-[strings] are arbitrary strings that will be echoed.
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-`,
-		},
-	}
-	runTestCases(t, prog, tests)
-}
-
-func TestLongCommandsHelp(t *testing.T) {
-	cmdLong := &Command{
-		Name:  "thisisaverylongcommand",
-		Short: "the short description of the very long command is very long, and will have to be wrapped",
-		Long:  "The long description of the very long command is also very long, and will similarly have to be wrapped",
-		Run:   runEcho,
-	}
-	cmdShort := &Command{
-		Name:  "x",
-		Short: "description of short command.",
-		Long:  "blah blah blah",
-		Run:   runEcho,
-	}
-	prog := &Command{
-		Name:     "program",
-		Short:    "Test help strings when there are long commands.",
-		Long:     "Test help strings when there are long commands.",
-		Children: []*Command{cmdShort, cmdLong},
-	}
-	var tests = []testCase{
-		{
-			Args: []string{"help"},
-			Stdout: `Test help strings when there are long commands.
-
-Usage:
-   program <command>
-
-The program commands are:
-   x                      description of short command.
-   thisisaverylongcommand the short description of the very long command is very
-                          long, and will have to be wrapped
-   help                   Display help for commands or topics
-Run "program help [command]" for command usage.
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-`,
-		},
-		{
-			Args: []string{"help", "thisisaverylongcommand"},
-			Stdout: `The long description of the very long command is also very long, and will
-similarly have to be wrapped
-
-Usage:
-   program thisisaverylongcommand
-
-The global flags are:
- -global1=
-   global test flag 1
- -global2=0
-   global test flag 2
-`,
-		},
-	}
-	runTestCases(t, prog, tests)
-}
diff --git a/lib/expect/expect.go b/lib/expect/expect.go
index 957f244..b9b8311 100644
--- a/lib/expect/expect.go
+++ b/lib/expect/expect.go
@@ -282,46 +282,75 @@
 	if s.Failed() {
 		return
 	}
+	if err := s.expectSetRE(len(expected), expected...); err != nil {
+		s.error(err)
+	}
+}
+
+// ExpectSetEventuallyRE is like ExpectSetRE except that it reads
+// all remaining output rather than just the next n lines and thus
+// can be used to look for a set of patterns that occur within that
+// output.
+func (s *Session) ExpectSetEventuallyRE(expected ...string) {
+	if s.Failed() {
+		return
+	}
+	if err := s.expectSetRE(-1, expected...); err != nil {
+		s.error(err)
+	}
+}
+
+// expectSetRE will look for the expected set of patterns in the next
+// numLines of output or in all remaining output.
+func (s *Session) expectSetRE(numLines int, expected ...string) error {
+
 	regexps := make([]*regexp.Regexp, len(expected))
 	for i, expRE := range expected {
 		re, err := regexp.Compile(expRE)
 		if err != nil {
-			s.error(err)
-			return
+			return err
 		}
 		regexps[i] = re
 	}
-	actual := make([]string, len(expected))
-	for i := 0; i < len(expected); i++ {
+	actual := []string{}
+	i := 0
+	for {
 		line, err := s.read(readLine)
 		line = strings.TrimRight(line, "\n")
 		s.log(err, "ExpectSetRE: %s", line)
 		if err != nil {
-			s.error(err)
-			return
+			if numLines >= 0 {
+				return err
+			}
+			break
 		}
-		actual[i] = line
+		actual = append(actual, line)
+		i++
+		if numLines > 0 && i >= numLines {
+			break
+		}
 	}
+
 	// Match each line against all regexp's and remove each regexp
 	// that matches.
 	for _, l := range actual {
-		found := false
 		for i, re := range regexps {
 			if re == nil {
 				continue
 			}
 			if re.MatchString(l) {
-				// We remove each RE that matches.
-				found = true
 				regexps[i] = nil
 				break
 			}
 		}
-		if !found {
-			s.error(fmt.Errorf("found no match for %q", l))
-			return
+	}
+	// It's an error if there are any unmatched regexps.
+	for _, re := range regexps {
+		if re != nil {
+			return fmt.Errorf("found no match for %q", re)
 		}
 	}
+	return nil
 }
 
 // ReadLine reads the next line, if any, from the input stream. It will set
diff --git a/lib/expect/expect_test.go b/lib/expect/expect_test.go
index 8fa62da..8e8424d 100644
--- a/lib/expect/expect_test.go
+++ b/lib/expect/expect_test.go
@@ -102,12 +102,42 @@
 	buffer.WriteString("ooh\n")
 	buffer.WriteString("aah\n")
 	s.ExpectSetRE("bar=.*", "def")
-	if got, want := s.Error(), "expect_test.go:104: found no match for \"ooh\""; got == nil || got.Error() != want {
+	if got, want := s.Error(), "expect_test.go:104: found no match for \"bar=.*\""; got == nil || got.Error() != want {
 		t.Errorf("got %v, want %q", got, want)
 	}
 	s.ExpectEOF()
 }
 
+func TestExpectSetEventuallyRE(t *testing.T) {
+	buf := []byte{}
+	buffer := bytes.NewBuffer(buf)
+	buffer.WriteString("bar=baz\n")
+	buffer.WriteString("abc\n")
+	buffer.WriteString("def\n")
+	buffer.WriteString("abc\n")
+	s := expect.NewSession(nil, bufio.NewReader(buffer), time.Minute)
+	s.SetVerbosity(testing.Verbose())
+	s.ExpectSetEventuallyRE("^bar=.*$", "def")
+	if s.Error() != nil {
+		t.Errorf("unexpected error: %s", s.Error())
+	}
+	s.ExpectSetEventuallyRE("abc")
+	if got, want := s.Error(), "expect_test.go:124: found no match for \"abc\""; got == nil || got.Error() != want {
+		t.Errorf("got %q, want %q", got, want)
+	}
+	// Need to clear the EOF from the previous ExpectSetEventuallyRE call
+	buf = []byte{}
+	buffer = bytes.NewBuffer(buf)
+	s = expect.NewSession(nil, bufio.NewReader(buffer), time.Minute)
+	buffer.WriteString("ooh\n")
+	buffer.WriteString("aah\n")
+	s.ExpectSetEventuallyRE("zzz")
+	if got, want := s.Error(), "expect_test.go:134: found no match for \"zzz\""; got == nil || got.Error() != want {
+		t.Errorf("got %q, want %q", got, want)
+	}
+	s.ExpectEOF()
+}
+
 func TestRead(t *testing.T) {
 	buf := []byte{}
 	buffer := bytes.NewBuffer(buf)
diff --git a/lib/filelocker/locker.go b/lib/filelocker/locker.go
index 5e6674f..43dfd5f 100644
--- a/lib/filelocker/locker.go
+++ b/lib/filelocker/locker.go
@@ -18,7 +18,7 @@
 // Lock locks the provided file.
 //
 // If the file is already locked then the calling goroutine
-// blocks until the lock can be acquired.
+// blocks until the lock can be grabbed.
 //
 // The file must exist otherwise an error is returned.
 func Lock(filepath string) (Unlocker, error) {
@@ -32,6 +32,21 @@
 	return &unlocker{file}, nil
 }
 
+// TryLock tries to grab a lock on the provided file and
+// returns an error if it fails. This function is non-blocking.
+//
+// The file must exist otherwise an error is returned.
+func TryLock(filepath string) (Unlocker, error) {
+	file, err := os.Open(filepath)
+	if err != nil {
+		return nil, err
+	}
+	if err = syscall.Flock(int(file.Fd()), syscall.LOCK_EX|syscall.LOCK_NB); err != nil {
+		return nil, err
+	}
+	return &unlocker{file}, nil
+}
+
 // unlocker implements Unlocker.
 type unlocker struct {
 	file *os.File
diff --git a/lib/filelocker/locker_test.go b/lib/filelocker/locker_test.go
index d98e093..f3ce658 100644
--- a/lib/filelocker/locker_test.go
+++ b/lib/filelocker/locker_test.go
@@ -6,6 +6,7 @@
 	"io"
 	"io/ioutil"
 	"os"
+	"syscall"
 	"testing"
 	"time"
 
@@ -14,7 +15,7 @@
 )
 
 func init() {
-	modules.RegisterChild("testLockUnlockChild", "", testLockUnlockChild)
+	modules.RegisterChild("testLockChild", "", testLockChild)
 }
 
 func TestHelperProcess(t *testing.T) {
@@ -39,7 +40,7 @@
 	}
 }
 
-func testLockUnlockChild(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+func testLockChild(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
 	// Lock the file
 	unlocker, err := Lock(args[1])
 	if err != nil {
@@ -58,13 +59,13 @@
 	return nil
 }
 
-func TestLockUnlockInterProcess(t *testing.T) {
+func TestLockInterProcess(t *testing.T) {
 	filepath := newFile()
 	defer os.Remove(filepath)
 
-	sh := modules.NewShell("testLockUnlockChild")
+	sh := modules.NewShell()
 	defer sh.Cleanup(os.Stderr, os.Stderr)
-	h, err := sh.Start("testLockUnlockChild", nil, filepath)
+	h, err := sh.Start("testLockChild", nil, filepath)
 	if err != nil {
 		t.Fatalf("sh.Start failed: %v", err)
 	}
@@ -97,7 +98,7 @@
 	s.ExpectEOF()
 }
 
-func TestLockUnlockIntraProcess(t *testing.T) {
+func TestLockIntraProcess(t *testing.T) {
 	filepath := newFile()
 	defer os.Remove(filepath)
 
@@ -127,3 +128,33 @@
 		t.Fatal("Another goroutine failed to grab the lock after this goroutine released it")
 	}
 }
+
+func TestTryLock(t *testing.T) {
+	filepath := newFile()
+	defer os.Remove(filepath)
+
+	sh := modules.NewShell()
+	defer sh.Cleanup(os.Stderr, os.Stderr)
+	h, err := sh.Start("testLockChild", nil, filepath)
+	if err != nil {
+		t.Fatalf("sh.Start failed: %v", err)
+	}
+	s := expect.NewSession(t, h.Stdout(), time.Minute)
+
+	// Test that child grabbed the lock.
+	s.Expect("locked")
+
+	// Test that parent cannot grab the lock, and then send a message
+	// to the child to release the lock.
+	if _, err := TryLock(filepath); err != syscall.EWOULDBLOCK {
+		t.Fatal("TryLock returned error: %v, want: %v", err, syscall.EWOULDBLOCK)
+	}
+
+	// Test that the parent can grab the lock after the child has released it.
+	h.Stdin().Write([]byte("unlock\n"))
+	s.Expect("unlocked")
+	if _, err = TryLock(filepath); err != nil {
+		t.Fatalf("TryLock failed: %v", err)
+	}
+	s.ExpectEOF()
+}
diff --git a/lib/modules/core/core.go b/lib/modules/core/core.go
index add670f..214d127 100644
--- a/lib/modules/core/core.go
+++ b/lib/modules/core/core.go
@@ -48,8 +48,6 @@
 //    runs a proxy server
 package core
 
-import "veyron.io/veyron/veyron/lib/modules"
-
 const (
 	// Functions
 	LSCommand                = "ls"
@@ -70,50 +68,3 @@
 	WSPRCommand        = "wsprd"
 	ShellCommand       = "sh"
 )
-
-// NewShell returns a new Shell instance with the core commands installed.
-func NewShell() *modules.Shell {
-	shell := modules.NewShell("")
-	Install(shell)
-	return shell
-}
-
-// Install installs the core commands into the supplied Shell.
-func Install(shell *modules.Shell) {
-	// Explicitly add the subprocesses so that we can provide a help string
-	shell.AddSubprocess(EchoServerCommand, `<message> <name>
-	mount an echo server at <name>, it will prepend <message> to its responses`)
-	shell.AddSubprocess(EchoClientCommand, `
-		<name> <text>
-		invoke name.Echo(<text>)`)
-	shell.AddSubprocess(RootMTCommand, `run a root mount table
-		it will output MT_NAME, MT_ADDR and PID`)
-	shell.AddSubprocess(MTCommand, `<name>
-		run a mount table mounted at <name>`)
-	shell.AddSubprocess(LSExternalCommand, `<glob>
-		run a glob command as an external subprocess`)
-	shell.AddSubprocess(ProxyServerCommand, `<name>...
-		run a proxy server mounted at the specified names`)
-	// TODO(sadovsky): It's unfortunate that we must duplicate help strings
-	// between RegisterChild and AddSubprocess. Will be fixed by my proposed
-	// refactoring.
-	shell.AddSubprocess(WSPRCommand, usageWSPR())
-	//shell.AddSubprocess(ShellCommand, subshell, "")
-
-	shell.AddFunction(LSCommand, ls, `<glob>...
-	issues glob requests using the current processes namespace library`)
-	shell.AddFunction(ResolveCommand, resolveObject, `<name>
-	resolves name to obtain an object server address`)
-	shell.AddFunction(ResolveMTCommand, resolveMT, `<name>
-	resolves name to obtain a mount table address`)
-	shell.AddFunction(SetNamespaceRootsCommand, setNamespaceRoots, `<name>...
-	set the in-process namespace roots to <name>...`)
-	shell.AddFunction(SleepCommand, sleep, `[duration]
-	sleep for a time (in go time.Duration format): defaults to 1s`)
-	shell.AddFunction(TimeCommand, now, `
-	prints the current time`)
-	shell.AddFunction(NamespaceCacheCommand, namespaceCache, `on|off
-	turns the namespace cache on or off`)
-	shell.AddFunction(MountCommand, mountServer, `<mountpoint> <server> <ttl> [M][R]
-	invokes namespace.Mount(<mountpoint>, <server>, <ttl>)`)
-}
diff --git a/lib/modules/core/core_test.go b/lib/modules/core/core_test.go
index d8a2a1d..5f72ee9 100644
--- a/lib/modules/core/core_test.go
+++ b/lib/modules/core/core_test.go
@@ -6,6 +6,7 @@
 	"reflect"
 	"sort"
 	"strconv"
+	"strings"
 	"testing"
 	"time"
 
@@ -22,7 +23,7 @@
 )
 
 func TestCommands(t *testing.T) {
-	sh := core.NewShell()
+	sh := modules.NewShell()
 	defer sh.Cleanup(nil, os.Stderr)
 	for _, c := range []string{core.RootMTCommand, core.MTCommand} {
 		if len(sh.Help(c)) == 0 {
@@ -37,7 +38,7 @@
 }
 
 func newShell() (*modules.Shell, func()) {
-	sh := core.NewShell()
+	sh := modules.NewShell()
 	return sh, func() {
 		if testing.Verbose() {
 			vlog.Infof("------ cleanup ------")
@@ -242,6 +243,7 @@
 	srvSession.ExpectVar("NAME")
 	addr := srvSession.ExpectVar("ADDR")
 	addr = naming.JoinAddressName(addr, "")
+	wsAddr := strings.Replace(addr, "@tcp@", "@ws@", 1)
 
 	// Resolve an object
 	resolver, err := sh.Start(core.ResolveCommand, nil, rootName+"/"+echoName)
@@ -249,11 +251,11 @@
 		t.Fatalf("unexpected error: %s", err)
 	}
 	resolverSession := expect.NewSession(t, resolver.Stdout(), time.Minute)
-	if got, want := resolverSession.ExpectVar("RN"), "1"; got != want {
+	if got, want := resolverSession.ExpectVar("RN"), "2"; got != want {
 		t.Fatalf("got %v, want %v", got, want)
 	}
-	if got, want := resolverSession.ExpectVar("R0"), addr; got != want {
-		t.Errorf("got %v, want %v", got, want)
+	if got, want := resolverSession.ExpectVar("R0"), addr; got != want && got != wsAddr {
+		t.Errorf("got %v, want either %v or %v", got, want, wsAddr)
 	}
 	if err = resolver.Shutdown(nil, os.Stderr); err != nil {
 		t.Fatalf("unexpected error: %s", err)
@@ -261,16 +263,17 @@
 
 	// Resolve to a mount table using a rooted name.
 	addr = naming.JoinAddressName(mountAddrs[mtName], "echo")
+	wsAddr = strings.Replace(addr, "@tcp@", "@ws@", 1)
 	resolver, err = sh.Start(core.ResolveMTCommand, nil, rootName+"/"+echoName)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
 	resolverSession = expect.NewSession(t, resolver.Stdout(), time.Minute)
-	if got, want := resolverSession.ExpectVar("RN"), "1"; got != want {
+	if got, want := resolverSession.ExpectVar("RN"), "2"; got != want {
 		t.Fatalf("got %v, want %v", got, want)
 	}
-	if got, want := resolverSession.ExpectVar("R0"), addr; got != want {
-		t.Fatalf("got %v, want %v", got, want)
+	if got, want := resolverSession.ExpectVar("R0"), addr; got != want && got != wsAddr {
+		t.Fatalf("got %v, want either %v or %v", got, want, wsAddr)
 	}
 	if err := resolver.Shutdown(nil, os.Stderr); err != nil {
 		t.Fatalf("unexpected error: %s", err)
@@ -290,11 +293,11 @@
 		t.Fatalf("unexpected error: %s", err)
 	}
 	resolverSession = expect.NewSession(t, resolver.Stdout(), time.Minute)
-	if got, want := resolverSession.ExpectVar("RN"), "1"; got != want {
+	if got, want := resolverSession.ExpectVar("RN"), "2"; got != want {
 		t.Fatalf("got %v, want %v", got, want)
 	}
-	if got, want := resolverSession.ExpectVar("R0"), addr; got != want {
-		t.Fatalf("got %v, want %v", got, want)
+	if got, want := resolverSession.ExpectVar("R0"), addr; got != want && got != wsAddr {
+		t.Fatalf("got %v, want either %v or %v", got, want, wsAddr)
 	}
 	if err := resolver.Shutdown(nil, os.Stderr); err != nil {
 		t.Fatalf("unexpected error: %s", err)
diff --git a/lib/modules/core/echo.go b/lib/modules/core/echo.go
index e6181cc..be24117 100644
--- a/lib/modules/core/echo.go
+++ b/lib/modules/core/echo.go
@@ -22,7 +22,7 @@
 
 type treeDispatcher struct{ id string }
 
-func (d treeDispatcher) Lookup(suffix, method string) (interface{}, security.Authorizer, error) {
+func (d treeDispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) {
 	return &echoServerObject{d.id, suffix}, nil, nil
 }
 
diff --git a/lib/modules/core/misc.go b/lib/modules/core/misc.go
index 577f3b2..35b54c2 100644
--- a/lib/modules/core/misc.go
+++ b/lib/modules/core/misc.go
@@ -11,6 +11,19 @@
 	"veyron.io/veyron/veyron/lib/modules"
 )
 
+func init() {
+
+	modules.RegisterFunction(NamespaceCacheCommand, `on|off
+	turns the namespace cache on or off`, namespaceCache)
+	modules.RegisterFunction(MountCommand, `<mountpoint> <server> <ttl> [M][R]
+	invokes namespace.Mount(<mountpoint>, <server>, <ttl>)`, mountServer)
+	modules.RegisterFunction(SleepCommand, `[duration]
+	sleep for a time(in go time.Duration format): defaults to 1s`, sleep)
+	modules.RegisterFunction(TimeCommand, `
+	prints the current time`, now)
+
+}
+
 func sleep(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
 	d := time.Second
 	if len(args) > 1 {
diff --git a/lib/modules/core/mounttable.go b/lib/modules/core/mounttable.go
index 306498f..51373a6 100644
--- a/lib/modules/core/mounttable.go
+++ b/lib/modules/core/mounttable.go
@@ -22,6 +22,18 @@
 	modules.RegisterChild(LSExternalCommand, `<glob>...
 	issues glob requests using the current processes namespace library`,
 		ls)
+	modules.RegisterFunction(LSCommand, `<glob>...
+	issues glob requests using the current processes namespace library`, ls)
+	modules.RegisterFunction(ResolveCommand, `<name>
+	resolves name to obtain an object server address`, resolveObject)
+	modules.RegisterFunction(ResolveMTCommand, `<name>
+	resolves name to obtain a mount table address`, resolveMT)
+	modules.RegisterFunction(SetNamespaceRootsCommand, `<name>...
+	set the in-process namespace roots to <name>...`, setNamespaceRoots)
+	modules.RegisterFunction(NamespaceCacheCommand, `on|off
+	turns the namespace cache on or off`, namespaceCache)
+	modules.RegisterFunction(MountCommand, `<mountpoint> <server> <ttl> [M][R]
+	invokes namespace.Mount(<mountpoint>, <server>, <ttl>)`, mountServer)
 }
 
 func mountTable(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
@@ -108,7 +120,7 @@
 	return nil
 }
 
-type resolver func(ctx context.T, name string, opts ...naming.ResolveOpt) (names []string, err error)
+type resolver func(ctx context.T, name string) (names []string, err error)
 
 func resolve(fn resolver, stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
 	if err := checkArgs(args[1:], 1, "<name>"); err != nil {
diff --git a/lib/modules/core/wspr.go b/lib/modules/core/wspr.go
index 7665530..18bdf7c 100644
--- a/lib/modules/core/wspr.go
+++ b/lib/modules/core/wspr.go
@@ -47,7 +47,8 @@
 	}
 	args = fl.Args()
 
-	proxy := wspr.NewWSPR(*port, initListenSpec(fl), *identd, nil)
+	l := initListenSpec(fl)
+	proxy := wspr.NewWSPR(*port, nil, &l, *identd, nil)
 	defer proxy.Shutdown()
 
 	addr := proxy.Listen()
diff --git a/lib/modules/exec.go b/lib/modules/exec.go
index 6d45f6c..d656d0a 100644
--- a/lib/modules/exec.go
+++ b/lib/modules/exec.go
@@ -2,11 +2,9 @@
 
 import (
 	"flag"
-	"fmt"
 	"io"
 	"os"
 	"os/exec"
-	"regexp"
 	"strings"
 	"sync"
 	"time"
@@ -63,14 +61,8 @@
 	return runFlag.Value.String() == "TestHelperProcess"
 }
 
-// IsModulesProcess returns true if this process is run using
-// the modules package.
-func IsModulesProcess() bool {
-	return os.Getenv(ShellEntryPoint) != ""
-}
-
 func newExecHandle(name string) command {
-	return &execHandle{name: name, entryPoint: ShellEntryPoint + "=" + name}
+	return &execHandle{name: name, entryPoint: shellEntryPoint + "=" + name}
 }
 
 func (eh *execHandle) Stdout() io.Reader {
@@ -101,11 +93,11 @@
 	newargs := []string{os.Args[0]}
 	newargs = append(newargs, testFlags()...)
 	newargs = append(newargs, args...)
-	// Be careful to remove any existing ShellEntryPoint env vars. This
+	// Be careful to remove any existing shellEntryPoint env vars. This
 	// can happen when subprocesses run other subprocesses etc.
 	cleaned := make([]string, 0, len(env)+1)
 	for _, e := range env {
-		if strings.HasPrefix(e, ShellEntryPoint+"=") {
+		if strings.HasPrefix(e, shellEntryPoint+"=") {
 			continue
 		}
 		cleaned = append(cleaned, e)
@@ -194,126 +186,3 @@
 	}
 	return procErr
 }
-
-const ShellEntryPoint = "VEYRON_SHELL_HELPER_PROCESS_ENTRY_POINT"
-
-func RegisterChild(name, help string, main Main) {
-	child.Lock()
-	defer child.Unlock()
-	child.mains[name] = &childEntryPoint{main, help}
-}
-
-// DispatchInTest will execute the requested subproccess command from within
-// a unit test run as a subprocess.
-func DispatchInTest() {
-	if !IsTestHelperProcess() {
-		return
-	}
-	if err := child.dispatch(); err != nil {
-		vlog.Fatalf("Failed: %s", err)
-	}
-	os.Exit(0)
-}
-
-// Dispatch will execute the requested subprocess command from a within a
-// a subprocess that is not a unit test.
-func Dispatch() error {
-	if IsTestHelperProcess() {
-		return fmt.Errorf("use DispatchInTest in unittests")
-	}
-	return child.dispatch()
-}
-
-func (child *childRegistrar) hasCommand(name string) bool {
-	child.Lock()
-	_, present := child.mains[name]
-	child.Unlock()
-	return present
-}
-
-func (child *childRegistrar) dispatch() error {
-	ch, err := vexec.GetChildHandle()
-	if err != nil {
-		// This is for debugging only. It's perfectly reasonable for this
-		// error to occur if the process is started by a means other
-		// than the exec library.
-		vlog.VI(1).Infof("failed to get child handle: %s", err)
-	}
-
-	// Only signal that the child is ready or failed if we successfully get
-	// a child handle. We most likely failed to get a child handle
-	// because the subprocess was run directly from the command line.
-	command := os.Getenv(ShellEntryPoint)
-	if len(command) == 0 {
-		err := fmt.Errorf("Failed to find entrypoint %q", ShellEntryPoint)
-		if ch != nil {
-			ch.SetFailed(err)
-		}
-		return err
-	}
-
-	child.Lock()
-	m := child.mains[command]
-	child.Unlock()
-
-	if m == nil {
-		err := fmt.Errorf("Shell command %q not registered", command)
-		if ch != nil {
-			ch.SetFailed(err)
-		}
-		return err
-	}
-
-	if ch != nil {
-		ch.SetReady()
-	}
-
-	go func(pid int) {
-		for {
-			_, err := os.FindProcess(pid)
-			if err != nil {
-				vlog.Fatalf("Looks like our parent exited: %v", err)
-			}
-			time.Sleep(time.Second)
-		}
-	}(os.Getppid())
-
-	args := append([]string{command}, flag.Args()...)
-	return m.fn(os.Stdin, os.Stdout, os.Stderr, envSliceToMap(os.Environ()), args...)
-}
-
-func (child *childRegistrar) addSubprocesses(sh *Shell, pattern string) error {
-	re, err := regexp.Compile(pattern)
-	if err != nil {
-		return err
-	}
-	child.Lock()
-	defer child.Unlock()
-	found := false
-	for name, subproc := range child.mains {
-		if re.MatchString(name) {
-			sh.addSubprocess(name, subproc.help)
-			found = true
-		}
-	}
-	if !found {
-		return fmt.Errorf("patterh %q failed to match any registered commands", pattern)
-	}
-	return nil
-}
-
-// AddRegisteredSubprocesses adds any commands that match the regexp pattern
-// to the supplied shell.
-func AddRegisteredSubprocesses(sh *Shell, pattern string) error {
-	return child.addSubprocesses(sh, pattern)
-}
-
-// WaitForEof returns when a read on its io.Reader parameter returns io.EOF
-func WaitForEOF(stdin io.Reader) {
-	buf := [1024]byte{}
-	for {
-		if _, err := stdin.Read(buf[:]); err == io.EOF {
-			return
-		}
-	}
-}
diff --git a/lib/modules/modules_internal_test.go b/lib/modules/modules_internal_test.go
index 07e76bc..d0112e6 100644
--- a/lib/modules/modules_internal_test.go
+++ b/lib/modules/modules_internal_test.go
@@ -8,10 +8,6 @@
 	"testing"
 )
 
-func init() {
-	RegisterChild("echos", "[args]*", Echo)
-}
-
 func Echo(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
 	if len(args) == 0 {
 		return fmt.Errorf("no args")
@@ -30,19 +26,18 @@
 }
 
 func TestState(t *testing.T) {
-	sh := NewShell("echos")
-	sh.AddSubprocess("echonotregistered", "[args]*")
-	sh.AddFunction("echof", Echo, "[args]*")
+	sh := NewShell()
+	RegisterFunction("echoff", "[args]*", Echo)
 	assertNumHandles(t, sh, 0)
 
 	_, _ = sh.Start("echonotregistered", nil) // won't start.
 	hs, _ := sh.Start("echos", nil, "a")
-	hf, _ := sh.Start("echof", nil, "b")
+	hf, _ := sh.Start("echoff", nil, "b")
 	assertNumHandles(t, sh, 2)
 
 	for i, h := range []Handle{hs, hf} {
 		if got := h.Shutdown(nil, nil); got != nil {
-			t.Errorf("%d: got %q, want %q", i, got, nil)
+			t.Errorf("%d: got %q, want %v", i, got, nil)
 		}
 	}
 	assertNumHandles(t, sh, 0)
diff --git a/lib/modules/modules_test.go b/lib/modules/modules_test.go
index e155934..0852a00 100644
--- a/lib/modules/modules_test.go
+++ b/lib/modules/modules_test.go
@@ -6,6 +6,8 @@
 	"fmt"
 	"io"
 	"os"
+	"reflect"
+	"sort"
 	"strings"
 	"testing"
 	"time"
@@ -19,8 +21,13 @@
 	testutil.Init()
 	modules.RegisterChild("envtest", "envtest: <variables to print>...", PrintFromEnv)
 	modules.RegisterChild("printenv", "printenv", PrintEnv)
-	modules.RegisterChild("echo", "[args]*", Echo)
-	modules.RegisterChild("errortest", "", ErrorMain)
+	modules.RegisterChild("echos", "[args]*", Echo)
+	modules.RegisterChild("errortestChild", "", ErrorMain)
+
+	modules.RegisterFunction("envtestf", "envtest: <variables to print>...", PrintFromEnv)
+	modules.RegisterFunction("echof", "[args]*", Echo)
+	modules.RegisterFunction("errortestFunc", "", ErrorMain)
+
 }
 
 func Echo(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
@@ -111,7 +118,7 @@
 }
 
 func TestChild(t *testing.T) {
-	sh := modules.NewShell("envtest")
+	sh := modules.NewShell()
 	defer sh.Cleanup(nil, nil)
 	key, val := "simpleVar", "foo & bar"
 	sh.SetVar(key, val)
@@ -125,24 +132,23 @@
 	sh.SetVar(key, val)
 	testCommand(t, sh, "envtest", key, val)
 	_, err := sh.Start("non-existent-command", nil, "random", "args")
-	if err == nil || err.Error() != `Shell command "non-existent-command" not registered` {
-		t.Fatalf("unexpected error %v", err)
+	if err == nil {
+		t.Fatalf("expected error")
 	}
 }
 
 func TestFunction(t *testing.T) {
-	sh := modules.NewShell(".*")
+	sh := modules.NewShell()
 	defer sh.Cleanup(nil, nil)
 	key, val := "simpleVar", "foo & bar & baz"
 	sh.SetVar(key, val)
-	sh.AddFunction("envtestf", PrintFromEnv, "envtest: <variables to print>...")
 	testCommand(t, sh, "envtestf", key, val)
 }
 
 func TestErrorChild(t *testing.T) {
-	sh := modules.NewShell("errortest")
+	sh := modules.NewShell()
 	defer sh.Cleanup(nil, nil)
-	h, err := sh.Start("errortest", nil)
+	h, err := sh.Start("errortestChild", nil)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -151,16 +157,16 @@
 	}
 }
 
-func testShutdown(t *testing.T, sh *modules.Shell, isfunc bool) {
+func testShutdown(t *testing.T, sh *modules.Shell, command string, isfunc bool) {
 	result := ""
 	args := []string{"a", "b c", "ddd"}
-	if _, err := sh.Start("echo", nil, args...); err != nil {
+	if _, err := sh.Start(command, nil, args...); err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
 	var stdoutBuf bytes.Buffer
 	var stderrBuf bytes.Buffer
 	sh.Cleanup(&stdoutBuf, &stderrBuf)
-	stdoutOutput, stderrOutput := "stdout: echo\n", "stderr: echo\n"
+	stdoutOutput, stderrOutput := "stdout: "+command+"\n", "stderr: "+command+"\n"
 	for _, a := range args {
 		stdoutOutput += fmt.Sprintf("stdout: %s\n", a)
 		stderrOutput += fmt.Sprintf("stderr: %s\n", a)
@@ -177,21 +183,17 @@
 }
 
 func TestShutdownSubprocess(t *testing.T) {
-	sh := modules.NewShell("echo")
-	testShutdown(t, sh, false)
+	testShutdown(t, modules.NewShell(), "echos", false)
 }
 
 func TestShutdownFunction(t *testing.T) {
-	sh := modules.NewShell()
-	sh.AddFunction("echo", Echo, "[args]*")
-	testShutdown(t, sh, true)
+	testShutdown(t, modules.NewShell(), "echof", true)
 }
 
 func TestErrorFunc(t *testing.T) {
 	sh := modules.NewShell()
 	defer sh.Cleanup(nil, nil)
-	sh.AddFunction("errortest", ErrorMain, "")
-	h, err := sh.Start("errortest", nil)
+	h, err := sh.Start("errortestFunc", nil)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -210,7 +212,7 @@
 }
 
 func TestEnvelope(t *testing.T) {
-	sh := modules.NewShell("printenv")
+	sh := modules.NewShell()
 	defer sh.Cleanup(nil, nil)
 	sh.SetVar("a", "1")
 	sh.SetVar("b", "2")
@@ -258,7 +260,7 @@
 }
 
 func TestEnvMerge(t *testing.T) {
-	sh := modules.NewShell("printenv")
+	sh := modules.NewShell()
 	defer sh.Cleanup(nil, nil)
 	sh.SetVar("a", "1")
 	os.Setenv("a", "wrong, should be 1")
@@ -285,14 +287,24 @@
 	}
 }
 
+func TestSetEntryPoint(t *testing.T) {
+	env := map[string]string{"a": "a", "b": "b"}
+	nenv := modules.SetEntryPoint(env, "eg1")
+	if got, want := len(nenv), 3; got != want {
+		t.Errorf("got %d, want %d", got, want)
+	}
+	nenv = modules.SetEntryPoint(env, "eg2")
+	if got, want := len(nenv), 3; got != want {
+		t.Errorf("got %d, want %d", got, want)
+	}
+	sort.Strings(nenv)
+	if got, want := nenv, []string{"VEYRON_SHELL_HELPER_PROCESS_ENTRY_POINT=eg2", "a=a", "b=b"}; !reflect.DeepEqual(got, want) {
+		t.Errorf("got %d, want %d", got, want)
+	}
+}
+
 func TestHelperProcess(t *testing.T) {
 	modules.DispatchInTest()
 }
 
-// TODO(cnicolaou): more complete tests for environment variables,
-// OS being overridden by Shell for example.
-//
-// TODO(cnicolaou): test for one or either of the io.Writers being nil
-// on calls to Shutdown
-//
 // TODO(cnicolaou): test for error return from cleanup
diff --git a/lib/modules/registry.go b/lib/modules/registry.go
new file mode 100644
index 0000000..61325d2
--- /dev/null
+++ b/lib/modules/registry.go
@@ -0,0 +1,184 @@
+package modules
+
+import (
+	"flag"
+	"fmt"
+	"io"
+	"os"
+	"strings"
+	"sync"
+	"time"
+
+	"veyron.io/veyron/veyron2/vlog"
+
+	vexec "veyron.io/veyron/veyron/lib/exec"
+)
+
+type commandDesc struct {
+	factory func() command
+	main    Main
+	help    string
+}
+
+type cmdRegistry struct {
+	sync.Mutex
+	cmds map[string]*commandDesc
+}
+
+var registry = &cmdRegistry{cmds: make(map[string]*commandDesc)}
+
+func (r *cmdRegistry) addCommand(name, help string, factory func() command, main Main) {
+	r.Lock()
+	defer r.Unlock()
+	r.cmds[name] = &commandDesc{factory, main, help}
+}
+
+func (r *cmdRegistry) getCommand(name string) *commandDesc {
+	r.Lock()
+	defer r.Unlock()
+	return r.cmds[name]
+}
+
+// RegisterChild adds a new command to the registery that will be run
+// as a subprocess. It must be called before Dispatch or DispatchInTest is
+// called, typically from an init function.
+func RegisterChild(name, help string, main Main) {
+	factory := func() command { return newExecHandle(name) }
+	registry.addCommand(name, help, factory, main)
+}
+
+// RegisterFunction adds a new command to the registry that will be run
+// within the current process. It can be called at any time prior to an
+// attempt to use it.
+func RegisterFunction(name, help string, main Main) {
+	factory := func() command { return newFunctionHandle(name, main) }
+	registry.addCommand(name, help, factory, main)
+}
+
+// Help returns the help message for the specified command, or a list
+// of all commands if the command parameter is an empty string.
+func Help(command string) string {
+	return registry.help(command)
+}
+
+func (r *cmdRegistry) help(command string) string {
+	r.Lock()
+	defer r.Unlock()
+	if len(command) == 0 {
+		h := ""
+		for c, _ := range r.cmds {
+			h += c + ", "
+		}
+		return strings.TrimRight(h, ", ")
+	}
+	if c := r.cmds[command]; c != nil {
+		return command + ": " + c.help
+	}
+	return ""
+}
+
+const shellEntryPoint = "VEYRON_SHELL_HELPER_PROCESS_ENTRY_POINT"
+
+// IsModulesProcess returns true if this process is run using
+// the modules package.
+func IsModulesProcess() bool {
+	return os.Getenv(shellEntryPoint) != ""
+}
+
+// SetEntryPoint returns a slice of strings that is guaranteed to contain
+// one and only instance of the entry point environment variable set to the
+// supplied name value.
+func SetEntryPoint(env map[string]string, name string) []string {
+	newEnv := make([]string, 0, len(env)+1)
+	for k, v := range env {
+		if k == shellEntryPoint {
+			continue
+		}
+		newEnv = append(newEnv, k+"="+v)
+	}
+	return append(newEnv, shellEntryPoint+"="+name)
+}
+
+// DispatchInTest will execute the requested subproccess command from within
+// a unit test run as a subprocess.
+func DispatchInTest() {
+	if !IsTestHelperProcess() {
+		return
+	}
+	if err := registry.dispatch(); err != nil {
+		vlog.Fatalf("Failed: %s", err)
+	}
+	os.Exit(0)
+}
+
+// Dispatch will execute the requested subprocess command from a within a
+// a subprocess that is not a unit test. It will return without an error
+// if it is executed by a process that does not specify an entry point
+// in its environment.
+func Dispatch() error {
+	if !IsModulesProcess() {
+		return nil
+	}
+	if IsTestHelperProcess() {
+		return fmt.Errorf("use DispatchInTest in unittests")
+	}
+	return registry.dispatch()
+}
+
+func (r *cmdRegistry) dispatch() error {
+	ch, err := vexec.GetChildHandle()
+	if err != nil {
+		// This is for debugging only. It's perfectly reasonable for this
+		// error to occur if the process is started by a means other
+		// than the exec library.
+		vlog.VI(1).Infof("failed to get child handle: %s", err)
+	}
+
+	// Only signal that the child is ready or failed if we successfully get
+	// a child handle. We most likely failed to get a child handle
+	// because the subprocess was run directly from the command line.
+	command := os.Getenv(shellEntryPoint)
+	if len(command) == 0 {
+		err := fmt.Errorf("Failed to find entrypoint %q", command)
+		if ch != nil {
+			ch.SetFailed(err)
+		}
+		return err
+	}
+
+	m := registry.getCommand(command)
+	if m == nil {
+		err := fmt.Errorf("%s: not registered", command)
+		if ch != nil {
+			ch.SetFailed(err)
+		}
+		return err
+	}
+
+	if ch != nil {
+		ch.SetReady()
+	}
+
+	go func(pid int) {
+		for {
+			_, err := os.FindProcess(pid)
+			if err != nil {
+				vlog.Fatalf("Looks like our parent exited: %v", err)
+			}
+			time.Sleep(time.Second)
+		}
+	}(os.Getppid())
+
+	args := append([]string{command}, flag.Args()...)
+	return m.main(os.Stdin, os.Stdout, os.Stderr, envSliceToMap(os.Environ()), args...)
+}
+
+// WaitForEof returns when a read on its io.Reader parameter returns io.EOF
+func WaitForEOF(stdin io.Reader) {
+	buf := [1024]byte{}
+	for {
+		if _, err := stdin.Read(buf[:]); err == io.EOF {
+			return
+		}
+	}
+}
diff --git a/lib/modules/shell.go b/lib/modules/shell.go
index 535cc01..9e4c3b9 100644
--- a/lib/modules/shell.go
+++ b/lib/modules/shell.go
@@ -1,21 +1,15 @@
 // Package modules provides a mechanism for running commonly used services
 // as subprocesses and client functionality for accessing those services.
 // Such services and functions are collectively called 'commands' and are
-// registered with and executed within a context, defined by the Shell type.
-// The Shell is analagous to the original UNIX shell and maintains a
-// key, value store of variables that is accessible to all of the commands that
-// it hosts. These variables may be referenced by the arguments passed to
-// commands.
+// registered with a single, per-process, registry, but executed within a
+// context, defined by the Shell type. The Shell is analagous to the original
+// UNIX shell and maintains a key, value store of variables that is accessible
+// to all of the commands that it hosts. These variables may be referenced by
+// the arguments passed to commands.
 //
-// Commands are added to a shell in two ways: one for a subprocess and another
-// for an inprocess function.
-//
-// - subprocesses are added using the AddSubprocess method in the parent
-//   and by the modules.RegisterChild function in the child process (typically
-//   RegisterChild is called from an init function). modules.Dispatch must
-//   be called in the child process to execute the subprocess 'Main' function
-//   provided to RegisterChild.
-// - inprocess functions are added using the AddFunction method.
+// Commands are added to the registry in two ways:
+// - via RegisterChild for a subprocess
+// - via RegisterFunction for an in-process function
 //
 // In all cases commands are started by invoking the Start method on the
 // Shell with the name of the command to run. An instance of the Handle
@@ -42,60 +36,34 @@
 
 import (
 	"flag"
+	"fmt"
 	"io"
 	"io/ioutil"
 	"os"
-	"strings"
 	"sync"
 	"time"
 
 	"veyron.io/veyron/veyron/lib/exec"
 	"veyron.io/veyron/veyron/lib/flags/consts"
-	"veyron.io/veyron/veyron2/vlog"
 )
 
 // Shell represents the context within which commands are run.
 type Shell struct {
 	mu           sync.Mutex
 	env          map[string]string
-	cmds         map[string]*commandDesc
 	handles      map[Handle]struct{}
 	credDir      string
 	startTimeout time.Duration
 	config       exec.Config
 }
 
-type commandDesc struct {
-	factory func() command
-	help    string
-}
-
-type childEntryPoint struct {
-	fn   Main
-	help string
-}
-
-type childRegistrar struct {
-	sync.Mutex
-	mains map[string]*childEntryPoint
-}
-
-var child = &childRegistrar{mains: make(map[string]*childEntryPoint)}
-
 // NewShell creates a new instance of Shell. If this new instance is is a test
 // and no credentials have been configured in the environment via
 // consts.VeyronCredentials then CreateAndUseNewCredentials will be used to
-// configure a new ID for the shell and its children.  NewShell takes optional
-// regexp patterns that can be used to specify subprocess commands that are
-// implemented in the same binary as this shell (i.e. have been registered
-// using modules.RegisterChild) to be automatically added to it. If the
-// patterns fail to match any such command then they have no effect.
-func NewShell(patterns ...string) *Shell {
-	// TODO(cnicolaou): should create a new identity if one doesn't
-	// already exist
+// configure a new ID for the shell and its children.
+func NewShell() *Shell {
 	sh := &Shell{
 		env:          make(map[string]string),
-		cmds:         make(map[string]*commandDesc),
 		handles:      make(map[Handle]struct{}),
 		startTimeout: time.Minute,
 		config:       exec.NewConfig(),
@@ -106,9 +74,6 @@
 			panic(err)
 		}
 	}
-	for _, pattern := range patterns {
-		child.addSubprocesses(sh, pattern)
-	}
 	return sh
 }
 
@@ -134,51 +99,15 @@
 
 type Main func(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error
 
-// AddSubprocess adds a new command to the Shell that will be run
-// as a subprocess. In addition, the child process must call RegisterChild
-// using the same name used here and provide the function to be executed
-// in the child.
-func (sh *Shell) AddSubprocess(name, help string) {
-	if !child.hasCommand(name) {
-		vlog.Infof("Warning: %q is not registered with modules.Dispatcher", name)
-	}
-	sh.addSubprocess(name, help)
-}
-
-func (sh *Shell) addSubprocess(name string, help string) {
-	sh.mu.Lock()
-	sh.cmds[name] = &commandDesc{func() command { return newExecHandle(name) }, help}
-	sh.mu.Unlock()
-}
-
-// AddFunction adds a new command to the Shell that will be run
-// within the current process.
-func (sh *Shell) AddFunction(name string, main Main, help string) {
-	sh.mu.Lock()
-	sh.cmds[name] = &commandDesc{func() command { return newFunctionHandle(name, main) }, help}
-	sh.mu.Unlock()
-}
-
 // String returns a string representation of the Shell, which is a
 // list of the commands currently available in the shell.
 func (sh *Shell) String() string {
-	sh.mu.Lock()
-	defer sh.mu.Unlock()
-	h := ""
-	for n, _ := range sh.cmds {
-		h += n + ", "
-	}
-	return strings.TrimRight(h, ", ")
+	return registry.help("")
 }
 
 // Help returns the help message for the specified command.
 func (sh *Shell) Help(command string) string {
-	sh.mu.Lock()
-	defer sh.mu.Unlock()
-	if c := sh.cmds[command]; c != nil {
-		return command + ": " + c.help
-	}
-	return ""
+	return registry.help(command)
 }
 
 // Start starts the specified command, it returns a Handle which can be used
@@ -191,16 +120,14 @@
 // error is returned, in which case it may be used to retrieve any output
 // from the failed command.
 //
-// Commands may have already been registered with the Shell using AddFunction
-// or AddSubprocess, but if not, they will treated as subprocess commands
-// and an attempt made to run them. Such 'dynamically' started subprocess
-// commands are not remembered the by Shell and do not provide a 'help'
-// message etc; their handles are remembered and will be acted on by
-// the Cleanup method. If the non-registered subprocess command does not
-// exist then the Start command will return an error.
+// Commands must have already been registered using RegisterFunction
+// or RegisterChild.
 func (sh *Shell) Start(name string, env []string, args ...string) (Handle, error) {
 	cenv := sh.MergedEnv(env)
-	cmd := sh.getCommand(name)
+	cmd := registry.getCommand(name)
+	if cmd == nil {
+		return nil, fmt.Errorf("%s: not registered", name)
+	}
 	expanded := append([]string{name}, sh.expand(args...)...)
 	h, err := cmd.factory().start(sh, cenv, expanded...)
 	if err != nil {
@@ -214,16 +141,6 @@
 	return h, nil
 }
 
-func (sh *Shell) getCommand(name string) *commandDesc {
-	sh.mu.Lock()
-	cmd := sh.cmds[name]
-	sh.mu.Unlock()
-	if cmd == nil {
-		cmd = &commandDesc{func() command { return newExecHandle(name) }, ""}
-	}
-	return cmd
-}
-
 // SetStartTimeout sets the timeout for starting subcommands.
 func (sh *Shell) SetStartTimeout(d time.Duration) {
 	sh.startTimeout = d
@@ -233,7 +150,11 @@
 // used for running the subprocess or function if it were started with the
 // specifed arguments.
 func (sh *Shell) CommandEnvelope(name string, env []string, args ...string) ([]string, []string) {
-	return sh.getCommand(name).factory().envelope(sh, sh.MergedEnv(env), args...)
+	cmd := registry.getCommand(name)
+	if cmd == nil {
+		return []string{}, []string{}
+	}
+	return cmd.factory().envelope(sh, sh.MergedEnv(env), args...)
 }
 
 // Forget tells the Shell to stop tracking the supplied Handle. This is
diff --git a/lib/netconfig/ipaux_other.go b/lib/netconfig/ipaux_other.go
index 2826424..948c0c7 100644
--- a/lib/netconfig/ipaux_other.go
+++ b/lib/netconfig/ipaux_other.go
@@ -49,5 +49,7 @@
 }
 
 func GetIPRoutes(defaultOnly bool) []*IPRoute {
-	panic("Not yet implemented")
+	// TODO(nlacasse,bprosnitz): Consider implementing? For now return
+	// empty array, since that seems to keep things working.
+	return []*IPRoute{}
 }
diff --git a/lib/signals/signals_test.go b/lib/signals/signals_test.go
index e101277..d57c65c 100644
--- a/lib/signals/signals_test.go
+++ b/lib/signals/signals_test.go
@@ -113,7 +113,6 @@
 
 func newShell(t *testing.T, command string) (*modules.Shell, modules.Handle, *expect.Session) {
 	sh := modules.NewShell()
-	sh.AddSubprocess(command, "")
 	handle, err := sh.Start(command, nil)
 	if err != nil {
 		sh.Cleanup(os.Stderr, os.Stderr)
@@ -330,7 +329,6 @@
 	defer r.Cleanup()
 
 	sh := modules.NewShell()
-	sh.AddSubprocess("handleDefaults", "")
 	defer sh.Cleanup(os.Stderr, os.Stderr)
 
 	// Set the child process up with a blessing from the parent so that
diff --git a/lib/textutil/doc.go b/lib/textutil/doc.go
deleted file mode 100644
index cd28c8c..0000000
--- a/lib/textutil/doc.go
+++ /dev/null
@@ -1,5 +0,0 @@
-// Package textutil implements utilities for handling human-readable text.
-//
-// This package includes a combination of low-level and high-level utilities.
-// The main high-level utility is available as NewUTF8LineWriter.
-package textutil
diff --git a/lib/textutil/line_writer.go b/lib/textutil/line_writer.go
deleted file mode 100644
index c7df3a5..0000000
--- a/lib/textutil/line_writer.go
+++ /dev/null
@@ -1,445 +0,0 @@
-package textutil
-
-import (
-	"fmt"
-	"io"
-	"unicode"
-)
-
-// LineWriter implements an io.Writer filter that formats input text into output
-// lines with a given target width in runes.
-//
-// Each input rune is classified into one of three kinds:
-//   EOL:    end-of-line, consisting of \f, \n, \r, \v, U+2028 or U+2029
-//   Space:  defined by unicode.IsSpace
-//   Letter: everything else
-//
-// The input text is expected to consist of words, defined as sequences of
-// letters.  Sequences of words form paragraphs, where paragraphs are separated
-// by either blank lines (that contain no letters), or an explicit U+2029
-// ParagraphSeparator.  Input lines with leading spaces are treated verbatim.
-//
-// Paragraphs are output as word-wrapped lines; line breaks only occur at word
-// boundaries.  Output lines are usually no longer than the target width.  The
-// exceptions are single words longer than the target width, which are output on
-// their own line, and verbatim lines, which may be arbitrarily longer or
-// shorter than the width.
-//
-// Output lines never contain trailing spaces.  Only verbatim output lines may
-// contain leading spaces.  Spaces separating input words are output verbatim,
-// unless it would result in a line with leading or trailing spaces.
-//
-// EOL runes within the input text are never written to the output; the output
-// line terminator and paragraph separator may be configured, and some EOL may
-// be output as a single space ' ' to maintain word separation.
-//
-// The algorithm greedily fills each output line with as many words as it can,
-// assuming that all Unicode code points have the same width.  Invalid UTF-8 is
-// silently transformed to the replacement character U+FFFD and treated as a
-// single rune.
-//
-// Flush must be called after the last call to Write; the input is buffered.
-//
-//   Implementation note: line breaking is a complicated topic.  This approach
-//   attempts to be simple and useful; a full implementation conforming to
-//   Unicode Standard Annex #14 would be complicated, and is not implemented.
-//   Languages that don't use spaces to separate words (e.g. CJK) won't work
-//   well under the current approach.
-//
-//   http://www.unicode.org/reports/tr14 [Unicode Line Breaking Algorithm]
-//   http://www.unicode.org/versions/Unicode4.0.0/ch05.pdf [5.8 Newline Guidelines]
-type LineWriter struct {
-	// State configured by the user.
-	w            io.Writer
-	runeDecoder  RuneChunkDecoder
-	width        runePos
-	lineTerm     []byte
-	paragraphSep string
-	indents      []string
-
-	// The buffer contains a single output line.
-	lineBuf byteRuneBuffer
-
-	// Keep track of the previous state and rune.
-	prevState state
-	prevRune  rune
-
-	// Keep track of blank input lines.
-	inputLineHasLetter bool
-
-	// lineBuf positions where the line starts (after separators and indents), a
-	// new word has started and the last word has ended.
-	lineStart    bytePos
-	newWordStart bytePos
-	lastWordEnd  bytePos
-
-	// Keep track of paragraph terminations and line indices, so we can output the
-	// paragraph separator and indents correctly.
-	terminateParagraph bool
-	paragraphLineIndex int
-	wroteFirstLine     bool
-}
-
-type state int
-
-const (
-	stateWordWrap  state = iota // Perform word-wrapping [start state]
-	stateVerbatim               // Verbatim output-line, no word-wrapping
-	stateSkipSpace              // Skip spaces in input line.
-)
-
-// NewLineWriter returns a new LineWriter with the given target width in runes,
-// producing output on the underlying writer w.  The dec and enc are used to
-// respectively decode runes from Write calls, and encode runes to w.
-func NewLineWriter(w io.Writer, width int, dec RuneChunkDecoder, enc RuneEncoder) *LineWriter {
-	ret := &LineWriter{
-		w:            w,
-		runeDecoder:  dec,
-		width:        runePos(width),
-		lineTerm:     []byte("\n"),
-		paragraphSep: "\n",
-		prevState:    stateWordWrap,
-		prevRune:     LineSeparator,
-		lineBuf:      byteRuneBuffer{enc: enc},
-	}
-	ret.resetLine()
-	return ret
-}
-
-// NewUTF8LineWriter returns a new LineWriter filter that implements io.Writer,
-// and decodes and encodes runes in UTF-8.
-func NewUTF8LineWriter(w io.Writer, width int) *LineWriter {
-	return NewLineWriter(w, width, &UTF8ChunkDecoder{}, UTF8Encoder{})
-}
-
-// Width returns the target width in runes.  If width < 0 the width is
-// unlimited; each paragraph is output as a single line.
-func (w *LineWriter) Width() int { return int(w.width) }
-
-// SetLineTerminator sets the line terminator for subsequent Write calls.  Every
-// output line is terminated with term; EOL runes from the input are never
-// written to the output.  A new LineWriter instance uses "\n" as the default
-// line terminator.
-//
-// Calls Flush internally, and returns any Flush error.
-func (w *LineWriter) SetLineTerminator(term string) error {
-	if err := w.Flush(); err != nil {
-		return err
-	}
-	w.lineTerm = []byte(term)
-	w.resetLine()
-	return nil
-}
-
-// SetParagraphSeparator sets the paragraph separator for subsequent Write
-// calls.  Every consecutive pair of non-empty paragraphs is separated with sep;
-// EOL runes from the input are never written to the output.  A new LineWriter
-// instance uses "\n" as the default paragraph separator.
-//
-// Calls Flush internally, and returns any Flush error.
-func (w *LineWriter) SetParagraphSeparator(sep string) error {
-	if err := w.Flush(); err != nil {
-		return err
-	}
-	w.paragraphSep = sep
-	w.resetLine()
-	return nil
-}
-
-// SetIndents sets the indentation for subsequent Write calls.  Multiple indents
-// may be set, corresponding to the indent to use for the corresponding
-// paragraph line.  E.g. SetIndents("AA", "BBB", C") means the first line in
-// each paragraph is indented with "AA", the second line in each paragraph is
-// indented with "BBB", and all subsequent lines in each paragraph are indented
-// with "C".
-//
-// SetIndents() is equivalent to SetIndents(""), SetIndents("", ""), etc.
-//
-// A new LineWriter instance has no indents by default.
-//
-// Calls Flush internally, and returns any Flush error.
-func (w *LineWriter) SetIndents(indents ...string) error {
-	if err := w.Flush(); err != nil {
-		return err
-	}
-	// Copy indents in case the user passed the slice via SetIndents(p...), and
-	// canonicalize the all empty case to nil.
-	allEmpty := true
-	w.indents = make([]string, len(indents))
-	for ix, indent := range indents {
-		w.indents[ix] = indent
-		if indent != "" {
-			allEmpty = false
-		}
-	}
-	if allEmpty {
-		w.indents = nil
-	}
-	w.resetLine()
-	return nil
-}
-
-// Write implements io.Writer by buffering data into the LineWriter w.  Actual
-// writes to the underlying writer may occur, and may include data buffered in
-// either this Write call or previous Write calls.
-//
-// Flush must be called after the last call to Write.
-func (w *LineWriter) Write(data []byte) (int, error) {
-	return RuneChunkWrite(w.runeDecoder, w.addRune, data)
-}
-
-// Flush flushes any remaining buffered text, and resets the paragraph line
-// count back to 0, so that indents will be applied starting from the first
-// line.  It does not imply a paragraph separator; repeated calls to Flush with
-// no intervening calls to other methods is equivalent to a single Flush.
-//
-// Flush must be called after the last call to Write, and may be called an
-// arbitrary number of times before the last Write.
-func (w *LineWriter) Flush() error {
-	if err := RuneChunkFlush(w.runeDecoder, w.addRune); err != nil {
-		return err
-	}
-	// Add U+2028 to force the last line (if any) to be written.
-	if err := w.addRune(LineSeparator); err != nil {
-		return err
-	}
-	// Reset the paragraph line count.
-	w.paragraphLineIndex = 0
-	w.resetLine()
-	return nil
-}
-
-// addRune is called every time w.runeDecoder decodes a full rune.
-func (w *LineWriter) addRune(r rune) error {
-	state, lineBreak := w.nextState(r, w.updateRune(r))
-	if lineBreak {
-		if err := w.writeLine(); err != nil {
-			return err
-		}
-	}
-	w.bufferRune(r, state, lineBreak)
-	w.prevState = state
-	w.prevRune = r
-	return nil
-}
-
-// We classify each incoming rune into three kinds for easier handling.
-type kind int
-
-const (
-	kindEOL kind = iota
-	kindSpace
-	kindLetter
-)
-
-func runeKind(r rune) kind {
-	switch r {
-	case '\f', '\n', '\r', '\v', LineSeparator, ParagraphSeparator:
-		return kindEOL
-	}
-	if unicode.IsSpace(r) {
-		return kindSpace
-	}
-	return kindLetter
-}
-
-func (w *LineWriter) updateRune(r rune) bool {
-	forceLineBreak := false
-	switch kind := runeKind(r); kind {
-	case kindEOL:
-		// Update lastWordEnd if the last word just ended.
-		if w.newWordStart != -1 {
-			w.newWordStart = -1
-			w.lastWordEnd = w.lineBuf.ByteLen()
-		}
-		switch {
-		case w.prevRune == '\r' && r == '\n':
-			// Treat "\r\n" as a single EOL; we've already handled the logic for '\r',
-			// so there's nothing to do when we see '\n'.
-		case r == LineSeparator:
-			// Treat U+2028 as a pure line break; it's never a paragraph break.
-			forceLineBreak = true
-		case r == ParagraphSeparator || !w.inputLineHasLetter:
-			// The paragraph has just been terminated if we see an explicit U+2029, or
-			// if we see a blank line, which may contain spaces.
-			forceLineBreak = true
-			w.terminateParagraph = true
-		}
-		w.inputLineHasLetter = false
-	case kindSpace:
-		// Update lastWordEnd if the last word just ended.
-		if w.newWordStart != -1 {
-			w.newWordStart = -1
-			w.lastWordEnd = w.lineBuf.ByteLen()
-		}
-	case kindLetter:
-		// Update newWordStart if a new word just started.
-		if w.newWordStart == -1 {
-			w.newWordStart = w.lineBuf.ByteLen()
-		}
-		w.inputLineHasLetter = true
-		w.terminateParagraph = false
-	default:
-		panic(fmt.Errorf("textutil: updateRune unhandled kind %d", kind))
-	}
-	return forceLineBreak
-}
-
-// nextState returns the next state and whether we should break the line.
-//
-// Here's a handy table that describes all the scenarios in which we will line
-// break input text, grouped by the reason for the break.  The current position
-// is the last non-* rune in each pattern, which is where we decide to break.
-//
-//              w.prevState   Next state   Buffer reset
-//              -----------   ----------   ------------
-//   ===== Force line break (U+2028 / U+2029, blank line) =====
-//   a..*|***   *             wordWrap     empty
-//   a._.|***   *             wordWrap     empty
-//   a+**|***   *             wordWrap     empty
-//
-//   ===== verbatim: wait for any EOL =====
-//   _*.*|***   verbatim      wordWrap     empty
-//
-//   ===== wordWrap: switch to verbatim =====
-//   a._*|***   wordWrap      verbatim     empty
-//
-//   ===== wordWrap: line is too wide =====
-//   abc.|***   wordWrap      wordWrap     empty
-//   abcd|.**   wordWrap      wordWrap     empty
-//   abcd|e.*   wordWrap      wordWrap     empty
-//   a_cd|.**   wordWrap      wordWrap     empty
-//
-//   abc_|***   wordWrap      skipSpace    empty
-//   abcd|_**   wordWrap      skipSpace    empty
-//   abcd|e_*   wordWrap      skipSpace    empty
-//   a_cd|_**   wordWrap      skipSpace    empty
-//
-//   a_cd|e**   wordWrap      start        newWordStart
-//
-//   LEGEND
-//     abcde  Letter
-//     .      End-of-line
-//     +      End-of-line (only U+2028 / U+2029)
-//     _      Space
-//     *      Any rune (letter, line-end or space)
-//     |      Visual indication of width=4, has no width itself.
-//
-// Note that Flush calls behave exactly as if an explicit U+2028 line separator
-// were added to the end of all buffered data.
-func (w *LineWriter) nextState(r rune, forceLineBreak bool) (state, bool) {
-	if forceLineBreak {
-		return stateWordWrap, true
-	}
-	kind := runeKind(r)
-	// Handle non word-wrap states, which are easy.
-	switch w.prevState {
-	case stateVerbatim:
-		if kind == kindEOL {
-			return stateWordWrap, true
-		}
-		return stateVerbatim, false
-	case stateSkipSpace:
-		if kind == kindSpace {
-			return stateSkipSpace, false
-		}
-		return stateWordWrap, false
-	}
-	// Handle stateWordWrap, which is more complicated.
-
-	// Switch to the verbatim state when we see a space right after an EOL.
-	if runeKind(w.prevRune) == kindEOL && kind == kindSpace {
-		return stateVerbatim, true
-	}
-	// Break on EOL or space when the line is too wide.  See above table.
-	if w.width >= 0 && w.width <= w.lineBuf.RuneLen()+1 {
-		switch kind {
-		case kindEOL:
-			return stateWordWrap, true
-		case kindSpace:
-			return stateSkipSpace, true
-		}
-		// case kindLetter falls through
-	}
-	// Handle the newWordStart case in the above table.
-	if w.width >= 0 && w.width < w.lineBuf.RuneLen()+1 && w.newWordStart != w.lineStart {
-		return stateWordWrap, true
-	}
-	// Stay in the wordWrap state and don't break the line.
-	return stateWordWrap, false
-}
-
-func (w *LineWriter) writeLine() error {
-	if w.lastWordEnd == -1 {
-		// Don't write blank lines, but we must reset the line in case the paragraph
-		// has just been terminated.
-		w.resetLine()
-		return nil
-	}
-	// Write the line (without trailing spaces) followed by the line terminator.
-	line := w.lineBuf.Bytes()[:w.lastWordEnd]
-	if _, err := w.w.Write(line); err != nil {
-		return err
-	}
-	if _, err := w.w.Write(w.lineTerm); err != nil {
-		return err
-	}
-	// Reset the line buffer.
-	w.wroteFirstLine = true
-	w.paragraphLineIndex++
-	if w.newWordStart != -1 {
-		// If we have an unterminated new word, we must be in the newWordStart case
-		// in the table above.  Handle the special buffer reset here.
-		newWord := string(w.lineBuf.Bytes()[w.newWordStart:])
-		w.resetLine()
-		w.newWordStart = w.lineBuf.ByteLen()
-		w.lineBuf.WriteString(newWord)
-	} else {
-		w.resetLine()
-	}
-	return nil
-}
-
-func (w *LineWriter) resetLine() {
-	w.lineBuf.Reset()
-	w.newWordStart = -1
-	w.lastWordEnd = -1
-	// Write the paragraph separator if the previous paragraph has terminated.
-	// This consumes no runes from the line width.
-	if w.wroteFirstLine && w.terminateParagraph {
-		w.lineBuf.WriteString0Runes(w.paragraphSep)
-		w.paragraphLineIndex = 0
-	}
-	// Add indent; a non-empty indent consumes runes from the line width.
-	var indent string
-	switch {
-	case w.paragraphLineIndex < len(w.indents):
-		indent = w.indents[w.paragraphLineIndex]
-	case len(w.indents) > 0:
-		indent = w.indents[len(w.indents)-1]
-	}
-	w.lineBuf.WriteString(indent)
-	w.lineStart = w.lineBuf.ByteLen()
-}
-
-func (w *LineWriter) bufferRune(r rune, state state, lineBreak bool) {
-	// Never add leading spaces to the buffer in the wordWrap state.
-	wordWrapNoLeadingSpaces := state == stateWordWrap && !lineBreak
-	switch kind := runeKind(r); kind {
-	case kindEOL:
-		// When we're word-wrapping and we see a letter followed by EOL, we convert
-		// the EOL into a single space in the buffer, to break the previous word
-		// from the next word.
-		if wordWrapNoLeadingSpaces && runeKind(w.prevRune) == kindLetter {
-			w.lineBuf.WriteRune(' ')
-		}
-	case kindSpace:
-		if wordWrapNoLeadingSpaces || state == stateVerbatim {
-			w.lineBuf.WriteRune(r)
-		}
-	case kindLetter:
-		w.lineBuf.WriteRune(r)
-	default:
-		panic(fmt.Errorf("textutil: bufferRune unhandled kind %d", kind))
-	}
-}
diff --git a/lib/textutil/line_writer_test.go b/lib/textutil/line_writer_test.go
deleted file mode 100644
index 29db23f..0000000
--- a/lib/textutil/line_writer_test.go
+++ /dev/null
@@ -1,268 +0,0 @@
-package textutil
-
-import (
-	"bytes"
-	"io"
-	"strings"
-	"testing"
-)
-
-type lp struct {
-	line, para string
-}
-
-var (
-	allIndents  = [][]int{nil, {}, {1}, {2}, {1, 2}, {2, 1}}
-	allIndents1 = [][]int{{1}, {2}, {1, 2}, {2, 1}}
-)
-
-func TestLineWriter(t *testing.T) {
-	tests := []struct {
-		Width   int
-		Indents [][]int
-		In      string // See xlateIn for details on the format
-		Want    string // See xlateWant for details on the format
-	}{
-		// Completely blank input yields empty output.
-		{4, allIndents, "", ""},
-		{4, allIndents, " ", ""},
-		{4, allIndents, "  ", ""},
-		{4, allIndents, "   ", ""},
-		{4, allIndents, "    ", ""},
-		{4, allIndents, "     ", ""},
-		{4, allIndents, "      ", ""},
-		{4, allIndents, "F N  R   V    L     P      ", ""},
-		// Single words never get word-wrapped, even if they're long.
-		{4, allIndents, "a", "0a."},
-		{4, allIndents, "ab", "0ab."},
-		{4, allIndents, "abc", "0abc."},
-		{4, allIndents, "abcd", "0abcd."},
-		{4, allIndents, "abcde", "0abcde."},
-		{4, allIndents, "abcdef", "0abcdef."},
-		// Word-wrapping boundary conditions.
-		{4, allIndents, "abc ", "0abc."},
-		{4, allIndents, "abc  ", "0abc."},
-		{4, allIndents, "abcN", "0abc."},
-		{4, allIndents, "abcN ", "0abc."},
-		{4, allIndents, "abcd ", "0abcd."},
-		{4, allIndents, "abcd  ", "0abcd."},
-		{4, allIndents, "abcdN", "0abcd."},
-		{4, allIndents, "abcdN ", "0abcd."},
-		{4, [][]int{nil}, "a cd", "0a cd."},
-		{4, [][]int{nil}, "a cd ", "0a cd."},
-		{4, [][]int{nil}, "a cdN", "0a cd."},
-		{4, allIndents1, "a cd", "0a.1cd."},
-		{4, allIndents1, "a cd ", "0a.1cd."},
-		{4, allIndents1, "a cdN", "0a.1cd."},
-		{4, allIndents, "a cde", "0a.1cde."},
-		{4, allIndents, "a cde ", "0a.1cde."},
-		{4, allIndents, "a cdeN", "0a.1cde."},
-		{4, [][]int{nil}, "a  d", "0a  d."},
-		{4, [][]int{nil}, "a  d ", "0a  d."},
-		{4, [][]int{nil}, "a  dN", "0a  d."},
-		{4, allIndents1, "a  d", "0a.1d."},
-		{4, allIndents1, "a  d ", "0a.1d."},
-		{4, allIndents1, "a  dN", "0a.1d."},
-		{4, allIndents, "a  de", "0a.1de."},
-		{4, allIndents, "a  de ", "0a.1de."},
-		{4, allIndents, "a  deN", "0a.1de."},
-		// Multi-line word-wrapping boundary conditions.
-		{4, allIndents, "abc e", "0abc.1e."},
-		{4, allIndents, "abc.e", "0abc.1e."},
-		{4, allIndents, "abc efgh", "0abc.1efgh."},
-		{4, allIndents, "abc.efgh", "0abc.1efgh."},
-		{4, allIndents, "abc efghi", "0abc.1efghi."},
-		{4, allIndents, "abc.efghi", "0abc.1efghi."},
-		{4, [][]int{nil}, "abc e gh", "0abc.1e gh."},
-		{4, [][]int{nil}, "abc.e.gh", "0abc.1e gh."},
-		{4, allIndents1, "abc e gh", "0abc.1e.2gh."},
-		{4, allIndents1, "abc.e.gh", "0abc.1e.2gh."},
-		{4, allIndents, "abc e ghijk", "0abc.1e.2ghijk."},
-		{4, allIndents, "abc.e.ghijk", "0abc.1e.2ghijk."},
-		// Verbatim lines.
-		{4, allIndents, " b", "0 b."},
-		{4, allIndents, "  bc", "0  bc."},
-		{4, allIndents, "   bcd", "0   bcd."},
-		{4, allIndents, "    bcde", "0    bcde."},
-		{4, allIndents, "     bcdef", "0     bcdef."},
-		{4, allIndents, "      bcdefg", "0      bcdefg."},
-		{4, allIndents, " b de ghijk", "0 b de ghijk."},
-		// Verbatim lines before word-wrapped lines.
-		{4, allIndents, " b.vw yz", "0 b.1vw.2yz."},
-		{4, allIndents, "  bc.vw yz", "0  bc.1vw.2yz."},
-		{4, allIndents, "   bcd.vw yz", "0   bcd.1vw.2yz."},
-		{4, allIndents, "    bcde.vw yz", "0    bcde.1vw.2yz."},
-		{4, allIndents, "     bcdef.vw yz", "0     bcdef.1vw.2yz."},
-		{4, allIndents, "      bcdefg.vw yz", "0      bcdefg.1vw.2yz."},
-		{4, allIndents, " b de ghijk.vw yz", "0 b de ghijk.1vw.2yz."},
-		// Verbatim lines after word-wrapped lines.
-		{4, allIndents, "vw yz. b", "0vw.1yz.2 b."},
-		{4, allIndents, "vw yz.  bc", "0vw.1yz.2  bc."},
-		{4, allIndents, "vw yz.   bcd", "0vw.1yz.2   bcd."},
-		{4, allIndents, "vw yz.    bcde", "0vw.1yz.2    bcde."},
-		{4, allIndents, "vw yz.     bcdef", "0vw.1yz.2     bcdef."},
-		{4, allIndents, "vw yz.      bcdefg", "0vw.1yz.2      bcdefg."},
-		{4, allIndents, "vw yz. b de ghijk", "0vw.1yz.2 b de ghijk."},
-		// Verbatim lines between word-wrapped lines.
-		{4, allIndents, "vw yz. b.mn pq", "0vw.1yz.2 b.2mn.2pq."},
-		{4, allIndents, "vw yz.  bc.mn pq", "0vw.1yz.2  bc.2mn.2pq."},
-		{4, allIndents, "vw yz.   bcd.mn pq", "0vw.1yz.2   bcd.2mn.2pq."},
-		{4, allIndents, "vw yz.    bcde.mn pq", "0vw.1yz.2    bcde.2mn.2pq."},
-		{4, allIndents, "vw yz.     bcdef.mn pq", "0vw.1yz.2     bcdef.2mn.2pq."},
-		{4, allIndents, "vw yz.      bcdefg.mn pq", "0vw.1yz.2      bcdefg.2mn.2pq."},
-		{4, allIndents, "vw yz. b de ghijk.mn pq", "0vw.1yz.2 b de ghijk.2mn.2pq."},
-		// Multi-paragraphs via explicit U+2029, and multi-newline.
-		{4, allIndents, "ab de ghPij lm op", "0ab.1de.2gh.:0ij.1lm.2op."},
-		{4, allIndents, "ab.de.ghPij.lm.op", "0ab.1de.2gh.:0ij.1lm.2op."},
-		{4, allIndents, "ab de gh Pij lm op", "0ab.1de.2gh.:0ij.1lm.2op."},
-		{4, allIndents, "ab.de.gh Pij.lm.op", "0ab.1de.2gh.:0ij.1lm.2op."},
-		{4, allIndents, "ab de ghNNij lm op", "0ab.1de.2gh.:0ij.1lm.2op."},
-		{4, allIndents, "ab.de.ghNNij.lm.op", "0ab.1de.2gh.:0ij.1lm.2op."},
-		{4, allIndents, "ab de ghNNNij lm op", "0ab.1de.2gh.:0ij.1lm.2op."},
-		{4, allIndents, "ab.de.ghNNNij.lm.op", "0ab.1de.2gh.:0ij.1lm.2op."},
-		{4, allIndents, "ab de gh N Nij lm op", "0ab.1de.2gh.:0ij.1lm.2op."},
-		{4, allIndents, "ab.de.gh N Nij.lm.op", "0ab.1de.2gh.:0ij.1lm.2op."},
-		{4, allIndents, "ab de gh N N Nij lm op", "0ab.1de.2gh.:0ij.1lm.2op."},
-		{4, allIndents, "ab.de.gh N N Nij.lm.op", "0ab.1de.2gh.:0ij.1lm.2op."},
-		// Special-case /r/n is a single EOL, but may be combined.
-		{4, allIndents, "ab de ghRNij lm op", "0ab.1de.2gh.2ij.2lm.2op."},
-		{4, allIndents, "ab.de.ghRNij.lm.op", "0ab.1de.2gh.2ij.2lm.2op."},
-		{4, allIndents, "ab de gh RNij lm op", "0ab.1de.2gh.2ij.2lm.2op."},
-		{4, allIndents, "ab.de.gh RNij.lm.op", "0ab.1de.2gh.2ij.2lm.2op."},
-		{4, allIndents, "ab de ghRNRNij lm op", "0ab.1de.2gh.:0ij.1lm.2op."},
-		{4, allIndents, "ab.de.ghRNRNij.lm.op", "0ab.1de.2gh.:0ij.1lm.2op."},
-		{4, allIndents, "ab de gh RN RNij lm op", "0ab.1de.2gh.:0ij.1lm.2op."},
-		{4, allIndents, "ab.de.gh RN RNij.lm.op", "0ab.1de.2gh.:0ij.1lm.2op."},
-		{4, allIndents, "ab de ghR Nij lm op", "0ab.1de.2gh.:0ij.1lm.2op."},
-		{4, allIndents, "ab.de.ghR Nij.lm.op", "0ab.1de.2gh.:0ij.1lm.2op."},
-		// Line separator via explicit U+2028 ends lines, but not paragraphs.
-		{4, allIndents, "aLcd", "0a.1cd."},
-		{4, allIndents, "a Lcd", "0a.1cd."},
-		{4, allIndents, "aLLcd", "0a.1cd."},
-		{4, allIndents, "a LLcd", "0a.1cd."},
-		// 0 width ends up with one word per line, except verbatim lines.
-		{0, allIndents, "a c e", "0a.1c.2e."},
-		{0, allIndents, "a cd fghij", "0a.1cd.2fghij."},
-		{0, allIndents, "a. cd fghij.l n", "0a.1 cd fghij.2l.2n."},
-		// -1 width ends up with all words on same line, except verbatim lines.
-		{-1, allIndents, "a c e", "0a c e."},
-		{-1, allIndents, "a cd fghij", "0a cd fghij."},
-		{-1, allIndents, "a. cd fghij.l n", "0a.1 cd fghij.2l n."},
-	}
-	for _, test := range tests {
-		// Run with a variety of chunk sizes.
-		for _, sizes := range [][]int{nil, {1}, {2}, {1, 2}, {2, 1}} {
-			// Run with a variety of line terminators and paragraph separators.
-			for _, lp := range []lp{{}, {"\n", "\n"}, {"L", "P"}, {"LLL", "PPP"}} {
-				// Run with a variety of indents.
-				if len(test.Indents) == 0 {
-					t.Errorf("%d %q %q has no indents, use [][]int{nil} rather than nil", test.Width, test.In, test.Want)
-				}
-				for _, indents := range test.Indents {
-					var buf bytes.Buffer
-					w := newUTF8LineWriter(t, &buf, test.Width, lp, indents)
-					lineWriterWriteFlush(t, w, xlateIn(test.In), sizes)
-					if got, want := buf.String(), xlateWant(test.Want, lp, indents); got != want {
-						t.Errorf("%q sizes:%v lp:%q indents:%v got %q, want %q", test.In, sizes, lp, indents, got, want)
-					}
-				}
-			}
-		}
-	}
-}
-
-// xlateIn translates our test.In pattern into an actual input string to feed
-// into the writer.  The point is to make it easy to specify the various control
-// sequences in a single character, so it's easier to understand.
-func xlateIn(text string) string {
-	text = strings.Replace(text, "F", "\f", -1)
-	text = strings.Replace(text, "N", "\n", -1)
-	text = strings.Replace(text, ".", "\n", -1) // Also allow . for easier reading
-	text = strings.Replace(text, "R", "\r", -1)
-	text = strings.Replace(text, "V", "\v", -1)
-	text = strings.Replace(text, "L", "\u2028", -1)
-	text = strings.Replace(text, "P", "\u2029", -1)
-	return text
-}
-
-// xlateWant translates our test.Want pattern into an actual expected string to
-// compare against the output.  The point is to make it easy to read and write
-// the expected patterns, and to make it easy to test various indents.
-func xlateWant(text string, lp lp, indents []int) string {
-	// Dot "." and colon ":" in the want string indicate line terminators and
-	// paragraph separators, respectively.
-	line := lp.line
-	if line == "" {
-		line = "\n"
-	}
-	text = strings.Replace(text, ".", line, -1)
-	para := lp.para
-	if para == "" {
-		para = "\n"
-	}
-	text = strings.Replace(text, ":", para, -1)
-	// The numbers in the want string indicate paragraph line numbers, to make it
-	// easier to automatically replace for various indent configurations.
-	switch len(indents) {
-	case 0:
-		text = strings.Replace(text, "0", "", -1)
-		text = strings.Replace(text, "1", "", -1)
-		text = strings.Replace(text, "2", "", -1)
-	case 1:
-		text = strings.Replace(text, "0", spaces(indents[0]), -1)
-		text = strings.Replace(text, "1", spaces(indents[0]), -1)
-		text = strings.Replace(text, "2", spaces(indents[0]), -1)
-	case 2:
-		text = strings.Replace(text, "0", spaces(indents[0]), -1)
-		text = strings.Replace(text, "1", spaces(indents[1]), -1)
-		text = strings.Replace(text, "2", spaces(indents[1]), -1)
-	case 3:
-		text = strings.Replace(text, "0", spaces(indents[0]), -1)
-		text = strings.Replace(text, "1", spaces(indents[1]), -1)
-		text = strings.Replace(text, "2", spaces(indents[2]), -1)
-	}
-	return text
-}
-
-func spaces(count int) string {
-	return strings.Repeat(" ", count)
-}
-
-func newUTF8LineWriter(t *testing.T, buf io.Writer, width int, lp lp, indents []int) *LineWriter {
-	w := NewUTF8LineWriter(buf, width)
-	if lp.line != "" || lp.para != "" {
-		if err := w.SetLineTerminator(lp.line); err != nil {
-			t.Errorf("SetLineTerminator(%q) got %v, want nil", lp.line, err)
-		}
-		if err := w.SetParagraphSeparator(lp.para); err != nil {
-			t.Errorf("SetParagraphSeparator(%q) got %v, want nil", lp.para, err)
-		}
-	}
-	if indents != nil {
-		indentStrs := make([]string, len(indents))
-		for ix, indent := range indents {
-			indentStrs[ix] = spaces(indent)
-		}
-		if err := w.SetIndents(indentStrs...); err != nil {
-			t.Errorf("SetIndents(%v) got %v, want nil", indentStrs, err)
-		}
-	}
-	return w
-}
-
-func lineWriterWriteFlush(t *testing.T, w *LineWriter, text string, sizes []int) {
-	// Write chunks of different sizes until we've exhausted the input.
-	remain := text
-	for ix := 0; len(remain) > 0; ix++ {
-		var chunk []byte
-		chunk, remain = nextChunk(remain, sizes, ix)
-		got, err := w.Write(chunk)
-		if want := len(chunk); got != want || err != nil {
-			t.Errorf("%q Write(%q) got (%d,%v), want (%d,nil)", text, chunk, got, err, want)
-		}
-	}
-	// Flush the writer.
-	if err := w.Flush(); err != nil {
-		t.Errorf("%q Flush() got %v, want nil", text, err)
-	}
-}
diff --git a/lib/textutil/rune.go b/lib/textutil/rune.go
deleted file mode 100644
index 57db02e..0000000
--- a/lib/textutil/rune.go
+++ /dev/null
@@ -1,119 +0,0 @@
-package textutil
-
-import (
-	"bytes"
-)
-
-// TODO(toddw): Add UTF16 support.
-
-const (
-	EOF                = rune(-1) // Indicates the end of a rune stream.
-	LineSeparator      = '\u2028' // Unicode line separator rune.
-	ParagraphSeparator = '\u2029' // Unicode paragraph separator rune.
-)
-
-// RuneEncoder is the interface to an encoder of a stream of runes into
-// bytes.Buffer.
-type RuneEncoder interface {
-	// Encode encodes r into buf.
-	Encode(r rune, buf *bytes.Buffer)
-}
-
-// RuneStreamDecoder is the interface to a decoder of a contiguous stream of
-// runes.
-type RuneStreamDecoder interface {
-	// Next returns the next rune.  Invalid encodings are returned as U+FFFD.
-	// Returns EOF at the end of the stream.
-	Next() rune
-	// BytePos returns the current byte position in the original data buffer.
-	BytePos() int
-}
-
-// RuneChunkDecoder is the interface to a decoder of a stream of encoded runes
-// that may be arbitrarily chunked.
-//
-// Implementations of RuneChunkDecoder are commonly used to implement io.Writer
-// wrappers, to handle buffering when chunk boundaries may occur in the middle
-// of an encoded rune.
-type RuneChunkDecoder interface {
-	// Decode returns a RuneStreamDecoder that decodes the data chunk.  Call Next
-	// repeatedly on the returned stream until it returns EOF to decode the chunk.
-	Decode(chunk []byte) RuneStreamDecoder
-	// DecodeLeftover returns a RuneStreamDecoder that decodes leftover buffered
-	// data.  Call Next repeatedly on the returned stream until it returns EOF to
-	// ensure all buffered data is processed.
-	DecodeLeftover() RuneStreamDecoder
-}
-
-// RuneChunkWrite is a helper that calls d.Decode(data) and repeatedly calls
-// Next in a loop, calling fn for every rune that is decoded.  Returns the
-// number of bytes in data that were successfully processed.  If fn returns an
-// error, Write will return with that error, without processing any more data.
-//
-// This is a convenience for implementing io.Writer, given a RuneChunkDecoder.
-func RuneChunkWrite(d RuneChunkDecoder, fn func(rune) error, data []byte) (int, error) {
-	stream := d.Decode(data)
-	for r := stream.Next(); r != EOF; r = stream.Next() {
-		if err := fn(r); err != nil {
-			return stream.BytePos(), err
-		}
-	}
-	return stream.BytePos(), nil
-}
-
-// RuneChunkFlush is a helper that calls d.DecodeLeftover and repeatedly calls
-// Next in a loop, calling fn for every rune that is decoded.  If fn returns an
-// error, Flush will return with that error, without processing any more data.
-//
-// This is a convenience for implementing an additional Flush() call on an
-// implementation of io.Writer, given a RuneChunkDecoder.
-func RuneChunkFlush(d RuneChunkDecoder, fn func(rune) error) error {
-	stream := d.DecodeLeftover()
-	for r := stream.Next(); r != EOF; r = stream.Next() {
-		if err := fn(r); err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-// bytePos and runePos distinguish positions that are used in either domain;
-// we're trying to avoid silly mistakes like adding a bytePos to a runePos.
-type bytePos int
-type runePos int
-
-// byteRuneBuffer maintains a buffer with both byte and rune based positions.
-type byteRuneBuffer struct {
-	enc     RuneEncoder
-	buf     bytes.Buffer
-	runeLen runePos
-}
-
-func (b *byteRuneBuffer) ByteLen() bytePos { return bytePos(b.buf.Len()) }
-func (b *byteRuneBuffer) RuneLen() runePos { return b.runeLen }
-func (b *byteRuneBuffer) Bytes() []byte    { return b.buf.Bytes() }
-
-func (b *byteRuneBuffer) Reset() {
-	b.buf.Reset()
-	b.runeLen = 0
-}
-
-// WriteRune writes r into b.
-func (b *byteRuneBuffer) WriteRune(r rune) {
-	b.enc.Encode(r, &b.buf)
-	b.runeLen++
-}
-
-// WriteString writes str into b.
-func (b *byteRuneBuffer) WriteString(str string) {
-	for _, r := range str {
-		b.WriteRune(r)
-	}
-}
-
-// WriteString0Runes writes str into b, not incrementing the rune length.
-func (b *byteRuneBuffer) WriteString0Runes(str string) {
-	for _, r := range str {
-		b.enc.Encode(r, &b.buf)
-	}
-}
diff --git a/lib/textutil/utf8.go b/lib/textutil/utf8.go
deleted file mode 100644
index 349033e..0000000
--- a/lib/textutil/utf8.go
+++ /dev/null
@@ -1,167 +0,0 @@
-package textutil
-
-import (
-	"bytes"
-	"fmt"
-	"unicode/utf8"
-)
-
-// UTF8Encoder implements RuneEncoder for the UTF-8 encoding.
-type UTF8Encoder struct{}
-
-var _ RuneEncoder = UTF8Encoder{}
-
-// Encode encodes r into buf in the UTF-8 encoding.
-func (UTF8Encoder) Encode(r rune, buf *bytes.Buffer) { buf.WriteRune(r) }
-
-// UTF8ChunkDecoder implements RuneChunkDecoder for a stream of UTF-8 data that
-// is arbitrarily chunked.
-//
-// UTF-8 is a byte-wise encoding that may use multiple bytes to encode a single
-// rune.  This decoder buffers partial runes that have been split across chunks,
-// so that a full rune is returned when the subsequent data chunk is provided.
-//
-// This is commonly used to implement an io.Writer wrapper over UTF-8 text.  It
-// is useful since the data provided to Write calls may be arbitrarily chunked.
-//
-// The zero UTF8ChunkDecoder is a decoder with an empty buffer.
-type UTF8ChunkDecoder struct {
-	// The only state we keep is the last partial rune we've encountered.
-	partial    [utf8.UTFMax]byte
-	partialLen int
-}
-
-var _ RuneChunkDecoder = (*UTF8ChunkDecoder)(nil)
-
-// Decode returns a RuneStreamDecoder that decodes the data chunk.  Call Next
-// repeatedly on the returned stream until it returns EOF to decode the chunk.
-//
-// If the data is chunked in the middle of an encoded rune, the final partial
-// rune in the chunk will be buffered, and the next call to Decode will continue
-// by combining the buffered data with the next chunk.
-//
-// Invalid encodings are transformed into U+FFFD, one byte at a time.  See
-// unicode/utf8.DecodeRune for details.
-func (d *UTF8ChunkDecoder) Decode(chunk []byte) RuneStreamDecoder {
-	return &utf8Stream{d, chunk, 0}
-}
-
-// DecodeLeftover returns a RuneStreamDecoder that decodes leftover buffered
-// data.  Call Next repeatedly on the returned stream until it returns EOF to
-// ensure all buffered data is processed.
-//
-// Since the only data that is buffered is the final partial rune, the returned
-// RuneStreamDecoder will only contain U+FFFD or EOF.
-func (d *UTF8ChunkDecoder) DecodeLeftover() RuneStreamDecoder {
-	return &utf8LeftoverStream{d, 0}
-}
-
-// nextRune decodes the next rune, logically combining any previously buffered
-// data with the data chunk.  It returns the decoded rune and the byte size of
-// the data that was used for the decoding.
-//
-// The returned size may be > 0 even if the returned rune == EOF, if a partial
-// rune was detected and buffered.  The returned size may be 0 even if the
-// returned rune != EOF, if previously buffered data was decoded.
-func (d *UTF8ChunkDecoder) nextRune(data []byte) (rune, int) {
-	if d.partialLen > 0 {
-		return d.nextRunePartial(data)
-	}
-	r, size := utf8.DecodeRune(data)
-	if r == utf8.RuneError && !utf8.FullRune(data) {
-		// Initialize the partial rune buffer with remaining data.
-		d.partialLen = copy(d.partial[:], data)
-		return d.verifyPartial(d.partialLen, data)
-	}
-	return r, size
-}
-
-// nextRunePartial implements nextRune when there is a previously buffered
-// partial rune.
-func (d *UTF8ChunkDecoder) nextRunePartial(data []byte) (rune, int) {
-	// Append as much data as we can to the partial rune, and see if it's full.
-	oldLen := d.partialLen
-	d.partialLen += copy(d.partial[oldLen:], data)
-	if !utf8.FullRune(d.partial[:d.partialLen]) {
-		// We still don't have a full rune - keep waiting.
-		return d.verifyPartial(d.partialLen-oldLen, data)
-	}
-	// We finally have a full rune.
-	r, size := utf8.DecodeRune(d.partial[:d.partialLen])
-	if size < oldLen {
-		// This occurs when we have a multi-byte rune that has the right number of
-		// bytes, but is an invalid code point.
-		//
-		// Say oldLen=2, and we just received the third byte of a 3-byte rune which
-		// isn't a UTF-8 trailing byte.  In this case utf8.DecodeRune returns U+FFFD
-		// and size=1, to indicate we should skip the first byte.
-		//
-		// We shift the unread portion of the old partial data forward, and update
-		// the partial len so that it's strictly decreasing.  The strictly
-		// decreasing property isn't necessary for correctness, but helps avoid
-		// repeatedly copying data into the partial buffer unecessarily.
-		copy(d.partial[:], d.partial[size:oldLen])
-		d.partialLen = oldLen - size
-		return r, 0
-	}
-	// We've used all the old buffered data; start decoding directly from data.
-	d.partialLen = 0
-	return r, size - oldLen
-}
-
-// verifyPartial is called when we don't have a full rune, and ncopy bytes have
-// been copied from data into the decoder partial rune buffer.  We expect that
-// all data has been buffered and we return EOF and the total size of the data.
-func (d *UTF8ChunkDecoder) verifyPartial(ncopy int, data []byte) (rune, int) {
-	if ncopy < len(data) {
-		// Something's very wrong if we managed to fill d.partial without copying
-		// all the data; any sequence of utf8.UTFMax bytes must be a full rune.
-		panic(fmt.Errorf("UTF8ChunkDecoder: partial rune %v with leftover data %v", d.partial[:d.partialLen], data[ncopy:]))
-	}
-	return EOF, len(data)
-}
-
-// utf8Stream implements UTF8ChunkDecoder.Decode.
-type utf8Stream struct {
-	d    *UTF8ChunkDecoder
-	data []byte
-	pos  int
-}
-
-var _ RuneStreamDecoder = (*utf8Stream)(nil)
-
-func (s *utf8Stream) Next() rune {
-	if s.pos == len(s.data) {
-		return EOF
-	}
-	r, size := s.d.nextRune(s.data[s.pos:])
-	s.pos += size
-	return r
-}
-
-func (s *utf8Stream) BytePos() int {
-	return s.pos
-}
-
-// utf8LeftoverStream implements UTF8ChunkDecoder.DecodeLeftover.
-type utf8LeftoverStream struct {
-	d   *UTF8ChunkDecoder
-	pos int
-}
-
-var _ RuneStreamDecoder = (*utf8LeftoverStream)(nil)
-
-func (s *utf8LeftoverStream) Next() rune {
-	if s.d.partialLen == 0 {
-		return EOF
-	}
-	r, size := utf8.DecodeRune(s.d.partial[:s.d.partialLen])
-	copy(s.d.partial[:], s.d.partial[size:])
-	s.d.partialLen -= size
-	s.pos += size
-	return r
-}
-
-func (s *utf8LeftoverStream) BytePos() int {
-	return s.pos
-}
diff --git a/lib/textutil/utf8_test.go b/lib/textutil/utf8_test.go
deleted file mode 100644
index 7517bef..0000000
--- a/lib/textutil/utf8_test.go
+++ /dev/null
@@ -1,110 +0,0 @@
-package textutil
-
-import (
-	"reflect"
-	"testing"
-)
-
-func TestUTF8ChunkDecoder(t *testing.T) {
-	r2 := "Δ"
-	r3 := "王"
-	r4 := "\U0001F680"
-	tests := []struct {
-		Text string
-		Want []rune
-	}{
-		{"", nil},
-		{"a", []rune{'a'}},
-		{"abc", []rune{'a', 'b', 'c'}},
-		{"abc def ghi", []rune{'a', 'b', 'c', ' ', 'd', 'e', 'f', ' ', 'g', 'h', 'i'}},
-		// 2-byte runes.
-		{"ΔΘΠΣΦ", []rune{'Δ', 'Θ', 'Π', 'Σ', 'Φ'}},
-		// 3-byte runes.
-		{"王普澤世界", []rune{'王', '普', '澤', '世', '界'}},
-		// 4-byte runes.
-		{"\U0001F680\U0001F681\U0001F682\U0001F683", []rune{'\U0001F680', '\U0001F681', '\U0001F682', '\U0001F683'}},
-		// Mixed-bytes.
-		{"aΔ王\U0001F680普Θb", []rune{'a', 'Δ', '王', '\U0001F680', '普', 'Θ', 'b'}},
-		// Error runes translated to U+FFFD.
-		{"\uFFFD", []rune{'\uFFFD'}},
-		{"a\uFFFDb", []rune{'a', '\uFFFD', 'b'}},
-		{"\xFF", []rune{'\uFFFD'}},
-		{"a\xFFb", []rune{'a', '\uFFFD', 'b'}},
-		// Multi-byte full runes.
-		{r2, []rune{[]rune(r2)[0]}},
-		{r3, []rune{[]rune(r3)[0]}},
-		{r4, []rune{[]rune(r4)[0]}},
-		// Partial runes translated to U+FFFD.
-		{r2[:1], []rune{'\uFFFD'}},
-		{r3[:1], []rune{'\uFFFD'}},
-		{r3[:2], []rune{'\uFFFD', '\uFFFD'}},
-		{r4[:1], []rune{'\uFFFD'}},
-		{r4[:2], []rune{'\uFFFD', '\uFFFD'}},
-		{r4[:3], []rune{'\uFFFD', '\uFFFD', '\uFFFD'}},
-		// Leading partial runes translated to U+FFFD.
-		{r2[:1] + "b", []rune{'\uFFFD', 'b'}},
-		{r3[:1] + "b", []rune{'\uFFFD', 'b'}},
-		{r3[:2] + "b", []rune{'\uFFFD', '\uFFFD', 'b'}},
-		{r4[:1] + "b", []rune{'\uFFFD', 'b'}},
-		{r4[:2] + "b", []rune{'\uFFFD', '\uFFFD', 'b'}},
-		{r4[:3] + "b", []rune{'\uFFFD', '\uFFFD', '\uFFFD', 'b'}},
-		// Trailing partial runes translated to U+FFFD.
-		{"a" + r2[:1], []rune{'a', '\uFFFD'}},
-		{"a" + r3[:1], []rune{'a', '\uFFFD'}},
-		{"a" + r3[:2], []rune{'a', '\uFFFD', '\uFFFD'}},
-		{"a" + r4[:1], []rune{'a', '\uFFFD'}},
-		{"a" + r4[:2], []rune{'a', '\uFFFD', '\uFFFD'}},
-		{"a" + r4[:3], []rune{'a', '\uFFFD', '\uFFFD', '\uFFFD'}},
-		// Bracketed partial runes translated to U+FFFD.
-		{"a" + r2[:1] + "b", []rune{'a', '\uFFFD', 'b'}},
-		{"a" + r3[:1] + "b", []rune{'a', '\uFFFD', 'b'}},
-		{"a" + r3[:2] + "b", []rune{'a', '\uFFFD', '\uFFFD', 'b'}},
-		{"a" + r4[:1] + "b", []rune{'a', '\uFFFD', 'b'}},
-		{"a" + r4[:2] + "b", []rune{'a', '\uFFFD', '\uFFFD', 'b'}},
-		{"a" + r4[:3] + "b", []rune{'a', '\uFFFD', '\uFFFD', '\uFFFD', 'b'}},
-	}
-	for _, test := range tests {
-		// Run with a variety of chunk sizes.
-		for _, sizes := range [][]int{nil, {1}, {2}, {1, 2}, {2, 1}, {3}, {1, 2, 3}} {
-			got := runeChunkWriteFlush(t, test.Text, sizes)
-			if want := test.Want; !reflect.DeepEqual(got, want) {
-				t.Errorf("%q got %v, want %v", test.Text, got, want)
-			}
-		}
-	}
-}
-
-func runeChunkWriteFlush(t *testing.T, text string, sizes []int) []rune {
-	var dec UTF8ChunkDecoder
-	var runes []rune
-	addRune := func(r rune) error {
-		runes = append(runes, r)
-		return nil
-	}
-	// Write chunks of different sizes until we've exhausted the input text.
-	remain := text
-	for ix := 0; len(remain) > 0; ix++ {
-		var chunk []byte
-		chunk, remain = nextChunk(remain, sizes, ix)
-		got, err := RuneChunkWrite(&dec, addRune, chunk)
-		if want := len(chunk); got != want || err != nil {
-			t.Errorf("%q RuneChunkWrite(%q) got (%d,%v), want (%d,nil)", text, chunk, got, err, want)
-		}
-	}
-	// Flush the decoder.
-	if err := RuneChunkFlush(&dec, addRune); err != nil {
-		t.Errorf("%q RuneChunkFlush got %v, want nil", text, err)
-	}
-	return runes
-}
-
-func nextChunk(text string, sizes []int, index int) (chunk []byte, remain string) {
-	if len(sizes) == 0 {
-		return []byte(text), ""
-	}
-	size := sizes[index%len(sizes)]
-	if size >= len(text) {
-		return []byte(text), ""
-	}
-	return []byte(text[:size]), text[size:]
-}
diff --git a/lib/textutil/util.go b/lib/textutil/util.go
deleted file mode 100644
index 85402de..0000000
--- a/lib/textutil/util.go
+++ /dev/null
@@ -1,40 +0,0 @@
-package textutil
-
-import (
-	"syscall"
-	"unsafe"
-)
-
-// TerminalSize returns the dimensions of the terminal, if it's available from
-// the OS, otherwise returns an error.
-func TerminalSize() (row, col int, _ error) {
-	// Try getting the terminal size from stdout, stderr and stdin respectively.
-	// We try each of these in turn because the mechanism we're using fails if any
-	// of the fds is redirected on the command line.  E.g. "tool | less" redirects
-	// the stdout of tool to the stdin of less, and will mean tool cannot retrieve
-	// the terminal size from stdout.
-	//
-	// TODO(toddw): This probably only works on some linux / unix variants; add
-	// build tags and support different platforms.
-	if row, col, err := terminalSize(syscall.Stdout); err == nil {
-		return row, col, err
-	}
-	if row, col, err := terminalSize(syscall.Stderr); err == nil {
-		return row, col, err
-	}
-	return terminalSize(syscall.Stdin)
-}
-
-func terminalSize(fd int) (int, int, error) {
-	var ws winsize
-	if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&ws))); err != 0 {
-		return 0, 0, err
-	}
-	return int(ws.row), int(ws.col), nil
-}
-
-// winsize must correspond to the struct defined in "sys/ioctl.h".  Do not
-// export this struct; it's a platform-specific implementation detail.
-type winsize struct {
-	row, col, xpixel, ypixel uint16
-}
diff --git a/lib/unixfd/unixfd_test.go b/lib/unixfd/unixfd_test.go
index 152efa8..74a2c65 100644
--- a/lib/unixfd/unixfd_test.go
+++ b/lib/unixfd/unixfd_test.go
@@ -142,7 +142,7 @@
 	done := make(chan struct{})
 	buf := make([]byte, 10)
 	go func() {
-		saddr, n, err = ReadConnection(server, buf)
+		saddr, n, readErr = ReadConnection(server, buf)
 		close(done)
 	}()
 	caddr, err := SendConnection(uclient.(*net.UnixConn), []byte("hello"), true)
diff --git a/lib/websocket/conn.go b/lib/websocket/conn.go
new file mode 100644
index 0000000..e6963c4
--- /dev/null
+++ b/lib/websocket/conn.go
@@ -0,0 +1,124 @@
+// +build !nacl
+
+package websocket
+
+import (
+	"fmt"
+	"github.com/gorilla/websocket"
+	"io"
+	"net"
+	"sync"
+	"time"
+)
+
+// WebsocketConn provides a net.Conn interface for a websocket connection.
+func WebsocketConn(ws *websocket.Conn) net.Conn {
+	return &wrappedConn{ws: ws}
+}
+
+// wrappedConn provides a net.Conn interface to a websocket.
+// The underlying websocket connection needs regular calls to Read to make sure
+// websocket control messages (such as pings) are processed by the websocket
+// library.
+type wrappedConn struct {
+	ws         *websocket.Conn
+	currReader io.Reader
+
+	// The gorilla docs aren't explicit about reading and writing from
+	// different goroutines.  It is explicit that only one goroutine can
+	// do a write at any given time and only one goroutine can do a read
+	// at any given time.  Based on inspection it seems that using a reader
+	// and writer simultaneously is safe, but this might change with
+	// future changes.  We can't actually share the lock, because this means
+	// that we can't write while we are waiting for a message, causing some
+	// deadlocks where a write is need to unblock a read.
+	writeLock sync.Mutex
+	readLock  sync.Mutex
+}
+
+func (c *wrappedConn) readFromCurrReader(b []byte) (int, error) {
+	n, err := c.currReader.Read(b)
+	if err == io.EOF {
+		err = nil
+		c.currReader = nil
+	}
+	return n, err
+}
+
+func (c *wrappedConn) Read(b []byte) (int, error) {
+	c.readLock.Lock()
+	defer c.readLock.Unlock()
+	var n int
+	var err error
+
+	// TODO(bjornick): It would be nice to be able to read multiple messages at
+	// a time in case the first message is not big enough to fill b and another
+	// message is ready.
+	// Loop until we either get data or an error.  This exists
+	// mostly to avoid return 0, nil.
+	for n == 0 && err == nil {
+		if c.currReader == nil {
+			t, r, err := c.ws.NextReader()
+
+			if t != websocket.BinaryMessage {
+				return 0, fmt.Errorf("Unexpected message type %d", t)
+			}
+			if err != nil {
+				return 0, err
+			}
+			c.currReader = r
+		}
+		n, err = c.readFromCurrReader(b)
+	}
+	return n, err
+}
+
+func (c *wrappedConn) Write(b []byte) (int, error) {
+	c.writeLock.Lock()
+	defer c.writeLock.Unlock()
+	if err := c.ws.WriteMessage(websocket.BinaryMessage, b); err != nil {
+		return 0, err
+	}
+	return len(b), nil
+}
+
+func (c *wrappedConn) Close() error {
+	c.writeLock.Lock()
+	defer c.writeLock.Unlock()
+	return c.ws.Close()
+}
+
+func (c *wrappedConn) LocalAddr() net.Addr {
+	return websocketAddr{s: c.ws.LocalAddr().String()}
+}
+
+func (c *wrappedConn) RemoteAddr() net.Addr {
+	return websocketAddr{s: c.ws.RemoteAddr().String()}
+}
+
+func (c *wrappedConn) SetDeadline(t time.Time) error {
+	if err := c.SetReadDeadline(t); err != nil {
+		return err
+	}
+	return c.SetWriteDeadline(t)
+}
+
+func (c *wrappedConn) SetReadDeadline(t time.Time) error {
+	return c.ws.SetReadDeadline(t)
+}
+
+func (c *wrappedConn) SetWriteDeadline(t time.Time) error {
+	return c.ws.SetWriteDeadline(t)
+}
+
+type websocketAddr struct {
+	s string
+}
+
+func (websocketAddr) Network() string {
+	return "ws"
+}
+
+func (w websocketAddr) String() string {
+	return w.s
+}
diff --git a/lib/websocket/conn_nacl.go b/lib/websocket/conn_nacl.go
new file mode 100644
index 0000000..22d368d
--- /dev/null
+++ b/lib/websocket/conn_nacl.go
@@ -0,0 +1,105 @@
+// +build nacl
+
+package websocket
+
+import (
+	"net"
+	"net/url"
+	"runtime/ppapi"
+	"sync"
+	"time"
+)
+
+// Ppapi instance which must be set before the Dial is called.
+var PpapiInstance ppapi.Instance
+
+func WebsocketConn(address string, ws *ppapi.WebsocketConn) net.Conn {
+	return &wrappedConn{
+		address: address,
+		ws:      ws,
+	}
+}
+
+type wrappedConn struct {
+	address    string
+	ws         *ppapi.WebsocketConn
+	readLock   sync.Mutex
+	writeLock  sync.Mutex
+	currBuffer []byte
+}
+
+func Dial(address string) (net.Conn, error) {
+	inst := PpapiInstance
+	u, err := url.Parse("ws://" + address)
+	if err != nil {
+		return nil, err
+	}
+
+	ws, err := inst.DialWebsocket(u.String())
+	if err != nil {
+		return nil, err
+	}
+	return WebsocketConn(address, ws), nil
+}
+
+func (c *wrappedConn) Read(b []byte) (int, error) {
+	c.readLock.Lock()
+	defer c.readLock.Unlock()
+
+	var err error
+	if len(c.currBuffer) == 0 {
+		c.currBuffer, err = c.ws.ReceiveMessage()
+		if err != nil {
+			return 0, nil
+		}
+	}
+
+	n := copy(b, c.currBuffer)
+	c.currBuffer = c.currBuffer[n:]
+	return n, nil
+}
+
+func (c *wrappedConn) Write(b []byte) (int, error) {
+	c.writeLock.Lock()
+	defer c.writeLock.Unlock()
+	if err := c.ws.SendMessage(b); err != nil {
+		return 0, err
+	}
+	return len(b), nil
+}
+
+func (c *wrappedConn) Close() error {
+	return c.ws.Close()
+}
+
+func (c *wrappedConn) LocalAddr() net.Addr {
+	return websocketAddr{s: c.address}
+}
+
+func (c *wrappedConn) RemoteAddr() net.Addr {
+	return websocketAddr{s: c.address}
+}
+
+func (c *wrappedConn) SetDeadline(t time.Time) error {
+	panic("SetDeadline not implemented.")
+}
+
+func (c *wrappedConn) SetReadDeadline(t time.Time) error {
+	panic("SetReadDeadline not implemented.")
+}
+
+func (c *wrappedConn) SetWriteDeadline(t time.Time) error {
+	panic("SetWriteDeadline not implemented.")
+}
+
+type websocketAddr struct {
+	s string
+}
+
+func (websocketAddr) Network() string {
+	return "ws"
+}
+
+func (w websocketAddr) String() string {
+	return w.s
+}
diff --git a/lib/websocket/conn_test.go b/lib/websocket/conn_test.go
new file mode 100644
index 0000000..db38cbe
--- /dev/null
+++ b/lib/websocket/conn_test.go
@@ -0,0 +1,111 @@
+// +build !nacl
+package websocket
+
+import (
+	"bytes"
+	"github.com/gorilla/websocket"
+	"net"
+	"net/http"
+	"sync"
+	"testing"
+	"time"
+)
+
+func writer(c net.Conn, data []byte, times int, wg *sync.WaitGroup) {
+	defer wg.Done()
+	b := []byte{byte(len(data))}
+	b = append(b, data...)
+	for i := 0; i < times; i++ {
+		c.Write(b)
+	}
+}
+
+func readMessage(c net.Conn) ([]byte, error) {
+	var length [1]byte
+	// Read the size
+	for {
+		n, err := c.Read(length[:])
+		if err != nil {
+			return nil, err
+		}
+		if n == 1 {
+			break
+		}
+	}
+	size := int(length[0])
+	buf := make([]byte, size)
+	n := 0
+	for n < size {
+		nn, err := c.Read(buf[n:])
+		if err != nil {
+			return buf, err
+		}
+		n += nn
+	}
+
+	return buf, nil
+}
+
+func reader(t *testing.T, c net.Conn, expected []byte, totalWrites int) {
+	totalReads := 0
+	for buf, err := readMessage(c); err == nil; buf, err = readMessage(c) {
+		totalReads++
+		if !bytes.Equal(buf, expected) {
+			t.Errorf("Unexpected message %v, expected %v", buf, expected)
+		}
+	}
+	if totalReads != totalWrites {
+		t.Errorf("wrong number of messages expected %v, got %v", totalWrites, totalReads)
+	}
+}
+
+func TestMultipleGoRoutines(t *testing.T) {
+	l, err := net.Listen("tcp", "127.0.0.1:0")
+	if err != nil {
+		t.Fatalf("Failed to listen: %v", err)
+	}
+	addr := l.Addr()
+	input := []byte("no races here")
+	const numWriters int = 12
+	const numWritesPerWriter int = 1000
+	const totalWrites int = numWriters * numWritesPerWriter
+	s := &http.Server{
+		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+			if r.Method != "GET" {
+				http.Error(w, "Method not allowed.", http.StatusMethodNotAllowed)
+				return
+			}
+			ws, err := websocket.Upgrade(w, r, nil, 1024, 1024)
+			if _, ok := err.(websocket.HandshakeError); ok {
+				http.Error(w, "Not a websocket handshake", 400)
+				return
+			} else if err != nil {
+				http.Error(w, "Internal Error", 500)
+				return
+			}
+			reader(t, WebsocketConn(ws), input, totalWrites)
+		}),
+	}
+	// Dial out in another go routine
+	go func() {
+		conn, err := Dial(addr.String())
+		numTries := 0
+		for err != nil && numTries < 5 {
+			numTries++
+			time.Sleep(time.Second)
+		}
+
+		if err != nil {
+			t.Fatalf("failed to connect to server: %v", err)
+		}
+		var writers sync.WaitGroup
+		writers.Add(numWriters)
+		for i := 0; i < numWriters; i++ {
+			go writer(conn, input, numWritesPerWriter, &writers)
+		}
+		writers.Wait()
+		conn.Close()
+		l.Close()
+	}()
+	s.Serve(l)
+}
diff --git a/lib/websocket/dialer.go b/lib/websocket/dialer.go
new file mode 100644
index 0000000..dea1262
--- /dev/null
+++ b/lib/websocket/dialer.go
@@ -0,0 +1,28 @@
+// +build !nacl
+
+package websocket
+
+import (
+	"github.com/gorilla/websocket"
+	"net"
+	"net/http"
+	"net/url"
+)
+
+func Dial(address string) (net.Conn, error) {
+	conn, err := net.Dial("tcp", address)
+	if err != nil {
+		return nil, err
+	}
+	u, err := url.Parse("ws://" + address)
+
+	if err != nil {
+		return nil, err
+	}
+	ws, _, err := websocket.NewClient(conn, u, http.Header{}, 4096, 4096)
+	if err != nil {
+		return nil, err
+	}
+
+	return WebsocketConn(ws), nil
+}
diff --git a/runtimes/google/ipc/client.go b/runtimes/google/ipc/client.go
index 2b15b7c..2121314 100644
--- a/runtimes/google/ipc/client.go
+++ b/runtimes/google/ipc/client.go
@@ -106,8 +106,10 @@
 		delete(c.vcMap, ep.String())
 	}
 	sm := c.streamMgr
+	vcOpts := make([]stream.VCOpt, len(c.vcOpts))
+	copy(vcOpts, c.vcOpts)
 	c.vcMapMu.Unlock()
-	vc, err := sm.Dial(ep, c.vcOpts...)
+	vc, err := sm.Dial(ep, vcOpts...)
 	c.vcMapMu.Lock()
 	if err != nil {
 		return nil, err
@@ -291,13 +293,13 @@
 // tryCall makes a single attempt at a call, against possibly multiple servers.
 func (c *client) tryCall(ctx context.T, name, method string, args []interface{}, opts []ipc.CallOpt) (ipc.Call, verror.E) {
 	ctx, _ = vtrace.WithNewSpan(ctx, fmt.Sprintf("<client>\"%s\".%s", name, method))
-	mtPattern, serverPattern, name := splitObjectName(name)
+	_, serverPattern, name := splitObjectName(name)
 	// Resolve name unless told not to.
 	var servers []string
 	if getNoResolveOpt(opts) {
 		servers = []string{name}
 	} else {
-		if resolved, err := c.ns.Resolve(ctx, name, naming.RootBlessingPatternOpt(mtPattern)); err != nil {
+		if resolved, err := c.ns.Resolve(ctx, name); err != nil {
 			return nil, verror.NoExistf("ipc: Resolve(%q) failed: %v", name, err)
 		} else {
 			// An empty set of protocols means all protocols...
diff --git a/runtimes/google/ipc/client_test.go b/runtimes/google/ipc/client_test.go
index d5be886..c4e8b8a 100644
--- a/runtimes/google/ipc/client_test.go
+++ b/runtimes/google/ipc/client_test.go
@@ -26,8 +26,7 @@
 }
 
 func runMountTable(t *testing.T) (*modules.Shell, func()) {
-	sh := modules.NewShell(".*")
-	core.Install(sh)
+	sh := modules.NewShell()
 	root, err := sh.Start(core.RootMTCommand, nil, testArgs()...)
 	if err != nil {
 		t.Fatalf("unexpected error for root mt: %s", err)
@@ -101,8 +100,8 @@
 		}
 	}
 
-	// Verify that there are 101 entries for echoServer in the mount table.
-	if got, want := numServers(t, sh, "echoServer"), "101"; got != want {
+	// Verify that there are 102 entries for echoServer in the mount table.
+	if got, want := numServers(t, sh, "echoServer"), "102"; got != want {
 		vlog.Fatalf("got: %q, want: %q", got, want)
 	}
 
@@ -119,7 +118,7 @@
 	// TODO(cnicolaou,p): figure out why the real entry isn't removed
 	// from the mount table.
 	// Verify that there are 100 entries for echoServer in the mount table.
-	if got, want := numServers(t, sh, "echoServer"), "101"; got != want {
+	if got, want := numServers(t, sh, "echoServer"), "102"; got != want {
 		vlog.Fatalf("got: %q, want: %q", got, want)
 	}
 }
diff --git a/runtimes/google/ipc/flow_test.go b/runtimes/google/ipc/flow_test.go
index 4031966..703a44f 100644
--- a/runtimes/google/ipc/flow_test.go
+++ b/runtimes/google/ipc/flow_test.go
@@ -60,7 +60,7 @@
 	newInvoker func(suffix string) ipc.Invoker
 }
 
-func (td testDisp) Lookup(suffix, method string) (interface{}, security.Authorizer, error) {
+func (td testDisp) Lookup(suffix string) (interface{}, security.Authorizer, error) {
 	return td.newInvoker(suffix), testServerAuthorizer{}, nil
 }
 
diff --git a/runtimes/google/ipc/full_test.go b/runtimes/google/ipc/full_test.go
index 67e14b9..b9ab62a 100644
--- a/runtimes/google/ipc/full_test.go
+++ b/runtimes/google/ipc/full_test.go
@@ -26,6 +26,7 @@
 	"veyron.io/veyron/veyron/lib/netstate"
 	"veyron.io/veyron/veyron/lib/testutil"
 	tsecurity "veyron.io/veyron/veyron/lib/testutil/security"
+	"veyron.io/veyron/veyron/lib/websocket"
 	imanager "veyron.io/veyron/veyron/runtimes/google/ipc/stream/manager"
 	"veyron.io/veyron/veyron/runtimes/google/ipc/stream/vc"
 	"veyron.io/veyron/veyron/runtimes/google/ipc/version"
@@ -35,6 +36,10 @@
 	vsecurity "veyron.io/veyron/veyron/security"
 )
 
+func init() {
+	stream.RegisterProtocol("ws", websocket.Dial, nil)
+}
+
 var (
 	errMethod  = verror.Abortedf("server returned an error")
 	clock      = new(fakeClock)
@@ -134,7 +139,7 @@
 
 type testServerDisp struct{ server interface{} }
 
-func (t testServerDisp) Lookup(suffix, method string) (interface{}, security.Authorizer, error) {
+func (t testServerDisp) Lookup(suffix string) (interface{}, security.Authorizer, error) {
 	// If suffix is "nilAuth" we use default authorization, if it is "aclAuth" we
 	// use an ACL based authorizer, and otherwise we use the custom testServerAuthorizer.
 	var authorizer security.Authorizer
@@ -237,6 +242,20 @@
 	vlog.VI(1).Info("server.Stop DONE")
 }
 
+func resolveWSEndpoint(ns naming.Namespace, name string) (string, error) {
+	// Find the ws endpoint and use that.
+	servers, err := ns.Resolve(testContext(), name)
+	if err != nil {
+		return "", err
+	}
+	for _, s := range servers {
+		if strings.Index(s, "@ws@") != -1 {
+			return s, nil
+		}
+	}
+	return "", fmt.Errorf("No ws endpoint found %v", servers)
+}
+
 type bundle struct {
 	client ipc.Client
 	server ipc.Server
@@ -440,20 +459,40 @@
 			}
 		}
 		client.Close()
+
 	}
 }
 
+type websocketMode bool
+type closeSendMode bool
+
+const (
+	useWebsocket websocketMode = true
+	noWebsocket  websocketMode = false
+
+	closeSend   closeSendMode = true
+	noCloseSend closeSendMode = false
+)
+
 func TestRPC(t *testing.T) {
-	testRPC(t, true)
+	testRPC(t, closeSend, noWebsocket)
+}
+
+func TestRPCWithWebsocket(t *testing.T) {
+	testRPC(t, closeSend, useWebsocket)
 }
 
 // TestCloseSendOnFinish tests that Finish informs the server that no more
 // inputs will be sent by the client if CloseSend has not already done so.
 func TestRPCCloseSendOnFinish(t *testing.T) {
-	testRPC(t, false)
+	testRPC(t, noCloseSend, noWebsocket)
 }
 
-func testRPC(t *testing.T, shouldCloseSend bool) {
+func TestRPCCloseSendOnFinishWithWebsocket(t *testing.T) {
+	testRPC(t, noCloseSend, useWebsocket)
+}
+
+func testRPC(t *testing.T, shouldCloseSend closeSendMode, shouldUseWebsocket websocketMode) {
 	type v []interface{}
 	type testcase struct {
 		name       string
@@ -494,7 +533,16 @@
 	pserver.AddToRoots(pclient.BlessingStore().Default())
 	for _, test := range tests {
 		vlog.VI(1).Infof("%s client.StartCall", name(test))
-		call, err := b.client.StartCall(testContext(), test.name, test.method, test.args)
+		vname := test.name
+		if shouldUseWebsocket {
+			var err error
+			vname, err = resolveWSEndpoint(b.ns, vname)
+			if err != nil && err != test.startErr {
+				t.Errorf(`%s ns.Resolve got error "%v", want "%v"`, name(test), err, test.startErr)
+				continue
+			}
+		}
+		call, err := b.client.StartCall(testContext(), vname, test.method, test.args)
 		if err != test.startErr {
 			t.Errorf(`%s client.StartCall got error "%v", want "%v"`, name(test), err, test.startErr)
 			continue
@@ -618,7 +666,7 @@
 }
 
 // Implements ipc.Dispatcher
-func (s *dischargeImpetusTester) Lookup(_, _ string) (interface{}, security.Authorizer, error) {
+func (s *dischargeImpetusTester) Lookup(_ string) (interface{}, security.Authorizer, error) {
 	return ipc.ReflectInvoker(dischargeImpetusServer{s}), testServerAuthorizer{}, nil
 }
 
diff --git a/runtimes/google/ipc/glob.go b/runtimes/google/ipc/glob.go
index 9d959f9..9dba1e4 100644
--- a/runtimes/google/ipc/glob.go
+++ b/runtimes/google/ipc/glob.go
@@ -82,7 +82,7 @@
 	if disp == nil {
 		return nil, verror.NoExistf("ipc: no dispatcher for %q.%s", ctx.Suffix(), ctx.Method())
 	}
-	obj, auth, err := disp.Lookup(ctx.Suffix(), ctx.Method())
+	obj, auth, err := disp.Lookup(ctx.Suffix())
 	switch {
 	case err != nil:
 		return nil, err
@@ -117,7 +117,7 @@
 	if disp == nil {
 		return ipc.MethodSig{}, verror.NoExistf("ipc: no such method %q", ctx.Method())
 	}
-	obj, auth, err := disp.Lookup(ctx.Suffix(), ctx.Method())
+	obj, auth, err := disp.Lookup(ctx.Suffix())
 	switch {
 	case err != nil:
 		return ipc.MethodSig{}, err
@@ -181,6 +181,7 @@
 		return err
 	}
 	disp := i.dispNormal
+	call.M.Method = ipc.GlobMethod
 	call.M.MethodTags = []interface{}{security.ResolveLabel}
 	if naming.IsReserved(i.receiver) || (i.receiver == "" && naming.IsReserved(pattern)) {
 		disp = i.dispReserved
@@ -199,7 +200,7 @@
 		vlog.Error(err)
 		return err
 	}
-	obj, auth, err := disp.Lookup(call.Suffix(), ipc.GlobMethod)
+	obj, auth, err := disp.Lookup(call.Suffix())
 	switch {
 	case err != nil:
 		return err
diff --git a/runtimes/google/ipc/glob_test.go b/runtimes/google/ipc/glob_test.go
index b34ce5d..5d497e8 100644
--- a/runtimes/google/ipc/glob_test.go
+++ b/runtimes/google/ipc/glob_test.go
@@ -173,7 +173,7 @@
 	tree *node
 }
 
-func (d *disp) Lookup(suffix, method string) (interface{}, security.Authorizer, error) {
+func (d *disp) Lookup(suffix string) (interface{}, security.Authorizer, error) {
 	elems := strings.Split(suffix, "/")
 	if len(elems) != 0 && elems[0] == "muah" {
 		// Infinite space. Each node has one child named "ha".
diff --git a/runtimes/google/ipc/server.go b/runtimes/google/ipc/server.go
index 75447aa..abd5986 100644
--- a/runtimes/google/ipc/server.go
+++ b/runtimes/google/ipc/server.go
@@ -283,6 +283,11 @@
 			s.active.Done()
 		}()
 		s.publisher.AddServer(s.publishEP(iep, s.servesMountTable), s.servesMountTable)
+		if strings.HasPrefix(iep.Protocol, "tcp") {
+			epCopy := *iep
+			epCopy.Protocol = "ws"
+			s.publisher.AddServer(s.publishEP(&epCopy, s.servesMountTable), s.servesMountTable)
+		}
 	}
 
 	if len(listenSpec.Proxy) > 0 {
@@ -321,6 +326,13 @@
 	s.listeners[ln] = nil
 	s.Unlock()
 	s.publisher.AddServer(s.publishEP(iep, s.servesMountTable), s.servesMountTable)
+
+	if strings.HasPrefix(iep.Protocol, "tcp") {
+		epCopy := *iep
+		epCopy.Protocol = "ws"
+		s.publisher.AddServer(s.publishEP(&epCopy, s.servesMountTable), s.servesMountTable)
+	}
+
 	return iep, ln, nil
 }
 
@@ -351,6 +363,11 @@
 			// The listener is done, so:
 			// (1) Unpublish its name
 			s.publisher.RemoveServer(s.publishEP(iep, s.servesMountTable))
+			if strings.HasPrefix(iep.Protocol, "tcp") {
+				iepCopy := *iep
+				iepCopy.Protocol = "ws"
+				s.publisher.RemoveServer(s.publishEP(&iepCopy, s.servesMountTable))
+			}
 		}
 
 		s.Lock()
@@ -820,7 +837,7 @@
 		disp = fs.server.dispReserved
 	}
 	if disp != nil {
-		obj, auth, err := disp.Lookup(suffix, *method)
+		obj, auth, err := disp.Lookup(suffix)
 		switch {
 		case err != nil:
 			return nil, nil, verror.Convert(err)
diff --git a/runtimes/google/ipc/server_test.go b/runtimes/google/ipc/server_test.go
index 1bb853a..a918f00 100644
--- a/runtimes/google/ipc/server_test.go
+++ b/runtimes/google/ipc/server_test.go
@@ -130,6 +130,10 @@
 	return addr
 }
 
+func addWSName(name string) []string {
+	return []string{name, strings.Replace(name, "@tcp@", "@ws@", 1)}
+}
+
 func testProxy(t *testing.T, spec ipc.ListenSpec) {
 	sm := imanager.InternalNew(naming.FixedRoutingID(0x555555555))
 	ns := tnaming.NewSimpleNamespace()
@@ -201,9 +205,9 @@
 		t.Fatalf("unexpected error: %s", err)
 	}
 	proxiedEP.RID = naming.FixedRoutingID(0x555555555)
-	expectedEndpoints := []string{proxiedEP.String()}
+	expectedEndpoints := addWSName(proxiedEP.String())
 	if hasLocalListener {
-		expectedEndpoints = append(expectedEndpoints, ep.String())
+		expectedEndpoints = append(expectedEndpoints, addWSName(ep.String())...)
 	}
 
 	// Proxy connetions are created asynchronously, so we wait for the
@@ -228,8 +232,12 @@
 	if hasLocalListener {
 		// Listen will publish both the local and proxied endpoint with the
 		// mount table, given that we're trying to test the proxy, we remove
-		// the local endpoint from the mount table entry!
-		ns.Unmount(testContext(), "mountpoint/server", naming.JoinAddressName(ep.String(), ""))
+		// the local endpoint from the mount table entry!  We have to remove both
+		// the tcp and the websocket address.
+		sep := ep.String()
+		wsep := strings.Replace(sep, "@tcp@", "@ws@", 1)
+		ns.Unmount(testContext(), "mountpoint/server", naming.JoinAddressName(sep, ""))
+		ns.Unmount(testContext(), "mountpoint/server", naming.JoinAddressName(wsep, ""))
 	}
 
 	// Proxied endpoint should be published and RPC should succeed (through proxy)
diff --git a/runtimes/google/ipc/sort_endpoints.go b/runtimes/google/ipc/sort_endpoints.go
index ab9f730..c31e072 100644
--- a/runtimes/google/ipc/sort_endpoints.go
+++ b/runtimes/google/ipc/sort_endpoints.go
@@ -73,30 +73,27 @@
 	return se
 }
 
-func sortByProtocol(eps []*serverEndpoint) map[string][]*serverEndpoint {
+// sortByProtocols sorts the supplied slice of serverEndpoints into a hash
+// map keyed by the protocol of each of those endpoints where that protocol
+// is listed in the protocols parameter and keyed by '*' if not so listed.
+func sortByProtocol(eps []*serverEndpoint, protocols []string) (bool, map[string][]*serverEndpoint) {
 	byProtocol := make(map[string][]*serverEndpoint)
+	matched := false
 	for _, ep := range eps {
-		p := ep.iep.Protocol
-		byProtocol[p] = append(byProtocol[p], ep)
-	}
-	return byProtocol
-}
-
-func unmatchedProtocols(hashed map[string][]*serverEndpoint, protocols []string) []*serverEndpoint {
-	unmatched := make([]*serverEndpoint, 0, 10)
-	for p, eps := range hashed {
 		found := false
-		for _, preferred := range protocols {
-			if p == preferred {
+		for _, p := range protocols {
+			if ep.iep.Protocol == p || (p == "tcp" && strings.HasPrefix(ep.iep.Protocol, "tcp")) {
+				byProtocol[p] = append(byProtocol[p], ep)
 				found = true
+				matched = true
 				break
 			}
 		}
 		if !found {
-			unmatched = append(unmatched, eps...)
+			byProtocol["*"] = append(byProtocol["*"], ep)
 		}
 	}
-	return unmatched
+	return matched, byProtocol
 }
 
 func orderByLocality(ifcs netstate.AddrList, eps []*serverEndpoint) []*serverEndpoint {
@@ -167,8 +164,21 @@
 	return r
 }
 
-var defaultPreferredProtocolOrder = []string{"unixfd", "tcp4", "tcp", "tcp6"}
+var defaultPreferredProtocolOrder = []string{"unixfd", "tcp4", "tcp", "*"}
 
+// filterAndOrderServers returns a set of servers that are compatible with
+// the current client in order of 'preference' specified by the supplied
+// protocols and a notion of 'locality' according to the supplied protocol
+// list as follows:
+// - if the protocol parameter is non-empty, then only servers matching those
+// protocols are returned and the endpoints are ordered first by protocol
+// and then by locality within each protocol. If tcp4 and unixfd are requested
+// for example then only protocols that match tcp4 and unixfd will returned
+// with the tcp4 ones preceeding the unixfd ones.
+// - if the protocol parameter is empty, then a default protocol ordering
+// will be used, but unlike the previous case, any servers that don't support
+// these protocols will be returned also, but following the default
+// preferences.
 func filterAndOrderServers(servers []string, protocols []string) ([]string, error) {
 	errs := newErrorAccumulator()
 	vlog.VI(3).Infof("Candidates[%v]: %v", protocols, servers)
@@ -178,39 +188,25 @@
 	}
 	vlog.VI(3).Infof("Version Compatible: %v", compatible)
 
+	defaultOrdering := len(protocols) == 0
+
 	// put the server endpoints into per-protocol lists
-	byProtocol := sortByProtocol(compatible)
+	matched, byProtocol := sortByProtocol(compatible, protocols)
 
-	if len(protocols) > 0 {
-		found := 0
-		for _, p := range protocols {
-			found += len(byProtocol[p])
-		}
-		if found == 0 {
-			return nil, fmt.Errorf("failed to find any servers compatible with %v from %s", protocols, servers)
-		}
+	if !defaultOrdering && !matched {
+		return nil, fmt.Errorf("failed to find any servers compatible with %v from %s", protocols, servers)
 	}
-
-	// If a set of protocols is specified, then we will order
-	// and return endpoints that contain those protocols alone.
-	// However, if no protocols are supplied we'll order by
-	// a default ordering but append any endpoints that don't belong
-	// to that default ordering set to the returned endpoints.
-	remaining := []*serverEndpoint{}
 	preferredProtocolOrder := defaultPreferredProtocolOrder
-	if len(protocols) > 0 {
+	if !defaultOrdering {
 		preferredProtocolOrder = protocols
-	} else {
-		remaining = unmatchedProtocols(byProtocol, preferredProtocolOrder)
 	}
 
 	vlog.VI(3).Infof("Have Protocols(%v): %v", protocols, byProtocol)
 
 	networks, err := netstate.GetAll()
 	if err != nil {
-		r := sliceByProtocol(byProtocol, preferredProtocolOrder)
-		r = append(r, slice(remaining)...)
-		return r, nil
+		// return whatever we have now, just not sorted by locality.
+		return sliceByProtocol(byProtocol, preferredProtocolOrder), nil
 	}
 
 	ordered := make([]*serverEndpoint, 0, len(byProtocol))
@@ -219,9 +215,6 @@
 		ordered = append(ordered, o...)
 	}
 
-	if len(protocols) == 0 {
-		ordered = append(ordered, remaining...)
-	}
 	vlog.VI(2).Infof("Ordered By Locality: %v", ordered)
 	return slice(ordered), nil
 }
diff --git a/runtimes/google/ipc/sort_internal_test.go b/runtimes/google/ipc/sort_internal_test.go
index 5b9b4cf..c234557 100644
--- a/runtimes/google/ipc/sort_internal_test.go
+++ b/runtimes/google/ipc/sort_internal_test.go
@@ -56,23 +56,44 @@
 		name := naming.JoinAddressName(naming.FormatEndpoint("foobar", a), "")
 		servers = append(servers, name)
 	}
+	for _, a := range []string{"127.0.0.7", "127.0.0.8"} {
+		name := naming.JoinAddressName(naming.FormatEndpoint("tcp6", a), "")
+		servers = append(servers, name)
+	}
 
-	got, err := filterAndOrderServers(servers, []string{"foobar", "tcp"})
+	got, err := filterAndOrderServers(servers, []string{"batman"})
+	if err == nil {
+		t.Fatalf("expected an error")
+	}
+
+	got, err = filterAndOrderServers(servers, []string{"foobar", "tcp4"})
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
 
-	// Just foobar and tcp
+	// Just foobar and tcp4
 	want := []string{
 		"/@2@foobar@127.0.0.10@00000000000000000000000000000000@@@@",
 		"/@2@foobar@127.0.0.11@00000000000000000000000000000000@@@@",
-		"/@2@tcp@127.0.0.3@00000000000000000000000000000000@@@@",
-		"/@2@tcp@127.0.0.4@00000000000000000000000000000000@@@@",
+		"/@2@tcp4@127.0.0.1@00000000000000000000000000000000@@@@",
+		"/@2@tcp4@127.0.0.2@00000000000000000000000000000000@@@@",
 	}
 	if !reflect.DeepEqual(got, want) {
 		t.Errorf("got: %v, want %v", got, want)
 	}
 
+	got, err = filterAndOrderServers(servers, []string{"tcp"})
+	if err != nil {
+		t.Fatalf("unexpected error: %s", err)
+	}
+	// tcp or tcp4
+	want = []string{
+		"/@2@tcp4@127.0.0.1@00000000000000000000000000000000@@@@",
+		"/@2@tcp4@127.0.0.2@00000000000000000000000000000000@@@@",
+		"/@2@tcp@127.0.0.3@00000000000000000000000000000000@@@@",
+		"/@2@tcp@127.0.0.4@00000000000000000000000000000000@@@@",
+	}
+
 	// Everything, since we didn't specify a protocol
 	got, err = filterAndOrderServers(servers, []string{})
 	if err != nil {
@@ -82,10 +103,12 @@
 	// original ordering within each protocol, with protocols that
 	// are not in the default ordering list at the end.
 	want = []string{
-		"/@2@tcp4@127.0.0.1@00000000000000000000000000000000@@@@",
-		"/@2@tcp4@127.0.0.2@00000000000000000000000000000000@@@@",
 		"/@2@tcp@127.0.0.3@00000000000000000000000000000000@@@@",
 		"/@2@tcp@127.0.0.4@00000000000000000000000000000000@@@@",
+		"/@2@tcp4@127.0.0.1@00000000000000000000000000000000@@@@",
+		"/@2@tcp4@127.0.0.2@00000000000000000000000000000000@@@@",
+		"/@2@tcp6@127.0.0.7@00000000000000000000000000000000@@@@",
+		"/@2@tcp6@127.0.0.8@00000000000000000000000000000000@@@@",
 		"/@2@foobar@127.0.0.10@00000000000000000000000000000000@@@@",
 		"/@2@foobar@127.0.0.11@00000000000000000000000000000000@@@@",
 	}
diff --git a/runtimes/google/ipc/stream/manager/manager.go b/runtimes/google/ipc/stream/manager/manager.go
index 8620a61..a96c725 100644
--- a/runtimes/google/ipc/stream/manager/manager.go
+++ b/runtimes/google/ipc/stream/manager/manager.go
@@ -12,11 +12,11 @@
 	"veyron.io/veyron/veyron/lib/stats"
 	"veyron.io/veyron/veyron/runtimes/google/ipc/stream/crypto"
 	"veyron.io/veyron/veyron/runtimes/google/ipc/stream/vif"
+	"veyron.io/veyron/veyron/runtimes/google/ipc/stream/wslistener"
 	"veyron.io/veyron/veyron/runtimes/google/ipc/version"
 	inaming "veyron.io/veyron/veyron/runtimes/google/naming"
 
 	"veyron.io/veyron/veyron2/ipc/stream"
-	ipcversion "veyron.io/veyron/veyron2/ipc/version"
 	"veyron.io/veyron/veyron2/naming"
 	"veyron.io/veyron/veyron2/verror"
 	"veyron.io/veyron/veyron2/vlog"
@@ -65,7 +65,20 @@
 	if d, _ := stream.RegisteredProtocol(network); d != nil {
 		return d(address)
 	}
-	return net.DialTimeout(network, address, timeout)
+	conn, err := net.DialTimeout(network, address, timeout)
+	if err != nil || !strings.HasPrefix(network, "tcp") {
+		return conn, err
+	}
+
+	// For tcp connections we add an extra magic byte so we can differentiate between
+	// raw tcp and websocket on the same port.
+	switch n, err := conn.Write([]byte{wslistener.BinaryMagicByte}); {
+	case err != nil:
+		return nil, err
+	case n != 1:
+		return nil, fmt.Errorf("Unable to write the magic byte")
+	}
+	return conn, nil
 }
 
 // FindOrDialVIF returns the network connection (VIF) to the provided address
@@ -106,14 +119,6 @@
 	vRange := version.SupportedRange
 	if ep, ok := remote.(*inaming.Endpoint); ok {
 		epRange := &version.Range{Min: ep.MinIPCVersion, Max: ep.MaxIPCVersion}
-		if epRange.Max == ipcversion.UnknownIPCVersion {
-			// If the server's version is unknown, we need to back off to
-			// something we are sure it can understand.
-			//
-			// TODO(jyh): Remove this once the min version is IPC6, where we can
-			// support version negotiation.
-			epRange.Max = ipcversion.IPCVersion5
-		}
 		if r, err := vRange.Intersect(epRange); err == nil {
 			vRange = r
 		}
@@ -187,6 +192,12 @@
 		closeNetListener(netln)
 		return nil, nil, errShutDown
 	}
+
+	// If the protocol is tcp, we add the listener that supports both tcp and websocket
+	// so that javascript can talk to this server.
+	if strings.HasPrefix(protocol, "tcp") {
+		netln = wslistener.NewListener(netln)
+	}
 	ln := newNetListener(m, netln, opts)
 	m.listeners[ln] = true
 	m.muListeners.Unlock()
diff --git a/runtimes/google/ipc/stream/manager/manager_test.go b/runtimes/google/ipc/stream/manager/manager_test.go
index ccddf69..8d4d872 100644
--- a/runtimes/google/ipc/stream/manager/manager_test.go
+++ b/runtimes/google/ipc/stream/manager/manager_test.go
@@ -12,6 +12,7 @@
 	"testing"
 	"time"
 
+	"veyron.io/veyron/veyron/lib/websocket"
 	"veyron.io/veyron/veyron2/ipc/stream"
 	"veyron.io/veyron/veyron2/naming"
 	"veyron.io/veyron/veyron2/security"
@@ -38,9 +39,10 @@
 	// introduces less variance in the behavior of the test.
 	runtime.GOMAXPROCS(1)
 	modules.RegisterChild("runServer", "", runServer)
+	stream.RegisterProtocol("ws", websocket.Dial, nil)
 }
 
-func TestSimpleFlow(t *testing.T) {
+func testSimpleFlow(t *testing.T, useWebsocket bool) {
 	server := InternalNew(naming.FixedRoutingID(0x55555555))
 	client := InternalNew(naming.FixedRoutingID(0xcccccccc))
 
@@ -49,6 +51,10 @@
 		t.Fatal(err)
 	}
 
+	if useWebsocket {
+		ep.(*inaming.Endpoint).Protocol = "ws"
+	}
+
 	data := "the dark knight rises"
 	var clientVC stream.VC
 	var clientF1 stream.Flow
@@ -124,6 +130,14 @@
 	}
 }
 
+func TestSimpleFlow(t *testing.T) {
+	testSimpleFlow(t, false)
+}
+
+func TestSimpleFlowWS(t *testing.T) {
+	testSimpleFlow(t, true)
+}
+
 func TestConnectionTimeout(t *testing.T) {
 	client := InternalNew(naming.FixedRoutingID(0xcccccccc))
 
@@ -145,7 +159,7 @@
 	}
 }
 
-func TestAuthenticatedByDefault(t *testing.T) {
+func testAuthenticatedByDefault(t *testing.T, useWebsocket bool) {
 	var (
 		server = InternalNew(naming.FixedRoutingID(0x55555555))
 		client = InternalNew(naming.FixedRoutingID(0xcccccccc))
@@ -161,6 +175,9 @@
 	if err != nil {
 		t.Fatal(err)
 	}
+	if useWebsocket {
+		ep.(*inaming.Endpoint).Protocol = "ws"
+	}
 
 	errs := make(chan error)
 
@@ -209,6 +226,14 @@
 	}
 }
 
+func TestAuthenticatedByDefault(t *testing.T) {
+	testAuthenticatedByDefault(t, false)
+}
+
+func TestAuthenticatedByDefaultWS(t *testing.T) {
+	testAuthenticatedByDefault(t, true)
+}
+
 func numListeners(m stream.Manager) int   { return len(m.(*manager).listeners) }
 func debugString(m stream.Manager) string { return m.(*manager).DebugString() }
 func numVIFs(m stream.Manager) int        { return len(m.(*manager).vifs.List()) }
@@ -271,6 +296,29 @@
 	}
 }
 
+func TestCloseListenerWS(t *testing.T) {
+	server := InternalNew(naming.FixedRoutingID(0x5e97e9))
+
+	ln, ep, err := server.Listen("tcp", "127.0.0.1:0")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	ep.(*inaming.Endpoint).Protocol = "ws"
+
+	// Server will just listen for flows and close them.
+	go acceptLoop(ln)
+	client := InternalNew(naming.FixedRoutingID(0xc1e41))
+	if _, err = client.Dial(ep); err != nil {
+		t.Fatal(err)
+	}
+	ln.Close()
+	client = InternalNew(naming.FixedRoutingID(0xc1e42))
+	if _, err := client.Dial(ep); err == nil {
+		t.Errorf("client.Dial(%q) should have failed", ep)
+	}
+}
+
 func TestShutdown(t *testing.T) {
 	server := InternalNew(naming.FixedRoutingID(0x5e97e9))
 	ln, _, err := server.Listen("tcp", "127.0.0.1:0")
@@ -316,6 +364,33 @@
 	}
 }
 
+func TestShutdownEndpointWS(t *testing.T) {
+	server := InternalNew(naming.FixedRoutingID(0x55555555))
+	client := InternalNew(naming.FixedRoutingID(0xcccccccc))
+
+	ln, ep, err := server.Listen("tcp", "127.0.0.1:0")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	ep.(*inaming.Endpoint).Protocol = "ws"
+
+	// Server will just listen for flows and close them.
+	go acceptLoop(ln)
+
+	vc, err := client.Dial(ep)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if f, err := vc.Connect(); f == nil || err != nil {
+		t.Errorf("vc.Connect failed: (%v, %v)", f, err)
+	}
+	client.ShutdownEndpoint(ep)
+	if f, err := vc.Connect(); f != nil || err == nil {
+		t.Errorf("vc.Connect unexpectedly succeeded: (%v, %v)", f, err)
+	}
+}
+
 /* TLS + resumption + channel bindings is broken: <https://secure-resumption.com/#channelbindings>.
 func TestSessionTicketCache(t *testing.T) {
 	server := InternalNew(naming.FixedRoutingID(0x55555555))
@@ -335,7 +410,7 @@
 }
 */
 
-func TestMultipleVCs(t *testing.T) {
+func testMultipleVCs(t *testing.T, useWebsocket bool) {
 	server := InternalNew(naming.FixedRoutingID(0x55555555))
 	client := InternalNew(naming.FixedRoutingID(0xcccccccc))
 
@@ -348,6 +423,10 @@
 	if err != nil {
 		t.Fatal(err)
 	}
+	if useWebsocket {
+		ep.(*inaming.Endpoint).Protocol = "ws"
+	}
+
 	read := func(flow stream.Flow, c chan string) {
 		var buf bytes.Buffer
 		var tmp [1024]byte
@@ -414,6 +493,14 @@
 	}
 }
 
+func TestMultipleVCs(t *testing.T) {
+	testMultipleVCs(t, false)
+}
+
+func TestMultipleVCsWS(t *testing.T) {
+	testMultipleVCs(t, true)
+}
+
 func TestAddressResolution(t *testing.T) {
 	server := InternalNew(naming.FixedRoutingID(0x55555555))
 	client := InternalNew(naming.FixedRoutingID(0xcccccccc))
@@ -457,7 +544,7 @@
 
 func TestServerRestartDuringClientLifetime(t *testing.T) {
 	client := InternalNew(naming.FixedRoutingID(0xcccccccc))
-	sh := modules.NewShell(".*")
+	sh := modules.NewShell()
 	defer sh.Cleanup(nil, nil)
 	h, err := sh.Start("runServer", nil, "127.0.0.1:0")
 	if err != nil {
@@ -494,6 +581,46 @@
 	}
 }
 
+func TestServerRestartDuringClientLifetimeWS(t *testing.T) {
+	client := InternalNew(naming.FixedRoutingID(0xcccccccc))
+	sh := modules.NewShell()
+	defer sh.Cleanup(nil, nil)
+	h, err := sh.Start("runServer", nil, "127.0.0.1:0")
+	if err != nil {
+		t.Fatalf("unexpected error: %s", err)
+	}
+	s := expect.NewSession(t, h.Stdout(), time.Minute)
+	addr := s.ReadLine()
+
+	ep, err := inaming.NewEndpoint(addr)
+	if err != nil {
+		t.Fatalf("inaming.NewEndpoint(%q): %v", addr, err)
+	}
+	ep.Protocol = "ws"
+	if _, err := client.Dial(ep); err != nil {
+		t.Fatal(err)
+	}
+	h.Shutdown(nil, os.Stderr)
+
+	// A new VC cannot be created since the server is dead
+	if _, err := client.Dial(ep); err == nil {
+		t.Fatal("Expected client.Dial to fail since server is dead")
+	}
+
+	h, err = sh.Start("runServer", nil, addr)
+	if err != nil {
+		t.Fatalf("unexpected error: %s", err)
+	}
+	s = expect.NewSession(t, h.Stdout(), time.Minute)
+	// Restarting the server, listening on the same address as before
+	if addr2 := s.ReadLine(); addr2 != addr || err != nil {
+		t.Fatalf("Got (%q, %v) want (%q, nil)", addr2, err, addr)
+	}
+	if _, err := client.Dial(ep); err != nil {
+		t.Fatal(err)
+	}
+}
+
 // Needed by modules framework
 func TestHelperProcess(t *testing.T) {
 	modules.DispatchInTest()
diff --git a/runtimes/google/ipc/stream/proxy/proxy.go b/runtimes/google/ipc/stream/proxy/proxy.go
index a0d794e..0da73ce 100644
--- a/runtimes/google/ipc/stream/proxy/proxy.go
+++ b/runtimes/google/ipc/stream/proxy/proxy.go
@@ -11,6 +11,7 @@
 	"veyron.io/veyron/veyron/runtimes/google/ipc/stream/message"
 	"veyron.io/veyron/veyron/runtimes/google/ipc/stream/vc"
 	"veyron.io/veyron/veyron/runtimes/google/ipc/stream/vif"
+	"veyron.io/veyron/veyron/runtimes/google/ipc/stream/wslistener"
 	"veyron.io/veyron/veyron/runtimes/google/ipc/version"
 	"veyron.io/veyron/veyron/runtimes/google/lib/bqueue"
 	"veyron.io/veyron/veyron/runtimes/google/lib/bqueue/drrqueue"
@@ -135,6 +136,7 @@
 	if err != nil {
 		return nil, fmt.Errorf("net.Listen(%q, %q) failed: %v", network, address, err)
 	}
+	ln = wslistener.NewListener(ln)
 	if len(pubAddress) == 0 {
 		pubAddress = ln.Addr().String()
 	}
diff --git a/runtimes/google/ipc/stream/vif/vif.go b/runtimes/google/ipc/stream/vif/vif.go
index b69cdb4..355ea0b 100644
--- a/runtimes/google/ipc/stream/vif/vif.go
+++ b/runtimes/google/ipc/stream/vif/vif.go
@@ -472,7 +472,7 @@
 	for {
 		f, err := hr.Listener.Accept()
 		if err != nil {
-			vlog.VI(2).Infof("Accept failed on VC %v on VIF %v", vc, vif)
+			vlog.VI(2).Infof("Accept failed on VC %v on VIF %v: %v", vc, vif, err)
 			return
 		}
 		if err := acceptor.Put(ConnectorAndFlow{vc, f}); err != nil {
diff --git a/runtimes/google/ipc/stream/wslistener/listener.go b/runtimes/google/ipc/stream/wslistener/listener.go
new file mode 100644
index 0000000..79dc035
--- /dev/null
+++ b/runtimes/google/ipc/stream/wslistener/listener.go
@@ -0,0 +1,214 @@
+// +build !nacl
+
+package wslistener
+
+import (
+	"bufio"
+	"errors"
+	"fmt"
+	"net"
+	"net/http"
+	"sync"
+
+	vwebsocket "veyron.io/veyron/veyron/lib/websocket"
+	"veyron.io/veyron/veyron/runtimes/google/lib/upcqueue"
+
+	"veyron.io/veyron/veyron2/vlog"
+
+	"github.com/gorilla/websocket"
+)
+
+var errListenerIsClosed = errors.New("Listener has been Closed")
+
+// We picked 0xFF because it's obviously outside the range of ASCII,
+// and is completely unused in UTF-8.
+const BinaryMagicByte byte = 0xFF
+
+const bufferSize int = 4096
+
+// A listener that is able to handle either raw tcp request or websocket requests.
+// The result of Accept is is a net.Conn interface.
+type wsTCPListener struct {
+	// The queue of net.Conn to be returned by Accept.
+	q *upcqueue.T
+
+	// The queue for the http listener when we detect an http request.
+	httpQ *upcqueue.T
+
+	// The underlying listener.
+	netLn    net.Listener
+	wsServer http.Server
+
+	netLoop sync.WaitGroup
+	wsLoop  sync.WaitGroup
+}
+
+// bufferedConn is used to allow us to Peek at the first byte to see if it
+// is the magic byte used by veyron tcp requests.  Other than that it behaves
+// like a normal net.Conn.
+type bufferedConn struct {
+	net.Conn
+	// TODO(bjornick): Remove this buffering because we have way too much
+	// buffering anyway.  We really only need to buffer the first byte.
+	r *bufio.Reader
+}
+
+func newBufferedConn(c net.Conn) bufferedConn {
+	return bufferedConn{Conn: c, r: bufio.NewReaderSize(c, bufferSize)}
+}
+
+func (c *bufferedConn) Peek(n int) ([]byte, error) {
+	return c.r.Peek(n)
+}
+
+func (c *bufferedConn) Read(p []byte) (int, error) {
+	return c.r.Read(p)
+}
+
+// queueListener is a listener that returns connections that are in q.
+type queueListener struct {
+	q *upcqueue.T
+	// ln is needed to implement Close and Addr
+	ln net.Listener
+}
+
+func (l *queueListener) Accept() (net.Conn, error) {
+	item, err := l.q.Get(nil)
+	switch {
+	case err == upcqueue.ErrQueueIsClosed:
+		return nil, errListenerIsClosed
+	case err != nil:
+		return nil, fmt.Errorf("Accept failed: %v", err)
+	default:
+		return item.(net.Conn), nil
+	}
+}
+
+func (l *queueListener) Close() error {
+	l.q.Shutdown()
+	return l.ln.Close()
+}
+
+func (l *queueListener) Addr() net.Addr {
+	return l.ln.Addr()
+}
+
+func NewListener(netLn net.Listener) net.Listener {
+	ln := &wsTCPListener{
+		q:     upcqueue.New(),
+		httpQ: upcqueue.New(),
+		netLn: netLn,
+	}
+	ln.netLoop.Add(1)
+	go ln.netAcceptLoop()
+	httpListener := &queueListener{
+		q:  ln.httpQ,
+		ln: ln,
+	}
+	handler := func(w http.ResponseWriter, r *http.Request) {
+		defer ln.wsLoop.Done()
+		if r.Method != "GET" {
+			http.Error(w, "Method not allowed.", http.StatusMethodNotAllowed)
+			return
+		}
+		ws, err := websocket.Upgrade(w, r, nil, bufferSize, bufferSize)
+		if _, ok := err.(websocket.HandshakeError); ok {
+			http.Error(w, "Not a websocket handshake", 400)
+			vlog.Errorf("Rejected a non-websocket request: %v", err)
+			return
+		} else if err != nil {
+			http.Error(w, "Internal Error", 500)
+			vlog.Errorf("Rejected a non-websocket request: %v", err)
+			return
+		}
+		conn := vwebsocket.WebsocketConn(ws)
+		if err := ln.q.Put(conn); err != nil {
+			vlog.VI(1).Infof("Shutting down conn from %s (local address: %s) as Put failed: %v", ws.RemoteAddr(), ws.LocalAddr(), err)
+			ws.Close()
+			return
+		}
+
+	}
+	ln.wsServer = http.Server{
+		Handler: http.HandlerFunc(handler),
+	}
+	go ln.wsServer.Serve(httpListener)
+	return ln
+}
+
+func (ln *wsTCPListener) netAcceptLoop() {
+	defer ln.Close()
+	defer ln.netLoop.Done()
+	for {
+		conn, err := ln.netLn.Accept()
+		if err != nil {
+			vlog.VI(1).Infof("Exiting netAcceptLoop: net.Listener.Accept() failed on %v with %v", ln.netLn, err)
+			return
+		}
+		vlog.VI(1).Infof("New net.Conn accepted from %s (local address: %s)", conn.RemoteAddr(), conn.LocalAddr())
+		bc := newBufferedConn(conn)
+		magic, err := bc.Peek(1)
+		if err != nil {
+			vlog.VI(1).Infof("Shutting down conn from %s (local address: %s) as the magic byte failed to be read: %v", conn.RemoteAddr(), conn.LocalAddr(), err)
+			bc.Close()
+			continue
+		}
+
+		vlog.VI(1).Info("Got a connection from %s (local address: %s)", conn.RemoteAddr(), conn.LocalAddr())
+		// Check to see if it is a regular connection or a http connection.
+		if magic[0] == BinaryMagicByte {
+			if _, err := bc.r.ReadByte(); err != nil {
+				vlog.VI(1).Infof("Shutting down conn from %s (local address: %s), could read past the magic byte: %v", conn.RemoteAddr(), conn.LocalAddr(), err)
+				bc.Close()
+				continue
+			}
+			if err := ln.q.Put(&bc); err != nil {
+				vlog.VI(1).Infof("Shutting down conn from %s (local address: %s) as Put failed in vifLoop: %v", conn.RemoteAddr(), conn.LocalAddr(), err)
+				bc.Close()
+				continue
+			}
+			continue
+		}
+
+		ln.wsLoop.Add(1)
+		if err := ln.httpQ.Put(&bc); err != nil {
+			ln.wsLoop.Done()
+			vlog.VI(1).Infof("Shutting down conn from %s (local address: %s) as Put failed in vifLoop: %v", conn.RemoteAddr(), conn.LocalAddr(), err)
+			bc.Close()
+			continue
+		}
+	}
+}
+
+func (ln *wsTCPListener) Accept() (net.Conn, error) {
+	item, err := ln.q.Get(nil)
+	switch {
+	case err == upcqueue.ErrQueueIsClosed:
+		return nil, errListenerIsClosed
+	case err != nil:
+		return nil, fmt.Errorf("Accept failed: %v", err)
+	default:
+		return item.(net.Conn), nil
+	}
+}
+
+func (ln *wsTCPListener) Close() error {
+	addr := ln.netLn.Addr()
+	err := ln.netLn.Close()
+	vlog.VI(1).Infof("Closed net.Listener on (%q, %q): %v", addr.Network(), addr, err)
+	ln.httpQ.Shutdown()
+	ln.netLoop.Wait()
+	ln.wsLoop.Wait()
+	// q has to be shutdown after the netAcceptLoop finishes because that loop
+	// could be in the process of accepting a websocket connection.  The ordering
+	// relative to wsLoop is not really relevant because the wsLoop counter wil
+	// decrement every time there a websocket connection has been handled and does
+	// not block on gets from q.
+	ln.q.Shutdown()
+	vlog.VI(3).Infof("Close stream.wsTCPListener %s", ln)
+	return nil
+}
+
+func (ln *wsTCPListener) Addr() net.Addr {
+	return ln.netLn.Addr()
+}
diff --git a/runtimes/google/ipc/stream/wslistener/listener_nacl.go b/runtimes/google/ipc/stream/wslistener/listener_nacl.go
new file mode 100644
index 0000000..686dfff
--- /dev/null
+++ b/runtimes/google/ipc/stream/wslistener/listener_nacl.go
@@ -0,0 +1,16 @@
+// +build nacl
+
+package wslistener
+
+import (
+	"net"
+)
+
+// Websocket listeners are not supported in NaCl.
+// This file is needed for compilation only.
+
+const BinaryMagicByte byte = 0x90
+
+func NewListener(netLn net.Listener) net.Listener {
+	panic("Websocket NewListener called in nacl code!")
+}
diff --git a/runtimes/google/naming/namespace/all_test.go b/runtimes/google/naming/namespace/all_test.go
index 163bcc9..645050a 100644
--- a/runtimes/google/naming/namespace/all_test.go
+++ b/runtimes/google/naming/namespace/all_test.go
@@ -3,13 +3,14 @@
 import (
 	"runtime"
 	"runtime/debug"
+	"strings"
 	"sync"
 	"testing"
 	"time"
 
 	"veyron.io/veyron/veyron2"
-	"veyron.io/veyron/veyron2/context"
 	"veyron.io/veyron/veyron2/ipc"
+	"veyron.io/veyron/veyron2/ipc/stream"
 	"veyron.io/veyron/veyron2/naming"
 	"veyron.io/veyron/veyron2/options"
 	"veyron.io/veyron/veyron2/rt"
@@ -20,19 +21,26 @@
 
 	"veyron.io/veyron/veyron/lib/glob"
 	"veyron.io/veyron/veyron/lib/testutil"
+	"veyron.io/veyron/veyron/lib/websocket"
 	_ "veyron.io/veyron/veyron/profiles"
-	"veyron.io/veyron/veyron/runtimes/google/ipc/stream/sectest"
 	"veyron.io/veyron/veyron/runtimes/google/naming/namespace"
 	service "veyron.io/veyron/veyron/services/mounttable/lib"
 )
 
-func init() { testutil.Init() }
+func init() {
+	testutil.Init()
+	stream.RegisterProtocol("ws", websocket.Dial, nil)
+}
 
 func boom(t *testing.T, f string, v ...interface{}) {
 	t.Logf(f, v...)
 	t.Fatal(string(debug.Stack()))
 }
 
+func addWSName(name string) []string {
+	return []string{name, strings.Replace(name, "@tcp@", "@ws@", 1)}
+}
+
 // N squared but who cares, this is a little test.
 // Ignores dups.
 func contains(container, contained []string) bool {
@@ -120,7 +128,7 @@
 
 type dispatcher struct{}
 
-func (d *dispatcher) Lookup(suffix, method string) (interface{}, security.Authorizer, error) {
+func (d *dispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) {
 	return &testServer{suffix}, allowEveryoneAuthorizer{}, nil
 }
 
@@ -140,28 +148,20 @@
 	}
 }
 
-func doResolveTest(t *testing.T, fname string, f func(context.T, string, ...naming.ResolveOpt) ([]string, error), ctx context.T, name string, want []string, opts ...naming.ResolveOpt) {
-	servers, err := f(ctx, name, opts...)
-	if err != nil {
-		boom(t, "Failed to %s %s: %s", fname, name, err)
-	}
-	compare(t, fname, name, servers, want)
-}
-
 func testResolveToMountTable(t *testing.T, r veyron2.Runtime, ns naming.Namespace, name string, want ...string) {
-	doResolveTest(t, "ResolveToMountTable", ns.ResolveToMountTable, r.NewContext(), name, want)
-}
-
-func testResolveToMountTableWithPattern(t *testing.T, r veyron2.Runtime, ns naming.Namespace, name string, pattern naming.ResolveOpt, want ...string) {
-	doResolveTest(t, "ResolveToMountTable", ns.ResolveToMountTable, r.NewContext(), name, want, pattern)
+	servers, err := ns.ResolveToMountTable(r.NewContext(), name)
+	if err != nil {
+		boom(t, "Failed to ResolveToMountTable %q: %s", name, err)
+	}
+	compare(t, "ResolveToMountTable", name, servers, want)
 }
 
 func testResolve(t *testing.T, r veyron2.Runtime, ns naming.Namespace, name string, want ...string) {
-	doResolveTest(t, "Resolve", ns.Resolve, r.NewContext(), name, want)
-}
-
-func testResolveWithPattern(t *testing.T, r veyron2.Runtime, ns naming.Namespace, name string, pattern naming.ResolveOpt, want ...string) {
-	doResolveTest(t, "Resolve", ns.Resolve, r.NewContext(), name, want, pattern)
+	servers, err := ns.Resolve(r.NewContext(), name)
+	if err != nil {
+		boom(t, "Failed to Resolve %q: %s", name, err)
+	}
+	compare(t, "Resolve", name, servers, want)
 }
 
 func testUnresolve(t *testing.T, r veyron2.Runtime, ns naming.Namespace, name string, want ...string) {
@@ -302,16 +302,16 @@
 		testResolveToMountTable(t, r, ns, m, rootMT)
 
 		// The server registered for each mount point is a mount table
-		testResolve(t, r, ns, m, mts[m].name)
+		testResolve(t, r, ns, m, addWSName(mts[m].name)...)
 
 		// ResolveToMountTable will walk through to the sub MountTables
 		mtbar := naming.Join(m, "bar")
 		subMT := naming.Join(mts[m].name, "bar")
-		testResolveToMountTable(t, r, ns, mtbar, subMT)
+		testResolveToMountTable(t, r, ns, mtbar, addWSName(subMT)...)
 	}
 
 	for _, j := range []string{j1MP, j2MP, j3MP} {
-		testResolve(t, r, ns, j, jokes[j].name)
+		testResolve(t, r, ns, j, addWSName(jokes[j].name)...)
 	}
 }
 
@@ -344,7 +344,7 @@
 
 	mt2mt := naming.Join(mts[mt2MP].name, "a")
 	// The mt2/a is served by the mt2 mount table
-	testResolveToMountTable(t, r, ns, mt2a, mt2mt)
+	testResolveToMountTable(t, r, ns, mt2a, addWSName(mt2mt)...)
 	// The server for mt2a is mt3server from the second mount above.
 	testResolve(t, r, ns, mt2a, mt3Server)
 
@@ -359,12 +359,14 @@
 		}
 	}
 
+	names := []string{naming.JoinAddressName(mts[mt4MP].name, "a"),
+		naming.JoinAddressName(mts[mt5MP].name, "a")}
+	names = append(names, addWSName(naming.JoinAddressName(mts[mt2MP].name, "a"))...)
 	// We now have 3 mount tables prepared to serve mt2/a
-	testResolveToMountTable(t, r, ns, "mt2/a",
-		naming.JoinAddressName(mts[mt2MP].name, "a"),
-		naming.JoinAddressName(mts[mt4MP].name, "a"),
-		naming.JoinAddressName(mts[mt5MP].name, "a"))
-	testResolve(t, r, ns, "mt2", mts[mt2MP].name, mts[mt4MP].name, mts[mt5MP].name)
+	testResolveToMountTable(t, r, ns, "mt2/a", names...)
+	names = []string{mts[mt4MP].name, mts[mt5MP].name}
+	names = append(names, addWSName(mts[mt2MP].name)...)
+	testResolve(t, r, ns, "mt2", names...)
 }
 
 // TestNestedMounts tests some more deeply nested mounts
@@ -380,15 +382,15 @@
 
 	// Set up some nested mounts and verify resolution.
 	for _, m := range []string{"mt4/foo", "mt4/foo/bar"} {
-		testResolve(t, r, ns, m, mts[m].name)
+		testResolve(t, r, ns, m, addWSName(mts[m].name)...)
 	}
 
 	testResolveToMountTable(t, r, ns, "mt4/foo",
-		naming.JoinAddressName(mts[mt4MP].name, "foo"))
+		addWSName(naming.JoinAddressName(mts[mt4MP].name, "foo"))...)
 	testResolveToMountTable(t, r, ns, "mt4/foo/bar",
-		naming.JoinAddressName(mts["mt4/foo"].name, "bar"))
+		addWSName(naming.JoinAddressName(mts["mt4/foo"].name, "bar"))...)
 	testResolveToMountTable(t, r, ns, "mt4/foo/baz",
-		naming.JoinAddressName(mts["mt4/foo"].name, "baz"))
+		addWSName(naming.JoinAddressName(mts["mt4/foo"].name, "baz"))...)
 }
 
 // TestServers tests invoking RPCs on simple servers
@@ -402,16 +404,16 @@
 
 	// Let's run some non-mount table services
 	for _, j := range []string{j1MP, j2MP, j3MP} {
-		testResolve(t, r, ns, j, jokes[j].name)
+		testResolve(t, r, ns, j, addWSName(jokes[j].name)...)
 		knockKnock(t, r, j)
 		globalName := naming.JoinAddressName(mts["mt4"].name, j)
 		disp := &dispatcher{}
 		gj := "g_" + j
 		jokes[gj] = runServer(t, r, disp, globalName)
-		testResolve(t, r, ns, "mt4/"+j, jokes[gj].name)
+		testResolve(t, r, ns, "mt4/"+j, addWSName(jokes[gj].name)...)
 		knockKnock(t, r, "mt4/"+j)
-		testResolveToMountTable(t, r, ns, "mt4/"+j, globalName)
-		testResolveToMountTable(t, r, ns, "mt4/"+j+"/garbage", globalName+"/garbage")
+		testResolveToMountTable(t, r, ns, "mt4/"+j, addWSName(globalName)...)
+		testResolveToMountTable(t, r, ns, "mt4/"+j+"/garbage", addWSName(globalName+"/garbage")...)
 	}
 }
 
@@ -557,7 +559,8 @@
 		boom(t, "Failed to Mount %s: %s", m, err)
 	}
 
-	testResolve(t, r, ns, "c1", c1.name)
+	// Since c1 was mounted with the Serve call, it will have both the tcp and ws endpoints.
+	testResolve(t, r, ns, "c1", addWSName(c1.name)...)
 	testResolve(t, r, ns, "c1/c2", c1.name)
 	testResolve(t, r, ns, "c1/c3", c3.name)
 	testResolve(t, r, ns, "c1/c3/c4", c1.name)
@@ -620,56 +623,3 @@
 		t.Errorf("namespace.New should have failed with an unrooted name")
 	}
 }
-
-func bless(blesser, delegate security.Principal, extension string) {
-	b, err := blesser.Bless(delegate.PublicKey(), blesser.BlessingStore().Default(), extension, security.UnconstrainedUse())
-	if err != nil {
-		panic(err)
-	}
-	delegate.BlessingStore().SetDefault(b)
-}
-
-func TestRootBlessing(t *testing.T) {
-	// We need the default runtime for the server-side mounttable code
-	// which references rt.R() to create new endpoints
-	cr := rt.Init()
-	r, _ := rt.New() // We use a different runtime for the client side.
-
-	proot := sectest.NewPrincipal("root")
-	bless(proot, r.Principal(), "server")
-	bless(proot, cr.Principal(), "client")
-
-	cr.Principal().AddToRoots(proot.BlessingStore().Default())
-	r.Principal().AddToRoots(proot.BlessingStore().Default())
-
-	root, mts, _, stopper := createNamespace(t, r)
-	defer stopper()
-	ns := r.Namespace()
-
-	name := naming.Join(root.name, mt2MP)
-	// First check with a non-matching blessing pattern.
-	_, err := ns.Resolve(r.NewContext(), name, naming.RootBlessingPatternOpt("root/foobar"))
-	if !verror.Is(err, verror.NoAccess.ID) {
-		t.Errorf("Resolve expected NoAccess error, got %v", err)
-	}
-	_, err = ns.ResolveToMountTable(r.NewContext(), name, naming.RootBlessingPatternOpt("root/foobar"))
-	if !verror.Is(err, verror.NoAccess.ID) {
-		t.Errorf("ResolveToMountTable expected NoAccess error, got %v", err)
-	}
-
-	// Now check a matching pattern.
-	testResolveWithPattern(t, r, ns, name, naming.RootBlessingPatternOpt("root/server"), mts[mt2MP].name)
-	testResolveToMountTableWithPattern(t, r, ns, name, naming.RootBlessingPatternOpt("root/server"), name)
-
-	// After successful lookup it should be cached, so the pattern doesn't matter.
-	testResolveWithPattern(t, r, ns, name, naming.RootBlessingPatternOpt("root/foobar"), mts[mt2MP].name)
-
-	// Test calling a method.
-	jokeName := naming.Join(root.name, mt4MP, j1MP)
-	runServer(t, r, &dispatcher{}, naming.Join(mts["mt4"].name, j1MP))
-	_, err = r.Client().StartCall(r.NewContext(), "[root/foobar]"+jokeName, "KnockKnock", nil)
-	if err == nil {
-		t.Errorf("StartCall expected NoAccess error, got %v", err)
-	}
-	knockKnock(t, r, "[root/server]"+jokeName)
-}
diff --git a/runtimes/google/naming/namespace/mount.go b/runtimes/google/naming/namespace/mount.go
index f82d5b3..ac2e875 100644
--- a/runtimes/google/naming/namespace/mount.go
+++ b/runtimes/google/naming/namespace/mount.go
@@ -3,6 +3,8 @@
 import (
 	"time"
 
+	inaming "veyron.io/veyron/veyron/runtimes/google/naming"
+
 	"veyron.io/veyron/veyron2/context"
 	"veyron.io/veyron/veyron2/ipc"
 	"veyron.io/veyron/veyron2/naming"
@@ -10,30 +12,93 @@
 	"veyron.io/veyron/veyron2/vlog"
 )
 
+type status struct {
+	id  string
+	err error
+}
+
 // mountIntoMountTable mounts a single server into a single mount table.
-func mountIntoMountTable(ctx context.T, client ipc.Client, name, server string, ttl time.Duration, flags naming.MountFlag) error {
+func mountIntoMountTable(ctx context.T, client ipc.Client, name, server string, ttl time.Duration, flags naming.MountFlag, id string) (s status) {
+	s.id = id
 	ctx, _ = ctx.WithTimeout(callTimeout)
 	call, err := client.StartCall(ctx, name, "Mount", []interface{}{server, uint32(ttl.Seconds()), flags}, options.NoResolve(true))
+	s.err = err
 	if err != nil {
-		return err
+		return
 	}
 	if ierr := call.Finish(&err); ierr != nil {
-		return ierr
+		s.err = ierr
 	}
-	return err
+	return
 }
 
 // unmountFromMountTable removes a single mounted server from a single mount table.
-func unmountFromMountTable(ctx context.T, client ipc.Client, name, server string) error {
+func unmountFromMountTable(ctx context.T, client ipc.Client, name, server string, id string) (s status) {
+	s.id = id
 	ctx, _ = ctx.WithTimeout(callTimeout)
 	call, err := client.StartCall(ctx, name, "Unmount", []interface{}{server}, options.NoResolve(true))
+	s.err = err
+	if err != nil {
+		return
+	}
+	if ierr := call.Finish(&err); ierr != nil {
+		s.err = ierr
+	}
+	return
+}
+
+// nameToRID converts a name to a routing ID string. If a routing ID can't be obtained,
+// it just returns the name.
+func nameToRID(name string) string {
+	address, _ := naming.SplitAddressName(name)
+	if ep, err := inaming.NewEndpoint(address); err == nil {
+		return ep.RID.String()
+	}
+	return name
+}
+
+// collectStati collects n status messages from channel c and returns an error if, for
+// any id, there is no successful reply.
+func collectStati(c chan status, n int) error {
+	// Make a map indexed by the routing id (or address if routing id not found) of
+	// each mount table.  A mount table may be reachable via multiple addresses but
+	// each address should have the same routing id.  We should only return an error
+	// if any of the ids had no successful mounts.
+	statusByID := make(map[string]error)
+	// Get the status of each request.
+	for i := 0; i < n; i++ {
+		s := <-c
+		if _, ok := statusByID[s.id]; !ok || s.err == nil {
+			statusByID[s.id] = s.err
+		}
+	}
+	// Return any error.
+	for _, s := range statusByID {
+		if s != nil {
+			return s
+		}
+	}
+	return nil
+}
+
+// dispatch executes f in parallel for each mount table implementing mTName.
+func (ns *namespace) dispatch(ctx context.T, mTName string, f func(context.T, string, string) status) error {
+	// Resolve to all the mount tables implementing name.
+	mts, err := ns.ResolveToMountTable(ctx, mTName)
 	if err != nil {
 		return err
 	}
-	if ierr := call.Finish(&err); ierr != nil {
-		return ierr
+	// Apply f to each of the returned mount tables.
+	c := make(chan status, len(mts))
+	for _, mt := range mts {
+		go func(mt string) {
+			c <- f(ctx, mt, nameToRID(mt))
+		}(mt)
 	}
-	return err
+	finalerr := collectStati(c, len(mts))
+	// Forget any previous cached information about these names.
+	ns.resolutionCache.forget(mts)
+	return finalerr
 }
 
 func (ns *namespace) Mount(ctx context.T, name, server string, ttl time.Duration, opts ...naming.MountOpt) error {
@@ -53,55 +118,22 @@
 			}
 		}
 	}
-
-	// Resolve to all the mount tables implementing name.
-	mtServers, err := ns.ResolveToMountTable(ctx, name)
-	if err != nil {
-		return err
-	}
 	// Mount the server in all the returned mount tables.
-	c := make(chan error, len(mtServers))
-	for _, mt := range mtServers {
-		go func() {
-			c <- mountIntoMountTable(ctx, ns.rt.Client(), mt, server, ttl, flags)
-		}()
+	f := func(ctx context.T, mt, id string) status {
+		return mountIntoMountTable(ctx, ns.rt.Client(), mt, server, ttl, flags, id)
 	}
-	// Return error if any mounts failed, since otherwise we'll get
-	// inconsistent resolutions down the road.
-	var finalerr error
-	for _ = range mtServers {
-		if err := <-c; err != nil {
-			finalerr = err
-		}
-	}
-	vlog.VI(1).Infof("Mount(%s, %s) -> %v", name, server, finalerr)
-	// Forget any previous cached information about these names.
-	ns.resolutionCache.forget(mtServers)
-	return finalerr
+	err := ns.dispatch(ctx, name, f)
+	vlog.VI(1).Infof("Mount(%s, %s) -> %v", name, server, err)
+	return err
 }
 
 func (ns *namespace) Unmount(ctx context.T, name, server string) error {
 	defer vlog.LogCall()()
-	mts, err := ns.ResolveToMountTable(ctx, name)
-	if err != nil {
-		return err
-	}
-
 	// Unmount the server from all the mount tables.
-	c := make(chan error, len(mts))
-	for _, mt := range mts {
-		go func() {
-			c <- unmountFromMountTable(ctx, ns.rt.Client(), mt, server)
-		}()
+	f := func(ctx context.T, mt, id string) status {
+		return unmountFromMountTable(ctx, ns.rt.Client(), mt, server, id)
 	}
-	// Return error if any mounts failed, since otherwise we'll get
-	// inconsistent resolutions down the road.
-	var finalerr error
-	for _ = range mts {
-		if err := <-c; err != nil {
-			finalerr = err
-		}
-	}
-	ns.resolutionCache.forget(mts)
-	return finalerr
+	err := ns.dispatch(ctx, name, f)
+	vlog.VI(1).Infof("Unmount(%s, %s) -> %v", name, server, err)
+	return err
 }
diff --git a/runtimes/google/naming/namespace/resolve.go b/runtimes/google/naming/namespace/resolve.go
index 8ac7773..eb69a3c 100644
--- a/runtimes/google/naming/namespace/resolve.go
+++ b/runtimes/google/naming/namespace/resolve.go
@@ -2,7 +2,6 @@
 
 import (
 	"errors"
-	"fmt"
 	"runtime"
 
 	"veyron.io/veyron/veyron2/context"
@@ -13,17 +12,11 @@
 	"veyron.io/veyron/veyron2/vlog"
 )
 
-func (ns *namespace) resolveAgainstMountTable(ctx context.T, client ipc.Client, e *naming.MountEntry, pattern string) (*naming.MountEntry, error) {
+func (ns *namespace) resolveAgainstMountTable(ctx context.T, client ipc.Client, e *naming.MountEntry) (*naming.MountEntry, error) {
 	// Try each server till one answers.
 	finalErr := errors.New("no servers to resolve query")
 	for _, s := range e.Servers {
-		var pattern_and_name string
 		name := naming.JoinAddressName(s.Server, e.Name)
-		if pattern != "" {
-			pattern_and_name = naming.JoinAddressName(s.Server, fmt.Sprintf("[%s]%s", pattern, e.Name))
-		} else {
-			pattern_and_name = name
-		}
 		// First check the cache.
 		if ne, err := ns.resolutionCache.lookup(name); err == nil {
 			vlog.VI(2).Infof("resolveAMT %s from cache -> %v", name, convertServersToStrings(ne.Servers, ne.Name))
@@ -31,7 +24,7 @@
 		}
 		// Not in cache, call the real server.
 		callCtx, _ := ctx.WithTimeout(callTimeout)
-		call, err := client.StartCall(callCtx, pattern_and_name, "ResolveStepX", nil, options.NoResolve(true))
+		call, err := client.StartCall(callCtx, name, "ResolveStepX", nil, options.NoResolve(true))
 		if err != nil {
 			finalErr = err
 			vlog.VI(2).Infof("ResolveStep.StartCall %s failed: %s", name, err)
@@ -68,7 +61,7 @@
 }
 
 // ResolveX implements veyron2/naming.Namespace.
-func (ns *namespace) ResolveX(ctx context.T, name string, opts ...naming.ResolveOpt) (*naming.MountEntry, error) {
+func (ns *namespace) ResolveX(ctx context.T, name string) (*naming.MountEntry, error) {
 	defer vlog.LogCall()()
 	e, _ := ns.rootMountEntry(name)
 	if vlog.V(2) {
@@ -79,7 +72,6 @@
 	if len(e.Servers) == 0 {
 		return nil, verror.Make(naming.ErrNoSuchName, ctx, name)
 	}
-	pattern := getRootPattern(opts)
 	// Iterate walking through mount table servers.
 	for remaining := ns.maxResolveDepth; remaining > 0; remaining-- {
 		vlog.VI(2).Infof("ResolveX(%s) loop %v", name, *e)
@@ -89,7 +81,7 @@
 		}
 		var err error
 		curr := e
-		if e, err = ns.resolveAgainstMountTable(ctx, ns.rt.Client(), curr, pattern); err != nil {
+		if e, err = ns.resolveAgainstMountTable(ctx, ns.rt.Client(), curr); err != nil {
 			// If the name could not be found in the mount table, return an error.
 			if verror.Is(err, naming.ErrNoSuchNameRoot.ID) {
 				err = verror.Make(naming.ErrNoSuchName, ctx, name)
@@ -98,26 +90,20 @@
 				vlog.VI(1).Infof("ResolveX(%s) -> (NoSuchName: %v)", name, curr)
 				return nil, err
 			}
-			if verror.Is(err, verror.NoAccess.ID) {
-				vlog.VI(1).Infof("ResolveX(%s) -> (NoAccess: %v)", name, curr)
-				return nil, err
-
-			}
 			// Any other failure (server not found, no ResolveStep
 			// method, etc.) are a sign that iterative resolution can
 			// stop.
 			vlog.VI(1).Infof("ResolveX(%s) -> %v", name, curr)
 			return curr, nil
 		}
-		pattern = ""
 	}
 	return nil, verror.Make(naming.ErrResolutionDepthExceeded, ctx)
 }
 
 // Resolve implements veyron2/naming.Namespace.
-func (ns *namespace) Resolve(ctx context.T, name string, opts ...naming.ResolveOpt) ([]string, error) {
+func (ns *namespace) Resolve(ctx context.T, name string) ([]string, error) {
 	defer vlog.LogCall()()
-	e, err := ns.ResolveX(ctx, name, opts...)
+	e, err := ns.ResolveX(ctx, name)
 	if err != nil {
 		return nil, err
 	}
@@ -125,7 +111,7 @@
 }
 
 // ResolveToMountTableX implements veyron2/naming.Namespace.
-func (ns *namespace) ResolveToMountTableX(ctx context.T, name string, opts ...naming.ResolveOpt) (*naming.MountEntry, error) {
+func (ns *namespace) ResolveToMountTableX(ctx context.T, name string) (*naming.MountEntry, error) {
 	defer vlog.LogCall()()
 	e, _ := ns.rootMountEntry(name)
 	if vlog.V(2) {
@@ -136,7 +122,6 @@
 	if len(e.Servers) == 0 {
 		return nil, verror.Make(naming.ErrNoMountTable, ctx)
 	}
-	pattern := getRootPattern(opts)
 	last := e
 	for remaining := ns.maxResolveDepth; remaining > 0; remaining-- {
 		vlog.VI(2).Infof("ResolveToMountTableX(%s) loop %v", name, e)
@@ -147,7 +132,7 @@
 			vlog.VI(1).Infof("ResolveToMountTableX(%s) -> %v", name, last)
 			return last, nil
 		}
-		if e, err = ns.resolveAgainstMountTable(ctx, ns.rt.Client(), e, pattern); err != nil {
+		if e, err = ns.resolveAgainstMountTable(ctx, ns.rt.Client(), e); err != nil {
 			if verror.Is(err, naming.ErrNoSuchNameRoot.ID) {
 				vlog.VI(1).Infof("ResolveToMountTableX(%s) -> %v (NoSuchRoot: %v)", name, last, curr)
 				return last, nil
@@ -172,15 +157,14 @@
 			return nil, err
 		}
 		last = curr
-		pattern = ""
 	}
 	return nil, verror.Make(naming.ErrResolutionDepthExceeded, ctx)
 }
 
 // ResolveToMountTable implements veyron2/naming.Namespace.
-func (ns *namespace) ResolveToMountTable(ctx context.T, name string, opts ...naming.ResolveOpt) ([]string, error) {
+func (ns *namespace) ResolveToMountTable(ctx context.T, name string) ([]string, error) {
 	defer vlog.LogCall()()
-	e, err := ns.ResolveToMountTableX(ctx, name, opts...)
+	e, err := ns.ResolveToMountTableX(ctx, name)
 	if err != nil {
 		return nil, err
 	}
@@ -270,12 +254,3 @@
 	}
 	return flushed
 }
-
-func getRootPattern(opts []naming.ResolveOpt) string {
-	for _, opt := range opts {
-		if pattern, ok := opt.(naming.RootBlessingPatternOpt); ok {
-			return string(pattern)
-		}
-	}
-	return ""
-}
diff --git a/runtimes/google/rt/mgmt_test.go b/runtimes/google/rt/mgmt_test.go
index cd3db2a..defc3c7 100644
--- a/runtimes/google/rt/mgmt_test.go
+++ b/runtimes/google/rt/mgmt_test.go
@@ -116,7 +116,7 @@
 // TestNoWaiters verifies that the child process exits in the absence of any
 // wait channel being registered with its runtime.
 func TestNoWaiters(t *testing.T) {
-	sh := modules.NewShell(noWaitersCmd)
+	sh := modules.NewShell()
 	defer sh.Cleanup(os.Stderr, os.Stderr)
 	h, err := sh.Start(noWaitersCmd, nil)
 	if err != nil {
@@ -143,7 +143,7 @@
 // TestForceStop verifies that ForceStop causes the child process to exit
 // immediately.
 func TestForceStop(t *testing.T) {
-	sh := modules.NewShell(forceStopCmd)
+	sh := modules.NewShell()
 	defer sh.Cleanup(os.Stderr, os.Stderr)
 	h, err := sh.Start(forceStopCmd, nil)
 	if err != nil {
@@ -295,7 +295,7 @@
 
 	childcreds := security.NewVeyronCredentials(r.Principal(), appCmd)
 	configServer, configServiceName, ch := createConfigServer(t, r)
-	sh := modules.NewShell(appCmd)
+	sh := modules.NewShell()
 	sh.SetVar(consts.VeyronCredentials, childcreds)
 	sh.SetConfigKey(mgmt.ParentNameConfigKey, configServiceName)
 	sh.SetConfigKey(mgmt.ProtocolConfigKey, "tcp")
diff --git a/runtimes/google/rt/rt_test.go b/runtimes/google/rt/rt_test.go
index 84085db..3f244fb 100644
--- a/runtimes/google/rt/rt_test.go
+++ b/runtimes/google/rt/rt_test.go
@@ -70,7 +70,7 @@
 }
 
 func TestInitArgs(t *testing.T) {
-	sh := modules.NewShell("child")
+	sh := modules.NewShell()
 	defer sh.Cleanup(os.Stderr, os.Stderr)
 	h, err := sh.Start("child", nil, "--logtostderr=true", "--vv=3", "--", "foobar")
 	if err != nil {
@@ -136,15 +136,13 @@
 	if err != nil {
 		return err
 	}
-	sh := modules.NewShell("principal")
-	defer sh.Cleanup(os.Stderr, os.Stderr)
-	h, err := sh.Start("principal", nil, args[1:]...)
+	sh := modules.NewShell()
+	_, err = sh.Start("principal", nil, args[1:]...)
 	if err != nil {
 		return err
 	}
-	s := expect.NewSession(nil, h.Stdout(), 1*time.Second) // time.Minute)
-	fmt.Fprintf(stdout, s.ReadLine()+"\n")
-	fmt.Fprintf(stdout, s.ReadLine()+"\n")
+	// Cleanup copies the output of sh to these Writers.
+	sh.Cleanup(stdout, stderr)
 	modules.WaitForEOF(stdin)
 	return nil
 }
@@ -191,7 +189,7 @@
 }
 
 func TestPrincipalInheritance(t *testing.T) {
-	sh := modules.NewShell("principal", "runner")
+	sh := modules.NewShell()
 	defer func() {
 		sh.Cleanup(os.Stdout, os.Stderr)
 	}()
@@ -259,7 +257,7 @@
 		t.Fatal(err)
 	}
 
-	sh := modules.NewShell("principal")
+	sh := modules.NewShell()
 	defer sh.Cleanup(os.Stderr, os.Stderr)
 
 	pubkey, err := collect(sh, "principal", nil)
diff --git a/runtimes/google/rt/shutdown_servers_test.go b/runtimes/google/rt/shutdown_servers_test.go
new file mode 100644
index 0000000..3e3f730
--- /dev/null
+++ b/runtimes/google/rt/shutdown_servers_test.go
@@ -0,0 +1,271 @@
+package rt_test
+
+import (
+	"bufio"
+	"fmt"
+	"io"
+	"os"
+	"os/signal"
+	"sync"
+	"syscall"
+
+	"veyron.io/veyron/veyron2/ipc"
+	"veyron.io/veyron/veyron2/rt"
+	"veyron.io/veyron/veyron2/vlog"
+
+	"veyron.io/veyron/veyron/lib/modules"
+	"veyron.io/veyron/veyron/lib/signals"
+	"veyron.io/veyron/veyron/profiles"
+)
+
+func init() {
+	modules.RegisterChild("simpleServerProgram", "", simpleServerProgram)
+	modules.RegisterChild("complexServerProgram", "", complexServerProgram)
+}
+
+type dummy struct{}
+
+func (*dummy) Echo(ipc.ServerContext) error { return nil }
+
+// makeServer sets up a simple dummy server.
+func makeServer() ipc.Server {
+	server, err := rt.R().NewServer()
+	if err != nil {
+		vlog.Fatalf("r.NewServer error: %s", err)
+	}
+	if _, err := server.Listen(profiles.LocalListenSpec); err != nil {
+		vlog.Fatalf("server.Listen error: %s", err)
+	}
+	if err := server.Serve("", &dummy{}, nil); err != nil {
+		vlog.Fatalf("server.Serve error: %s", err)
+	}
+	return server
+}
+
+// remoteCmdLoop listens on stdin and interprets commands sent over stdin (from
+// the parent process).
+func remoteCmdLoop(stdin io.Reader) func() {
+	done := make(chan struct{})
+	go func() {
+		defer close(done)
+		scanner := bufio.NewScanner(stdin)
+		for scanner.Scan() {
+			switch scanner.Text() {
+			case "stop":
+				rt.R().AppCycle().Stop()
+			case "forcestop":
+				fmt.Println("straight exit")
+				rt.R().AppCycle().ForceStop()
+			case "close":
+				return
+			}
+		}
+	}()
+	return func() { <-done }
+}
+
+// complexServerProgram demonstrates the recommended way to write a more
+// complex server application (with several servers, a mix of interruptible
+// and blocking cleanup, and parallel and sequential cleanup execution).
+// For a more typical server, see simpleServerProgram.
+func complexServerProgram(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+	// Initialize the runtime.  This is boilerplate.
+	r := rt.Init()
+
+	// This is part of the test setup -- we need a way to accept
+	// commands from the parent process to simulate Stop and
+	// RemoteStop commands that would normally be issued from
+	// application code.
+	go remoteCmdLoop(stdin)()
+
+	// r.Cleanup is optional, but it's a good idea to clean up, especially
+	// since it takes care of flushing the logs before exiting.
+	defer r.Cleanup()
+
+	// Create a couple servers, and start serving.
+	server1 := makeServer()
+	server2 := makeServer()
+
+	// This is how to wait for a shutdown.  In this example, a shutdown
+	// comes from a signal or a stop command.
+	var done sync.WaitGroup
+	done.Add(1)
+
+	// This is how to configure signal handling to allow clean shutdown.
+	sigChan := make(chan os.Signal, 2)
+	signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT)
+
+	// This is how to configure handling of stop commands to allow clean
+	// shutdown.
+	stopChan := make(chan string, 2)
+	r.AppCycle().WaitForStop(stopChan)
+
+	// Blocking is used to prevent the process from exiting upon receiving a
+	// second signal or stop command while critical cleanup code is
+	// executing.
+	var blocking sync.WaitGroup
+	blockingCh := make(chan struct{})
+
+	// This is how to wait for a signal or stop command and initiate the
+	// clean shutdown steps.
+	go func() {
+		// First signal received.
+		select {
+		case sig := <-sigChan:
+			// If the developer wants to take different actions
+			// depending on the type of signal, they can do it here.
+			fmt.Fprintln(stdout, "Received signal", sig)
+		case stop := <-stopChan:
+			fmt.Fprintln(stdout, "Stop", stop)
+		}
+		// This commences the cleanup stage.
+		done.Done()
+		// Wait for a second signal or stop command, and force an exit,
+		// but only once all blocking cleanup code (if any) has
+		// completed.
+		select {
+		case <-sigChan:
+		case <-stopChan:
+		}
+		<-blockingCh
+		os.Exit(1)
+	}()
+
+	// This communicates to the parent test driver process in our unit test
+	// that this server is ready and waiting on signals or stop commands.
+	// It's purely an artifact of our test setup.
+	fmt.Fprintln(stdout, "Ready")
+
+	// Wait for shutdown.
+	done.Wait()
+
+	// Stop the servers.  In this example we stop them in goroutines to
+	// parallelize the wait, but if there was a dependency between the
+	// servers, the developer can simply stop them sequentially.
+	var waitServerStop sync.WaitGroup
+	waitServerStop.Add(2)
+	go func() {
+		server1.Stop()
+		waitServerStop.Done()
+	}()
+	go func() {
+		server2.Stop()
+		waitServerStop.Done()
+	}()
+	waitServerStop.Wait()
+
+	// This is where all cleanup code should go.  By placing it at the end,
+	// we make its purpose and order of execution clear.
+
+	// This is an example of how to mix parallel and sequential cleanup
+	// steps.  Most real-world servers will likely be simpler, with either
+	// just sequential or just parallel cleanup stages.
+
+	// parallelCleanup is used to wait for all goroutines executing cleanup
+	// code in parallel to finish.
+	var parallelCleanup sync.WaitGroup
+
+	// Simulate four parallel cleanup steps, two blocking and two
+	// interruptible.
+	parallelCleanup.Add(1)
+	blocking.Add(1)
+	go func() {
+		fmt.Fprintln(stdout, "Parallel blocking cleanup1")
+		blocking.Done()
+		parallelCleanup.Done()
+	}()
+
+	parallelCleanup.Add(1)
+	blocking.Add(1)
+	go func() {
+		fmt.Fprintln(stdout, "Parallel blocking cleanup2")
+		blocking.Done()
+		parallelCleanup.Done()
+	}()
+
+	parallelCleanup.Add(1)
+	go func() {
+		fmt.Fprintln(stdout, "Parallel interruptible cleanup1")
+		parallelCleanup.Done()
+	}()
+
+	parallelCleanup.Add(1)
+	go func() {
+		fmt.Fprintln(stdout, "Parallel interruptible cleanup2")
+		parallelCleanup.Done()
+	}()
+
+	// Simulate two sequential cleanup steps, one blocking and one
+	// interruptible.
+	fmt.Fprintln(stdout, "Sequential blocking cleanup")
+	blocking.Wait()
+	close(blockingCh)
+
+	fmt.Fprintln(stdout, "Sequential interruptible cleanup")
+
+	parallelCleanup.Wait()
+	return nil
+}
+
+// simpleServerProgram demonstrates the recommended way to write a typical
+// simple server application (with one server and a clean shutdown triggered by
+// a signal or a stop command).  For an example of something more involved, see
+// complexServerProgram.
+func simpleServerProgram(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+	// Initialize the runtime.  This is boilerplate.
+	r := rt.Init()
+
+	// This is part of the test setup -- we need a way to accept
+	// commands from the parent process to simulate Stop and
+	// RemoteStop commands that would normally be issued from
+	// application code.
+	go remoteCmdLoop(stdin)()
+
+	// r.Cleanup is optional, but it's a good idea to clean up, especially
+	// since it takes care of flushing the logs before exiting.
+	//
+	// We use defer to ensure this is the last thing in the program (to
+	// avoid shutting down the runtime while it may still be in use), and to
+	// allow it to execute even if a panic occurs down the road.
+	defer r.Cleanup()
+
+	// Create a server, and start serving.
+	server := makeServer()
+
+	// This is how to wait for a shutdown.  In this example, a shutdown
+	// comes from a signal or a stop command.
+	//
+	// Note, if the developer wants to exit immediately upon receiving a
+	// signal or stop command, they can skip this, in which case the default
+	// behavior is for the process to exit.
+	waiter := signals.ShutdownOnSignals()
+
+	// This communicates to the parent test driver process in our unit test
+	// that this server is ready and waiting on signals or stop commands.
+	// It's purely an artifact of our test setup.
+	fmt.Fprintln(stdout, "Ready")
+
+	// Use defer for anything that should still execute even if a panic
+	// occurs.
+	defer fmt.Fprintln(stdout, "Deferred cleanup")
+
+	// Wait for shutdown.
+	sig := <-waiter
+	// The developer could take different actions depending on the type of
+	// signal.
+	fmt.Fprintln(stdout, "Received signal", sig)
+
+	// Cleanup code starts here.  Alternatively, these steps could be
+	// invoked through defer, but we list them here to make the order of
+	// operations obvious.
+
+	// Stop the server.
+	server.Stop()
+
+	// Note, this will not execute in cases of forced shutdown
+	// (e.g. SIGSTOP), when the process calls os.Exit (e.g. via log.Fatal),
+	// or when a panic occurs.
+	fmt.Fprintln(stdout, "Interruptible cleanup")
+
+	return nil
+}
diff --git a/runtimes/google/rt/shutdown_test.go b/runtimes/google/rt/shutdown_test.go
new file mode 100644
index 0000000..f0a57aa
--- /dev/null
+++ b/runtimes/google/rt/shutdown_test.go
@@ -0,0 +1,228 @@
+package rt_test
+
+import (
+	"fmt"
+	"io"
+	"os"
+	"syscall"
+	"testing"
+	"time"
+
+	"veyron.io/veyron/veyron2"
+
+	"veyron.io/veyron/veyron/lib/expect"
+	"veyron.io/veyron/veyron/lib/modules"
+	"veyron.io/veyron/veyron/lib/signals"
+	"veyron.io/veyron/veyron/lib/testutil"
+)
+
+var cstderr io.Writer
+
+func init() {
+	testutil.Init()
+	if testing.Verbose() {
+		cstderr = os.Stderr
+	}
+}
+
+// TestSimpleServerSignal verifies that sending a signal to the simple server
+// causes it to exit cleanly.
+func TestSimpleServerSignal(t *testing.T) {
+	sh := modules.NewShell()
+	defer sh.Cleanup(os.Stdout, cstderr)
+	h, _ := sh.Start("simpleServerProgram", nil)
+	s := expect.NewSession(t, h.Stdout(), time.Minute)
+	s.Expect("Ready")
+	syscall.Kill(h.Pid(), syscall.SIGINT)
+	s.Expect("Received signal interrupt")
+	s.Expect("Interruptible cleanup")
+	s.Expect("Deferred cleanup")
+	fmt.Fprintln(h.Stdin(), "close")
+	s.ExpectEOF()
+}
+
+// TestSimpleServerLocalStop verifies that sending a local stop command to the
+// simple server causes it to exit cleanly.
+func TestSimpleServerLocalStop(t *testing.T) {
+	sh := modules.NewShell()
+	defer sh.Cleanup(os.Stdout, cstderr)
+	h, _ := sh.Start("simpleServerProgram", nil)
+	s := expect.NewSession(t, h.Stdout(), time.Minute)
+	s.Expect("Ready")
+	fmt.Fprintln(h.Stdin(), "stop")
+	s.Expect(fmt.Sprintf("Received signal %s", veyron2.LocalStop))
+	s.Expect("Interruptible cleanup")
+	s.Expect("Deferred cleanup")
+	fmt.Fprintln(h.Stdin(), "close")
+	s.ExpectEOF()
+}
+
+// TestSimpleServerDoubleSignal verifies that sending a succession of two
+// signals to the simple server causes it to initiate the cleanup sequence on
+// the first signal and then exit immediately on the second signal.
+func TestSimpleServerDoubleSignal(t *testing.T) {
+	sh := modules.NewShell()
+	defer sh.Cleanup(os.Stdout, cstderr)
+	h, _ := sh.Start("simpleServerProgram", nil)
+	s := expect.NewSession(t, h.Stdout(), time.Minute)
+	s.Expect("Ready")
+	syscall.Kill(h.Pid(), syscall.SIGINT)
+	s.Expect("Received signal interrupt")
+	syscall.Kill(h.Pid(), syscall.SIGINT)
+	err := h.Shutdown(os.Stdout, cstderr)
+	if err == nil {
+		t.Fatalf("expected an error")
+	}
+	if got, want := err.Error(), fmt.Sprintf("exit status %d", signals.DoubleStopExitCode); got != want {
+		t.Errorf("got %q, want %q", got, want)
+	}
+}
+
+// TestSimpleServerLocalForceStop verifies that sending a local ForceStop
+// command to the simple server causes it to exit immediately.
+func TestSimpleServerLocalForceStop(t *testing.T) {
+	sh := modules.NewShell()
+	defer sh.Cleanup(os.Stdout, cstderr)
+	h, _ := sh.Start("simpleServerProgram", nil)
+	s := expect.NewSession(t, h.Stdout(), time.Minute)
+	s.Expect("Ready")
+	fmt.Fprintln(h.Stdin(), "forcestop")
+	s.Expect("straight exit")
+	err := h.Shutdown(os.Stdout, cstderr)
+	if err == nil {
+		t.Fatalf("expected an error")
+	}
+	if got, want := err.Error(), fmt.Sprintf("exit status %d", veyron2.ForceStopExitCode); got != want {
+		t.Errorf("got %q, want %q", got, want)
+	}
+}
+
+// TestSimpleServerKill demonstrates that a SIGKILL still forces the server
+// to exit regardless of our signal handling.
+func TestSimpleServerKill(t *testing.T) {
+	sh := modules.NewShell()
+	defer sh.Cleanup(os.Stdout, cstderr)
+	h, _ := sh.Start("simpleServerProgram", nil)
+	s := expect.NewSession(t, h.Stdout(), time.Minute)
+	s.Expect("Ready")
+	syscall.Kill(h.Pid(), syscall.SIGKILL)
+	err := h.Shutdown(os.Stdout, cstderr)
+	if err == nil {
+		t.Fatalf("expected an error")
+	}
+	if got, want := err.Error(), "signal: killed"; got != want {
+		t.Errorf("got %q, want %q", got, want)
+	}
+}
+
+// TestComplexServerSignal verifies that sending a signal to the complex server
+// initiates the cleanup sequence in that server (we observe the printouts
+// corresponding to all the simulated sequential/parallel and
+// blocking/interruptible shutdown steps), and then exits cleanly.
+func TestComplexServerSignal(t *testing.T) {
+	sh := modules.NewShell()
+	defer sh.Cleanup(os.Stdout, cstderr)
+	h, _ := sh.Start("complexServerProgram", nil)
+	s := expect.NewSession(t, h.Stdout(), time.Minute)
+	s.Expect("Ready")
+	syscall.Kill(h.Pid(), syscall.SIGINT)
+	s.Expect("Received signal interrupt")
+	s.ExpectSetRE("Sequential blocking cleanup",
+		"Sequential interruptible cleanup",
+		"Parallel blocking cleanup1",
+		"Parallel blocking cleanup2",
+		"Parallel interruptible cleanup1",
+		"Parallel interruptible cleanup2")
+	fmt.Fprintln(h.Stdin(), "close")
+	s.ExpectEOF()
+}
+
+// TestComplexServerLocalStop verifies that sending a local stop command to the
+// complex server initiates the cleanup sequence in that server (we observe the
+// printouts corresponding to all the simulated sequential/parallel and
+// blocking/interruptible shutdown steps), and then exits cleanly.
+func TestComplexServerLocalStop(t *testing.T) {
+	sh := modules.NewShell()
+	defer sh.Cleanup(os.Stdout, cstderr)
+	h, _ := sh.Start("complexServerProgram", nil)
+	s := expect.NewSession(t, h.Stdout(), time.Minute)
+	s.Expect("Ready")
+
+	fmt.Fprintln(h.Stdin(), "stop")
+	s.Expect(fmt.Sprintf("Stop %s", veyron2.LocalStop))
+	s.ExpectSetRE(
+		"Sequential blocking cleanup",
+		"Sequential interruptible cleanup",
+		"Parallel blocking cleanup1",
+		"Parallel blocking cleanup2",
+		"Parallel interruptible cleanup1",
+		"Parallel interruptible cleanup2",
+	)
+	fmt.Fprintln(h.Stdin(), "close")
+	s.ExpectEOF()
+}
+
+// TestComplexServerDoubleSignal verifies that sending a succession of two
+// signals to the complex server has the expected effect: the first signal
+// initiates the cleanup steps and the second signal kills the process, but only
+// after the blocking shutdown steps were allowed to complete (as observed by
+// the corresponding printouts from the server).  Note that we have no
+// expectations on whether or not the interruptible shutdown steps execute.
+func TestComplexServerDoubleSignal(t *testing.T) {
+	sh := modules.NewShell()
+	defer sh.Cleanup(os.Stdout, cstderr)
+	h, _ := sh.Start("complexServerProgram", nil)
+	s := expect.NewSession(t, h.Stdout(), time.Minute)
+	s.Expect("Ready")
+	syscall.Kill(h.Pid(), syscall.SIGINT)
+	s.Expect("Received signal interrupt")
+	syscall.Kill(h.Pid(), syscall.SIGINT)
+	s.ExpectSetEventuallyRE(
+		"Sequential blocking cleanup",
+		"Parallel blocking cleanup1",
+		"Parallel blocking cleanup2")
+	err := h.Shutdown(os.Stdout, cstderr)
+	if err == nil {
+		t.Fatalf("expected an error")
+	}
+	if got, want := err.Error(), fmt.Sprintf("exit status %d", signals.DoubleStopExitCode); got != want {
+		t.Errorf("got %q, want %q", got, want)
+	}
+}
+
+// TestComplexServerLocalForceStop verifies that sending a local ForceStop
+// command to the complex server forces it to exit immediately.
+func TestComplexServerLocalForceStop(t *testing.T) {
+	sh := modules.NewShell()
+	defer sh.Cleanup(os.Stdout, cstderr)
+	h, _ := sh.Start("complexServerProgram", nil)
+	s := expect.NewSession(t, h.Stdout(), time.Minute)
+	s.Expect("Ready")
+	fmt.Fprintln(h.Stdin(), "forcestop")
+	s.Expect("straight exit")
+	err := h.Shutdown(os.Stdout, cstderr)
+	if err == nil {
+		t.Fatalf("expected an error")
+	}
+	if got, want := err.Error(), fmt.Sprintf("exit status %d", veyron2.ForceStopExitCode); got != want {
+		t.Errorf("got %q, want %q", got, want)
+	}
+}
+
+// TestComplexServerKill demonstrates that a SIGKILL still forces the server to
+// exit regardless of our signal handling.
+func TestComplexServerKill(t *testing.T) {
+	sh := modules.NewShell()
+	defer sh.Cleanup(os.Stdout, cstderr)
+	h, _ := sh.Start("complexServerProgram", nil)
+	s := expect.NewSession(t, h.Stdout(), time.Minute)
+	s.Expect("Ready")
+	syscall.Kill(h.Pid(), syscall.SIGKILL)
+	err := h.Shutdown(os.Stdout, cstderr)
+	if err == nil {
+		t.Fatalf("expected an error")
+	}
+	if got, want := err.Error(), "signal: killed"; got != want {
+		t.Errorf("got %q, want %q", got, want)
+	}
+}
diff --git a/runtimes/google/rt/signal.go b/runtimes/google/rt/signal.go
index fc13847..0e5d607 100644
--- a/runtimes/google/rt/signal.go
+++ b/runtimes/google/rt/signal.go
@@ -4,6 +4,7 @@
 	"os"
 	"os/signal"
 	"syscall"
+
 	"veyron.io/veyron/veyron2/vlog"
 )
 
diff --git a/runtimes/google/rt/signal_test.go b/runtimes/google/rt/signal_test.go
index de2d98c..df93748 100644
--- a/runtimes/google/rt/signal_test.go
+++ b/runtimes/google/rt/signal_test.go
@@ -73,7 +73,7 @@
 }
 
 func TestWithRuntime(t *testing.T) {
-	sh := modules.NewShell("withRuntime")
+	sh := modules.NewShell()
 	defer sh.Cleanup(os.Stderr, os.Stderr)
 	h, err := sh.Start("withRuntime", nil)
 	if err != nil {
@@ -90,7 +90,7 @@
 }
 
 func TestWithoutRuntime(t *testing.T) {
-	sh := modules.NewShell("withoutRuntime")
+	sh := modules.NewShell()
 	defer sh.Cleanup(os.Stderr, os.Stderr)
 	h, err := sh.Start("withoutRuntime", nil)
 	if err != nil {
diff --git a/runtimes/google/testing/mocks/naming/namespace.go b/runtimes/google/testing/mocks/naming/namespace.go
index f6b0ba0..bff47f6 100644
--- a/runtimes/google/testing/mocks/naming/namespace.go
+++ b/runtimes/google/testing/mocks/naming/namespace.go
@@ -57,7 +57,7 @@
 	return nil
 }
 
-func (ns *namespace) Resolve(ctx context.T, name string, opts ...naming.ResolveOpt) ([]string, error) {
+func (ns *namespace) Resolve(ctx context.T, name string) ([]string, error) {
 	defer vlog.LogCall()()
 	if address, _ := naming.SplitAddressName(name); len(address) > 0 {
 		return []string{name}, nil
@@ -77,7 +77,7 @@
 	return nil, verror.NoExistf("Resolve name %q not found in %v", name, ns.mounts)
 }
 
-func (ns *namespace) ResolveX(ctx context.T, name string, opts ...naming.ResolveOpt) (*naming.MountEntry, error) {
+func (ns *namespace) ResolveX(ctx context.T, name string) (*naming.MountEntry, error) {
 	defer vlog.LogCall()()
 	e := new(naming.MountEntry)
 	if address, _ := naming.SplitAddressName(name); len(address) > 0 {
@@ -98,14 +98,14 @@
 	return nil, verror.NoExistf("Resolve name %q not found in %v", name, ns.mounts)
 }
 
-func (ns *namespace) ResolveToMountTableX(ctx context.T, name string, opts ...naming.ResolveOpt) (*naming.MountEntry, error) {
+func (ns *namespace) ResolveToMountTableX(ctx context.T, name string) (*naming.MountEntry, error) {
 	defer vlog.LogCall()()
 	// TODO(mattr): Implement this method for tests that might need it.
 	panic("ResolveToMountTable not implemented")
 	return nil, nil
 }
 
-func (ns *namespace) ResolveToMountTable(ctx context.T, name string, opts ...naming.ResolveOpt) ([]string, error) {
+func (ns *namespace) ResolveToMountTable(ctx context.T, name string) ([]string, error) {
 	defer vlog.LogCall()()
 	// TODO(mattr): Implement this method for tests that might need it.
 	panic("ResolveToMountTable not implemented")
diff --git a/security/agent/agentd/main.go b/security/agent/agentd/main.go
index e76941e..ed9fe2e 100644
--- a/security/agent/agentd/main.go
+++ b/security/agent/agentd/main.go
@@ -3,6 +3,7 @@
 import (
 	"flag"
 	"fmt"
+	"io"
 	"os"
 	"os/exec"
 	"os/signal"
@@ -115,8 +116,7 @@
 }
 
 func handleDoesNotExist(dir string) (security.Principal, []byte, error) {
-	fmt.Println("Private key file does not exist. Creating new private key...")
-	pass, err := getPassword("Enter passphrase (entering nothing will store unencrypted): ")
+	pass, err := getPassword("Private key file does not exist. Creating new private key...\nEnter passphrase (entering nothing will store unencrypted): ")
 	if err != nil {
 		return nil, nil, fmt.Errorf("failed to read passphrase: %v", err)
 	}
@@ -129,8 +129,7 @@
 }
 
 func handlePassphrase(dir string) (security.Principal, []byte, error) {
-	fmt.Println("Private key file is encrypted. Please enter passphrase.")
-	pass, err := getPassword("Enter passphrase: ")
+	pass, err := getPassword("Private key file is encrypted. Please enter passphrase.\nEnter passphrase: ")
 	if err != nil {
 		return nil, nil, fmt.Errorf("failed to read passphrase: %v", err)
 	}
@@ -139,6 +138,10 @@
 }
 
 func getPassword(prompt string) ([]byte, error) {
+	if !terminal.IsTerminal(int(os.Stdin.Fd())) {
+		// If the standard input is not a terminal, the password is obtained by reading a line from it.
+		return readPassword()
+	}
 	fmt.Printf(prompt)
 	stop := make(chan bool)
 	defer close(stop)
@@ -147,9 +150,43 @@
 		return nil, err
 	}
 	go catchTerminationSignals(stop, state)
+	defer fmt.Printf("\n")
 	return terminal.ReadPassword(int(os.Stdin.Fd()))
 }
 
+// readPassword reads form Stdin until it sees '\n' or EOF.
+func readPassword() ([]byte, error) {
+	var pass []byte
+	var total int
+	for {
+		b := make([]byte, 1)
+		count, err := os.Stdin.Read(b)
+		if err != nil && err != io.EOF {
+			return nil, err
+		}
+		if err == io.EOF || b[0] == '\n' {
+			return pass[:total], nil
+		}
+		total += count
+		pass = secureAppend(pass, b)
+	}
+}
+
+func secureAppend(s, t []byte) []byte {
+	res := append(s, t...)
+	if len(res) > cap(s) {
+		// When append needs to allocate a new array, clear out the old one.
+		for i := range s {
+			s[i] = '0'
+		}
+	}
+	// Clear out the second array.
+	for i := range t {
+		t[i] = '0'
+	}
+	return res
+}
+
 // catchTerminationSignals catches signals to allow us to turn terminal echo back on.
 func catchTerminationSignals(stop <-chan bool, state *terminal.State) {
 	var successErrno syscall.Errno
diff --git a/security/blessingstore.go b/security/blessingstore.go
index 1a7540d..1adfbe2 100644
--- a/security/blessingstore.go
+++ b/security/blessingstore.go
@@ -175,22 +175,103 @@
 	}
 }
 
-// newPersistingBlessingStore returns a security.BlessingStore for a principal
-// that is initialized with the persisted data. The returned security.BlessingStore
-// also persists any updates to its state.
-func newPersistingBlessingStore(serializer SerializerReaderWriter, signer serialization.Signer) (security.BlessingStore, error) {
-	verifyBlessings := func(wb *blessings, key security.PublicKey) error {
-		if wb == nil {
-			return nil
+// TODO(ataly, ashankar): Get rid of this struct once we have switched all credentials
+// directories to the new serialization format.
+type oldState struct {
+	Store   map[security.BlessingPattern]security.WireBlessings
+	Default security.WireBlessings
+}
+
+// TODO(ataly, ashankar): Get rid of this method once we have switched all
+// credentials directories to the new serialization format.
+func (bs *blessingStore) tryOldFormat() bool {
+	var empty security.WireBlessings
+	if len(bs.state.Store) == 0 {
+		return bs.state.Default == nil || reflect.DeepEqual(bs.state.Default.Value, empty)
+	}
+	for _, wb := range bs.state.Store {
+		if len(wb.Value.CertificateChains) == 0 {
+			return true
 		}
+	}
+	return false
+}
+
+func (bs *blessingStore) verifyState() error {
+	verifyBlessings := func(wb *blessings, key security.PublicKey) error {
 		if err := wb.Verify(); err != nil {
 			return err
 		}
-		if b := wb.Blessings(); !reflect.DeepEqual(b.PublicKey(), key) {
+		if b := wb.Blessings(); b != nil && !reflect.DeepEqual(b.PublicKey(), key) {
 			return fmt.Errorf("read Blessings: %v that are not for provided PublicKey: %v", b, key)
 		}
 		return nil
 	}
+	for _, wb := range bs.state.Store {
+		if err := verifyBlessings(wb, bs.publicKey); err != nil {
+			return err
+		}
+	}
+	if bs.state.Default != nil {
+		if err := verifyBlessings(bs.state.Default, bs.publicKey); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+// TODO(ataly, ashankar): Get rid of this method once we have switched all
+// credentials directories to the new serialization format.
+func (bs *blessingStore) deserializeOld() error {
+	data, signature, err := bs.serializer.Readers()
+	if err != nil {
+		return err
+	}
+	if data == nil && signature == nil {
+		return nil
+	}
+	var old oldState
+	if err := decodeFromStorage(&old, data, signature, bs.signer.PublicKey()); err != nil {
+		return err
+	}
+	for p, wire := range old.Store {
+		bs.state.Store[p] = &blessings{Value: wire}
+	}
+	bs.state.Default = &blessings{Value: old.Default}
+
+	if err := bs.verifyState(); err != nil {
+		return err
+	}
+	// Save the blessingstore in the new serialization format. This will ensure
+	// that all credentials directories in the old format will switch to the new
+	// format.
+	if err := bs.save(); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (bs *blessingStore) deserialize() error {
+	data, signature, err := bs.serializer.Readers()
+	if err != nil {
+		return err
+	}
+	if data == nil && signature == nil {
+		return nil
+	}
+	if err := decodeFromStorage(&bs.state, data, signature, bs.signer.PublicKey()); err == nil && !bs.tryOldFormat() {
+		return bs.verifyState()
+	}
+	if err := bs.deserializeOld(); err != nil {
+		return err
+	}
+	return nil
+}
+
+// newPersistingBlessingStore returns a security.BlessingStore for a principal
+// that is initialized with the persisted data. The returned security.BlessingStore
+// also persists any updates to its state.
+func newPersistingBlessingStore(serializer SerializerReaderWriter, signer serialization.Signer) (security.BlessingStore, error) {
 	if serializer == nil || signer == nil {
 		return nil, errors.New("persisted data or signer is not specified")
 	}
@@ -200,21 +281,7 @@
 		serializer: serializer,
 		signer:     signer,
 	}
-	data, signature, err := bs.serializer.Readers()
-	if err != nil {
-		return nil, err
-	}
-	if data != nil && signature != nil {
-		if err := decodeFromStorage(&bs.state, data, signature, bs.signer.PublicKey()); err != nil {
-			return nil, err
-		}
-	}
-	for _, wb := range bs.state.Store {
-		if err := verifyBlessings(wb, bs.publicKey); err != nil {
-			return nil, err
-		}
-	}
-	if err := verifyBlessings(bs.state.Default, bs.publicKey); err != nil {
+	if err := bs.deserialize(); err != nil {
 		return nil, err
 	}
 	return bs, nil
diff --git a/security/principal.go b/security/principal.go
index 364ee96..4ebf6e6 100644
--- a/security/principal.go
+++ b/security/principal.go
@@ -43,17 +43,9 @@
 	if err := mkDir(dir); err != nil {
 		return nil, err
 	}
-	roots, err := NewFileSerializer(path.Join(dir, blessingRootsDataFile), path.Join(dir, blessingRootsSigFile))
-	if err != nil {
-		return nil, err
-	}
-	store, err := NewFileSerializer(path.Join(dir, blessingStoreDataFile), path.Join(dir, blessingStoreSigFile))
-	if err != nil {
-		return nil, err
-	}
 	return &PrincipalStateSerializer{
-		BlessingRoots: roots,
-		BlessingStore: store,
+		BlessingRoots: NewFileSerializer(path.Join(dir, blessingRootsDataFile), path.Join(dir, blessingRootsSigFile)),
+		BlessingStore: NewFileSerializer(path.Join(dir, blessingStoreDataFile), path.Join(dir, blessingStoreSigFile)),
 	}, nil
 }
 
diff --git a/security/serializer_reader_writer.go b/security/serializer_reader_writer.go
index 633456d..6a7c03c 100644
--- a/security/serializer_reader_writer.go
+++ b/security/serializer_reader_writer.go
@@ -18,48 +18,44 @@
 
 // FileSerializer implements SerializerReaderWriter that persists state to files.
 type FileSerializer struct {
-	data      *os.File
-	signature *os.File
-
 	dataFilePath      string
 	signatureFilePath string
 }
 
 // NewFileSerializer creates a FileSerializer with the given data and signature files.
-func NewFileSerializer(dataFilePath, signatureFilePath string) (*FileSerializer, error) {
-	data, err := os.Open(dataFilePath)
-	if err != nil && !os.IsNotExist(err) {
-		return nil, err
-	}
-	signature, err := os.Open(signatureFilePath)
-	if err != nil && !os.IsNotExist(err) {
-		return nil, err
-	}
+func NewFileSerializer(dataFilePath, signatureFilePath string) *FileSerializer {
 	return &FileSerializer{
-		data:              data,
-		signature:         signature,
 		dataFilePath:      dataFilePath,
 		signatureFilePath: signatureFilePath,
-	}, nil
+	}
 }
 
 func (fs *FileSerializer) Readers() (io.ReadCloser, io.ReadCloser, error) {
-	if fs.data == nil || fs.signature == nil {
+	data, err := os.Open(fs.dataFilePath)
+	if err != nil && !os.IsNotExist(err) {
+		return nil, nil, err
+	}
+	signature, err := os.Open(fs.signatureFilePath)
+	if err != nil && !os.IsNotExist(err) {
+		return nil, nil, err
+	}
+	if data == nil || signature == nil {
 		return nil, nil, nil
 	}
-	return fs.data, fs.signature, nil
+	return data, signature, nil
 }
 
 func (fs *FileSerializer) Writers() (io.WriteCloser, io.WriteCloser, error) {
 	// Remove previous version of the files
 	os.Remove(fs.dataFilePath)
 	os.Remove(fs.signatureFilePath)
-	var err error
-	if fs.data, err = os.Create(fs.dataFilePath); err != nil {
+	data, err := os.Create(fs.dataFilePath)
+	if err != nil {
 		return nil, nil, err
 	}
-	if fs.signature, err = os.Create(fs.signatureFilePath); err != nil {
+	signature, err := os.Create(fs.signatureFilePath)
+	if err != nil {
 		return nil, nil, err
 	}
-	return fs.data, fs.signature, nil
+	return data, signature, nil
 }
diff --git a/security/testdata/blessingstore.sig b/security/testdata/blessingstore.sig
index 0256a55..1040fb8 100644
--- a/security/testdata/blessingstore.sig
+++ b/security/testdata/blessingstore.sig
Binary files differ
diff --git a/security/util.go b/security/util.go
index e7ed33f..ea9df1c 100644
--- a/security/util.go
+++ b/security/util.go
@@ -118,28 +118,6 @@
 	return json.NewEncoder(w).Encode(acl)
 }
 
-// CaveatValidators returns the set of security.CaveatValidators
-// obtained by decoding the provided caveat bytes.
-//
-// It is an error if any of the provided caveat bytes cannot
-// be decoded into a security.CaveatValidator.
-// TODO(suharshs,ashankar,ataly): Rather than quitting on non-decodable caveats, just skip
-// them and return on caveats that we can decode.
-func CaveatValidators(caveats ...security.Caveat) ([]security.CaveatValidator, error) {
-	if len(caveats) == 0 {
-		return nil, nil
-	}
-	validators := make([]security.CaveatValidator, len(caveats))
-	for i, c := range caveats {
-		var v security.CaveatValidator
-		if err := vom.NewDecoder(bytes.NewReader(c.ValidatorVOM)).Decode(&v); err != nil {
-			return nil, fmt.Errorf("caveat bytes could not be VOM-decoded: %s", err)
-		}
-		validators[i] = v
-	}
-	return validators, nil
-}
-
 // ThirdPartyCaveats returns the set of security.ThirdPartyCaveats
 // that could be successfully decoded from the provided caveat bytes.
 func ThirdPartyCaveats(caveats ...security.Caveat) []security.ThirdPartyCaveat {
diff --git a/security/util_test.go b/security/util_test.go
index daf4fc1..2274a70 100644
--- a/security/util_test.go
+++ b/security/util_test.go
@@ -5,7 +5,6 @@
 	"crypto/ecdsa"
 	"crypto/elliptic"
 	"crypto/rand"
-	"fmt"
 	"reflect"
 	"testing"
 
@@ -133,29 +132,14 @@
 		{C{newCaveat(tp)}, V{tp}, TP{tp}},
 		{C{newCaveat(fp), newCaveat(tp)}, V{fp, tp}, TP{tp}},
 	}
-	for i, d := range testdata {
-		// Test CaveatValidators.
-		got, err := CaveatValidators(d.caveats...)
-		if err != nil {
-			t.Errorf("CaveatValidators(%v) failed: %s", d.caveats, err)
-			continue
-		}
-		if !reflect.DeepEqual(got, d.validators) {
-			fmt.Println("TEST ", i)
-			t.Errorf("CaveatValidators(%v): got: %#v, want: %#v", d.caveats, got, d.validators)
-			continue
-		}
-		if _, err := CaveatValidators(append(d.caveats, invalid)...); err == nil {
-			t.Errorf("CaveatValidators(%v) succeeded unexpectedly", d.caveats)
-			continue
-		}
+	for _, d := range testdata {
 		// Test ThirdPartyCaveats.
 		if got := ThirdPartyCaveats(d.caveats...); !reflect.DeepEqual(got, d.tpCaveats) {
 			t.Errorf("ThirdPartyCaveats(%v): got: %#v, want: %#v", d.caveats, got, d.tpCaveats)
 			continue
 		}
 		if got := ThirdPartyCaveats(append(d.caveats, invalid)...); !reflect.DeepEqual(got, d.tpCaveats) {
-			t.Errorf("ThirdPartyCaveats(%v): got: %#v, want: %#v", d.caveats, got, d.tpCaveats)
+			t.Errorf("ThirdPartyCaveats(%v, invalid): got: %#v, want: %#v", d.caveats, got, d.tpCaveats)
 			continue
 		}
 	}
diff --git a/services/identity/auditor/blessing_auditor.go b/services/identity/auditor/blessing_auditor.go
index 28f7b62..998797c 100644
--- a/services/identity/auditor/blessing_auditor.go
+++ b/services/identity/auditor/blessing_auditor.go
@@ -2,8 +2,8 @@
 
 import (
 	"bytes"
+	"database/sql"
 	"fmt"
-	_ "github.com/go-sql-driver/mysql"
 	"strings"
 	"time"
 
@@ -32,8 +32,8 @@
 // NewSQLBlessingAuditor returns an auditor for wrapping a principal with, and a BlessingLogReader
 // for reading the audits made by that auditor. The config is used to construct the connection
 // to the SQL database that the auditor and BlessingLogReader use.
-func NewSQLBlessingAuditor(config SQLConfig) (audit.Auditor, BlessingLogReader, error) {
-	db, err := newSQLDatabase(config)
+func NewSQLBlessingAuditor(sqlDB *sql.DB) (audit.Auditor, BlessingLogReader, error) {
+	db, err := newSQLDatabase(sqlDB, "BlessingAudit")
 	if err != nil {
 		return nil, nil, fmt.Errorf("failed to create sql db: %v", err)
 	}
@@ -129,21 +129,13 @@
 	if err = vom.NewDecoder(bytes.NewBuffer(dbentry.caveats)).Decode(&b.Caveats); err != nil {
 		return BlessingEntry{DecodeError: fmt.Errorf("failed to decode caveats: %s", err)}
 	}
-	if b.RevocationCaveatID, err = revocationCaveatID(b.Caveats); err != nil {
-		return BlessingEntry{DecodeError: fmt.Errorf("error getting revocationCaveatID: %s", err)}
-	}
+	b.RevocationCaveatID = revocationCaveatID(b.Caveats)
 	return b
 }
 
-func revocationCaveatID(caveats []security.Caveat) (string, error) {
-	validators, err := vsecurity.CaveatValidators(caveats...)
-	if err != nil {
-		return "", err
+func revocationCaveatID(caveats []security.Caveat) string {
+	for _, tpcav := range vsecurity.ThirdPartyCaveats(caveats...) {
+		return tpcav.ID()
 	}
-	for _, cav := range validators {
-		if tpcav, ok := cav.(security.ThirdPartyCaveat); ok {
-			return tpcav.ID(), nil
-		}
-	}
-	return "", nil
+	return ""
 }
diff --git a/services/identity/auditor/blessing_auditor_test.go b/services/identity/auditor/blessing_auditor_test.go
index 946d976..7970ae3 100644
--- a/services/identity/auditor/blessing_auditor_test.go
+++ b/services/identity/auditor/blessing_auditor_test.go
@@ -76,12 +76,12 @@
 		if !reflect.DeepEqual(got.Blessings, test.Blessings) {
 			t.Errorf("got %v, want %v", got.Blessings, test.Blessings)
 		}
-		var extraRoutines bool
+		var extra bool
 		for _ = range ch {
 			// Drain the channel to prevent the producer goroutines from being leaked.
-			extraRoutines = true
+			extra = true
 		}
-		if extraRoutines {
+		if extra {
 			t.Errorf("Got more entries that expected for test %+v", test)
 		}
 	}
diff --git a/services/identity/auditor/sql_database.go b/services/identity/auditor/sql_database.go
index 405f97f..aeb9af6 100644
--- a/services/identity/auditor/sql_database.go
+++ b/services/identity/auditor/sql_database.go
@@ -4,61 +4,52 @@
 	"database/sql"
 	"fmt"
 	_ "github.com/go-sql-driver/mysql"
-
 	"time"
+
 	"veyron.io/veyron/veyron2/vlog"
 )
 
-// SQLConfig contains the information to create a connection to a sql database.
-type SQLConfig struct {
-	// Database is a driver specific string specifying how to connect to the database.
-	Database string `json:"database"`
-	Table    string `json:"table"`
-}
-
 type database interface {
 	Insert(entry databaseEntry) error
 	Query(email string) <-chan databaseEntry
 }
 
 type databaseEntry struct {
-	email, revocationCaveatID string
-	caveats, blessings        []byte
-	timestamp                 time.Time
-	decodeErr                 error
+	email              string
+	caveats, blessings []byte
+	timestamp          time.Time
+	decodeErr          error
 }
 
 // newSQLDatabase returns a SQL implementation of the database interface.
 // If the table does not exist it creates it.
-func newSQLDatabase(config SQLConfig) (database, error) {
-	db, err := sql.Open("mysql", config.Database)
-	if err != nil {
-		return nil, fmt.Errorf("failed to create database with config(%v): %v", config, err)
-	}
-	if err := db.Ping(); err != nil {
-		return nil, err
-	}
-	createStmt, err := db.Prepare(fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s ( Email NVARCHAR(256), Caveats BLOB, Timestamp DATETIME, RevocationCaveatID NVARCHAR(1000), Blessings BLOB );", config.Table))
+func newSQLDatabase(db *sql.DB, table string) (database, error) {
+	createStmt, err := db.Prepare(fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s ( Email NVARCHAR(256), Caveats BLOB, Timestamp DATETIME, Blessings BLOB );", table))
 	if err != nil {
 		return nil, err
 	}
 	if _, err = createStmt.Exec(); err != nil {
 		return nil, err
 	}
-	insertStmt, err := db.Prepare(fmt.Sprintf("INSERT INTO %s (Email, Caveats, RevocationCaveatID, Timestamp, Blessings) VALUES (?, ?, ?, ?, ?)", config.Table))
+	insertStmt, err := db.Prepare(fmt.Sprintf("INSERT INTO %s (Email, Caveats, Timestamp, Blessings) VALUES (?, ?, ?, ?)", table))
 	if err != nil {
 		return nil, err
 	}
-	queryStmt, err := db.Prepare(fmt.Sprintf("SELECT Email, Caveats, RevocationCaveatID, Timestamp, Blessings from %s WHERE Email=?", config.Table))
+	queryStmt, err := db.Prepare(fmt.Sprintf("SELECT Email, Caveats, Timestamp, Blessings FROM %s WHERE Email=?", table))
 	return sqlDatabase{insertStmt, queryStmt}, err
 }
 
+// Table with 4 columns:
+// (1) Email = string email of the Blessee.
+// (2) Caveats = vom encoded caveats
+// (3) Blessings = vom encoded resulting blessings.
+// (4) Timestamp = time that the blessing happened.
 type sqlDatabase struct {
 	insertStmt, queryStmt *sql.Stmt
 }
 
 func (s sqlDatabase) Insert(entry databaseEntry) error {
-	_, err := s.insertStmt.Exec(entry.email, entry.caveats, entry.revocationCaveatID, entry.timestamp, entry.blessings)
+	_, err := s.insertStmt.Exec(entry.email, entry.caveats, entry.timestamp, entry.blessings)
 	return err
 }
 
@@ -78,7 +69,7 @@
 	}
 	for rows.Next() {
 		var dbentry databaseEntry
-		if err = rows.Scan(&dbentry.email, &dbentry.caveats, &dbentry.revocationCaveatID, &dbentry.timestamp, &dbentry.blessings); err != nil {
+		if err = rows.Scan(&dbentry.email, &dbentry.caveats, &dbentry.timestamp, &dbentry.blessings); err != nil {
 			vlog.Errorf("scan of row failed %v", err)
 			dbentry.decodeErr = fmt.Errorf("failed to read sql row, %s", err)
 		}
diff --git a/services/identity/auditor/sql_database_test.go b/services/identity/auditor/sql_database_test.go
new file mode 100644
index 0000000..bbd2fa6
--- /dev/null
+++ b/services/identity/auditor/sql_database_test.go
@@ -0,0 +1,53 @@
+package auditor
+
+import (
+	"github.com/DATA-DOG/go-sqlmock"
+	"reflect"
+	"testing"
+	"time"
+)
+
+func TestSQLDatabaseQuery(t *testing.T) {
+	db, err := sqlmock.New()
+	if err != nil {
+		t.Fatalf("failed to create new mock database stub: %v", err)
+	}
+	columns := []string{"Email", "Caveat", "Timestamp", "Blessings"}
+	sqlmock.ExpectExec("CREATE TABLE IF NOT EXISTS tableName (.+)").
+		WillReturnResult(sqlmock.NewResult(0, 1))
+	d, err := newSQLDatabase(db, "tableName")
+	if err != nil {
+		t.Fatalf("failed to create SQLDatabase: %v", err)
+	}
+
+	entry := databaseEntry{
+		email:     "email",
+		caveats:   []byte("caveats"),
+		timestamp: time.Now(),
+		blessings: []byte("blessings"),
+	}
+	sqlmock.ExpectExec("INSERT INTO tableName (.+) VALUES (.+)").
+		WithArgs(entry.email, entry.caveats, entry.timestamp, entry.blessings).
+		WillReturnResult(sqlmock.NewResult(0, 1)) // no insert id, 1 affected row
+	if err := d.Insert(entry); err != nil {
+		t.Errorf("failed to insert into SQLDatabase: %v", err)
+	}
+
+	// Test the querying.
+	sqlmock.ExpectQuery("SELECT Email, Caveats, Timestamp, Blessings FROM tableName").
+		WithArgs(entry.email).
+		WillReturnRows(sqlmock.NewRows(columns).AddRow(entry.email, entry.caveats, entry.timestamp, entry.blessings))
+	ch := d.Query(entry.email)
+	if res := <-ch; !reflect.DeepEqual(res, entry) {
+		t.Errorf("got %#v, expected %#v", res, entry)
+	}
+
+	var extra bool
+	for _ = range ch {
+		// Drain the channel to prevent the producer goroutines from being leaked.
+		extra = true
+	}
+	if extra {
+		t.Errorf("Got more entries that expected")
+	}
+}
diff --git a/services/identity/blesser/oauth.go b/services/identity/blesser/oauth.go
index 7b59171..c50ed3a 100644
--- a/services/identity/blesser/oauth.go
+++ b/services/identity/blesser/oauth.go
@@ -122,11 +122,7 @@
 	var caveat security.Caveat
 	var err error
 	if b.revocationManager != nil {
-		revocationCaveat, err := b.revocationManager.NewCaveat(self.PublicKey(), b.dischargerLocation)
-		if err != nil {
-			return noblessings, "", err
-		}
-		caveat, err = security.NewCaveat(revocationCaveat)
+		caveat, err = b.revocationManager.NewCaveat(self.PublicKey(), b.dischargerLocation)
 	} else {
 		caveat, err = security.ExpiryCaveat(time.Now().Add(b.duration))
 	}
diff --git a/services/identity/googleoauth/handler.go b/services/identity/googleoauth/handler.go
index 109f3fd..fb93bc1 100644
--- a/services/identity/googleoauth/handler.go
+++ b/services/identity/googleoauth/handler.go
@@ -327,6 +327,8 @@
 	ToolRedirectURL, ToolState, Email string
 }
 
+var caveatList = []string{"ExpiryCaveat", "MethodCaveat"}
+
 func (h *handler) addCaveats(w http.ResponseWriter, r *http.Request) {
 	var inputMacaroon seekBlessingsMacaroon
 	if err := h.csrfCop.ValidateToken(r.FormValue("state"), r, clientIDCookie, &inputMacaroon); err != nil {
@@ -354,9 +356,9 @@
 	}
 	tmplargs := struct {
 		Extension               string
-		CaveatMap               map[string]caveatInfo
+		CaveatList              []string
 		Macaroon, MacaroonRoute string
-	}{email, caveatMap, outputMacaroon, sendMacaroonRoute}
+	}{email, caveatList, outputMacaroon, sendMacaroonRoute}
 	w.Header().Set("Context-Type", "text/html")
 	if err := tmplSelectCaveats.Execute(w, tmplargs); err != nil {
 		vlog.Errorf("Unable to execute bless page template: %v", err)
@@ -416,12 +418,7 @@
 	// Fill in the required caveat.
 	switch required := r.FormValue("requiredCaveat"); required {
 	case "Expiry":
-		str := r.FormValue("expiry")
-		d, err := time.ParseDuration(str)
-		if err != nil {
-			return nil, fmt.Errorf("failed to parse expiration duration(%q): %v", str, err)
-		}
-		expiry, err := security.ExpiryCaveat(time.Now().Add(d))
+		expiry, err := newExpiryCaveat(r.FormValue("expiry"), r.FormValue("timezoneOffset"))
 		if err != nil {
 			return nil, fmt.Errorf("failed to create ExpiryCaveat: %v", err)
 		}
@@ -430,11 +427,7 @@
 		if h.args.RevocationManager == nil {
 			return nil, fmt.Errorf("server not configured to support revocation")
 		}
-		tpc, err := h.args.RevocationManager.NewCaveat(h.args.R.Principal().PublicKey(), h.args.DischargerLocation)
-		if err != nil {
-			return nil, fmt.Errorf("failed to create revocation caveat: %v", err)
-		}
-		revocation, err := security.NewCaveat(tpc)
+		revocation, err := h.args.RevocationManager.NewCaveat(h.args.R.Principal().PublicKey(), h.args.DischargerLocation)
 		if err != nil {
 			return nil, fmt.Errorf("failed to create revocation caveat: %v", err)
 		}
@@ -448,56 +441,45 @@
 
 	// And find any additional ones
 	for i, cavName := range r.Form["caveat"] {
-		if cavName == "none" {
+		var err error
+		var caveat security.Caveat
+		switch cavName {
+		case "ExpiryCaveat":
+			caveat, err = newExpiryCaveat(r.Form[cavName][i], r.FormValue("timezoneOffset"))
+		case "MethodCaveat":
+			caveat, err = newMethodCaveat(strings.Split(r.Form[cavName][i], ","))
+		case "none":
 			continue
-		}
-		args := strings.Split(r.Form[cavName][i], ",")
-		cavInfo, ok := caveatMap[cavName]
-		if !ok {
+		default:
 			return nil, fmt.Errorf("unable to create caveat %s: caveat does not exist", cavName)
 		}
-		caveat, err := cavInfo.New(args...)
 		if err != nil {
-			return nil, fmt.Errorf("unable to create caveat %s(%v): cavInfo.New failed: %v", cavName, args, err)
+			return nil, fmt.Errorf("unable to create caveat %s: %v", cavName, err)
 		}
 		caveats = append(caveats, caveat)
 	}
 	return caveats, nil
 }
 
-type caveatInfo struct {
-	New         func(args ...string) (security.Caveat, error)
-	Placeholder string
+func newExpiryCaveat(timestamp, utcOffset string) (security.Caveat, error) {
+	var empty security.Caveat
+	t, err := time.Parse("2006-01-02T15:04", timestamp)
+	if err != nil {
+		return empty, fmt.Errorf("parseTime failed: %v", err)
+	}
+	// utcOffset is returned as minutes from JS, so we need to parse it to a duration.
+	offset, err := time.ParseDuration(utcOffset + "m")
+	if err != nil {
+		return empty, fmt.Errorf("failed to parse duration: %v", err)
+	}
+	return security.ExpiryCaveat(t.Add(time.Minute * offset))
 }
 
-// caveatMap is a map from Caveat name to caveat information.
-// To add to this map append
-// key = "CaveatName"
-// New = func that returns instantiation of specific caveat wrapped in security.Caveat.
-// Placeholder = the placeholder text for the html input element.
-var caveatMap = map[string]caveatInfo{
-	"ExpiryCaveat": {
-		New: func(args ...string) (security.Caveat, error) {
-			if len(args) != 1 {
-				return security.Caveat{}, fmt.Errorf("must pass exactly one duration string.")
-			}
-			dur, err := time.ParseDuration(args[0])
-			if err != nil {
-				return security.Caveat{}, fmt.Errorf("parse duration failed: %v", err)
-			}
-			return security.ExpiryCaveat(time.Now().Add(dur))
-		},
-		Placeholder: "i.e. 2h45m. Valid time units are ns, us (or µs), ms, s, m, h.",
-	},
-	"MethodCaveat": {
-		New: func(args ...string) (security.Caveat, error) {
-			if len(args) < 1 {
-				return security.Caveat{}, fmt.Errorf("must pass at least one method")
-			}
-			return security.MethodCaveat(args[0], args[1:]...)
-		},
-		Placeholder: "Comma-separated method names.",
-	},
+func newMethodCaveat(methods []string) (security.Caveat, error) {
+	if len(methods) < 1 {
+		return security.Caveat{}, fmt.Errorf("must pass at least one method")
+	}
+	return security.MethodCaveat(methods[0], methods[1:]...)
 }
 
 // exchangeAuthCodeForEmail exchanges the authorization code (which must
diff --git a/services/identity/googleoauth/template.go b/services/identity/googleoauth/template.go
index 2bd72bd..a3dc4ee 100644
--- a/services/identity/googleoauth/template.go
+++ b/services/identity/googleoauth/template.go
@@ -124,7 +124,10 @@
 <title>Blessings: Select caveats</title>
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css">
+<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/css/toastr.min.css">
+<script src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.7.0/moment.min.js"></script>
 <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
+<script src="//cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/js/toastr.min.js"></script>
 <script>
   // TODO(suharshs): Move this and other JS/CSS to an assets directory in identity server.
   $(document).ready(function() {
@@ -152,37 +155,47 @@
 
     // Upon clicking the '-' button caveats should be removed.
     $('body').on('click', '.removeCaveat', function() {
-      $(this).parents(".caveatRow").remove();
+      $(this).parents('.caveatRow').remove();
     });
+
+    // Get the timezoneOffset for the server to create a correct expiry caveat.
+    // The offset is the minutes between UTC and local time.
+    var d = new Date();
+    $('#timezoneOffset').val(d.getTimezoneOffset());
+
+    // Set the datetime picker to have a default value of one day from now.
+    var m = moment().add(1, 'd').format("YYYY-MM-DDTHH:MM")
+    $('#expiry').val(m);
+    $('#ExpiryCaveat').val(m);
   });
 </script>
 </head>
 <body class="container">
-<form class="form-horizontal" method="POST" name="input" action="/google/{{.MacaroonRoute}}" role="form">
+<form class="form-horizontal" method="POST" id="caveats-form" name="input" action="/google/{{.MacaroonRoute}}" role="form">
 <h2 class="form-signin-heading">{{.Extension}}</h2>
 <input type="text" class="hidden" name="macaroon" value="{{.Macaroon}}">
 <div class="form-group form-group-lg">
   <label class="col-sm-2" for="blessing-extension">Extension</label>
   <div class="col-sm-10">
   <input name="blessingExtension" type="text" class="form-control" id="blessing-extension" placeholder="(optional) name of the device/application for which the blessing is being sought, e.g. homelaptop">
+  <input type="text" class="hidden" id="timezoneOffset" name="timezoneOffset">
   </div>
 </div>
 <div class="form-group form-group-lg">
   <label class="col-sm-2" for="required-caveat">Expiration</label>
   <div class="col-sm-10" class="input-group" name="required-caveat">
     <div class="radio">
-      <div class="input-group">
-        <input type="radio" name="requiredCaveat" id="requiredCaveat" value="Expiry" checked>
-        <input type="text" name="expiry" id="expiry" value="1h" placeholder="time after which the blessing will expire">
-      </div>
-    </div>
-    <div class="radio">
       <label>
-      <!-- TODO(suharshs): Re-enable -->
-      <input type="radio" name="requiredCaveat" id="requiredCaveat" value="Revocation" disabled>
+      <input type="radio" name="requiredCaveat" id="requiredCaveat" value="Revocation" checked>
       When explicitly revoked
       </label>
     </div>
+    <div class="radio">
+      <div class="input-group">
+        <input type="radio" name="requiredCaveat" id="requiredCaveat" value="Expiry">
+        <input type="datetime-local" id="expiry" name="expiry">
+      </div>
+    </div>
   </div>
 </div>
 <h4 class="form-signin-heading">Additional caveats</h4>
@@ -191,15 +204,19 @@
   <div class="col-md-4">
     <select name="caveat" class="form-control caveats">
       <option value="none" selected="selected">Select a caveat.</option>
-      {{ $caveatMap := .CaveatMap }}
-      {{range $key, $value := $caveatMap}}
-      <option name="{{$key}}" value="{{$key}}">{{$key}}</option>
+      {{ $caveatList := .CaveatList }}
+      {{range $index, $name := $caveatList}}
+      <option name="{{$name}}" value="{{$name}}">{{$name}}</option>
       {{end}}
     </select>
   </div>
   <div class="col-md-7">
-    {{range $key, $entry := $caveatMap}}
-      <input type="text" id="{{$key}}" class="form-control caveatInput" name="{{$key}}" placeholder="{{$entry.Placeholder}}">
+    {{range $index, $name := $caveatList}}
+      {{if eq $name "ExpiryCaveat"}}
+      <input type="datetime-local" class="form-control caveatInput" id="{{$name}}" name="{{$name}}">
+      {{else if eq $name "MethodCaveat"}}
+      <input type="text" id="{{$name}}" class="form-control caveatInput" name="{{$name}}" placeholder="comma-separated method list">
+      {{end}}
     {{end}}
   </div>
   <div class="col-md-1">
diff --git a/services/identity/identityd/main.go b/services/identity/identityd/main.go
index 3372b0b..55e0b87 100644
--- a/services/identity/identityd/main.go
+++ b/services/identity/identityd/main.go
@@ -3,7 +3,7 @@
 
 import (
 	"crypto/rand"
-	"encoding/json"
+	"database/sql"
 	"flag"
 	"fmt"
 	"html/template"
@@ -42,18 +42,14 @@
 	tlsconfig = flag.String("tlsconfig", "", "Comma-separated list of TLS certificate and private key files. This must be provided.")
 	host      = flag.String("host", defaultHost(), "Hostname the HTTP server listens on. This can be the name of the host running the webserver, but if running behind a NAT or load balancer, this should be the host name that clients will connect to. For example, if set to 'x.com', Veyron identities will have the IssuerName set to 'x.com' and clients can expect to find the public key of the signer at 'x.com/pubkey/'.")
 
-	// Flag controlling auditing of Blessing operations.
-	auditConfig = flag.String("audit_config", "", "A JSON-encoded file with sql server configuration information for auditing. The file must have an entry for user, host, password, database, and table.")
+	// Flag controlling auditing and revocation of Blessing operations.
+	sqlConfig = flag.String("sqlconfig", "", "Path to file containing go-sql-driver connection string of the following form: [username[:password]@][protocol[(address)]]/dbname")
 
 	// Configuration for various Google OAuth-based clients.
 	googleConfigWeb     = flag.String("google_config_web", "", "Path to JSON-encoded OAuth client configuration for the web application that renders the audit log for blessings provided by this provider.")
 	googleConfigChrome  = flag.String("google_config_chrome", "", "Path to the JSON-encoded OAuth client configuration for Chrome browser applications that obtain blessings from this server (via the OAuthBlesser.BlessUsingAccessToken RPC) from this server.")
 	googleConfigAndroid = flag.String("google_config_android", "", "Path to the JSON-encoded OAuth client configuration for Android applications that obtain blessings from this server (via the OAuthBlesser.BlessUsingAccessToken RPC) from this server.")
 	googleDomain        = flag.String("google_domain", "", "An optional domain name. When set, only email addresses from this domain are allowed to authenticate via Google OAuth")
-
-	// Revocation/expiry configuration.
-	// TODO(ashankar,ataly,suharshs): Re-enable by default once the move to the new security API is complete?
-	revocationDir = flag.String("revocation_dir", "" /*filepath.Join(os.TempDir(), "revocation_dir")*/, "Path where the revocation manager will store caveat and revocation information.")
 )
 
 const (
@@ -64,14 +60,32 @@
 
 func main() {
 	flag.Usage = usage
-	p, blessingLogReader := providerPrincipal()
+	flag.Parse()
+
+	var sqlDB *sql.DB
+	var err error
+	if len(*sqlConfig) > 0 {
+		config, err := ioutil.ReadFile(*sqlConfig)
+		if err != nil {
+			vlog.Fatalf("failed to read sql config from %v", *sqlConfig)
+		}
+		sqlDB, err = dbFromConfigDatabase(strings.Trim(string(config), "\n"))
+		if err != nil {
+			vlog.Fatalf("failed to create sqlDB: %v", err)
+		}
+	}
+
+	p, blessingLogReader := providerPrincipal(sqlDB)
 	r := rt.Init(options.RuntimePrincipal{p})
 	defer r.Cleanup()
 
-	// Calling with empty string returns a empty RevocationManager
-	revocationManager, err := revocation.NewRevocationManager(*revocationDir)
-	if err != nil {
-		vlog.Fatalf("Failed to start RevocationManager: %v", err)
+	var revocationManager *revocation.RevocationManager
+	// Only set revocationManager sqlConfig (and thus sqlDB) is set.
+	if sqlDB != nil {
+		revocationManager, err = revocation.NewRevocationManager(sqlDB)
+		if err != nil {
+			vlog.Fatalf("Failed to start RevocationManager: %v", err)
+		}
 	}
 
 	// Setup handlers
@@ -119,7 +133,7 @@
 		if len(*googleConfigChrome) > 0 || len(*googleConfigAndroid) > 0 {
 			args.GoogleServers = appendSuffixTo(published, googleService)
 		}
-		if len(*auditConfig) > 0 && len(*googleConfigWeb) > 0 {
+		if sqlDB != nil && len(*googleConfigWeb) > 0 {
 			args.ListBlessingsRoute = googleoauth.ListBlessingsRoute
 		}
 		if err := tmpl.Execute(w, args); err != nil {
@@ -158,7 +172,7 @@
 
 type dispatcher map[string]interface{}
 
-func (d dispatcher) Lookup(suffix, method string) (interface{}, security.Authorizer, error) {
+func (d dispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) {
 	if invoker := d[suffix]; invoker != nil {
 		return invoker, allowEveryoneAuthorizer{}, nil
 	}
@@ -281,7 +295,7 @@
 
 // providerPrincipal returns the Principal to use for the identity provider (i.e., this program) and
 // the database where audits will be store. If no database exists nil will be returned.
-func providerPrincipal() (security.Principal, auditor.BlessingLogReader) {
+func providerPrincipal(sqlDB *sql.DB) (security.Principal, auditor.BlessingLogReader) {
 	// TODO(ashankar): Somewhat silly to have to create a runtime, but oh-well.
 	r, err := rt.New()
 	if err != nil {
@@ -289,33 +303,25 @@
 	}
 	defer r.Cleanup()
 	p := r.Principal()
-	if len(*auditConfig) == 0 {
+	if sqlDB == nil {
 		return p, nil
 	}
-	config, err := readSQLConfigFromFile(*auditConfig)
-	if err != nil {
-		vlog.Fatalf("Failed to read sql config: %v", err)
-	}
-	auditor, reader, err := auditor.NewSQLBlessingAuditor(config)
+	auditor, reader, err := auditor.NewSQLBlessingAuditor(sqlDB)
 	if err != nil {
 		vlog.Fatalf("Failed to create sql auditor from config: %v", err)
 	}
 	return audit.NewPrincipal(p, auditor), reader
 }
 
-func readSQLConfigFromFile(file string) (auditor.SQLConfig, error) {
-	var config auditor.SQLConfig
-	content, err := ioutil.ReadFile(file)
+func dbFromConfigDatabase(database string) (*sql.DB, error) {
+	db, err := sql.Open("mysql", database+"?parseTime=true")
 	if err != nil {
-		return config, err
+		return nil, fmt.Errorf("failed to create database with database(%v): %v", database, err)
 	}
-	if err := json.Unmarshal(content, &config); err != nil {
-		return config, err
+	if err := db.Ping(); err != nil {
+		return nil, err
 	}
-	if len(strings.Split(config.Table, " ")) != 1 || strings.Contains(config.Table, ";") {
-		return config, fmt.Errorf("sql config table value must be 1 word long")
-	}
-	return config, nil
+	return db, nil
 }
 
 func httpaddress() string {
diff --git a/services/identity/revocation/revocation_manager.go b/services/identity/revocation/revocation_manager.go
index 698e759..b4b2178 100644
--- a/services/identity/revocation/revocation_manager.go
+++ b/services/identity/revocation/revocation_manager.go
@@ -3,112 +3,89 @@
 
 import (
 	"crypto/rand"
-	"encoding/hex"
+	"database/sql"
 	"fmt"
-	"path/filepath"
-	"strconv"
 	"sync"
 	"time"
 
-	"veyron.io/veyron/veyron/services/identity/util"
 	"veyron.io/veyron/veyron2/security"
 	"veyron.io/veyron/veyron2/vom"
 )
 
 // RevocationManager persists information for revocation caveats to provided discharges and allow for future revocations.
-type RevocationManager struct {
-	caveatMap *util.DirectoryStore // Map of blessed identity's caveats.  ThirdPartyCaveatID -> revocationCaveatID
+type RevocationManager struct{}
+
+// NewRevocationManager returns a RevocationManager that persists information about
+// revocationCaveats in a SQL database and allows for revocation and caveat creation.
+// This function can only be called once because of the use of global variables.
+func NewRevocationManager(sqlDB *sql.DB) (*RevocationManager, error) {
+	revocationLock.Lock()
+	defer revocationLock.Unlock()
+	if revocationDB != nil {
+		return nil, fmt.Errorf("NewRevocationManager can only be called once")
+	}
+	var err error
+	revocationDB, err = newSQLDatabase(sqlDB, "RevocationCaveatInfo")
+	if err != nil {
+		return nil, err
+	}
+	return &RevocationManager{}, nil
 }
 
-var revocationMap *util.DirectoryStore
+var revocationDB database
 var revocationLock sync.RWMutex
 
-// NewCaveat returns a security.ThirdPartyCaveat for which discharges will be
+// NewCaveat returns a security.Caveat constructed with a ThirdPartyCaveat for which discharges will be
 // issued iff Revoke has not been called for the returned caveat.
-func (r *RevocationManager) NewCaveat(discharger security.PublicKey, dischargerLocation string) (security.ThirdPartyCaveat, error) {
+func (r *RevocationManager) NewCaveat(discharger security.PublicKey, dischargerLocation string) (security.Caveat, error) {
+	var empty security.Caveat
 	var revocation [16]byte
 	if _, err := rand.Read(revocation[:]); err != nil {
-		return nil, err
+		return empty, err
 	}
 	restriction, err := security.NewCaveat(revocationCaveat(revocation))
 	if err != nil {
-		return nil, err
+		return empty, err
 	}
 	cav, err := security.NewPublicKeyCaveat(discharger, dischargerLocation, security.ThirdPartyRequirements{}, restriction)
 	if err != nil {
-		return nil, err
+		return empty, err
 	}
-	if err = r.caveatMap.Put(hex.EncodeToString([]byte(cav.ID())), hex.EncodeToString(revocation[:])); err != nil {
-		return nil, err
+	if err = revocationDB.InsertCaveat(cav.ID(), revocation[:]); err != nil {
+		return empty, err
 	}
-	return cav, nil
+	return security.NewCaveat(cav)
 }
 
 // Revoke disables discharges from being issued for the provided third-party caveat.
 func (r *RevocationManager) Revoke(caveatID string) error {
-	token, err := r.caveatMap.Get(hex.EncodeToString([]byte(caveatID)))
-	if err != nil {
-		return err
-	}
-	return revocationMap.Put(token, strconv.FormatInt(time.Now().Unix(), 10))
+	return revocationDB.Revoke(caveatID)
 }
 
 // GetRevocationTimestamp returns the timestamp at which a caveat was revoked.
 // If the caveat wasn't revoked returns nil
 func (r *RevocationManager) GetRevocationTime(caveatID string) *time.Time {
-	token, err := r.caveatMap.Get(hex.EncodeToString([]byte(caveatID)))
+	timestamp, err := revocationDB.RevocationTime(caveatID)
 	if err != nil {
 		return nil
 	}
-	timestamp, err := revocationMap.Get(token)
-	if err != nil {
-		return nil
-	}
-	unix_int, err := strconv.ParseInt(timestamp, 10, 64)
-	if err != nil {
-		return nil
-	}
-	revocationTime := time.Unix(unix_int, 0)
-	return &revocationTime
+	return timestamp
 }
 
 type revocationCaveat [16]byte
 
 func (cav revocationCaveat) Validate(security.Context) error {
 	revocationLock.RLock()
-	if revocationMap == nil {
+	if revocationDB == nil {
 		revocationLock.RUnlock()
 		return fmt.Errorf("missing call to NewRevocationManager")
 	}
 	revocationLock.RUnlock()
-	if revocationMap.Exists(hex.EncodeToString(cav[:])) {
+	revoked, err := revocationDB.IsRevoked(cav[:])
+	if revoked {
 		return fmt.Errorf("revoked")
 	}
-	return nil
-}
-
-// NewRevocationManager returns a RevocationManager that persists information about
-// revocationCaveats and allows for revocation and caveat creation.
-// This function can only be called once because of the use of global variables.
-func NewRevocationManager(dir string) (*RevocationManager, error) {
-	revocationLock.Lock()
-	defer revocationLock.Unlock()
-	if revocationMap != nil {
-		return nil, fmt.Errorf("NewRevocationManager can only be called once")
-	}
-	// If empty string return nil revocationManager
-	if len(dir) == 0 {
-		return nil, nil
-	}
-	caveatMap, err := util.NewDirectoryStore(filepath.Join(dir, "caveat_dir"))
-	if err != nil {
-		return nil, err
-	}
-	revocationMap, err = util.NewDirectoryStore(filepath.Join(dir, "revocation_dir"))
-	if err != nil {
-		return nil, err
-	}
-	return &RevocationManager{caveatMap}, nil
+	return err
 }
 
 func init() {
diff --git a/services/identity/revocation/revocation_test.go b/services/identity/revocation/revocation_test.go
new file mode 100644
index 0000000..171d036
--- /dev/null
+++ b/services/identity/revocation/revocation_test.go
@@ -0,0 +1,104 @@
+package revocation
+
+import (
+	"bytes"
+	"testing"
+	"time"
+
+	"veyron.io/veyron/veyron2"
+	"veyron.io/veyron/veyron2/naming"
+	"veyron.io/veyron/veyron2/rt"
+	"veyron.io/veyron/veyron2/security"
+	"veyron.io/veyron/veyron2/vom"
+
+	"veyron.io/veyron/veyron/profiles"
+	services "veyron.io/veyron/veyron/services/security"
+	"veyron.io/veyron/veyron/services/security/discharger"
+)
+
+type mockDatabase struct {
+	tpCavIDToRevCavID   map[string][]byte
+	revCavIDToTimestamp map[string]*time.Time
+}
+
+func (m *mockDatabase) InsertCaveat(thirdPartyCaveatID string, revocationCaveatID []byte) error {
+	m.tpCavIDToRevCavID[thirdPartyCaveatID] = revocationCaveatID
+	return nil
+}
+
+func (m *mockDatabase) Revoke(thirdPartyCaveatID string) error {
+	timestamp := time.Now()
+	m.revCavIDToTimestamp[string(m.tpCavIDToRevCavID[thirdPartyCaveatID])] = &timestamp
+	return nil
+}
+
+func (m *mockDatabase) IsRevoked(revocationCaveatID []byte) (bool, error) {
+	_, exists := m.revCavIDToTimestamp[string(revocationCaveatID)]
+	return exists, nil
+}
+
+func (m *mockDatabase) RevocationTime(thirdPartyCaveatID string) (*time.Time, error) {
+	return m.revCavIDToTimestamp[string(m.tpCavIDToRevCavID[thirdPartyCaveatID])], nil
+}
+
+func newRevocationManager(t *testing.T) *RevocationManager {
+	revocationDB = &mockDatabase{make(map[string][]byte), make(map[string]*time.Time)}
+	return &RevocationManager{}
+}
+
+func revokerSetup(t *testing.T) (dischargerKey security.PublicKey, dischargerEndpoint string, revoker *RevocationManager, closeFunc func(), runtime veyron2.Runtime) {
+	r := rt.Init()
+	revokerService := newRevocationManager(t)
+	dischargerServer, err := r.NewServer()
+	if err != nil {
+		t.Fatalf("rt.R().NewServer: %s", err)
+	}
+	dischargerEP, err := dischargerServer.Listen(profiles.LocalListenSpec)
+	if err != nil {
+		t.Fatalf("dischargerServer.Listen failed: %v", err)
+	}
+	dischargerServiceStub := services.DischargerServer(discharger.NewDischarger())
+	if err := dischargerServer.Serve("", dischargerServiceStub, nil); err != nil {
+		t.Fatalf("dischargerServer.Serve revoker: %s", err)
+	}
+	return r.Principal().PublicKey(),
+		naming.JoinAddressName(dischargerEP.String(), ""),
+		revokerService,
+		func() {
+			dischargerServer.Stop()
+		},
+		r
+}
+
+func TestDischargeRevokeDischargeRevokeDischarge(t *testing.T) {
+	dcKey, dc, revoker, closeFunc, r := revokerSetup(t)
+	defer closeFunc()
+
+	discharger := services.DischargerClient(dc)
+	caveat, err := revoker.NewCaveat(dcKey, dc)
+	if err != nil {
+		t.Fatalf("failed to create revocation caveat: %s", err)
+	}
+	var cav security.ThirdPartyCaveat
+	if err := vom.NewDecoder(bytes.NewBuffer(caveat.ValidatorVOM)).Decode(&cav); err != nil {
+		t.Fatalf("failed to create decode tp caveat: %s", err)
+	}
+
+	var impetus security.DischargeImpetus
+
+	if _, err = discharger.Discharge(r.NewContext(), cav, impetus); err != nil {
+		t.Fatalf("failed to get discharge: %s", err)
+	}
+	if err = revoker.Revoke(cav.ID()); err != nil {
+		t.Fatalf("failed to revoke: %s", err)
+	}
+	if discharge, err := discharger.Discharge(r.NewContext(), cav, impetus); err == nil || discharge != nil {
+		t.Fatalf("got a discharge for a revoked caveat: %s", err)
+	}
+	if err = revoker.Revoke(cav.ID()); err != nil {
+		t.Fatalf("failed to revoke again: %s", err)
+	}
+	if discharge, err := discharger.Discharge(r.NewContext(), cav, impetus); err == nil || discharge != nil {
+		t.Fatalf("got a discharge for a doubly revoked caveat: %s", err)
+	}
+}
diff --git a/services/identity/revocation/revoker_test.go b/services/identity/revocation/revoker_test.go
deleted file mode 100644
index 7f5338b..0000000
--- a/services/identity/revocation/revoker_test.go
+++ /dev/null
@@ -1,75 +0,0 @@
-package revocation
-
-import (
-	"os"
-	"path/filepath"
-	"testing"
-
-	"veyron.io/veyron/veyron2"
-	"veyron.io/veyron/veyron2/naming"
-	"veyron.io/veyron/veyron2/rt"
-	"veyron.io/veyron/veyron2/security"
-
-	"veyron.io/veyron/veyron/profiles"
-	services "veyron.io/veyron/veyron/services/security"
-	"veyron.io/veyron/veyron/services/security/discharger"
-)
-
-func revokerSetup(t *testing.T) (dischargerKey security.PublicKey, dischargerEndpoint string, revoker *RevocationManager, closeFunc func(), runtime veyron2.Runtime) {
-	var dir = filepath.Join(os.TempDir(), "revoker_test_dir")
-	r := rt.Init()
-	revokerService, err := NewRevocationManager(dir)
-	if err != nil {
-		t.Fatalf("NewRevocationManager failed: %v", err)
-	}
-
-	dischargerServer, err := r.NewServer()
-	if err != nil {
-		t.Fatalf("rt.R().NewServer: %s", err)
-	}
-	dischargerEP, err := dischargerServer.Listen(profiles.LocalListenSpec)
-	if err != nil {
-		t.Fatalf("dischargerServer.Listen failed: %v", err)
-	}
-	dischargerServiceStub := services.DischargerServer(discharger.NewDischarger())
-	if err := dischargerServer.Serve("", dischargerServiceStub, nil); err != nil {
-		t.Fatalf("dischargerServer.Serve revoker: %s", err)
-	}
-	return r.Principal().PublicKey(),
-		naming.JoinAddressName(dischargerEP.String(), ""),
-		revokerService,
-		func() {
-			defer os.RemoveAll(dir)
-			dischargerServer.Stop()
-		},
-		r
-}
-
-func TestDischargeRevokeDischargeRevokeDischarge(t *testing.T) {
-	dcKey, dc, revoker, closeFunc, r := revokerSetup(t)
-	defer closeFunc()
-
-	discharger := services.DischargerClient(dc)
-	cav, err := revoker.NewCaveat(dcKey, dc)
-	if err != nil {
-		t.Fatalf("failed to create public key caveat: %s", err)
-	}
-
-	var impetus security.DischargeImpetus
-
-	if _, err = discharger.Discharge(r.NewContext(), cav, impetus); err != nil {
-		t.Fatalf("failed to get discharge: %s", err)
-	}
-	if err = revoker.Revoke(cav.ID()); err != nil {
-		t.Fatalf("failed to revoke: %s", err)
-	}
-	if discharge, err := discharger.Discharge(r.NewContext(), cav, impetus); err == nil || discharge != nil {
-		t.Fatalf("got a discharge for a revoked caveat: %s", err)
-	}
-	if err = revoker.Revoke(cav.ID()); err != nil {
-		t.Fatalf("failed to revoke again: %s", err)
-	}
-	if discharge, err := discharger.Discharge(r.NewContext(), cav, impetus); err == nil || discharge != nil {
-		t.Fatalf("got a discharge for a doubly revoked caveat: %s", err)
-	}
-}
diff --git a/services/identity/revocation/sql_database.go b/services/identity/revocation/sql_database.go
new file mode 100644
index 0000000..cfe5633
--- /dev/null
+++ b/services/identity/revocation/sql_database.go
@@ -0,0 +1,80 @@
+package revocation
+
+import (
+	"database/sql"
+	"encoding/hex"
+	"fmt"
+	"time"
+)
+
+type database interface {
+	InsertCaveat(thirdPartyCaveatID string, revocationCaveatID []byte) error
+	Revoke(thirdPartyCaveatID string) error
+	IsRevoked(revocationCaveatID []byte) (bool, error)
+	RevocationTime(thirdPartyCaveatID string) (*time.Time, error)
+}
+
+// Table with 3 columns:
+// (1) ThirdPartyCaveatID= string thirdPartyCaveatID.
+// (2) RevocationCaveatID= hex encoded revcationCaveatID.
+// (3) RevocationTime= time (if any) that the Caveat was revoked.
+type sqlDatabase struct {
+	insertCaveatStmt, revokeStmt, isRevokedStmt, revocationTimeStmt *sql.Stmt
+}
+
+func (s *sqlDatabase) InsertCaveat(thirdPartyCaveatID string, revocationCaveatID []byte) error {
+	_, err := s.insertCaveatStmt.Exec(thirdPartyCaveatID, hex.EncodeToString(revocationCaveatID))
+	return err
+}
+
+func (s *sqlDatabase) Revoke(thirdPartyCaveatID string) error {
+	_, err := s.revokeStmt.Exec(time.Now(), thirdPartyCaveatID)
+	return err
+}
+
+func (s *sqlDatabase) IsRevoked(revocationCaveatID []byte) (bool, error) {
+	rows, err := s.isRevokedStmt.Query(hex.EncodeToString(revocationCaveatID))
+	if err != nil {
+		return false, err
+	}
+	return rows.Next(), nil
+}
+
+func (s *sqlDatabase) RevocationTime(thirdPartyCaveatID string) (*time.Time, error) {
+	rows, err := s.revocationTimeStmt.Query(thirdPartyCaveatID)
+	if err != nil {
+		return nil, err
+	}
+	if rows.Next() {
+		var timestamp time.Time
+		if err := rows.Scan(&timestamp); err != nil {
+			return nil, err
+		}
+		return &timestamp, nil
+	}
+	return nil, fmt.Errorf("the caveat (%v) was not revoked", thirdPartyCaveatID)
+}
+
+func newSQLDatabase(db *sql.DB, table string) (database, error) {
+	createStmt, err := db.Prepare(fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s ( ThirdPartyCaveatID NVARCHAR(255), RevocationCaveatID NVARCHAR(255), RevocationTime DATETIME, PRIMARY KEY (ThirdPartyCaveatID), KEY (RevocationCaveatID) );", table))
+	if err != nil {
+		return nil, err
+	}
+	if _, err = createStmt.Exec(); err != nil {
+		return nil, err
+	}
+	insertCaveatStmt, err := db.Prepare(fmt.Sprintf("INSERT INTO %s (ThirdPartyCaveatID, RevocationCaveatID, RevocationTime) VALUES (?, ?, NULL)", table))
+	if err != nil {
+		return nil, err
+	}
+	revokeStmt, err := db.Prepare(fmt.Sprintf("UPDATE %s SET RevocationTime=? WHERE ThirdPartyCaveatID=?", table))
+	if err != nil {
+		return nil, err
+	}
+	isRevokedStmt, err := db.Prepare(fmt.Sprintf("SELECT 1 FROM %s WHERE RevocationCaveatID=? AND RevocationTime IS NOT NULL", table))
+	if err != nil {
+		return nil, err
+	}
+	revocationTimeStmt, err := db.Prepare(fmt.Sprintf("SELECT RevocationTime FROM %s WHERE ThirdPartyCaveatID=?", table))
+	return &sqlDatabase{insertCaveatStmt, revokeStmt, isRevokedStmt, revocationTimeStmt}, err
+}
diff --git a/services/identity/revocation/sql_database_test.go b/services/identity/revocation/sql_database_test.go
new file mode 100644
index 0000000..de9171b
--- /dev/null
+++ b/services/identity/revocation/sql_database_test.go
@@ -0,0 +1,73 @@
+package revocation
+
+import (
+	"encoding/hex"
+	"github.com/DATA-DOG/go-sqlmock"
+	"reflect"
+	"testing"
+	"time"
+)
+
+func TestSQLDatabase(t *testing.T) {
+	db, err := sqlmock.New()
+	if err != nil {
+		t.Fatalf("failed to create new mock database stub: %v", err)
+	}
+	columns := []string{"ThirdPartyCaveatID", "RevocationCaveatID", "RevocationTime"}
+	sqlmock.ExpectExec("CREATE TABLE IF NOT EXISTS tableName (.+)").
+		WillReturnResult(sqlmock.NewResult(0, 1))
+	d, err := newSQLDatabase(db, "tableName")
+	if err != nil {
+		t.Fatalf("failed to create SQLDatabase: %v", err)
+	}
+
+	tpCavID, revCavID := "tpCavID", []byte("revCavID")
+	tpCavID2, revCavID2 := "tpCavID2", []byte("revCavID2")
+	encRevCavID := hex.EncodeToString(revCavID)
+	encRevCavID2 := hex.EncodeToString(revCavID2)
+	sqlmock.ExpectExec("INSERT INTO tableName (.+) VALUES (.+)").
+		WithArgs(tpCavID, encRevCavID).
+		WillReturnResult(sqlmock.NewResult(0, 1)) // no insert id, 1 affected row
+	if err := d.InsertCaveat(tpCavID, revCavID); err != nil {
+		t.Errorf("failed to InsertCaveat into SQLDatabase: %v", err)
+	}
+
+	sqlmock.ExpectExec("INSERT INTO tableName (.+) VALUES (.+)").
+		WithArgs(tpCavID2, encRevCavID2).
+		WillReturnResult(sqlmock.NewResult(0, 1)) // no insert id, 1 affected row
+	if err := d.InsertCaveat(tpCavID2, revCavID2); err != nil {
+		t.Errorf("second InsertCaveat into SQLDatabase failed: %v", err)
+	}
+
+	// Test Revocation
+	sqlmock.ExpectExec("UPDATE tableName SET RevocationTime=.+").
+		WillReturnResult(sqlmock.NewResult(0, 1))
+	if err := d.Revoke(tpCavID); err != nil {
+		t.Errorf("failed to Revoke Caveat: %v", err)
+	}
+
+	// Test IsRevoked returns true.
+	sqlmock.ExpectQuery("SELECT 1 FROM tableName").
+		WithArgs(encRevCavID).
+		WillReturnRows(sqlmock.NewRows(columns).AddRow(1, 1, 1))
+	if revoked, err := d.IsRevoked(revCavID); err != nil || !revoked {
+		t.Errorf("expected revCavID to be revoked: err: (%v)", err)
+	}
+
+	// Test IsRevoked returns false.
+	sqlmock.ExpectQuery("SELECT 1 FROM tableName").
+		WithArgs(encRevCavID2).
+		WillReturnRows(sqlmock.NewRows(columns))
+	if revoked, err := d.IsRevoked(revCavID2); err != nil || revoked {
+		t.Errorf("expected revCavID to not be revoked: err: (%v)", err)
+	}
+
+	// Test RevocationTime.
+	revocationTime := time.Now()
+	sqlmock.ExpectQuery("SELECT RevocationTime FROM tableName").
+		WithArgs(tpCavID).
+		WillReturnRows(sqlmock.NewRows([]string{"RevocationTime"}).AddRow(revocationTime))
+	if got, err := d.RevocationTime(tpCavID); err != nil || !reflect.DeepEqual(*got, revocationTime) {
+		t.Errorf("got %v, expected %v: err : %v", got, revocationTime, err)
+	}
+}
diff --git a/services/identity/util/directory_store.go b/services/identity/util/directory_store.go
deleted file mode 100644
index 7c93638..0000000
--- a/services/identity/util/directory_store.go
+++ /dev/null
@@ -1,46 +0,0 @@
-package util
-
-import (
-	"fmt"
-	"io/ioutil"
-	"os"
-	"path/filepath"
-)
-
-// DirectoryStore implements a key-value store on a filesystem where data for each key is stored in its own file.
-// TODO(suharshs): When vstore is ready replace this with the veyron store.
-type DirectoryStore struct {
-	dir string
-}
-
-func (s DirectoryStore) Exists(key string) bool {
-	_, err := os.Stat(s.pathName(key))
-	return !os.IsNotExist(err)
-}
-
-func (s DirectoryStore) Put(key, value string) error {
-	return ioutil.WriteFile(s.pathName(key), []byte(value), 0600)
-}
-
-func (s DirectoryStore) Get(key string) (string, error) {
-	bytes, err := ioutil.ReadFile(s.pathName(key))
-	return string(bytes), err
-}
-
-func (s DirectoryStore) pathName(key string) string {
-	return filepath.Join(string(s.dir), key)
-}
-
-// NewDirectoryStore returns a key-value store that uses one file per key,
-// and places all data in the provided directory.
-func NewDirectoryStore(dir string) (*DirectoryStore, error) {
-	if len(dir) == 0 {
-		return nil, fmt.Errorf("must provide non-empty directory name")
-	}
-	// Make the directory if it doesn't already exist.
-	if err := os.MkdirAll(dir, 0700); err != nil {
-		return nil, err
-	}
-
-	return &DirectoryStore{dir}, nil
-}
diff --git a/services/identity/util/sql_config.go b/services/identity/util/sql_config.go
new file mode 100644
index 0000000..f375d63
--- /dev/null
+++ b/services/identity/util/sql_config.go
@@ -0,0 +1,18 @@
+package util
+
+import (
+	"database/sql"
+	"fmt"
+	_ "github.com/go-sql-driver/mysql"
+)
+
+func DBFromConfigDatabase(database string) (*sql.DB, error) {
+	db, err := sql.Open("mysql", database+"?parseTime=true")
+	if err != nil {
+		return nil, fmt.Errorf("failed to create database with database(%v): %v", database, err)
+	}
+	if err := db.Ping(); err != nil {
+		return nil, err
+	}
+	return db, nil
+}
diff --git a/services/mgmt/application/impl/dispatcher.go b/services/mgmt/application/impl/dispatcher.go
index 3fda1a4..92b03f8 100644
--- a/services/mgmt/application/impl/dispatcher.go
+++ b/services/mgmt/application/impl/dispatcher.go
@@ -29,6 +29,6 @@
 
 // DISPATCHER INTERFACE IMPLEMENTATION
 
-func (d *dispatcher) Lookup(suffix, method string) (interface{}, security.Authorizer, error) {
+func (d *dispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) {
 	return repository.ApplicationServer(NewApplicationService(d.store, d.storeRoot, suffix)), d.auth, nil
 }
diff --git a/services/mgmt/binary/impl/dispatcher.go b/services/mgmt/binary/impl/dispatcher.go
index fa3e900..150247b 100644
--- a/services/mgmt/binary/impl/dispatcher.go
+++ b/services/mgmt/binary/impl/dispatcher.go
@@ -27,6 +27,6 @@
 
 // DISPATCHER INTERFACE IMPLEMENTATION
 
-func (d *dispatcher) Lookup(suffix, method string) (interface{}, security.Authorizer, error) {
+func (d *dispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) {
 	return repository.BinaryServer(newBinaryService(d.state, suffix)), d.auth, nil
 }
diff --git a/services/mgmt/debug/debug.vdl b/services/mgmt/debug/debug.vdl
deleted file mode 100644
index 10f1925..0000000
--- a/services/mgmt/debug/debug.vdl
+++ /dev/null
@@ -1,17 +0,0 @@
-package debug
-
-import (
-	"veyron.io/veyron/veyron2/services/mgmt/logreader"
-	"veyron.io/veyron/veyron2/services/mgmt/pprof"
-	"veyron.io/veyron/veyron2/services/mgmt/stats"
-)
-
-// This interface exists only for the purpose of generating a valid Signature
-// for all the interfaces that are served by the debug server. It should be
-// removed when https://code.google.com/p/envyor/issues/detail?id=285 is
-// resolved.
-type Debug interface {
-	logreader.LogFile
-	stats.Stats
-	pprof.PProf
-}
diff --git a/services/mgmt/debug/debug.vdl.go b/services/mgmt/debug/debug.vdl.go
deleted file mode 100644
index a1d7c2b..0000000
--- a/services/mgmt/debug/debug.vdl.go
+++ /dev/null
@@ -1,358 +0,0 @@
-// This file was auto-generated by the veyron vdl tool.
-// Source: debug.vdl
-
-package debug
-
-import (
-	"veyron.io/veyron/veyron2/services/mgmt/logreader"
-
-	"veyron.io/veyron/veyron2/services/mgmt/pprof"
-
-	"veyron.io/veyron/veyron2/services/mgmt/stats"
-
-	// The non-user imports are prefixed with "__" to prevent collisions.
-	__veyron2 "veyron.io/veyron/veyron2"
-	__context "veyron.io/veyron/veyron2/context"
-	__ipc "veyron.io/veyron/veyron2/ipc"
-	__vdlutil "veyron.io/veyron/veyron2/vdl/vdlutil"
-	__wiretype "veyron.io/veyron/veyron2/wiretype"
-)
-
-// TODO(toddw): Remove this line once the new signature support is done.
-// It corrects a bug where __wiretype is unused in VDL pacakges where only
-// bootstrap types are used on interfaces.
-const _ = __wiretype.TypeIDInvalid
-
-// DebugClientMethods is the client interface
-// containing Debug methods.
-//
-// This interface exists only for the purpose of generating a valid Signature
-// for all the interfaces that are served by the debug server. It should be
-// removed when https://code.google.com/p/envyor/issues/detail?id=285 is
-// resolved.
-type DebugClientMethods interface {
-	// LogFile can be used to access log files remotely.
-	logreader.LogFileClientMethods
-	// The Stats interface is used to access stats for troubleshooting and
-	// monitoring purposes. The stats objects are discoverable via the Globbable
-	// interface and watchable via the GlobWatcher interface.
-	//
-	// The types of the object values are implementation specific, but should be
-	// primarily numeric in nature, e.g. counters, memory usage, latency metrics,
-	// etc.
-	stats.StatsClientMethods
-	pprof.PProfClientMethods
-}
-
-// DebugClientStub adds universal methods to DebugClientMethods.
-type DebugClientStub interface {
-	DebugClientMethods
-	__ipc.UniversalServiceMethods
-}
-
-// DebugClient returns a client stub for Debug.
-func DebugClient(name string, opts ...__ipc.BindOpt) DebugClientStub {
-	var client __ipc.Client
-	for _, opt := range opts {
-		if clientOpt, ok := opt.(__ipc.Client); ok {
-			client = clientOpt
-		}
-	}
-	return implDebugClientStub{name, client, logreader.LogFileClient(name, client), stats.StatsClient(name, client), pprof.PProfClient(name, client)}
-}
-
-type implDebugClientStub struct {
-	name   string
-	client __ipc.Client
-
-	logreader.LogFileClientStub
-	stats.StatsClientStub
-	pprof.PProfClientStub
-}
-
-func (c implDebugClientStub) c(ctx __context.T) __ipc.Client {
-	if c.client != nil {
-		return c.client
-	}
-	return __veyron2.RuntimeFromContext(ctx).Client()
-}
-
-func (c implDebugClientStub) Signature(ctx __context.T, opts ...__ipc.CallOpt) (o0 __ipc.ServiceSignature, err error) {
-	var call __ipc.Call
-	if call, err = c.c(ctx).StartCall(ctx, c.name, "Signature", nil, opts...); err != nil {
-		return
-	}
-	if ierr := call.Finish(&o0, &err); ierr != nil {
-		err = ierr
-	}
-	return
-}
-
-// DebugServerMethods is the interface a server writer
-// implements for Debug.
-//
-// This interface exists only for the purpose of generating a valid Signature
-// for all the interfaces that are served by the debug server. It should be
-// removed when https://code.google.com/p/envyor/issues/detail?id=285 is
-// resolved.
-type DebugServerMethods interface {
-	// LogFile can be used to access log files remotely.
-	logreader.LogFileServerMethods
-	// The Stats interface is used to access stats for troubleshooting and
-	// monitoring purposes. The stats objects are discoverable via the Globbable
-	// interface and watchable via the GlobWatcher interface.
-	//
-	// The types of the object values are implementation specific, but should be
-	// primarily numeric in nature, e.g. counters, memory usage, latency metrics,
-	// etc.
-	stats.StatsServerMethods
-	pprof.PProfServerMethods
-}
-
-// DebugServerStubMethods is the server interface containing
-// Debug methods, as expected by ipc.Server.
-// The only difference between this interface and DebugServerMethods
-// is the streaming methods.
-type DebugServerStubMethods interface {
-	// LogFile can be used to access log files remotely.
-	logreader.LogFileServerStubMethods
-	// The Stats interface is used to access stats for troubleshooting and
-	// monitoring purposes. The stats objects are discoverable via the Globbable
-	// interface and watchable via the GlobWatcher interface.
-	//
-	// The types of the object values are implementation specific, but should be
-	// primarily numeric in nature, e.g. counters, memory usage, latency metrics,
-	// etc.
-	stats.StatsServerStubMethods
-	pprof.PProfServerStubMethods
-}
-
-// DebugServerStub adds universal methods to DebugServerStubMethods.
-type DebugServerStub interface {
-	DebugServerStubMethods
-	// Describe the Debug interfaces.
-	Describe__() []__ipc.InterfaceDesc
-	// Signature will be replaced with Describe__.
-	Signature(ctx __ipc.ServerContext) (__ipc.ServiceSignature, error)
-}
-
-// DebugServer returns a server stub for Debug.
-// It converts an implementation of DebugServerMethods into
-// an object that may be used by ipc.Server.
-func DebugServer(impl DebugServerMethods) DebugServerStub {
-	stub := implDebugServerStub{
-		impl:              impl,
-		LogFileServerStub: logreader.LogFileServer(impl),
-		StatsServerStub:   stats.StatsServer(impl),
-		PProfServerStub:   pprof.PProfServer(impl),
-	}
-	// Initialize GlobState; always check the stub itself first, to handle the
-	// case where the user has the Glob method defined in their VDL source.
-	if gs := __ipc.NewGlobState(stub); gs != nil {
-		stub.gs = gs
-	} else if gs := __ipc.NewGlobState(impl); gs != nil {
-		stub.gs = gs
-	}
-	return stub
-}
-
-type implDebugServerStub struct {
-	impl DebugServerMethods
-	logreader.LogFileServerStub
-	stats.StatsServerStub
-	pprof.PProfServerStub
-	gs *__ipc.GlobState
-}
-
-func (s implDebugServerStub) VGlob() *__ipc.GlobState {
-	return s.gs
-}
-
-func (s implDebugServerStub) Describe__() []__ipc.InterfaceDesc {
-	return []__ipc.InterfaceDesc{DebugDesc, logreader.LogFileDesc, stats.StatsDesc, pprof.PProfDesc}
-}
-
-// DebugDesc describes the Debug interface.
-var DebugDesc __ipc.InterfaceDesc = descDebug
-
-// descDebug hides the desc to keep godoc clean.
-var descDebug = __ipc.InterfaceDesc{
-	Name:    "Debug",
-	PkgPath: "veyron.io/veyron/veyron/services/mgmt/debug",
-	Doc:     "// This interface exists only for the purpose of generating a valid Signature\n// for all the interfaces that are served by the debug server. It should be\n// removed when https://code.google.com/p/envyor/issues/detail?id=285 is\n// resolved.",
-	Embeds: []__ipc.EmbedDesc{
-		{"LogFile", "veyron.io/veyron/veyron2/services/mgmt/logreader", "// LogFile can be used to access log files remotely."},
-		{"Stats", "veyron.io/veyron/veyron2/services/mgmt/stats", "// The Stats interface is used to access stats for troubleshooting and\n// monitoring purposes. The stats objects are discoverable via the Globbable\n// interface and watchable via the GlobWatcher interface.\n//\n// The types of the object values are implementation specific, but should be\n// primarily numeric in nature, e.g. counters, memory usage, latency metrics,\n// etc."},
-		{"PProf", "veyron.io/veyron/veyron2/services/mgmt/pprof", ``},
-	},
-}
-
-func (s implDebugServerStub) Signature(ctx __ipc.ServerContext) (__ipc.ServiceSignature, error) {
-	// TODO(toddw): Replace with new Describe__ implementation.
-	result := __ipc.ServiceSignature{Methods: make(map[string]__ipc.MethodSignature)}
-
-	result.TypeDefs = []__vdlutil.Any{}
-	var ss __ipc.ServiceSignature
-	var firstAdded int
-	ss, _ = s.LogFileServerStub.Signature(ctx)
-	firstAdded = len(result.TypeDefs)
-	for k, v := range ss.Methods {
-		for i, _ := range v.InArgs {
-			if v.InArgs[i].Type >= __wiretype.TypeIDFirst {
-				v.InArgs[i].Type += __wiretype.TypeID(firstAdded)
-			}
-		}
-		for i, _ := range v.OutArgs {
-			if v.OutArgs[i].Type >= __wiretype.TypeIDFirst {
-				v.OutArgs[i].Type += __wiretype.TypeID(firstAdded)
-			}
-		}
-		if v.InStream >= __wiretype.TypeIDFirst {
-			v.InStream += __wiretype.TypeID(firstAdded)
-		}
-		if v.OutStream >= __wiretype.TypeIDFirst {
-			v.OutStream += __wiretype.TypeID(firstAdded)
-		}
-		result.Methods[k] = v
-	}
-	//TODO(bprosnitz) combine type definitions from embeded interfaces in a way that doesn't cause duplication.
-	for _, d := range ss.TypeDefs {
-		switch wt := d.(type) {
-		case __wiretype.SliceType:
-			if wt.Elem >= __wiretype.TypeIDFirst {
-				wt.Elem += __wiretype.TypeID(firstAdded)
-			}
-			d = wt
-		case __wiretype.ArrayType:
-			if wt.Elem >= __wiretype.TypeIDFirst {
-				wt.Elem += __wiretype.TypeID(firstAdded)
-			}
-			d = wt
-		case __wiretype.MapType:
-			if wt.Key >= __wiretype.TypeIDFirst {
-				wt.Key += __wiretype.TypeID(firstAdded)
-			}
-			if wt.Elem >= __wiretype.TypeIDFirst {
-				wt.Elem += __wiretype.TypeID(firstAdded)
-			}
-			d = wt
-		case __wiretype.StructType:
-			for i, fld := range wt.Fields {
-				if fld.Type >= __wiretype.TypeIDFirst {
-					wt.Fields[i].Type += __wiretype.TypeID(firstAdded)
-				}
-			}
-			d = wt
-			// NOTE: other types are missing, but we are upgrading anyways.
-		}
-		result.TypeDefs = append(result.TypeDefs, d)
-	}
-	ss, _ = s.StatsServerStub.Signature(ctx)
-	firstAdded = len(result.TypeDefs)
-	for k, v := range ss.Methods {
-		for i, _ := range v.InArgs {
-			if v.InArgs[i].Type >= __wiretype.TypeIDFirst {
-				v.InArgs[i].Type += __wiretype.TypeID(firstAdded)
-			}
-		}
-		for i, _ := range v.OutArgs {
-			if v.OutArgs[i].Type >= __wiretype.TypeIDFirst {
-				v.OutArgs[i].Type += __wiretype.TypeID(firstAdded)
-			}
-		}
-		if v.InStream >= __wiretype.TypeIDFirst {
-			v.InStream += __wiretype.TypeID(firstAdded)
-		}
-		if v.OutStream >= __wiretype.TypeIDFirst {
-			v.OutStream += __wiretype.TypeID(firstAdded)
-		}
-		result.Methods[k] = v
-	}
-	//TODO(bprosnitz) combine type definitions from embeded interfaces in a way that doesn't cause duplication.
-	for _, d := range ss.TypeDefs {
-		switch wt := d.(type) {
-		case __wiretype.SliceType:
-			if wt.Elem >= __wiretype.TypeIDFirst {
-				wt.Elem += __wiretype.TypeID(firstAdded)
-			}
-			d = wt
-		case __wiretype.ArrayType:
-			if wt.Elem >= __wiretype.TypeIDFirst {
-				wt.Elem += __wiretype.TypeID(firstAdded)
-			}
-			d = wt
-		case __wiretype.MapType:
-			if wt.Key >= __wiretype.TypeIDFirst {
-				wt.Key += __wiretype.TypeID(firstAdded)
-			}
-			if wt.Elem >= __wiretype.TypeIDFirst {
-				wt.Elem += __wiretype.TypeID(firstAdded)
-			}
-			d = wt
-		case __wiretype.StructType:
-			for i, fld := range wt.Fields {
-				if fld.Type >= __wiretype.TypeIDFirst {
-					wt.Fields[i].Type += __wiretype.TypeID(firstAdded)
-				}
-			}
-			d = wt
-			// NOTE: other types are missing, but we are upgrading anyways.
-		}
-		result.TypeDefs = append(result.TypeDefs, d)
-	}
-	ss, _ = s.PProfServerStub.Signature(ctx)
-	firstAdded = len(result.TypeDefs)
-	for k, v := range ss.Methods {
-		for i, _ := range v.InArgs {
-			if v.InArgs[i].Type >= __wiretype.TypeIDFirst {
-				v.InArgs[i].Type += __wiretype.TypeID(firstAdded)
-			}
-		}
-		for i, _ := range v.OutArgs {
-			if v.OutArgs[i].Type >= __wiretype.TypeIDFirst {
-				v.OutArgs[i].Type += __wiretype.TypeID(firstAdded)
-			}
-		}
-		if v.InStream >= __wiretype.TypeIDFirst {
-			v.InStream += __wiretype.TypeID(firstAdded)
-		}
-		if v.OutStream >= __wiretype.TypeIDFirst {
-			v.OutStream += __wiretype.TypeID(firstAdded)
-		}
-		result.Methods[k] = v
-	}
-	//TODO(bprosnitz) combine type definitions from embeded interfaces in a way that doesn't cause duplication.
-	for _, d := range ss.TypeDefs {
-		switch wt := d.(type) {
-		case __wiretype.SliceType:
-			if wt.Elem >= __wiretype.TypeIDFirst {
-				wt.Elem += __wiretype.TypeID(firstAdded)
-			}
-			d = wt
-		case __wiretype.ArrayType:
-			if wt.Elem >= __wiretype.TypeIDFirst {
-				wt.Elem += __wiretype.TypeID(firstAdded)
-			}
-			d = wt
-		case __wiretype.MapType:
-			if wt.Key >= __wiretype.TypeIDFirst {
-				wt.Key += __wiretype.TypeID(firstAdded)
-			}
-			if wt.Elem >= __wiretype.TypeIDFirst {
-				wt.Elem += __wiretype.TypeID(firstAdded)
-			}
-			d = wt
-		case __wiretype.StructType:
-			for i, fld := range wt.Fields {
-				if fld.Type >= __wiretype.TypeIDFirst {
-					wt.Fields[i].Type += __wiretype.TypeID(firstAdded)
-				}
-			}
-			d = wt
-			// NOTE: other types are missing, but we are upgrading anyways.
-		}
-		result.TypeDefs = append(result.TypeDefs, d)
-	}
-
-	return result, nil
-}
diff --git a/services/mgmt/debug/dispatcher.go b/services/mgmt/debug/dispatcher.go
index c4ece25..e11cf1e 100644
--- a/services/mgmt/debug/dispatcher.go
+++ b/services/mgmt/debug/dispatcher.go
@@ -30,7 +30,7 @@
 // The first part of the names of the objects served by this dispatcher.
 var rootName = "__debug"
 
-func (d *dispatcher) Lookup(suffix, method string) (interface{}, security.Authorizer, error) {
+func (d *dispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) {
 	if suffix == "" {
 		return ipc.VChildrenGlobberInvoker(rootName), d.auth, nil
 	}
@@ -40,9 +40,6 @@
 	suffix = strings.TrimPrefix(suffix, rootName)
 	suffix = strings.TrimLeft(suffix, "/")
 
-	if method == "Signature" {
-		return NewSignatureObject(suffix), d.auth, nil
-	}
 	if suffix == "" {
 		return ipc.VChildrenGlobberInvoker("logs", "pprof", "stats", "vtrace"), d.auth, nil
 	}
@@ -54,11 +51,11 @@
 	}
 	switch parts[0] {
 	case "logs":
-		return logreaderimpl.NewLogFileServer(d.logsDir, suffix), d.auth, nil
+		return logreaderimpl.NewLogFileService(d.logsDir, suffix), d.auth, nil
 	case "pprof":
-		return pprofimpl.NewServer(), d.auth, nil
+		return pprofimpl.NewPProfService(), d.auth, nil
 	case "stats":
-		return statsimpl.NewStatsServer(suffix, 10*time.Second), d.auth, nil
+		return statsimpl.NewStatsService(suffix, 10*time.Second), d.auth, nil
 	case "vtrace":
 		return vtraceimpl.NewVtraceService(d.store), d.auth, nil
 	}
diff --git a/services/mgmt/debug/signature.go b/services/mgmt/debug/signature.go
deleted file mode 100644
index 6781010..0000000
--- a/services/mgmt/debug/signature.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package debug
-
-import (
-	"strings"
-
-	"veyron.io/veyron/veyron2/ipc"
-)
-
-type signatureInvoker struct {
-	suffix string
-}
-
-// NewSignatureObject is the signature object factory.
-func NewSignatureObject(suffix string) interface{} {
-	return &signatureInvoker{suffix}
-}
-
-// TODO(rthellend): This is a temporary hack until https://code.google.com/p/envyor/issues/detail?id=285 is resolved.
-func (s signatureInvoker) Signature(ipc.ServerContext) (ipc.ServiceSignature, error) {
-	debugStub := DebugServer(nil)
-	fullSig, _ := debugStub.Signature(nil)
-
-	var show []string
-	if strings.HasPrefix(s.suffix, "logs") {
-		show = []string{"ReadLog", "Size", "Glob"}
-	} else if strings.HasPrefix(s.suffix, "pprof") {
-		show = []string{"Cmdline", "CPUProfile", "Profile", "Profiles", "Symbol"}
-	} else if strings.HasPrefix(s.suffix, "stats") {
-		show = []string{"Value", "Glob", "WatchGlob"}
-	} else {
-		show = []string{"Glob"}
-	}
-
-	sig := ipc.ServiceSignature{TypeDefs: fullSig.TypeDefs, Methods: make(map[string]ipc.MethodSignature)}
-	for _, m := range show {
-		sig.Methods[m] = fullSig.Methods[m]
-	}
-	return sig, nil
-}
diff --git a/services/mgmt/logreader/impl/logfile.go b/services/mgmt/logreader/impl/logfile.go
index 52936af..fd6f354 100644
--- a/services/mgmt/logreader/impl/logfile.go
+++ b/services/mgmt/logreader/impl/logfile.go
@@ -12,6 +12,7 @@
 	"strings"
 
 	"veyron.io/veyron/veyron2/ipc"
+	"veyron.io/veyron/veyron2/services/mgmt/logreader"
 	"veyron.io/veyron/veyron2/services/mgmt/logreader/types"
 	"veyron.io/veyron/veyron2/verror"
 	"veyron.io/veyron/veyron2/vlog"
@@ -24,9 +25,9 @@
 	errOperationFailed = verror.Internalf("operation failed")
 )
 
-// NewLogFileServer returns a new log file server.
-func NewLogFileServer(root, suffix string) interface{} {
-	return &logfileImpl{filepath.Clean(root), suffix}
+// NewLogFileService returns a new log file server.
+func NewLogFileService(root, suffix string) interface{} {
+	return logreader.LogFileServer(&logfileService{filepath.Clean(root), suffix})
 }
 
 // translateNameToFilename returns the file name that corresponds to the object
@@ -43,8 +44,8 @@
 	return p, nil
 }
 
-// logfileImpl holds the state of a logfile invocation.
-type logfileImpl struct {
+// logfileService holds the state of a logfile invocation.
+type logfileService struct {
 	// root is the root directory from which the object names are based.
 	root string
 	// suffix is the suffix of the current invocation that is assumed to
@@ -53,7 +54,7 @@
 }
 
 // Size returns the size of the log file, in bytes.
-func (i *logfileImpl) Size(call ipc.ServerCall) (int64, error) {
+func (i *logfileService) Size(ipc.ServerContext) (int64, error) {
 	vlog.VI(1).Infof("%v.Size()", i.suffix)
 	fname, err := translateNameToFilename(i.root, i.suffix)
 	if err != nil {
@@ -74,7 +75,7 @@
 }
 
 // ReadLog returns log entries from the log file.
-func (i *logfileImpl) ReadLog(call ipc.ServerCall, startpos int64, numEntries int32, follow bool) (int64, error) {
+func (i *logfileService) ReadLog(ctx logreader.LogFileReadLogContext, startpos int64, numEntries int32, follow bool) (int64, error) {
 	vlog.VI(1).Infof("%v.ReadLog(%v, %v, %v)", i.suffix, startpos, numEntries, follow)
 	fname, err := translateNameToFilename(i.root, i.suffix)
 	if err != nil {
@@ -87,7 +88,7 @@
 		}
 		return 0, errOperationFailed
 	}
-	reader := newFollowReader(call, f, startpos, follow)
+	reader := newFollowReader(ctx, f, startpos, follow)
 	if numEntries == types.AllEntries {
 		numEntries = int32(math.MaxInt32)
 	}
@@ -102,7 +103,7 @@
 		if err != nil {
 			return reader.tell(), errOperationFailed
 		}
-		if err := call.Send(types.LogEntry{Position: offset, Line: line}); err != nil {
+		if err := ctx.SendStream().Send(types.LogEntry{Position: offset, Line: line}); err != nil {
 			return reader.tell(), err
 		}
 	}
@@ -111,7 +112,7 @@
 
 // VGlobChildren returns the list of files in a directory, or an empty list if
 // the object is a file.
-func (i *logfileImpl) VGlobChildren() ([]string, error) {
+func (i *logfileService) VGlobChildren() ([]string, error) {
 	vlog.VI(1).Infof("%v.VGlobChildren()", i.suffix)
 	dirName, err := translateNameToFilename(i.root, i.suffix)
 	if err != nil {
diff --git a/services/mgmt/logreader/impl/logfile_test.go b/services/mgmt/logreader/impl/logfile_test.go
index 5d48e15..11a6ae1 100644
--- a/services/mgmt/logreader/impl/logfile_test.go
+++ b/services/mgmt/logreader/impl/logfile_test.go
@@ -46,8 +46,8 @@
 	root string
 }
 
-func (d *logFileDispatcher) Lookup(suffix, _ string) (interface{}, security.Authorizer, error) {
-	return impl.NewLogFileServer(d.root, suffix), nil, nil
+func (d *logFileDispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) {
+	return impl.NewLogFileService(d.root, suffix), nil, nil
 }
 
 func writeAndSync(t *testing.T, w *os.File, s string) {
diff --git a/services/mgmt/logreader/impl/reader.go b/services/mgmt/logreader/impl/reader.go
index 0a063b1..97d1272 100644
--- a/services/mgmt/logreader/impl/reader.go
+++ b/services/mgmt/logreader/impl/reader.go
@@ -14,7 +14,7 @@
 // - it aborts when the parent RPC is canceled.
 type followReader struct {
 	reader io.ReadSeeker
-	call   ipc.ServerCall
+	ctx    ipc.ServerContext
 	offset int64
 	follow bool
 	err    error
@@ -22,11 +22,11 @@
 }
 
 // newFollowReader is the factory for followReader.
-func newFollowReader(call ipc.ServerCall, reader io.ReadSeeker, startpos int64, follow bool) *followReader {
+func newFollowReader(ctx ipc.ServerContext, reader io.ReadSeeker, startpos int64, follow bool) *followReader {
 	_, err := reader.Seek(startpos, 0)
 	return &followReader{
 		reader: reader,
-		call:   call,
+		ctx:    ctx,
 		offset: startpos,
 		follow: follow,
 		err:    err,
@@ -43,9 +43,9 @@
 		return 0, f.err
 	}
 	for {
-		if f.call != nil {
+		if f.ctx != nil {
 			select {
-			case <-f.call.Done():
+			case <-f.ctx.Done():
 				return 0, errCanceled
 			default:
 			}
diff --git a/services/mgmt/node/impl/app_service.go b/services/mgmt/node/impl/app_service.go
index f64e908..3473a37 100644
--- a/services/mgmt/node/impl/app_service.go
+++ b/services/mgmt/node/impl/app_service.go
@@ -554,7 +554,7 @@
 	return installationDirCore(i.suffix, i.config.Root)
 }
 
-func initializeInstanceACLs(key, installationDir, instanceDir string, blessings []string, acl security.ACL) error {
+func initializeInstanceACLs(instanceDir string, blessings []string, acl security.ACL) error {
 	if acl.In == nil {
 		// The acl.In will be empty for an unclaimed node manager. In this case,
 		// create it
@@ -610,7 +610,7 @@
 		return instanceDir, instanceID, err
 	}
 
-	if err := initializeInstanceACLs(installationDir, installationDir, instanceDir, call.RemoteBlessings().ForContext(call), i.nodeACL); err != nil {
+	if err := initializeInstanceACLs(instanceDir, call.RemoteBlessings().ForContext(call), i.nodeACL); err != nil {
 		return instanceDir, instanceID, err
 	}
 	return instanceDir, instanceID, nil
@@ -1207,13 +1207,12 @@
 }
 
 // TODO(rjkroege): Consider maintaining an in-memory ACL cache.
-// TODO(rjkroege): Excise the idea of the key. Use the dir instead.
 func (i *appService) SetACL(_ ipc.ServerContext, acl security.ACL, etag string) error {
 	dir, err := dirFromSuffix(i.suffix, i.config.Root)
 	if err != nil {
 		return err
 	}
-	return setAppACL(i.locks, dir, dir, acl, etag)
+	return setAppACL(i.locks, dir, acl, etag)
 }
 
 func (i *appService) GetACL(_ ipc.ServerContext) (acl security.ACL, etag string, err error) {
@@ -1221,5 +1220,5 @@
 	if err != nil {
 		return security.ACL{}, "", err
 	}
-	return getAppACL(i.locks, dir, dir)
+	return getAppACL(i.locks, dir)
 }
diff --git a/services/mgmt/node/impl/dispatcher.go b/services/mgmt/node/impl/dispatcher.go
index be18829..dd4b647 100644
--- a/services/mgmt/node/impl/dispatcher.go
+++ b/services/mgmt/node/impl/dispatcher.go
@@ -184,15 +184,15 @@
 }
 
 // TODO(rjkroege): Further refactor ACL-setting code.
-func setAppACL(locks aclLocks, key, dir string, acl security.ACL, etag string) error {
+func setAppACL(locks aclLocks, dir string, acl security.ACL, etag string) error {
 	aclpath := path.Join(dir, "acls", "data")
 	sigpath := path.Join(dir, "acls", "signature")
 
 	// Acquire lock. Locks are per path to an acls file.
-	lck, contains := locks[key]
+	lck, contains := locks[dir]
 	if !contains {
 		lck = new(sync.Mutex)
-		locks[key] = lck
+		locks[dir] = lck
 	}
 	lck.Lock()
 	defer lck.Unlock()
@@ -222,15 +222,14 @@
 	return writeACLs(aclpath, sigpath, dir, acl)
 }
 
-// TODO(rjkroege): Use the dir as the key.
-func getAppACL(locks aclLocks, key, dir string) (security.ACL, string, error) {
+func getAppACL(locks aclLocks, dir string) (security.ACL, string, error) {
 	aclpath := path.Join(dir, "acls", "data")
 
 	// Acquire lock. Locks are per path to an acls file.
-	lck, contains := locks[key]
+	lck, contains := locks[dir]
 	if !contains {
 		lck = new(sync.Mutex)
-		locks[key] = lck
+		locks[dir] = lck
 	}
 	lck.Lock()
 	defer lck.Unlock()
@@ -338,7 +337,7 @@
 }
 
 // DISPATCHER INTERFACE IMPLEMENTATION
-func (d *dispatcher) Lookup(suffix, method string) (interface{}, security.Authorizer, error) {
+func (d *dispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) {
 	components := strings.Split(suffix, "/")
 	for i := 0; i < len(components); i++ {
 		if len(components[i]) == 0 {
@@ -347,10 +346,7 @@
 		}
 	}
 	if len(components) == 0 {
-		if method == ipc.GlobMethod {
-			return ipc.VChildrenGlobberInvoker(nodeSuffix, appsSuffix), d.auth, nil
-		}
-		return nil, nil, errInvalidSuffix
+		return ipc.VChildrenGlobberInvoker(nodeSuffix, appsSuffix), d.auth, nil
 	}
 	// The implementation of the node manager is split up into several
 	// invokers, which are instantiated depending on the receiver name
@@ -366,7 +362,7 @@
 		})
 		return receiver, d.auth, nil
 	case appsSuffix:
-		// Requests to apps/*/*/*/logs are handled locally by LogFileServer.
+		// Requests to apps/*/*/*/logs are handled locally by LogFileService.
 		// Requests to apps/*/*/*/pprof are proxied to the apps' __debug/pprof object.
 		// Requests to apps/*/*/*/stats are proxied to the apps' __debug/stats object.
 		// Everything else is handled by the Application server.
@@ -379,7 +375,7 @@
 			case "logs":
 				logsDir := filepath.Join(appInstanceDir, "logs")
 				suffix := naming.Join(components[5:]...)
-				return logsimpl.NewLogFileServer(logsDir, suffix), d.auth, nil
+				return logsimpl.NewLogFileService(logsDir, suffix), d.auth, nil
 			case "pprof", "stats":
 				info, err := loadInstanceInfo(appInstanceDir)
 				if err != nil {
diff --git a/services/mgmt/node/impl/impl_test.go b/services/mgmt/node/impl/impl_test.go
index 37b5bb9..0baca5b 100644
--- a/services/mgmt/node/impl/impl_test.go
+++ b/services/mgmt/node/impl/impl_test.go
@@ -10,6 +10,7 @@
 	"encoding/base64"
 	"encoding/hex"
 	"encoding/json"
+	"flag"
 	"fmt"
 	"io"
 	"io/ioutil"
@@ -73,6 +74,17 @@
 		return
 	}
 	initRT()
+
+	// NOTE(caprita): The default 1m timeout that the modules package gives
+	// to subprocesses is insufficient, especially with --race.
+	// If not explicitly set to a non-default value by the user, we set the
+	// test-wide timeout to 2 minutes (which will get propagated by the
+	// modules framework to the subprocesses).
+	timeoutFlag := flag.Lookup("test.timeout")
+	if timeoutFlag.Value.String() == timeoutFlag.DefValue {
+		timeoutFlag.Value.Set("2m")
+		vlog.Infof("Setting test.timeout to 2m.")
+	}
 }
 
 func initRT() {
@@ -190,16 +202,10 @@
 	// args[0] is the entrypoint for the binary to be run from the shell script
 	// that SelfInstall will write out.
 	entrypoint := args[0]
-	osenv := make([]string, 0, len(env))
-	for k, v := range env {
-		if k == modules.ShellEntryPoint {
-			// Overwrite the entrypoint in our environment (i.e. the one
-			// that got us here), with the one we want written out in the shell
-			// script.
-			v = entrypoint
-		}
-		osenv = append(osenv, k+"="+v)
-	}
+	// Overwrite the entrypoint in our environment (i.e. the one
+	// that got us here), with the one we want written out in the shell
+	// script.
+	osenv := modules.SetEntryPoint(env, entrypoint)
 	if args[1] != "--" {
 		vlog.Fatalf("expected '--' immediately following command name")
 	}
@@ -367,12 +373,6 @@
 		t.Fatalf("current link didn't change")
 	}
 
-	// This is from the child node manager started by the node manager
-	// as an update test.
-	readPID(t, nms)
-
-	nms.Expect("v2NM terminating")
-
 	updateNodeExpectError(t, "factoryNM", verror.Exists) // Update already in progress.
 
 	nmh.CloseStdin()
@@ -399,8 +399,7 @@
 	// Create a third version of the node manager and issue an update.
 	crDir, crEnv = credentialsForChild("nodemanager")
 	defer os.RemoveAll(crDir)
-	*envelope = envelopeFromShell(sh, crEnv, nodeManagerCmd, application.NodeManagerTitle,
-		"v3NM")
+	*envelope = envelopeFromShell(sh, crEnv, nodeManagerCmd, application.NodeManagerTitle, "v3NM")
 	updateNode(t, "v2NM")
 
 	scriptPathV3 := evalLink()
@@ -408,12 +407,7 @@
 		t.Fatalf("current link didn't change")
 	}
 
-	// This is from the child node manager started by the node manager
-	// as an update test.
-	readPID(t, nms)
-	// Both the parent and child node manager should terminate upon successful
-	// update.
-	nms.ExpectSetRE("v3NM terminating", "v2NM terminating")
+	nms.Expect("v2NM terminating")
 
 	nmh.Shutdown(os.Stderr, os.Stderr)
 
diff --git a/services/mgmt/node/impl/node_service.go b/services/mgmt/node/impl/node_service.go
index 1ddf4da..773d4d9 100644
--- a/services/mgmt/node/impl/node_service.go
+++ b/services/mgmt/node/impl/node_service.go
@@ -183,33 +183,29 @@
 	path := filepath.Join(workspace, "noded.sh")
 	cmd := exec.Command(path)
 
-	// We use a combination of a pipe and file to capture output from
-	// the child node manager more reliably than merging their
-	// stdout, stderr file descriptors.
-
-	// Using a pipe reduces the likelihood of the two processes corrupting
-	// each other's output.
-	stdout, err := cmd.StdoutPipe()
-	if err != nil {
-		vlog.Errorf("StdoutPipe() failed: %v", err)
-		return errOperationFailed
-	}
-	go func(r io.Reader) {
-		scanner := bufio.NewScanner(r)
-		for scanner.Scan() {
-			fmt.Fprintf(os.Stdout, "%s\n", scanner.Text())
+	for k, v := range map[string]*io.Writer{
+		"stdout": &cmd.Stdout,
+		"stderr": &cmd.Stderr,
+	} {
+		// Using a log file makes it less likely that stdout and stderr
+		// output will be lost if the child crashes.
+		file, err := i.newLogfile(fmt.Sprintf("noded-test-%s", k))
+		if err != nil {
+			return err
 		}
-	}(stdout)
+		fName := file.Name()
+		defer os.Remove(fName)
+		*v = file
 
-	// Using a log file for stderr makes it less likely that error output
-	// will be lost if the child crashes.
-	stderr, err := i.newLogfile("noded-test-stderr")
-	if err != nil {
-		return err
+		defer func() {
+			if f, err := os.Open(fName); err == nil {
+				scanner := bufio.NewScanner(f)
+				for scanner.Scan() {
+					vlog.Infof("[testNodeManager %s] %s", k, scanner.Text())
+				}
+			}
+		}()
 	}
-	defer os.Remove(stderr.Name())
-
-	cmd.Stderr = stderr
 
 	// Setup up the child process callback.
 	callbackState := i.callback
@@ -234,13 +230,6 @@
 				vlog.Errorf("Clean() failed: %v", err)
 			}
 		}
-		logFile := stderr.Name()
-		if f, err := os.Open(logFile); err == nil {
-			scanner := bufio.NewScanner(f)
-			for scanner.Scan() {
-				vlog.Infof("%s", scanner.Text())
-			}
-		}
 	}()
 	// Wait for the child process to start.
 	testTimeout := 10 * time.Second
diff --git a/services/mgmt/node/impl/only_for_test.go b/services/mgmt/node/impl/only_for_test.go
index b110699..8424d36 100644
--- a/services/mgmt/node/impl/only_for_test.go
+++ b/services/mgmt/node/impl/only_for_test.go
@@ -25,6 +25,9 @@
 
 func init() {
 	cleanupDir = func(dir, helper string) {
+		if dir == "" {
+			return
+		}
 		parentDir, base := filepath.Dir(dir), filepath.Base(dir)
 		var renamed string
 		if helper != "" {
diff --git a/services/mgmt/node/impl/proxy_invoker_test.go b/services/mgmt/node/impl/proxy_invoker_test.go
index 3f3e01c..0fcb836 100644
--- a/services/mgmt/node/impl/proxy_invoker_test.go
+++ b/services/mgmt/node/impl/proxy_invoker_test.go
@@ -101,6 +101,6 @@
 	sigStub signatureStub
 }
 
-func (d *proxyDispatcher) Lookup(suffix, method string) (interface{}, security.Authorizer, error) {
+func (d *proxyDispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) {
 	return &proxyInvoker{naming.Join(d.remote, suffix), d.label, d.sigStub}, nil, nil
 }
diff --git a/services/mgmt/node/impl/util_test.go b/services/mgmt/node/impl/util_test.go
index 4e8a866..0db0633 100644
--- a/services/mgmt/node/impl/util_test.go
+++ b/services/mgmt/node/impl/util_test.go
@@ -8,6 +8,7 @@
 	"reflect"
 	"runtime"
 	"sort"
+	"strings"
 	"testing"
 	"time"
 
@@ -59,7 +60,7 @@
 }
 
 func createShellAndMountTable(t *testing.T) (*modules.Shell, func()) {
-	sh := core.NewShell()
+	sh := modules.NewShell()
 	// The shell, will, by default share credentials with its children.
 	sh.ClearVar(consts.VeyronCredentials)
 
@@ -159,10 +160,18 @@
 	if err != nil {
 		t.Fatalf("Resolve(%v) failed: %v", name, err)
 	}
-	if want, got := replicas, len(results); want != got {
+
+	filteredResults := []string{}
+	for _, r := range results {
+		if strings.Index(r, "@tcp") != -1 {
+			filteredResults = append(filteredResults, r)
+		}
+	}
+	// We are going to get a websocket and a tcp endpoint for each replica.
+	if want, got := replicas, len(filteredResults); want != got {
 		t.Fatalf("Resolve(%v) expected %d result(s), got %d instead", name, want, got)
 	}
-	return results
+	return filteredResults
 }
 
 // The following set of functions are convenience wrappers around Update and
diff --git a/services/mgmt/pprof/client/proxy_test.go b/services/mgmt/pprof/client/proxy_test.go
index 3fa6ace..bce0e99 100644
--- a/services/mgmt/pprof/client/proxy_test.go
+++ b/services/mgmt/pprof/client/proxy_test.go
@@ -19,7 +19,7 @@
 	server interface{}
 }
 
-func (d *dispatcher) Lookup(suffix, method string) (interface{}, security.Authorizer, error) {
+func (d *dispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) {
 	return d.server, nil, nil
 }
 
@@ -36,7 +36,7 @@
 	if err != nil {
 		t.Fatalf("failed to listen: %v", err)
 	}
-	if err := s.ServeDispatcher("", &dispatcher{impl.NewServer()}); err != nil {
+	if err := s.ServeDispatcher("", &dispatcher{impl.NewPProfService()}); err != nil {
 		t.Fatalf("failed to serve: %v", err)
 	}
 	l, err := client.StartProxy(r, naming.JoinAddressName(endpoint.String(), ""))
diff --git a/services/mgmt/pprof/impl/server.go b/services/mgmt/pprof/impl/server.go
index 842aaf2..21e300d 100644
--- a/services/mgmt/pprof/impl/server.go
+++ b/services/mgmt/pprof/impl/server.go
@@ -8,24 +8,25 @@
 	"time"
 
 	"veyron.io/veyron/veyron2/ipc"
+	spprof "veyron.io/veyron/veyron2/services/mgmt/pprof"
 	"veyron.io/veyron/veyron2/verror"
 )
 
-// NewServer returns a new pprof server implementation.
-func NewServer() interface{} {
-	return &pprofImpl{}
+// NewPProfService returns a new pprof service implementation.
+func NewPProfService() interface{} {
+	return spprof.PProfServer(&pprofService{})
 }
 
-type pprofImpl struct {
+type pprofService struct {
 }
 
 // CmdLine returns the command-line argument of the server.
-func (pprofImpl) CmdLine(ipc.ServerContext) ([]string, error) {
+func (pprofService) CmdLine(ipc.ServerContext) ([]string, error) {
 	return os.Args, nil
 }
 
 // Profiles returns the list of available profiles.
-func (pprofImpl) Profiles(ipc.ServerContext) ([]string, error) {
+func (pprofService) Profiles(ipc.ServerContext) ([]string, error) {
 	profiles := pprof.Profiles()
 	results := make([]string, len(profiles))
 	for i, v := range profiles {
@@ -39,14 +40,12 @@
 // addresses that pprof needs. Passing debug=1 adds comments translating
 // addresses to function names and line numbers, so that a programmer
 // can read the profile without tools.
-//
-// TODO(toddw): Change ipc.ServerCall into a struct stub context.
-func (pprofImpl) Profile(call ipc.ServerCall, name string, debug int32) error {
+func (pprofService) Profile(ctx spprof.PProfProfileContext, name string, debug int32) error {
 	profile := pprof.Lookup(name)
 	if profile == nil {
 		return verror.NoExistf("profile does not exist")
 	}
-	if err := profile.WriteTo(&streamWriter{call}, int(debug)); err != nil {
+	if err := profile.WriteTo(&streamWriter{ctx.SendStream()}, int(debug)); err != nil {
 		return verror.Convert(err)
 	}
 	return nil
@@ -54,13 +53,11 @@
 
 // CPUProfile enables CPU profiling for the requested duration and
 // streams the profile data.
-//
-// TODO(toddw): Change ipc.ServerCall into a struct stub context.
-func (pprofImpl) CPUProfile(call ipc.ServerCall, seconds int32) error {
+func (pprofService) CPUProfile(ctx spprof.PProfCPUProfileContext, seconds int32) error {
 	if seconds <= 0 || seconds > 3600 {
 		return verror.BadArgf("invalid number of seconds: %d", seconds)
 	}
-	if err := pprof.StartCPUProfile(&streamWriter{call}); err != nil {
+	if err := pprof.StartCPUProfile(&streamWriter{ctx.SendStream()}); err != nil {
 		return verror.Convert(err)
 	}
 	time.Sleep(time.Duration(seconds) * time.Second)
@@ -70,7 +67,7 @@
 
 // Symbol looks up the program counters and returns their respective
 // function names.
-func (pprofImpl) Symbol(_ ipc.ServerContext, programCounters []uint64) ([]string, error) {
+func (pprofService) Symbol(_ ipc.ServerContext, programCounters []uint64) ([]string, error) {
 	results := make([]string, len(programCounters))
 	for i, v := range programCounters {
 		f := runtime.FuncForPC(uintptr(v))
@@ -82,11 +79,13 @@
 }
 
 type streamWriter struct {
-	call ipc.ServerCall
+	sender interface {
+		Send(item []byte) error
+	}
 }
 
 func (w *streamWriter) Write(p []byte) (int, error) {
-	if err := w.call.Send(p); err != nil {
+	if err := w.sender.Send(p); err != nil {
 		return 0, err
 	}
 	return len(p), nil
diff --git a/services/mgmt/profile/impl/dispatcher.go b/services/mgmt/profile/impl/dispatcher.go
index 6c28885..b806796 100644
--- a/services/mgmt/profile/impl/dispatcher.go
+++ b/services/mgmt/profile/impl/dispatcher.go
@@ -29,6 +29,6 @@
 
 // DISPATCHER INTERFACE IMPLEMENTATION
 
-func (d *dispatcher) Lookup(suffix, method string) (interface{}, security.Authorizer, error) {
+func (d *dispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) {
 	return repository.ProfileServer(NewProfileService(d.store, d.storeRoot, suffix)), d.auth, nil
 }
diff --git a/services/mgmt/root/impl/dispatcher.go b/services/mgmt/root/impl/dispatcher.go
index c112fe8..7d34f29 100644
--- a/services/mgmt/root/impl/dispatcher.go
+++ b/services/mgmt/root/impl/dispatcher.go
@@ -20,6 +20,6 @@
 
 // DISPATCHER INTERFACE IMPLEMENTATION
 
-func (d *dispatcher) Lookup(suffix, method string) (interface{}, security.Authorizer, error) {
+func (d *dispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) {
 	return root.RootServer(d.state), nil, nil
 }
diff --git a/services/mgmt/stats/impl/stats.go b/services/mgmt/stats/impl/stats.go
index 7f7657f..b398ee8 100644
--- a/services/mgmt/stats/impl/stats.go
+++ b/services/mgmt/stats/impl/stats.go
@@ -5,18 +5,20 @@
 import (
 	"time"
 
-	"veyron.io/veyron/veyron/lib/stats"
+	libstats "veyron.io/veyron/veyron/lib/stats"
 
 	"veyron.io/veyron/veyron2/ipc"
 	"veyron.io/veyron/veyron2/naming"
+	"veyron.io/veyron/veyron2/services/mgmt/stats"
 	"veyron.io/veyron/veyron2/services/mgmt/stats/types"
+	"veyron.io/veyron/veyron2/services/watch"
 	watchtypes "veyron.io/veyron/veyron2/services/watch/types"
 	"veyron.io/veyron/veyron2/vdl/vdlutil"
 	"veyron.io/veyron/veyron2/verror"
 	"veyron.io/veyron/veyron2/vlog"
 )
 
-type statsImpl struct {
+type statsService struct {
 	suffix    string
 	watchFreq time.Duration
 }
@@ -27,22 +29,22 @@
 	errOperationFailed = verror.Internalf("operation failed")
 )
 
-// NewStatsServer returns a stats server implementation. The value of watchFreq
+// NewStatsService returns a stats server implementation. The value of watchFreq
 // is used to specify the time between WatchGlob updates.
-func NewStatsServer(suffix string, watchFreq time.Duration) interface{} {
-	return &statsImpl{suffix, watchFreq}
+func NewStatsService(suffix string, watchFreq time.Duration) interface{} {
+	return stats.StatsServer(&statsService{suffix, watchFreq})
 }
 
 // Glob returns the name of all objects that match pattern.
-func (i *statsImpl) Glob(ctx *ipc.GlobContextStub, pattern string) error {
+func (i *statsService) Glob(ctx ipc.GlobContext, pattern string) error {
 	vlog.VI(1).Infof("%v.Glob(%q)", i.suffix, pattern)
 
-	it := stats.Glob(i.suffix, pattern, time.Time{}, false)
+	it := libstats.Glob(i.suffix, pattern, time.Time{}, false)
 	for it.Advance() {
 		ctx.SendStream().Send(naming.VDLMountEntry{Name: it.Value().Key})
 	}
 	if err := it.Err(); err != nil {
-		if err == stats.ErrNotFound {
+		if err == libstats.ErrNotFound {
 			return errNotFound
 		}
 		return errOperationFailed
@@ -52,7 +54,7 @@
 
 // WatchGlob returns the name and value of the objects that match the request,
 // followed by periodic updates when values change.
-func (i *statsImpl) WatchGlob(call ipc.ServerCall, req watchtypes.GlobRequest) error {
+func (i *statsService) WatchGlob(ctx watch.GlobWatcherWatchGlobContext, req watchtypes.GlobRequest) error {
 	vlog.VI(1).Infof("%v.WatchGlob(%+v)", i.suffix, req)
 
 	var t time.Time
@@ -60,7 +62,7 @@
 	for {
 		prevTime := t
 		t = time.Now()
-		it := stats.Glob(i.suffix, req.Pattern, prevTime, true)
+		it := libstats.Glob(i.suffix, req.Pattern, prevTime, true)
 		changes := []watchtypes.Change{}
 		for it.Advance() {
 			v := it.Value()
@@ -72,18 +74,18 @@
 			changes = append(changes, c)
 		}
 		if err := it.Err(); err != nil {
-			if err == stats.ErrNotFound {
+			if err == libstats.ErrNotFound {
 				return errNotFound
 			}
 			return errOperationFailed
 		}
 		for _, change := range changes {
-			if err := call.Send(change); err != nil {
+			if err := ctx.SendStream().Send(change); err != nil {
 				return err
 			}
 		}
 		select {
-		case <-call.Done():
+		case <-ctx.Done():
 			break Loop
 		case <-time.After(i.watchFreq):
 		}
@@ -92,14 +94,14 @@
 }
 
 // Value returns the value of the receiver object.
-func (i *statsImpl) Value(ctx ipc.ServerContext) (vdlutil.Any, error) {
+func (i *statsService) Value(ctx ipc.ServerContext) (vdlutil.Any, error) {
 	vlog.VI(1).Infof("%v.Value()", i.suffix)
 
-	v, err := stats.Value(i.suffix)
+	v, err := libstats.Value(i.suffix)
 	switch err {
-	case stats.ErrNotFound:
+	case libstats.ErrNotFound:
 		return nil, errNotFound
-	case stats.ErrNoValue:
+	case libstats.ErrNoValue:
 		return nil, errNoValue
 	case nil:
 		return v, nil
diff --git a/services/mgmt/stats/impl/stats_test.go b/services/mgmt/stats/impl/stats_test.go
index 11cb6c3..6b526f6 100644
--- a/services/mgmt/stats/impl/stats_test.go
+++ b/services/mgmt/stats/impl/stats_test.go
@@ -22,8 +22,8 @@
 type statsDispatcher struct {
 }
 
-func (d *statsDispatcher) Lookup(suffix, method string) (interface{}, security.Authorizer, error) {
-	return impl.NewStatsServer(suffix, 100*time.Millisecond), nil, nil
+func (d *statsDispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) {
+	return impl.NewStatsService(suffix, 100*time.Millisecond), nil, nil
 }
 
 func startServer(t *testing.T) (string, func()) {
diff --git a/services/mgmt/vtrace/impl/vtrace.go b/services/mgmt/vtrace/impl/vtrace.go
index 7f23763..d389436 100644
--- a/services/mgmt/vtrace/impl/vtrace.go
+++ b/services/mgmt/vtrace/impl/vtrace.go
@@ -2,16 +2,17 @@
 
 import (
 	"veyron.io/veyron/veyron2/ipc"
+	svtrace "veyron.io/veyron/veyron2/services/mgmt/vtrace"
 	"veyron.io/veyron/veyron2/uniqueid"
 	"veyron.io/veyron/veyron2/verror2"
 	"veyron.io/veyron/veyron2/vtrace"
 )
 
-type vtraceServer struct {
+type vtraceService struct {
 	store vtrace.Store
 }
 
-func (v *vtraceServer) Trace(ctx ipc.ServerContext, id uniqueid.ID) (vtrace.TraceRecord, error) {
+func (v *vtraceService) Trace(ctx ipc.ServerContext, id uniqueid.ID) (vtrace.TraceRecord, error) {
 	tr := v.store.TraceRecord(id)
 	if tr == nil {
 		return vtrace.TraceRecord{}, verror2.Make(verror2.NoExist, ctx, "No trace with id %x", id)
@@ -19,13 +20,12 @@
 	return *tr, nil
 }
 
-// TODO(toddw): Change ipc.ServerCall into a struct stub context.
-func (v *vtraceServer) AllTraces(call ipc.ServerCall) error {
+func (v *vtraceService) AllTraces(ctx svtrace.StoreAllTracesContext) error {
 	// TODO(mattr): Consider changing the store to allow us to iterate through traces
 	// when there are many.
 	traces := v.store.TraceRecords()
 	for i := range traces {
-		if err := call.Send(traces[i]); err != nil {
+		if err := ctx.SendStream().Send(traces[i]); err != nil {
 			return err
 		}
 	}
@@ -33,5 +33,5 @@
 }
 
 func NewVtraceService(store vtrace.Store) interface{} {
-	return &vtraceServer{store}
+	return svtrace.StoreServer(&vtraceService{store})
 }
diff --git a/services/mounttable/lib/collectionserver_test.go b/services/mounttable/lib/collectionserver_test.go
index 2d3be92..3bb1fb2 100644
--- a/services/mounttable/lib/collectionserver_test.go
+++ b/services/mounttable/lib/collectionserver_test.go
@@ -30,7 +30,7 @@
 }
 
 // Lookup implements ipc.Dispatcher.Lookup.
-func (d *collectionDispatcher) Lookup(name, method string) (interface{}, security.Authorizer, error) {
+func (d *collectionDispatcher) Lookup(name string) (interface{}, security.Authorizer, error) {
 	rpcc := &rpcContext{name: name, collectionServer: d.collectionServer}
 	return rpcc, d, nil
 }
diff --git a/services/mounttable/lib/mounttable.go b/services/mounttable/lib/mounttable.go
index e7cfbf1..5ca2af2 100644
--- a/services/mounttable/lib/mounttable.go
+++ b/services/mounttable/lib/mounttable.go
@@ -94,7 +94,7 @@
 }
 
 // Lookup implements ipc.Dispatcher.Lookup.
-func (mt *mountTable) Lookup(name, method string) (interface{}, security.Authorizer, error) {
+func (mt *mountTable) Lookup(name string) (interface{}, security.Authorizer, error) {
 	vlog.VI(2).Infof("*********************Lookup %s", name)
 	mt.RLock()
 	defer mt.RUnlock()
diff --git a/services/mounttable/lib/neighborhood.go b/services/mounttable/lib/neighborhood.go
index d0462ad..7d649f9 100644
--- a/services/mounttable/lib/neighborhood.go
+++ b/services/mounttable/lib/neighborhood.go
@@ -123,7 +123,7 @@
 }
 
 // Lookup implements ipc.Dispatcher.Lookup.
-func (nh *neighborhood) Lookup(name, method string) (interface{}, security.Authorizer, error) {
+func (nh *neighborhood) Lookup(name string) (interface{}, security.Authorizer, error) {
 	vlog.VI(1).Infof("*********************LookupServer '%s'\n", name)
 	elems := strings.Split(name, "/")[nh.nelems:]
 	if name == "" {
diff --git a/services/mounttable/mounttabled/test.sh b/services/mounttable/mounttabled/test.sh
index bb65687..2ebdc7f 100755
--- a/services/mounttable/mounttabled/test.sh
+++ b/services/mounttable/mounttabled/test.sh
@@ -37,7 +37,8 @@
     || (cat "${MTLOG}"; shell_test::fail "line ${LINENO}: failed to identify endpoint")
 
   # Get the neighborhood endpoint from the mounttable.
-  NHEP=$(${MOUNTTABLE_BIN} glob "${EP}" nh | grep ^nh | cut -d' ' -f2) \
+  NHEP=$(${MOUNTTABLE_BIN} glob "${EP}" nh | grep ^nh | \
+	 sed -e 's/ \/@.@ws@[^ ]* (TTL .m..s)//' | cut -d' ' -f2) \
     || (cat "${MTLOG}"; shell_test::fail "line ${LINENO}: failed to identify neighborhood endpoint")
 
   # Mount objects and verify the result.
@@ -47,7 +48,8 @@
     || shell_test::fail "line ${LINENO}: failed to mount www.google.com"
 
   # <mounttable>.Glob('*')
-  GOT=$(${MOUNTTABLE_BIN} glob "${EP}" '*' | sed 's/TTL [^)]*/TTL XmXXs/' | sort) \
+  GOT=$(${MOUNTTABLE_BIN} glob "${EP}" '*' | \
+	  sed -e 's/ \/@.@ws@[^ ]* (TTL .m..s)//' -e 's/TTL [^)]*/TTL XmXXs/' | sort) \
     || shell_test::fail "line ${LINENO}: failed to run mounttable"
   WANT="[${EP}]
 google /www.google.com:80 (TTL XmXXs)
diff --git a/tools/application/impl.go b/tools/application/impl.go
index 2d06ad5..e37608f 100644
--- a/tools/application/impl.go
+++ b/tools/application/impl.go
@@ -11,9 +11,8 @@
 	"strings"
 	"time"
 
-	"veyron.io/veyron/veyron/lib/cmdline"
+	"veyron.io/lib/cmdline"
 	"veyron.io/veyron/veyron/services/mgmt/repository"
-
 	"veyron.io/veyron/veyron2/context"
 	"veyron.io/veyron/veyron2/rt"
 	"veyron.io/veyron/veyron2/services/mgmt/application"
diff --git a/tools/application/impl_test.go b/tools/application/impl_test.go
index 502fb9c..010ace1 100644
--- a/tools/application/impl_test.go
+++ b/tools/application/impl_test.go
@@ -68,7 +68,7 @@
 	return &dispatcher{}
 }
 
-func (d *dispatcher) Lookup(suffix, method string) (interface{}, security.Authorizer, error) {
+func (d *dispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) {
 	return repository.ApplicationServer(&server{suffix: suffix}), nil, nil
 }
 
diff --git a/tools/application/main.go b/tools/application/main.go
index a99a159..9e5d556 100644
--- a/tools/application/main.go
+++ b/tools/application/main.go
@@ -1,11 +1,5 @@
 // The following enables go generate to generate the doc.go file.
-// Things to look out for:
-// 1) go:generate evaluates double-quoted strings into a single argument.
-// 2) go:generate performs $NAME expansion, so the bash cmd can't contain '$'.
-// 3) We generate into a *.tmp file first, otherwise go run will pick up the
-//    initially empty *.go file, and fail.
-//
-//go:generate bash -c "{ echo -e '// This file was auto-generated via go generate.\n// DO NOT UPDATE MANUALLY\n\n/*' && veyron go run *.go help -style=godoc ... && echo -e '*/\npackage main'; } > ./doc.go.tmp && mv ./doc.go.tmp ./doc.go"
+//go:generate go run $VEYRON_ROOT/lib/cmdline/testdata/gendoc.go .
 
 package main
 
diff --git a/tools/binary/impl.go b/tools/binary/impl.go
index 1df2314..69f0f51 100644
--- a/tools/binary/impl.go
+++ b/tools/binary/impl.go
@@ -3,7 +3,7 @@
 import (
 	"fmt"
 
-	"veyron.io/veyron/veyron/lib/cmdline"
+	"veyron.io/lib/cmdline"
 	"veyron.io/veyron/veyron/services/mgmt/lib/binary"
 )
 
diff --git a/tools/binary/impl_test.go b/tools/binary/impl_test.go
index 16031b6..0f32f85 100644
--- a/tools/binary/impl_test.go
+++ b/tools/binary/impl_test.go
@@ -77,7 +77,7 @@
 	return &dispatcher{}
 }
 
-func (d *dispatcher) Lookup(suffix, method string) (interface{}, security.Authorizer, error) {
+func (d *dispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) {
 	return repository.BinaryServer(&server{suffix: suffix}), nil, nil
 }
 
diff --git a/tools/binary/main.go b/tools/binary/main.go
index a99a159..9e5d556 100644
--- a/tools/binary/main.go
+++ b/tools/binary/main.go
@@ -1,11 +1,5 @@
 // The following enables go generate to generate the doc.go file.
-// Things to look out for:
-// 1) go:generate evaluates double-quoted strings into a single argument.
-// 2) go:generate performs $NAME expansion, so the bash cmd can't contain '$'.
-// 3) We generate into a *.tmp file first, otherwise go run will pick up the
-//    initially empty *.go file, and fail.
-//
-//go:generate bash -c "{ echo -e '// This file was auto-generated via go generate.\n// DO NOT UPDATE MANUALLY\n\n/*' && veyron go run *.go help -style=godoc ... && echo -e '*/\npackage main'; } > ./doc.go.tmp && mv ./doc.go.tmp ./doc.go"
+//go:generate go run $VEYRON_ROOT/lib/cmdline/testdata/gendoc.go .
 
 package main
 
diff --git a/tools/build/impl.go b/tools/build/impl.go
index 1d39669..a4b9b87 100644
--- a/tools/build/impl.go
+++ b/tools/build/impl.go
@@ -10,8 +10,7 @@
 	"strings"
 	"time"
 
-	"veyron.io/veyron/veyron/lib/cmdline"
-
+	"veyron.io/lib/cmdline"
 	"veyron.io/veyron/veyron2/context"
 	"veyron.io/veyron/veyron2/rt"
 	vbuild "veyron.io/veyron/veyron2/services/mgmt/build"
diff --git a/tools/build/main.go b/tools/build/main.go
index a99a159..9e5d556 100644
--- a/tools/build/main.go
+++ b/tools/build/main.go
@@ -1,11 +1,5 @@
 // The following enables go generate to generate the doc.go file.
-// Things to look out for:
-// 1) go:generate evaluates double-quoted strings into a single argument.
-// 2) go:generate performs $NAME expansion, so the bash cmd can't contain '$'.
-// 3) We generate into a *.tmp file first, otherwise go run will pick up the
-//    initially empty *.go file, and fail.
-//
-//go:generate bash -c "{ echo -e '// This file was auto-generated via go generate.\n// DO NOT UPDATE MANUALLY\n\n/*' && veyron go run *.go help -style=godoc ... && echo -e '*/\npackage main'; } > ./doc.go.tmp && mv ./doc.go.tmp ./doc.go"
+//go:generate go run $VEYRON_ROOT/lib/cmdline/testdata/gendoc.go .
 
 package main
 
diff --git a/tools/debug/impl.go b/tools/debug/impl.go
index 36faee4..f3dc121 100644
--- a/tools/debug/impl.go
+++ b/tools/debug/impl.go
@@ -12,12 +12,11 @@
 	"sync"
 	"time"
 
-	"veyron.io/veyron/veyron/lib/cmdline"
+	"veyron.io/lib/cmdline"
 	"veyron.io/veyron/veyron/lib/glob"
 	"veyron.io/veyron/veyron/lib/signals"
 	"veyron.io/veyron/veyron/services/mgmt/pprof/client"
 	istats "veyron.io/veyron/veyron/services/mgmt/stats"
-
 	"veyron.io/veyron/veyron2/context"
 	"veyron.io/veyron/veyron2/naming"
 	"veyron.io/veyron/veyron2/rt"
diff --git a/tools/debug/main.go b/tools/debug/main.go
index a99a159..9e5d556 100644
--- a/tools/debug/main.go
+++ b/tools/debug/main.go
@@ -1,11 +1,5 @@
 // The following enables go generate to generate the doc.go file.
-// Things to look out for:
-// 1) go:generate evaluates double-quoted strings into a single argument.
-// 2) go:generate performs $NAME expansion, so the bash cmd can't contain '$'.
-// 3) We generate into a *.tmp file first, otherwise go run will pick up the
-//    initially empty *.go file, and fail.
-//
-//go:generate bash -c "{ echo -e '// This file was auto-generated via go generate.\n// DO NOT UPDATE MANUALLY\n\n/*' && veyron go run *.go help -style=godoc ... && echo -e '*/\npackage main'; } > ./doc.go.tmp && mv ./doc.go.tmp ./doc.go"
+//go:generate go run $VEYRON_ROOT/lib/cmdline/testdata/gendoc.go .
 
 package main
 
diff --git a/tools/mgmt/nodex/acl_impl.go b/tools/mgmt/nodex/acl_impl.go
index 8348f5b..ca2b0a9 100644
--- a/tools/mgmt/nodex/acl_impl.go
+++ b/tools/mgmt/nodex/acl_impl.go
@@ -6,17 +6,19 @@
 	"fmt"
 	"sort"
 
+	"veyron.io/lib/cmdline"
 	"veyron.io/veyron/veyron2/rt"
+	"veyron.io/veyron/veyron2/security"
 	"veyron.io/veyron/veyron2/services/mgmt/node"
-
-	"veyron.io/veyron/veyron/lib/cmdline"
+	"veyron.io/veyron/veyron2/services/security/access"
+	"veyron.io/veyron/veyron2/verror"
 )
 
 var cmdGet = &cmdline.Command{
 	Run:      runGet,
 	Name:     "get",
 	Short:    "Get ACLs for the given target.",
-	Long:     "Get ACLs for the given target with friendly output. Also see getraw.",
+	Long:     "Get ACLs for the given target.",
 	ArgsName: "<node manager name>",
 	ArgsLong: `
 <node manager name> can be a Vanadium name for a node manager,
@@ -37,9 +39,8 @@
 func (a byBlessing) Less(i, j int) bool { return a[i].blessing < a[j].blessing }
 
 func runGet(cmd *cmdline.Command, args []string) error {
-
 	if expected, got := 1, len(args); expected != got {
-		return cmd.UsageErrorf("install: incorrect number of arguments, expected %d, got %d", expected, got)
+		return cmd.UsageErrorf("get: incorrect number of arguments, expected %d, got %d", expected, got)
 	}
 
 	vanaName := args[0]
@@ -51,31 +52,143 @@
 	// TODO(rjkroege): Update for custom labels.
 	output := make([]formattedACLEntry, 0)
 	for k, _ := range objACL.In {
-		output = append(output, formattedACLEntry{string(k), "in", objACL.In[k].String()})
+		output = append(output, formattedACLEntry{string(k), "", objACL.In[k].String()})
 	}
 	for k, _ := range objACL.NotIn {
-
-		output = append(output, formattedACLEntry{string(k), "nin", objACL.NotIn[k].String()})
+		output = append(output, formattedACLEntry{string(k), "!", objACL.NotIn[k].String()})
 	}
 
 	sort.Sort(byBlessing(output))
 
 	for _, e := range output {
-		fmt.Fprintf(cmd.Stdout(), "%s %s %s\n", e.blessing, e.inout, e.label)
+		fmt.Fprintf(cmd.Stdout(), "%s %s%s\n", e.blessing, e.inout, e.label)
 	}
 	return nil
 }
 
-// TODO(rjkroege): Implement the remaining sub-commands.
-// nodex acl set <target>  ([!]<label> <blessing>)...
+var cmdSet = &cmdline.Command{
+	Run:      runSet,
+	Name:     "set",
+	Short:    "Set ACLs for the given target.",
+	Long:     "Set ACLs for the given target",
+	ArgsName: "<node manager name>  (<blessing> [!]<label>)...",
+	ArgsLong: `
+<node manager name> can be a Vanadium name for a node manager,
+application installation or instance.
+
+<blessing> is a blessing pattern.
+
+<label> is a character sequence defining a set of rights: some subset
+of the defined standard Vanadium labels of XRWADM where X is resolve,
+R is read, W for write, A for admin, D for debug and M is for
+monitoring. By default, the combination of <blessing>, <label>
+replaces whatever entry is present in the ACL's In field for the
+<blessing> but it can instead be added to the NotIn field by prefacing
+<label> with a '!' character. Use the <label> of 0 to clear the label.
+
+For example: root/self !0 will clear the NotIn field for blessingroot/self.`,
+}
+
+type inAdditionTuple struct {
+	blessing security.BlessingPattern
+	ls       *security.LabelSet
+}
+
+type notInAdditionTuple struct {
+	blessing string
+	ls       *security.LabelSet
+}
+
+func runSet(cmd *cmdline.Command, 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)
+	}
+
+	vanaName := args[0]
+	pairs := args[1:]
+
+	// Parse each pair and aggregate what should happen to all of them
+	notInDeletions := make([]string, 0)
+	inDeletions := make([]security.BlessingPattern, 0)
+	inAdditions := make([]inAdditionTuple, 0)
+	notInAdditions := make([]notInAdditionTuple, 0)
+
+	for i := 0; i < len(pairs); i += 2 {
+		blessing, label := pairs[i], pairs[i+1]
+		if label == "" || label == "!" {
+			return cmd.UsageErrorf("failed to parse LabelSet pair %s, %s", blessing, label)
+		}
+
+		switch {
+		case label == "!0":
+			notInDeletions = append(notInDeletions, blessing)
+		case label == "0":
+			inDeletions = append(inDeletions, security.BlessingPattern(blessing))
+		case label[0] == '!':
+			// e.g. !RW
+			ls := new(security.LabelSet)
+			if err := ls.FromString(label[1:]); err != nil {
+				return cmd.UsageErrorf("failed to parse LabelSet %s:  %v", label, err)
+			}
+			notInAdditions = append(notInAdditions, notInAdditionTuple{blessing, ls})
+		default:
+			// e.g. X
+			ls := new(security.LabelSet)
+			if err := ls.FromString(label); err != nil {
+				return fmt.Errorf("failed to parse LabelSet %s:  %v", label, err)
+			}
+			inAdditions = append(inAdditions, inAdditionTuple{security.BlessingPattern(blessing), ls})
+		}
+	}
+
+	// Set the ACLs on the specified name.
+	for {
+		objACL, etag, err := node.ApplicationClient(vanaName).GetACL(rt.R().NewContext())
+		if err != nil {
+			return cmd.UsageErrorf("GetACL(%s) failed: %v", vanaName, err)
+		}
+
+		// Insert into objACL
+		for _, b := range notInDeletions {
+			if _, contains := objACL.NotIn[b]; !contains {
+				fmt.Fprintf(cmd.Stderr(), "WARNING: ignoring attempt to remove non-existing NotIn ACL for %s\n", b)
+			}
+			delete(objACL.NotIn, b)
+		}
+
+		for _, b := range inDeletions {
+			if _, contains := objACL.In[b]; !contains {
+				fmt.Fprintf(cmd.Stderr(), "WARNING: ignoring attempt to remove non-existing In ACL for %s\n", b)
+			}
+			delete(objACL.In, b)
+		}
+
+		for _, b := range inAdditions {
+			objACL.In[b.blessing] = *b.ls
+		}
+
+		for _, b := range notInAdditions {
+			objACL.NotIn[b.blessing] = *b.ls
+		}
+
+		switch err := node.ApplicationClient(vanaName).SetACL(rt.R().NewContext(), objACL, etag); {
+		case err != nil && !verror.Is(err, access.ErrBadEtag):
+			return cmd.UsageErrorf("SetACL(%s) failed: %v", vanaName, err)
+		case err == nil:
+			return nil
+		}
+		fmt.Fprintf(cmd.Stderr(), "WARNING: trying again because of asynchronous change\n")
+	}
+	return nil
+}
 
 func aclRoot() *cmdline.Command {
 	return &cmdline.Command{
 		Name:  "acl",
-		Short: "Tool for creating associations between Vanadium blessings and a system account",
+		Short: "Tool for setting node manager ACLs",
 		Long: `
-The associate tool facilitates managing blessing to system account associations.
+The acl tool manages ACLs on the node manger, installations and instances.
 `,
-		Children: []*cmdline.Command{cmdGet},
+		Children: []*cmdline.Command{cmdGet, cmdSet},
 	}
 }
diff --git a/tools/mgmt/nodex/acl_test.go b/tools/mgmt/nodex/acl_test.go
index fb629b9..9f4d581 100644
--- a/tools/mgmt/nodex/acl_test.go
+++ b/tools/mgmt/nodex/acl_test.go
@@ -2,6 +2,7 @@
 
 import (
 	"bytes"
+	"fmt"
 	"reflect"
 	"strings"
 	"testing"
@@ -9,6 +10,8 @@
 	"veyron.io/veyron/veyron2/naming"
 	"veyron.io/veyron/veyron2/rt"
 	"veyron.io/veyron/veyron2/security"
+	"veyron.io/veyron/veyron2/services/security/access"
+	"veyron.io/veyron/veyron2/verror"
 )
 
 func TestACLGetCommand(t *testing.T) {
@@ -45,8 +48,8 @@
 	if err := cmd.Execute([]string{"acl", "get", nodeName}); err != nil {
 		t.Fatalf("%v, ouput: %v, error: %v", err)
 	}
-	if expected, got := "root/bob/... nin W\nroot/other in R\nroot/self/... in XRWADM", strings.TrimSpace(stdout.String()); got != expected {
-		t.Fatalf("Unexpected output from list. Got %q, expected %q", got, expected)
+	if expected, got := "root/bob/... !W\nroot/other R\nroot/self/... XRWADM", strings.TrimSpace(stdout.String()); got != expected {
+		t.Fatalf("Unexpected output from get. Got %q, expected %q", got, expected)
 	}
 	if got, expected := tape.Play(), []interface{}{"GetACL"}; !reflect.DeepEqual(expected, got) {
 		t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
@@ -54,3 +57,256 @@
 	tape.Rewind()
 	stdout.Reset()
 }
+
+func TestACLSetCommand(t *testing.T) {
+	runtime := rt.Init()
+	tape := NewTape()
+	server, endpoint, err := startServer(t, runtime, tape)
+	if err != nil {
+		return
+	}
+	defer stopServer(t, server)
+
+	// Setup the command-line.
+	cmd := root()
+	var stdout, stderr bytes.Buffer
+	cmd.Init(nil, &stdout, &stderr)
+	nodeName := naming.JoinAddressName(endpoint.String(), "")
+
+	// Some tests to validate parse.
+	if err := cmd.Execute([]string{"acl", "set", nodeName}); 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) {
+		t.Fatalf("Unexpected output from list. Got %q, expected prefix %q", got, expected)
+	}
+
+	stderr.Reset()
+	stdout.Reset()
+	if err := cmd.Execute([]string{"acl", "set", nodeName, "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) {
+		t.Fatalf("Unexpected output from list. Got %q, expected prefix %q", got, expected)
+	}
+
+	stderr.Reset()
+	stdout.Reset()
+	if err := cmd.Execute([]string{"acl", "set", nodeName, "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) {
+		t.Fatalf("Unexpected output from list. Got %q, expected prefix %q", got, expected)
+	}
+
+	stderr.Reset()
+	stdout.Reset()
+	if err := cmd.Execute([]string{"acl", "set", nodeName, "foo", "!"}); err == nil {
+		t.Fatalf("failed to detect invalid parameter")
+	}
+	if expected, got := "ERROR: failed to parse LabelSet pair foo, !", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
+		t.Fatalf("Unexpected output from list. Got %q, expected prefix %q", got, expected)
+	}
+
+	// Correct operation in the absence of errors.
+	stderr.Reset()
+	stdout.Reset()
+	tape.SetResponses([]interface{}{GetACLResponse{
+		acl: security.ACL{
+			In: map[security.BlessingPattern]security.LabelSet{
+				"root/self/...": security.AllLabels,
+				"root/other":    security.LabelSet(security.ReadLabel),
+			},
+			NotIn: map[string]security.LabelSet{
+				"root/bob": security.LabelSet(security.WriteLabel),
+			},
+		},
+		etag: "anEtagForToday",
+		err:  nil,
+	},
+		verror.Make(access.ErrBadEtag, fmt.Sprintf("etag mismatch in:%s vers:%s", "anEtagForToday", "anEtagForTomorrow")),
+		GetACLResponse{
+			acl: security.ACL{
+				In: map[security.BlessingPattern]security.LabelSet{
+					"root/self/...": security.AllLabels,
+					"root/other":    security.LabelSet(security.ReadLabel),
+				},
+				NotIn: map[string]security.LabelSet{
+					"root/bob":       security.LabelSet(security.WriteLabel),
+					"root/alice/cat": security.LabelSet(security.AdminLabel),
+				},
+			},
+			etag: "anEtagForTomorrow",
+			err:  nil,
+		},
+		nil,
+	})
+
+	if err := cmd.Execute([]string{"acl", "set", nodeName, "root/vana/bad", "!XR", "root/vana/...", "RD", "root/other", "0", "root/bob", "!0"}); err != nil {
+		t.Fatalf("SetACL failed: %v", err)
+	}
+
+	if expected, got := "", strings.TrimSpace(stdout.String()); got != expected {
+		t.Fatalf("Unexpected output from list. Got %q, expected %q", got, expected)
+	}
+	if expected, got := "WARNING: trying again because of asynchronous change", strings.TrimSpace(stderr.String()); got != expected {
+		t.Fatalf("Unexpected output from list. Got %q, expected %q", got, expected)
+	}
+
+	expected := []interface{}{
+		"GetACL",
+		SetACLStimulus{
+			fun: "SetACL",
+			acl: security.ACL{
+				In: map[security.BlessingPattern]security.LabelSet{
+					"root/self/...": security.AllLabels,
+					"root/vana/...": security.LabelSet(security.ReadLabel | security.DebugLabel),
+				},
+				NotIn: map[string]security.LabelSet{
+					"root/vana/bad": security.LabelSet(security.ResolveLabel | security.ReadLabel),
+				},
+			},
+			etag: "anEtagForToday",
+		},
+		"GetACL",
+		SetACLStimulus{
+			fun: "SetACL",
+			acl: security.ACL{
+				In: map[security.BlessingPattern]security.LabelSet{
+					"root/self/...": security.AllLabels,
+					"root/vana/...": security.LabelSet(security.ReadLabel | security.DebugLabel),
+				},
+				NotIn: map[string]security.LabelSet{
+					"root/alice/cat": security.LabelSet(security.AdminLabel),
+					"root/vana/bad":  security.LabelSet(security.ResolveLabel | security.ReadLabel),
+				},
+			},
+			etag: "anEtagForTomorrow",
+		},
+	}
+
+	if got := tape.Play(); !reflect.DeepEqual(expected, got) {
+		t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
+	}
+	tape.Rewind()
+	stdout.Reset()
+	stderr.Reset()
+
+	// GetACL fails.
+	tape.SetResponses([]interface{}{GetACLResponse{
+		acl:  security.ACL{In: nil, NotIn: nil},
+		etag: "anEtagForToday",
+		err:  verror.BadArgf("oops!"),
+	},
+	})
+
+	if err := cmd.Execute([]string{"acl", "set", nodeName, "root/vana/bad", "!XR", "root/vana/...", "RD", "root/other", "0", "root/bob", "!0"}); err == nil {
+		t.Fatalf("GetACL RPC inside acl set command failed but error wrongly not detected")
+	}
+	if expected, got := "ERROR: GetACL("+nodeName+") failed: oops!", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
+		t.Fatalf("Unexpected output from list. Got %q, prefix %q", got, expected)
+	}
+	if expected, got := "", strings.TrimSpace(stdout.String()); got != expected {
+		t.Fatalf("Unexpected output from list. Got %q, expected %q", got, expected)
+	}
+	expected = []interface{}{
+		"GetACL",
+	}
+
+	if got := tape.Play(); !reflect.DeepEqual(expected, got) {
+		t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
+	}
+	tape.Rewind()
+	stdout.Reset()
+	stderr.Reset()
+
+	// SetACL fails with not a bad etag failure.
+	tape.SetResponses([]interface{}{GetACLResponse{
+		acl: security.ACL{
+			In: map[security.BlessingPattern]security.LabelSet{
+				"root/self/...": security.AllLabels,
+				"root/other":    security.LabelSet(security.ReadLabel),
+			},
+			NotIn: map[string]security.LabelSet{
+				"root/bob": security.LabelSet(security.WriteLabel),
+			},
+		},
+		etag: "anEtagForToday",
+		err:  nil,
+	},
+		verror.BadArgf("oops!"),
+	})
+
+	if err := cmd.Execute([]string{"acl", "set", nodeName, "root/vana/bad", "!XR", "root/vana/...", "RD", "root/other", "0", "root/bob", "!0"}); err == nil {
+		t.Fatalf("SetACL should have failed: %v", err)
+	}
+	if expected, got := "", strings.TrimSpace(stdout.String()); got != expected {
+		t.Fatalf("Unexpected output from list. Got %q, expected %q", got, expected)
+	}
+	if expected, got := "ERROR: SetACL("+nodeName+") failed: oops!", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
+		t.Fatalf("Unexpected output from list. Got %q, prefix %q", got, expected)
+	}
+
+	expected = []interface{}{
+		"GetACL",
+		SetACLStimulus{
+			fun: "SetACL",
+			acl: security.ACL{
+				In: map[security.BlessingPattern]security.LabelSet{
+					"root/self/...": security.AllLabels,
+					"root/vana/...": security.LabelSet(security.ReadLabel | security.DebugLabel),
+				},
+				NotIn: map[string]security.LabelSet{
+					"root/vana/bad": security.LabelSet(security.ResolveLabel | security.ReadLabel),
+				},
+			},
+			etag: "anEtagForToday",
+		},
+	}
+
+	if got := tape.Play(); !reflect.DeepEqual(expected, got) {
+		t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
+	}
+	tape.Rewind()
+	stdout.Reset()
+	stderr.Reset()
+
+	// Trying to delete non-existent items.
+	stderr.Reset()
+	stdout.Reset()
+	tape.SetResponses([]interface{}{GetACLResponse{
+		acl: security.ACL{
+			In:    map[security.BlessingPattern]security.LabelSet{},
+			NotIn: map[string]security.LabelSet{},
+		},
+		etag: "anEtagForToday",
+		err:  nil,
+	},
+		nil,
+	})
+
+	if err := cmd.Execute([]string{"acl", "set", nodeName, "root/vana/notin/missing", "!0", "root/vana/in/missing", "0"}); err != nil {
+		t.Fatalf("SetACL failed: %v", err)
+	}
+	if expected, got := "", strings.TrimSpace(stdout.String()); got != expected {
+		t.Fatalf("Unexpected output from list. Got %q, expected %q", got, expected)
+	}
+	if expected, got := "WARNING: ignoring attempt to remove non-existing NotIn ACL for root/vana/notin/missing\nWARNING: ignoring attempt to remove non-existing In ACL for root/vana/in/missing", strings.TrimSpace(stderr.String()); got != expected {
+		t.Fatalf("Unexpected output from list. Got %q, expected %q", got, expected)
+	}
+
+	expected = []interface{}{
+		"GetACL",
+		SetACLStimulus{
+			fun:  "SetACL",
+			acl:  security.ACL{},
+			etag: "anEtagForToday",
+		},
+	}
+	if got := tape.Play(); !reflect.DeepEqual(expected, got) {
+		t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
+	}
+	tape.Rewind()
+	stdout.Reset()
+	stderr.Reset()
+}
diff --git a/tools/mgmt/nodex/associate_impl.go b/tools/mgmt/nodex/associate_impl.go
index a3d2a49..342c35f 100644
--- a/tools/mgmt/nodex/associate_impl.go
+++ b/tools/mgmt/nodex/associate_impl.go
@@ -4,8 +4,7 @@
 	"fmt"
 	"time"
 
-	"veyron.io/veyron/veyron/lib/cmdline"
-
+	"veyron.io/lib/cmdline"
 	"veyron.io/veyron/veyron2/rt"
 	"veyron.io/veyron/veyron2/services/mgmt/node"
 )
diff --git a/tools/mgmt/nodex/doc.go b/tools/mgmt/nodex/doc.go
index 7f2fa78..6b3ff6d 100644
--- a/tools/mgmt/nodex/doc.go
+++ b/tools/mgmt/nodex/doc.go
@@ -13,7 +13,10 @@
    associate   Tool for creating associations between Vanadium blessings and a
                system account
    claim       Claim the node.
-   instance    Subtool for managing application instances
+   stop        Stop the given application instance.
+   suspend     Suspend the given application instance.
+   resume      Resume the given application instance.
+   acl         Tool for setting node manager ACLs
    help        Display help for commands or topics
 Run "nodex help [command]" for command usage.
 
@@ -124,66 +127,75 @@
 <grant extension> is used to extend the default blessing of the current
 principal when blessing the app instance.
 
-Nodex Instance
-
-The instance tool permits controlling application instances.
-
-Usage:
-   nodex instance <command>
-
-The nodex instance commands are:
-   stop        Stop the given application instance.
-   suspend     Suspend the given application instance.
-   resume      Resume the given application instance.
-   refresh     Refresh the given application instance.
-   suspend     Restart the given application instance.
-
-Nodex Instance Stop
+Nodex Stop
 
 Stop the given application instance.
 
 Usage:
-   nodex instance stop <app instance> [<deadline>]
+   nodex stop <app instance>
 
 <app instance> is the veyron object name of the application instance to stop.
-<deadline> is the optional deadline to shut down by. If not provided, defaults
-to 5 seconds.
 
-Nodex Instance Suspend
+Nodex Suspend
 
 Suspend the given application instance.
 
 Usage:
-   nodex instance suspend <app instance>
+   nodex suspend <app instance>
 
-<app instance> is the veyron object name of the application instance to stop.
+<app instance> is the veyron object name of the application instance to suspend.
 
-Nodex Instance Resume
+Nodex Resume
 
 Resume the given application instance.
 
 Usage:
-   nodex instance resume <app instance>
+   nodex resume <app instance>
 
-<app instance> is the veyron object name of the application instance to stop.
+<app instance> is the veyron object name of the application instance to resume.
 
-Nodex Instance Refresh
+Nodex Acl
 
-Refresh the given application instance.
+The acl tool manages ACLs on the node manger, installations and instances.
 
 Usage:
-   nodex instance refresh <app instance>
+   nodex acl <command>
 
-<app instance> is the veyron object name of the application instance to stop.
+The nodex acl commands are:
+   get         Get ACLs for the given target.
+   set         Set ACLs for the given target.
 
-Nodex Instance Suspend
+Nodex Acl Get
 
-Restart the given application instance.
+Get ACLs for the given target.
 
 Usage:
-   nodex instance suspend <app instance>
+   nodex acl get <node manager name>
 
-<app instance> is the veyron object name of the application instance to stop.
+<node manager name> can be a Vanadium name for a node manager, application
+installation or instance.
+
+Nodex Acl Set
+
+Set ACLs for the given target
+
+Usage:
+   nodex acl set <node manager name>  (<blessing> [!]<label>)...
+
+<node manager name> can be a Vanadium name for a node manager, application
+installation or instance.
+
+<blessing> is a blessing pattern.
+
+<label> is a character sequence defining a set of rights: some subset of the
+defined standard Vanadium labels of XRWADM where X is resolve, R is read, W for
+write, A for admin, D for debug and M is for monitoring. By default, the
+combination of <blessing>, <label> replaces whatever entry is present in the
+ACL's In field for the <blessing> but it can instead be added to the NotIn field
+by prefacing <label> with a '!' character. Use the <label> of 0 to clear the
+label.
+
+For example: root/self !0 will clear the NotIn field for blessingroot/self.
 
 Nodex Help
 
diff --git a/tools/mgmt/nodex/impl.go b/tools/mgmt/nodex/impl.go
index e062da2..3507e12 100644
--- a/tools/mgmt/nodex/impl.go
+++ b/tools/mgmt/nodex/impl.go
@@ -3,13 +3,12 @@
 import (
 	"fmt"
 
+	"veyron.io/lib/cmdline"
 	"veyron.io/veyron/veyron2/ipc"
 	"veyron.io/veyron/veyron2/naming"
 	"veyron.io/veyron/veyron2/rt"
 	"veyron.io/veyron/veyron2/security"
 	"veyron.io/veyron/veyron2/services/mgmt/node"
-
-	"veyron.io/veyron/veyron/lib/cmdline"
 )
 
 var cmdInstall = &cmdline.Command{
diff --git a/tools/mgmt/nodex/impl_test.go b/tools/mgmt/nodex/impl_test.go
index 3eb47c7..462f9f5 100644
--- a/tools/mgmt/nodex/impl_test.go
+++ b/tools/mgmt/nodex/impl_test.go
@@ -7,147 +7,11 @@
 	"strings"
 	"testing"
 
-	"veyron.io/veyron/veyron2/security"
-	"veyron.io/veyron/veyron2/services/mgmt/binary"
-	"veyron.io/veyron/veyron2/services/mgmt/node"
-
-	"veyron.io/veyron/veyron/profiles"
-	"veyron.io/veyron/veyron2"
-	"veyron.io/veyron/veyron2/ipc"
 	"veyron.io/veyron/veyron2/naming"
 	"veyron.io/veyron/veyron2/rt"
-	"veyron.io/veyron/veyron2/vlog"
+	"veyron.io/veyron/veyron2/services/mgmt/node"
 )
 
-type mockNodeInvoker struct {
-	tape *Tape
-	t    *testing.T
-}
-
-// Mock ListAssociations
-type ListAssociationResponse struct {
-	na  []node.Association
-	err error
-}
-
-func (mni *mockNodeInvoker) ListAssociations(ipc.ServerContext) (associations []node.Association, err error) {
-	vlog.VI(2).Infof("ListAssociations() was called")
-
-	ir := mni.tape.Record("ListAssociations")
-	r := ir.(ListAssociationResponse)
-	return r.na, r.err
-}
-
-// Mock AssociateAccount
-type AddAssociationStimulus struct {
-	fun           string
-	identityNames []string
-	accountName   string
-}
-
-func (i *mockNodeInvoker) AssociateAccount(call ipc.ServerContext, identityNames []string, accountName string) error {
-	ri := i.tape.Record(AddAssociationStimulus{"AssociateAccount", identityNames, accountName})
-	switch r := ri.(type) {
-	case nil:
-		return nil
-	case error:
-		return r
-	}
-	i.t.Fatalf("AssociateAccount (mock) response %v is of bad type", ri)
-	return nil
-}
-
-func (i *mockNodeInvoker) Claim(call ipc.ServerContext) error { return nil }
-func (*mockNodeInvoker) Describe(ipc.ServerContext) (node.Description, error) {
-	return node.Description{}, nil
-}
-func (*mockNodeInvoker) IsRunnable(_ ipc.ServerContext, description binary.Description) (bool, error) {
-	return false, nil
-}
-func (*mockNodeInvoker) Reset(call ipc.ServerContext, deadline uint64) error { return nil }
-
-// Mock Install
-type InstallStimulus struct {
-	fun     string
-	appName string
-}
-
-type InstallResponse struct {
-	appId string
-	err   error
-}
-
-func (mni *mockNodeInvoker) Install(call ipc.ServerContext, appName string) (string, error) {
-	ir := mni.tape.Record(InstallStimulus{"Install", appName})
-	r := ir.(InstallResponse)
-	return r.appId, r.err
-}
-
-func (*mockNodeInvoker) Refresh(ipc.ServerContext) error           { return nil }
-func (*mockNodeInvoker) Restart(ipc.ServerContext) error           { return nil }
-func (*mockNodeInvoker) Resume(ipc.ServerContext) error            { return nil }
-func (i *mockNodeInvoker) Revert(call ipc.ServerContext) error     { return nil }
-func (*mockNodeInvoker) Start(ipc.ServerContext) ([]string, error) { return []string{}, nil }
-func (*mockNodeInvoker) Stop(ipc.ServerContext, uint32) error      { return nil }
-func (*mockNodeInvoker) Suspend(ipc.ServerContext) error           { return nil }
-func (*mockNodeInvoker) Uninstall(ipc.ServerContext) error         { return nil }
-func (i *mockNodeInvoker) Update(ipc.ServerContext) error          { return nil }
-func (*mockNodeInvoker) UpdateTo(ipc.ServerContext, string) error  { return nil }
-
-// Mock ACL getting and setting
-type GetACLResponse struct {
-	acl  security.ACL
-	etag string
-	err  error
-}
-
-func (mni *mockNodeInvoker) SetACL(ipc.ServerContext, security.ACL, string) error { return nil }
-func (mni *mockNodeInvoker) GetACL(ipc.ServerContext) (security.ACL, string, error) {
-	ir := mni.tape.Record("GetACL")
-	r := ir.(GetACLResponse)
-	return r.acl, r.etag, r.err
-}
-
-type dispatcher struct {
-	tape *Tape
-	t    *testing.T
-}
-
-func NewDispatcher(t *testing.T, tape *Tape) *dispatcher {
-	return &dispatcher{tape: tape, t: t}
-}
-
-func (d *dispatcher) Lookup(suffix, method string) (interface{}, security.Authorizer, error) {
-	return node.NodeServer(&mockNodeInvoker{tape: d.tape, t: d.t}), nil, nil
-}
-
-func startServer(t *testing.T, r veyron2.Runtime, tape *Tape) (ipc.Server, naming.Endpoint, error) {
-	dispatcher := NewDispatcher(t, tape)
-	server, err := r.NewServer()
-	if err != nil {
-		t.Errorf("NewServer failed: %v", err)
-		return nil, nil, err
-	}
-	endpoint, err := server.Listen(profiles.LocalListenSpec)
-	if err != nil {
-		t.Errorf("Listen failed: %v", err)
-		stopServer(t, server)
-		return nil, nil, err
-	}
-	if err := server.ServeDispatcher("", dispatcher); err != nil {
-		t.Errorf("ServeDispatcher failed: %v", err)
-		stopServer(t, server)
-		return nil, nil, err
-	}
-	return server, endpoint, nil
-}
-
-func stopServer(t *testing.T, server ipc.Server) {
-	if err := server.Stop(); err != nil {
-		t.Errorf("server.Stop failed: %v", err)
-	}
-}
-
 func TestListCommand(t *testing.T) {
 	runtime := rt.Init()
 	tape := NewTape()
diff --git a/tools/mgmt/nodex/instance_impl.go b/tools/mgmt/nodex/instance_impl.go
index 251db6f..f328193 100644
--- a/tools/mgmt/nodex/instance_impl.go
+++ b/tools/mgmt/nodex/instance_impl.go
@@ -5,10 +5,9 @@
 import (
 	"fmt"
 
+	"veyron.io/lib/cmdline"
 	"veyron.io/veyron/veyron2/rt"
 	"veyron.io/veyron/veyron2/services/mgmt/node"
-
-	"veyron.io/veyron/veyron/lib/cmdline"
 )
 
 var cmdStop = &cmdline.Command{
diff --git a/tools/mgmt/nodex/main.go b/tools/mgmt/nodex/main.go
index a99a159..9e5d556 100644
--- a/tools/mgmt/nodex/main.go
+++ b/tools/mgmt/nodex/main.go
@@ -1,11 +1,5 @@
 // The following enables go generate to generate the doc.go file.
-// Things to look out for:
-// 1) go:generate evaluates double-quoted strings into a single argument.
-// 2) go:generate performs $NAME expansion, so the bash cmd can't contain '$'.
-// 3) We generate into a *.tmp file first, otherwise go run will pick up the
-//    initially empty *.go file, and fail.
-//
-//go:generate bash -c "{ echo -e '// This file was auto-generated via go generate.\n// DO NOT UPDATE MANUALLY\n\n/*' && veyron go run *.go help -style=godoc ... && echo -e '*/\npackage main'; } > ./doc.go.tmp && mv ./doc.go.tmp ./doc.go"
+//go:generate go run $VEYRON_ROOT/lib/cmdline/testdata/gendoc.go .
 
 package main
 
diff --git a/tools/mgmt/nodex/nodemanager_mock_test.go b/tools/mgmt/nodex/nodemanager_mock_test.go
new file mode 100644
index 0000000..0fa29fd
--- /dev/null
+++ b/tools/mgmt/nodex/nodemanager_mock_test.go
@@ -0,0 +1,162 @@
+package main
+
+import (
+	"log"
+	"testing"
+
+	"veyron.io/veyron/veyron2"
+	"veyron.io/veyron/veyron2/ipc"
+	"veyron.io/veyron/veyron2/naming"
+	"veyron.io/veyron/veyron2/security"
+	"veyron.io/veyron/veyron2/services/mgmt/binary"
+	"veyron.io/veyron/veyron2/services/mgmt/node"
+	"veyron.io/veyron/veyron2/vlog"
+
+	"veyron.io/veyron/veyron/profiles"
+)
+
+type mockNodeInvoker struct {
+	tape *Tape
+	t    *testing.T
+}
+
+// Mock ListAssociations
+type ListAssociationResponse struct {
+	na  []node.Association
+	err error
+}
+
+func (mni *mockNodeInvoker) ListAssociations(ipc.ServerContext) (associations []node.Association, err error) {
+	vlog.VI(2).Infof("ListAssociations() was called")
+
+	ir := mni.tape.Record("ListAssociations")
+	r := ir.(ListAssociationResponse)
+	return r.na, r.err
+}
+
+// Mock AssociateAccount
+type AddAssociationStimulus struct {
+	fun           string
+	identityNames []string
+	accountName   string
+}
+
+func (i *mockNodeInvoker) AssociateAccount(call ipc.ServerContext, identityNames []string, accountName string) error {
+	ri := i.tape.Record(AddAssociationStimulus{"AssociateAccount", identityNames, accountName})
+	switch r := ri.(type) {
+	case nil:
+		return nil
+	case error:
+		return r
+	}
+	log.Fatalf("AssociateAccount (mock) response %v is of bad type", ri)
+	return nil
+}
+
+func (i *mockNodeInvoker) Claim(call ipc.ServerContext) error { return nil }
+func (*mockNodeInvoker) Describe(ipc.ServerContext) (node.Description, error) {
+	return node.Description{}, nil
+}
+func (*mockNodeInvoker) IsRunnable(_ ipc.ServerContext, description binary.Description) (bool, error) {
+	return false, nil
+}
+func (*mockNodeInvoker) Reset(call ipc.ServerContext, deadline uint64) error { return nil }
+
+// Mock Install
+type InstallStimulus struct {
+	fun     string
+	appName string
+}
+
+type InstallResponse struct {
+	appId string
+	err   error
+}
+
+func (mni *mockNodeInvoker) Install(call ipc.ServerContext, appName string) (string, error) {
+	ir := mni.tape.Record(InstallStimulus{"Install", appName})
+	r := ir.(InstallResponse)
+	return r.appId, r.err
+}
+
+func (*mockNodeInvoker) Refresh(ipc.ServerContext) error           { return nil }
+func (*mockNodeInvoker) Restart(ipc.ServerContext) error           { return nil }
+func (*mockNodeInvoker) Resume(ipc.ServerContext) error            { return nil }
+func (i *mockNodeInvoker) Revert(call ipc.ServerContext) error     { return nil }
+func (*mockNodeInvoker) Start(ipc.ServerContext) ([]string, error) { return []string{}, nil }
+func (*mockNodeInvoker) Stop(ipc.ServerContext, uint32) error      { return nil }
+func (*mockNodeInvoker) Suspend(ipc.ServerContext) error           { return nil }
+func (*mockNodeInvoker) Uninstall(ipc.ServerContext) error         { return nil }
+func (i *mockNodeInvoker) Update(ipc.ServerContext) error          { return nil }
+func (*mockNodeInvoker) UpdateTo(ipc.ServerContext, string) error  { return nil }
+
+// Mock ACL getting and setting
+type GetACLResponse struct {
+	acl  security.ACL
+	etag string
+	err  error
+}
+
+type SetACLStimulus struct {
+	fun  string
+	acl  security.ACL
+	etag string
+}
+
+func (mni *mockNodeInvoker) SetACL(_ ipc.ServerContext, acl security.ACL, etag string) error {
+	ri := mni.tape.Record(SetACLStimulus{"SetACL", acl, etag})
+	switch r := ri.(type) {
+	case nil:
+		return nil
+	case error:
+		return r
+	}
+	log.Fatalf("AssociateAccount (mock) response %v is of bad type\n", ri)
+	return nil
+}
+
+func (mni *mockNodeInvoker) GetACL(ipc.ServerContext) (security.ACL, string, error) {
+	ir := mni.tape.Record("GetACL")
+	r := ir.(GetACLResponse)
+	return r.acl, r.etag, r.err
+}
+
+type dispatcher struct {
+	tape *Tape
+	t    *testing.T
+}
+
+func NewDispatcher(t *testing.T, tape *Tape) *dispatcher {
+	return &dispatcher{tape: tape, t: t}
+}
+
+func (d *dispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) {
+	return node.NodeServer(&mockNodeInvoker{tape: d.tape, t: d.t}), nil, nil
+}
+
+func startServer(t *testing.T, r veyron2.Runtime, tape *Tape) (ipc.Server, naming.Endpoint, error) {
+	dispatcher := NewDispatcher(t, tape)
+	server, err := r.NewServer()
+	if err != nil {
+		t.Errorf("NewServer failed: %v", err)
+		return nil, nil, err
+	}
+	endpoint, err := server.Listen(profiles.LocalListenSpec)
+	if err != nil {
+		t.Errorf("Listen failed: %v", err)
+		stopServer(t, server)
+		return nil, nil, err
+	}
+	if err := server.ServeDispatcher("", dispatcher); err != nil {
+		t.Errorf("ServeDispatcher failed: %v", err)
+		stopServer(t, server)
+		return nil, nil, err
+	}
+	return server, endpoint, nil
+}
+
+func stopServer(t *testing.T, server ipc.Server) {
+	if err := server.Stop(); err != nil {
+		t.Errorf("server.Stop failed: %v", err)
+	}
+}
diff --git a/tools/mounttable/impl.go b/tools/mounttable/impl.go
index 99bd958..efa69fc 100644
--- a/tools/mounttable/impl.go
+++ b/tools/mounttable/impl.go
@@ -4,8 +4,7 @@
 	"fmt"
 	"time"
 
-	"veyron.io/veyron/veyron/lib/cmdline"
-
+	"veyron.io/lib/cmdline"
 	"veyron.io/veyron/veyron2/context"
 	"veyron.io/veyron/veyron2/naming"
 	"veyron.io/veyron/veyron2/options"
diff --git a/tools/mounttable/impl_test.go b/tools/mounttable/impl_test.go
index d4161ba..90e83e2 100644
--- a/tools/mounttable/impl_test.go
+++ b/tools/mounttable/impl_test.go
@@ -55,7 +55,7 @@
 type dispatcher struct {
 }
 
-func (d *dispatcher) Lookup(suffix, method string) (interface{}, security.Authorizer, error) {
+func (d *dispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) {
 	return mounttable.MountTableServer(&server{suffix: suffix}), nil, nil
 }
 
diff --git a/tools/mounttable/main.go b/tools/mounttable/main.go
index a99a159..9e5d556 100644
--- a/tools/mounttable/main.go
+++ b/tools/mounttable/main.go
@@ -1,11 +1,5 @@
 // The following enables go generate to generate the doc.go file.
-// Things to look out for:
-// 1) go:generate evaluates double-quoted strings into a single argument.
-// 2) go:generate performs $NAME expansion, so the bash cmd can't contain '$'.
-// 3) We generate into a *.tmp file first, otherwise go run will pick up the
-//    initially empty *.go file, and fail.
-//
-//go:generate bash -c "{ echo -e '// This file was auto-generated via go generate.\n// DO NOT UPDATE MANUALLY\n\n/*' && veyron go run *.go help -style=godoc ... && echo -e '*/\npackage main'; } > ./doc.go.tmp && mv ./doc.go.tmp ./doc.go"
+//go:generate go run $VEYRON_ROOT/lib/cmdline/testdata/gendoc.go .
 
 package main
 
diff --git a/tools/namespace/impl.go b/tools/namespace/impl.go
index 759548d..a1f1f47 100644
--- a/tools/namespace/impl.go
+++ b/tools/namespace/impl.go
@@ -4,7 +4,7 @@
 	"fmt"
 	"time"
 
-	"veyron.io/veyron/veyron/lib/cmdline"
+	"veyron.io/lib/cmdline"
 	"veyron.io/veyron/veyron2/naming"
 	"veyron.io/veyron/veyron2/rt"
 	"veyron.io/veyron/veyron2/vlog"
diff --git a/tools/namespace/main.go b/tools/namespace/main.go
index a99a159..9e5d556 100644
--- a/tools/namespace/main.go
+++ b/tools/namespace/main.go
@@ -1,11 +1,5 @@
 // The following enables go generate to generate the doc.go file.
-// Things to look out for:
-// 1) go:generate evaluates double-quoted strings into a single argument.
-// 2) go:generate performs $NAME expansion, so the bash cmd can't contain '$'.
-// 3) We generate into a *.tmp file first, otherwise go run will pick up the
-//    initially empty *.go file, and fail.
-//
-//go:generate bash -c "{ echo -e '// This file was auto-generated via go generate.\n// DO NOT UPDATE MANUALLY\n\n/*' && veyron go run *.go help -style=godoc ... && echo -e '*/\npackage main'; } > ./doc.go.tmp && mv ./doc.go.tmp ./doc.go"
+//go:generate go run $VEYRON_ROOT/lib/cmdline/testdata/gendoc.go .
 
 package main
 
diff --git a/tools/naming/simulator/driver.go b/tools/naming/simulator/driver.go
index e27e4bb..9242830 100644
--- a/tools/naming/simulator/driver.go
+++ b/tools/naming/simulator/driver.go
@@ -21,7 +21,6 @@
 	"veyron.io/veyron/veyron/lib/expect"
 	"veyron.io/veyron/veyron/lib/flags/consts"
 	"veyron.io/veyron/veyron/lib/modules"
-	"veyron.io/veyron/veyron/lib/modules/core"
 	_ "veyron.io/veyron/veyron/profiles"
 )
 
@@ -101,7 +100,7 @@
 	// Subprocesses commands are run by fork/execing this binary
 	// so we must test to see if this instance is a subprocess or the
 	// the original command line instance.
-	if os.Getenv(modules.ShellEntryPoint) != "" {
+	if modules.IsModulesProcess() {
 		// Subprocess, run the requested command.
 		if err := modules.Dispatch(); err != nil {
 			fmt.Fprintf(os.Stderr, "failed: %v\n", err)
@@ -116,8 +115,6 @@
 		shell.CreateAndUseNewCredentials()
 	}
 
-	core.Install(shell)
-
 	scanner := bufio.NewScanner(os.Stdin)
 	lineno := 1
 	prompt(lineno)
diff --git a/tools/principal/main.go b/tools/principal/main.go
index 5112eef..9096bb3 100644
--- a/tools/principal/main.go
+++ b/tools/principal/main.go
@@ -12,17 +12,16 @@
 	"os/user"
 	"time"
 
+	"veyron.io/lib/cmdline"
+	profile "veyron.io/veyron/veyron/profiles/static"
+	vsecurity "veyron.io/veyron/veyron/security"
+	"veyron.io/veyron/veyron/services/identity"
 	"veyron.io/veyron/veyron2"
 	"veyron.io/veyron/veyron2/ipc"
 	"veyron.io/veyron/veyron2/naming"
 	"veyron.io/veyron/veyron2/rt"
 	"veyron.io/veyron/veyron2/security"
 	"veyron.io/veyron/veyron2/vom"
-
-	"veyron.io/veyron/veyron/lib/cmdline"
-	profile "veyron.io/veyron/veyron/profiles/static"
-	vsecurity "veyron.io/veyron/veyron/security"
-	"veyron.io/veyron/veyron/services/identity"
 )
 
 var (
diff --git a/tools/profile/impl.go b/tools/profile/impl.go
index 2216a60..459b2ab 100644
--- a/tools/profile/impl.go
+++ b/tools/profile/impl.go
@@ -4,10 +4,9 @@
 	"fmt"
 	"time"
 
-	"veyron.io/veyron/veyron/lib/cmdline"
+	"veyron.io/lib/cmdline"
 	"veyron.io/veyron/veyron/services/mgmt/profile"
 	"veyron.io/veyron/veyron/services/mgmt/repository"
-
 	"veyron.io/veyron/veyron2/rt"
 	"veyron.io/veyron/veyron2/services/mgmt/build"
 )
diff --git a/tools/profile/impl_test.go b/tools/profile/impl_test.go
index e24fb92..1dd149d 100644
--- a/tools/profile/impl_test.go
+++ b/tools/profile/impl_test.go
@@ -79,7 +79,7 @@
 	return &dispatcher{}
 }
 
-func (d *dispatcher) Lookup(suffix, method string) (interface{}, security.Authorizer, error) {
+func (d *dispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) {
 	return repository.ProfileServer(&server{suffix: suffix}), nil, nil
 }
 
diff --git a/tools/profile/main.go b/tools/profile/main.go
index a99a159..9e5d556 100644
--- a/tools/profile/main.go
+++ b/tools/profile/main.go
@@ -1,11 +1,5 @@
 // The following enables go generate to generate the doc.go file.
-// Things to look out for:
-// 1) go:generate evaluates double-quoted strings into a single argument.
-// 2) go:generate performs $NAME expansion, so the bash cmd can't contain '$'.
-// 3) We generate into a *.tmp file first, otherwise go run will pick up the
-//    initially empty *.go file, and fail.
-//
-//go:generate bash -c "{ echo -e '// This file was auto-generated via go generate.\n// DO NOT UPDATE MANUALLY\n\n/*' && veyron go run *.go help -style=godoc ... && echo -e '*/\npackage main'; } > ./doc.go.tmp && mv ./doc.go.tmp ./doc.go"
+//go:generate go run $VEYRON_ROOT/lib/cmdline/testdata/gendoc.go .
 
 package main
 
diff --git a/tools/servicerunner/main.go b/tools/servicerunner/main.go
index 8620d14..839af0d 100644
--- a/tools/servicerunner/main.go
+++ b/tools/servicerunner/main.go
@@ -14,7 +14,7 @@
 	"veyron.io/veyron/veyron/lib/expect"
 	"veyron.io/veyron/veyron/lib/flags/consts"
 	"veyron.io/veyron/veyron/lib/modules"
-	"veyron.io/veyron/veyron/lib/modules/core"
+	_ "veyron.io/veyron/veyron/lib/modules/core"
 	_ "veyron.io/veyron/veyron/profiles"
 )
 
@@ -57,9 +57,7 @@
 func main() {
 	rt.Init()
 
-	// NOTE(sadovsky): It would be better if Dispatch() itself performed the env
-	// check.
-	if os.Getenv(modules.ShellEntryPoint) != "" {
+	if modules.IsModulesProcess() {
 		panicOnError(modules.Dispatch())
 		return
 	}
@@ -78,9 +76,6 @@
 		}
 		vars[consts.VeyronCredentials] = v
 	}
-	// NOTE(sadovsky): The following line will not be needed if the modules
-	// library is restructured per my proposal.
-	core.Install(sh)
 
 	h, err := sh.Start("root", nil, "--", "--veyron.tcp.address=127.0.0.1:0")
 	panicOnError(err)
diff --git a/tools/vrpc/impl.go b/tools/vrpc/impl.go
index 8891515..7d0e560 100644
--- a/tools/vrpc/impl.go
+++ b/tools/vrpc/impl.go
@@ -6,9 +6,9 @@
 	"io"
 	"time"
 
-	"veyron.io/veyron/veyron/lib/cmdline"
+	"veyron.io/lib/cmdline"
+	_ "veyron.io/veyron/veyron/profiles/static"
 	idl_test_base "veyron.io/veyron/veyron/tools/vrpc/test_base"
-
 	"veyron.io/veyron/veyron2"
 	"veyron.io/veyron/veyron2/context"
 	"veyron.io/veyron/veyron2/ipc"
diff --git a/tools/vrpc/impl_test.go b/tools/vrpc/impl_test.go
index 49e94e1..f585f2b 100644
--- a/tools/vrpc/impl_test.go
+++ b/tools/vrpc/impl_test.go
@@ -6,15 +6,14 @@
 	"strings"
 	"testing"
 
+	"veyron.io/lib/cmdline"
+	"veyron.io/veyron/veyron/profiles"
+	"veyron.io/veyron/veyron/tools/vrpc/test_base"
 	"veyron.io/veyron/veyron2"
 	"veyron.io/veyron/veyron2/ipc"
 	"veyron.io/veyron/veyron2/naming"
 	"veyron.io/veyron/veyron2/rt"
 	"veyron.io/veyron/veyron2/vlog"
-
-	"veyron.io/veyron/veyron/lib/cmdline"
-	"veyron.io/veyron/veyron/profiles"
-	"veyron.io/veyron/veyron/tools/vrpc/test_base"
 )
 
 type server struct{}
diff --git a/tools/vrpc/main.go b/tools/vrpc/main.go
index 3c2043c..f52ab6f 100644
--- a/tools/vrpc/main.go
+++ b/tools/vrpc/main.go
@@ -1,11 +1,5 @@
 // The following enables go generate to generate the doc.go file.
-// Things to look out for:
-// 1) go:generate evaluates double-quoted strings into a single argument.
-// 2) go:generate performs $NAME expansion, so the bash cmd can't contain '$'.
-// 3) We generate into a *.tmp file first, otherwise go run will pick up the
-//    initially empty *.go file, and fail.
-//
-//go:generate bash -c "{ echo -e '// This file was auto-generated via go generate.\n// DO NOT UPDATE MANUALLY\n\n/*' && veyron go run *.go help -style=godoc ... && echo -e '*/\npackage main'; } > ./doc.go.tmp && mv ./doc.go.tmp ./doc.go"
+//go:generate go run $VEYRON_ROOT/lib/cmdline/testdata/gendoc.go .
 
 package main