Merge "websocket: Do not serialize accepts."
diff --git a/cmd/principal/doc.go b/cmd/principal/doc.go
index 93a5977..9ffcd6d 100644
--- a/cmd/principal/doc.go
+++ b/cmd/principal/doc.go
@@ -223,7 +223,7 @@
 
 The principal dump flags are:
  -s=false
-   If true, show a only the default blessing names
+   If true, show only the default blessing names
 
 Principal Dumpblessings
 
@@ -389,6 +389,7 @@
 The principal get commands are:
    default         Return blessings marked as default
    forpeer         Return blessings marked for the provided peer
+   publickey       Prints the public key of the principal.
    recognizedroots Return recognized blessings, and their associated public key.
    peermap         Shows the map from peer pattern to which blessing name to
                    present.
@@ -396,22 +397,29 @@
 Principal Get Default
 
 Returns blessings that are marked as default in the BlessingStore specified by
-the environment that this tool is running in.
+the environment that this tool is running in. Providing --names will print the
+default blessings' chain names. Providing --rootkey <chain_name> will print the
+root key of the certificate chain with chain_name. Providing --caveats
+<chain_name> will print the caveats on the certificate chain with chain_name.
 
 Usage:
    principal get default [flags]
 
 The principal get default flags are:
- -name=false
+ -caveats=
+   Shows the caveats on the provided certificate chain name.
+ -names=false
    If true, shows the value of the blessing name to be presented to the peer
- -rootkey=false
-   If true, shows the value of the root key of the certificate chain to be
-   presented to the peer
+ -rootkey=
+   Shows the value of the root key of the provided certificate chain name.
 
 Principal Get Forpeer
 
 Returns blessings that are marked for the provided peer in the BlessingStore
-specified by the environment that this tool is running in.
+specified by the environment that this tool is running in. Providing --names
+will print the blessings' chain names. Providing --rootkey <chain_name> will
+print the root key of the certificate chain with chain_name. Providing --caveats
+<chain_name> will print the caveats on the certificate chain with chain_name.
 
 Usage:
    principal get forpeer [flags] [<peer_1> ... <peer_k>]
@@ -423,11 +431,20 @@
 the "..." pattern).
 
 The principal get forpeer flags are:
- -name=false
+ -caveats=
+   Shows the caveats on the provided certificate chain name.
+ -names=false
    If true, shows the value of the blessing name to be presented to the peer
- -rootkey=false
-   If true, shows the value of the root key of the certificate chain to be
-   presented to the peer
+ -rootkey=
+   Shows the value of the root key of the provided certificate chain name.
+
+Principal Get Publickey
+
+Prints out the public key of the principal specified by the environment that
+this tool is running in.
+
+Usage:
+   principal get publickey
 
 Principal Get Recognizedroots
 
diff --git a/cmd/principal/main.go b/cmd/principal/main.go
index e6f2a6b..5aa4ae8 100644
--- a/cmd/principal/main.go
+++ b/cmd/principal/main.go
@@ -18,6 +18,7 @@
 	"io/ioutil"
 	"os"
 	"os/user"
+	"strings"
 	"time"
 
 	"v.io/v23"
@@ -68,12 +69,14 @@
 	flagRecvBlessingsSetDefault bool
 	flagRecvBlessingsForPeer    string
 
-	// Flags for the commands the get blessings
-	flagBlessingsName    bool
-	flagBlessingsRootKey bool
+	// Flags for the commands that get blessings
+	flagBlessingsNames   bool
+	flagBlessingsRootKey string
+	flagBlessingsCaveats string
 
 	errNoCaveats = fmt.Errorf("no caveats provided: it is generally dangerous to bless another principal without any caveats as that gives them almost unrestricted access to the blesser's credentials. If you really want to do this, set --require-caveats=false")
