"veyron2": Add PublicIDStore to Runtime

This CL hooks up the PublicIDStore (holding blessings on the Runtime's
public key) to the Veyron Runtime, and changes the default client
and server to use the Runtime's PrivateID and PublicIDStore. Servers
authenticate using the DefaultPublicID from the store, and clients
authenticate by using a PublicID from the store that matches the remote
end's identity.

Clients and Servers can also be created with options specifying a PublicID.
In this case they still use the PrivateID from the runtime to sign any messages
but use the provided PublicID to authenticate to the other end. Therefore it
is important for the public key of the provided PublicID and the Runtime's
private key correspond. The provided PublicID is not required to be in the
Runtime's PublicIDStore.

Overall we enforce the following invariants:
1) One private key/PrivateID per Runtime.
2) All clients and servers always use the Runtime's private key.

In summary, the CL makes the folliwing changes:
1) Adds a method to the Runtime to access the PublicIDStore.
2) Changes the behavior of default clients and servers to always pick a
PublicID from the Runtime's PublicIDStore during authentication.
3) Changes the LocalID option for clients and servers to be a PublicID,
and adds a new option "RuntimeID" that specifies the PrivateID to be used
by the Runtime.
4) Cleans up some of the tests in runtimes/google/ipc/....

Change-Id: I56c017957fd26ff3d844dbe438a14b84c5143e08
diff --git a/examples/bank/bank/main.go b/examples/bank/bank/main.go
index 92228f6..1145486 100644
--- a/examples/bank/bank/main.go
+++ b/examples/bank/bank/main.go
@@ -95,7 +95,7 @@
 		}
 
 		// Make a NewClient and update the other one. Do not close the old client; we need its connection to the Mount Table.
-		client, err = runtime.NewClient(veyron2.LocalID(derivedIdentity))
+		client, err = runtime.NewClient(veyron2.LocalID(newPublicID))
 		if err != nil {
 			log.Fatalf("failed to create new client: %s\n", err)
 		}
diff --git a/examples/boxes/android/src/boxesp2p/main.go b/examples/boxes/android/src/boxesp2p/main.go
index 8b0b724..a4748e3 100644
--- a/examples/boxes/android/src/boxesp2p/main.go
+++ b/examples/boxes/android/src/boxesp2p/main.go
@@ -398,7 +398,7 @@
 
 	// Initialize veyron runtime and bind to the signalling server used to rendezvous with
 	// another peer device. TODO(gauthamt): Switch to using the nameserver for signalling.
-	gs.runtime = rt.Init(veyron2.LocalID(privateID))
+	gs.runtime = rt.Init(veyron2.RuntimeID(privateID))
 	if gs.signalling, err = boxes.BindBoxSignalling(naming.JoinAddressName("@2@tcp@162.222.181.93:8509@08a93d90836cd94d4dc1acbe40b9048d@1@1@@", "signalling")); err != nil {
 		panic(fmt.Errorf("failed to bind to signalling server:%v\n", err))
 	}
diff --git a/examples/todos/test/util.go b/examples/todos/test/util.go
index e3555c9..9f10a45 100644
--- a/examples/todos/test/util.go
+++ b/examples/todos/test/util.go
@@ -21,7 +21,7 @@
 // getRuntime initializes the veyron2.Runtime if needed, then returns it.
 func getRuntime() veyron2.Runtime {
 	// returns Runtime if already initialized
-	return rt.Init(veyron2.LocalID(security.FakePrivateID("todos")))
+	return rt.Init(veyron2.RuntimeID(security.FakePrivateID("todos")))
 }
 
 // startServer starts a store server and returns the server name as well as a
diff --git a/examples/todos/todos_init/main.go b/examples/todos/todos_init/main.go
index 6121a38..44db7c2 100644
--- a/examples/todos/todos_init/main.go
+++ b/examples/todos/todos_init/main.go
@@ -166,7 +166,7 @@
 	// (since only the admin can put data). The identity here matches with that
 	// used for server.ServerConfig.Admin in todos_stored/main.go. An alternative
 	// would be to relax the ACLs on the store.
-	rt.Init(veyron2.LocalID(security.FakePrivateID("anonymous")))
+	rt.Init(veyron2.RuntimeID(security.FakePrivateID("anonymous")))
 
 	vlog.Infof("Binding to store on %s", storeName)
 	st, err := vstore.New(storeName)
diff --git a/runtimes/google/ipc/flow_test.go b/runtimes/google/ipc/flow_test.go
index 8850b21..47dfb93 100644
--- a/runtimes/google/ipc/flow_test.go
+++ b/runtimes/google/ipc/flow_test.go
@@ -9,12 +9,16 @@
 	"time"
 
 	_ "veyron/lib/testutil"
+	isecurity "veyron/runtimes/google/security"
+
 	"veyron2/ipc"
 	"veyron2/naming"
 	"veyron2/security"
 	"veyron2/verror"
 )
 
+var testID = newID("test")
+
 // newTestFlows returns the two ends of a bidirectional flow.  Each end has its
 // own bookkeeping, to allow testing of method calls.
 func newTestFlows() (*testFlow, *testFlow) {
@@ -34,8 +38,8 @@
 func (f *testFlow) RemoteAddr() net.Addr               { return nil }
 func (f *testFlow) LocalEndpoint() naming.Endpoint     { return nil }
 func (f *testFlow) RemoteEndpoint() naming.Endpoint    { return nil }
-func (f *testFlow) LocalID() security.PublicID         { return security.FakePublicID("test") }
-func (f *testFlow) RemoteID() security.PublicID        { return security.FakePublicID("test") }
+func (f *testFlow) LocalID() security.PublicID         { return testID.PublicID() }
+func (f *testFlow) RemoteID() security.PublicID        { return testID.PublicID() }
 func (f *testFlow) SetReadDeadline(t time.Time) error  { return nil }
 func (f *testFlow) SetWriteDeadline(t time.Time) error { return nil }
 func (f *testFlow) SetDeadline(t time.Time) error      { return nil }
@@ -139,3 +143,7 @@
 		}
 	}
 }
+
+func init() {
+	isecurity.TrustIdentityProviders(testID)
+}
diff --git a/runtimes/google/ipc/full_test.go b/runtimes/google/ipc/full_test.go
index 9b24996..03a3d5a 100644
--- a/runtimes/google/ipc/full_test.go
+++ b/runtimes/google/ipc/full_test.go
@@ -39,8 +39,8 @@
 var (
 	errAuthorizer = errors.New("ipc: application Authorizer denied access")
 	errMethod     = verror.Abortedf("server returned an error")
-	clientID      security.PrivateID
-	serverID      security.PrivateID
+	clientID      = newID("client")
+	serverID      = newID("server")
 	clock         = new(fakeClock)
 )
 
