Merge "TBR v.io/x/ref/cmd/vdl: update vdl tool for the Java roadmap -> release move."
diff --git a/cmd/mounttable/impl_test.go b/cmd/mounttable/impl_test.go
index 5bc59cf..f4fa627 100644
--- a/cmd/mounttable/impl_test.go
+++ b/cmd/mounttable/impl_test.go
@@ -22,6 +22,7 @@
 	"v.io/x/lib/cmdline"
 	"v.io/x/lib/vlog"
 	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/lib/xrpc"
 	_ "v.io/x/ref/runtime/factories/generic"
 	"v.io/x/ref/test"
 )
@@ -87,40 +88,15 @@
 	return mounttable.MountTableServer(&server{suffix: suffix}), nil, nil
 }
 
-func startServer(t *testing.T, ctx *context.T) (rpc.Server, naming.Endpoint, error) {
-	dispatcher := new(dispatcher)
-	server, err := v23.NewServer(ctx)
-	if err != nil {
-		t.Errorf("NewServer failed: %v", err)
-		return nil, nil, err
-	}
-	endpoints, err := server.Listen(v23.GetListenSpec(ctx))
-	if err != nil {
-		t.Errorf("Listen failed: %v", err)
-		return nil, nil, err
-	}
-	if err := server.ServeDispatcher("", dispatcher); err != nil {
-		t.Errorf("ServeDispatcher failed: %v", err)
-		return nil, nil, err
-	}
-	return server, endpoints[0], nil
-}
-
-func stopServer(t *testing.T, server rpc.Server) {
-	if err := server.Stop(); err != nil {
-		t.Errorf("server.Stop failed: %v", err)
-	}
-}
-
 func TestMountTableClient(t *testing.T) {
 	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
-	server, endpoint, err := startServer(t, ctx)
+	server, err := xrpc.NewDispatchingServer(ctx, "", new(dispatcher))
 	if err != nil {
-		return
+		t.Fatalf("NewServer failed: %v", err)
 	}
-	defer stopServer(t, server)
+	endpoint := server.Status().Endpoints[0]
 
 	// Make sure to use our newly created mounttable rather than the
 	// default.
diff --git a/cmd/principal/main.go b/cmd/principal/main.go
index c18bb1b..e1eaadf 100644
--- a/cmd/principal/main.go
+++ b/cmd/principal/main.go
@@ -31,6 +31,7 @@
 	"v.io/x/ref"
 	vsecurity "v.io/x/ref/lib/security"
 	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/lib/xrpc"
 	_ "v.io/x/ref/runtime/factories/static"
 )
 
@@ -758,41 +759,33 @@
 			if len(args) != 0 {
 				return fmt.Errorf("command accepts no arguments")
 			}
-			server, err := v23.NewServer(ctx)
-			if err != nil {
-				return fmt.Errorf("failed to create server to listen for blessings: %v", err)
-			}
-			defer server.Stop()
-			eps, err := server.Listen(v23.GetListenSpec(ctx))
-			if err != nil {
-				return fmt.Errorf("failed to setup listening: %v", err)
-			}
 			var token [24]byte
 			if _, err := rand.Read(token[:]); err != nil {
 				return fmt.Errorf("unable to generate token: %v", err)
 			}
-
 			p := v23.GetPrincipal(ctx)
 			service := &recvBlessingsService{
 				principal: p,
 				token:     base64.URLEncoding.EncodeToString(token[:]),
 				notify:    make(chan error),
 			}
-			if err := server.Serve("", service, security.AllowEveryone()); err != nil {
-				return fmt.Errorf("failed to setup service: %v", err)
+			server, err := xrpc.NewServer(ctx, "", service, security.AllowEveryone())
+			if err != nil {
+				return fmt.Errorf("failed to create server to listen for blessings: %v", err)
 			}
+			name := server.Status().Endpoints[0].Name()
 			fmt.Println("Run the following command on behalf of the principal that will send blessings:")
 			fmt.Println("You may want to adjust flags affecting the caveats on this blessing, for example using")
 			fmt.Println("the --for flag")
 			fmt.Println()
 			if len(flagRemoteArgFile) > 0 {
-				if err := writeRecvBlessingsInfo(flagRemoteArgFile, p.PublicKey().String(), service.token, eps[0].Name()); err != nil {
+				if err := writeRecvBlessingsInfo(flagRemoteArgFile, p.PublicKey().String(), service.token, name); err != nil {
 					return fmt.Errorf("failed to write recvblessings info to %v: %v", flagRemoteArgFile, err)
 				}
 				fmt.Printf("make %q accessible to the blesser, possibly by copying the file over and then run:\n", flagRemoteArgFile)
 				fmt.Printf("principal bless --remote-arg-file=%v", flagRemoteArgFile)
 			} else {
-				fmt.Printf("principal bless --remote-key=%v --remote-token=%v %v\n", p.PublicKey(), service.token, eps[0].Name())
+				fmt.Printf("principal bless --remote-key=%v --remote-token=%v %v\n", p.PublicKey(), service.token, name)
 			}
 			fmt.Println()
 			fmt.Println("...waiting for sender..")
diff --git a/cmd/servicerunner/main.go b/cmd/servicerunner/main.go
index d984741..34b7fe4 100644
--- a/cmd/servicerunner/main.go
+++ b/cmd/servicerunner/main.go
@@ -29,6 +29,7 @@
 	"v.io/x/lib/set"
 	"v.io/x/ref"
 	"v.io/x/ref/lib/signals"
+	"v.io/x/ref/lib/xrpc"
 	"v.io/x/ref/runtime/factories/generic"
 	"v.io/x/ref/services/identity/identitylib"
 	"v.io/x/ref/services/mounttable/mounttablelib"
@@ -68,24 +69,16 @@
 	ctx, shutdown := v23.Init()
 	defer shutdown()
 
-	lspec := v23.GetListenSpec(ctx)
-	server, err := v23.NewServer(ctx, options.ServesMountTable(true))
-	if err != nil {
-		return fmt.Errorf("root failed: %v", err)
-	}
 	mt, err := mounttablelib.NewMountTableDispatcher("", "", "mounttable")
 	if err != nil {
 		return fmt.Errorf("mounttablelib.NewMountTableDispatcher failed: %s", err)
 	}
-	eps, err := server.Listen(lspec)
+	server, err := xrpc.NewDispatchingServer(ctx, "", mt, options.ServesMountTable(true))
 	if err != nil {
-		return fmt.Errorf("server.Listen failed: %s", err)
-	}
-	if err := server.ServeDispatcher("", mt); err != nil {
-		return fmt.Errorf("root failed: %s", err)
+		return fmt.Errorf("root failed: %v", err)
 	}
 	fmt.Fprintf(env.Stdout, "PID=%d\n", os.Getpid())
-	for _, ep := range eps {
+	for _, ep := range server.Status().Endpoints {
 		fmt.Fprintf(env.Stdout, "MT_NAME=%s\n", ep.Name())
 	}
 	modules.WaitForEOF(env.Stdin)
diff --git a/cmd/vdl/arith_test.go b/cmd/vdl/arith_test.go
index f5769b6..d0294ae 100644
--- a/cmd/vdl/arith_test.go
+++ b/cmd/vdl/arith_test.go
@@ -15,12 +15,12 @@
 	"reflect"
 	"testing"
 
-	"v.io/v23"
 	"v.io/v23/context"
 	"v.io/v23/rpc"
 	"v.io/v23/vdl"
 	"v.io/x/ref/lib/vdl/testdata/arith"
 	"v.io/x/ref/lib/vdl/testdata/base"
+	"v.io/x/ref/lib/xrpc"
 	"v.io/x/ref/test"
 
 	_ "v.io/x/ref/runtime/factories/generic"
@@ -28,14 +28,6 @@
 
 var generatedError = errors.New("generated error")
 
-func newServer(ctx *context.T) rpc.Server {
-	s, err := v23.NewServer(ctx)
-	if err != nil {
-		panic(err)
-	}
-	return s
-}
-
 // serverArith implements the arith.Arith interface.
 type serverArith struct{}
 
@@ -111,12 +103,11 @@
 	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
-	server := newServer(ctx)
-	eps, err := server.Listen(v23.GetListenSpec(ctx))
-	if err := server.Serve("", arith.CalculatorServer(&serverCalculator{}), nil); err != nil {
+	server, err := xrpc.NewServer(ctx, "", arith.CalculatorServer(&serverCalculator{}), nil)
+	if err != nil {
 		t.Fatal(err)
 	}
-	root := eps[0].Name()
+	root := server.Status().Endpoints[0].Name()
 	// Synchronous calls
 	calculator := arith.CalculatorClient(root)
 	sine, err := calculator.Sine(ctx, 0)
@@ -296,16 +287,12 @@
 	}
 
 	for i, obj := range objects {
-		server := newServer(ctx)
-		defer server.Stop()
-		eps, err := server.Listen(v23.GetListenSpec(ctx))
+		server, err := xrpc.NewServer(ctx, "", obj, nil)
 		if err != nil {
-			t.Fatal(err)
-		}
-		root := eps[0].Name()
-		if err := server.Serve("", obj, nil); err != nil {
 			t.Fatalf("%d: %v", i, err)
 		}
+		root := server.Status().Endpoints[0].Name()
+
 		// Synchronous calls
 		ar := arith.ArithClient(root)
 		sum, err := ar.Add(ctx, 7, 8)
diff --git a/cmd/vrpc/vrpc_test.go b/cmd/vrpc/vrpc_test.go
index 5fc6067..d4ffcac 100644
--- a/cmd/vrpc/vrpc_test.go
+++ b/cmd/vrpc/vrpc_test.go
@@ -17,6 +17,7 @@
 	"v.io/x/lib/vlog"
 	"v.io/x/ref/cmd/vrpc/internal"
 	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/lib/xrpc"
 	_ "v.io/x/ref/runtime/factories/generic"
 	"v.io/x/ref/test"
 )
@@ -118,23 +119,13 @@
 
 func initTest(t *testing.T) (ctx *context.T, name string, shutdown v23.Shutdown) {
 	ctx, shutdown = test.V23Init()
-
-	rpcServer, err := v23.NewServer(ctx)
+	obj := internal.TypeTesterServer(&server{})
+	server, err := xrpc.NewServer(ctx, "", obj, nil)
 	if err != nil {
 		t.Fatalf("NewServer failed: %v", err)
 		return
 	}
-	endpoints, err := rpcServer.Listen(v23.GetListenSpec(ctx))
-	if err != nil {
-		t.Fatalf("Listen failed: %v", err)
-		return
-	}
-	name = endpoints[0].Name()
-	obj := internal.TypeTesterServer(&server{})
-	if err := rpcServer.Serve("", obj, nil); err != nil {
-		t.Fatalf("Serve failed: %v", err)
-		return
-	}
+	name = server.Status().Endpoints[0].Name()
 	return
 }
 
diff --git a/examples/fortune/fortuned/impl_test.go b/examples/fortune/fortuned/impl_test.go
index 37c88dc..248bb03 100644
--- a/examples/fortune/fortuned/impl_test.go
+++ b/examples/fortune/fortuned/impl_test.go
@@ -11,6 +11,7 @@
 	"v.io/v23/context"
 	"v.io/v23/security"
 	"v.io/x/ref/examples/fortune"
+	"v.io/x/ref/lib/xrpc"
 )
 
 func TestGet(t *testing.T) {
@@ -50,28 +51,17 @@
 func setup(t *testing.T) (*context.T, fortune.FortuneClientStub, v23.Shutdown) {
 	ctx, shutdown := v23.Init()
 
-	server, err := v23.NewServer(ctx)
-	if err != nil {
-		t.Errorf("Failure creating server")
-	}
-
-	spec := v23.GetListenSpec(ctx)
-	endpoints, err := server.Listen(spec)
-	if err != nil {
-		t.Errorf("Error listening")
-	}
-
 	authorizer := security.DefaultAuthorizer()
 	impl := newImpl()
 	service := fortune.FortuneServer(impl)
 	name := ""
 
-	err = server.Serve(name, service, authorizer)
+	server, err := xrpc.NewServer(ctx, name, service, authorizer)
 	if err != nil {
-		t.Errorf("Error Serving")
+		t.Errorf("Failure creating server: %v", err)
 	}
 
-	endpoint := endpoints[0].Name()
+	endpoint := server.Status().Endpoints[0].Name()
 	client := fortune.FortuneClient(endpoint)
 
 	return ctx, client, shutdown
diff --git a/examples/fortune/fortuned/main.go b/examples/fortune/fortuned/main.go
index 59d4b81..deb214e 100644
--- a/examples/fortune/fortuned/main.go
+++ b/examples/fortune/fortuned/main.go
@@ -13,7 +13,8 @@
 	"v.io/v23/security"
 	"v.io/x/ref/examples/fortune"
 	"v.io/x/ref/lib/signals"
-	// The v23.Init call below will use the generic runtime configuration.
+	"v.io/x/ref/lib/xrpc"
+	// The v23.Init call below will use the generic runtime factory.
 	_ "v.io/x/ref/runtime/factories/generic"
 )
 
@@ -25,27 +26,15 @@
 	ctx, shutdown := v23.Init()
 	defer shutdown()
 
-	server, err := v23.NewServer(ctx)
-	if err != nil {
-		log.Panic("Failure creating server: ", err)
-	}
-
-	spec := v23.GetListenSpec(ctx)
-	endpoints, err := server.Listen(spec)
-	if err != nil {
-		log.Panic("Error listening: ", err)
-	}
-
 	authorizer := security.DefaultAuthorizer()
 	impl := newImpl()
 	service := fortune.FortuneServer(impl)
 
-	err = server.Serve(*name, service, authorizer)
+	server, err := xrpc.NewServer(ctx, *name, service, authorizer)
 	if err != nil {
-		log.Panic("Error serving: ", err)
-	} else {
-		log.Printf("Listening at: %v\n", endpoints[0])
+		log.Panic("Failure creating server: ", err)
 	}
+	log.Printf("Listening at: %v\n", server.Status().Endpoints[0])
 
 	<-signals.ShutdownOnSignals(ctx)
 }
diff --git a/examples/rps/rpsbot/impl_test.go b/examples/rps/rpsbot/impl_test.go
index ec531e2..ec2d3da 100644
--- a/examples/rps/rpsbot/impl_test.go
+++ b/examples/rps/rpsbot/impl_test.go
@@ -20,8 +20,8 @@
 	"v.io/v23"
 	"v.io/v23/context"
 	"v.io/v23/options"
-	"v.io/v23/rpc"
 	"v.io/x/ref/examples/rps"
+	"v.io/x/ref/lib/xrpc"
 	"v.io/x/ref/services/mounttable/mounttablelib"
 	"v.io/x/ref/test"
 	"v.io/x/ref/test/modules"
@@ -30,51 +30,34 @@
 //go:generate v23 test generate
 
 var rootMT = modules.Register(func(env *modules.Env, args ...string) error {
-	ctx, shutdown := v23.Init()
+	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
-	lspec := v23.GetListenSpec(ctx)
-	server, err := v23.NewServer(ctx, options.ServesMountTable(true))
-	if err != nil {
-		return fmt.Errorf("root failed: %v", err)
-	}
 	mt, err := mounttablelib.NewMountTableDispatcher("", "", "mounttable")
 	if err != nil {
 		return fmt.Errorf("mounttablelib.NewMountTableDispatcher failed: %s", err)
 	}
-	eps, err := server.Listen(lspec)
+	server, err := xrpc.NewDispatchingServer(ctx, "", mt, options.ServesMountTable(true))
 	if err != nil {
-		return fmt.Errorf("server.Listen failed: %s", err)
-	}
-	if err := server.ServeDispatcher("", mt); err != nil {
-		return fmt.Errorf("root failed: %s", err)
+		return fmt.Errorf("root failed: %v", err)
 	}
 	fmt.Fprintf(env.Stdout, "PID=%d\n", os.Getpid())
-	for _, ep := range eps {
+	for _, ep := range server.Status().Endpoints {
 		fmt.Fprintf(env.Stdout, "MT_NAME=%s\n", ep.Name())
 	}
 	modules.WaitForEOF(env.Stdin)
 	return nil
 }, "rootMT")
 
-var spec = rpc.ListenSpec{Addrs: rpc.ListenAddrs{{"tcp", "127.0.0.1:0"}}}
-
 func startRockPaperScissors(t *testing.T, ctx *context.T, mtAddress string) (*RPS, func()) {
 	ns := v23.GetNamespace(ctx)
 	ns.SetRoots(mtAddress)
-	server, err := v23.NewServer(ctx)
+	rpsService := NewRPS(ctx)
+	names := []string{"rps/judge/test", "rps/player/test", "rps/scorekeeper/test"}
+	server, err := xrpc.NewServer(ctx, names[0], rps.RockPaperScissorsServer(rpsService), nil)
 	if err != nil {
 		t.Fatalf("NewServer failed: %v", err)
 	}
-	rpsService := NewRPS(ctx)
-
-	if _, err = server.Listen(spec); err != nil {
-		t.Fatalf("Listen failed: %v", err)
-	}
-	names := []string{"rps/judge/test", "rps/player/test", "rps/scorekeeper/test"}
-	if err := server.Serve(names[0], rps.RockPaperScissorsServer(rpsService), nil); err != nil {
-		t.Fatalf("Serve(%v) failed: %v", names[0], err)
-	}
 	for _, n := range names[1:] {
 		server.AddName(n)
 	}
diff --git a/examples/rps/rpsbot/main.go b/examples/rps/rpsbot/main.go
index fff0e7a..86bcef5 100644
--- a/examples/rps/rpsbot/main.go
+++ b/examples/rps/rpsbot/main.go
@@ -14,13 +14,13 @@
 
 	"v.io/x/lib/cmdline"
 
-	"v.io/v23"
 	"v.io/v23/context"
 
 	"v.io/x/ref/examples/rps"
 	"v.io/x/ref/examples/rps/internal"
 	"v.io/x/ref/lib/signals"
 	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/lib/xrpc"
 
 	_ "v.io/x/ref/runtime/factories/roaming"
 )
@@ -51,19 +51,8 @@
 
 func runBot(ctx *context.T, env *cmdline.Env, args []string) error {
 	auth := internal.NewAuthorizer(aclFile)
-	server, err := v23.NewServer(ctx)
-	if err != nil {
-		return fmt.Errorf("NewServer failed: %v", err)
-	}
-
 	rand.Seed(time.Now().UnixNano())
 	rpsService := NewRPS(ctx)
-
-	listenSpec := v23.GetListenSpec(ctx)
-	eps, err := server.Listen(listenSpec)
-	if err != nil {
-		return fmt.Errorf("Listen(%v) failed: %v", listenSpec, err)
-	}
 	if name == "" {
 		name = internal.CreateName()
 	}
@@ -72,15 +61,16 @@
 		fmt.Sprintf("rps/player/%s", name),
 		fmt.Sprintf("rps/scorekeeper/%s", name),
 	}
-	if err := server.Serve(names[0], rps.RockPaperScissorsServer(rpsService), auth); err != nil {
-		return fmt.Errorf("Serve(%v) failed: %v", names[0], err)
+	server, err := xrpc.NewServer(ctx, names[0], rps.RockPaperScissorsServer(rpsService), auth)
+	if err != nil {
+		return fmt.Errorf("NewServer failed: %v", err)
 	}
 	for _, n := range names[1:] {
 		if err := server.AddName(n); err != nil {
 			return fmt.Errorf("(%v) failed: %v", n, err)
 		}
 	}
-	ctx.Infof("Listening on endpoint %s (published as %v)", eps, names)
+	ctx.Infof("Listening on endpoint %s (published as %v)", server.Status().Endpoints[0], names)
 
 	go initiateGames(ctx, rpsService)
 	<-signals.ShutdownOnSignals(ctx)
diff --git a/examples/rps/rpsplayer/main.go b/examples/rps/rpsplayer/main.go
index dae9a77..3f496f5 100644
--- a/examples/rps/rpsplayer/main.go
+++ b/examples/rps/rpsplayer/main.go
@@ -16,7 +16,6 @@
 	"time"
 
 	"v.io/x/lib/cmdline"
-	"v.io/x/ref/lib/v23cmd"
 
 	"v.io/v23"
 	"v.io/v23/context"
@@ -28,6 +27,8 @@
 	"v.io/x/ref/examples/rps"
 	"v.io/x/ref/examples/rps/internal"
 	"v.io/x/ref/internal/logger"
+	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/lib/xrpc"
 
 	_ "v.io/x/ref/runtime/factories/roaming"
 )
@@ -124,24 +125,18 @@
 // recvChallenge runs a server until a game challenge is accepted by the user.
 // The server is stopped afterwards.
 func recvChallenge(ctx *context.T) gameChallenge {
-	server, err := v23.NewServer(ctx)
-	if err != nil {
-		ctx.Fatalf("NewServer failed: %v", err)
-	}
 	ch := make(chan gameChallenge)
-
-	listenSpec := v23.GetListenSpec(ctx)
-	ep, err := server.Listen(listenSpec)
-	if err != nil {
-		ctx.Fatalf("Listen(%v) failed: %v", listenSpec, err)
-	}
 	if name == "" {
 		name = internal.CreateName()
 	}
-	if err := server.Serve(fmt.Sprintf("rps/player/%s", name), rps.PlayerServer(&impl{ch: ch}), internal.NewAuthorizer(aclFile)); err != nil {
-		ctx.Fatalf("Serve failed: %v", err)
+	fullname := fmt.Sprintf("rps/player/%s", name)
+	service := rps.PlayerServer(&impl{ch: ch})
+	auth := internal.NewAuthorizer(aclFile)
+	server, err := xrpc.NewServer(ctx, fullname, service, auth)
+	if err != nil {
+		ctx.Fatalf("NewServer failed: %v", err)
 	}
-	ctx.Infof("Listening on endpoint /%s", ep)
+	ctx.Infof("Listening on endpoint /%s", server.Status().Endpoints[0])
 	result := <-ch
 	server.Stop()
 	return result
diff --git a/examples/rps/rpsscorekeeper/main.go b/examples/rps/rpsscorekeeper/main.go
index 8deb4bf..6c8fd39 100644
--- a/examples/rps/rpsscorekeeper/main.go
+++ b/examples/rps/rpsscorekeeper/main.go
@@ -13,7 +13,6 @@
 
 	"v.io/x/lib/cmdline"
 
-	"v.io/v23"
 	"v.io/v23/context"
 	"v.io/v23/rpc"
 	"v.io/v23/security"
@@ -21,6 +20,8 @@
 	"v.io/x/ref/examples/rps"
 	"v.io/x/ref/examples/rps/internal"
 	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/lib/xrpc"
+
 	_ "v.io/x/ref/runtime/factories/roaming"
 )
 
@@ -55,28 +56,21 @@
 }
 
 func runScoreKeeper(ctx *context.T, env *cmdline.Env, args []string) error {
-	server, err := v23.NewServer(ctx)
-	if err != nil {
-		return fmt.Errorf("NewServer failed: %v", err)
-	}
-	defer server.Stop()
-
 	ch := make(chan rps.ScoreCard)
 	rpsService := &impl{ch}
-
-	listenSpec := v23.GetListenSpec(ctx)
-	ep, err := server.Listen(listenSpec)
-	if err != nil {
-		return fmt.Errorf("Listen(%v) failed: %v", listenSpec, err)
-	}
 	hostname, err := os.Hostname()
 	if err != nil {
 		return fmt.Errorf("os.Hostname failed: %v", err)
 	}
-	if err := server.Serve(fmt.Sprintf("rps/scorekeeper/%s", hostname), rps.ScoreKeeperServer(rpsService), internal.NewAuthorizer(aclFile)); err != nil {
-		return fmt.Errorf("Serve failed: %v", err)
+	name := fmt.Sprintf("rps/scorekeeper/%s", hostname)
+	service := rps.ScoreKeeperServer(rpsService)
+	authorizer := internal.NewAuthorizer(aclFile)
+	server, err := xrpc.NewServer(ctx, name, service, authorizer)
+	if err != nil {
+		return fmt.Errorf("NewServer failed: %v", err)
 	}
-	ctx.Infof("Listening on endpoint /%s", ep)
+
+	ctx.Infof("Listening on endpoint /%s", server.Status().Endpoints[0])
 
 	for score := range ch {
 		fmt.Print("======================\n", internal.FormatScoreCard(score))
diff --git a/examples/tunnel/tunneld/main.go b/examples/tunnel/tunneld/main.go
index b454ed2..5291e67 100644
--- a/examples/tunnel/tunneld/main.go
+++ b/examples/tunnel/tunneld/main.go
@@ -12,13 +12,13 @@
 
 	"v.io/x/lib/cmdline"
 
-	"v.io/v23"
 	"v.io/v23/context"
 
 	"v.io/x/ref/examples/tunnel"
 	"v.io/x/ref/lib/security/securityflag"
 	"v.io/x/ref/lib/signals"
 	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/lib/xrpc"
 
 	_ "v.io/x/ref/runtime/factories/roaming"
 )
@@ -42,19 +42,10 @@
 
 func runTunnelD(ctx *context.T, env *cmdline.Env, args []string) error {
 	auth := securityflag.NewAuthorizerOrDie()
-	server, err := v23.NewServer(ctx)
+	server, err := xrpc.NewServer(ctx, name, tunnel.TunnelServer(&T{}), auth)
 	if err != nil {
 		return fmt.Errorf("NewServer failed: %v", err)
 	}
-	defer server.Stop()
-
-	listenSpec := v23.GetListenSpec(ctx)
-	if _, err := server.Listen(listenSpec); err != nil {
-		return fmt.Errorf("Listen(%v) failed: %v", listenSpec, err)
-	}
-	if err := server.Serve(name, tunnel.TunnelServer(&T{}), auth); err != nil {
-		return fmt.Errorf("Serve(%v) failed: %v", name, err)
-	}
 	status := server.Status()
 	ctx.Infof("Listening on: %v", status.Endpoints)
 	if len(status.Endpoints) > 0 {
diff --git a/lib/signals/signals_test.go b/lib/signals/signals_test.go
index 63b0097..2acdad1 100644
--- a/lib/signals/signals_test.go
+++ b/lib/signals/signals_test.go
@@ -16,12 +16,12 @@
 
 	"v.io/v23"
 	"v.io/v23/context"
-	"v.io/v23/naming"
 	"v.io/v23/rpc"
 	"v.io/v23/services/appcycle"
 	"v.io/v23/vtrace"
 	"v.io/x/ref/lib/mgmt"
 	"v.io/x/ref/lib/security/securityflag"
+	"v.io/x/ref/lib/xrpc"
 	"v.io/x/ref/services/device"
 	"v.io/x/ref/test"
 	"v.io/x/ref/test/modules"
@@ -334,23 +334,6 @@
 
 }
 
-func createConfigServer(t *testing.T, ctx *context.T) (rpc.Server, string, <-chan string) {
-	server, err := v23.NewServer(ctx)
-	if err != nil {
-		t.Fatalf("Got error: %v", err)
-	}
-	ch := make(chan string)
-	var ep []naming.Endpoint
-	if ep, err = server.Listen(v23.GetListenSpec(ctx)); err != nil {
-		t.Fatalf("Got error: %v", err)
-	}
-	if err := server.Serve("", device.ConfigServer(&configServer{ch}), securityflag.NewAuthorizerOrDie()); err != nil {
-		t.Fatalf("Got error: %v", err)
-	}
-	return server, ep[0].Name(), ch
-
-}
-
 // TestCleanRemoteShutdown verifies that remote shutdown works correctly.
 func TestCleanRemoteShutdown(t *testing.T) {
 	ctx, shutdown := test.V23Init()
@@ -362,8 +345,13 @@
 	}
 	defer sh.Cleanup(os.Stderr, os.Stderr)
 
-	configServer, configServiceName, ch := createConfigServer(t, ctx)
-	defer configServer.Stop()
+	ch := make(chan string)
+	server, err := xrpc.NewServer(ctx, "", device.ConfigServer(&configServer{ch}), securityflag.NewAuthorizerOrDie())
+	if err != nil {
+		t.Fatalf("Got error: %v", err)
+	}
+	configServiceName := server.Status().Endpoints[0].Name()
+
 	sh.SetConfigKey(mgmt.ParentNameConfigKey, configServiceName)
 	sh.SetConfigKey(mgmt.ProtocolConfigKey, "tcp")
 	sh.SetConfigKey(mgmt.AddressConfigKey, "127.0.0.1:0")
diff --git a/lib/xrpc/xserver.go b/lib/xrpc/xserver.go
index cc1f775..30bb7c1 100644
--- a/lib/xrpc/xserver.go
+++ b/lib/xrpc/xserver.go
@@ -41,9 +41,11 @@
 		return nil, err
 	}
 	if _, err = s.Listen(v23.GetListenSpec(ctx)); err != nil {
+		s.Stop()
 		return nil, err
 	}
 	if err = s.Serve(name, object, auth); err != nil {
+		s.Stop()
 		return nil, err
 	}
 	return &Server{s: s}, nil
diff --git a/runtime/factories/fake/rpc.go b/runtime/factories/fake/rpc.go
index d80e123..1d8ccdf 100644
--- a/runtime/factories/fake/rpc.go
+++ b/runtime/factories/fake/rpc.go
@@ -37,3 +37,8 @@
 	defer apilog.LogCall(ctx)(ctx) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
 	return rpc.ListenSpec{}
 }
+
+func (r *Runtime) WithListenSpec(ctx *context.T, ls rpc.ListenSpec) *context.T {
+	// nologcall
+	return ctx
+}
diff --git a/runtime/factories/roaming/roaming_server.go b/runtime/factories/roaming/roaming_server.go
index c3e9a2f..10e801a 100644
--- a/runtime/factories/roaming/roaming_server.go
+++ b/runtime/factories/roaming/roaming_server.go
@@ -14,6 +14,7 @@
 	"v.io/v23/rpc"
 	"v.io/x/lib/vlog"
 
+	"v.io/x/ref/lib/xrpc"
 	_ "v.io/x/ref/runtime/factories/roaming"
 )
 
@@ -21,22 +22,10 @@
 	ctx, shutdown := v23.Init()
 	defer shutdown()
 
-	server, err := v23.NewServer(ctx)
+	server, err := xrpc.NewServer(ctx, "roamer", &dummy{}, nil)
 	if err != nil {
 		vlog.Fatalf("unexpected error: %q", err)
 	}
-
-	listenSpec := v23.GetListenSpec(ctx)
-	fmt.Printf("listen spec: %v\n", listenSpec)
-
-	_, err = server.Listen(listenSpec)
-	if err != nil {
-		vlog.Fatalf("unexpected error: %q", err)
-	}
-	err = server.Serve("roamer", &dummy{}, nil)
-	if err != nil {
-		log.Fatalf("unexpected error: %q", err)
-	}
 	watcher := make(chan rpc.NetworkChange, 1)
 	server.WatchNetwork(watcher)
 
diff --git a/runtime/internal/naming/namespace/all_test.go b/runtime/internal/naming/namespace/all_test.go
index 457fc23..58be9fa 100644
--- a/runtime/internal/naming/namespace/all_test.go
+++ b/runtime/internal/naming/namespace/all_test.go
@@ -22,6 +22,7 @@
 	"v.io/v23/verror"
 	"v.io/x/lib/vlog"
 
+	"v.io/x/ref/lib/xrpc"
 	_ "v.io/x/ref/runtime/factories/generic"
 	inamespace "v.io/x/ref/runtime/internal/naming/namespace"
 	"v.io/x/ref/services/mounttable/mounttablelib"
@@ -201,7 +202,7 @@
 
 type serverEntry struct {
 	mountPoint string
-	server     rpc.Server
+	stop       func() error
 	endpoint   naming.Endpoint
 	name       string
 }
@@ -219,23 +220,15 @@
 }
 
 func run(t *testing.T, ctx *context.T, disp rpc.Dispatcher, mountPoint string, mt bool) *serverEntry {
-	s, err := v23.NewServer(ctx, options.ServesMountTable(mt))
+	s, err := xrpc.NewDispatchingServer(ctx, mountPoint, disp, options.ServesMountTable(mt))
 	if err != nil {
 		boom(t, "r.NewServer: %s", err)
 	}
-	// Add a mount table server.
-	// Start serving on a loopback address.
-	eps, err := s.Listen(v23.GetListenSpec(ctx))
-	if err != nil {
-		boom(t, "Failed to Listen: %s", err)
-	}
-	if err := s.ServeDispatcher(mountPoint, disp); err != nil {
-		boom(t, "Failed to serve mount table at %s: %s", mountPoint, err)
-	}
+	eps := s.Status().Endpoints
 	t.Logf("server %q -> %s", eps[0].Name(), mountPoint)
 	// Wait until the mount point appears in the mount table.
 	resolveWithRetry(ctx, mountPoint)
-	return &serverEntry{mountPoint: mountPoint, server: s, endpoint: eps[0], name: eps[0].Name()}
+	return &serverEntry{mountPoint: mountPoint, stop: s.Stop, endpoint: eps[0], name: eps[0].Name()}
 }
 
 const (
@@ -282,12 +275,12 @@
 	}
 	return root, mts, jokes, func() {
 		for _, s := range jokes {
-			s.server.Stop()
+			s.stop()
 		}
 		for _, s := range mts {
-			s.server.Stop()
+			s.stop()
 		}
-		root.server.Stop()
+		root.stop()
 	}
 }
 
@@ -532,7 +525,7 @@
 	globServer := &GlobbableServer{}
 	name := naming.JoinAddressName(mts["mt4/foo/bar"].name, "glob")
 	runningGlobServer := runServer(t, c, testutil.LeafDispatcher(globServer, nil), name)
-	defer runningGlobServer.server.Stop()
+	defer runningGlobServer.stop()
 
 	ns := v23.GetNamespace(c)
 	ns.SetRoots(root.name)
@@ -573,9 +566,9 @@
 	c1 := runMT(t, c, "c1")
 	c2 := runMT(t, c, "c2")
 	c3 := runMT(t, c, "c3")
-	defer c1.server.Stop()
-	defer c2.server.Stop()
-	defer c3.server.Stop()
+	defer c1.stop()
+	defer c2.stop()
+	defer c3.stop()
 
 	m := "c1/c2"
 	if err := ns.Mount(c, m, c1.name, ttl, naming.ServesMountTable(true)); err != nil {
@@ -681,7 +674,7 @@
 	// Intermediate mounttables should be authenticated.
 	mt := runMT(t, mtCtx, "mt")
 	defer func() {
-		mt.server.Stop()
+		mt.stop()
 	}()
 
 	// Mount a server on "mt".
@@ -775,21 +768,14 @@
 	_, ctx, cleanup := createContexts(t)
 	defer cleanup()
 	root := runMT(t, ctx, "")
-	defer func() { root.server.Stop() }()
+	defer func() { root.stop() }()
 
 	ns := v23.GetNamespace(ctx)
 	ns.SetRoots(root.name)
 
-	server, err := v23.NewServer(ctx)
+	server, err := xrpc.NewServer(ctx, "leaf", &leafObject{}, nil)
 	if err != nil {
-		boom(t, "v23.NewServer: %s", err)
-	}
-	ls := rpc.ListenSpec{Addrs: rpc.ListenAddrs{{"tcp", "127.0.0.1:0"}}}
-	if _, err := server.Listen(ls); err != nil {
-		boom(t, "Failed to Listen: %s", err)
-	}
-	if err := server.Serve("leaf", &leafObject{}, nil); err != nil {
-		boom(t, "server.Serve failed: %s", err)
+		boom(t, "xrpc.NewServer: %s", err)
 	}
 	defer server.Stop()
 
diff --git a/runtime/internal/naming/namespace/perms_test.go b/runtime/internal/naming/namespace/perms_test.go
index 92090fb..bf2dba4 100644
--- a/runtime/internal/naming/namespace/perms_test.go
+++ b/runtime/internal/naming/namespace/perms_test.go
@@ -16,6 +16,7 @@
 	"v.io/v23/security"
 	"v.io/v23/security/access"
 
+	"v.io/x/ref/lib/xrpc"
 	_ "v.io/x/ref/runtime/factories/generic"
 	"v.io/x/ref/services/mounttable/mounttablelib"
 	"v.io/x/ref/test"
@@ -235,18 +236,9 @@
 	if err := ns.SetPermissions(rootCtx, name, closedPerms, version); err != nil {
 		t.Fatalf("SetPermissions %s: %s", name, err)
 	}
-	server, err := v23.NewServer(rootCtx)
-	if err != nil {
+	if _, err := xrpc.NewServer(rootCtx, name, &nopServer{1}, nil); err != nil {
 		t.Fatalf("v23.NewServer failed: %v", err)
 	}
-	defer server.Stop()
-	if _, err := server.Listen(v23.GetListenSpec(rootCtx)); err != nil {
-		t.Fatalf("Failed to Listen: %s", err)
-	}
-	if err := server.Serve(name, &nopServer{1}, nil); err != nil {
-		t.Fatalf("Failed to Serve: %s", err)
-	}
-
 	// Alice shouldn't be able to resolve it.
 	_, err = v23.GetNamespace(aliceCtx).Resolve(aliceCtx, name)
 	if err == nil {
diff --git a/runtime/internal/rpc/benchmark/benchmark/doc.go b/runtime/internal/rpc/benchmark/benchmark/doc.go
index 7688db4..ba2ef7e 100644
--- a/runtime/internal/rpc/benchmark/benchmark/doc.go
+++ b/runtime/internal/rpc/benchmark/benchmark/doc.go
@@ -78,11 +78,6 @@
    Displays metadata for the program and exits.
  -v23.namespace.root=[/(dev.v.io/role/vprod/service/mounttabled)@ns.dev.v.io:8101]
    local namespace root; can be repeated to provided multiple roots
- -v23.permissions.file=map[]
-   specify a perms file as <name>:<permsfile>
- -v23.permissions.literal=
-   explicitly specify the runtime perms as a JSON-encoded access.Permissions.
-   Overrides all --v23.permissions.file flags.
  -v23.proxy=
    object name of proxy service to use to export services across network
    boundaries
diff --git a/runtime/internal/rpc/benchmark/benchmark/main.go b/runtime/internal/rpc/benchmark/benchmark/main.go
index b74bd81..15bf193 100644
--- a/runtime/internal/rpc/benchmark/benchmark/main.go
+++ b/runtime/internal/rpc/benchmark/benchmark/main.go
@@ -12,9 +12,10 @@
 	"testing"
 	"time"
 
-	"v.io/v23/context"
 	"v.io/x/lib/cmdline"
-	"v.io/x/lib/vlog"
+
+	"v.io/v23/context"
+
 	"v.io/x/ref/lib/v23cmd"
 	_ "v.io/x/ref/runtime/factories/generic"
 	"v.io/x/ref/runtime/internal/rpc/benchmark/internal"
@@ -50,7 +51,7 @@
 		dummyB := testing.B{}
 		_, stop := internal.StartEchoStream(&dummyB, ctx, server, 0, chunkCntMux, payloadSizeMux, nil)
 		defer stop()
-		vlog.Infof("Started background streaming (chunk_size=%d, payload_size=%d)", chunkCntMux, payloadSizeMux)
+		ctx.Infof("Started background streaming (chunk_size=%d, payload_size=%d)", chunkCntMux, payloadSizeMux)
 	}
 
 	dummyB := testing.B{}
diff --git a/runtime/internal/rpc/benchmark/benchmark_test.go b/runtime/internal/rpc/benchmark/benchmark_test.go
index 5d98e34..6859361 100644
--- a/runtime/internal/rpc/benchmark/benchmark_test.go
+++ b/runtime/internal/rpc/benchmark/benchmark_test.go
@@ -10,7 +10,9 @@
 
 	"v.io/v23"
 	"v.io/v23/context"
-
+	"v.io/x/lib/vlog"
+	"v.io/x/ref/lib/security/securityflag"
+	"v.io/x/ref/lib/xrpc"
 	_ "v.io/x/ref/runtime/factories/static"
 	"v.io/x/ref/runtime/internal/rpc/benchmark/internal"
 	"v.io/x/ref/test"
@@ -112,17 +114,16 @@
 	var shutdown v23.Shutdown
 	ctx, shutdown = test.V23Init()
 
-	var serverStop func()
-	serverEP, serverStop := internal.StartServer(ctx, v23.GetListenSpec(ctx))
-	serverAddr = serverEP.Name()
+	server, err := xrpc.NewServer(ctx, "", internal.NewService(), securityflag.NewAuthorizerOrDie())
+	if err != nil {
+		vlog.Fatalf("NewServer failed: %v", err)
+	}
+	serverAddr = server.Status().Endpoints[0].Name()
 
 	// Create a VC to exclude the VC setup time from the benchmark.
 	internal.CallEcho(&testing.B{}, ctx, serverAddr, 1, 0, benchmark.NewStats(1))
 
 	r := benchmark.RunTestMain(m)
-
-	serverStop()
 	shutdown()
-
 	os.Exit(r)
 }
diff --git a/runtime/internal/rpc/benchmark/benchmarkd/main.go b/runtime/internal/rpc/benchmark/benchmarkd/main.go
index e5053fd..f20bb3c 100644
--- a/runtime/internal/rpc/benchmark/benchmarkd/main.go
+++ b/runtime/internal/rpc/benchmark/benchmarkd/main.go
@@ -8,12 +8,14 @@
 package main
 
 import (
-	"v.io/v23"
-	"v.io/v23/context"
 	"v.io/x/lib/cmdline"
-	"v.io/x/lib/vlog"
+
+	"v.io/v23/context"
+
+	"v.io/x/ref/lib/security/securityflag"
 	"v.io/x/ref/lib/signals"
 	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/lib/xrpc"
 	_ "v.io/x/ref/runtime/factories/roaming"
 	"v.io/x/ref/runtime/internal/rpc/benchmark/internal"
 )
@@ -31,9 +33,11 @@
 }
 
 func runBenchmarkD(ctx *context.T, env *cmdline.Env, args []string) error {
-	ep, stop := internal.StartServer(ctx, v23.GetListenSpec(ctx))
-	vlog.Infof("Listening on %s", ep.Name())
-	defer stop()
+	server, err := xrpc.NewServer(ctx, "", internal.NewService(), securityflag.NewAuthorizerOrDie())
+	if err != nil {
+		ctx.Fatalf("NewServer failed: %v", err)
+	}
+	ctx.Infof("Listening on %s", server.Status().Endpoints[0].Name())
 	<-signals.ShutdownOnSignals(ctx)
 	return nil
 }
diff --git a/runtime/internal/rpc/benchmark/glob/glob_test.go b/runtime/internal/rpc/benchmark/glob/glob_test.go
index 3bb9c31..24e5691 100644
--- a/runtime/internal/rpc/benchmark/glob/glob_test.go
+++ b/runtime/internal/rpc/benchmark/glob/glob_test.go
@@ -14,6 +14,7 @@
 	"v.io/v23/rpc"
 	"v.io/v23/security"
 
+	"v.io/x/ref/lib/xrpc"
 	_ "v.io/x/ref/runtime/factories/generic"
 	"v.io/x/ref/test"
 )