-	cmdDump      = &cmdline.Command{
+
+	cmdDump = &cmdline.Command{
 		Name:  "dump",
 		Short: "Dump out information about the principal",
 		Long: `
@@ -286,6 +289,22 @@
 		},
 	}
 
+	cmdGetPublicKey = &cmdline.Command{
+		Name:  "publickey",
+		Short: "Prints the public key of the principal.",
+		Long: `
+Prints out the public key of the principal specified by the environment
+that this tool is running in.
+`,
+		Run: func(cmd *cmdline.Command, args []string) error {
+			ctx, shutdown := v23.Init()
+			defer shutdown()
+			p := v23.GetPrincipal(ctx)
+			fmt.Println(p.PublicKey())
+			return nil
+		},
+	}
+
 	cmdGetTrustedRoots = &cmdline.Command{
 		Name:  "recognizedroots",
 		Short: "Return recognized blessings, and their associated public key.",
@@ -327,6 +346,11 @@
 Returns blessings that are marked for the provided peer in the
 BlessingStore specified by the environment that this tool is
 running in.
+Providing --names will print the blessings' chain names.
+Providing --rootkey <chain_name> will print the root key of the certificate chain
+with chain_name.
+Providing --caveats <chain_name> will print the caveats on the certificate chain
+with chain_name.
 `,
 		ArgsName: "[<peer_1> ... <peer_k>]",
 		ArgsLong: `
@@ -349,6 +373,11 @@
 		Long: `
 Returns blessings that are marked as default in the BlessingStore specified by
 the environment that this tool is running in.
+Providing --names will print the default blessings' chain names.
+Providing --rootkey <chain_name> will print the root key of the certificate chain
+with chain_name.
+Providing --caveats <chain_name> will print the caveats on the certificate chain
+with chain_name.
 `,
 		Run: func(cmd *cmdline.Command, args []string) error {
 			ctx, shutdown := v23.Init()
@@ -846,7 +875,7 @@
 	cmdBlessSelf.Flags.Var(&flagBlessSelfCaveats, "caveat", flagBlessSelfCaveats.usage())
 	cmdBlessSelf.Flags.DurationVar(&flagBlessSelfFor, "for", 0, "Duration of blessing validity (zero implies no expiration)")
 
-	cmdDump.Flags.BoolVar(&flagDumpShort, "s", false, "If true, show a only the default blessing names")
+	cmdDump.Flags.BoolVar(&flagDumpShort, "s", false, "If true, show only the default blessing names")
 
 	cmdFork.Flags.BoolVar(&flagCreateOverwrite, "overwrite", false, "If true, any existing principal data in the directory will be overwritten")
 	cmdFork.Flags.Var(&flagForkCaveats, "caveat", flagForkCaveats.usage())
@@ -878,11 +907,13 @@
 	cmdRecvBlessings.Flags.StringVar(&flagRecvBlessingsForPeer, "for-peer", string(security.AllPrincipals), "If non-empty, the blessings received will be marked for peers matching this pattern in the store")
 	cmdRecvBlessings.Flags.StringVar(&flagRemoteArgFile, "remote-arg-file", "", "If non-empty, the remote key, remote token, and principal will be written to the specified file in a JSON object. This can be provided to 'principal bless --remote-arg-file FILE EXTENSION'")
 
-	cmdGetForPeer.Flags.BoolVar(&flagBlessingsName, "name", false, "If true, shows the value of the blessing name to be presented to the peer")
-	cmdGetForPeer.Flags.BoolVar(&flagBlessingsRootKey, "rootkey", false, "If true, shows the value of the root key of the certificate chain to be presented to the peer")
+	cmdGetForPeer.Flags.BoolVar(&flagBlessingsNames, "names", false, "If true, shows the value of the blessing name to be presented to the peer")
+	cmdGetForPeer.Flags.StringVar(&flagBlessingsRootKey, "rootkey", "", "Shows the value of the root key of the provided certificate chain name.")
+	cmdGetForPeer.Flags.StringVar(&flagBlessingsCaveats, "caveats", "", "Shows the caveats on the provided certificate chain name.")
 
-	cmdGetDefault.Flags.BoolVar(&flagBlessingsName, "name", false, "If true, shows the value of the blessing name to be presented to the peer")
-	cmdGetDefault.Flags.BoolVar(&flagBlessingsRootKey, "rootkey", false, "If true, shows the value of the root key of the certificate chain to be presented to the peer")
+	cmdGetDefault.Flags.BoolVar(&flagBlessingsNames, "names", false, "If true, shows the value of the blessing name to be presented to the peer")
+	cmdGetDefault.Flags.StringVar(&flagBlessingsRootKey, "rootkey", "", "Shows the value of the root key of the provided certificate chain name.")
+	cmdGetDefault.Flags.StringVar(&flagBlessingsCaveats, "caveats", "", "Shows the caveats on the provided certificate chain name.")
 
 	cmdSet := &cmdline.Command{
 		Name:  "set",
@@ -904,7 +935,7 @@
 
 All blessings are printed to stdout using base64-VOM-encoding.
 `,
-		Children: []*cmdline.Command{cmdGetDefault, cmdGetForPeer, cmdGetTrustedRoots, cmdGetPeerMap},
+		Children: []*cmdline.Command{cmdGetDefault, cmdGetForPeer, cmdGetPublicKey, cmdGetTrustedRoots, cmdGetPeerMap},
 	}
 
 	root := &cmdline.Command{
@@ -942,20 +973,82 @@
 	if blessings.IsZero() {
 		return fmt.Errorf("no blessings found")
 	}
-	if flagBlessingsName {
-		fmt.Println(blessings)
+	if flagBlessingsNames {
+		fmt.Println(strings.Replace(fmt.Sprint(blessings), ",", "\n", -1))
 		return nil
-	} else if flagBlessingsRootKey {
-		wire, err := blessings2wire(blessings)
+	} else if len(flagBlessingsRootKey) > 0 {
+		chain, err := getChainByName(blessings, flagBlessingsRootKey)
 		if err != nil {
 			return err
 		}
-		fmt.Println(rootkey(wire.CertificateChains[0]))
+		fmt.Println(rootkey(chain))
+		return nil
+	} else if len(flagBlessingsCaveats) > 0 {
+		chain, err := getChainByName(blessings, flagBlessingsCaveats)
+		if err != nil {
+			return err
+		}
+		cavs, err := prettyPrintCaveats(chain)
+		if err != nil {
+			return err
+		}
+		for _, c := range cavs {
+			fmt.Println(c)
+		}
 		return nil
 	}
 	return dumpBlessings(blessings)
 }
 
+func prettyPrintCaveats(chain []security.Certificate) ([]string, error) {
+	var cavs []security.Caveat
+	for _, cert := range chain {
+		cavs = append(cavs, cert.Caveats...)
+	}
+	var s []string
+	for _, cav := range cavs {
+		if cav.Id == security.PublicKeyThirdPartyCaveatX.Id {
+			c := cav.ThirdPartyDetails()
+			s = append(s, fmt.Sprintf("ThirdPartyCaveat: Requires discharge from %v (ID=%q)", c.Location(), c.ID()))
+			continue
+		}
+		var param interface{}
+		if err := vom.Decode(cav.ParamVom, &param); err != nil {
+			return nil, err
+		}
+		switch cav.Id {
+		case security.ConstCaveat.Id:
+			// In the case a ConstCaveat is specified, we only want to print it
+			// if it never validates.
+			if !param.(bool) {
+				s = append(s, fmt.Sprintf("Never validates"))
+			}
+		case security.ExpiryCaveatX.Id:
+			s = append(s, fmt.Sprintf("Expires at %v", param))
+		case security.MethodCaveatX.Id:
+			s = append(s, fmt.Sprintf("Restricted to methods %v", param))
+		case security.PeerBlessingsCaveat.Id:
+			s = append(s, fmt.Sprintf("Restricted to peers with blessings %v", param))
+		default:
+			s = append(s, cav.String())
+		}
+	}
+	return s, nil
+}
+
+func getChainByName(b security.Blessings, name string) ([]security.Certificate, error) {
+	wire, err := blessings2wire(b)
+	if err != nil {
+		return nil, err
+	}
+	for _, chain := range wire.CertificateChains {
+		if chainName(chain) == name {
+			return chain, nil
+		}
+	}
+	return nil, fmt.Errorf("no chains of name %v in %v", name, b)
+}
+
 func read(fname string) (string, error) {
 	if len(fname) == 0 {
 		return "", nil
@@ -1010,6 +1103,14 @@
 	return fmt.Sprintf("%v", key)
 }
 
+func chainName(chain []security.Certificate) string {
+	exts := make([]string, len(chain))
+	for i, cert := range chain {
+		exts[i] = cert.Extension
+	}
+	return strings.Join(exts, security.ChainSeparator)
+}
+
 func base64VomEncode(i interface{}) (string, error) {
 	buf := &bytes.Buffer{}
 	closer := base64.NewEncoder(base64.URLEncoding, buf)
diff --git a/cmd/principal/principal_v23_test.go b/cmd/principal/principal_v23_test.go
index 1f3cef1..04f9fa3 100644
--- a/cmd/principal/principal_v23_test.go
+++ b/cmd/principal/principal_v23_test.go
@@ -72,14 +72,14 @@
 		aliceDir    = filepath.Join(outputDir, "alice")
 		aliceFriend = filepath.Join(outputDir, "alice.bless")
 		bobDir      = filepath.Join(outputDir, "bob")
-		bobForPeer  = filepath.Join(outputDir, "bob.store.forpeer")
+		bobForPeer  = filepath.Join(outputDir, "bob.get.forpeer")
 	)
 
 	// Create two principals: alice and bob.
 	bin.Start("create", aliceDir, "alice").WaitOrDie(os.Stdout, os.Stderr)
 	bin.Start("create", bobDir, "bob").WaitOrDie(os.Stdout, os.Stderr)
 
-	// Bless Alice with Bob's principal.
+	// Bless Bob with Alice's principal.
 	blessEnv := credEnv(aliceDir)
 	redirect(t, bin.WithEnv(blessEnv).Start("bless", "--for=1m", bobDir, "friend"), aliceFriend)
 
@@ -99,7 +99,30 @@
     (0) ExpiryCaveat
 `
 	if want != got {
-		t.Fatalf("unexpected output, got\n%s, wanted\n%s", got, want)
+		t.Errorf("unexpected output, got\n%s, wanted\n%s", got, want)
+	}
+
+	// Test the names flag.
+	got = bin.WithEnv(blessEnv).Start("--v23.credentials="+bobDir, "get", "forpeer", "--names", "alice/server").Output()
+	want = `bob
+alice/friend
+`
+	if got != want {
+		t.Errorf("unexpected output, got %s, want %s", got, want)
+	}
+
+	// Test the rootkey flag. In particular alice/friend's rootkey should be equal to alice's publickey.
+	got = bin.WithEnv(blessEnv).Start("--v23.credentials="+bobDir, "get", "forpeer", "--rootkey", "alice/friend", "alice/server").Output()
+	want = bin.WithEnv(blessEnv).Start("get", "publickey").Output()
+	if got != want {
+		t.Errorf("unexpected output, got %s, want %s", got, want)
+	}
+
+	// Test the caveats flag.
+	got = bin.WithEnv(blessEnv).Start("--v23.credentials="+bobDir, "get", "forpeer", "--caveats", "alice/friend", "alice/server").Output()
+	want = "Expires at"
+	if !strings.HasPrefix(got, want) {
+		t.Errorf("unexpected output, got %s, want %s", got, want)
 	}
 }
 
@@ -157,7 +180,7 @@
 	bin.Start("create", aliceDir, "alice").WaitOrDie(os.Stdout, os.Stderr)
 
 	blessEnv := credEnv(aliceDir)
-	got := removePublicKeys(bin.WithEnv(blessEnv).Start("get", "peermap").Output())
+	got := bin.WithEnv(blessEnv).Start("get", "peermap").Output()
 	want := `Default Blessings                alice
 Peer pattern                     Blessings
 ...                              alice
diff --git a/cmd/vrun/vrun.go b/cmd/vrun/vrun.go
index d6524e5..5f3d8f1 100644
--- a/cmd/vrun/vrun.go
+++ b/cmd/vrun/vrun.go
@@ -20,7 +20,7 @@
 	"v.io/x/lib/cmdline"
 	"v.io/x/lib/vlog"
 	"v.io/x/ref/envvar"
-	"v.io/x/ref/services/agent"
+	"v.io/x/ref/services/agent/agentlib"
 	"v.io/x/ref/services/agent/keymgr"
 	"v.io/x/ref/services/role"
 
@@ -121,7 +121,7 @@
 
 func doExec(cmd []string, conn *os.File) error {
 	envvar.ClearCredentials()
-	os.Setenv(agent.FdVarName, "3")
+	os.Setenv(agentlib.FdVarName, "3")
 	if conn.Fd() != 3 {
 		if err := syscall.Dup2(int(conn.Fd()), 3); err != nil {
 			vlog.Errorf("Couldn't dup fd")
@@ -159,7 +159,7 @@
 		return nil, nil, err
 	}
 	syscall.CloseOnExec(fd)
-	principal, err := agent.NewAgentPrincipal(ctx, fd, v23.GetClient(ctx))
+	principal, err := agentlib.NewAgentPrincipal(ctx, fd, v23.GetClient(ctx))
 	if err != nil {
 		vlog.Errorf("Couldn't connect to principal")
 		return nil, nil, err
diff --git a/profiles/internal/rt/security.go b/profiles/internal/rt/security.go
index 8f350c2..1b99d5c 100644
--- a/profiles/internal/rt/security.go
+++ b/profiles/internal/rt/security.go
@@ -18,7 +18,7 @@
 	"v.io/x/ref/lib/exec"
 	"v.io/x/ref/lib/mgmt"
 	vsecurity "v.io/x/ref/lib/security"
-	"v.io/x/ref/services/agent"
+	"v.io/x/ref/services/agent/agentlib"
 )
 
 func initSecurity(ctx *context.T, credentials string, client rpc.Client) (security.Principal, error) {
@@ -80,7 +80,7 @@
 		// We were started by a parent (presumably, device manager).
 		fd, _ = handle.Config.Get(mgmt.SecurityAgentFDConfigKey)
 	} else {
-		fd = os.Getenv(agent.FdVarName)
+		fd = os.Getenv(agentlib.FdVarName)
 	}
 	if fd == "" {
 		return -1, nil
@@ -117,5 +117,5 @@
 	if err != nil {
 		return nil, err
 	}
-	return agent.NewAgentPrincipal(ctx, newfd, client)
+	return agentlib.NewAgentPrincipal(ctx, newfd, client)
 }
diff --git a/services/agent/agentd/main.go b/services/agent/agentd/main.go
index 60bccd3..f5d69e0 100644
--- a/services/agent/agentd/main.go
+++ b/services/agent/agentd/main.go
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// Daemon agentd holds a private key in memory and makes it available to a
+// subprocess via the agent protocol.
 package main
 
 import (
@@ -23,8 +25,8 @@
 	"v.io/x/ref/envvar"
 	vsecurity "v.io/x/ref/lib/security"
 	vsignals "v.io/x/ref/lib/signals"
-	"v.io/x/ref/services/agent"
-	"v.io/x/ref/services/agent/server"
+	"v.io/x/ref/services/agent/agentlib"
+	"v.io/x/ref/services/agent/internal/server"
 
 	_ "v.io/x/ref/profiles"
 )
@@ -108,7 +110,7 @@
 		vlog.Panic("failed to set principal for ctx: %v", err)
 	}
 
-	if err = os.Setenv(agent.FdVarName, "3"); err != nil {
+	if err = os.Setenv(agentlib.FdVarName, "3"); err != nil {
 		vlog.Fatalf("setenv: %v", err)
 	}
 
diff --git a/services/agent/agent_test.go b/services/agent/agentlib/agent_test.go
similarity index 96%
rename from services/agent/agent_test.go
rename to services/agent/agentlib/agent_test.go
index 2c3c17d..a8a70e5 100644
--- a/services/agent/agent_test.go
+++ b/services/agent/agentlib/agent_test.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package agent_test
+package agentlib_test
 
 import (
 	"fmt"
@@ -17,8 +17,8 @@
 	"v.io/v23"
 	"v.io/v23/context"
 	"v.io/v23/security"
-	"v.io/x/ref/services/agent"
-	"v.io/x/ref/services/agent/server"
+	"v.io/x/ref/services/agent/agentlib"
+	"v.io/x/ref/services/agent/internal/server"
 	"v.io/x/ref/test"
 	"v.io/x/ref/test/modules"
 	"v.io/x/ref/test/testutil"
@@ -44,9 +44,9 @@
 		return nil, err
 	}
 	if cached {
-		return agent.NewAgentPrincipal(ctx, fd, v23.GetClient(ctx))
+		return agentlib.NewAgentPrincipal(ctx, fd, v23.GetClient(ctx))
 	} else {
-		return agent.NewUncachedPrincipal(ctx, fd, v23.GetClient(ctx))
+		return agentlib.NewUncachedPrincipal(ctx, fd, v23.GetClient(ctx))
 	}
 }
 
diff --git a/services/agent/agent_v23_test.go b/services/agent/agentlib/agent_v23_test.go
similarity index 99%
rename from services/agent/agent_v23_test.go
rename to services/agent/agentlib/agent_v23_test.go
index 33f60eb..daa9185 100644
--- a/services/agent/agent_v23_test.go
+++ b/services/agent/agentlib/agent_v23_test.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package agent_test
+package agentlib_test
 
 import (
 	"bytes"
diff --git a/services/agent/client.go b/services/agent/agentlib/client.go
similarity index 97%
rename from services/agent/client.go
rename to services/agent/agentlib/client.go
index 64cd670..f6aaeba 100644
--- a/services/agent/client.go
+++ b/services/agent/agentlib/client.go
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Package agent provides a client for communicating with an "Agent"
-// process holding the private key for an identity.
-package agent
+// Package agentlib implements a client for communicating with an agentd process
+// holding the private key for an identity.
+package agentlib
 
 import (
 	"fmt"
diff --git a/services/agent/peer_test.go b/services/agent/agentlib/peer_test.go
similarity index 95%
rename from services/agent/peer_test.go
rename to services/agent/agentlib/peer_test.go
index 3c5ff13..ea5959d 100644
--- a/services/agent/peer_test.go
+++ b/services/agent/agentlib/peer_test.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package agent
+package agentlib
 
 import (
 	"v.io/v23/context"
diff --git a/services/agent/v23_test.go b/services/agent/agentlib/v23_test.go
similarity index 97%
rename from services/agent/v23_test.go
rename to services/agent/agentlib/v23_test.go
index a6b4044..3e4061b 100644
--- a/services/agent/v23_test.go
+++ b/services/agent/agentlib/v23_test.go
@@ -4,7 +4,7 @@
 
 // This file was auto-generated via go generate.
 // DO NOT UPDATE MANUALLY
-package agent_test
+package agentlib_test
 
 import "fmt"
 import "testing"
diff --git a/services/agent/server/server.go b/services/agent/internal/server/server.go
similarity index 97%
rename from services/agent/server/server.go
rename to services/agent/internal/server/server.go
index 6710b1a..46b5e0b 100644
--- a/services/agent/server/server.go
+++ b/services/agent/internal/server/server.go
@@ -26,12 +26,13 @@
 	"v.io/v23/verror"
 	"v.io/x/lib/vlog"
 	vsecurity "v.io/x/ref/lib/security"
+	"v.io/x/ref/services/agent"
 	"v.io/x/ref/services/agent/internal/unixfd"
 )
 
 const PrincipalHandleByteSize = sha512.Size
 
-const pkgPath = "v.io/x/ref/services/agent/server"
+const pkgPath = "v.io/x/ref/services/agent/internal/server"
 
 // Errors
 var (
@@ -261,9 +262,8 @@
 				}
 				spec := rpc.ListenSpec{Addrs: rpc.ListenAddrs(a)}
 				if _, err = s.Listen(spec); err == nil {
-					agent := &agentd{w.newID(), w, principal, ctx}
-					serverAgent := AgentServer(agent)
-					err = s.Serve("", serverAgent, nil)
+					server := agent.AgentServer(&agentd{w.newID(), w, principal, ctx})
+					err = s.Serve("", server, nil)
 				}
 				ack()
 			}
@@ -430,7 +430,7 @@
 	return a.principal.Roots().DebugString(), nil
 }
 
-func (a agentd) NotifyWhenChanged(call AgentNotifyWhenChangedServerCall) error {
+func (a agentd) NotifyWhenChanged(call agent.AgentNotifyWhenChangedServerCall) error {
 	ch := a.w.register(a.id)
 	defer a.w.unregister(a.id, ch)
 	for {
diff --git a/services/agent/server/sharing.go b/services/agent/internal/server/sharing.go
similarity index 100%
rename from services/agent/server/sharing.go
rename to services/agent/internal/server/sharing.go
diff --git a/services/agent/server/sharing_test.go b/services/agent/internal/server/sharing_test.go
similarity index 100%
rename from services/agent/server/sharing_test.go
rename to services/agent/internal/server/sharing_test.go
diff --git a/services/agent/internal/test_principal/main.go b/services/agent/internal/test_principal/main.go
index d60f430..06bbc52 100644
--- a/services/agent/internal/test_principal/main.go
+++ b/services/agent/internal/test_principal/main.go
@@ -16,7 +16,7 @@
 	"v.io/v23"
 	"v.io/v23/security"
 	"v.io/x/ref/envvar"
-	"v.io/x/ref/services/agent"
+	"v.io/x/ref/services/agent/agentlib"
 
 	_ "v.io/x/ref/profiles"
 )
@@ -56,8 +56,8 @@
 	if got := os.Getenv(envvar.Credentials); len(got) != 0 {
 		errorf("%v environment variable is unexpectedly set", envvar.Credentials)
 	}
-	if got := os.Getenv(agent.FdVarName); len(got) == 0 {
-		errorf("%v environment variable is not set", agent.FdVarName)
+	if got := os.Getenv(agentlib.FdVarName); len(got) == 0 {
+		errorf("%v environment variable is not set", agentlib.FdVarName)
 	}
 	// A pristine agent has a single blessing "agent_principal" (from agentd/main.go).
 	if blessings := p.BlessingsInfo(p.BlessingStore().Default()); len(blessings) != 1 {
diff --git a/services/agent/keymgr/client.go b/services/agent/keymgr/client.go
index d6c34a3..01098de 100644
--- a/services/agent/keymgr/client.go
+++ b/services/agent/keymgr/client.go
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Package keymgr provides a client for the Device Manager to manage keys in
-// the "Agent" process.
+// Package keymgr implements a client for deviced to manage keys in the agentd
+// process.
 package keymgr
 
 import (
@@ -14,8 +14,8 @@
 
 	"v.io/v23/context"
 	"v.io/v23/verror"
+	"v.io/x/ref/services/agent/internal/server"
 	"v.io/x/ref/services/agent/internal/unixfd"
-	"v.io/x/ref/services/agent/server"
 )
 
 const pkgPath = "v.io/x/ref/services/agent/keymgr"
diff --git a/services/agent/keymgr/keymgr_test.go b/services/agent/keymgr/keymgr_test.go
index 1e2a521..15348b6 100644
--- a/services/agent/keymgr/keymgr_test.go
+++ b/services/agent/keymgr/keymgr_test.go
@@ -15,8 +15,8 @@
 	"v.io/v23"
 	"v.io/v23/context"
 	"v.io/v23/security"
-	"v.io/x/ref/services/agent"
-	"v.io/x/ref/services/agent/server"
+	"v.io/x/ref/services/agent/agentlib"
+	"v.io/x/ref/services/agent/internal/server"
 	"v.io/x/ref/test"
 
 	_ "v.io/x/ref/profiles"
@@ -72,7 +72,7 @@
 		return nil, err
 	}
 
-	return agent.NewAgentPrincipal(ctx, fd, v23.GetClient(ctx))
+	return agentlib.NewAgentPrincipal(ctx, fd, v23.GetClient(ctx))
 }
 
 func TestSigning(t *testing.T) {
diff --git a/services/agent/server/wire.vdl b/services/agent/wire.vdl
similarity index 95%
rename from services/agent/server/wire.vdl
rename to services/agent/wire.vdl
index c025f6f..4c93acb 100644
--- a/services/agent/server/wire.vdl
+++ b/services/agent/wire.vdl
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Package server provides a server which keeps a principal in memory
-// and allows clients to use that principal.
+// Package agent defines an interface to keep a private key in memory, and for
+// clients to have access to the private key.
 //
-// PROTOCOL
+// Protocol
 //
 // The agent starts processes with the VEYRON_AGENT_FD set to one end of a
 // unix domain socket. To connect to the agent, a client should create
@@ -30,9 +30,7 @@
 // The protocol also has limited support for caching: A client can
 // request notification when any other client modifies the principal so it
 // can flush the cache. See NotifyWhenChanged for details.
-
-
-package server
+package agent
 
 import (
 	"v.io/v23/security"
diff --git a/services/agent/server/wire.vdl.go b/services/agent/wire.vdl.go
similarity index 93%
rename from services/agent/server/wire.vdl.go
rename to services/agent/wire.vdl.go
index 16af73a..f01c204 100644
--- a/services/agent/server/wire.vdl.go
+++ b/services/agent/wire.vdl.go
@@ -5,7 +5,35 @@
 // This file was auto-generated by the vanadium vdl tool.
 // Source: wire.vdl
 
-package server
+// Package agent defines an interface to keep a private key in memory, and for
+// clients to have access to the private key.
+//
+// Protocol
+//
+// The agent starts processes with the VEYRON_AGENT_FD set to one end of a
+// unix domain socket. To connect to the agent, a client should create
+// a unix domain socket pair. Then send one end of the socket to the agent
+// with 1 byte of data. The agent will then serve the Agent service on
+// the received socket, using SecurityNone.
+//
+// The agent also supports an optional mode where it can manage multiple principals.
+// Typically this is only used by Device Manager. In this mode, VEYRON_AGENT_FD
+// will be 3, and there will be another socket at fd 4.
+// Creating a new principal is similar to connecting to to agent: create a socket
+// pair and send one end on fd 4 with 1 byte of data.
+// Set the data to 1 to request the principal only be stored in memory.
+// The agent will create a new principal and respond with a principal handle on fd 4.
+// To connect using a previously created principal, create a socket pair and send
+// one end with the principal handle as data on fd 4. The agent will not send a
+// response on fd 4.
+// In either, you can use the normal process to connect to an agent over the
+// other end of the pair. Typically you would pass the other end to a child
+// process and set VEYRON_AGENT_FD so it knows to connect.
+//
+// The protocol also has limited support for caching: A client can
+// request notification when any other client modifies the principal so it
+// can flush the cache. See NotifyWhenChanged for details.
+package agent
 
 import (
 	// VDL system imports
@@ -393,7 +421,7 @@
 // descAgent hides the desc to keep godoc clean.
 var descAgent = rpc.InterfaceDesc{
 	Name:    "Agent",
-	PkgPath: "v.io/x/ref/services/agent/server",
+	PkgPath: "v.io/x/ref/services/agent",
 	Methods: []rpc.MethodDesc{
 		{
 			Name: "Bless",
diff --git a/services/device/internal/impl/app_service.go b/services/device/internal/impl/app_service.go
index 8e3926a..d5c9a5a 100644
--- a/services/device/internal/impl/app_service.go
+++ b/services/device/internal/impl/app_service.go
@@ -153,7 +153,7 @@
 	vexec "v.io/x/ref/lib/exec"
 	"v.io/x/ref/lib/mgmt"
 	vsecurity "v.io/x/ref/lib/security"
-	"v.io/x/ref/services/agent"
+	"v.io/x/ref/services/agent/agentlib"
 	"v.io/x/ref/services/agent/keymgr"
 	"v.io/x/ref/services/device/internal/config"
 	"v.io/x/ref/services/internal/acls"
@@ -516,7 +516,7 @@
 		conn.Close()
 		return nil, nil, err
 	}
-	p, err := agent.NewAgentPrincipal(agentctx, int(conn.Fd()), v23.GetClient(agentctx))
+	p, err := agentlib.NewAgentPrincipal(agentctx, int(conn.Fd()), v23.GetClient(agentctx))
 	if err != nil {
 		cancel()
 		conn.Close()
diff --git a/services/device/internal/impl/dispatcher.go b/services/device/internal/impl/dispatcher.go
index da5a9a2..88679c1 100644
--- a/services/device/internal/impl/dispatcher.go
+++ b/services/device/internal/impl/dispatcher.go
@@ -25,7 +25,7 @@
 	"v.io/v23/vdlroot/signature"
 	"v.io/v23/verror"
 	"v.io/x/lib/vlog"
-	"v.io/x/ref/services/agent"
+	"v.io/x/ref/services/agent/agentlib"
 	"v.io/x/ref/services/agent/keymgr"
 	s_device "v.io/x/ref/services/device"
 	"v.io/x/ref/services/device/internal/config"
@@ -137,7 +137,7 @@
 	}
 
 	// If we're in 'security agent mode', set up the key manager agent.
-	if len(os.Getenv(agent.FdVarName)) > 0 {
+	if len(os.Getenv(agentlib.FdVarName)) > 0 {
 		if keyMgrAgent, err := keymgr.NewAgent(); err != nil {
 			return nil, verror.New(errNewAgentFailed, ctx, err)
 		} else {
diff --git a/services/mounttable/mounttablelib/mounttable.go b/services/mounttable/mounttablelib/mounttable.go
index 8bd6e21..7fbcf7e 100644
--- a/services/mounttable/mounttablelib/mounttable.go
+++ b/services/mounttable/mounttablelib/mounttable.go
@@ -778,7 +778,7 @@
 	ch <- naming.GlobReplyEntry{naming.MountEntry{Name: "", Servers: servers}}
 }
 
-func (ms *mountContext) SetPermissions(call rpc.ServerCall, tam access.Permissions, version string) error {
+func (ms *mountContext) SetPermissions(call rpc.ServerCall, perms access.Permissions, version string) error {
 	vlog.VI(2).Infof("SetPermissions %q", ms.name)
 	mt := ms.mt
 
@@ -793,7 +793,22 @@
 	}
 	n.parent.Unlock()
 	defer n.Unlock()
-	n.acls, err = n.acls.Set(version, tam)
+
+	// If the caller is trying to add a Permission that they are no longer Admin in,
+	// retain the caller's blessings that were in Admin to prevent them from locking themselves out.
+	bnames, _ := security.RemoteBlessingNames(call.Context())
+	if acl, ok := perms[string(mounttable.Admin)]; !ok || !acl.Includes(bnames...) {
+		_, oldPerms := n.acls.Get()
+		oldAcl := oldPerms[string(mounttable.Admin)]
+		for _, bname := range bnames {
+			if oldAcl.Includes(bname) {
+				perms.Add(security.BlessingPattern(bname), string(mounttable.Admin))
+			}
+		}
+	}
+	perms.Normalize()
+
+	n.acls, err = n.acls.Set(version, perms)
 	if err == nil {
 		n.explicitAccessLists = true
 	}
diff --git a/services/mounttable/mounttablelib/mounttable_test.go b/services/mounttable/mounttablelib/mounttable_test.go
index b3a9594..fdb08c2 100644
--- a/services/mounttable/mounttablelib/mounttable_test.go
+++ b/services/mounttable/mounttablelib/mounttable_test.go
@@ -279,6 +279,26 @@
 	doSetPermissions(t, aliceCtx, mtAddr, "onlybob", acl, "", false)
 	doSetPermissions(t, bobCtx, mtAddr, "onlybob", acl, "", true)
 
+	// Test that setting Permissions to an acl that don't include the the setter's
+	// blessings in Admin, automatically add their Blessings to Admin to prevent
+	// locking everyone out.
+	acl, _ = doGetPermissions(t, bobCtx, mtAddr, "onlybob", true)
+	noRootAcl := acl.Copy()
+	noRootAcl.Clear("bob", "Admin")
+	doSetPermissions(t, bobCtx, mtAddr, "onlybob", noRootAcl, "", true)
+	// This should succeed, because "bob" should automatically be added to "Admin"
+	// even though he cleared himself from "Admin".
+	doSetPermissions(t, bobCtx, mtAddr, "onlybob", acl, "", true)
+	// Test that adding a non-standard acl is normalized when retrieved.
+	admin := acl["Admin"]
+	admin.In = []security.BlessingPattern{"bob", "bob"}
+	acl["Admin"] = admin
+	doSetPermissions(t, bobCtx, mtAddr, "onlybob", acl, "", true)
+	acl, _ = doGetPermissions(t, bobCtx, mtAddr, "onlybob", true)
+	if got, want := acl["Admin"].In, []security.BlessingPattern{"bob"}; !reflect.DeepEqual(got, want) {
+		boom(t, "got %v, want %v", got, want)
+	}
+
 	// Test generic unmount.
 	vlog.Info("Test generic unmount.")
 	doUnmount(t, rootCtx, mtAddr, "a/b", "", true)
diff --git a/test/modules/shell.go b/test/modules/shell.go
index 5e669e7..8ae6194 100644
--- a/test/modules/shell.go
+++ b/test/modules/shell.go
@@ -157,7 +157,7 @@
 	"v.io/v23/security"
 	"v.io/x/ref/envvar"
 	"v.io/x/ref/lib/exec"
-	"v.io/x/ref/services/agent"
+	"v.io/x/ref/services/agent/agentlib"
 	"v.io/x/ref/services/agent/keymgr"
 	"v.io/x/ref/test/expect"
 )
@@ -300,7 +300,7 @@
 	if err != nil {
 		return nil, err
 	}
-	p, err := agent.NewAgentPrincipal(sh.ctx, fd, v23.GetClient(sh.ctx))
+	p, err := agentlib.NewAgentPrincipal(sh.ctx, fd, v23.GetClient(sh.ctx))
 	if err != nil {
 		syscall.Close(fd)
 		return nil, err
@@ -729,7 +729,7 @@
 	// want the child to directly use the directory specified
 	// by the shell's VeyronCredentials.
 	delete(m1, envvar.Credentials)
-	delete(m1, agent.FdVarName)
+	delete(m1, agentlib.FdVarName)
 
 	m2 := mergeMaps(m1, evmap)
 	r := []string{}
diff --git a/test/v23tests/v23tests.go b/test/v23tests/v23tests.go
index 3192ba1..caf7fc9 100644
--- a/test/v23tests/v23tests.go
+++ b/test/v23tests/v23tests.go
@@ -25,7 +25,7 @@
 	"v.io/v23/security"
 
 	"v.io/x/ref/envvar"
-	"v.io/x/ref/services/agent"
+	"v.io/x/ref/services/agent/agentlib"
 	"v.io/x/ref/test"
 	"v.io/x/ref/test/modules"
 	"v.io/x/ref/test/testutil"
@@ -340,7 +340,7 @@
 	}
 	// Set up agent for Child.
 	attr.Files = append(attr.Files, agentFile)
-	attr.Env = append(attr.Env, fmt.Sprintf("%s=%d", agent.FdVarName, len(attr.Files)-1))
+	attr.Env = append(attr.Env, fmt.Sprintf("%s=%d", agentlib.FdVarName, len(attr.Files)-1))
 
 	// Set up environment for Child.
 	for _, v := range t.shell.Env() {