@@ -259,7 +259,7 @@
 
 func startServer(t *testing.T, serverID security.PrivateID, sm stream.Manager, ns naming.Namespace, ts interface{}) (naming.Endpoint, ipc.Server) {
 	vlog.VI(1).Info("InternalNewServer")
-	server, err := InternalNewServer(InternalNewContext(), sm, ns, listenerID(serverID))
+	server, err := InternalNewServer(InternalNewContext(), sm, ns, vc.FixedLocalID(serverID))
 	if err != nil {
 		t.Errorf("InternalNewServer failed: %v", err)
 	}
@@ -327,18 +327,25 @@
 }
 
 func (b bundle) cleanup(t *testing.T) {
-	stopServer(t, b.server, b.ns)
-	b.client.Close()
+	if b.server != nil {
+		stopServer(t, b.server, b.ns)
+	}
+	if b.client != nil {
+		b.client.Close()
+	}
 }
 
 func createBundle(t *testing.T, clientID, serverID security.PrivateID, ts interface{}) (b bundle) {
 	b.sm = imanager.InternalNew(naming.FixedRoutingID(0x555555555))
 	b.ns = newNamespace()
-	b.ep, b.server = startServer(t, serverID, b.sm, b.ns, ts)
-	var err error
-	b.client, err = InternalNewClient(b.sm, b.ns, veyron2.LocalID(clientID))
-	if err != nil {
-		t.Fatalf("InternalNewClient failed: %v", err)
+	if serverID != nil {
+		b.ep, b.server = startServer(t, serverID, b.sm, b.ns, ts)
+	}
+	if clientID != nil {
+		var err error
+		if b.client, err = InternalNewClient(b.sm, b.ns, vc.FixedLocalID(clientID)); err != nil {
+			t.Fatalf("InternalNewClient failed: %v", err)
+		}
 	}
 	return
 }
@@ -352,10 +359,7 @@
 }
 
 func derive(blessor security.PrivateID, name string, caveats ...security.ServiceCaveat) security.PrivateID {
-	id, err := isecurity.NewPrivateID("irrelevant")
-	if err != nil {
-		panic(err)
-	}
+	id := newID("irrelevant")
 	derivedID, err := id.Derive(bless(blessor, id.PublicID(), name, caveats...))
 	if err != nil {
 		panic(err)
@@ -394,7 +398,7 @@
 func TestMultipleCallsToServe(t *testing.T) {
 	sm := imanager.InternalNew(naming.FixedRoutingID(0x555555555))
 	ns := newNamespace()
-	server, err := InternalNewServer(InternalNewContext(), sm, ns, listenerID(serverID))
+	server, err := InternalNewServer(InternalNewContext(), sm, ns, vc.FixedLocalID(serverID))
 	if err != nil {
 		t.Errorf("InternalNewServer failed: %v", err)
 	}
@@ -484,7 +488,7 @@
 	for _, test := range tests {
 		name := fmt.Sprintf("(clientID:%q serverID:%q)", test.clientID, test.serverID)
 		_, server := startServer(t, test.serverID, mgr, ns, &testServer{})
-		client, err := InternalNewClient(mgr, ns, veyron2.LocalID(test.clientID))
+		client, err := InternalNewClient(mgr, ns, vc.FixedLocalID(test.clientID))
 		if err != nil {
 			t.Errorf("%s: Client creation failed: %v", name, err)
 			stopServer(t, server, ns)
@@ -715,10 +719,10 @@
 		return fmt.Sprintf("%q RPCing %s.%s(%v)", t.clientID.PublicID(), t.name, t.method, t.args)
 	}
 
-	b := createBundle(t, nil, serverID, &testServer{})
+	b := createBundle(t, nil, serverID, &testServer{}) // we only create the server, a separate client will be created for each test.
 	defer b.cleanup(t)
 	for _, test := range tests {
-		client, err := InternalNewClient(b.sm, b.ns, veyron2.LocalID(test.clientID))
+		client, err := InternalNewClient(b.sm, b.ns, vc.FixedLocalID(test.clientID))
 		if err != nil {
 			t.Fatalf("InternalNewClient failed: %v", err)
 		}
@@ -969,7 +973,7 @@
 		{[]ipc.ServerOpt{veyron2.PublishFirst, veyron2.EndpointRewriteOpt("example.com")}, []string{"example.com"}},
 	}
 	for i, c := range cases {
-		server, err := InternalNewServer(InternalNewContext(), sm, ns, append([]ipc.ServerOpt{listenerID(serverID)}, c.opts...)...)
+		server, err := InternalNewServer(InternalNewContext(), sm, ns, append(c.opts, vc.FixedLocalID(serverID))...)
 		if err != nil {
 			t.Errorf("InternalNewServer failed: %v", err)
 			continue
@@ -1102,12 +1106,12 @@
 func TestProxy(t *testing.T) {
 	sm := imanager.InternalNew(naming.FixedRoutingID(0x555555555))
 	ns := newNamespace()
-	client, err := InternalNewClient(sm, ns, veyron2.LocalID(clientID))
+	client, err := InternalNewClient(sm, ns, vc.FixedLocalID(clientID))
 	if err != nil {
 		t.Fatal(err)
 	}
 	defer client.Close()
-	server, err := InternalNewServer(InternalNewContext(), sm, ns, listenerID(serverID))
+	server, err := InternalNewServer(InternalNewContext(), sm, ns, vc.FixedLocalID(serverID))
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -1189,7 +1193,7 @@
 	ns := newNamespace()
 	id := loadIdentityFromFile(argv[1])
 	isecurity.TrustIdentityProviders(id)
-	server, err := InternalNewServer(InternalNewContext(), mgr, ns, listenerID(id))
+	server, err := InternalNewServer(InternalNewContext(), mgr, ns, vc.FixedLocalID(id))
 	if err != nil {
 		vlog.Fatalf("InternalNewServer failed: %v", err)
 	}
@@ -1225,13 +1229,6 @@
 }
 
 func init() {
-	var err error
-	if clientID, err = isecurity.NewPrivateID("client"); err != nil {
-		vlog.Fatalf("failed isecurity.NewPrivateID: %s", err)
-	}
-	if serverID, err = isecurity.NewPrivateID("server"); err != nil {
-		vlog.Fatalf("failed isecurity.NewPrivateID: %s", err)
-	}
 	isecurity.TrustIdentityProviders(clientID)
 	isecurity.TrustIdentityProviders(serverID)
 
diff --git a/runtimes/google/ipc/stream/manager/manager_test.go b/runtimes/google/ipc/stream/manager/manager_test.go
index 943a667..2e02ffb 100644
--- a/runtimes/google/ipc/stream/manager/manager_test.go
+++ b/runtimes/google/ipc/stream/manager/manager_test.go
@@ -15,14 +15,22 @@
 	"veyron/runtimes/google/ipc/stream/vc"
 	"veyron/runtimes/google/ipc/version"
 	inaming "veyron/runtimes/google/naming"
+	isecurity "veyron/runtimes/google/security"
 
-	"veyron2"
 	"veyron2/ipc/stream"
 	"veyron2/naming"
 	"veyron2/security"
 	"veyron2/vlog"
 )
 
+func newID(name string) security.PrivateID {
+	id, err := isecurity.NewPrivateID(name)
+	if err != nil {
+		panic(err)
+	}
+	return id
+}
+
 func init() {
 	// The testutil package's init sets GOMAXPROCS to NumCPU.  We want to
 	// force GOMAXPROCS to remain at 1, in order to trigger a particular
@@ -121,12 +129,11 @@
 	server := InternalNew(naming.FixedRoutingID(0x55555555))
 	client := InternalNew(naming.FixedRoutingID(0xcccccccc))
 
-	serverID := security.FakePrivateID("server")
-	clientID := security.FakePrivateID("client")
-
+	clientID := newID("client")
+	serverID := newID("server")
 	// VCSecurityLevel is intentionally not provided to Listen - to test
 	// default behavior.
-	ln, ep, err := server.Listen("tcp", "localhost:0", vc.ListenerID(serverID))
+	ln, ep, err := server.Listen("tcp", "localhost:0", vc.FixedLocalID(serverID))
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -156,7 +163,7 @@
 	go func() {
 		// VCSecurityLevel is intentionally not provided to Dial - to
 		// test default behavior.
-		vc, err := client.Dial(ep, veyron2.LocalID(clientID))
+		vc, err := client.Dial(ep, vc.FixedLocalID(clientID))
 		if err != nil {
 			errs <- err
 			return
@@ -287,16 +294,14 @@
 }
 
 func TestSessionTicketCache(t *testing.T) {
-	serverID := vc.ListenerID(security.FakePrivateID("TestSessionTicketCacheServer"))
 	server := InternalNew(naming.FixedRoutingID(0x55555555))
-	_, ep, err := server.Listen("tcp", "localhost:0", serverID)
+	_, ep, err := server.Listen("tcp", "localhost:0", vc.FixedLocalID(newID("server")))
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	clientID := veyron2.LocalID(security.FakePrivateID("TestSessionTicketCacheClient"))
 	client := InternalNew(naming.FixedRoutingID(0xcccccccc))
-	if _, err = client.Dial(ep, clientID); err != nil {
+	if _, err = client.Dial(ep, vc.FixedLocalID(newID("TestSessionTicketCacheClient"))); err != nil {
 		t.Fatalf("Dial(%q) failed: %v", ep, err)
 	}
 
@@ -314,7 +319,7 @@
 
 	// Have the server read from each flow and write to rchan.
 	rchan := make(chan string)
-	ln, ep, err := server.Listen("tcp", "localhost:0", vc.ListenerID(security.FakePrivateID("server")))
+	ln, ep, err := server.Listen("tcp", "localhost:0", vc.FixedLocalID(newID("server")))
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -350,7 +355,7 @@
 	var vcs [nVCs]stream.VC
 	for i := 0; i < nVCs; i++ {
 		var err error
-		vcs[i], err = client.Dial(ep, veyron2.LocalID(security.FakePrivateID("client")))
+		vcs[i], err = client.Dial(ep, vc.FixedLocalID(newID("client")))
 		if err != nil {
 			t.Fatal(err)
 		}
@@ -399,7 +404,7 @@
 	}
 	go acceptLoop(ln)
 
-	// We'd like an enpoint that contains an address that's different
+	// We'd like an endpoint that contains an address that's different
 	// to the one used for the connection. In practice this is awkward
 	// to achieve since we don't want to listen on ":0" since that will
 	// annoy firewalls. Instead we listen on 127.0.0.1 and we fabricate an
@@ -465,7 +470,7 @@
 
 func runServer(argv []string) {
 	server := InternalNew(naming.FixedRoutingID(0x55555555))
-	_, ep, err := server.Listen("tcp", argv[0], vc.ListenerID(security.FakePrivateID("server")))
+	_, ep, err := server.Listen("tcp", argv[0], vc.FixedLocalID(newID("server")))
 	if err != nil {
 		fmt.Println(err)
 		return
diff --git a/runtimes/google/ipc/stream/proxy/proxy.go b/runtimes/google/ipc/stream/proxy/proxy.go
index e0022d5..433ad73 100644
--- a/runtimes/google/ipc/stream/proxy/proxy.go
+++ b/runtimes/google/ipc/stream/proxy/proxy.go
@@ -31,7 +31,7 @@
 type Proxy struct {
 	ln         net.Listener
 	rid        naming.RoutingID
-	id         security.PrivateID
+	id         vc.LocalID
 	mu         sync.RWMutex
 	servers    *servermap
 	processes  map[*process]struct{}
@@ -135,14 +135,17 @@
 	proxy := &Proxy{
 		ln:         ln,
 		rid:        rid,
-		id:         identity,
 		servers:    &servermap{m: make(map[naming.RoutingID]*server)},
 		processes:  make(map[*process]struct{}),
 		pubAddress: pubAddress,
 	}
+	if identity != nil {
+		proxy.id = vc.FixedLocalID(identity)
+	}
 	go proxy.listenLoop()
 	return proxy, nil
 }
+
 func (p *Proxy) listenLoop() {
 	proxyLog().Infof("Proxy listening on (%q, %q): %v", p.ln.Addr().Network(), p.ln.Addr(), p.Endpoint())
 	for {
@@ -313,7 +316,7 @@
 				p.routeCounters(process, m.Counters)
 				if vcObj != nil {
 					server := &server{Process: process, VC: vcObj}
-					go p.runServer(server, vcObj.HandshakeAcceptedVC(vc.ListenerID(p.id)))
+					go p.runServer(server, vcObj.HandshakeAcceptedVC(p.id))
 				}
 				break
 			}
diff --git a/runtimes/google/ipc/stream/proxy/proxy_test.go b/runtimes/google/ipc/stream/proxy/proxy_test.go
index c28e618..7b3e1dd 100644
--- a/runtimes/google/ipc/stream/proxy/proxy_test.go
+++ b/runtimes/google/ipc/stream/proxy/proxy_test.go
@@ -4,6 +4,7 @@
 	"bytes"
 	"fmt"
 	"io"
+	"reflect"
 	"strings"
 	"testing"
 
@@ -11,12 +12,21 @@
 	"veyron/runtimes/google/ipc/stream/manager"
 	"veyron/runtimes/google/ipc/stream/proxy"
 	"veyron/runtimes/google/ipc/stream/vc"
+	isecurity "veyron/runtimes/google/security"
 
 	"veyron2/ipc/stream"
 	"veyron2/naming"
 	"veyron2/security"
 )
 
+func newID(name string) security.PrivateID {
+	id, err := isecurity.NewPrivateID(name)
+	if err != nil {
+		panic(err)
+	}
+	return id
+}
+
 func TestProxy(t *testing.T) {
 	// Using "tcp4" instead of "tcp" because the latter can end up with
 	// IPv6 addresses and our Google Compute Engine integration test
@@ -110,7 +120,8 @@
 }
 
 func TestProxyIdentity(t *testing.T) {
-	proxy, err := proxy.New(naming.FixedRoutingID(0xbbbbbbbbbbbbbbbb), security.FakePrivateID("proxy"), "tcp4", "127.0.0.1:0", "")
+	proxyID := newID("proxy")
+	proxy, err := proxy.New(naming.FixedRoutingID(0xbbbbbbbbbbbbbbbb), proxyID, "tcp4", "127.0.0.1:0", "")
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -128,7 +139,7 @@
 	if err != nil {
 		t.Fatal(err)
 	}
-	if got, want := fmt.Sprintf("%v", flow.RemoteID()), "fake/proxy"; got != want {
+	if got, want := fmt.Sprintf("%v", flow.RemoteID()), fmt.Sprintf("%v", proxyID.PublicID()); got != want {
 		t.Errorf("Proxy has identity %q want %q", flow.RemoteID(), want)
 	}
 }
@@ -141,8 +152,11 @@
 
 	server := manager.InternalNew(naming.FixedRoutingID(0x5555555555555555))
 	defer server.Shutdown()
-	serverID := security.FakePrivateID("server")
-	ln, ep, err := server.Listen(proxy.Endpoint().Network(), proxy.Endpoint().String(), vc.ListenerID(serverID))
+	serverID := newID("server")
+	if err != nil {
+		t.Fatal(err)
+	}
+	ln, ep, err := server.Listen(proxy.Endpoint().Network(), proxy.Endpoint().String(), vc.FixedLocalID(serverID))
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -165,7 +179,7 @@
 	if err != nil {
 		t.Fatal(err)
 	}
-	if got, want := fmt.Sprintf("%v", flow.RemoteID()), "fake/server"; got != want {
+	if got, want := flow.RemoteID(), serverID.PublicID(); !reflect.DeepEqual(got, want) {
 		t.Errorf("Got %q want %q", got, want)
 	}
 }
diff --git a/runtimes/google/ipc/stream/vc/auth.go b/runtimes/google/ipc/stream/vc/auth.go
index 0378b31..0d4b094 100644
--- a/runtimes/google/ipc/stream/vc/auth.go
+++ b/runtimes/google/ipc/stream/vc/auth.go
@@ -21,15 +21,19 @@
 )
 
 // authenticateAsServer executes the authentication protocol at the server and
-// returns the identity of the client.
-func authenticateAsServer(conn io.ReadWriteCloser, serverID security.PrivateID, crypter crypto.Crypter) (clientID security.PublicID, err error) {
+// returns the identity of the client and server.
+func authenticateAsServer(conn io.ReadWriteCloser, localID LocalID, crypter crypto.Crypter) (clientID, serverID security.PublicID, err error) {
 	// The authentication protocol has the server doing the final read, so
 	// it is the one that closes the connection.
 	defer conn.Close()
-	if err = writeIdentity(conn, serverChannelEnd, crypter, serverID); err != nil {
+	if serverID, err = localID.AsServer(); err != nil {
 		return
 	}
-	return readIdentity(conn, clientChannelEnd, crypter)
+	if err = writeIdentity(conn, serverChannelEnd, crypter, localID, serverID); err != nil {
+		return
+	}
+	clientID, err = readIdentity(conn, clientChannelEnd, crypter)
+	return
 }
 
 // authenticateAsClient executes the authentication protocol at the client and
@@ -37,12 +41,17 @@
 //
 // If serverName is non-nil, the authentication protocol will be considered
 // successfull iff the server identity matches the provided regular expression.
-func authenticateAsClient(conn io.ReadWriteCloser, clientID security.PrivateID, crypter crypto.Crypter) (serverID security.PublicID, err error) {
+func authenticateAsClient(conn io.ReadWriteCloser, localID LocalID, crypter crypto.Crypter) (serverID, clientID security.PublicID, err error) {
 	defer conn.Close()
 	if serverID, err = readIdentity(conn, serverChannelEnd, crypter); err != nil {
 		return
 	}
-	err = writeIdentity(conn, clientChannelEnd, crypter, clientID)
+	// TODO(ashankar,ataly): Have the ability to avoid talking to a server we do not want to.
+	// Will require calling Authorize on the server id?
+	if clientID, err = localID.AsClient(serverID); err != nil {
+		return
+	}
+	err = writeIdentity(conn, clientChannelEnd, crypter, localID, clientID)
 	return
 }
 
@@ -64,7 +73,7 @@
 	errSingleCertificateRequired = errors.New("exactly one X.509 certificate chain with exactly one certificate is required")
 )
 
-func writeIdentity(w io.Writer, chEnd string, enc crypto.Encrypter, id security.PrivateID) error {
+func writeIdentity(w io.Writer, chEnd string, enc crypto.Encrypter, id LocalID, pub security.PublicID) error {
 	// Compute channel id - encrypted chEnd string
 	chid, err := enc.Encrypt(iobuf.NewSlice([]byte(chEnd)))
 	if err != nil {
@@ -74,7 +83,7 @@
 
 	// VOM-encode and encrypt the (public) identity.
 	var buf bytes.Buffer
-	if err := vom.NewEncoder(&buf).Encode(id.PublicID()); err != nil {
+	if err := vom.NewEncoder(&buf).Encode(pub); err != nil {
 		return err
 	}
 	eid, err := enc.Encrypt(iobuf.NewSlice(buf.Bytes()))
diff --git a/runtimes/google/ipc/stream/vc/init.go b/runtimes/google/ipc/stream/vc/init.go
new file mode 100644
index 0000000..a801353
--- /dev/null
+++ b/runtimes/google/ipc/stream/vc/init.go
@@ -0,0 +1,17 @@
+package vc
+
+import (
+	isecurity "veyron/runtimes/google/security"
+
+	"veyron2/security"
+	"veyron2/vlog"
+)
+
+var anonymousID security.PrivateID
+
+func init() {
+	var err error
+	if anonymousID, err = isecurity.NewPrivateID("anonymous"); err != nil {
+		vlog.Fatalf("could create anonymousID for IPCs: %s", err)
+	}
+}
diff --git a/runtimes/google/ipc/stream/vc/listener_test.go b/runtimes/google/ipc/stream/vc/listener_test.go
index a449242..a621b97 100644
--- a/runtimes/google/ipc/stream/vc/listener_test.go
+++ b/runtimes/google/ipc/stream/vc/listener_test.go
@@ -5,12 +5,24 @@
 	"testing"
 	"time"
 
+	isecurity "veyron/runtimes/google/security"
+
 	"veyron2/naming"
 	"veyron2/security"
 )
 
+var testID = newID("test")
+
 type noopFlow struct{}
 
+func newID(name string) security.PrivateID {
+	id, err := isecurity.NewPrivateID(name)
+	if err != nil {
+		panic(err)
+	}
+	return id
+}
+
 // net.Conn methods
 func (*noopFlow) Read([]byte) (int, error)           { return 0, nil }
 func (*noopFlow) Write([]byte) (int, error)          { return 0, nil }
@@ -27,8 +39,8 @@
 func (*noopFlow) SetWriteDeadline(t time.Time) error { return nil }
 
 // Other stream.Flow methods
-func (*noopFlow) LocalID() security.PublicID  { return security.FakePublicID("test") }
-func (*noopFlow) RemoteID() security.PublicID { return security.FakePublicID("test") }
+func (*noopFlow) LocalID() security.PublicID  { return testID.PublicID() }
+func (*noopFlow) RemoteID() security.PublicID { return testID.PublicID() }
 
 func TestListener(t *testing.T) {
 	ln := newListener()
diff --git a/runtimes/google/ipc/stream/vc/vc.go b/runtimes/google/ipc/stream/vc/vc.go
index 09d719d..7f70721 100644
--- a/runtimes/google/ipc/stream/vc/vc.go
+++ b/runtimes/google/ipc/stream/vc/vc.go
@@ -90,27 +90,48 @@
 	Helper       Helper
 }
 
-// ListenerIDOpt is the interface for providing an identity to an ipc.StreamListener.
-type ListenerIDOpt interface {
+// LocalID is the interface for providing a PrivateID and a PublicIDStore to
+// be used at the local end of VCs.
+type LocalID interface {
 	stream.ListenerOpt
-	// Identity returns the identity to be used by the ipc.StreamListener.
-	Identity() security.PrivateID
+	stream.VCOpt
+	// Sign signs an arbitrary length message (often the hash of a larger message)
+	// using a private key.
+	Sign(message []byte) (security.Signature, error)
+
+	// AsClient returns a PublicID to be used while authenticating as a client to the
+	// provided server as a client. An error is returned if no such PublicID can be returned.
+	AsClient(server security.PublicID) (security.PublicID, error)
+
+	// AsServer returns a PublicID to be used while authenticating as a server to other
+	// clients. An error is returned if no such PublicID can be returned.
+	AsServer() (security.PublicID, error)
+	IPCClientOpt()
+	IPCServerOpt()
 }
 
-// listenerIDOpt implements ListenerIDOpt.
-type listenerIDOpt struct {
-	id security.PrivateID
+// fixedLocalID implements vc.LocalID.
+type fixedLocalID struct {
+	security.PrivateID
 }
 
-func (opt *listenerIDOpt) Identity() security.PrivateID {
-	return opt.id
+func (f fixedLocalID) AsClient(security.PublicID) (security.PublicID, error) {
+	return f.PrivateID.PublicID(), nil
 }
 
-func (*listenerIDOpt) IPCStreamListenerOpt() {}
+func (f fixedLocalID) AsServer() (security.PublicID, error) {
+	return f.PrivateID.PublicID(), nil
+}
 
-// ListenerID provides an implementation of ListenerIDOpt with a fixed identity.
-func ListenerID(id security.PrivateID) ListenerIDOpt {
-	return &listenerIDOpt{id}
+func (fixedLocalID) IPCStreamListenerOpt() {}
+func (fixedLocalID) IPCStreamVCOpt()       {}
+func (fixedLocalID) IPCClientOpt()         {}
+func (fixedLocalID) IPCServerOpt()         {}
+
+// FixedLocalID creates a LocalID using the provided PrivateID. The
+// provided PrivateID must always be non-nil.
+func FixedLocalID(id security.PrivateID) LocalID {
+	return fixedLocalID{id}
 }
 
 // InternalNew creates a new VC, which implements the stream.VC interface.
@@ -347,13 +368,13 @@
 // authentication etc.) under the assumption that the VC was initiated by the
 // local process (i.e., the local process "Dial"ed to create the VC).
 func (vc *VC) HandshakeDialedVC(opts ...stream.VCOpt) error {
-	var localID security.PrivateID
+	var localID LocalID
 	var tlsSessionCache tls.ClientSessionCache
 	var securityLevel veyron2.VCSecurityLevel
 	for _, o := range opts {
 		switch v := o.(type) {
-		case veyron2.LocalIDOpt:
-			localID = v.PrivateID
+		case LocalID:
+			localID = v
 		case veyron2.VCSecurityLevel:
 			securityLevel = v
 		case crypto.TLSClientSessionCache:
@@ -362,7 +383,9 @@
 	}
 	switch securityLevel {
 	case veyron2.VCSecurityConfidential:
-		localID = anonymousIfNilPrivateID(localID)
+		if localID == nil {
+			localID = FixedLocalID(anonymousID)
+		}
 	case veyron2.VCSecurityNone:
 		return nil
 	default:
@@ -394,7 +417,7 @@
 	if err != nil {
 		return vc.err(fmt.Errorf("failed to create a Flow for authentication: %v", err))
 	}
-	remoteID, err := authenticateAsClient(authConn, localID, crypter)
+	rID, lID, err := authenticateAsClient(authConn, localID, crypter)
 	if err != nil {
 		return vc.err(fmt.Errorf("authentication failed: %v", err))
 	}
@@ -403,11 +426,11 @@
 	vc.handshakeFID = handshakeFID
 	vc.authFID = authFID
 	vc.crypter = crypter
-	vc.localID = localID.PublicID()
-	vc.remoteID = remoteID
+	vc.localID = lID
+	vc.remoteID = rID
 	vc.mu.Unlock()
 
-	vlog.VI(1).Infof("Client VC %v authenticated. RemoteID:%v LocalID:%v", vc, remoteID, localID)
+	vlog.VI(1).Infof("Client VC %v authenticated. RemoteID:%v LocalID:%v", vc, rID, lID)
 	return nil
 }
 
@@ -430,12 +453,12 @@
 		result <- HandshakeResult{ln, err}
 		return result
 	}
-	var localID security.PrivateID
+	var localID LocalID
 	var securityLevel veyron2.VCSecurityLevel
 	for _, o := range opts {
 		switch v := o.(type) {
-		case ListenerIDOpt:
-			localID = v.Identity()
+		case LocalID:
+			localID = v
 		case veyron2.VCSecurityLevel:
 			securityLevel = v
 		}
@@ -450,7 +473,9 @@
 	vc.helper.AddReceiveBuffers(vc.VCI(), SharedFlowID, DefaultBytesBufferedPerFlow)
 	switch securityLevel {
 	case veyron2.VCSecurityConfidential:
-		localID = anonymousIfNilPrivateID(localID)
+		if localID == nil {
+			localID = FixedLocalID(anonymousID)
+		}
 	case veyron2.VCSecurityNone:
 		return finish(ln, nil)
 	default:
@@ -493,7 +518,7 @@
 		vc.mu.Lock()
 		vc.authFID = vc.findFlowLocked(authConn)
 		vc.mu.Unlock()
-		remoteID, err := authenticateAsServer(authConn, localID, crypter)
+		rID, lID, err := authenticateAsServer(authConn, localID, crypter)
 		if err != nil {
 			sendErr(fmt.Errorf("Authentication failed: %v", err))
 			return
@@ -501,12 +526,12 @@
 
 		vc.mu.Lock()
 		vc.crypter = crypter
-		vc.localID = localID.PublicID()
-		vc.remoteID = remoteID
+		vc.localID = lID
+		vc.remoteID = rID
 		close(vc.acceptHandshakeDone)
 		vc.acceptHandshakeDone = nil
 		vc.mu.Unlock()
-		vlog.VI(1).Infof("Server VC %v authenticated. RemoteID:%v LocalID:%v", vc, remoteID, localID)
+		vlog.VI(1).Infof("Server VC %v authenticated. RemoteID:%v LocalID:%v", vc, rID, lID)
 		result <- HandshakeResult{ln, nil}
 	}()
 	return result
@@ -641,15 +666,5 @@
 	if id != nil {
 		return id
 	}
-	// TODO(ashankar): Have an Anonymous identity that also encodes the
-	// public key so that changing the keys in code doesn't prevent new
-	// binaries from talking to old ones.
-	return security.FakePublicID("anonymous")
-}
-
-func anonymousIfNilPrivateID(id security.PrivateID) security.PrivateID {
-	if id != nil {
-		return id
-	}
-	return security.FakePrivateID("anonymous")
+	return anonymousID.PublicID()
 }
diff --git a/runtimes/google/ipc/stream/vc/vc_test.go b/runtimes/google/ipc/stream/vc/vc_test.go
index ae93fd8..f1b1e2e 100644
--- a/runtimes/google/ipc/stream/vc/vc_test.go
+++ b/runtimes/google/ipc/stream/vc/vc_test.go
@@ -4,6 +4,7 @@
 
 import (
 	"bytes"
+	"fmt"
 	"io"
 	"net"
 	"reflect"
@@ -18,6 +19,7 @@
 	"veyron/runtimes/google/lib/bqueue"
 	"veyron/runtimes/google/lib/bqueue/drrqueue"
 	"veyron/runtimes/google/lib/iobuf"
+	isecurity "veyron/runtimes/google/security"
 
 	"veyron2"
 	"veyron2/ipc/stream"
@@ -35,10 +37,18 @@
 )
 
 var (
-	clientID = security.FakePrivateID("client")
-	serverID = security.FakePrivateID("server")
+	clientID = newID("client")
+	serverID = newID("server")
 )
 
+func newID(name string) security.PrivateID {
+	id, err := isecurity.NewPrivateID(name)
+	if err != nil {
+		panic(err)
+	}
+	return id
+}
+
 // testFlowEcho writes a random string of 'size' bytes on the flow and then
 // ensures that the same string is read back.
 func testFlowEcho(t *testing.T, flow stream.Flow, size int) {
@@ -73,30 +83,43 @@
 	}
 }
 
+func matchID(got, want security.PublicID) error {
+	if want == nil {
+		if got.Names() != nil {
+			return fmt.Errorf("got identity with names: %v, want one with names: nil", got.Names())
+		}
+		return nil
+	}
+	if g, w := got.Names(), want.Names(); !reflect.DeepEqual(got.Names(), want.Names()) {
+		return fmt.Errorf("got identity with names: %v, want one with names: %v", g, w)
+	}
+	if g, w := got.PublicKey(), want.PublicKey(); !reflect.DeepEqual(got.PublicKey(), want.PublicKey()) {
+		return fmt.Errorf("got identity with public key: %v, want one with public key: %v", g, w)
+	}
+	return nil
+}
+
 func testHandshake(t *testing.T, security veyron2.VCSecurityLevel, localID, remoteID security.PublicID) {
 	h, vc := New(security)
 	flow, err := vc.Connect()
 	if err != nil {
 		t.Fatal(err)
 	}
-	lID := flow.LocalID()
-	if !reflect.DeepEqual(lID.Names(), localID.Names()) {
-		t.Errorf("Client says LocalID is %q want %q", lID, localID)
+	lID, rID := flow.LocalID(), flow.RemoteID()
+	if (lID == nil) || (rID == nil) {
+		t.Error("Either the LocalID or the RemoteID of the flow is nil")
 	}
-	rID := flow.RemoteID()
-	if !reflect.DeepEqual(rID.Names(), remoteID.Names()) {
-		t.Errorf("Client says RemoteID is %q want %q", rID, remoteID)
+	if err := matchID(lID, localID); err != nil {
+		t.Errorf("Client identity mismatch: %s", err)
 	}
-	if g, w := lID.PublicKey(), localID.PublicKey(); !reflect.DeepEqual(g, w) {
-		t.Errorf("Client identity public key mismatch. Got %v want %v", g, w)
-	}
-	if g, w := rID.PublicKey(), remoteID.PublicKey(); !reflect.DeepEqual(g, w) {
-		t.Errorf("Server identity public key mismatch. Got %v want %v", g, w)
+	if err := matchID(rID, remoteID); err != nil {
+		t.Errorf("Server identity mismatch: %s", err)
 	}
 	h.Close()
 }
+
 func TestHandshake(t *testing.T) {
-	testHandshake(t, SecurityNone, security.FakePublicID("anonymous"), security.FakePublicID("anonymous"))
+	testHandshake(t, SecurityNone, nil, nil)
 }
 func TestHandshakeTLS(t *testing.T) {
 	testHandshake(t, SecurityTLS, clientID.PublicID(), serverID.PublicID())
@@ -273,8 +296,8 @@
 	go clientH.pipeLoop(serverH.VC)
 	go serverH.pipeLoop(clientH.VC)
 
-	c := serverH.VC.HandshakeAcceptedVC(security, vc.ListenerID(serverID))
-	if err := clientH.VC.HandshakeDialedVC(security, veyron2.LocalID(clientID)); err != nil {
+	c := serverH.VC.HandshakeAcceptedVC(security, vc.FixedLocalID(serverID))
+	if err := clientH.VC.HandshakeDialedVC(security, vc.FixedLocalID(clientID)); err != nil {
 		panic(err)
 	}
 	hr := <-c
diff --git a/runtimes/google/ipc/stream/vif/vif_test.go b/runtimes/google/ipc/stream/vif/vif_test.go
index ab377f4..a743542 100644
--- a/runtimes/google/ipc/stream/vif/vif_test.go
+++ b/runtimes/google/ipc/stream/vif/vif_test.go
@@ -18,14 +18,21 @@
 	"veyron/runtimes/google/ipc/stream/vc"
 	"veyron/runtimes/google/ipc/stream/vif"
 	iversion "veyron/runtimes/google/ipc/version"
+	isecurity "veyron/runtimes/google/security"
 
-	"veyron2"
 	"veyron2/ipc/stream"
 	"veyron2/ipc/version"
 	"veyron2/naming"
-	"veyron2/security"
 )
 
+func newLocalID(name string) vc.LocalID {
+	id, err := isecurity.NewPrivateID(name)
+	if err != nil {
+		panic(err)
+	}
+	return vc.FixedLocalID(id)
+}
+
 func TestSingleFlowCreatedAtClient(t *testing.T) {
 	client, server := NewClientServer()
 	defer client.Close()
@@ -415,8 +422,7 @@
 	if client, err = vif.InternalNewDialedVIF(c1, naming.FixedRoutingID(0xc), clientVersions); err != nil {
 		panic(err)
 	}
-	serverID := vc.ListenerID(security.FakePrivateID("server"))
-	if server, err = vif.InternalNewAcceptedVIF(c2, naming.FixedRoutingID(0x5), serverVersions, serverID); err != nil {
+	if server, err = vif.InternalNewAcceptedVIF(c2, naming.FixedRoutingID(0x5), serverVersions, newLocalID("server")); err != nil {
 		panic(err)
 	}
 	return
@@ -456,8 +462,7 @@
 	scChan := make(chan stream.Connector)
 	errChan := make(chan error)
 	go func() {
-		clientID := veyron2.LocalID(security.FakePrivateID("client"))
-		vc, err := client.Dial(ep, clientID)
+		vc, err := client.Dial(ep, newLocalID("client"))
 		errChan <- err
 		vcChan <- vc
 	}()
diff --git a/runtimes/google/ipc/testutil_test.go b/runtimes/google/ipc/testutil_test.go
index e726013..f990ca7 100644
--- a/runtimes/google/ipc/testutil_test.go
+++ b/runtimes/google/ipc/testutil_test.go
@@ -5,7 +5,10 @@
 	"testing"
 
 	_ "veyron/lib/testutil"
+	"veyron/runtimes/google/ipc/stream/vc"
+	isecurity "veyron/runtimes/google/security"
 
+	"veyron2/ipc"
 	"veyron2/security"
 	"veyron2/verror"
 )
@@ -38,19 +41,13 @@
 	}
 }
 
-// listenerIDOpt implements vc.ListenerIDOpt and veyron2/ipc.ServerOpt.
-type listenerIDOpt struct {
-	id security.PrivateID
+func newID(name string) security.PrivateID {
+	id, err := isecurity.NewPrivateID(name)
+	if err != nil {
+		panic(err)
+	}
+	return id
 }
 
-func (opt *listenerIDOpt) Identity() security.PrivateID {
-	return opt.id
-}
-
-func (*listenerIDOpt) IPCStreamListenerOpt() {}
-
-func (*listenerIDOpt) IPCServerOpt() {}
-
-func listenerID(id security.PrivateID) *listenerIDOpt {
-	return &listenerIDOpt{id}
-}
+var _ ipc.ClientOpt = vc.FixedLocalID(newID("irrelevant"))
+var _ ipc.ServerOpt = vc.FixedLocalID(newID("irrelevant"))
diff --git a/runtimes/google/rt/ipc.go b/runtimes/google/rt/ipc.go
index 2b54fad..150bfdf 100644
--- a/runtimes/google/rt/ipc.go
+++ b/runtimes/google/rt/ipc.go
@@ -1,22 +1,81 @@
 package rt
 
 import (
+	"errors"
 	"fmt"
 
 	iipc "veyron/runtimes/google/ipc"
 	imanager "veyron/runtimes/google/ipc/stream/manager"
+	"veyron/runtimes/google/ipc/stream/vc"
 
 	"veyron2"
 	"veyron2/context"
 	"veyron2/ipc"
 	"veyron2/ipc/stream"
 	"veyron2/naming"
+	"veyron2/security"
 )
 
+// fixedPublicIDStore implements security.PublicIDStore. It embeds a (fixed) PublicID that
+// is both the default and the PublicID to be used for any peer. Adding a new PublicID
+// to the store is disallowed, and setting the default principal-pattern is a no-op.
+type fixedPublicIDStore struct {
+	id security.PublicID
+}
+
+func (fixedPublicIDStore) Add(id security.PublicID, peerPattern security.PrincipalPattern) error {
+	return errors.New("adding new PublicIDs is disallowed for this PublicIDStore")
+}
+
+func (s fixedPublicIDStore) ForPeer(peer security.PublicID) (security.PublicID, error) {
+	return s.id, nil
+}
+
+func (s fixedPublicIDStore) DefaultPublicID() (security.PublicID, error) {
+	return s.id, nil
+}
+
+func (fixedPublicIDStore) SetDefaultPrincipalPattern(pattern security.PrincipalPattern) {}
+
+// localID is an option for passing a PrivateID and PublicIDStore
+// to a server or client.
+type localID struct {
+	id    security.PrivateID
+	store security.PublicIDStore
+}
+
+func (lID *localID) Sign(message []byte) (security.Signature, error) {
+	return lID.id.Sign(message)
+}
+
+func (lID *localID) AsClient(server security.PublicID) (security.PublicID, error) {
+	return lID.store.ForPeer(server)
+}
+
+func (lID *localID) AsServer() (security.PublicID, error) {
+	return lID.store.DefaultPublicID()
+}
+
+func (*localID) IPCClientOpt()         {}
+func (*localID) IPCStreamVCOpt()       {}
+func (*localID) IPCServerOpt()         {}
+func (*localID) IPCStreamListenerOpt() {}
+
+// newLocalID returns a localID embedding the runtime's PrivateID and a fixed
+// PublicIDStore constructed from the provided PublicID or the runtiume's PublicIDStore
+// if the provided PublicID is nil.
+func (rt *vrt) newLocalID(id security.PublicID) vc.LocalID {
+	lID := &localID{id: rt.id, store: rt.store}
+	if id != nil {
+		lID.store = fixedPublicIDStore{id}
+	}
+	return lID
+}
+
 func (rt *vrt) NewClient(opts ...ipc.ClientOpt) (ipc.Client, error) {
 	sm := rt.sm
 	ns := rt.ns
-	cIDOpt := veyron2.LocalID(rt.id.Identity())
+	var id security.PublicID
 	var otherOpts []ipc.ClientOpt
 	for _, opt := range opts {
 		switch topt := opt.(type) {
@@ -25,14 +84,14 @@
 		case veyron2.NamespaceOpt:
 			ns = topt.Namespace
 		case veyron2.LocalIDOpt:
-			cIDOpt = topt
+			id = topt.PublicID
 		default:
 			otherOpts = append(otherOpts, opt)
 		}
 	}
-	if cIDOpt.PrivateID != nil {
-		otherOpts = append(otherOpts, cIDOpt)
-	}
+	// Add the option that provides the local identity to the client.
+	otherOpts = append(otherOpts, rt.newLocalID(id))
+
 	return iipc.InternalNewClient(sm, ns, otherOpts...)
 }
 
@@ -68,17 +127,20 @@
 	// Start the http debug server exactly once for this runtime.
 	rt.startHTTPDebugServerOnce()
 	ns := rt.ns
+	var id security.PublicID
 	var otherOpts []ipc.ServerOpt
 	for _, opt := range opts {
 		switch topt := opt.(type) {
 		case veyron2.NamespaceOpt:
 			ns = topt
+		case veyron2.LocalIDOpt:
+			id = topt.PublicID
 		default:
 			otherOpts = append(otherOpts, opt)
 		}
 	}
-	// Add the option that provides the identity currently used by the runtime.
-	otherOpts = append(otherOpts, rt.id)
+	// Add the option that provides the local identity to the server.
+	otherOpts = append(otherOpts, rt.newLocalID(id))
 
 	ctx := rt.NewContext()
 	return iipc.InternalNewServer(ctx, sm, ns, otherOpts...)
diff --git a/runtimes/google/rt/ipc_test.go b/runtimes/google/rt/ipc_test.go
new file mode 100644
index 0000000..1822541
--- /dev/null
+++ b/runtimes/google/rt/ipc_test.go
@@ -0,0 +1,188 @@
+package rt_test
+
+import (
+	"fmt"
+	"reflect"
+	"sort"
+	"testing"
+	"time"
+
+	_ "veyron/lib/testutil"
+	isecurity "veyron/runtimes/google/security"
+
+	"veyron2"
+	"veyron2/ipc"
+	"veyron2/naming"
+	"veyron2/rt"
+	"veyron2/security"
+)
+
+type testService struct{}
+
+func (*testService) EchoIDs(call ipc.ServerCall) (server, client []string) {
+	return call.LocalID().Names(), call.RemoteID().Names()
+}
+
+type S []string
+
+func newID(name string) security.PrivateID {
+	id, err := isecurity.NewPrivateID(name)
+	if err != nil {
+		panic(err)
+	}
+	return id
+}
+
+func bless(blessor security.PrivateID, blessee security.PublicID, name string) security.PublicID {
+	blessedID, err := blessor.Bless(blessee, name, 5*time.Minute, nil)
+	if err != nil {
+		panic(err)
+	}
+	return blessedID
+}
+
+func add(store security.PublicIDStore, id security.PublicID, pattern security.PrincipalPattern) {
+	if err := store.Add(id, pattern); err != nil {
+		panic(err)
+	}
+}
+
+func call(r veyron2.Runtime, client ipc.Client, name string) (clientNames, serverNames []string, err error) {
+	c, err := client.StartCall(r.NewContext(), name, "EchoIDs", nil)
+	if err != nil {
+		return nil, nil, err
+	}
+	if err := c.Finish(&serverNames, &clientNames); err != nil {
+		return nil, nil, err
+	}
+	sort.Strings(clientNames)
+	sort.Strings(serverNames)
+	return
+}
+
+func TestClientServerIDs(t *testing.T) {
+	stopServer := func(server ipc.Server) {
+		if err := server.Stop(); err != nil {
+			t.Fatalf("server.Stop failed: %s", err)
+		}
+	}
+	var (
+		self   = newID("self")
+		google = newID("google")
+		veyron = newID("veyron")
+
+		googleGmailService   = bless(google, self.PublicID(), "gmail")
+		googleYoutubeService = bless(google, self.PublicID(), "youtube")
+		veyronService        = bless(veyron, self.PublicID(), "service")
+		googleGmailClient    = bless(google, self.PublicID(), "gmailClient")
+		googleYoutubeClient  = bless(google, self.PublicID(), "youtubeClient")
+		veyronClient         = bless(veyron, self.PublicID(), "client")
+	)
+	isecurity.TrustIdentityProviders(google)
+	isecurity.TrustIdentityProviders(veyron)
+
+	serverR, err := rt.New(veyron2.RuntimeID(self))
+	if err != nil {
+		t.Fatalf("rt.New() failed: %s", err)
+	}
+	clientR, err := rt.New(veyron2.RuntimeID(self))
+	if err != nil {
+		t.Fatalf("rt.New() failed: %s", err)
+	}
+
+	// Add PublicIDs for running "google/gmail" and "google/youtube" services to
+	// serverR's PublicIDStore. Since these PublicIDs are meant to be by
+	// servers only they are tagged with "".
+	add(serverR.PublicIDStore(), googleGmailService, "")
+	add(serverR.PublicIDStore(), googleYoutubeService, "")
+	// Add PublicIDs for communicating the "google/gmail" and "google/youtube" services
+	// to the clientR's PublicIDStore.
+	add(clientR.PublicIDStore(), googleGmailClient, "google/*")
+	add(clientR.PublicIDStore(), googleYoutubeClient, "google/youtube")
+
+	type testcase struct {
+		server, client                   security.PublicID
+		defaultPattern                   security.PrincipalPattern
+		wantServerNames, wantClientNames []string
+	}
+	tests := []testcase{
+		{
+			defaultPattern:  security.AllPrincipals,
+			wantServerNames: S{"self", "google/gmail", "google/youtube"},
+			wantClientNames: S{"self", "google/gmailClient", "google/youtubeClient"},
+		},
+		{
+			defaultPattern:  "google/gmail",
+			wantServerNames: S{"google/gmail"},
+			wantClientNames: S{"self", "google/gmailClient"},
+		},
+		{
+			defaultPattern:  "google/youtube",
+			wantServerNames: S{"google/youtube"},
+			wantClientNames: S{"self", "google/gmailClient", "google/youtubeClient"},
+		},
+		{
+			server:          veyronService,
+			defaultPattern:  security.AllPrincipals,
+			wantServerNames: S{"veyron/service"},
+			wantClientNames: S{"self"},
+		},
+		{
+			client:          veyronClient,
+			defaultPattern:  security.AllPrincipals,
+			wantServerNames: S{"self", "google/gmail", "google/youtube"},
+			wantClientNames: S{"veyron/client"},
+		},
+		{
+			server:          veyronService,
+			client:          veyronClient,
+			defaultPattern:  security.AllPrincipals,
+			wantServerNames: S{"veyron/service"},
+			wantClientNames: S{"veyron/client"},
+		},
+	}
+	name := func(t testcase) string {
+		return fmt.Sprintf("TestCase{clientPublicIDStore: %v, serverPublicIDStore: %v, client option: %v, server option: %v}", clientR.PublicIDStore(), serverR.PublicIDStore(), t.client, t.server)
+	}
+	for _, test := range tests {
+		serverR.PublicIDStore().SetDefaultPrincipalPattern(test.defaultPattern)
+		server, err := serverR.NewServer(veyron2.LocalID(test.server))
+		if err != nil {
+			t.Errorf("serverR.NewServer(...) failed: %s", err)
+			continue
+		}
+		endpoint, err := server.Listen("tcp", "127.0.0.1:0")
+		if err != nil {
+			t.Errorf("error listening to service: ", err)
+			continue
+		}
+		defer stopServer(server)
+		if err := server.Serve("", ipc.SoloDispatcher(&testService{}, security.NewACLAuthorizer(security.ACL{security.AllPrincipals: security.AllLabels}))); err != nil {
+			t.Errorf("error serving service: ", err)
+			continue
+		}
+
+		client, err := clientR.NewClient(veyron2.LocalID(test.client))
+		if err != nil {
+			t.Errorf("clientR.NewClient(...) failed: %s", err)
+			continue
+		}
+		defer client.Close()
+
+		clientNames, serverNames, err := call(clientR, client, naming.JoinAddressName(fmt.Sprintf("%v", endpoint), ""))
+		if err != nil {
+			t.Errorf("IPC failed: %s", err)
+			continue
+		}
+		sort.Strings(test.wantClientNames)
+		sort.Strings(test.wantServerNames)
+		if !reflect.DeepEqual(clientNames, test.wantClientNames) {
+			t.Errorf("TestCase: %s, Got clientNames: %v, want: %v", name(test), clientNames, test.wantClientNames)
+			continue
+		}
+		if !reflect.DeepEqual(serverNames, test.wantServerNames) {
+			t.Errorf("TestCase: %s, Got serverNames: %v, want: %v", name(test), serverNames, test.wantServerNames)
+			continue
+		}
+	}
+}
diff --git a/runtimes/google/rt/rt.go b/runtimes/google/rt/rt.go
index 805adf4..3f4f429 100644
--- a/runtimes/google/rt/rt.go
+++ b/runtimes/google/rt/rt.go
@@ -15,6 +15,7 @@
 	"veyron2/ipc/stream"
 	"veyron2/naming"
 	"veyron2/product"
+	"veyron2/security"
 	"veyron2/vlog"
 )
 
@@ -23,7 +24,8 @@
 	sm      stream.Manager
 	ns      naming.Namespace
 	signals chan os.Signal
-	id      *currentIDOpt
+	id      security.PrivateID
+	store   security.PublicIDStore
 	client  ipc.Client
 	mgmt    *mgmtImpl
 	debug   debugServer
@@ -61,13 +63,13 @@
 func (rt *vrt) init(opts ...veyron2.ROpt) error {
 	flag.Parse()
 	rt.initHTTPDebugServer()
-	rt.id = &currentIDOpt{}
 	nsRoots := []string{}
-
 	for _, o := range opts {
 		switch v := o.(type) {
-		case veyron2.LocalIDOpt:
-			rt.id.setIdentity(v.PrivateID)
+		case veyron2.RuntimeIDOpt:
+			rt.id = v.PrivateID
+		case veyron2.RuntimePublicIDStoreOpt:
+			rt.store = v
 		case veyron2.ProductOpt:
 			rt.product = v.T
 		case veyron2.NamespaceRoots:
@@ -123,11 +125,11 @@
 		return err
 	}
 
-	if err = rt.initIdentity(); err != nil {
+	if err = rt.initSecurity(); err != nil {
 		return err
 	}
 
-	if rt.client, err = rt.NewClient(veyron2.LocalID(rt.id.Identity())); err != nil {
+	if rt.client, err = rt.NewClient(); err != nil {
 		return err
 	}
 
diff --git a/runtimes/google/rt/security.go b/runtimes/google/rt/security.go
index b25b95d..b14babf 100644
--- a/runtimes/google/rt/security.go
+++ b/runtimes/google/rt/security.go
@@ -4,7 +4,6 @@
 	"fmt"
 	"os"
 	"os/user"
-	"sync"
 
 	isecurity "veyron/runtimes/google/security"
 
@@ -12,61 +11,53 @@
 	"veyron2/vlog"
 )
 
-// currentIDOpt is an option that can be used to pass the identity currently used
-// by the runtime to an ipc.Server or ipc.StreamListener.
-type currentIDOpt struct {
-	id security.PrivateID
-	mu sync.RWMutex
-}
-
-func (id *currentIDOpt) Identity() security.PrivateID {
-	id.mu.RLock()
-	defer id.mu.RUnlock()
-	return id.id
-}
-
-func (id *currentIDOpt) setIdentity(newID security.PrivateID) {
-	// TODO(ataly): Whenever setIdentity is invoked on the identity currently used by
-	// the runtime, the following changes must also be performed:
-	// * the identity provider of the new identity must be tursted.
-	// * the default client used by the runtime must also be replaced with
-	//   a client using the new identity.
-	id.mu.Lock()
-	defer id.mu.Unlock()
-	id.id = newID
-}
-
-func (*currentIDOpt) IPCServerOpt() {}
-
-func (*currentIDOpt) IPCStreamListenerOpt() {}
-
 func (rt *vrt) NewIdentity(name string) (security.PrivateID, error) {
 	return isecurity.NewPrivateID(name)
 }
 
 func (rt *vrt) Identity() security.PrivateID {
-	return rt.id.Identity()
+	return rt.id
+}
+
+func (rt *vrt) PublicIDStore() security.PublicIDStore {
+	return rt.store
+}
+
+func (rt *vrt) initSecurity() error {
+	if err := rt.initIdentity(); err != nil {
+		return err
+	}
+	if rt.store == nil {
+		rt.store = isecurity.NewPublicIDStore()
+		// TODO(ashankar,ataly): What should the tag for the runtime's PublicID in the
+		// runtime's store be? Below we use security.AllPrincipals but this means that
+		// the PublicID *always* gets used for any peer. This may not be desirable.
+		if err := rt.store.Add(rt.id.PublicID(), security.AllPrincipals); err != nil {
+			return fmt.Errorf("could not initialize a PublicIDStore for the runtime: %s", err)
+		}
+	}
+	// Always trust our own identity providers.
+	// TODO(ataly, ashankar): We should trust the identity providers of all PublicIDs in the store.
+	trustIdentityProviders(rt.id)
+	return nil
 }
 
 func (rt *vrt) initIdentity() error {
-	if rt.id.Identity() == nil {
-		var id security.PrivateID
-		var err error
-		if file := os.Getenv("VEYRON_IDENTITY"); len(file) > 0 {
-			if id, err = loadIdentityFromFile(file); err != nil {
-				return fmt.Errorf("Could not load identity from %q: %v", file, err)
-			}
-		} else {
-			name := defaultIdentityName()
-			vlog.VI(2).Infof("No identity provided to the runtime, minting one for %q", name)
-			if id, err = rt.NewIdentity(name); err != nil {
-				return fmt.Errorf("Could not create new identity: %v", err)
-			}
-		}
-		rt.id.setIdentity(id)
+	if rt.id != nil {
+		return nil
 	}
-	// Always trust our own identity providers.
-	trustIdentityProviders(rt.id.Identity())
+	var err error
+	if file := os.Getenv("VEYRON_IDENTITY"); len(file) > 0 {
+		if rt.id, err = loadIdentityFromFile(file); err != nil || rt.id == nil {
+			return fmt.Errorf("Could not load identity from %q: %v", file, err)
+		}
+	} else {
+		name := defaultIdentityName()
+		vlog.VI(2).Infof("No identity provided to the runtime, minting one for %q", name)
+		if rt.id, err = rt.NewIdentity(name); err != nil || rt.id == nil {
+			return fmt.Errorf("Could not create new identity: %v", err)
+		}
+	}
 	return nil
 }
 
diff --git a/services/mounttable/lib/mounttable_test.go b/services/mounttable/lib/mounttable_test.go
index e961bc5..cfea7f8 100644
--- a/services/mounttable/lib/mounttable_test.go
+++ b/services/mounttable/lib/mounttable_test.go
@@ -29,9 +29,9 @@
 }
 
 var (
-	rootID  = veyron2.LocalID(security.FakePrivateID("root"))
-	bobID   = veyron2.LocalID(security.FakePrivateID("bob"))
-	aliceID = veyron2.LocalID(security.FakePrivateID("alice"))
+	rootID  = veyron2.LocalID(security.FakePublicID("root"))
+	bobID   = veyron2.LocalID(security.FakePublicID("bob"))
+	aliceID = veyron2.LocalID(security.FakePublicID("alice"))
 )
 
 const ttlSecs = 60 * 60
@@ -169,7 +169,12 @@
 }
 
 func newMT(t *testing.T, acl string) (ipc.Server, string) {
-	r := rt.Init()
+	// It is necessary for the private key of runtime's identity and
+	// the public key of the LocalIDOpts passed to clients to correspond.
+	// Since the LocalIDOpts are FakePublicIDs, we initialize the runtime
+	// below with a FakePrivateID. (Note all FakePublicIDs and FakePrivateIDs
+	// always have corresponding public and private keys respectively.)
+	r := rt.Init(veyron2.RuntimeID(security.FakePrivateID("irrelevant")))
 	server, err := r.NewServer(veyron2.ServesMountTableOpt(true))
 	if err != nil {
 		boom(t, "r.NewServer: %s", err)
diff --git a/services/mounttable/lib/neighborhood_test.go b/services/mounttable/lib/neighborhood_test.go
index 471edba..4c5bbb3 100644
--- a/services/mounttable/lib/neighborhood_test.go
+++ b/services/mounttable/lib/neighborhood_test.go
@@ -25,7 +25,7 @@
 
 func TestNeighborhood(t *testing.T) {
 	r := rt.Init()
-	id := veyron2.LocalID(rt.R().Identity())
+	id := veyron2.LocalID(rt.R().Identity().PublicID())
 	vlog.Infof("TestNeighborhood")
 	server, err := r.NewServer()
 	if err != nil {
diff --git a/services/wsprd/wspr/wspr_test.go b/services/wsprd/wspr/wspr_test.go
index d03d7fa..fd697ee 100644
--- a/services/wsprd/wspr/wspr_test.go
+++ b/services/wsprd/wspr/wspr_test.go
@@ -410,9 +410,8 @@
 	defer rt.mounttableServer.Stop()
 	defer rt.proxyServer.Shutdown()
 	defer rt.wsp.cleanup()
-
 	if err != nil {
-		t.Errorf("could not serve server %v", err)
+		t.Fatalf("could not serve server %v", err)
 	}
 
 	if len(rt.writer.stream) != 1 {