@@ -82,21 +83,6 @@
 	return d.obj, nil, nil
 }
 
-func startServer(b *testing.B, ctx *context.T, obj interface{}) (string, func(), error) {
-	server, err := v23.NewServer(ctx)
-	if err != nil {
-		return "", nil, fmt.Errorf("failed to start server: %v", err)
-	}
-	endpoints, err := server.Listen(v23.GetListenSpec(ctx))
-	if err != nil {
-		return "", nil, fmt.Errorf("failed to listen: %v", err)
-	}
-	if err := server.ServeDispatcher("", &disp{obj}); err != nil {
-		return "", nil, err
-	}
-	return endpoints[0].Name(), func() { server.Stop() }, nil
-}
-
 type globObject struct {
 	b          *testing.B
 	bufferSize int
@@ -162,11 +148,11 @@
 	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
-	addr, stop, err := startServer(b, ctx, obj)
+	server, err := xrpc.NewDispatchingServer(ctx, "", &disp{obj})
 	if err != nil {
-		b.Fatalf("startServer failed: %v", err)
+		b.Fatalf("failed to start server: %v", err)
 	}
-	defer stop()
+	addr := server.Status().Endpoints[0].Name()
 
 	count, err := globClient(b, ctx, addr)
 	if err != nil {
diff --git a/runtime/internal/rpc/benchmark/internal/client.go b/runtime/internal/rpc/benchmark/internal/client.go
index 95a76e0..8a712b2 100644
--- a/runtime/internal/rpc/benchmark/internal/client.go
+++ b/runtime/internal/rpc/benchmark/internal/client.go
@@ -12,7 +12,6 @@
 
 	"v.io/v23/context"
 
-	"v.io/x/lib/vlog"
 	"v.io/x/ref/runtime/internal/rpc/benchmark"
 	tbm "v.io/x/ref/test/benchmark"
 )
@@ -38,10 +37,10 @@
 		b.StopTimer()
 
 		if err != nil {
-			vlog.Fatalf("Echo failed: %v", err)
+			ctx.Fatalf("Echo failed: %v", err)
 		}
 		if !bytes.Equal(r, payload) {
-			vlog.Fatalf("Echo returned %v, but expected %v", r, payload)
+			ctx.Fatalf("Echo returned %v, but expected %v", r, payload)
 		}
 
 		stats.Add(elapsed)
@@ -95,7 +94,7 @@
 
 			stream, err := stub.EchoStream(ctx)
 			if err != nil {
-				vlog.Fatalf("EchoStream failed: %v", err)
+				ctx.Fatalf("EchoStream failed: %v", err)
 			}
 
 			rDone := make(chan error, 1)
@@ -121,19 +120,19 @@
 			sStream := stream.SendStream()
 			for i := 0; i < chunkCnt; i++ {
 				if err = sStream.Send(payload); err != nil {
-					vlog.Fatalf("EchoStream Send failed: %v", err)
+					ctx.Fatalf("EchoStream Send failed: %v", err)
 				}
 			}
 			if err = sStream.Close(); err != nil {
-				vlog.Fatalf("EchoStream Close failed: %v", err)
+				ctx.Fatalf("EchoStream Close failed: %v", err)
 			}
 
 			if err = <-rDone; err != nil {
-				vlog.Fatalf("%v", err)
+				ctx.Fatalf("%v", err)
 			}
 
 			if err = stream.Finish(); err != nil {
-				vlog.Fatalf("Finish failed: %v", err)
+				ctx.Fatalf("Finish failed: %v", err)
 			}
 
 			elapsed := time.Since(start)
diff --git a/runtime/internal/rpc/benchmark/internal/server.go b/runtime/internal/rpc/benchmark/internal/server.go
index ec3c0f5..8bbd8ea 100644
--- a/runtime/internal/rpc/benchmark/internal/server.go
+++ b/runtime/internal/rpc/benchmark/internal/server.go
@@ -5,19 +5,19 @@
 package internal
 
 import (
-	"v.io/v23"
 	"v.io/v23/context"
-	"v.io/v23/naming"
 	"v.io/v23/rpc"
 
-	"v.io/x/lib/vlog"
-	"v.io/x/ref/lib/security/securityflag"
 	"v.io/x/ref/runtime/internal/rpc/benchmark"
 )
 
 type impl struct {
 }
 
+func NewService() benchmark.BenchmarkServerStub {
+	return benchmark.BenchmarkServer(&impl{})
+}
+
 func (i *impl) Echo(_ *context.T, _ rpc.ServerCall, payload []byte) ([]byte, error) {
 	return payload, nil
 }
@@ -33,29 +33,3 @@
 	}
 	return nil
 }
-
-// StartServer starts a server that implements the Benchmark service. The
-// server listens to the given protocol and address, and returns the vanadium
-// address of the server and a callback function to stop the server.
-func StartServer(ctx *context.T, listenSpec rpc.ListenSpec) (naming.Endpoint, func()) {
-	server, err := v23.NewServer(ctx)
-	if err != nil {
-		vlog.Fatalf("NewServer failed: %v", err)
-	}
-	eps, err := server.Listen(listenSpec)
-	if err != nil {
-		vlog.Fatalf("Listen failed: %v", err)
-	}
-	if len(eps) == 0 {
-		vlog.Fatal("No local address to listen on")
-	}
-
-	if err := server.Serve("", benchmark.BenchmarkServer(&impl{}), securityflag.NewAuthorizerOrDie()); err != nil {
-		vlog.Fatalf("Serve failed: %v", err)
-	}
-	return eps[0], func() {
-		if err := server.Stop(); err != nil {
-			vlog.Fatalf("Stop() failed: %v", err)
-		}
-	}
-}
diff --git a/runtime/internal/rpc/benchmark/simple/main.go b/runtime/internal/rpc/benchmark/simple/main.go
index 6806775..fabc662 100644
--- a/runtime/internal/rpc/benchmark/simple/main.go
+++ b/runtime/internal/rpc/benchmark/simple/main.go
@@ -16,6 +16,9 @@
 	"v.io/v23/naming"
 
 	"v.io/x/lib/vlog"
+	"v.io/x/ref/lib/security/securityflag"
+	"v.io/x/ref/lib/xrpc"
+
 	_ "v.io/x/ref/runtime/factories/static"
 	"v.io/x/ref/runtime/internal/rpc/benchmark/internal"
 	"v.io/x/ref/runtime/internal/rpc/stream/manager"
@@ -48,6 +51,7 @@
 	defer runtime.GOMAXPROCS(mp)
 
 	principal := testutil.NewPrincipal("test")
+	nctx, _ := v23.WithPrincipal(ctx, principal)
 
 	b.ResetTimer()
 	for i := 0; i < b.N; i++ {
@@ -55,9 +59,9 @@
 
 		b.StartTimer()
 
-		_, err := client.Dial(serverEP, principal)
+		_, err := client.Dial(serverEP, v23.GetPrincipal(nctx))
 		if err != nil {
-			vlog.Fatalf("Dial failed: %v", err)
+			ctx.Fatalf("Dial failed: %v", err)
 		}
 
 		b.StopTimer()
@@ -124,9 +128,11 @@
 	ctx, shutdown = test.V23Init()
 	defer shutdown()
 
-	var serverStop func()
-	serverEP, serverStop = internal.StartServer(ctx, v23.GetListenSpec(ctx))
-	defer serverStop()
+	server, err := xrpc.NewServer(ctx, "", internal.NewService(), securityflag.NewAuthorizerOrDie())
+	if err != nil {
+		vlog.Fatalf("NewServer failed: %v", err)
+	}
+	serverEP = server.Status().Endpoints[0]
 
 	runBenchmarks()
 }
diff --git a/runtime/internal/rpc/resolve_test.go b/runtime/internal/rpc/resolve_test.go
index 08b400a..2c33e04 100644
--- a/runtime/internal/rpc/resolve_test.go
+++ b/runtime/internal/rpc/resolve_test.go
@@ -18,6 +18,7 @@
 	"v.io/v23/rpc"
 
 	"v.io/x/ref/lib/flags"
+	"v.io/x/ref/lib/xrpc"
 	"v.io/x/ref/runtime/factories/fake"
 	"v.io/x/ref/runtime/internal"
 	"v.io/x/ref/runtime/internal/lib/appcycle"
@@ -70,25 +71,17 @@
 	ctx, shutdown := v23.Init()
 	defer shutdown()
 
-	lspec := v23.GetListenSpec(ctx)
-	server, err := v23.NewServer(ctx, options.ServesMountTable(true))
-	if err != nil {
-		return fmt.Errorf("root failed: %v", err)
-	}
 	mp := ""
 	mt, err := mounttablelib.NewMountTableDispatcher("", "", "mounttable")
 	if err != nil {
 		return fmt.Errorf("mounttablelib.NewMountTableDispatcher failed: %s", err)
 	}
-	eps, err := server.Listen(lspec)
+	server, err := xrpc.NewDispatchingServer(ctx, mp, mt, options.ServesMountTable(true))
 	if err != nil {
-		return fmt.Errorf("server.Listen failed: %s", err)
-	}
-	if err := server.ServeDispatcher(mp, mt); err != nil {
-		return fmt.Errorf("root failed: %s", err)
+		return fmt.Errorf("root failed: %v", err)
 	}
 	fmt.Fprintf(env.Stdout, "PID=%d\n", os.Getpid())
-	for _, ep := range eps {
+	for _, ep := range server.Status().Endpoints {
 		fmt.Fprintf(env.Stdout, "MT_NAME=%s\n", ep.Name())
 	}
 	modules.WaitForEOF(env.Stdin)
diff --git a/runtime/internal/rpc/stream/vif/faketimer.go b/runtime/internal/rpc/stream/vif/faketimer.go
index 914c4e2..d257a85 100644
--- a/runtime/internal/rpc/stream/vif/faketimer.go
+++ b/runtime/internal/rpc/stream/vif/faketimer.go
@@ -37,9 +37,15 @@
 func (t *fakeTimer) Reset(d time.Duration) bool {
 	t.mu.Lock()
 	defer t.mu.Unlock()
+	active := t.timer.Stop()
 	t.timeout = d
 	t.stopped = false
-	return t.timer.Reset(t.timeout)
+	if t.timeout > 0 {
+		t.timer = newTimer(t.timeout, t.timeoutFunc)
+	} else {
+		t.timer = noopTimer{}
+	}
+	return active
 }
 
 func (t *fakeTimer) run(release <-chan struct{}, wg *sync.WaitGroup) {
@@ -47,12 +53,9 @@
 	<-release // Wait until notified to run.
 	t.mu.Lock()
 	defer t.mu.Unlock()
-	if t.timeout > 0 {
+	if t.timeout > 0 && !t.stopped {
 		t.timer = newTimer(t.timeout, t.timeoutFunc)
 	}
-	if t.stopped {
-		t.timer.Stop()
-	}
 }
 
 // SetFakeTimers causes the idle timers to use a fake timer instead of one
diff --git a/runtime/internal/rpc/stream/vif/vif_test.go b/runtime/internal/rpc/stream/vif/vif_test.go
index 101258b..9d53964 100644
--- a/runtime/internal/rpc/stream/vif/vif_test.go
+++ b/runtime/internal/rpc/stream/vif/vif_test.go
@@ -476,9 +476,8 @@
 		}
 		return
 	}
-	newVC := func(vf, remote *vif.VIF) (VC stream.VC, ln stream.Listener, remoteVC stream.Connector) {
-		triggerTimers := vif.SetFakeTimers()
-		defer triggerTimers()
+	newVC := func(vf, remote *vif.VIF) (VC stream.VC, ln stream.Listener, remoteVC stream.Connector, triggerTimers func()) {
+		triggerTimers = vif.SetFakeTimers()
 		var err error
 		VC, remoteVC, err = createVC(vf, remote, pclient, makeEP(0x10), vc.IdleTimeout{idleTime})
 		if err != nil {
@@ -500,14 +499,15 @@
 
 	// No active flow. Should be notified.
 	vf, remote := newVIF()
-	_, _, _ = newVC(vf, remote)
+	_, _, _, triggerTimers := newVC(vf, remote)
+	triggerTimers()
 	if err := vif.WaitForNotifications(notify, vf, remote); err != nil {
 		t.Error(err)
 	}
 
 	// Same as above, but with multiple VCs.
 	vf, remote = newVIF()
-	triggerTimers := vif.SetFakeTimers()
+	triggerTimers = vif.SetFakeTimers()
 	if _, _, err := createNVCs(vf, remote, pclient, 0x10, 5, vc.IdleTimeout{idleTime}); err != nil {
 		t.Fatal(err)
 	}
@@ -518,10 +518,11 @@
 
 	// Open one flow. Should not be notified.
 	vf, remote = newVIF()
-	vc, _, _ := newVC(vf, remote)
+	vc, _, _, triggerTimers := newVC(vf, remote)
 	f1 := newFlow(vc, remote)
+	triggerTimers()
 	if err := vif.WaitWithTimeout(notify, waitTime); err != nil {
-		t.Error(err)
+		t.Fatal(err)
 	}
 
 	// Close the flow. Should be notified.
@@ -532,17 +533,17 @@
 
 	// Open two flows.
 	vf, remote = newVIF()
-	vc, _, _ = newVC(vf, remote)
+	vc, _, _, triggerTimers = newVC(vf, remote)
 	f1 = newFlow(vc, remote)
 	f2 := newFlow(vc, remote)
+	triggerTimers()
 
 	// Close the first flow twice. Should not be notified.
 	f1.Close()
 	f1.Close()
 	if err := vif.WaitWithTimeout(notify, waitTime); err != nil {
-		t.Error(err)
+		t.Fatal(err)
 	}
-
 	// Close the second flow. Should be notified now.
 	f2.Close()
 	if err := vif.WaitForNotifications(notify, vf, remote); err != nil {
@@ -551,14 +552,15 @@
 
 	// Same as above, but open a flow from the remote side.
 	vf, remote = newVIF()
-	_, ln, remoteVC := newVC(vf, remote)
+	_, ln, remoteVC, triggerTimers := newVC(vf, remote)
 	f1, err := remoteVC.Connect()
 	if err != nil {
 		t.Fatal(err)
 	}
 	acceptFlowAtClient(ln)
+	triggerTimers()
 	if err := vif.WaitWithTimeout(notify, waitTime); err != nil {
-		t.Error(err)
+		t.Fatal(err)
 	}
 	f1.Close()
 	if err := vif.WaitForNotifications(notify, vf, remote); err != nil {
diff --git a/runtime/internal/rpc/stress/internal/server.go b/runtime/internal/rpc/stress/internal/server.go
index 30b03da..563491a 100644
--- a/runtime/internal/rpc/stress/internal/server.go
+++ b/runtime/internal/rpc/stress/internal/server.go
@@ -7,13 +7,8 @@
 import (
 	"sync"
 
-	"v.io/v23"
 	"v.io/v23/context"
-	"v.io/v23/naming"
 	"v.io/v23/rpc"
-	"v.io/v23/security"
-	"v.io/x/lib/vlog"
-
 	"v.io/x/ref/runtime/internal/rpc/stress"
 )
 
@@ -24,6 +19,11 @@
 	stop chan struct{}
 }
 
+func NewService() (stress.StressServerStub, <-chan struct{}) {
+	s := &impl{stop: make(chan struct{})}
+	return stress.StressServer(s), s.stop
+}
+
 func (s *impl) Echo(_ *context.T, _ rpc.ServerCall, payload []byte) ([]byte, error) {
 	return payload, nil
 }
@@ -80,26 +80,3 @@
 	s.stop <- struct{}{}
 	return nil
 }
-
-// StartServer starts a server that implements the Stress service, and returns
-// the server and its vanadium address. It also returns a channel carrying stop
-// requests. After reading from the stop channel, the application should exit.
-func StartServer(ctx *context.T, listenSpec rpc.ListenSpec) (rpc.Server, naming.Endpoint, <-chan struct{}) {
-	server, err := v23.NewServer(ctx)
-	if err != nil {
-		vlog.Fatalf("NewServer failed: %v", err)
-	}
-	eps, err := server.Listen(listenSpec)
-	if err != nil {
-		vlog.Fatalf("Listen failed: %v", err)
-	}
-	if len(eps) == 0 {
-		vlog.Fatal("No local address to listen on")
-	}
-
-	s := impl{stop: make(chan struct{})}
-	if err := server.Serve("", stress.StressServer(&s), security.AllowEveryone()); err != nil {
-		vlog.Fatalf("Serve failed: %v", err)
-	}
-	return server, eps[0], s.stop
-}
diff --git a/runtime/internal/rpc/stress/stressd/main.go b/runtime/internal/rpc/stress/stressd/main.go
index 71ae935..8a27535 100644
--- a/runtime/internal/rpc/stress/stressd/main.go
+++ b/runtime/internal/rpc/stress/stressd/main.go
@@ -8,16 +8,16 @@
 package main
 
 import (
-	"fmt"
 	"runtime"
 	"time"
 
-	"v.io/v23"
 	"v.io/v23/context"
+	"v.io/v23/security"
 	"v.io/x/lib/cmdline"
 	"v.io/x/lib/vlog"
 	"v.io/x/ref/lib/signals"
 	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/lib/xrpc"
 	_ "v.io/x/ref/runtime/factories/static"
 	"v.io/x/ref/runtime/internal/rpc/stress/internal"
 )
@@ -40,8 +40,12 @@
 func runStressD(ctx *context.T, env *cmdline.Env, args []string) error {
 	runtime.GOMAXPROCS(runtime.NumCPU())
 
-	server, ep, stop := internal.StartServer(ctx, v23.GetListenSpec(ctx))
-	vlog.Infof("listening on %s", ep.Name())
+	service, stop := internal.NewService()
+	server, err := xrpc.NewServer(ctx, "", service, security.AllowEveryone())
+	if err != nil {
+		vlog.Fatalf("NewServer failed: %v", err)
+	}
+	vlog.Infof("listening on %s", server.Status().Endpoints[0].Name())
 
 	var timeout <-chan time.Time
 	if duration > 0 {
@@ -52,10 +56,5 @@
 	case <-stop:
 	case <-signals.ShutdownOnSignals(ctx):
 	}
-
-	if err := server.Stop(); err != nil {
-		return fmt.Errorf("Stop() failed: %v", err)
-	}
-	vlog.Info("stopped.")
 	return nil
 }
diff --git a/runtime/internal/rpc/test/client_test.go b/runtime/internal/rpc/test/client_test.go
index bd29251..e131796 100644
--- a/runtime/internal/rpc/test/client_test.go
+++ b/runtime/internal/rpc/test/client_test.go
@@ -25,6 +25,7 @@
 
 	"v.io/x/ref"
 	"v.io/x/ref/internal/logger"
+	"v.io/x/ref/lib/xrpc"
 	_ "v.io/x/ref/runtime/factories/generic"
 	inaming "v.io/x/ref/runtime/internal/naming"
 	irpc "v.io/x/ref/runtime/internal/rpc"
@@ -51,24 +52,16 @@
 	ctx, shutdown := v23.Init()
 	defer shutdown()
 
-	lspec := v23.GetListenSpec(ctx)
-	server, err := v23.NewServer(ctx, options.ServesMountTable(true), seclevel)
-	if err != nil {
-		return fmt.Errorf("root failed: %v", err)
-	}
 	mt, err := mounttablelib.NewMountTableDispatcher("", "", "mounttable")
 	if err != nil {
 		return fmt.Errorf("mounttablelib.NewMountTableDispatcher failed: %s", err)
 	}
-	eps, err := server.Listen(lspec)
+	server, err := xrpc.NewDispatchingServer(ctx, "", mt, options.ServesMountTable(true), seclevel)
 	if err != nil {
-		return fmt.Errorf("server.Listen failed: %s", err)
-	}
-	if err := server.ServeDispatcher("", mt); err != nil {
-		return fmt.Errorf("root failed: %s", err)
+		return fmt.Errorf("root failed: %v", err)
 	}
 	fmt.Fprintf(env.Stdout, "PID=%d\n", os.Getpid())
-	for _, ep := range eps {
+	for _, ep := range server.Status().Endpoints {
 		fmt.Fprintf(env.Stdout, "MT_NAME=%s\n", ep.Name())
 	}
 	modules.WaitForEOF(env.Stdin)
@@ -107,20 +100,12 @@
 
 	id, mp := args[0], args[1]
 	disp := &treeDispatcher{id: id}
-	server, err := v23.NewServer(ctx)
+	server, err := xrpc.NewDispatchingServer(ctx, mp, disp)
 	if err != nil {
 		return err
 	}
-	defer server.Stop()
-	eps, err := server.Listen(v23.GetListenSpec(ctx))
-	if err != nil {
-		return err
-	}
-	if err := server.ServeDispatcher(mp, disp); err != nil {
-		return err
-	}
 	fmt.Fprintf(env.Stdout, "PID=%d\n", os.Getpid())
-	for _, ep := range eps {
+	for _, ep := range server.Status().Endpoints {
 		fmt.Fprintf(env.Stdout, "NAME=%s\n", ep.Name())
 	}
 	modules.WaitForEOF(env.Stdin)
@@ -527,19 +512,12 @@
 }, "childPing")
 
 func initServer(t *testing.T, ctx *context.T, opts ...rpc.ServerOpt) (string, func()) {
-	server, err := v23.NewServer(ctx, opts...)
-	if err != nil {
-		t.Fatalf("unexpected error: %s", err)
-	}
 	done := make(chan struct{})
-	deferFn := func() { close(done); server.Stop() }
-
-	eps, err := server.Listen(v23.GetListenSpec(ctx))
+	server, err := xrpc.NewServer(ctx, "", &simple{done}, nil, opts...)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
-	server.Serve("", &simple{done}, nil)
-	return eps[0].Name(), deferFn
+	return server.Status().Endpoints[0].Name(), func() { close(done) }
 }
 
 func TestTimeoutResponse(t *testing.T) {
@@ -547,6 +525,7 @@
 	defer shutdown()
 	name, fn := initServer(t, ctx)
 	defer fn()
+
 	ctx, _ = context.WithTimeout(ctx, time.Millisecond)
 	err := v23.GetClient(ctx).Call(ctx, name, "Sleep", nil, nil)
 	if got, want := verror.ErrorID(err), verror.ErrTimeout.ID; got != want {
diff --git a/runtime/internal/rpc/test/glob_test.go b/runtime/internal/rpc/test/glob_test.go
index 471b6f2..3cb1979 100644
--- a/runtime/internal/rpc/test/glob_test.go
+++ b/runtime/internal/rpc/test/glob_test.go
@@ -11,7 +11,6 @@
 	"strings"
 	"testing"
 
-	"v.io/v23"
 	"v.io/v23/context"
 	"v.io/v23/i18n"
 	"v.io/v23/naming"
@@ -20,27 +19,12 @@
 	"v.io/v23/security"
 	"v.io/v23/verror"
 	"v.io/x/ref/lib/glob"
+	"v.io/x/ref/lib/xrpc"
 	_ "v.io/x/ref/runtime/factories/generic"
 	"v.io/x/ref/test"
 	"v.io/x/ref/test/testutil"
 )
 
-func startGlobServer(ctx *context.T, tree *node) (string, func(), error) {
-	server, err := v23.NewServer(ctx)
-	if err != nil {
-		return "", nil, fmt.Errorf("failed to start debug server: %v", err)
-	}
-	endpoints, err := server.Listen(v23.GetListenSpec(ctx))
-	if err != nil {
-		return "", nil, fmt.Errorf("failed to listen: %v", err)
-	}
-	if err := server.ServeDispatcher("", &disp{tree}); err != nil {
-		return "", nil, err
-	}
-	ep := endpoints[0].String()
-	return ep, func() { server.Stop() }, nil
-}
-
 func TestGlob(t *testing.T) {
 	ctx, shutdown := test.V23Init()
 	defer shutdown()
@@ -58,11 +42,11 @@
 		tree.find(strings.Split(p, "/"), true)
 	}
 
-	ep, stop, err := startGlobServer(ctx, tree)
+	server, err := xrpc.NewDispatchingServer(ctx, "", &disp{tree})
 	if err != nil {
-		t.Fatalf("startGlobServer: %v", err)
+		t.Fatalf("failed to start debug server: %v", err)
 	}
-	defer stop()
+	ep := server.Status().Endpoints[0].String()
 
 	var (
 		noExist        = verror.New(verror.ErrNoExist, ctx, "")
@@ -209,11 +193,12 @@
 	tree := newNode()
 	tree.find([]string{"a", "b"}, true)
 	tree.find([]string{"a", "deny", "x"}, true)
-	ep, stop, err := startGlobServer(ctx, tree)
+
+	server, err := xrpc.NewDispatchingServer(ctx, "", &disp{tree})
 	if err != nil {
-		t.Fatalf("startGlobServer: %v", err)
+		t.Fatalf("failed to start debug server: %v", err)
 	}
-	defer stop()
+	ep := server.Status().Endpoints[0].String()
 
 	testcases := []struct {
 		name, pattern string
diff --git a/runtime/internal/rpc/test/retry_test.go b/runtime/internal/rpc/test/retry_test.go
index 0b28efb..736f45e 100644
--- a/runtime/internal/rpc/test/retry_test.go
+++ b/runtime/internal/rpc/test/retry_test.go
@@ -13,6 +13,7 @@
 	"v.io/v23/rpc"
 	"v.io/v23/security"
 	"v.io/v23/verror"
+	"v.io/x/ref/lib/xrpc"
 )
 
 var errRetryThis = verror.Register("retry_test.retryThis", verror.RetryBackoff, "retryable error")
@@ -37,19 +38,12 @@
 	defer shutdown()
 
 	// Start the server.
-	server, err := v23.NewServer(ctx)
-	if err != nil {
-		t.Fatal(err)
-	}
-	eps, err := server.Listen(v23.GetListenSpec(ctx))
-	if err != nil {
-		t.Fatal(err)
-	}
 	rs := retryServer{}
-	if err = server.Serve("", &rs, security.AllowEveryone()); err != nil {
+	server, err := xrpc.NewServer(ctx, "", &rs, security.AllowEveryone())
+	if err != nil {
 		t.Fatal(err)
 	}
-	name := eps[0].Name()
+	name := server.Status().Endpoints[0].Name()
 
 	client := v23.GetClient(ctx)
 	// A traditional client.StartCall/call.Finish sequence should fail at
diff --git a/runtime/internal/rpc/test/signature_test.go b/runtime/internal/rpc/test/signature_test.go
index f6197c0..8c3c492 100644
--- a/runtime/internal/rpc/test/signature_test.go
+++ b/runtime/internal/rpc/test/signature_test.go
@@ -5,37 +5,20 @@
 package test
 
 import (
-	"fmt"
 	"reflect"
 	"testing"
 
-	"v.io/v23"
 	"v.io/v23/context"
-	"v.io/v23/naming"
 	"v.io/v23/rpc"
 	"v.io/v23/rpc/reserved"
 	"v.io/v23/vdl"
 	"v.io/v23/vdlroot/signature"
 
+	"v.io/x/ref/lib/xrpc"
 	_ "v.io/x/ref/runtime/factories/generic"
 	"v.io/x/ref/test"
 )
 
-func startSigServer(ctx *context.T, sig sigImpl) (string, func(), error) {
-	server, err := v23.NewServer(ctx)
-	if err != nil {
-		return "", nil, fmt.Errorf("failed to start sig server: %v", err)
-	}
-	eps, err := server.Listen(v23.GetListenSpec(ctx))
-	if err != nil {
-		return "", nil, fmt.Errorf("failed to listen: %v", err)
-	}
-	if err := server.Serve("", sig, nil); err != nil {
-		return "", nil, err
-	}
-	return eps[0].String(), func() { server.Stop() }, nil
-}
-
 type sigImpl struct{}
 
 func (sigImpl) NonStreaming0(*context.T, rpc.ServerCall) error                   { panic("X") }
@@ -62,12 +45,11 @@
 func TestMethodSignature(t *testing.T) {
 	ctx, shutdown := test.V23Init()
 	defer shutdown()
-	ep, stop, err := startSigServer(ctx, sigImpl{})
+	server, err := xrpc.NewServer(ctx, "", sigImpl{}, nil)
 	if err != nil {
-		t.Fatalf("startSigServer: %v", err)
+		t.Fatalf("failed to start sig server: %v", err)
 	}
-	defer stop()
-	name := naming.JoinAddressName(ep, "")
+	name := server.Status().Endpoints[0].Name()
 
 	tests := []struct {
 		Method string
@@ -108,12 +90,11 @@
 func TestSignature(t *testing.T) {
 	ctx, shutdown := test.V23Init()
 	defer shutdown()
-	ep, stop, err := startSigServer(ctx, sigImpl{})
+	server, err := xrpc.NewServer(ctx, "", sigImpl{}, nil)
 	if err != nil {
-		t.Fatalf("startSigServer: %v", err)
+		t.Fatalf("failed to start sig server: %v", err)
 	}
-	defer stop()
-	name := naming.JoinAddressName(ep, "")
+	name := server.Status().Endpoints[0].Name()
 	sig, err := reserved.Signature(ctx, name)
 	if err != nil {
 		t.Errorf("call failed: %v", err)
diff --git a/runtime/internal/rt/ipc_test.go b/runtime/internal/rt/ipc_test.go
index f14549f..5b3242b 100644
--- a/runtime/internal/rt/ipc_test.go
+++ b/runtime/internal/rt/ipc_test.go
@@ -14,11 +14,11 @@
 
 	"v.io/v23"
 	"v.io/v23/context"
-	"v.io/v23/naming"
 	"v.io/v23/options"
 	"v.io/v23/rpc"
 	"v.io/v23/security"
 	"v.io/v23/verror"
+	"v.io/x/ref/lib/xrpc"
 	_ "v.io/x/ref/runtime/factories/generic"
 	"v.io/x/ref/runtime/internal/rpc/stream/vc"
 	"v.io/x/ref/test"
@@ -82,22 +82,6 @@
 	return tpc
 }
 
-func startServer(ctx *context.T, s interface{}, opts ...rpc.ServerOpt) (rpc.Server, string, error) {
-	server, err := v23.NewServer(ctx, opts...)
-	if err != nil {
-		return nil, "", err
-	}
-	endpoints, err := server.Listen(v23.GetListenSpec(ctx))
-	if err != nil {
-		return nil, "", err
-	}
-	serverObjectName := naming.JoinAddressName(endpoints[0].String(), "")
-	if err := server.Serve("", s, security.AllowEveryone()); err != nil {
-		return nil, "", err
-	}
-	return server, serverObjectName, nil
-}
-
 func TestClientServerBlessings(t *testing.T) {
 	ctx, shutdown := test.V23Init()
 	defer shutdown()
@@ -153,10 +137,11 @@
 			t.Errorf("pserver.SetDefault(%v) failed: %v", test.server, err)
 			continue
 		}
-		server, serverObjectName, err := startServer(serverCtx, testService{})
+		server, err := xrpc.NewServer(serverCtx, "", testService{}, security.AllowEveryone())
 		if err != nil {
 			t.Fatal(err)
 		}
+		serverObjectName := server.Status().Endpoints[0].Name()
 		ctx, client, err := v23.WithNewClient(clientCtx)
 		if err != nil {
 			panic(err)
@@ -201,34 +186,14 @@
 		t.Fatal(err)
 	}
 	for idx, test := range tests {
-		server, err := v23.NewServer(ctx, test.opts...)
+		server, err := xrpc.NewServer(ctx, "", testService{}, nil, test.opts...)
 		if err != nil {
 			t.Errorf("test #%d: %v", idx, err)
 			continue
 		}
-		endpoints, err := server.Listen(v23.GetListenSpec(ctx))
-		if err != nil {
-			t.Errorf("test #%d: Listen(%#v) failed with %v", idx, v23.GetListenSpec(ctx), err)
-			continue
-		}
-		if len(endpoints) == 0 {
-			t.Errorf("test #%d: No endpoints?", idx)
-		}
+		status := server.Status()
 		want := test.blessings
 		sort.Strings(want)
-		for _, ep := range endpoints {
-			got := ep.BlessingNames()
-			sort.Strings(got)
-			if !reflect.DeepEqual(got, want) {
-				t.Errorf("test #%d: endpoint=%q: Got blessings %v, want %v", idx, ep, got, want)
-			}
-		}
-		status := server.Status()
-		// The tests below are dubious: status.Endpoints might be empty and
-		// more likely at this point status.Proxies[i].Endpoints is
-		// empoty for all i because at the time this test was written,
-		// no proxies were started. Anyway, just to express the
-		// intent...
 		for _, ep := range status.Endpoints {
 			got := ep.BlessingNames()
 			sort.Strings(got)
@@ -236,6 +201,10 @@
 				t.Errorf("test #%d: endpoint=%q: Got blessings %v, want %v", idx, ep, got, want)
 			}
 		}
+		// The tests below are dubious: status.Proxies[i].Endpoints is
+		// empty for all i because at the time this test was written,
+		// no proxies were started. Anyway, just to express the
+		// intent...
 		for _, proxy := range status.Proxies {
 			ep := proxy.Endpoint
 			if got := ep.BlessingNames(); !reflect.DeepEqual(got, want) {
@@ -289,19 +258,20 @@
 		t.Fatal(err)
 	}
 	ds := &dischargeService{}
-	dischargeServer, dischargeServerName, err := startServer(dischargerCtx, ds)
+	server, err := xrpc.NewServer(dischargerCtx, "", ds, security.AllowEveryone())
 	if err != nil {
 		t.Fatal(err)
 	}
-	defer dischargeServer.Stop()
+	dischargeServerName := server.Status().Endpoints[0].Name()
+
 	if err := root.Bless(pserver, "server", mkThirdPartyCaveat(pdischarger.PublicKey(), dischargeServerName)); err != nil {
 		t.Fatal(err)
 	}
-	server, serverName, err := startServer(serverCtx, &testService{}, vc.DischargeExpiryBuffer(10*time.Millisecond))
+	server, err = xrpc.NewServer(serverCtx, "", testService{}, security.AllowEveryone(), vc.DischargeExpiryBuffer(10*time.Millisecond))
 	if err != nil {
 		t.Fatal(err)
 	}
-	defer server.Stop()
+	serverName := server.Status().Endpoints[0].Name()
 
 	// Setup up the client's blessing store so that it can talk to the server.
 	rootClient := mkBlessings(root.NewBlessings(pclient, "client"))
diff --git a/runtime/internal/rt/mgmt_test.go b/runtime/internal/rt/mgmt_test.go
index 0a0d49d..be8c552 100644
--- a/runtime/internal/rt/mgmt_test.go
+++ b/runtime/internal/rt/mgmt_test.go
@@ -14,11 +14,11 @@
 
 	"v.io/v23"
 	"v.io/v23/context"
-	"v.io/v23/naming"
 	"v.io/v23/rpc"
 	"v.io/v23/services/appcycle"
 	"v.io/x/ref/lib/mgmt"
 	"v.io/x/ref/lib/security/securityflag"
+	"v.io/x/ref/lib/xrpc"
 	"v.io/x/ref/services/device"
 	"v.io/x/ref/test"
 	"v.io/x/ref/test/expect"
@@ -272,26 +272,18 @@
 
 }
 
-func createConfigServer(t *testing.T, ctx *context.T) (rpc.Server, string, <-chan string) {
-	server, err := v23.NewServer(ctx)
-	if err != nil {
-		t.Fatalf("Got error: %v", err)
-	}
-	ch := make(chan string)
-	var eps []naming.Endpoint
-	if eps, err = server.Listen(v23.GetListenSpec(ctx)); err != nil {
-		t.Fatalf("Got error: %v", err)
-	}
-	if err := server.Serve("", device.ConfigServer(&configServer{ch}), securityflag.NewAuthorizerOrDie()); err != nil {
-		t.Fatalf("Got error: %v", err)
-	}
-	return server, eps[0].Name(), ch
-}
-
 func setupRemoteAppCycleMgr(t *testing.T) (*context.T, modules.Handle, appcycle.AppCycleClientMethods, func()) {
 	ctx, shutdown := test.V23Init()
 
-	configServer, configServiceName, ch := createConfigServer(t, ctx)
+	ch := make(chan string)
+	service := device.ConfigServer(&configServer{ch})
+	authorizer := securityflag.NewAuthorizerOrDie()
+	configServer, err := xrpc.NewServer(ctx, "", service, authorizer)
+	if err != nil {
+		t.Fatalf("Got error: %v", err)
+	}
+	configServiceName := configServer.Status().Endpoints[0].Name()
+
 	sh, err := modules.NewShell(ctx, v23.GetPrincipal(ctx), testing.Verbose(), t)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
diff --git a/runtime/internal/rt/runtime.go b/runtime/internal/rt/runtime.go
index 09d1b70..5f62d86 100644
--- a/runtime/internal/rt/runtime.go
+++ b/runtime/internal/rt/runtime.go
@@ -51,6 +51,7 @@
 	principalKey
 	backgroundKey
 	reservedNameKey
+	listenKey
 
 	// initKey is used to store values that are only set at init time.
 	initKey
@@ -58,7 +59,6 @@
 
 type initData struct {
 	appCycle          v23.AppCycle
-	listenSpec        *rpc.ListenSpec
 	protocols         []string
 	settingsPublisher *pubsub.Publisher
 	settingsName      string
@@ -86,12 +86,15 @@
 
 	ctx = context.WithValue(ctx, initKey, &initData{
 		protocols:         protocols,
-		listenSpec:        listenSpec,
 		appCycle:          appCycle,
 		settingsPublisher: settingsPublisher,
 		settingsName:      settingsName,
 	})
 
+	if listenSpec != nil {
+		ctx = context.WithValue(ctx, listenKey, listenSpec.Copy())
+	}
+
 	if reservedDispatcher != nil {
 		ctx = context.WithValue(ctx, reservedNameKey, reservedDispatcher)
 	}
@@ -441,10 +444,13 @@
 
 func (*Runtime) GetListenSpec(ctx *context.T) rpc.ListenSpec {
 	// nologcall
-	if id, _ := ctx.Value(initKey).(*initData); id.listenSpec != nil {
-		return id.listenSpec.Copy()
-	}
-	return rpc.ListenSpec{}
+	ls, _ := ctx.Value(listenKey).(rpc.ListenSpec)
+	return ls
+}
+
+func (*Runtime) WithListenSpec(ctx *context.T, ls rpc.ListenSpec) *context.T {
+	// nologcall
+	return context.WithValue(ctx, listenKey, ls.Copy())
 }
 
 func (*Runtime) WithBackgroundContext(ctx *context.T) *context.T {
diff --git a/runtime/internal/rt/shutdown_servers_test.go b/runtime/internal/rt/shutdown_servers_test.go
index 38dd5e3..8514017 100644
--- a/runtime/internal/rt/shutdown_servers_test.go
+++ b/runtime/internal/rt/shutdown_servers_test.go
@@ -18,6 +18,7 @@
 	"v.io/v23/rpc"
 	"v.io/x/lib/vlog"
 	"v.io/x/ref/lib/signals"
+	"v.io/x/ref/lib/xrpc"
 	_ "v.io/x/ref/runtime/factories/generic"
 	"v.io/x/ref/test"
 	"v.io/x/ref/test/modules"
@@ -27,21 +28,6 @@
 
 func (*dummy) Echo(*context.T, rpc.ServerCall) error { return nil }
 
-// makeServer sets up a simple dummy server.
-func makeServer(ctx *context.T) rpc.Server {
-	server, err := v23.NewServer(ctx)
-	if err != nil {
-		vlog.Fatalf("r.NewServer error: %s", err)
-	}
-	if _, err := server.Listen(v23.GetListenSpec(ctx)); 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(ctx *context.T, stdin io.Reader) func() {
@@ -82,8 +68,14 @@
 	defer remoteCmdLoop(ctx, env.Stdin)()
 
 	// Create a couple servers, and start serving.
-	server1 := makeServer(ctx)
-	server2 := makeServer(ctx)
+	server1, err := xrpc.NewServer(ctx, "", &dummy{}, nil)
+	if err != nil {
+		vlog.Fatalf("r.NewServer error: %s", err)
+	}
+	server2, err := xrpc.NewServer(ctx, "", &dummy{}, nil)
+	if err != nil {
+		vlog.Fatalf("r.NewServer error: %s", err)
+	}
 
 	// This is how to wait for a shutdown.  In this example, a shutdown
 	// comes from a signal or a stop command.
@@ -228,7 +220,10 @@
 	defer remoteCmdLoop(ctx, env.Stdin)()
 
 	// Create a server, and start serving.
-	server := makeServer(ctx)
+	server, err := xrpc.NewServer(ctx, "", &dummy{}, nil)
+	if err != nil {
+		vlog.Fatalf("r.NewServer error: %s", err)
+	}
 
 	// This is how to wait for a shutdown.  In this example, a shutdown
 	// comes from a signal or a stop command.
diff --git a/runtime/internal/testing/mocks/mocknet/mocknet_test.go b/runtime/internal/testing/mocks/mocknet/mocknet_test.go
index f038325..8e7a73c 100644
--- a/runtime/internal/testing/mocks/mocknet/mocknet_test.go
+++ b/runtime/internal/testing/mocks/mocknet/mocknet_test.go
@@ -21,6 +21,7 @@
 	"v.io/v23/rpc"
 	"v.io/v23/verror"
 
+	"v.io/x/ref/lib/xrpc"
 	_ "v.io/x/ref/runtime/factories/generic"
 	"v.io/x/ref/runtime/internal/rpc/stream/crypto"
 	"v.io/x/ref/runtime/internal/rpc/stream/message"
@@ -332,24 +333,18 @@
 
 type simple struct{}
 
-func (s *simple) Ping(call rpc.ServerCall) (string, error) {
+func (s *simple) Ping(ctx *context.T, call rpc.ServerCall) (string, error) {
 	return "pong", nil
 }
 
 func initServer(t *testing.T, ctx *context.T) (string, func()) {
-	server, err := v23.NewServer(ctx, options.SecurityNone)
+	server, err := xrpc.NewServer(ctx, "", &simple{}, nil, options.SecurityNone)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
 	done := make(chan struct{})
 	deferFn := func() { close(done); server.Stop() }
-
-	eps, err := server.Listen(v23.GetListenSpec(ctx))
-	if err != nil {
-		t.Fatalf("unexpected error: %s", err)
-	}
-	server.Serve("", &simple{}, nil)
-	return eps[0].Name(), deferFn
+	return server.Status().Endpoints[0].Name(), deferFn
 }
 
 func TestV23Control(t *testing.T) {
diff --git a/runtime/internal/vtrace/vtrace_test.go b/runtime/internal/vtrace/vtrace_test.go
index 9971176..dbdb299 100644
--- a/runtime/internal/vtrace/vtrace_test.go
+++ b/runtime/internal/vtrace/vtrace_test.go
@@ -22,6 +22,7 @@
 
 	"v.io/x/ref/lib/flags"
 	_ "v.io/x/ref/lib/security/securityflag"
+	"v.io/x/ref/lib/xrpc"
 	_ "v.io/x/ref/runtime/factories/generic"
 	ivtrace "v.io/x/ref/runtime/internal/vtrace"
 	"v.io/x/ref/services/mounttable/mounttablelib"
@@ -37,27 +38,20 @@
 func initForTest(t *testing.T) (*context.T, v23.Shutdown, *testutil.IDProvider) {
 	idp := testutil.NewIDProvider("base")
 	ctx, shutdown := test.V23Init()
+
 	if err := idp.Bless(v23.GetPrincipal(ctx), "alice"); err != nil {
 		t.Fatalf("Could not bless initial principal %v", err)
 	}
-
 	// Start a local mounttable.
-	s, err := v23.NewServer(ctx, options.ServesMountTable(true))
-	if err != nil {
-		t.Fatalf("Could not create mt server %v", err)
-	}
-	eps, err := s.Listen(v23.GetListenSpec(ctx))
-	if err != nil {
-		t.Fatalf("Could not listen for mt %v", err)
-	}
 	disp, err := mounttablelib.NewMountTableDispatcher("", "", "mounttable")
 	if err != nil {
 		t.Fatalf("Could not create mt dispatcher %v", err)
 	}
-	if err := s.ServeDispatcher("", disp); err != nil {
-		t.Fatalf("Could not serve mt dispatcher %v", err)
+	s, err := xrpc.NewDispatchingServer(ctx, "", disp, options.ServesMountTable(true))
+	if err != nil {
+		t.Fatalf("Could not create mt server %v", err)
 	}
-	v23.GetNamespace(ctx).SetRoots(eps[0].Name())
+	v23.GetNamespace(ctx).SetRoots(s.Status().Endpoints[0].Name())
 	return ctx, shutdown, idp
 }
 
@@ -179,20 +173,14 @@
 	if err != nil {
 		return nil, err
 	}
-	s, err := v23.NewServer(ctx)
+	c := &testServer{
+		name: name,
+	}
+	s, err := xrpc.NewServer(ctx, name, c, security.AllowEveryone())
 	if err != nil {
 		return nil, err
 	}
-	if _, err := s.Listen(v23.GetListenSpec(ctx)); err != nil {
-		return nil, err
-	}
-	c := &testServer{
-		name: name,
-		stop: s.Stop,
-	}
-	if err := s.Serve(name, c, security.AllowEveryone()); err != nil {
-		return nil, err
-	}
+	c.stop = s.Stop
 	return c, nil
 }
 
diff --git a/services/agent/internal/pingpong/main.go b/services/agent/internal/pingpong/main.go
index 24265f7..d1771e9 100644
--- a/services/agent/internal/pingpong/main.go
+++ b/services/agent/internal/pingpong/main.go
@@ -10,14 +10,15 @@
 import (
 	"fmt"
 
-	"v.io/v23"
 	"v.io/v23/context"
 	"v.io/v23/rpc"
 	"v.io/v23/security"
 	"v.io/x/lib/cmdline"
 	"v.io/x/lib/vlog"
+	"v.io/x/ref/lib/flags"
 	"v.io/x/ref/lib/signals"
 	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/lib/xrpc"
 
 	_ "v.io/x/ref/runtime/factories/generic"
 )
@@ -25,6 +26,7 @@
 func main() {
 	cmdline.HideGlobalFlagsExcept()
 	cmdline.Main(cmdPingPong)
+	flags.SetDefaultHostPort("127.0.0.1:0")
 }
 
 var cmdPingPong = &cmdline.Command{
@@ -61,26 +63,17 @@
 }
 
 func serverMain(ctx *context.T) error {
-	s, err := v23.NewServer(ctx)
-	if err != nil {
-		return fmt.Errorf("failure creating server: %v", err)
-	}
-	vlog.Info("Waiting for ping")
-	spec := rpc.ListenSpec{Addrs: rpc.ListenAddrs{{"tcp", "127.0.0.1:0"}}}
-	endpoints, err := s.Listen(spec)
-	if err != nil {
-		return fmt.Errorf("error listening to service: %v", err)
-	}
-	fmt.Printf("NAME=%v\n", endpoints[0].Name())
 	// Provide an empty name, no need to mount on any mounttable.
 	//
 	// Use the default authorization policy (nil authorizer), which will
 	// only authorize clients if the blessings of the client is a prefix of
 	// that of the server or vice-versa.
-	if err := s.Serve("", PingPongServer(&pongd{}), nil); err != nil {
-		return fmt.Errorf("error serving service: %v", err)
+	s, err := xrpc.NewServer(ctx, "", PingPongServer(&pongd{}), nil)
+	if err != nil {
+		return fmt.Errorf("failure creating server: %v", err)
 	}
-
+	vlog.Info("Waiting for ping")
+	fmt.Printf("NAME=%v\n", s.Status().Endpoints[0].Name())
 	// Wait forever.
 	<-signals.ShutdownOnSignals(ctx)
 	return nil
diff --git a/services/application/application/impl_test.go b/services/application/application/impl_test.go
index 676676a..87c1074 100644
--- a/services/application/application/impl_test.go
+++ b/services/application/application/impl_test.go
@@ -11,7 +11,6 @@
 	"strings"
 	"testing"
 
-	"v.io/v23"
 	"v.io/v23/context"
 	"v.io/v23/naming"
 	"v.io/v23/rpc"
@@ -21,6 +20,7 @@
 	"v.io/x/lib/cmdline"
 	"v.io/x/lib/vlog"
 	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/lib/xrpc"
 	_ "v.io/x/ref/runtime/factories/generic"
 	"v.io/x/ref/services/repository"
 	"v.io/x/ref/test"
@@ -109,51 +109,23 @@
 	return nil, "", nil
 }
 
-type dispatcher struct {
-}
-
-func NewDispatcher() rpc.Dispatcher {
-	return &dispatcher{}
-}
+type dispatcher struct{}
 
 func (d *dispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) {
 	return repository.ApplicationServer(&server{suffix: suffix}), nil, nil
 }
 
-func startServer(t *testing.T, ctx *context.T) (rpc.Server, naming.Endpoint, error) {
-	dispatcher := NewDispatcher()
-	server, err := v23.NewServer(ctx)
-	if err != nil {
-		t.Errorf("NewServer failed: %v", err)
-		return nil, nil, err
-	}
-	endpoints, err := server.Listen(v23.GetListenSpec(ctx))
-	if err != nil {
-		t.Errorf("Listen failed: %v", err)
-		return nil, nil, err
-	}
-	if err := server.ServeDispatcher("", dispatcher); err != nil {
-		t.Errorf("Serve failed: %v", err)
-		return nil, nil, err
-	}
-	return server, endpoints[0], nil
-}
-
-func stopServer(t *testing.T, server rpc.Server) {
-	if err := server.Stop(); err != nil {
-		t.Errorf("server.Stop failed: %v", err)
-	}
-}
-
 func TestApplicationClient(t *testing.T) {
 	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
-	server, endpoint, err := startServer(t, ctx)
+	server, err := xrpc.NewDispatchingServer(ctx, "", &dispatcher{})
 	if err != nil {
+		t.Errorf("NewServer failed: %v", err)
 		return
 	}
-	defer stopServer(t, server)
+	endpoint := server.Status().Endpoints[0]
+
 	// Setup the command-line.
 	var stdout, stderr bytes.Buffer
 	env := &cmdline.Env{Stdout: &stdout, Stderr: &stderr}
diff --git a/services/application/applicationd/impl_test.go b/services/application/applicationd/impl_test.go
index 7284d8a..68b07f9 100644
--- a/services/application/applicationd/impl_test.go
+++ b/services/application/applicationd/impl_test.go
@@ -17,8 +17,8 @@
 	"v.io/v23/services/application"
 	"v.io/v23/verror"
 
+	"v.io/x/ref/lib/xrpc"
 	appd "v.io/x/ref/services/application/applicationd"
-	"v.io/x/ref/services/internal/servicetest"
 	"v.io/x/ref/services/repository"
 	"v.io/x/ref/test"
 	"v.io/x/ref/test/testutil"
@@ -55,12 +55,11 @@
 		t.Fatalf("NewDispatcher() failed: %v", err)
 	}
 
-	server, endpoint := servicetest.NewServer(ctx)
-	defer server.Stop()
-
-	if err := server.ServeDispatcher("", dispatcher); err != nil {
-		t.Fatalf("Serve(%v) failed: %v", dispatcher, err)
+	server, err := xrpc.NewDispatchingServer(ctx, "", dispatcher)
+	if err != nil {
+		t.Fatalf("NewServer(%v) failed: %v", dispatcher, err)
 	}
+	endpoint := server.Status().Endpoints[0].String()
 
 	// Create client stubs for talking to the server.
 	stub := repository.ApplicationClient(naming.JoinAddressName(endpoint, "search"))
@@ -245,11 +244,11 @@
 		t.Fatalf("NewDispatcher() failed: %v", err)
 	}
 
-	server, endpoint := servicetest.NewServer(ctx)
-
-	if err := server.ServeDispatcher("", dispatcher); err != nil {
+	server, err := xrpc.NewDispatchingServer(ctx, "", dispatcher)
+	if err != nil {
 		t.Fatalf("Serve(%v) failed: %v", dispatcher, err)
 	}
+	endpoint := server.Status().Endpoints[0].String()
 
 	// Create client stubs for talking to the server.
 	stubV1 := repository.ApplicationClient(naming.JoinAddressName(endpoint, "search/v1"))
@@ -288,12 +287,11 @@
 		t.Fatalf("NewDispatcher() failed: %v", err)
 	}
 
-	server, endpoint = servicetest.NewServer(ctx)
-	defer server.Stop()
-
-	if err := server.ServeDispatcher("", dispatcher); err != nil {
-		t.Fatalf("Serve(%v) failed: %v", dispatcher, err)
+	server, err = xrpc.NewDispatchingServer(ctx, "", dispatcher)
+	if err != nil {
+		t.Fatalf("NewServer(%v) failed: %v", dispatcher, err)
 	}
+	endpoint = server.Status().Endpoints[0].String()
 
 	stubV1 = repository.ApplicationClient(naming.JoinAddressName(endpoint, "search/v1"))
 
diff --git a/services/application/applicationd/main.go b/services/application/applicationd/main.go
index 0d3756c..8ba7d28 100644
--- a/services/application/applicationd/main.go
+++ b/services/application/applicationd/main.go
@@ -10,12 +10,12 @@
 import (
 	"fmt"
 
-	"v.io/v23"
 	"v.io/v23/context"
 	"v.io/x/lib/cmdline"
 	"v.io/x/lib/vlog"
 	"v.io/x/ref/lib/signals"
 	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/lib/xrpc"
 	_ "v.io/x/ref/runtime/factories/roaming"
 )
 
@@ -44,26 +44,17 @@
 		return env.UsageErrorf("Specify a directory for storing application envelopes using --store=<name>")
 	}
 
-	server, err := v23.NewServer(ctx)
-	if err != nil {
-		return fmt.Errorf("NewServer() failed: %v", err)
-	}
-	defer server.Stop()
-
 	dispatcher, err := NewDispatcher(store)
 	if err != nil {
 		return fmt.Errorf("NewDispatcher() failed: %v", err)
 	}
 
-	ls := v23.GetListenSpec(ctx)
-	endpoints, err := server.Listen(ls)
+	server, err := xrpc.NewDispatchingServer(ctx, name, dispatcher)
 	if err != nil {
-		return fmt.Errorf("Listen(%s) failed: %v", ls, err)
+		return fmt.Errorf("NewServer() failed: %v", err)
 	}
-	if err := server.ServeDispatcher(name, dispatcher); err != nil {
-		return fmt.Errorf("Serve(%v) failed: %v", name, err)
-	}
-	epName := endpoints[0].Name()
+	defer server.Stop()
+	epName := server.Status().Endpoints[0].Name()
 	if name != "" {
 		vlog.Infof("Application repository serving at %q (%q)", name, epName)
 	} else {
diff --git a/services/application/applicationd/perms_test.go b/services/application/applicationd/perms_test.go
index ad04614..686279c 100644
--- a/services/application/applicationd/perms_test.go
+++ b/services/application/applicationd/perms_test.go
@@ -19,6 +19,7 @@
 	"v.io/v23/verror"
 	"v.io/x/lib/vlog"
 	"v.io/x/ref/lib/signals"
+	"v.io/x/ref/lib/xrpc"
 	appd "v.io/x/ref/services/application/applicationd"
 	"v.io/x/ref/services/internal/servicetest"
 	"v.io/x/ref/services/repository"
@@ -43,19 +44,16 @@
 
 	defer fmt.Fprintf(env.Stdout, "%v terminating\n", publishName)
 	defer vlog.VI(1).Infof("%v terminating", publishName)
-	server, endpoint := servicetest.NewServer(ctx)
-	defer server.Stop()
-
-	name := naming.JoinAddressName(endpoint, "")
-	vlog.VI(1).Infof("applicationd name: %v", name)
 
 	dispatcher, err := appd.NewDispatcher(storedir)
 	if err != nil {
 		vlog.Fatalf("Failed to create repository dispatcher: %v", err)
 	}
-	if err := server.ServeDispatcher(publishName, dispatcher); err != nil {
+	server, err := xrpc.NewDispatchingServer(ctx, publishName, dispatcher)
+	if err != nil {
 		vlog.Fatalf("Serve(%v) failed: %v", publishName, err)
 	}
+	vlog.VI(1).Infof("applicationd name: %v", server.Status().Endpoints[0].Name())
 
 	fmt.Fprintf(env.Stdout, "ready:%d\n", os.Getpid())
 	<-signals.ShutdownOnSignals(ctx)
diff --git a/services/binary/binary/impl_test.go b/services/binary/binary/impl_test.go
index e4dc1a6..ecd41fd 100644
--- a/services/binary/binary/impl_test.go
+++ b/services/binary/binary/impl_test.go
@@ -15,7 +15,6 @@
 	"strings"
 	"testing"
 
-	"v.io/v23"
 	"v.io/v23/context"
 	"v.io/v23/naming"
 	"v.io/v23/rpc"
@@ -26,6 +25,7 @@
 	"v.io/x/lib/cmdline"
 	"v.io/x/lib/vlog"
 	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/lib/xrpc"
 	_ "v.io/x/ref/runtime/factories/generic"
 	"v.io/x/ref/test"
 )
@@ -101,40 +101,15 @@
 	return repository.BinaryServer(&server{suffix: suffix}), nil, nil
 }
 
-func startServer(t *testing.T, ctx *context.T) (rpc.Server, naming.Endpoint, error) {
-	dispatcher := NewDispatcher()
-	server, err := v23.NewServer(ctx)
-	if err != nil {
-		t.Errorf("NewServer failed: %v", err)
-		return nil, nil, err
-	}
-	endpoints, err := server.Listen(v23.GetListenSpec(ctx))
-	if err != nil {
-		t.Errorf("Listen failed: %v", err)
-		return nil, nil, err
-	}
-	if err := server.ServeDispatcher("", dispatcher); err != nil {
-		t.Errorf("ServeDispatcher failed: %v", err)
-		return nil, nil, err
-	}
-	return server, endpoints[0], nil
-}
-
-func stopServer(t *testing.T, server rpc.Server) {
-	if err := server.Stop(); err != nil {
-		t.Errorf("server.Stop failed: %v", err)
-	}
-}
-
 func TestBinaryClient(t *testing.T) {
 	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
-	server, endpoint, err := startServer(t, ctx)
+	server, err := xrpc.NewDispatchingServer(ctx, "", NewDispatcher())
 	if err != nil {
-		return
+		t.Fatalf("NewServer failed: %v", err)
 	}
-	defer stopServer(t, server)
+	endpoint := server.Status().Endpoints[0]
 
 	// Setup the command-line.
 	var out bytes.Buffer
diff --git a/services/binary/binaryd/main.go b/services/binary/binaryd/main.go
index 5854f1b..71916c0 100644
--- a/services/binary/binaryd/main.go
+++ b/services/binary/binaryd/main.go
@@ -20,6 +20,7 @@
 	"v.io/x/lib/vlog"
 	"v.io/x/ref/lib/signals"
 	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/lib/xrpc"
 	_ "v.io/x/ref/runtime/factories/roaming"
 	"v.io/x/ref/services/internal/binarylib"
 )
@@ -93,25 +94,17 @@
 			os.Exit(1)
 		}
 	}()
-	server, err := v23.NewServer(ctx)
-	if err != nil {
-		return fmt.Errorf("NewServer() failed: %v", err)
-	}
-	defer server.Stop()
-	ls := v23.GetListenSpec(ctx)
-	endpoints, err := server.Listen(ls)
-	if err != nil {
-		return fmt.Errorf("Listen(%s) failed: %v", ls, err)
-	}
 
 	dis, err := binarylib.NewDispatcher(v23.GetPrincipal(ctx), state)
 	if err != nil {
 		return fmt.Errorf("NewDispatcher() failed: %v\n", err)
 	}
-	if err := server.ServeDispatcher(name, dis); err != nil {
-		return fmt.Errorf("ServeDispatcher(%v) failed: %v", name, err)
+	server, err := xrpc.NewDispatchingServer(ctx, name, dis)
+	if err != nil {
+		return fmt.Errorf("NewServer() failed: %v", err)
 	}
-	epName := endpoints[0].Name()
+	defer server.Stop()
+	epName := server.Status().Endpoints[0].Name()
 	if name != "" {
 		vlog.Infof("Binary repository serving at %q (%q)", name, epName)
 	} else {
diff --git a/services/build/build/impl_test.go b/services/build/build/impl_test.go
index 64c1479..f3bc90a 100644
--- a/services/build/build/impl_test.go
+++ b/services/build/build/impl_test.go
@@ -9,7 +9,6 @@
 	"strings"
 	"testing"
 
-	"v.io/v23"
 	"v.io/v23/context"
 	"v.io/v23/naming"
 	"v.io/v23/rpc"
@@ -19,6 +18,7 @@
 	"v.io/x/lib/cmdline"
 	"v.io/x/lib/vlog"
 	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/lib/xrpc"
 	_ "v.io/x/ref/runtime/factories/generic"
 	"v.io/x/ref/test"
 )
@@ -46,35 +46,20 @@
 
 type dispatcher struct{}
 
-func startServer(ctx *context.T, t *testing.T) (rpc.Server, naming.Endpoint) {
-	server, err := v23.NewServer(ctx)
+func startServer(ctx *context.T, t *testing.T) naming.Endpoint {
+	unpublished := ""
+	server, err := xrpc.NewServer(ctx, unpublished, build.BuilderServer(&mock{}), nil)
 	if err != nil {
 		t.Fatalf("NewServer failed: %v", err)
 	}
-	l := v23.GetListenSpec(ctx)
-	endpoints, err := server.Listen(l)
-	if err != nil {
-		t.Fatalf("Listen(%s) failed: %v", l, err)
-	}
-	unpublished := ""
-	if err := server.Serve(unpublished, build.BuilderServer(&mock{}), nil); err != nil {
-		t.Fatalf("Serve(%v) failed: %v", unpublished, err)
-	}
-	return server, endpoints[0]
-}
-
-func stopServer(t *testing.T, server rpc.Server) {
-	if err := server.Stop(); err != nil {
-		t.Errorf("Stop() failed: %v", err)
-	}
+	return server.Status().Endpoints[0]
 }
 
 func TestBuildClient(t *testing.T) {
 	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
-	server, endpoint := startServer(ctx, t)
-	defer stopServer(t, server)
+	endpoint := startServer(ctx, t)
 
 	var stdout, stderr bytes.Buffer
 	env := &cmdline.Env{Stdout: &stdout, Stderr: &stderr}
diff --git a/services/build/buildd/impl_test.go b/services/build/buildd/impl_test.go
index 6956828..147b165 100644
--- a/services/build/buildd/impl_test.go
+++ b/services/build/buildd/impl_test.go
@@ -12,10 +12,10 @@
 	"strings"
 	"testing"
 
-	"v.io/v23"
 	"v.io/v23/context"
 	"v.io/v23/services/build"
 
+	"v.io/x/ref/lib/xrpc"
 	"v.io/x/ref/test"
 )
 
@@ -66,21 +66,13 @@
 // startServer starts the build server.
 func startServer(t *testing.T, ctx *context.T) build.BuilderClientMethods {
 	gobin, goroot := findGoBinary(t, "go")
-	server, err := v23.NewServer(ctx)
+	service := build.BuilderServer(NewBuilderService(gobin, goroot))
+	unpublished := ""
+	server, err := xrpc.NewServer(ctx, unpublished, service, nil)
 	if err != nil {
 		t.Fatalf("NewServer() failed: %v", err)
 	}
-	l := v23.GetListenSpec(ctx)
-	endpoints, err := server.Listen(l)
-	if err != nil {
-		t.Fatalf("Listen(%s) failed: %v", l, err)
-	}
-	unpublished := ""
-	if err := server.Serve(unpublished, build.BuilderServer(NewBuilderService(gobin, goroot)), nil); err != nil {
-		t.Fatalf("Serve(%q) failed: %v", unpublished, err)
-	}
-	name := "/" + endpoints[0].String()
-	return build.BuilderClient(name)
+	return build.BuilderClient(server.Status().Endpoints[0].Name())
 }
 
 func invokeBuild(t *testing.T, ctx *context.T, client build.BuilderClientMethods, files []build.File) ([]byte, []build.File, error) {
diff --git a/services/build/buildd/main.go b/services/build/buildd/main.go
index ed25222..56626d6 100644
--- a/services/build/buildd/main.go
+++ b/services/build/buildd/main.go
@@ -11,7 +11,6 @@
 	"fmt"
 	"os"
 
-	"v.io/v23"
 	"v.io/v23/context"
 	"v.io/v23/services/build"
 	"v.io/x/lib/cmdline"
@@ -19,6 +18,7 @@
 	"v.io/x/ref/lib/security/securityflag"
 	"v.io/x/ref/lib/signals"
 	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/lib/xrpc"
 	_ "v.io/x/ref/runtime/factories/roaming"
 )
 
@@ -45,19 +45,11 @@
 }
 
 func runBuildD(ctx *context.T, env *cmdline.Env, args []string) error {
-	server, err := v23.NewServer(ctx)
+	server, err := xrpc.NewServer(ctx, name, build.BuilderServer(NewBuilderService(gobin, goroot)), securityflag.NewAuthorizerOrDie())
 	if err != nil {
 		return fmt.Errorf("NewServer() failed: %v", err)
 	}
-	ls := v23.GetListenSpec(ctx)
-	endpoint, err := server.Listen(ls)
-	if err != nil {
-		return fmt.Errorf("Listen(%s) failed: %v", ls, err)
-	}
-	if err := server.Serve(name, build.BuilderServer(NewBuilderService(gobin, goroot)), securityflag.NewAuthorizerOrDie()); err != nil {
-		return fmt.Errorf("Serve(%v) failed: %v", name, err)
-	}
-	vlog.Infof("Build server running at endpoint=%q", endpoint)
+	vlog.Infof("Build server running at endpoint=%q", server.Status().Endpoints[0].Name())
 
 	// Wait until shutdown.
 	<-signals.ShutdownOnSignals(ctx)
diff --git a/services/debug/debuglib/dispatcher_test.go b/services/debug/debuglib/dispatcher_test.go
index 5bcd18c..441f20a 100644
--- a/services/debug/debuglib/dispatcher_test.go
+++ b/services/debug/debuglib/dispatcher_test.go
@@ -5,7 +5,6 @@
 package debuglib
 
 import (
-	"fmt"
 	"io"
 	"io/ioutil"
 	"os"
@@ -19,7 +18,6 @@
 	"v.io/v23"
 	"v.io/v23/context"
 	"v.io/v23/naming"
-	"v.io/v23/rpc"
 	"v.io/v23/services/logreader"
 	"v.io/v23/services/stats"
 	s_vtrace "v.io/v23/services/vtrace"
@@ -28,6 +26,7 @@
 	"v.io/v23/vtrace"
 
 	libstats "v.io/x/ref/lib/stats"
+	"v.io/x/ref/lib/xrpc"
 	_ "v.io/x/ref/runtime/factories/generic"
 	"v.io/x/ref/test"
 	"v.io/x/ref/test/testutil"
@@ -35,27 +34,6 @@
 
 //go:generate v23 test generate
 
-// startDebugServer starts a debug server.
-func startDebugServer(ctx *context.T, listenSpec rpc.ListenSpec, logsDir string) (string, func(), error) {
-	if len(logsDir) == 0 {
-		return "", nil, fmt.Errorf("logs directory missing")
-	}
-	disp := NewDispatcher(func() string { return logsDir }, nil)
-	server, err := v23.NewServer(ctx)
-	if err != nil {
-		return "", nil, fmt.Errorf("failed to start debug server: %v", err)
-	}
-	endpoints, err := server.Listen(listenSpec)
-	if err != nil {
-		return "", nil, fmt.Errorf("failed to listen on %s: %v", listenSpec, err)
-	}
-	if err := server.ServeDispatcher("", disp); err != nil {
-		return "", nil, err
-	}
-	ep := endpoints[0].String()
-	return ep, func() { server.Stop() }, nil
-}
-
 func TestDebugServer(t *testing.T) {
 	ctx, shutdown := test.V23Init()
 	defer shutdown()
@@ -76,11 +54,12 @@
 		t.Fatalf("ioutil.WriteFile failed: %v", err)
 	}
 
-	endpoint, stop, err := startDebugServer(ctx, v23.GetListenSpec(ctx), workdir)
+	disp := NewDispatcher(func() string { return workdir }, nil)
+	server, err := xrpc.NewDispatchingServer(ctx, "", disp)
 	if err != nil {
-		t.Fatalf("StartDebugServer failed: %v", err)
+		t.Fatalf("failed to start debug server: %v", err)
 	}
-	defer stop()
+	endpoint := server.Status().Endpoints[0].String()
 
 	// Access a logs directory that exists.
 	{
diff --git a/services/device/device/acl_test.go b/services/device/device/acl_test.go
index 4ad61bc..5a401c9 100644
--- a/services/device/device/acl_test.go
+++ b/services/device/device/acl_test.go
@@ -16,6 +16,7 @@
 	"v.io/v23/verror"
 	"v.io/x/lib/cmdline"
 	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/lib/xrpc"
 	"v.io/x/ref/test"
 
 	cmd_device "v.io/x/ref/services/device/device"
@@ -32,17 +33,16 @@
 	defer shutdown()
 
 	tapes := newTapeMap()
-	server, endpoint, err := startServer(t, ctx, tapes)
+	server, err := xrpc.NewDispatchingServer(ctx, "", newDispatcher(t, tapes))
 	if err != nil {
-		return
+		t.Fatalf("NewServer failed: %v", err)
 	}
-	defer stopServer(t, server)
 
 	// Setup the command-line.
 	cmd := cmd_device.CmdRoot
 	var stdout, stderr bytes.Buffer
 	env := &cmdline.Env{Stdout: &stdout, Stderr: &stderr}
-	deviceName := endpoint.Name()
+	deviceName := server.Status().Endpoints[0].Name()
 
 	// Test the 'get' command.
 	rootTape := tapes.forSuffix("")
@@ -80,17 +80,16 @@
 	defer shutdown()
 
 	tapes := newTapeMap()
-	server, endpoint, err := startServer(t, ctx, tapes)
+	server, err := xrpc.NewDispatchingServer(ctx, "", newDispatcher(t, tapes))
 	if err != nil {
-		return
+		t.Fatalf("NewServer failed: %v", err)
 	}
-	defer stopServer(t, server)
 
 	// Setup the command-line.
 	cmd := cmd_device.CmdRoot
 	var stdout, stderr bytes.Buffer
 	env := &cmdline.Env{Stdout: &stdout, Stderr: &stderr}
-	deviceName := endpoint.Name()
+	deviceName := server.Status().Endpoints[0].Name()
 
 	// Some tests to validate parse.
 	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"acl", "set", deviceName}); err == nil {
diff --git a/services/device/device/associate_test.go b/services/device/device/associate_test.go
index 20d01d5..78a5140 100644
--- a/services/device/device/associate_test.go
+++ b/services/device/device/associate_test.go
@@ -10,10 +10,10 @@
 	"strings"
 	"testing"
 
-	"v.io/v23/naming"
 	"v.io/v23/services/device"
 	"v.io/x/lib/cmdline"
 	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/lib/xrpc"
 	"v.io/x/ref/test"
 
 	cmd_device "v.io/x/ref/services/device/device"
@@ -24,17 +24,16 @@
 	defer shutdown()
 
 	tapes := newTapeMap()
-	server, endpoint, err := startServer(t, ctx, tapes)
+	server, err := xrpc.NewDispatchingServer(ctx, "", newDispatcher(t, tapes))
 	if err != nil {
-		return
+		t.Fatalf("NewServer failed: %v", err)
 	}
-	defer stopServer(t, server)
 
 	// Setup the command-line.
 	cmd := cmd_device.CmdRoot
 	var stdout, stderr bytes.Buffer
 	env := &cmdline.Env{Stdout: &stdout, Stderr: &stderr}
-	deviceName := naming.JoinAddressName(endpoint.String(), "")
+	deviceName := server.Status().Endpoints[0].Name()
 
 	rootTape := tapes.forSuffix("")
 	// Test the 'list' command.
@@ -78,17 +77,16 @@
 	defer shutdown()
 
 	tapes := newTapeMap()
-	server, endpoint, err := startServer(t, ctx, tapes)
+	server, err := xrpc.NewDispatchingServer(ctx, "", newDispatcher(t, tapes))
 	if err != nil {
-		return
+		t.Fatalf("NewServer failed: %v", err)
 	}
-	defer stopServer(t, server)
 
 	// Setup the command-line.
 	cmd := cmd_device.CmdRoot
 	var stdout, stderr bytes.Buffer
 	env := &cmdline.Env{Stdout: &stdout, Stderr: &stderr}
-	deviceName := naming.JoinAddressName(endpoint.String(), "")
+	deviceName := server.Status().Endpoints[0].Name()
 
 	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"add", "one"}); err == nil {
 		t.Fatalf("wrongly failed to receive a non-nil error.")
@@ -130,17 +128,16 @@
 	defer shutdown()
 
 	tapes := newTapeMap()
-	server, endpoint, err := startServer(t, ctx, tapes)
+	server, err := xrpc.NewDispatchingServer(ctx, "", newDispatcher(t, tapes))
 	if err != nil {
-		return
+		t.Fatalf("NewServer failed: %v", err)
 	}
-	defer stopServer(t, server)
 
 	// Setup the command-line.
 	cmd := cmd_device.CmdRoot
 	var stdout, stderr bytes.Buffer
 	env := &cmdline.Env{Stdout: &stdout, Stderr: &stderr}
-	deviceName := naming.JoinAddressName(endpoint.String(), "")
+	deviceName := server.Status().Endpoints[0].Name()
 
 	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"remove", "one"}); err == nil {
 		t.Fatalf("wrongly failed to receive a non-nil error.")
diff --git a/services/device/device/claim_test.go b/services/device/device/claim_test.go
index f62baea..e5df2cb 100644
--- a/services/device/device/claim_test.go
+++ b/services/device/device/claim_test.go
@@ -12,11 +12,11 @@
 	"testing"
 
 	"v.io/v23"
-	"v.io/v23/naming"
 	"v.io/v23/verror"
 	"v.io/x/lib/cmdline"
 	"v.io/x/ref/lib/security"
 	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/lib/xrpc"
 	"v.io/x/ref/test"
 
 	cmd_device "v.io/x/ref/services/device/device"
@@ -27,17 +27,16 @@
 	defer shutdown()
 
 	tapes := newTapeMap()
-	server, endpoint, err := startServer(t, ctx, tapes)
+	server, err := xrpc.NewDispatchingServer(ctx, "", newDispatcher(t, tapes))
 	if err != nil {
-		return
+		t.Fatalf("NewServer failed: %v", err)
 	}
-	defer stopServer(t, server)
 
 	// Setup the command-line.
 	cmd := cmd_device.CmdRoot
 	var stdout, stderr bytes.Buffer
 	env := &cmdline.Env{Stdout: &stdout, Stderr: &stderr}
-	deviceName := naming.JoinAddressName(endpoint.String(), "")
+	deviceName := server.Status().Endpoints[0].Name()
 	deviceKey, err := v23.GetPrincipal(ctx).PublicKey().MarshalBinary()
 	if err != nil {
 		t.Fatalf("Failed to marshal principal public key: %v", err)
diff --git a/services/device/device/debug_test.go b/services/device/device/debug_test.go
index 86748fe..11295a9 100644
--- a/services/device/device/debug_test.go
+++ b/services/device/device/debug_test.go
@@ -14,6 +14,7 @@
 	"v.io/v23/naming"
 	"v.io/x/lib/cmdline"
 	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/lib/xrpc"
 	"v.io/x/ref/test"
 
 	cmd_device "v.io/x/ref/services/device/device"
@@ -23,15 +24,15 @@
 	ctx, shutdown := test.V23Init()
 	defer shutdown()
 	tapes := newTapeMap()
-	server, endpoint, err := startServer(t, ctx, tapes)
+	server, err := xrpc.NewDispatchingServer(ctx, "", newDispatcher(t, tapes))
 	if err != nil {
-		return
+		t.Fatalf("NewServer failed: %v", err)
 	}
-	defer stopServer(t, server)
 
 	cmd := cmd_device.CmdRoot
-	globName := naming.JoinAddressName(endpoint.String(), "glob")
-	appName := naming.JoinAddressName(endpoint.String(), "app")
+	addr := server.Status().Endpoints[0].String()
+	globName := naming.JoinAddressName(addr, "glob")
+	appName := naming.JoinAddressName(addr, "app")
 	rootTape, appTape := tapes.forSuffix(""), tapes.forSuffix("app")
 	rootTape.SetResponses(GlobResponse{results: []string{"app"}})
 
diff --git a/services/device/device/devicemanager_mock_test.go b/services/device/device/devicemanager_mock_test.go
index 5e2c333..a726392 100644
--- a/services/device/device/devicemanager_mock_test.go
+++ b/services/device/device/devicemanager_mock_test.go
@@ -13,7 +13,6 @@
 	"testing"
 	"time"
 
-	"v.io/v23"
 	"v.io/v23/context"
 	"v.io/v23/naming"
 	"v.io/v23/rpc"
@@ -315,30 +314,3 @@
 func (d *dispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) {
 	return &mockDeviceInvoker{tape: d.tapes.forSuffix(suffix), t: d.t}, nil, nil
 }
-
-func startServer(t *testing.T, ctx *context.T, tapes *tapeMap) (rpc.Server, naming.Endpoint, error) {
-	dispatcher := newDispatcher(t, tapes)
-	server, err := v23.NewServer(ctx)
-	if err != nil {
-		t.Errorf("NewServer failed: %v", err)
-		return nil, nil, err
-	}
-	endpoints, err := server.Listen(v23.GetListenSpec(ctx))
-	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, endpoints[0], nil
-}
-
-func stopServer(t *testing.T, server rpc.Server) {
-	if err := server.Stop(); err != nil {
-		t.Errorf("server.Stop failed: %v", err)
-	}
-}
diff --git a/services/device/device/glob_test.go b/services/device/device/glob_test.go
index 505db06..ad8db50 100644
--- a/services/device/device/glob_test.go
+++ b/services/device/device/glob_test.go
@@ -20,6 +20,7 @@
 	"v.io/v23/services/device"
 
 	"v.io/x/lib/cmdline"
+	"v.io/x/ref/lib/xrpc"
 	"v.io/x/ref/test"
 
 	cmd_device "v.io/x/ref/services/device/device"
@@ -148,12 +149,12 @@
 	defer shutdown()
 	tapes := newTapeMap()
 	rootTape := tapes.forSuffix("")
-	server, endpoint, err := startServer(t, ctx, tapes)
+	server, err := xrpc.NewDispatchingServer(ctx, "", newDispatcher(t, tapes))
 	if err != nil {
-		return
+		t.Fatalf("NewServer failed: %v", err)
 	}
+	endpoint := server.Status().Endpoints[0]
 	appName := naming.JoinAddressName(endpoint.String(), "app")
-	defer stopServer(t, server)
 
 	allGlobArgs := []string{"glob1", "glob2"}
 	allGlobResponses := []GlobResponse{
diff --git a/services/device/device/install_test.go b/services/device/device/install_test.go
index 216916d..dd95099 100644
--- a/services/device/device/install_test.go
+++ b/services/device/device/install_test.go
@@ -17,6 +17,7 @@
 	"v.io/v23/services/device"
 	"v.io/x/lib/cmdline"
 	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/lib/xrpc"
 	"v.io/x/ref/test"
 
 	cmd_device "v.io/x/ref/services/device/device"
@@ -27,17 +28,16 @@
 	defer shutdown()
 
 	tapes := newTapeMap()
-	server, endpoint, err := startServer(t, ctx, tapes)
+	server, err := xrpc.NewDispatchingServer(ctx, "", newDispatcher(t, tapes))
 	if err != nil {
-		return
+		t.Fatalf("NewServer failed: %v", err)
 	}
-	defer stopServer(t, server)
 
 	// Setup the command-line.
 	cmd := cmd_device.CmdRoot
 	var stdout, stderr bytes.Buffer
 	env := &cmdline.Env{Stdout: &stdout, Stderr: &stderr}
-	deviceName := naming.JoinAddressName(endpoint.String(), "")
+	deviceName := server.Status().Endpoints[0].Name()
 	appId := "myBestAppID"
 	cfg := device.Config{"someflag": "somevalue"}
 	pkg := application.Packages{"pkg": application.SignedFile{File: "somename"}}
diff --git a/services/device/device/instantiate_test.go b/services/device/device/instantiate_test.go
index edfa861..db6d97d 100644
--- a/services/device/device/instantiate_test.go
+++ b/services/device/device/instantiate_test.go
@@ -11,10 +11,10 @@
 	"strings"
 	"testing"
 
-	"v.io/v23/naming"
 	"v.io/v23/verror"
 	"v.io/x/lib/cmdline"
 	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/lib/xrpc"
 	"v.io/x/ref/test"
 
 	cmd_device "v.io/x/ref/services/device/device"
@@ -25,17 +25,16 @@
 	defer shutdown()
 
 	tapes := newTapeMap()
-	server, endpoint, err := startServer(t, ctx, tapes)
+	server, err := xrpc.NewDispatchingServer(ctx, "", newDispatcher(t, tapes))
 	if err != nil {
-		return
+		t.Fatalf("NewServer failed: %v", err)
 	}
-	defer stopServer(t, server)
 
 	// Setup the command-line.
 	cmd := cmd_device.CmdRoot
 	var stdout, stderr bytes.Buffer
 	env := &cmdline.Env{Stdout: &stdout, Stderr: &stderr}
-	appName := naming.JoinAddressName(endpoint.String(), "")
+	appName := server.Status().Endpoints[0].Name()
 
 	// Confirm that we correctly enforce the number of arguments.
 	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"instantiate", "nope"}); err == nil {
diff --git a/services/device/device/kill_test.go b/services/device/device/kill_test.go
index adf7662..f5fd65d 100644
--- a/services/device/device/kill_test.go
+++ b/services/device/device/kill_test.go
@@ -15,6 +15,7 @@
 	"v.io/v23/verror"
 	"v.io/x/lib/cmdline"
 	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/lib/xrpc"
 	"v.io/x/ref/test"
 
 	cmd_device "v.io/x/ref/services/device/device"
@@ -25,17 +26,16 @@
 	defer shutdown()
 
 	tapes := newTapeMap()
-	server, endpoint, err := startServer(t, ctx, tapes)
+	server, err := xrpc.NewDispatchingServer(ctx, "", newDispatcher(t, tapes))
 	if err != nil {
-		return
+		t.Fatalf("NewServer failed: %v", err)
 	}
-	defer stopServer(t, server)
 
 	// Setup the command-line.
 	cmd := cmd_device.CmdRoot
 	var stdout, stderr bytes.Buffer
 	env := &cmdline.Env{Stdout: &stdout, Stderr: &stderr}
-	appName := naming.JoinAddressName(endpoint.String(), "appname")
+	appName := naming.JoinAddressName(server.Status().Endpoints[0].String(), "appname")
 
 	// Confirm that we correctly enforce the number of arguments.
 	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{"kill"}); err == nil {
diff --git a/services/device/device/local_install.go b/services/device/device/local_install.go
index c2cf446..e0bde29 100644
--- a/services/device/device/local_install.go
+++ b/services/device/device/local_install.go
@@ -29,6 +29,7 @@
 
 	"v.io/x/lib/cmdline"
 	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/lib/xrpc"
 	"v.io/x/ref/services/internal/packages"
 )
 
@@ -78,13 +79,10 @@
 }
 
 func createServer(ctx *context.T, stderr io.Writer) (*mapServer, func(), error) {
-	server, err := v23.NewServer(ctx)
-	if err != nil {
-		return nil, nil, err
-	}
-	spec := v23.GetListenSpec(ctx)
+	dispatcher := make(mapDispatcher)
+
 	var name string
-	if spec.Proxy != "" {
+	if spec := v23.GetListenSpec(ctx); spec.Proxy != "" {
 		id, err := uniqueid.Random()
 		if err != nil {
 			return nil, nil, err
@@ -94,15 +92,13 @@
 		// local endpoints to the mount table.  The only thing published
 		// should be the proxied endpoint.
 		spec.Addrs = nil
+		ctx = v23.WithListenSpec(ctx, spec)
 	}
-	endpoints, err := server.Listen(spec)
+	server, err := xrpc.NewDispatchingServer(ctx, name, dispatcher)
 	if err != nil {
 		return nil, nil, err
 	}
-	dispatcher := make(mapDispatcher)
-	if err := server.ServeDispatcher(name, dispatcher); err != nil {
-		return nil, nil, err
-	}
+	endpoints := server.Status().Endpoints
 	vlog.VI(1).Infof("Server listening on %v (%v)", endpoints, name)
 	cleanup := func() {
 		if err := server.Stop(); err != nil {
diff --git a/services/device/device/local_install_test.go b/services/device/device/local_install_test.go
index f3e938c..b9996ca 100644
--- a/services/device/device/local_install_test.go
+++ b/services/device/device/local_install_test.go
@@ -21,6 +21,7 @@
 	"v.io/v23/services/device"
 	"v.io/x/lib/cmdline"
 	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/lib/xrpc"
 	"v.io/x/ref/test"
 
 	cmd_device "v.io/x/ref/services/device/device"
@@ -37,16 +38,15 @@
 	defer shutdown()
 
 	tapes := newTapeMap()
-	server, endpoint, err := startServer(t, ctx, tapes)
+	server, err := xrpc.NewDispatchingServer(ctx, "", newDispatcher(t, tapes))
 	if err != nil {
-		return
+		t.Fatalf("NewServer failed: %v", err)
 	}
-	defer stopServer(t, server)
 	// Setup the command-line.
 	cmd := cmd_device.CmdRoot
 	var stdout, stderr bytes.Buffer
 	env := &cmdline.Env{Stdout: &stdout, Stderr: &stderr}
-	deviceName := naming.JoinAddressName(endpoint.String(), "")
+	deviceName := server.Status().Endpoints[0].Name()
 	const appTitle = "Appo di tutti Appi"
 	binary := os.Args[0]
 	fi, err := os.Stat(binary)
diff --git a/services/device/device/ls_test.go b/services/device/device/ls_test.go
index a2a4090..d85b6d3 100644
--- a/services/device/device/ls_test.go
+++ b/services/device/device/ls_test.go
@@ -12,6 +12,7 @@
 	"v.io/v23/naming"
 	"v.io/x/lib/cmdline"
 	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/lib/xrpc"
 	"v.io/x/ref/test"
 
 	cmd_device "v.io/x/ref/services/device/device"
@@ -24,13 +25,12 @@
 	ctx, shutdown := test.V23Init()
 	defer shutdown()
 	tapes := newTapeMap()
-	server, endpoint, err := startServer(t, ctx, tapes)
+	server, err := xrpc.NewDispatchingServer(ctx, "", newDispatcher(t, tapes))
 	if err != nil {
-		return
+		t.Fatalf("NewServer failed: %v", err)
 	}
-	defer stopServer(t, server)
-
 	cmd := cmd_device.CmdRoot
+	endpoint := server.Status().Endpoints[0]
 	appName := naming.JoinAddressName(endpoint.String(), "app")
 	rootTape := tapes.forSuffix("")
 	cannedGlobResponses := [][]string{
diff --git a/services/device/device/status_test.go b/services/device/device/status_test.go
index 1d4c2e6..6ea4bf5 100644
--- a/services/device/device/status_test.go
+++ b/services/device/device/status_test.go
@@ -15,6 +15,7 @@
 	"v.io/v23/services/device"
 	"v.io/x/lib/cmdline"
 	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/lib/xrpc"
 	"v.io/x/ref/test"
 
 	cmd_device "v.io/x/ref/services/device/device"
@@ -24,15 +25,15 @@
 	ctx, shutdown := test.V23Init()
 	defer shutdown()
 	tapes := newTapeMap()
-	server, endpoint, err := startServer(t, ctx, tapes)
+	server, err := xrpc.NewDispatchingServer(ctx, "", newDispatcher(t, tapes))
 	if err != nil {
-		return
+		t.Fatalf("NewServer failed: %v", err)
 	}
-	defer stopServer(t, server)
 
 	cmd := cmd_device.CmdRoot
-	globName := naming.JoinAddressName(endpoint.String(), "glob")
-	appName := naming.JoinAddressName(endpoint.String(), "app")
+	addr := server.Status().Endpoints[0].String()
+	globName := naming.JoinAddressName(addr, "glob")
+	appName := naming.JoinAddressName(addr, "app")
 
 	rootTape, appTape := tapes.forSuffix(""), tapes.forSuffix("app")
 	for _, c := range []struct {
diff --git a/services/device/device/update_test.go b/services/device/device/update_test.go
index b753343..f0f2720 100644
--- a/services/device/device/update_test.go
+++ b/services/device/device/update_test.go
@@ -19,6 +19,7 @@
 	"v.io/v23/naming"
 	"v.io/x/lib/cmdline"
 	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/lib/xrpc"
 	"v.io/x/ref/test"
 
 	cmd_device "v.io/x/ref/services/device/device"
@@ -37,16 +38,15 @@
 	ctx, shutdown := test.V23Init()
 	defer shutdown()
 	tapes := newTapeMap()
-	server, endpoint, err := startServer(t, ctx, tapes)
+	server, err := xrpc.NewDispatchingServer(ctx, "", newDispatcher(t, tapes))
 	if err != nil {
-		return
+		t.Fatalf("NewServer failed: %v", err)
 	}
-	defer stopServer(t, server)
-
+	addr := server.Status().Endpoints[0].String()
 	root := cmd_device.CmdRoot
-	appName := naming.JoinAddressName(endpoint.String(), "app")
+	appName := naming.JoinAddressName(addr, "app")
 	rootTape := tapes.forSuffix("")
-	globName := naming.JoinAddressName(endpoint.String(), "glob")
+	globName := naming.JoinAddressName(addr, "glob")
 	// TODO(caprita): Move joinLines to a common place.
 	joinLines := func(args ...string) string {
 		return strings.Join(args, "\n")
diff --git a/services/device/device/util_test.go b/services/device/device/util_test.go
index c1e67c6..7ee71bb 100644
--- a/services/device/device/util_test.go
+++ b/services/device/device/util_test.go
@@ -15,6 +15,7 @@
 	"v.io/v23/verror"
 	"v.io/x/lib/cmdline"
 	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/lib/xrpc"
 	"v.io/x/ref/test"
 
 	cmd_device "v.io/x/ref/services/device/device"
@@ -58,17 +59,17 @@
 	defer shutdown()
 
 	tapes := newTapeMap()
-	server, endpoint, err := startServer(t, ctx, tapes)
+	server, err := xrpc.NewDispatchingServer(ctx, "", newDispatcher(t, tapes))
 	if err != nil {
-		return
+		t.Fatalf("NewServer failed: %v", err)
 	}
-	defer stopServer(t, server)
+	addr := server.Status().Endpoints[0].String()
 
 	// Setup the command-line.
 	cmd := cmd_device.CmdRoot
 	var stdout, stderr bytes.Buffer
 	env := &cmdline.Env{Stdout: &stdout, Stderr: &stderr}
-	appName := naming.JoinAddressName(endpoint.String(), "appname")
+	appName := naming.JoinAddressName(addr, "appname")
 
 	// Confirm that we correctly enforce the number of arguments.
 	if err := v23cmd.ParseAndRunForTest(cmd, ctx, env, []string{lower}); err == nil {
diff --git a/services/device/internal/impl/applife/app_life_test.go b/services/device/internal/impl/applife/app_life_test.go
index 3eb8b3d..13fc965 100644
--- a/services/device/internal/impl/applife/app_life_test.go
+++ b/services/device/internal/impl/applife/app_life_test.go
@@ -358,13 +358,35 @@
 		t.Fatalf("Pid of hanging app (%d) has not exited after Stop() call", hangingPid)
 	}
 
-	shouldKeepInstances := determineShouldKeep(t, root, filepath.Join(root, "app*", "installation*", "instances", "instance*"), "Deleted")
-	shouldKeepInstallations := addBackLinks(t, root, determineShouldKeep(t, root, filepath.Join(root, "app*", "installation*"), "Uninstalled"))
+	// In the first pass, TidyNow (below), finds that everything should be too
+	// young to be tidied becasue TidyNow's first call to MockableNow()
+	// provides the current time.
+	shouldKeepInstances := keepAll(t, root, filepath.Join(root, "app*", "installation*", "instances", "instance*"))
+	shouldKeepInstallations := keepAll(t, root, filepath.Join(root, "app*", "installation*"))
+	shouldKeepLogFiles := keepAll(t, root, filepath.Join(root, "app*", "installation*", "instances", "instance*", "logs", "*"))
+
 	if err := utiltest.DeviceStub("dm").TidyNow(ctx); err != nil {
 		t.Fatalf("TidyNow failed: %v", err)
 	}
-	validateTidying(t, root, filepath.Join(root, "app*", "installation*", "instances", "instance*"), shouldKeepInstances)
-	validateTidying(t, root, filepath.Join(root, "app*", "installation*"), shouldKeepInstallations)
+
+	verifyTidying(t, root, filepath.Join(root, "app*", "installation*", "instances", "instance*"), shouldKeepInstances)
+	verifyTidying(t, root, filepath.Join(root, "app*", "installation*"), shouldKeepInstallations)
+	verifyTidying(t, root, filepath.Join(root, "app*", "installation*", "instances", "instance*", "logs", "*"), shouldKeepLogFiles)
+
+	// In the second pass, TidyNow() (below) calls MockableNow() again
+	// which has advanced to tomorrow so it should find that all items have
+	// become old enough to tidy.
+	shouldKeepInstances = determineShouldKeep(t, root, filepath.Join(root, "app*", "installation*", "instances", "instance*"), "Deleted")
+	shouldKeepInstallations = addBackLinks(t, root, determineShouldKeep(t, root, filepath.Join(root, "app*", "installation*"), "Uninstalled"))
+	shouldKeepLogFiles = determineLogFilesToKeep(t, shouldKeepInstances)
+
+	if err := utiltest.DeviceStub("dm").TidyNow(ctx); err != nil {
+		t.Fatalf("TidyNow failed: %v", err)
+	}
+
+	verifyTidying(t, root, filepath.Join(root, "app*", "installation*", "instances", "instance*"), shouldKeepInstances)
+	verifyTidying(t, root, filepath.Join(root, "app*", "installation*"), shouldKeepInstallations)
+	verifyTidying(t, root, filepath.Join(root, "app*", "installation*", "instances", "instance*", "logs", "*"), shouldKeepLogFiles)
 
 	// Cleanly shut down the device manager.
 	defer utiltest.VerifyNoRunningProcesses(t)
@@ -373,6 +395,18 @@
 	dmh.ExpectEOF()
 }
 
+func keepAll(t *testing.T, root, globpath string) map[string]bool {
+	paths, err := filepath.Glob(globpath)
+	if err != nil {
+		t.Errorf("keepAll %v", err)
+	}
+	shouldKeep := make(map[string]bool)
+	for _, idir := range paths {
+		shouldKeep[idir] = true
+	}
+	return shouldKeep
+}
+
 func determineShouldKeep(t *testing.T, root, globpath, state string) map[string]bool {
 	paths, err := filepath.Glob(globpath)
 	if err != nil {
@@ -415,10 +449,49 @@
 	return installationShouldKeep
 }
 
-func validateTidying(t *testing.T, root, globpath string, shouldKeep map[string]bool) {
+// determineLogFilesToKeep produces a map of the log files that
+// should remain after tidying. It returns a map to be compatible
+// with the verifyTidying.
+func determineLogFilesToKeep(t *testing.T, instances map[string]bool) map[string]bool {
+	shouldKeep := make(map[string]bool)
+	for idir, keep := range instances {
+		if !keep {
+			continue
+		}
+
+		paths, err := filepath.Glob(filepath.Join(idir, "logs", "*"))
+		if err != nil {
+			t.Errorf("determineLogFilesToKeep filepath.Glob(%s) failed: %v", idir, err)
+			return shouldKeep
+		}
+
+		for _, p := range paths {
+			fi, err := os.Stat(p)
+			if err != nil {
+				t.Errorf("determineLogFilesToKeep os.Stat(%s): %v", p, err)
+				return shouldKeep
+			}
+
+			if fi.Mode()&os.ModeSymlink == 0 {
+				continue
+			}
+
+			shouldKeep[p] = true
+			target, err := os.Readlink(p)
+			if err != nil {
+				t.Errorf("determineLogFilesToKeep os.Readlink(%s): %v", p, err)
+				return shouldKeep
+			}
+			shouldKeep[target] = true
+		}
+	}
+	return shouldKeep
+}
+
+func verifyTidying(t *testing.T, root, globpath string, shouldKeep map[string]bool) {
 	paths, err := filepath.Glob(globpath)
 	if err != nil {
-		t.Errorf("validateTidying %v", err)
+		t.Errorf("verifyTidying %v", err)
 	}
 
 	// TidyUp adds nothing: pth should be a subset of shouldKeep.
diff --git a/services/device/internal/impl/device_service.go b/services/device/internal/impl/device_service.go
index 97f4e51..68a4d76 100644
--- a/services/device/internal/impl/device_service.go
+++ b/services/device/internal/impl/device_service.go
@@ -110,6 +110,7 @@
 	disp           *dispatcher
 	uat            BlessingSystemAssociationStore
 	securityAgent  *securityAgentState
+	tidying        chan<- tidyRequests
 }
 
 // Version info for this device manager binary. Increment as appropriate when the binary changes.
@@ -677,15 +678,8 @@
 	}}, nil
 }
 
-// tidyHarness runs device manager cleanup operations
-func (s *deviceService) tidyHarness(ctx *context.T) error {
-	if err := pruneDeletedInstances(ctx, s.config.Root); err != nil {
-		return err
-	}
-
-	return pruneUninstalledInstallations(ctx, s.config.Root)
-}
-
-func (s *deviceService) TidyNow(ctx *context.T, call rpc.ServerCall) error {
-	return s.tidyHarness(ctx)
+func (s *deviceService) TidyNow(ctx *context.T, _ rpc.ServerCall) error {
+	ec := make(chan error)
+	s.tidying <- tidyRequests{ctx: ctx, bc: ec}
+	return <-ec
 }
diff --git a/services/device/internal/impl/dispatcher.go b/services/device/internal/impl/dispatcher.go
index 62d4787..16f81d0 100644
--- a/services/device/internal/impl/dispatcher.go
+++ b/services/device/internal/impl/dispatcher.go
@@ -43,6 +43,8 @@
 	testMode       bool
 	// reap is the app process monitoring subsystem.
 	reap *reaper
+	// tidying is the automatic state tidying subsystem.
+	tidying chan<- tidyRequests
 }
 
 // dispatcher holds the state of the device manager dispatcher.
@@ -125,6 +127,7 @@
 			updating:       newUpdatingState(),
 			restartHandler: restartHandler,
 			testMode:       testMode,
+			tidying:        newTidyingDaemon(config.Root),
 		},
 		config:     config,
 		uat:        uat,
@@ -278,6 +281,7 @@
 			disp:           d,
 			uat:            d.uat,
 			securityAgent:  d.internal.securityAgent,
+			tidying:        d.internal.tidying,
 		})
 		return receiver, auth, nil
 	case appsSuffix:
diff --git a/services/device/internal/impl/proxy_invoker_test.go b/services/device/internal/impl/proxy_invoker_test.go
index ad4a8aa..a3e414d 100644
--- a/services/device/internal/impl/proxy_invoker_test.go
+++ b/services/device/internal/impl/proxy_invoker_test.go
@@ -16,6 +16,7 @@
 	"v.io/v23/security/access"
 	"v.io/v23/services/stats"
 	"v.io/x/lib/vlog"
+	"v.io/x/ref/lib/xrpc"
 	"v.io/x/ref/test"
 	"v.io/x/ref/test/testutil"
 )
@@ -29,46 +30,31 @@
 	v23.GetNamespace(ctx).CacheCtl(naming.DisableCache(true))
 
 	// server1 is a normal server
-	server1, err := v23.NewServer(ctx)
+	server1, err := xrpc.NewServer(ctx, "", &dummy{}, nil)
 	if err != nil {
 		t.Fatalf("NewServer: %v", err)
 	}
-	localSpec := rpc.ListenSpec{Addrs: rpc.ListenAddrs{{"tcp", "127.0.0.1:0"}}}
-	eps1, err := server1.Listen(localSpec)
-	if err != nil {
-		t.Fatalf("Listen: %v", err)
-	}
-	if err := server1.Serve("", &dummy{}, nil); err != nil {
-		t.Fatalf("server1.Serve: %v", err)
-	}
 
 	// server2 proxies requests to <suffix> to server1/__debug/stats/<suffix>
-	server2, err := v23.NewServer(ctx)
+	disp := &proxyDispatcher{
+		naming.JoinAddressName(server1.Status().Endpoints[0].String(), "__debug/stats"),
+		stats.StatsServer(nil).Describe__(),
+	}
+	server2, err := xrpc.NewDispatchingServer(ctx, "", disp)
 	if err != nil {
 		t.Fatalf("NewServer: %v", err)
 	}
-	defer server2.Stop()
-	eps2, err := server2.Listen(localSpec)
-	if err != nil {
-		t.Fatalf("Listen: %v", err)
-	}
-	disp := &proxyDispatcher{
-		naming.JoinAddressName(eps1[0].String(), "__debug/stats"),
-		stats.StatsServer(nil).Describe__(),
-	}
-	if err := server2.ServeDispatcher("", disp); err != nil {
-		t.Fatalf("server2.Serve: %v", err)
-	}
+	addr2 := server2.Status().Endpoints[0].String()
 
 	// Call Value()
-	name := naming.JoinAddressName(eps2[0].String(), "system/start-time-rfc1123")
+	name := naming.JoinAddressName(addr2, "system/start-time-rfc1123")
 	c := stats.StatsClient(name)
 	if _, err := c.Value(ctx); err != nil {
 		t.Fatalf("%q.Value() error: %v", name, err)
 	}
 
 	// Call Glob()
-	results, _, err := testutil.GlobName(ctx, naming.JoinAddressName(eps2[0].String(), "system"), "start-time-*")
+	results, _, err := testutil.GlobName(ctx, naming.JoinAddressName(addr2, "system"), "start-time-*")
 	if err != nil {
 		t.Fatalf("Glob failed: %v", err)
 	}
diff --git a/services/device/internal/impl/tidyup.go b/services/device/internal/impl/tidyup.go
index 6b1ad1d..3b9c3ca 100644
--- a/services/device/internal/impl/tidyup.go
+++ b/services/device/internal/impl/tidyup.go
@@ -14,37 +14,45 @@
 	"v.io/v23/context"
 	"v.io/v23/services/device"
 	"v.io/v23/verror"
+
+	"v.io/x/lib/vlog"
 )
 
 // This file contains the various routines that the device manager uses
 // to tidy up its persisted but no longer necessary state.
 
-// TidyAge defaults to 1 day. Settable for tests.
-var TidyOlderThan = time.Hour * 24
+const aboutOneDay = time.Hour * 24
 
-func shouldDelete(idir, suffix string) (bool, error) {
+func oldEnoughToTidy(fi os.FileInfo, now time.Time) bool {
+	return fi.ModTime().Add(aboutOneDay).Before(now)
+}
+
+// AutomaticTidyingInterval defaults to 1 day.
+// Settable for tests.
+var AutomaticTidyingInterval = time.Hour * 24
+
+func shouldDelete(idir, suffix string, now time.Time) (bool, error) {
 	fi, err := os.Stat(filepath.Join(idir, suffix))
 	if err != nil {
 		return false, err
 	}
 
-	if fi.ModTime().Add(TidyOlderThan).Before(time.Now()) {
-		return true, nil
-	}
-
-	return false, nil
+	return oldEnoughToTidy(fi, now), nil
 }
 
+// Exposed for replacability in tests.
+var MockableNow = time.Now
+
 // shouldDeleteInstallation returns true if the tidying policy holds
 // for this installation.
-func shouldDeleteInstallation(idir string) (bool, error) {
-	return shouldDelete(idir, device.InstallationStateUninstalled.String())
+func shouldDeleteInstallation(idir string, now time.Time) (bool, error) {
+	return shouldDelete(idir, device.InstallationStateUninstalled.String(), now)
 }
 
 // shouldDeleteInstance returns true if the tidying policy holds
 // that the instance should be deleted.
-func shouldDeleteInstance(idir string) (bool, error) {
-	return shouldDelete(idir, device.InstanceStateDeleted.String())
+func shouldDeleteInstance(idir string, now time.Time) (bool, error) {
+	return shouldDelete(idir, device.InstanceStateDeleted.String(), now)
 }
 
 type pthError struct {
@@ -52,7 +60,7 @@
 	err error
 }
 
-func pruneDeletedInstances(ctx *context.T, root string) error {
+func pruneDeletedInstances(ctx *context.T, root string, now time.Time) error {
 	paths, err := filepath.Glob(filepath.Join(root, "app*", "installation*", "instances", "instance*"))
 	if err != nil {
 		return err
@@ -70,7 +78,7 @@
 			continue
 		}
 
-		shouldDelete, err := shouldDeleteInstance(pth)
+		shouldDelete, err := shouldDeleteInstance(pth, now)
 		if err != nil {
 			allerrors = append(allerrors, pthError{pth, err})
 			continue
@@ -96,7 +104,7 @@
 	return nil
 }
 
-func pruneUninstalledInstallations(ctx *context.T, root string) error {
+func pruneUninstalledInstallations(ctx *context.T, root string, now time.Time) error {
 	// Read all the Uninstalled installations into a map.
 	installationPaths, err := filepath.Glob(filepath.Join(root, "app*", "installation*"))
 	if err != nil {
@@ -142,7 +150,7 @@
 	// All remaining entries in pruneCandidates are not referenced by
 	// any instance.
 	for pth, _ := range pruneCandidates {
-		shouldDelete, err := shouldDeleteInstallation(pth)
+		shouldDelete, err := shouldDeleteInstallation(pth, now)
 		if err != nil {
 			allerrors = append(allerrors, pthError{pth, err})
 			continue
@@ -156,3 +164,95 @@
 	}
 	return processErrors(ctx, allerrors)
 }
+
+// pruneOldLogs removes logs more than a day old. Symlinks (the
+// cannonical log file name) the (newest) log files that they point to
+// are preserved.
+func pruneOldLogs(ctx *context.T, root string, now time.Time) error {
+	logPaths, err := filepath.Glob(filepath.Join(root, "app*", "installation*", "instances", "instance*", "logs", "*"))
+	if err != nil {
+		return err
+	}
+
+	pruneCandidates := make(map[string]struct{}, len(logPaths))
+	for _, p := range logPaths {
+		pruneCandidates[p] = struct{}{}
+	}
+
+	allerrors := make([]pthError, 0)
+	for p, _ := range pruneCandidates {
+		fi, err := os.Stat(p)
+		if err != nil {
+			allerrors = append(allerrors, pthError{p, err})
+			delete(pruneCandidates, p)
+			continue
+		}
+
+		if fi.Mode()&os.ModeSymlink != 0 {
+			delete(pruneCandidates, p)
+			target, err := os.Readlink(p)
+			if err != nil {
+				allerrors = append(allerrors, pthError{p, err})
+				continue
+			}
+			delete(pruneCandidates, target)
+			continue
+		}
+
+		if !oldEnoughToTidy(fi, now) {
+			delete(pruneCandidates, p)
+		}
+	}
+
+	for pth, _ := range pruneCandidates {
+		if err := suidHelper.deleteFileTree(pth, nil, nil); err != nil {
+			allerrors = append(allerrors, pthError{pth, err})
+		}
+	}
+	return processErrors(ctx, allerrors)
+}
+
+// tidyHarness runs device manager cleanup operations
+func tidyHarness(ctx *context.T, root string) error {
+	now := MockableNow()
+
+	if err := pruneDeletedInstances(ctx, root, now); err != nil {
+		return err
+	}
+
+	if err := pruneUninstalledInstallations(ctx, root, now); err != nil {
+		return err
+	}
+
+	return pruneOldLogs(ctx, root, now)
+}
+
+// tidyDaemon runs in a Go routine, processing requests to tidy
+// or tidying on a schedule.
+func tidyDaemon(c <-chan tidyRequests, root string) {
+	for {
+		select {
+		case req, ok := <-c:
+			if !ok {
+				return
+			}
+			req.bc <- tidyHarness(req.ctx, root)
+		case <-time.After(AutomaticTidyingInterval):
+			if err := tidyHarness(nil, root); err != nil {
+				vlog.Errorf("tidyDaemon failed to tidy: %v", err)
+			}
+		}
+
+	}
+}
+
+type tidyRequests struct {
+	ctx *context.T
+	bc  chan<- error
+}
+
+func newTidyingDaemon(root string) chan<- tidyRequests {
+	c := make(chan tidyRequests)
+	go tidyDaemon(c, root)
+	return c
+}
diff --git a/services/device/internal/impl/utiltest/app.go b/services/device/internal/impl/utiltest/app.go
index 7ef524b..1f959b8 100644
--- a/services/device/internal/impl/utiltest/app.go
+++ b/services/device/internal/impl/utiltest/app.go
@@ -23,8 +23,8 @@
 	"v.io/v23/security"
 	"v.io/x/lib/vlog"
 	"v.io/x/ref/lib/signals"
+	"v.io/x/ref/lib/xrpc"
 	"v.io/x/ref/services/device/internal/suid"
-	"v.io/x/ref/services/internal/servicetest"
 	"v.io/x/ref/test"
 	"v.io/x/ref/test/modules"
 	"v.io/x/ref/test/testutil"
@@ -125,10 +125,9 @@
 	}
 	publishName := args[0]
 
-	server, _ := servicetest.NewServer(ctx)
-	defer server.Stop()
-	if err := server.Serve(publishName, new(appService), nil); err != nil {
-		vlog.Fatalf("Serve(%v) failed: %v", publishName, err)
+	_, err := xrpc.NewServer(ctx, publishName, new(appService), nil)
+	if err != nil {
+		vlog.Fatalf("NewServer(%v) failed: %v", publishName, err)
 	}
 	// Some of our tests look for log files, so make sure they are flushed
 	// to ensure that at least the files exist.
@@ -158,10 +157,10 @@
 // returns a channel on which the app's ping message is returned, and a cleanup
 // function.
 func SetupPingServer(t *testing.T, ctx *context.T) (PingServer, func()) {
-	server, _ := servicetest.NewServer(ctx)
 	pingCh := make(chan PingArgs, 1)
-	if err := server.Serve("pingserver", PingServer{pingCh}, security.AllowEveryone()); err != nil {
-		t.Fatalf("Serve(%q, <dispatcher>) failed: %v", "pingserver", err)
+	server, err := xrpc.NewServer(ctx, "pingserver", PingServer{pingCh}, security.AllowEveryone())
+	if err != nil {
+		t.Fatalf("NewServer(%q, <dispatcher>) failed: %v", "pingserver", err)
 	}
 	return PingServer{pingCh}, func() {
 		if err := server.Stop(); err != nil {
diff --git a/services/device/internal/impl/utiltest/helpers.go b/services/device/internal/impl/utiltest/helpers.go
index 844c99e..0f1260d 100644
--- a/services/device/internal/impl/utiltest/helpers.go
+++ b/services/device/internal/impl/utiltest/helpers.go
@@ -34,6 +34,7 @@
 	"v.io/v23/services/stats"
 	"v.io/v23/verror"
 	"v.io/x/ref"
+	"v.io/x/ref/lib/xrpc"
 	_ "v.io/x/ref/runtime/factories/roaming"
 	"v.io/x/ref/services/device/internal/impl"
 	"v.io/x/ref/services/internal/binarylib"
@@ -69,8 +70,16 @@
 		}
 	}
 
-	// So that TidyUp runs eagerly in tests.
-	impl.TidyOlderThan = -time.Hour
+	// Return a sequence of times separated by 25 hours.
+	impl.MockableNow = func() time.Time {
+		now := time.Now()
+		impl.MockableNow = func() time.Time {
+			now = now.Add(time.Hour * 25)
+			return now
+		}
+		return now
+	}
+
 }
 
 func EnvelopeFromShell(sh *modules.Shell, env []string, prog modules.Program, title string, retries int, window time.Duration, args ...string) application.Envelope {
@@ -446,7 +455,7 @@
 func Status(t *testing.T, ctx *context.T, nameComponents ...string) device.Status {
 	s, err := AppStub(nameComponents...).Status(ctx)
 	if err != nil {
-		t.Fatalf(testutil.FormatLogLine(3, "Status(%v) failed: %v [%v]", nameComponents, verror.ErrorID(err), err))
+		t.Errorf(testutil.FormatLogLine(3, "Status(%v) failed: %v [%v]", nameComponents, verror.ErrorID(err), err))
 	}
 	return s
 }
@@ -465,10 +474,10 @@
 		state = s.Value.State
 		version = s.Value.Version
 	default:
-		t.Fatalf(testutil.FormatLogLine(2, "Status(%v) returned unknown type: %T", nameComponents, s))
+		t.Errorf(testutil.FormatLogLine(2, "Status(%v) returned unknown type: %T", nameComponents, s))
 	}
 	if state != want {
-		t.Fatalf(testutil.FormatLogLine(2, "Status(%v) state: wanted %v (%T), got %v (%T)", nameComponents, want, want, state, state))
+		t.Errorf(testutil.FormatLogLine(2, "Status(%v) state: wanted %v (%T), got %v (%T)", nameComponents, want, want, state, state))
 	}
 	return version
 }
@@ -484,7 +493,7 @@
 	sort.Sort(byIdentity(got))
 	sort.Sort(byIdentity(expected))
 	if !reflect.DeepEqual(got, expected) {
-		t.Fatalf("ListAssociations() got %v, expected %v", got, expected)
+		t.Errorf("ListAssociations() got %v, expected %v", got, expected)
 	}
 }
 
@@ -700,7 +709,7 @@
 		t.Errorf(testutil.FormatLogLine(2, "CmdLine(%q) failed: %v", name, err))
 	}
 	if len(v) == 0 {
-		t.Fatalf("Unexpected empty cmdline: %v", v)
+		t.Errorf("Unexpected empty cmdline: %v", v)
 	}
 	if got, want := filepath.Base(v[0]), appName; got != want {
 		t.Errorf(testutil.FormatLogLine(2, "Unexpected value for argv[0]. Got %v, want %v", got, want))
@@ -762,12 +771,12 @@
 	if err != nil {
 		t.Fatalf("binarylib.NewState failed: %v", err)
 	}
-	server, _ := servicetest.NewServer(ctx)
 	d, err := binarylib.NewDispatcher(v23.GetPrincipal(ctx), state)
 	if err != nil {
 		t.Fatalf("server.NewDispatcher failed: %v", err)
 	}
-	if err := server.ServeDispatcher(von, d); err != nil {
+	server, err := xrpc.NewDispatchingServer(ctx, von, d)
+	if err != nil {
 		t.Fatalf("server.ServeDispatcher failed: %v", err)
 	}
 	return func() {
@@ -797,6 +806,7 @@
 	for syscall.Kill(pid, 0) == nil {
 		select {
 		case <-timeOut:
+			syscall.Kill(pid, 9)
 			t.Fatalf("Timed out waiting for PID %v to terminate", pid)
 		case <-time.After(time.Millisecond):
 			// Try again.
diff --git a/services/device/internal/impl/utiltest/mock_repo.go b/services/device/internal/impl/utiltest/mock_repo.go
index 3d9aff6..ae908d0 100644
--- a/services/device/internal/impl/utiltest/mock_repo.go
+++ b/services/device/internal/impl/utiltest/mock_repo.go
@@ -23,8 +23,7 @@
 	"v.io/v23/services/repository"
 	"v.io/v23/verror"
 	"v.io/x/lib/vlog"
-
-	"v.io/x/ref/services/internal/servicetest"
+	"v.io/x/ref/lib/xrpc"
 )
 
 const MockBinaryRepoName = "br"
@@ -44,11 +43,11 @@
 // repository.  It returns a pointer to the envelope that the repository returns
 // to clients (so that it can be changed).  It also returns a cleanup function.
 func StartApplicationRepository(ctx *context.T) (*application.Envelope, func()) {
-	server, _ := servicetest.NewServer(ctx)
 	invoker := new(arInvoker)
 	name := MockApplicationRepoName
-	if err := server.Serve(name, repository.ApplicationServer(invoker), security.AllowEveryone()); err != nil {
-		vlog.Fatalf("Serve(%v) failed: %v", name, err)
+	server, err := xrpc.NewServer(ctx, name, repository.ApplicationServer(invoker), security.AllowEveryone())
+	if err != nil {
+		vlog.Fatalf("NewServer(%v) failed: %v", name, err)
 	}
 	return &invoker.envelope, func() {
 		if err := server.Stop(); err != nil {
@@ -88,9 +87,9 @@
 // StartBinaryRepository sets up a server running the binary repository and
 // returns a cleanup function.
 func StartBinaryRepository(ctx *context.T) func() {
-	server, _ := servicetest.NewServer(ctx)
 	name := MockBinaryRepoName
-	if err := server.Serve(name, repository.BinaryServer(new(brInvoker)), security.AllowEveryone()); err != nil {
+	server, err := xrpc.NewServer(ctx, name, repository.BinaryServer(new(brInvoker)), security.AllowEveryone())
+	if err != nil {
 		vlog.Fatalf("Serve(%q) failed: %v", name, err)
 	}
 	return func() {
diff --git a/services/device/internal/starter/starter.go b/services/device/internal/starter/starter.go
index ac47761..977567e 100644
--- a/services/device/internal/starter/starter.go
+++ b/services/device/internal/starter/starter.go
@@ -28,6 +28,7 @@
 	"v.io/v23/security"
 	"v.io/v23/verror"
 	"v.io/x/lib/vlog"
+	"v.io/x/ref/lib/xrpc"
 )
 
 const pkgPath = "v.io/x/ref/services/device/internal/starter"
@@ -152,7 +153,7 @@
 		cancel()
 		return "", nil, err
 	}
-	server, err := v23.NewServer(ctx)
+	server, err := xrpc.NewDispatchingServer(ctx, "", dispatcher)
 	if err != nil {
 		cancel()
 		return "", nil, err
@@ -163,15 +164,7 @@
 		vlog.Infof("Stopped claimable server.")
 		cancel()
 	}
-	endpoints, err := server.Listen(args.Device.ListenSpec)
-	if err != nil {
-		shutdown()
-		return "", nil, err
-	}
-	if err := server.ServeDispatcher("", dispatcher); err != nil {
-		shutdown()
-		return "", nil, err
-	}
+	endpoints := server.Status().Endpoints
 	publicKey, err := v23.GetPrincipal(ctx).PublicKey().MarshalBinary()
 	if err != nil {
 		shutdown()
diff --git a/services/groups/groupsd/main.go b/services/groups/groupsd/main.go
index 2d51c6d..795d776 100644
--- a/services/groups/groupsd/main.go
+++ b/services/groups/groupsd/main.go
@@ -18,6 +18,7 @@
 	"v.io/x/lib/vlog"
 	"v.io/x/ref/lib/security/securityflag"
 	"v.io/x/ref/lib/signals"
+	"v.io/x/ref/lib/xrpc"
 	_ "v.io/x/ref/runtime/factories/generic"
 	"v.io/x/ref/services/groups/internal/server"
 	"v.io/x/ref/services/groups/internal/store/memstore"
@@ -44,14 +45,6 @@
 	ctx, shutdown := v23.Init()
 	defer shutdown()
 
-	s, err := v23.NewServer(ctx)
-	if err != nil {
-		vlog.Fatal("v23.NewServer() failed: ", err)
-	}
-	if _, err := s.Listen(v23.GetListenSpec(ctx)); err != nil {
-		vlog.Fatal("s.Listen() failed: ", err)
-	}
-
 	perms, err := securityflag.PermissionsFromFlag()
 	if err != nil {
 		vlog.Fatal("securityflag.PermissionsFromFlag() failed: ", err)
@@ -65,10 +58,8 @@
 	}
 
 	m := server.NewManager(memstore.New(), perms)
-
-	// Publish the service in the mount table.
-	if err := s.ServeDispatcher(*name, m); err != nil {
-		vlog.Fatal("s.ServeDispatcher() failed: ", err)
+	if _, err = xrpc.NewDispatchingServer(ctx, *name, m); err != nil {
+		vlog.Fatal("NewDispatchingServer() failed: ", err)
 	}
 	vlog.Info("Mounted at: ", *name)
 
diff --git a/services/groups/internal/server/server_test.go b/services/groups/internal/server/server_test.go
index 20fa159..0ea807e 100644
--- a/services/groups/internal/server/server_test.go
+++ b/services/groups/internal/server/server_test.go
@@ -19,6 +19,7 @@
 	"v.io/v23/services/groups"
 	"v.io/v23/verror"
 	"v.io/x/lib/vlog"
+	"v.io/x/ref/lib/xrpc"
 	_ "v.io/x/ref/runtime/factories/generic"
 	"v.io/x/ref/services/groups/internal/server"
 	"v.io/x/ref/services/groups/internal/store"
@@ -93,19 +94,11 @@
 const useMemstore = false
 
 func newServer(ctx *context.T) (string, func()) {
-	s, err := v23.NewServer(ctx)
-	if err != nil {
-		vlog.Fatal("v23.NewServer() failed: ", err)
-	}
-	eps, err := s.Listen(v23.GetListenSpec(ctx))
-	if err != nil {
-		vlog.Fatal("s.Listen() failed: ", err)
-	}
-
 	// TODO(sadovsky): Pass in perms and test perms-checking in Group.Create().
 	perms := access.Permissions{}
 	var st store.Store
 	var file *os.File
+	var err error
 
 	if useMemstore {
 		st = memstore.New()
@@ -122,13 +115,14 @@
 
 	m := server.NewManager(st, perms)
 
-	if err := s.ServeDispatcher("", m); err != nil {
-		vlog.Fatal("s.ServeDispatcher() failed: ", err)
+	server, err := xrpc.NewDispatchingServer(ctx, "", m)
+	if err != nil {
+		vlog.Fatal("NewDispatchingServer() failed: ", err)
 	}
 
-	name := naming.JoinAddressName(eps[0].String(), "")
+	name := server.Status().Endpoints[0].Name()
 	return name, func() {
-		s.Stop()
+		server.Stop()
 		if file != nil {
 			os.Remove(file.Name())
 		}
diff --git a/services/identity/internal/revocation/revocation_test.go b/services/identity/internal/revocation/revocation_test.go
index 662af26..81ceb88 100644
--- a/services/identity/internal/revocation/revocation_test.go
+++ b/services/identity/internal/revocation/revocation_test.go
@@ -7,6 +7,7 @@
 import (
 	"testing"
 
+	"v.io/x/ref/lib/xrpc"
 	_ "v.io/x/ref/runtime/factories/generic"
 	"v.io/x/ref/services/discharger"
 	"v.io/x/ref/services/identity/internal/dischargerlib"
@@ -19,34 +20,21 @@
 
 //go:generate v23 test generate
 
-func revokerSetup(t *testing.T, ctx *context.T) (dischargerKey security.PublicKey, dischargerEndpoint string, revoker RevocationManager, closeFunc func()) {
-	revokerService := NewMockRevocationManager()
-	dischargerServer, err := v23.NewServer(ctx)
+func revokerSetup(t *testing.T, ctx *context.T) (dischargerKey security.PublicKey, dischargerEndpoint string, revoker RevocationManager) {
+	dischargerServiceStub := discharger.DischargerServer(dischargerlib.NewDischarger())
+	dischargerServer, err := xrpc.NewServer(ctx, "", dischargerServiceStub, nil)
 	if err != nil {
 		t.Fatalf("r.NewServer: %s", err)
 	}
-	dischargerEPs, err := dischargerServer.Listen(v23.GetListenSpec(ctx))
-	if err != nil {
-		t.Fatalf("dischargerServer.Listen failed: %v", err)
-	}
-	dischargerServiceStub := discharger.DischargerServer(dischargerlib.NewDischarger())
-	if err := dischargerServer.Serve("", dischargerServiceStub, nil); err != nil {
-		t.Fatalf("dischargerServer.Serve revoker: %s", err)
-	}
-	return v23.GetPrincipal(ctx).PublicKey(),
-		dischargerEPs[0].Name(),
-		revokerService,
-		func() {
-			dischargerServer.Stop()
-		}
+	name := dischargerServer.Status().Endpoints[0].Name()
+	return v23.GetPrincipal(ctx).PublicKey(), name, NewMockRevocationManager()
 }
 
 func TestDischargeRevokeDischargeRevokeDischarge(t *testing.T) {
 	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
-	dcKey, dc, revoker, closeFunc := revokerSetup(t, ctx)
-	defer closeFunc()
+	dcKey, dc, revoker := revokerSetup(t, ctx)
 
 	discharger := discharger.DischargerClient(dc)
 	caveat, err := revoker.NewCaveat(dcKey, dc)
diff --git a/services/internal/binarylib/client_test.go b/services/internal/binarylib/client_test.go
index 7cd2435..48e6820 100644
--- a/services/internal/binarylib/client_test.go
+++ b/services/internal/binarylib/client_test.go
@@ -19,6 +19,7 @@
 	"v.io/v23/services/repository"
 	"v.io/x/lib/vlog"
 
+	"v.io/x/ref/lib/xrpc"
 	"v.io/x/ref/test"
 	"v.io/x/ref/test/testutil"
 )
@@ -40,30 +41,20 @@
 		vlog.Fatalf("WriteFile(%v, %v, %v) failed: %v", path, Version, perm, err)
 	}
 	// Setup and start the binary repository server.
-	server, err := v23.NewServer(ctx)
-	if err != nil {
-		t.Fatalf("NewServer() failed: %v", err)
-	}
 	depth := 2
 	state, err := NewState(rootDir, "http://test-root-url", depth)
 	if err != nil {
 		t.Fatalf("NewState(%v, %v) failed: %v", rootDir, depth, err)
 	}
-
 	dispatcher, err := NewDispatcher(v23.GetPrincipal(ctx), state)
 	if err != nil {
 		t.Fatalf("NewDispatcher() failed: %v\n", err)
 	}
-	l := v23.GetListenSpec(ctx)
-	endpoints, err := server.Listen(l)
+	server, err := xrpc.NewDispatchingServer(ctx, "", dispatcher)
 	if err != nil {
-		t.Fatalf("Listen(%s) failed: %v", l, err)
+		t.Fatalf("NewServer() failed: %v", err)
 	}
-	suffix := ""
-	if err := server.ServeDispatcher(suffix, dispatcher); err != nil {
-		t.Fatalf("Serve(%v, %v) failed: %v", suffix, dispatcher, err)
-	}
-	von := naming.JoinAddressName(endpoints[0].String(), "test")
+	von := naming.JoinAddressName(server.Status().Endpoints[0].String(), "test")
 	return von, func() {
 		if err := os.Remove(path); err != nil {
 			t.Fatalf("Remove(%v) failed: %v", path, err)
diff --git a/services/internal/binarylib/impl_test.go b/services/internal/binarylib/impl_test.go
index 6ce83b0..e80062a 100644
--- a/services/internal/binarylib/impl_test.go
+++ b/services/internal/binarylib/impl_test.go
@@ -21,6 +21,7 @@
 	"v.io/v23/verror"
 	"v.io/x/lib/vlog"
 
+	"v.io/x/ref/lib/xrpc"
 	_ "v.io/x/ref/runtime/factories/static"
 	"v.io/x/ref/services/internal/binarylib"
 	"v.io/x/ref/services/internal/servicetest"
@@ -38,9 +39,6 @@
 	rootDir, cleanup := servicetest.SetupRootDir(t, "bindir")
 	prepDirectory(t, rootDir)
 
-	// Setup and start the binary repository server.
-	server, endpoint := servicetest.NewServer(ctx)
-
 	listener, err := net.Listen("tcp", "127.0.0.1:0")
 	if err != nil {
 		t.Fatal(err)
@@ -54,14 +52,18 @@
 			vlog.Fatalf("Serve() failed: %v", err)
 		}
 	}()
+
+	// Setup and start the binary repository server.
 	dispatcher, err := binarylib.NewDispatcher(v23.GetPrincipal(ctx), state)
 	if err != nil {
 		t.Fatalf("NewDispatcher failed: %v", err)
 	}
 	dontPublishName := ""
-	if err := server.ServeDispatcher(dontPublishName, dispatcher); err != nil {
-		t.Fatalf("Serve(%q) failed: %v", dontPublishName, err)
+	server, err := xrpc.NewDispatchingServer(ctx, dontPublishName, dispatcher)
+	if err != nil {
+		t.Fatalf("NewServer(%q) failed: %v", dontPublishName, err)
 	}
+	endpoint := server.Status().Endpoints[0].String()
 	name := naming.JoinAddressName(endpoint, "test")
 	binary := repository.BinaryClient(name)
 	return binary, endpoint, fmt.Sprintf("http://%s/test", listener.Addr()), func() {
diff --git a/services/internal/binarylib/perms_test.go b/services/internal/binarylib/perms_test.go
index 80b5307..9d3308d 100644
--- a/services/internal/binarylib/perms_test.go
+++ b/services/internal/binarylib/perms_test.go
@@ -21,6 +21,7 @@
 	"v.io/x/lib/vlog"
 	vsecurity "v.io/x/ref/lib/security"
 	"v.io/x/ref/lib/signals"
+	"v.io/x/ref/lib/xrpc"
 	"v.io/x/ref/services/internal/binarylib"
 	"v.io/x/ref/services/internal/servicetest"
 	"v.io/x/ref/test"
@@ -43,10 +44,6 @@
 	defer vlog.VI(1).Infof("%v terminating", publishName)
 	defer shutdown()
 
-	server, endpoint := servicetest.NewServer(ctx)
-	name := naming.JoinAddressName(endpoint, "")
-	vlog.VI(1).Infof("binaryd name: %v", name)
-
 	depth := 2
 	state, err := binarylib.NewState(storedir, "", depth)
 	if err != nil {
@@ -56,9 +53,11 @@
 	if err != nil {
 		vlog.Fatalf("Failed to create binaryd dispatcher: %v", err)
 	}
-	if err := server.ServeDispatcher(publishName, dispatcher); err != nil {
-		vlog.Fatalf("Serve(%v) failed: %v", publishName, err)
+	server, err := xrpc.NewDispatchingServer(ctx, publishName, dispatcher)
+	if err != nil {
+		vlog.Fatalf("NewDispatchingServer(%v) failed: %v", publishName, err)
 	}
+	vlog.VI(1).Infof("binaryd name: %v", server.Status().Endpoints[0].Name())
 
 	fmt.Fprintf(env.Stdout, "ready:%d\n", os.Getpid())
 	<-signals.ShutdownOnSignals(ctx)
diff --git a/services/internal/logreaderlib/logfile_test.go b/services/internal/logreaderlib/logfile_test.go
index ca3dd6e..1b0a7b9 100644
--- a/services/internal/logreaderlib/logfile_test.go
+++ b/services/internal/logreaderlib/logfile_test.go
@@ -10,13 +10,11 @@
 	"path"
 	"testing"
 
-	"v.io/v23"
-	"v.io/v23/context"
 	"v.io/v23/naming"
-	"v.io/v23/rpc"
 	"v.io/v23/security"
 	"v.io/v23/services/logreader"
 	"v.io/v23/verror"
+	"v.io/x/ref/lib/xrpc"
 	"v.io/x/ref/services/internal/logreaderlib"
 	"v.io/x/ref/test"
 
@@ -25,30 +23,6 @@
 
 //go:generate v23 test generate
 
-func startServer(t *testing.T, ctx *context.T, disp rpc.Dispatcher) (rpc.Server, string, error) {
-	server, err := v23.NewServer(ctx)
-	if err != nil {
-		t.Fatalf("NewServer failed: %v", err)
-		return nil, "", err
-	}
-	endpoints, err := server.Listen(v23.GetListenSpec(ctx))
-	if err != nil {
-		t.Fatalf("Listen failed: %v", err)
-		return nil, "", err
-	}
-	if err := server.ServeDispatcher("", disp); err != nil {
-		t.Fatalf("Serve failed: %v", err)
-		return nil, "", err
-	}
-	return server, endpoints[0].String(), nil
-}
-
-func stopServer(t *testing.T, server rpc.Server) {
-	if err := server.Stop(); err != nil {
-		t.Errorf("server.Stop failed: %v", err)
-	}
-}
-
 type logFileDispatcher struct {
 	root string
 }
@@ -75,12 +49,11 @@
 		t.Fatalf("ioutil.TempDir: %v", err)
 	}
 	defer os.RemoveAll(workdir)
-	server, endpoint, err := startServer(t, ctx, &logFileDispatcher{workdir})
+	server, err := xrpc.NewDispatchingServer(ctx, "", &logFileDispatcher{workdir})
 	if err != nil {
-		t.Fatalf("startServer failed: %v", err)
+		t.Fatalf("NewDispatchingServer failed: %v", err)
 	}
-	defer stopServer(t, server)
-
+	endpoint := server.Status().Endpoints[0].String()
 	const testFile = "mylogfile.INFO"
 	writer, err := os.Create(path.Join(workdir, testFile))
 	if err != nil {
@@ -162,11 +135,11 @@
 		t.Fatalf("ioutil.TempDir: %v", err)
 	}
 	defer os.RemoveAll(workdir)
-	server, endpoint, err := startServer(t, ctx, &logFileDispatcher{workdir})
+	server, err := xrpc.NewDispatchingServer(ctx, "", &logFileDispatcher{workdir})
 	if err != nil {
-		t.Fatalf("startServer failed: %v", err)
+		t.Fatalf("NewDispatchingServer failed: %v", err)
 	}
-	defer stopServer(t, server)
+	endpoint := server.Status().Endpoints[0].String()
 
 	const testFile = "mylogfile.INFO"
 	writer, err := os.Create(path.Join(workdir, testFile))
diff --git a/services/internal/pproflib/proxy_test.go b/services/internal/pproflib/proxy_test.go
index 6f30246..8a3f976 100644
--- a/services/internal/pproflib/proxy_test.go
+++ b/services/internal/pproflib/proxy_test.go
@@ -10,8 +10,8 @@
 	"net/http"
 	"testing"
 
-	"v.io/v23"
 	"v.io/v23/security"
+	"v.io/x/ref/lib/xrpc"
 	"v.io/x/ref/services/internal/pproflib"
 	"v.io/x/ref/test"
 
@@ -32,18 +32,11 @@
 	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
-	s, err := v23.NewServer(ctx)
+	s, err := xrpc.NewDispatchingServer(ctx, "", &dispatcher{pproflib.NewPProfService()})
 	if err != nil {
 		t.Fatalf("failed to start server: %v", err)
 	}
-	defer s.Stop()
-	endpoints, err := s.Listen(v23.GetListenSpec(ctx))
-	if err != nil {
-		t.Fatalf("failed to listen: %v", err)
-	}
-	if err := s.ServeDispatcher("", &dispatcher{pproflib.NewPProfService()}); err != nil {
-		t.Fatalf("failed to serve: %v", err)
-	}
+	endpoints := s.Status().Endpoints
 	l, err := pproflib.StartProxy(ctx, endpoints[0].Name())
 	if err != nil {
 		t.Fatalf("failed to start proxy: %v", err)
diff --git a/services/internal/servicetest/modules.go b/services/internal/servicetest/modules.go
index e69c624..dcd226b 100644
--- a/services/internal/servicetest/modules.go
+++ b/services/internal/servicetest/modules.go
@@ -15,10 +15,10 @@
 	"v.io/v23"
 	"v.io/v23/context"
 	"v.io/v23/options"
-	"v.io/v23/rpc"
 	"v.io/v23/security"
 	"v.io/x/lib/vlog"
 	"v.io/x/ref"
+	"v.io/x/ref/lib/xrpc"
 	"v.io/x/ref/services/mounttable/mounttablelib"
 	"v.io/x/ref/test/modules"
 	"v.io/x/ref/test/testutil"
@@ -36,24 +36,16 @@
 	ctx, shutdown := v23.Init()
 	defer shutdown()
 
-	lspec := v23.GetListenSpec(ctx)
-	server, err := v23.NewServer(ctx, options.ServesMountTable(true))
-	if err != nil {
-		return fmt.Errorf("root failed: %v", err)
-	}
 	mt, err := mounttablelib.NewMountTableDispatcher("", "", "mounttable")
 	if err != nil {
 		return fmt.Errorf("mounttablelib.NewMountTableDispatcher failed: %s", err)
 	}
-	eps, err := server.Listen(lspec)
+	server, err := xrpc.NewDispatchingServer(ctx, "", mt, options.ServesMountTable(true))
 	if err != nil {
-		return fmt.Errorf("server.Listen failed: %s", err)
-	}
-	if err := server.ServeDispatcher("", mt); err != nil {
-		return fmt.Errorf("root failed: %s", err)
+		return fmt.Errorf("root failed: %v", err)
 	}
 	fmt.Fprintf(env.Stdout, "PID=%d\n", os.Getpid())
-	for _, ep := range eps {
+	for _, ep := range server.Status().Endpoints {
 		fmt.Fprintf(env.Stdout, "MT_NAME=%s\n", ep.Name())
 	}
 	modules.WaitForEOF(env.Stdin)
@@ -128,20 +120,6 @@
 	return h
 }
 
-// NewServer creates a new server.
-func NewServer(ctx *context.T) (rpc.Server, string) {
-	server, err := v23.NewServer(ctx)
-	if err != nil {
-		vlog.Fatalf("NewServer() failed: %v", err)
-	}
-	spec := rpc.ListenSpec{Addrs: rpc.ListenAddrs{{"tcp", "127.0.0.1:0"}}}
-	endpoints, err := server.Listen(spec)
-	if err != nil {
-		vlog.Fatalf("Listen(%s) failed: %v", spec, err)
-	}
-	return server, endpoints[0].String()
-}
-
 // ReadPID waits for the "ready:<PID>" line from the child and parses out the
 // PID of the child.
 func ReadPID(t *testing.T, h modules.ExpectSession) int {
diff --git a/services/internal/statslib/stats_test.go b/services/internal/statslib/stats_test.go
index 9e38a73..b158721 100644
--- a/services/internal/statslib/stats_test.go
+++ b/services/internal/statslib/stats_test.go
@@ -10,7 +10,6 @@
 	"testing"
 	"time"
 
-	"v.io/v23"
 	"v.io/v23/context"
 	"v.io/v23/naming"
 	"v.io/v23/security"
@@ -20,6 +19,7 @@
 
 	libstats "v.io/x/ref/lib/stats"
 	"v.io/x/ref/lib/stats/histogram"
+	"v.io/x/ref/lib/xrpc"
 	"v.io/x/ref/services/internal/statslib"
 	s_stats "v.io/x/ref/services/stats"
 	"v.io/x/ref/test"
@@ -37,31 +37,16 @@
 	return statslib.NewStatsService(suffix, 100*time.Millisecond), nil, nil
 }
 
-func startServer(t *testing.T, ctx *context.T) (string, func()) {
-	disp := &statsDispatcher{}
-	server, err := v23.NewServer(ctx)
-	if err != nil {
-		t.Fatalf("NewServer failed: %v", err)
-		return "", nil
-	}
-	endpoints, err := server.Listen(v23.GetListenSpec(ctx))
-	if err != nil {
-		t.Fatalf("Listen failed: %v", err)
-		return "", nil
-	}
-	if err := server.ServeDispatcher("", disp); err != nil {
-		t.Fatalf("Serve failed: %v", err)
-		return "", nil
-	}
-	return endpoints[0].String(), func() { server.Stop() }
-}
-
 func TestStatsImpl(t *testing.T) {
 	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
-	endpoint, stop := startServer(t, ctx)
-	defer stop()
+	server, err := xrpc.NewDispatchingServer(ctx, "", &statsDispatcher{})
+	if err != nil {
+		t.Fatalf("NewServer failed: %v", err)
+		return
+	}
+	endpoint := server.Status().Endpoints[0].String()
 
 	counter := libstats.NewCounter("testing/foo/bar")
 	counter.Incr(10)
diff --git a/services/internal/vtracelib/vtrace_test.go b/services/internal/vtracelib/vtrace_test.go
index 5eb8a82..3848ad2 100644
--- a/services/internal/vtracelib/vtrace_test.go
+++ b/services/internal/vtracelib/vtrace_test.go
@@ -8,9 +8,9 @@
 	"io"
 	"testing"
 
-	"v.io/v23"
 	s_vtrace "v.io/v23/services/vtrace"
 	"v.io/v23/vtrace"
+	"v.io/x/ref/lib/xrpc"
 	"v.io/x/ref/services/internal/vtracelib"
 	"v.io/x/ref/test"
 
@@ -23,17 +23,11 @@
 	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
-	server, err := v23.NewServer(ctx)
+	server, err := xrpc.NewServer(ctx, "", vtracelib.NewVtraceService(), nil)
 	if err != nil {
 		t.Fatalf("Could not create server: %s", err)
 	}
-	endpoints, err := server.Listen(v23.GetListenSpec(ctx))
-	if err != nil {
-		t.Fatalf("Listen failed: %s", err)
-	}
-	if err := server.Serve("", vtracelib.NewVtraceService(), nil); err != nil {
-		t.Fatalf("Serve failed: %s", err)
-	}
+	endpoints := server.Status().Endpoints
 
 	sctx, span := vtrace.WithNewSpan(ctx, "The Span")
 	vtrace.ForceCollect(sctx)
diff --git a/services/mounttable/mounttablelib/mounttable_test.go b/services/mounttable/mounttablelib/mounttable_test.go
index 42b5cee..4e08341 100644
--- a/services/mounttable/mounttablelib/mounttable_test.go
+++ b/services/mounttable/mounttablelib/mounttable_test.go
@@ -28,6 +28,7 @@
 	"v.io/x/lib/vlog"
 
 	libstats "v.io/x/ref/lib/stats"
+	"v.io/x/ref/lib/xrpc"
 	"v.io/x/ref/services/debug/debuglib"
 	"v.io/x/ref/services/mounttable/mounttablelib"
 	"v.io/x/ref/test"
@@ -185,60 +186,47 @@
 	}
 }
 
-func newMT(t *testing.T, permsFile, persistDir, statsDir string, rootCtx *context.T) (rpc.Server, string) {
+func newMT(t *testing.T, permsFile, persistDir, statsDir string, rootCtx *context.T) (func() error, string) {
 	reservedDisp := debuglib.NewDispatcher(vlog.Log.LogDir, nil)
 	ctx := v23.WithReservedNameDispatcher(rootCtx, reservedDisp)
-	server, err := v23.NewServer(ctx, options.ServesMountTable(true))
-	if err != nil {
-		boom(t, "r.NewServer: %s", err)
-	}
+
 	// Add mount table service.
 	mt, err := mounttablelib.NewMountTableDispatcher(permsFile, persistDir, statsDir)
 	if err != nil {
 		boom(t, "mounttablelib.NewMountTableDispatcher: %v", err)
 	}
-	// Start serving on a loopback address.
-	eps, err := server.Listen(v23.GetListenSpec(ctx))
-	if err != nil {
-		boom(t, "Failed to Listen mount table: %s", err)
-	}
-	if err := server.ServeDispatcher("", mt); err != nil {
-		boom(t, "Failed to register mock collection: %s", err)
-	}
-	estr := eps[0].String()
-	t.Logf("endpoint %s", estr)
-	return server, estr
-}
 
-func newCollection(t *testing.T, rootCtx *context.T) (rpc.Server, string) {
-	server, err := v23.NewServer(rootCtx)
+	// Start serving on a loopback address.
+	server, err := xrpc.NewDispatchingServer(ctx, "", mt, options.ServesMountTable(true))
 	if err != nil {
 		boom(t, "r.NewServer: %s", err)
 	}
-	// Start serving on a loopback address.
-	eps, err := server.Listen(v23.GetListenSpec(rootCtx))
-	if err != nil {
-		boom(t, "Failed to Listen mount table: %s", err)
-	}
-	// Add a collection service.  This is just a service we can mount
-	// and test against.
-	cPrefix := "collection"
-	if err := server.ServeDispatcher(cPrefix, newCollectionServer()); err != nil {
-		boom(t, "Failed to register mock collection: %s", err)
-	}
-	estr := eps[0].String()
+
+	estr := server.Status().Endpoints[0].String()
 	t.Logf("endpoint %s", estr)
-	return server, estr
+	return server.Stop, estr
+}
+
+func newCollection(t *testing.T, rootCtx *context.T) (func() error, string) {
+	// Start serving a collection service on a loopback address.  This
+	// is just a service we can mount and test against.
+	server, err := xrpc.NewDispatchingServer(rootCtx, "collection", newCollectionServer())
+	if err != nil {
+		boom(t, "r.NewServer: %s", err)
+	}
+	estr := server.Status().Endpoints[0].String()
+	t.Logf("endpoint %s", estr)
+	return server.Stop, estr
 }
 
 func TestMountTable(t *testing.T) {
 	rootCtx, aliceCtx, bobCtx, shutdown := initTest()
 	defer shutdown()
 
-	mt, mtAddr := newMT(t, "testdata/test.perms", "", "testMountTable", rootCtx)
-	defer mt.Stop()
-	collection, collectionAddr := newCollection(t, rootCtx)
-	defer collection.Stop()
+	stop, mtAddr := newMT(t, "testdata/test.perms", "", "testMountTable", rootCtx)
+	defer stop()
+	stop, collectionAddr := newCollection(t, rootCtx)
+	defer stop()
 
 	collectionName := naming.JoinAddressName(collectionAddr, "collection")
 
@@ -405,8 +393,8 @@
 	rootCtx, shutdown := test.V23Init()
 	defer shutdown()
 
-	server, estr := newMT(t, "", "", "testGlob", rootCtx)
-	defer server.Stop()
+	stop, estr := newMT(t, "", "", "testGlob", rootCtx)
+	defer stop()
 
 	// set up a mount space
 	fakeServer := naming.JoinAddressName(estr, "quux")
@@ -452,8 +440,8 @@
 	rootCtx, aliceCtx, bobCtx, shutdown := initTest()
 	defer shutdown()
 
-	server, estr := newMT(t, "testdata/test.perms", "", "testAccessListTemplate", rootCtx)
-	defer server.Stop()
+	stop, estr := newMT(t, "testdata/test.perms", "", "testAccessListTemplate", rootCtx)
+	defer stop()
 	fakeServer := naming.JoinAddressName(estr, "quux")
 
 	// Noone should be able to mount on someone else's names.
@@ -525,8 +513,8 @@
 	rootCtx, aliceCtx, bobCtx, shutdown := initTest()
 	defer shutdown()
 
-	server, estr := newMT(t, "testdata/test.perms", "", "testGlobAccessLists", rootCtx)
-	defer server.Stop()
+	stop, estr := newMT(t, "testdata/test.perms", "", "testGlobAccessLists", rootCtx)
+	defer stop()
 
 	// set up a mount space
 	fakeServer := naming.JoinAddressName(estr, "quux")
@@ -558,8 +546,8 @@
 	rootCtx, shutdown := test.V23Init()
 	defer shutdown()
 
-	server, estr := newMT(t, "", "", "testCleanup", rootCtx)
-	defer server.Stop()
+	stop, estr := newMT(t, "", "", "testCleanup", rootCtx)
+	defer stop()
 
 	// Set up one mount.
 	fakeServer := naming.JoinAddressName(estr, "quux")
@@ -586,8 +574,8 @@
 	rootCtx, aliceCtx, bobCtx, shutdown := initTest()
 	defer shutdown()
 
-	server, estr := newMT(t, "testdata/test.perms", "", "testDelete", rootCtx)
-	defer server.Stop()
+	stop, estr := newMT(t, "testdata/test.perms", "", "testDelete", rootCtx)
+	defer stop()
 
 	// set up a mount space
 	fakeServer := naming.JoinAddressName(estr, "quux")
@@ -619,8 +607,8 @@
 	rootCtx, shutdown := test.V23Init()
 	defer shutdown()
 
-	server, estr := newMT(t, "", "", "testerverFormat", rootCtx)
-	defer server.Stop()
+	stop, estr := newMT(t, "", "", "testerverFormat", rootCtx)
+	defer stop()
 
 	doMount(t, rootCtx, estr, "endpoint", naming.JoinAddressName(estr, "life/on/the/mississippi"), true)
 	doMount(t, rootCtx, estr, "hostport", "/atrampabroad:8000", true)
@@ -633,10 +621,10 @@
 	rootCtx, shutdown := test.V23Init()
 	defer shutdown()
 
-	server, estr := newMT(t, "", "", "testExpiry", rootCtx)
-	defer server.Stop()
-	collection, collectionAddr := newCollection(t, rootCtx)
-	defer collection.Stop()
+	stop, estr := newMT(t, "", "", "testExpiry", rootCtx)
+	defer stop()
+	stop, collectionAddr := newCollection(t, rootCtx)
+	defer stop()
 
 	collectionName := naming.JoinAddressName(collectionAddr, "collection")
 
@@ -701,8 +689,8 @@
 	ft := mounttablelib.NewFakeTimeClock()
 	mounttablelib.SetServerListClock(ft)
 
-	server, estr := newMT(t, "", "", "mounttable", rootCtx)
-	defer server.Stop()
+	stop, estr := newMT(t, "", "", "mounttable", rootCtx)
+	defer stop()
 
 	// Test flat tree
 	for i := 1; i <= 10; i++ {
@@ -811,8 +799,8 @@
 	rootCtx, _, _, shutdown := initTest()
 	defer shutdown()
 
-	server, estr := newMT(t, "testdata/intermediate.perms", "", "TestIntermediateNodesCreatedFromConfig", rootCtx)
-	defer server.Stop()
+	stop, estr := newMT(t, "testdata/intermediate.perms", "", "TestIntermediateNodesCreatedFromConfig", rootCtx)
+	defer stop()
 
 	// x and x/y should have the same permissions at the root.
 	rootPerms, _ := doGetPermissions(t, rootCtx, estr, "", true)
diff --git a/services/mounttable/mounttablelib/persist_test.go b/services/mounttable/mounttablelib/persist_test.go
index a1b90b9..74e6e1e 100644
--- a/services/mounttable/mounttablelib/persist_test.go
+++ b/services/mounttable/mounttablelib/persist_test.go
@@ -25,7 +25,7 @@
 	}
 	defer os.RemoveAll(td)
 	fmt.Printf("temp persist dir %s\n", td)
-	mt, mtAddr := newMT(t, "", td, "testPersistence", rootCtx)
+	stop, mtAddr := newMT(t, "", td, "testPersistence", rootCtx)
 
 	perms1 := access.Permissions{
 		"Read":    access.AccessList{In: []security.BlessingPattern{security.AllPrincipals}},
@@ -50,10 +50,10 @@
 	doSetPermissions(t, rootCtx, mtAddr, "a/b/c/d/e", perms2, "", true)
 	doDeleteSubtree(t, rootCtx, mtAddr, "a/b/c", true)
 	doSetPermissions(t, rootCtx, mtAddr, "a/c/d", perms3, "", true)
-	mt.Stop()
+	stop()
 
 	// Restart with the persisted data.
-	mt, mtAddr = newMT(t, "", td, "testPersistence", rootCtx)
+	stop, mtAddr = newMT(t, "", td, "testPersistence", rootCtx)
 
 	// Add root as Admin to each of the perms since the mounttable itself will.
 	perms1["Admin"] = access.AccessList{In: []security.BlessingPattern{"root"}}
@@ -74,5 +74,5 @@
 	if perm, _ := doGetPermissions(t, rootCtx, mtAddr, "a/c/d", true); !reflect.DeepEqual(perm, perms3) {
 		t.Fatalf("a/c/d: got %v, want %v", perm, perms3)
 	}
-	mt.Stop()
+	stop()
 }
diff --git a/services/mounttable/mounttablelib/servers.go b/services/mounttable/mounttablelib/servers.go
index 0396ecc..ef8c550 100644
--- a/services/mounttable/mounttablelib/servers.go
+++ b/services/mounttable/mounttablelib/servers.go
@@ -13,6 +13,7 @@
 	"v.io/v23/options"
 	"v.io/v23/rpc"
 	"v.io/x/lib/vlog"
+	"v.io/x/ref/lib/xrpc"
 )
 
 func StartServers(ctx *context.T, listenSpec rpc.ListenSpec, mountName, nhName, permsFile, persistDir, debugPrefix string) (string, func(), error) {
@@ -23,74 +24,48 @@
 		}
 	}
 
-	mtServer, err := v23.NewServer(ctx, options.ServesMountTable(true))
+	mt, err := NewMountTableDispatcher(permsFile, persistDir, debugPrefix)
+	if err != nil {
+		vlog.Errorf("NewMountTable failed: %v", err)
+		return "", nil, err
+	}
+	mtServer, err := xrpc.NewDispatchingServer(ctx, mountName, mt, options.ServesMountTable(true))
 	if err != nil {
 		vlog.Errorf("v23.NewServer failed: %v", err)
 		return "", nil, err
 	}
 	stopFuncs = append(stopFuncs, mtServer.Stop)
-	mt, err := NewMountTableDispatcher(permsFile, persistDir, debugPrefix)
-	if err != nil {
-		vlog.Errorf("NewMountTable failed: %v", err)
-		stop()
-		return "", nil, err
-	}
-	mtEndpoints, err := mtServer.Listen(listenSpec)
-	if err != nil {
-		vlog.Errorf("mtServer.Listen failed: %v", err)
-		stop()
-		return "", nil, err
-	}
-	mtEndpoint := mtEndpoints[0]
-	if err := mtServer.ServeDispatcher(mountName, mt); err != nil {
-		vlog.Errorf("ServeDispatcher() failed: %v", err)
-		stop()
-		return "", nil, err
-	}
-
-	mtName := mtEndpoint.Name()
+	mtEndpoints := mtServer.Status().Endpoints
+	mtName := mtEndpoints[0].Name()
 	vlog.Infof("Mount table service at: %q endpoint: %s", mountName, mtName)
 
 	if len(nhName) > 0 {
-		neighborhoodListenSpec := listenSpec.Copy()
 		// The ListenSpec code ensures that we have a valid address here.
 		host, port, _ := net.SplitHostPort(listenSpec.Addrs[0].Address)
 		if port != "" {
+			neighborhoodListenSpec := listenSpec.Copy()
 			neighborhoodListenSpec.Addrs[0].Address = net.JoinHostPort(host, "0")
+			ctx = v23.WithListenSpec(ctx, neighborhoodListenSpec)
 		}
-		nhServer, err := v23.NewServer(ctx, options.ServesMountTable(true))
+
+		names := []string{}
+		for _, ep := range mtEndpoints {
+			names = append(names, ep.Name())
+		}
+		var nh rpc.Dispatcher
+		if host == "127.0.0.1" || host == "localhost" {
+			nh, err = NewLoopbackNeighborhoodDispatcher(nhName, names...)
+		} else {
+			nh, err = NewNeighborhoodDispatcher(nhName, names...)
+		}
+
+		nhServer, err := xrpc.NewDispatchingServer(ctx, naming.Join(mtName, "nh"), nh, options.ServesMountTable(true))
 		if err != nil {
 			vlog.Errorf("v23.NewServer failed: %v", err)
 			stop()
 			return "", nil, err
 		}
 		stopFuncs = append(stopFuncs, nhServer.Stop)
-		if _, err := nhServer.Listen(neighborhoodListenSpec); err != nil {
-			vlog.Errorf("nhServer.Listen failed: %v", err)
-			stop()
-			return "", nil, err
-		}
-
-		addresses := []string{}
-		for _, ep := range mtEndpoints {
-			addresses = append(addresses, ep.Name())
-		}
-		var nh rpc.Dispatcher
-		if host == "127.0.0.1" || host == "localhost" {
-			nh, err = NewLoopbackNeighborhoodDispatcher(nhName, addresses...)
-		} else {
-			nh, err = NewNeighborhoodDispatcher(nhName, addresses...)
-		}
-		if err != nil {
-			vlog.Errorf("NewNeighborhoodServer failed: %v", err)
-			stop()
-			return "", nil, err
-		}
-		if err := nhServer.ServeDispatcher(naming.JoinAddressName(mtName, "nh"), nh); err != nil {
-			vlog.Errorf("nhServer.ServeDispatcher failed to register neighborhood: %v", err)
-			stop()
-			return "", nil, err
-		}
 	}
 	return mtName, stop, nil
 }
diff --git a/services/profile/profile/impl_test.go b/services/profile/profile/impl_test.go
index 7ac0fcb..e3695fb 100644
--- a/services/profile/profile/impl_test.go
+++ b/services/profile/profile/impl_test.go
@@ -10,7 +10,6 @@
 	"strings"
 	"testing"
 
-	"v.io/v23"
 	"v.io/v23/context"
 	"v.io/v23/naming"
 	"v.io/v23/rpc"
@@ -20,6 +19,7 @@
 	"v.io/x/lib/cmdline"
 	"v.io/x/lib/vlog"
 	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/lib/xrpc"
 	_ "v.io/x/ref/runtime/factories/generic"
 	"v.io/x/ref/services/profile"
 	"v.io/x/ref/services/repository"
@@ -84,51 +84,23 @@
 type dispatcher struct {
 }
 
-func NewDispatcher() rpc.Dispatcher {
-	return &dispatcher{}
-}
-
 func (d *dispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) {
 	return repository.ProfileServer(&server{suffix: suffix}), nil, nil
 }
 
-func startServer(t *testing.T, ctx *context.T) (rpc.Server, naming.Endpoint, error) {
-	server, err := v23.NewServer(ctx)
-	if err != nil {
-		t.Errorf("NewServer failed: %v", err)
-		return nil, nil, err
-	}
-	endpoints, err := server.Listen(v23.GetListenSpec(ctx))
-	if err != nil {
-		t.Errorf("Listen failed: %v", err)
-		return nil, nil, err
-	}
-	if err := server.ServeDispatcher("", NewDispatcher()); err != nil {
-		t.Errorf("ServeDispatcher failed: %v", err)
-		return nil, nil, err
-	}
-	return server, endpoints[0], nil
-}
-
-func stopServer(t *testing.T, server rpc.Server) {
-	if err := server.Stop(); err != nil {
-		t.Errorf("server.Stop failed: %v", err)
-	}
-}
-
 func TestProfileClient(t *testing.T) {
 	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
-	server, endpoint, err := startServer(t, ctx)
+	server, err := xrpc.NewDispatchingServer(ctx, "", &dispatcher{})
 	if err != nil {
 		return
 	}
-	defer stopServer(t, server)
+
 	// Setup the command-line.
 	var stdout, stderr bytes.Buffer
 	env := &cmdline.Env{Stdout: &stdout, Stderr: &stderr}
-	exists := naming.JoinAddressName(endpoint.String(), "exists")
+	exists := naming.JoinAddressName(server.Status().Endpoints[0].String(), "exists")
 
 	// Test the 'label' command.
 	if err := v23cmd.ParseAndRunForTest(cmdRoot, ctx, env, []string{"label", exists}); err != nil {
diff --git a/services/profile/profiled/impl_test.go b/services/profile/profiled/impl_test.go
index 133c88a..89e5667 100644
--- a/services/profile/profiled/impl_test.go
+++ b/services/profile/profiled/impl_test.go
@@ -10,10 +10,10 @@
 	"reflect"
 	"testing"
 
-	"v.io/v23"
 	"v.io/v23/naming"
 	"v.io/v23/services/build"
 
+	"v.io/x/ref/lib/xrpc"
 	"v.io/x/ref/services/profile"
 	"v.io/x/ref/services/repository"
 	"v.io/x/ref/test"
@@ -39,13 +39,6 @@
 	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
-	// Setup and start the profile repository server.
-	server, err := v23.NewServer(ctx)
-	if err != nil {
-		t.Fatalf("NewServer() failed: %v", err)
-	}
-	defer server.Stop()
-
 	dir, prefix := "", ""
 	store, err := ioutil.TempDir(dir, prefix)
 	if err != nil {
@@ -56,20 +49,14 @@
 	if err != nil {
 		t.Fatalf("NewDispatcher() failed: %v", err)
 	}
-	l := v23.GetListenSpec(ctx)
-	endpoints, err := server.Listen(l)
+	server, err := xrpc.NewDispatchingServer(ctx, "", dispatcher)
 	if err != nil {
-		t.Fatalf("Listen(%s) failed: %v", l, err)
+		t.Fatalf("NewServer() failed: %v", err)
 	}
-	endpoint := endpoints[0]
-	if err := server.ServeDispatcher("", dispatcher); err != nil {
-		t.Fatalf("Serve failed: %v", err)
-	}
-	t.Logf("Profile repository at %v", endpoint)
+	endpoint := server.Status().Endpoints[0].String()
 
 	// Create client stubs for talking to the server.
-	stub := repository.ProfileClient(naming.JoinAddressName(endpoint.String(), "linux/base"))
-
+	stub := repository.ProfileClient(naming.JoinAddressName(endpoint, "linux/base"))
 	// Put
 	if err := stub.Put(ctx, spec); err != nil {
 		t.Fatalf("Put() failed: %v", err)
@@ -106,48 +93,30 @@
 	if err := stub.Remove(ctx); err != nil {
 		t.Fatalf("Remove() failed: %v", err)
 	}
-
-	// Shutdown the content manager server.
-	if err := server.Stop(); err != nil {
-		t.Fatalf("Stop() failed: %v", err)
-	}
 }
 
 func TestPreserveAcrossRestarts(t *testing.T) {
 	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
-	// Setup and start the profile repository server.
-	server, err := v23.NewServer(ctx)
-	if err != nil {
-		t.Fatalf("NewServer() failed: %v", err)
-	}
-	defer server.Stop()
-
 	dir, prefix := "", ""
-	storedir, err := ioutil.TempDir(dir, prefix)
+	store, err := ioutil.TempDir(dir, prefix)
 	if err != nil {
 		t.Fatalf("TempDir(%q, %q) failed: %v", dir, prefix, err)
 	}
-	defer os.RemoveAll(storedir)
-
-	dispatcher, err := NewDispatcher(storedir, nil)
+	defer os.RemoveAll(store)
+	dispatcher, err := NewDispatcher(store, nil)
 	if err != nil {
 		t.Fatalf("NewDispatcher() failed: %v", err)
 	}
-	l := v23.GetListenSpec(ctx)
-	endpoints, err := server.Listen(l)
+	server, err := xrpc.NewDispatchingServer(ctx, "", dispatcher)
 	if err != nil {
-		t.Fatalf("Listen(%s) failed: %v", l, err)
+		t.Fatalf("NewServer() failed: %v", err)
 	}
-	endpoint := endpoints[0]
-	if err := server.ServeDispatcher("", dispatcher); err != nil {
-		t.Fatalf("Serve failed: %v", err)
-	}
-	t.Logf("Profile repository at %v", endpoint)
+	endpoint := server.Status().Endpoints[0].String()
 
 	// Create client stubs for talking to the server.
-	stub := repository.ProfileClient(naming.JoinAddressName(endpoint.String(), "linux/base"))
+	stub := repository.ProfileClient(naming.JoinAddressName(endpoint, "linux/base"))
 
 	if err := stub.Put(ctx, spec); err != nil {
 		t.Fatalf("Put() failed: %v", err)
@@ -165,26 +134,18 @@
 	server.Stop()
 
 	// Setup and start a second server.
-	server, err = v23.NewServer(ctx)
-	if err != nil {
-		t.Fatalf("NewServer() failed: %v", err)
-	}
-	defer server.Stop()
-
-	dispatcher, err = NewDispatcher(storedir, nil)
+	dispatcher, err = NewDispatcher(store, nil)
 	if err != nil {
 		t.Fatalf("NewDispatcher() failed: %v", err)
 	}
-	endpoints, err = server.Listen(l)
+	server, err = xrpc.NewDispatchingServer(ctx, "", dispatcher)
 	if err != nil {
-		t.Fatalf("Listen(%s) failed: %v", l, err)
+		t.Fatalf("NewServer() failed: %v", err)
 	}
-	if err = server.ServeDispatcher("", dispatcher); err != nil {
-		t.Fatalf("Serve failed: %v", err)
-	}
+	endpoint = server.Status().Endpoints[0].String()
 
 	// Create client stubs for talking to the server.
-	stub = repository.ProfileClient(naming.JoinAddressName(endpoints[0].String(), "linux/base"))
+	stub = repository.ProfileClient(naming.JoinAddressName(endpoint, "linux/base"))
 
 	// Label
 	label, err = stub.Label(ctx)
diff --git a/services/profile/profiled/main.go b/services/profile/profiled/main.go
index 9fd3b35..72518f7 100644
--- a/services/profile/profiled/main.go
+++ b/services/profile/profiled/main.go
@@ -10,13 +10,13 @@
 import (
 	"fmt"
 
-	"v.io/v23"
 	"v.io/v23/context"
 	"v.io/x/lib/cmdline"
 	"v.io/x/lib/vlog"
 	"v.io/x/ref/lib/security/securityflag"
 	"v.io/x/ref/lib/signals"
 	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/lib/xrpc"
 	_ "v.io/x/ref/runtime/factories/roaming"
 )
 
@@ -45,25 +45,16 @@
 		return env.UsageErrorf("Specify a directory for storing profiles using --store=<name>")
 	}
 
-	server, err := v23.NewServer(ctx)
-	if err != nil {
-		return fmt.Errorf("NewServer() failed: %v", err)
-	}
-
 	dispatcher, err := NewDispatcher(store, securityflag.NewAuthorizerOrDie())
 	if err != nil {
 		return fmt.Errorf("NewDispatcher() failed: %v", err)
 	}
 
-	ls := v23.GetListenSpec(ctx)
-	endpoint, err := server.Listen(ls)
+	server, err := xrpc.NewDispatchingServer(ctx, name, dispatcher)
 	if err != nil {
-		return fmt.Errorf("Listen(%s) failed: %v", ls, err)
+		return fmt.Errorf("NewServer() failed: %v", err)
 	}
-	if err := server.ServeDispatcher(name, dispatcher); err != nil {
-		return fmt.Errorf("ServeDispatcher(%v) failed: %v", name, err)
-	}
-	vlog.Infof("Profile repository running at endpoint=%q", endpoint)
+	vlog.Infof("Profile repository running at endpoint=%v", server.Status().Endpoints[0])
 
 	// Wait until shutdown.
 	<-signals.ShutdownOnSignals(ctx)
diff --git a/services/proxy/proxyd/main.go b/services/proxy/proxyd/main.go
index 2804ef6..42053e9 100644
--- a/services/proxy/proxyd/main.go
+++ b/services/proxy/proxyd/main.go
@@ -23,6 +23,7 @@
 	"v.io/x/lib/vlog"
 	"v.io/x/ref/lib/signals"
 	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/lib/xrpc"
 	"v.io/x/ref/runtime/factories/static"
 )
 
@@ -89,23 +90,16 @@
 
 	// Start an RPC Server that listens through the proxy itself. This
 	// server will serve reserved methods only.
-	server, err := v23.NewServer(ctx)
-	if err != nil {
-		return fmt.Errorf("NewServer failed: %v", err)
-	}
-	defer server.Stop()
-	ls := rpc.ListenSpec{Proxy: proxyEndpoint.Name()}
-	if _, err := server.Listen(ls); err != nil {
-		return fmt.Errorf("Listen(%v) failed: %v", ls, err)
-	}
 	var monitoringName string
 	if len(name) > 0 {
 		monitoringName = name + "-mon"
 	}
-	if err := server.ServeDispatcher(monitoringName, &nilDispatcher{}); err != nil {
-		return fmt.Errorf("ServeDispatcher(%v) failed: %v", monitoringName, err)
+	ctx = v23.WithListenSpec(ctx, rpc.ListenSpec{Proxy: proxyEndpoint.Name()})
+	server, err := xrpc.NewDispatchingServer(ctx, monitoringName, &nilDispatcher{})
+	if err != nil {
+		return fmt.Errorf("NewServer failed: %v", err)
 	}
-
+	defer server.Stop()
 	<-signals.ShutdownOnSignals(ctx)
 	return nil
 }
diff --git a/services/proxy/proxyd/proxyd_v23_test.go b/services/proxy/proxyd/proxyd_v23_test.go
index 167ea42..86b7c64 100644
--- a/services/proxy/proxyd/proxyd_v23_test.go
+++ b/services/proxy/proxyd/proxyd_v23_test.go
@@ -11,6 +11,7 @@
 	"v.io/v23/context"
 	"v.io/v23/rpc"
 	"v.io/v23/security"
+	"v.io/x/ref/lib/xrpc"
 	"v.io/x/ref/test/modules"
 	"v.io/x/ref/test/v23tests"
 )
@@ -57,19 +58,9 @@
 var runServer = modules.Register(func(env *modules.Env, args ...string) error {
 	ctx, shutdown := v23.Init()
 	defer shutdown()
-
-	server, err := v23.NewServer(ctx)
-	if err != nil {
+	if _, err := xrpc.NewServer(ctx, serverName, service{}, security.AllowEveryone()); err != nil {
 		return err
 	}
-	defer server.Stop()
-	if _, err := server.Listen(rpc.ListenSpec{Proxy: proxyName}); err != nil {
-		return err
-	}
-	if err := server.Serve(serverName, service{}, security.AllowEveryone()); err != nil {
-		return err
-	}
-
 	modules.WaitForEOF(env.Stdin)
 	return nil
 }, "runServer")
@@ -77,13 +68,8 @@
 var runClient = modules.Register(func(env *modules.Env, args ...string) error {
 	ctx, shutdown := v23.Init()
 	defer shutdown()
-
-	call, err := v23.GetClient(ctx).StartCall(ctx, serverName, "Echo", nil)
-	if err != nil {
-		return err
-	}
 	var response string
-	if err := call.Finish(&response); err != nil {
+	if err := v23.GetClient(ctx).Call(ctx, serverName, "Echo", nil, []interface{}{&response}); err != nil {
 		return err
 	}
 	fmt.Fprintf(env.Stdout, "%v=%v\n", responseVar, response)
diff --git a/services/role/roled/main.go b/services/role/roled/main.go
index 42bce3f..e1a3857 100644
--- a/services/role/roled/main.go
+++ b/services/role/roled/main.go
@@ -10,12 +10,11 @@
 import (
 	"fmt"
 
-	"v.io/v23"
 	"v.io/v23/context"
 	"v.io/x/lib/cmdline"
-	"v.io/x/lib/vlog"
 	"v.io/x/ref/lib/signals"
 	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/lib/xrpc"
 	_ "v.io/x/ref/runtime/factories/static"
 	irole "v.io/x/ref/services/role/roled/internal"
 )
@@ -44,25 +43,11 @@
 	if len(name) == 0 {
 		return env.UsageErrorf("-name must be specified")
 	}
-	server, err := v23.NewServer(ctx)
+	_, err := xrpc.NewDispatchingServer(ctx, name, irole.NewDispatcher(configDir, name))
 	if err != nil {
 		return fmt.Errorf("NewServer failed: %v", err)
 	}
-
-	listenSpec := v23.GetListenSpec(ctx)
-	eps, err := server.Listen(listenSpec)
-	if err != nil {
-		return fmt.Errorf("Listen(%v) failed: %v", listenSpec, err)
-	}
-	vlog.Infof("Listening on: %q", eps)
-	if err := server.ServeDispatcher(name, irole.NewDispatcher(configDir, name)); err != nil {
-		return fmt.Errorf("ServeDispatcher(%q) failed: %v", name, err)
-	}
-	if len(name) > 0 {
-		fmt.Printf("NAME=%s\n", name)
-	} else if len(eps) > 0 {
-		fmt.Printf("NAME=%s\n", eps[0].Name())
-	}
+	fmt.Printf("NAME=%s\n", name)
 	<-signals.ShutdownOnSignals(ctx)
 	return nil
 }
diff --git a/services/wspr/internal/app/app_test.go b/services/wspr/internal/app/app_test.go
index 1e83241..b9d242f 100644
--- a/services/wspr/internal/app/app_test.go
+++ b/services/wspr/internal/app/app_test.go
@@ -15,7 +15,6 @@
 
 	"v.io/v23"
 	"v.io/v23/context"
-	"v.io/v23/naming"
 	"v.io/v23/options"
 	"v.io/v23/rpc"
 	"v.io/v23/security"
@@ -26,6 +25,7 @@
 	"v.io/v23/vom"
 	"v.io/v23/vtrace"
 	vsecurity "v.io/x/ref/lib/security"
+	"v.io/x/ref/lib/xrpc"
 	"v.io/x/ref/runtime/factories/generic"
 	"v.io/x/ref/services/mounttable/mounttablelib"
 	"v.io/x/ref/services/wspr/internal/lib"
@@ -100,36 +100,6 @@
 	},
 }
 
-func startAnyServer(ctx *context.T, servesMT bool, dispatcher rpc.Dispatcher) (rpc.Server, naming.Endpoint, error) {
-	// Create a new server instance.
-	s, err := v23.NewServer(ctx, options.ServesMountTable(servesMT))
-	if err != nil {
-		return nil, nil, err
-	}
-
-	endpoints, err := s.Listen(v23.GetListenSpec(ctx))
-	if err != nil {
-		return nil, nil, err
-	}
-
-	if err := s.ServeDispatcher("", dispatcher); err != nil {
-		return nil, nil, err
-	}
-	return s, endpoints[0], nil
-}
-
-func startAdderServer(ctx *context.T) (rpc.Server, naming.Endpoint, error) {
-	return startAnyServer(ctx, false, testutil.LeafDispatcher(simpleAdder{}, nil))
-}
-
-func startMountTableServer(ctx *context.T) (rpc.Server, naming.Endpoint, error) {
-	mt, err := mounttablelib.NewMountTableDispatcher("", "", "mounttable")
-	if err != nil {
-		return nil, nil, err
-	}
-	return startAnyServer(ctx, true, mt)
-}
-
 func createWriterCreator(w lib.ClientWriter) func(id int32) lib.ClientWriter {
 	return func(int32) lib.ClientWriter {
 		return w
@@ -139,13 +109,11 @@
 	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
-	s, endpoint, err := startAdderServer(ctx)
+	s, err := xrpc.NewServer(ctx, "", simpleAdder{}, nil)
 	if err != nil {
-		t.Errorf("unable to start server: %v", err)
-		t.Fail()
-		return
+		t.Fatalf("unable to start server: %v", err)
 	}
-	defer s.Stop()
+	name := s.Status().Endpoints[0].Name()
 
 	spec := v23.GetListenSpec(ctx)
 	spec.Proxy = "mockVeyronProxyEP"
@@ -155,7 +123,7 @@
 	if err != nil {
 		t.Fatalf("Failed to create controller: %v", err)
 	}
-	sig, err := controller.getSignature(ctx, "/"+endpoint.String())
+	sig, err := controller.getSignature(ctx, name)
 	if err != nil {
 		t.Fatalf("Failed to get signature: %v", err)
 	}
@@ -184,13 +152,11 @@
 	ctx, shutdown := test.V23Init()
 	defer shutdown()
 
-	s, endpoint, err := startAdderServer(ctx)
+	s, err := xrpc.NewServer(ctx, "", simpleAdder{}, nil)
 	if err != nil {
-		t.Errorf("unable to start server: %v", err)
-		t.Fail()
-		return
+		t.Fatalf("unable to start server: %v", err)
 	}
-	defer s.Stop()
+	name := s.Status().Endpoints[0].Name()
 
 	spec := v23.GetListenSpec(ctx)
 	spec.Proxy = "mockVeyronProxyEP"
@@ -217,7 +183,7 @@
 	}
 
 	request := RpcRequest{
-		Name:        "/" + endpoint.String(),
+		Name:        name,
 		Method:      testCase.method,
 		NumInArgs:   int32(len(testCase.inArgs)),
 		NumOutArgs:  testCase.numOutArgs,
@@ -319,11 +285,10 @@
 }
 
 type runningTest struct {
-	controller       *Controller
-	writer           *testwriter.Writer
-	mounttableServer rpc.Server
-	proxyShutdown    func()
-	typeEncoder      *vom.TypeEncoder
+	controller    *Controller
+	writer        *testwriter.Writer
+	proxyShutdown func()
+	typeEncoder   *vom.TypeEncoder
 }
 
 func makeRequest(typeEncoder *vom.TypeEncoder, rpc RpcRequest, args ...interface{}) (string, error) {
@@ -350,10 +315,16 @@
 }
 
 func serveServer(ctx *context.T, writer lib.ClientWriter, setController func(*Controller)) (*runningTest, error) {
-	mounttableServer, endpoint, err := startMountTableServer(ctx)
+	mt, err := mounttablelib.NewMountTableDispatcher("", "", "mounttable")
 	if err != nil {
 		return nil, fmt.Errorf("unable to start mounttable: %v", err)
 	}
+	s, err := xrpc.NewDispatchingServer(ctx, "", mt, options.ServesMountTable(true))
+	if err != nil {
+		return nil, fmt.Errorf("unable to start mounttable: %v", err)
+	}
+	mtName := s.Status().Endpoints[0].Name()
+
 	proxySpec := rpc.ListenSpec{
 		Addrs: rpc.ListenAddrs{
 			// This '0' label is required by go vet.
@@ -384,7 +355,7 @@
 		setController(controller)
 	}
 
-	v23.GetNamespace(controller.Context()).SetRoots("/" + endpoint.String())
+	v23.GetNamespace(controller.Context()).SetRoots(mtName)
 	typeStream := &typeEncoderWriter{c: controller}
 	typeEncoder := vom.NewTypeEncoder(typeStream)
 	req, err := makeRequest(typeEncoder, RpcRequest{
@@ -399,7 +370,7 @@
 
 	testWriter, _ := writer.(*testwriter.Writer)
 	return &runningTest{
-		controller, testWriter, mounttableServer, proxyShutdown,
+		controller, testWriter, proxyShutdown,
 		typeEncoder,
 	}, nil
 }
@@ -456,7 +427,6 @@
 	})
 
 	mock.typeEncoder = rt.typeEncoder
-	defer rt.mounttableServer.Stop()
 	defer rt.proxyShutdown()
 	defer rt.controller.Cleanup()
 
diff --git a/services/wspr/internal/browspr/browspr_test.go b/services/wspr/internal/browspr/browspr_test.go
index 956fe54..270d263 100644
--- a/services/wspr/internal/browspr/browspr_test.go
+++ b/services/wspr/internal/browspr/browspr_test.go
@@ -13,7 +13,6 @@
 
 	"v.io/v23"
 	"v.io/v23/context"
-	"v.io/v23/naming"
 	"v.io/v23/options"
 	"v.io/v23/rpc"
 	"v.io/v23/security"
@@ -21,6 +20,7 @@
 	vdltime "v.io/v23/vdlroot/time"
 	"v.io/v23/vom"
 
+	"v.io/x/ref/lib/xrpc"
 	"v.io/x/ref/runtime/factories/generic"
 	"v.io/x/ref/services/mounttable/mounttablelib"
 	"v.io/x/ref/services/wspr/internal/app"
@@ -30,54 +30,12 @@
 
 //go:generate v23 test generate
 
-func startMounttable(ctx *context.T) (rpc.Server, naming.Endpoint, error) {
-	mt, err := mounttablelib.NewMountTableDispatcher("", "", "mounttable")
-	if err != nil {
-		return nil, nil, err
-	}
-
-	s, err := v23.NewServer(ctx, options.ServesMountTable(true))
-	if err != nil {
-		return nil, nil, err
-	}
-
-	endpoints, err := s.Listen(v23.GetListenSpec(ctx))
-	if err != nil {
-		return nil, nil, err
-	}
-
-	if err := s.ServeDispatcher("", mt); err != nil {
-		return nil, nil, err
-	}
-
-	return s, endpoints[0], nil
-}
-
 type mockServer struct{}
 
 func (s mockServer) BasicCall(_ *context.T, _ rpc.StreamServerCall, txt string) (string, error) {
 	return "[" + txt + "]", nil
 }
 
-func startMockServer(ctx *context.T, desiredName string) (rpc.Server, naming.Endpoint, error) {
-	// Create a new server instance.
-	s, err := v23.NewServer(ctx)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	endpoints, err := s.Listen(v23.GetListenSpec(ctx))
-	if err != nil {
-		return nil, nil, err
-	}
-
-	if err := s.Serve(desiredName, mockServer{}, nil); err != nil {
-		return nil, nil, err
-	}
-
-	return s, endpoints[0], nil
-}
-
 func parseBrowsperResponse(data string, t *testing.T) (uint64, uint64, []byte) {
 	receivedBytes, err := hex.DecodeString(data)
 	if err != nil {
@@ -119,22 +77,27 @@
 	}
 	defer proxyShutdown()
 
-	mtServer, mtEndpoint, err := startMounttable(ctx)
+	mt, err := mounttablelib.NewMountTableDispatcher("", "", "mounttable")
+	if err != nil {
+		t.Fatalf("Failed to create mounttable: %v", err)
+	}
+	s, err := xrpc.NewDispatchingServer(ctx, "", mt, options.ServesMountTable(true))
 	if err != nil {
 		t.Fatalf("Failed to start mounttable server: %v", err)
 	}
-	defer mtServer.Stop()
+	mtEndpoint := s.Status().Endpoints[0]
 	root := mtEndpoint.Name()
+
 	if err := v23.GetNamespace(ctx).SetRoots(root); err != nil {
 		t.Fatalf("Failed to set namespace roots: %v", err)
 	}
 
 	mockServerName := "mock/server"
-	mockServer, mockServerEndpoint, err := startMockServer(ctx, mockServerName)
+	mockServer, err := xrpc.NewServer(ctx, mockServerName, mockServer{}, nil)
 	if err != nil {
 		t.Fatalf("Failed to start mock server: %v", err)
 	}
-	defer mockServer.Stop()
+	mockServerEndpoint := mockServer.Status().Endpoints[0]
 
 	then := time.Now()
 found:
diff --git a/test/hello/helloserver/helloserver.go b/test/hello/helloserver/helloserver.go
index 9acde0d..00d65fd 100644
--- a/test/hello/helloserver/helloserver.go
+++ b/test/hello/helloserver/helloserver.go
@@ -10,13 +10,13 @@
 import (
 	"fmt"
 
-	"v.io/v23"
 	"v.io/v23/context"
 	"v.io/v23/rpc"
 	"v.io/v23/security"
 	"v.io/x/lib/cmdline"
 	"v.io/x/ref/lib/signals"
 	"v.io/x/ref/lib/v23cmd"
+	"v.io/x/ref/lib/xrpc"
 	_ "v.io/x/ref/runtime/factories/generic"
 )
 
@@ -44,22 +44,16 @@
 }
 
 func runHelloServer(ctx *context.T, env *cmdline.Env, args []string) error {
-	server, err := v23.NewServer(ctx)
+	server, err := xrpc.NewServer(ctx, name, &helloServer{}, security.AllowEveryone())
 	if err != nil {
 		return fmt.Errorf("NewServer: %v", err)
 	}
-	eps, err := server.Listen(v23.GetListenSpec(ctx))
-	if err != nil {
-		return fmt.Errorf("Listen: %v", err)
-	}
+	eps := server.Status().Endpoints
 	if len(eps) > 0 {
 		fmt.Printf("SERVER_NAME=%s\n", eps[0].Name())
 	} else {
 		fmt.Println("SERVER_NAME=proxy")
 	}
-	if err := server.Serve(name, &helloServer{}, security.AllowEveryone()); err != nil {
-		return fmt.Errorf("Serve: %v", err)
-	}
 	<-signals.ShutdownOnSignals(ctx)
 	return nil
 }