Merge "groups: profile: s/generic/roaming/"
diff --git a/lib/mgmt/model.go b/lib/mgmt/model.go
index e1f3032..1aa425f 100644
--- a/lib/mgmt/model.go
+++ b/lib/mgmt/model.go
@@ -14,4 +14,6 @@
 	ParentBlessingConfigKey        = "MGMT_PARENT_BLESSING_PEER_PATTERN"
 	SecurityAgentEndpointConfigKey = "MGMT_SECURITY_AGENT_EP"
 	AppOriginConfigKey             = "MGMT_APP_ORIGIN"
+	PublisherBlessingPrefixesKey   = "MGMT_PUBLISHER_BLESSING_PREFIXES"
+	InstanceNameKey                = "MGMT_INSTANCE_NAME"
 )
diff --git a/runtime/internal/flow/flowcontrol/flowcontrol.go b/runtime/internal/flow/flowcontrol/flowcontrol.go
new file mode 100644
index 0000000..1a47ada
--- /dev/null
+++ b/runtime/internal/flow/flowcontrol/flowcontrol.go
@@ -0,0 +1,339 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package flowcontrol
+
+import (
+	"bytes"
+	"fmt"
+	"sync"
+
+	"v.io/v23/context"
+	"v.io/v23/verror"
+)
+
+const pkgPath = "v.io/x/ref/runtime/internal/flow/flowcontrol"
+
+var ErrConcurrentRun = verror.Register(
+	verror.ID(pkgPath+".ErrConcurrentRun"),
+	verror.NoRetry, "Run called concurrently.")
+var ErrWrongFlowController = verror.Register(
+	verror.ID(pkgPath+".ErrWrongFlowController"),
+	verror.NoRetry, "Release called for worker from different flow controller.")
+
+// Runners are called by Workers.  For a given flow controller
+// only one Runner will be running at a time.  tokens specifies
+// the number of tokens available for this call.  Implementors
+// should return the number of tokens used, whether they are done
+// with all their work, and any error encountered.
+// Runners will be called repeatedly within a single Run call until
+// either err != nil or done is true.
+type Runner func(tokens int) (used int, done bool, err error)
+
+type counterState struct {
+	// TODO(mattr): Add deficit if we allow multi-slice writes.
+	borrowed     int  // Number of tokens borrowed from the shared pool.
+	released     int  // Number of tokens available via our flow control counters.
+	everReleased bool // True if tokens have ever been released to this worker.
+}
+
+type state int
+
+const (
+	idle = state(iota)
+	running
+	active
+)
+
+// Worker represents a single flowcontrolled worker.
+// Workers keep track of flow control counters to ensure
+// producers do not overwhelm consumers.  Only one Worker
+// will be executing at a time.
+type Worker struct {
+	fc       *FlowController
+	priority int
+	work     chan struct{}
+
+	// These variables are protected by fc.mu.
+	counters   *counterState // State related to the flow control counters.
+	state      state
+	next, prev *Worker // Used as a list when in an active queue.
+}
+
+// Run runs r potentially multiple times.
+// Only one worker's r function will run at a time for a given FlowController.
+// A single worker's Run function should not be called concurrently from multiple
+// goroutines.
+func (w *Worker) Run(ctx *context.T, r Runner) (err error) {
+	w.fc.mu.Lock()
+	if w.state != idle {
+		w.fc.mu.Unlock()
+		return verror.New(ErrConcurrentRun, ctx)
+	}
+
+	w.state = running
+	if w.readyLocked() {
+		w.fc.activateLocked(w)
+		w.state = active
+	}
+
+	for {
+		next := w.fc.nextWorkerLocked()
+		for w.fc.writing != w && err == nil {
+			w.fc.mu.Unlock()
+			if next != nil {
+				next.work <- struct{}{}
+			}
+			select {
+			case <-ctx.Done():
+				err = ctx.Err()
+			case <-w.work:
+			}
+			w.fc.mu.Lock()
+		}
+		if err != nil {
+			break
+		}
+
+		toWrite := w.fc.mtu
+		if w.counters != nil {
+			if !w.counters.everReleased {
+				toWrite = min(w.fc.shared, w.fc.mtu)
+				w.counters.released += toWrite
+				w.counters.borrowed += toWrite
+				w.fc.shared -= toWrite
+			} else {
+				toWrite = min(w.counters.released, w.fc.mtu)
+			}
+		}
+
+		w.fc.mu.Unlock()
+		var written int
+		var done bool
+		written, done, err = r(toWrite)
+		w.fc.mu.Lock()
+
+		if w.counters != nil {
+			w.counters.released -= written
+			if w.counters.released > 0 && w.counters.borrowed > 0 {
+				toReturn := min(w.counters.released, w.counters.borrowed)
+				w.counters.borrowed -= toReturn
+				w.counters.released -= toReturn
+				w.fc.shared += toReturn
+			}
+		}
+
+		w.fc.writing = nil
+		if err != nil || done {
+			break
+		}
+		if !w.readyLocked() {
+			w.fc.deactivateLocked(w)
+			w.state = running
+		}
+	}
+
+	w.state = idle
+	w.fc.deactivateLocked(w)
+	next := w.fc.nextWorkerLocked()
+	w.fc.mu.Unlock()
+	if next != nil {
+		next.work <- struct{}{}
+	}
+	return err
+}
+
+func (w *Worker) releaseLocked(tokens int) {
+	if w.counters == nil {
+		return
+	}
+	w.counters.everReleased = true
+	if w.counters.borrowed > 0 {
+		n := min(w.counters.borrowed, tokens)
+		w.counters.borrowed -= n
+		w.fc.shared += n
+		tokens -= n
+	}
+	w.counters.released += tokens
+	if w.state == running && w.readyLocked() {
+		w.fc.activateLocked(w)
+	}
+}
+
+// Release releases tokens to this worker.
+// Workers will first repay any debts to the flow controllers shared pool
+// and use any surplus in subsequent calls to Run.
+func (w *Worker) Release(tokens int) {
+	w.fc.mu.Lock()
+	w.releaseLocked(tokens)
+	next := w.fc.nextWorkerLocked()
+	w.fc.mu.Unlock()
+	if next != nil {
+		next.work <- struct{}{}
+	}
+}
+
+func (w *Worker) readyLocked() bool {
+	if w.counters == nil {
+		return true
+	}
+	return w.counters.released > 0 || (!w.counters.everReleased && w.fc.shared > 0)
+}
+
+// FlowController manages multiple Workers to ensure only one runs at a time.
+// The workers also obey counters so that producers don't overwhelm consumers.
+type FlowController struct {
+	mtu int
+
+	mu      sync.Mutex
+	shared  int
+	active  []*Worker
+	writing *Worker
+}
+
+// New creates a new FlowController.  Shared is the number of shared tokens
+// that flows can borrow from before they receive their first Release.
+// Mtu is the maximum number of tokens to be consumed by a single Runner
+// invocation.
+func New(shared, mtu int) *FlowController {
+	return &FlowController{shared: shared, mtu: mtu}
+}
+
+// NewWorker creates a new worker.  Workers keep track of token counters
+// for a flow controlled process.  The order that workers
+// execute is controlled by priority.  Higher priority
+// workers that are ready will run before any lower priority
+// workers.
+func (fc *FlowController) NewWorker(priority int) *Worker {
+	w := &Worker{
+		fc:       fc,
+		priority: priority,
+		work:     make(chan struct{}),
+		counters: &counterState{},
+	}
+	w.next, w.prev = w, w
+	return w
+}
+
+type Release struct {
+	Worker *Worker
+	Tokens int
+}
+
+// Release releases to many Workers atomically.  It is conceptually
+// the same as calling release on each worker indepedently.
+func (fc *FlowController) Release(to []Release) error {
+	fc.mu.Lock()
+	for _, t := range to {
+		if t.Worker.fc != fc {
+			return verror.New(ErrWrongFlowController, nil)
+		}
+		t.Worker.releaseLocked(t.Tokens)
+	}
+	next := fc.nextWorkerLocked()
+	fc.mu.Unlock()
+	if next != nil {
+		next.work <- struct{}{}
+	}
+	return nil
+}
+
+// Run runs the given runner on a non-flow controlled Worker.  This
+// worker does not wait for any flow control tokens and is limited
+// only by the MTU.
+func (fc *FlowController) Run(ctx *context.T, p int, r Runner) error {
+	w := &Worker{
+		fc:       fc,
+		priority: p,
+		work:     make(chan struct{}),
+	}
+	w.next, w.prev = w, w
+	return w.Run(ctx, r)
+}
+
+func (fc *FlowController) nextWorkerLocked() *Worker {
+	if fc.writing == nil {
+		for p, head := range fc.active {
+			if head != nil {
+				fc.active[p] = head.next
+				fc.writing = head
+				return head
+			}
+		}
+	}
+	return nil
+}
+
+func (fc *FlowController) activateLocked(w *Worker) {
+	if w.priority >= len(fc.active) {
+		newActive := make([]*Worker, int(w.priority)+1)
+		copy(newActive, fc.active)
+		fc.active = newActive
+	}
+	head := fc.active[w.priority]
+	if head == nil {
+		fc.active[w.priority] = w
+	} else {
+		w.prev, w.next = head.prev, head
+		w.prev.next, w.next.prev = w, w
+	}
+}
+
+func (fc *FlowController) deactivateLocked(w *Worker) {
+	if head := fc.active[w.priority]; head == w {
+		if w.next == w {
+			fc.active[w.priority] = nil
+		} else {
+			fc.active[w.priority] = w.next
+		}
+	}
+	w.next.prev, w.prev.next = w.prev, w.next
+	w.next, w.prev = w, w
+}
+
+func (fc *FlowController) numActive() int {
+	n := 0
+	fc.mu.Lock()
+	for _, head := range fc.active {
+		if head != nil {
+			n++
+			for cur := head.next; cur != head; cur = cur.next {
+				n++
+			}
+		}
+	}
+	fc.mu.Unlock()
+	return n
+}
+
+// String writes a string representation of the flow controller.
+// This can be helpful in debugging.
+func (fc *FlowController) String() string {
+	buf := &bytes.Buffer{}
+	fmt.Fprintf(buf, "FlowController %p: \n", fc)
+
+	fc.mu.Lock()
+	fmt.Fprintf(buf, "writing: %p\n", fc.writing)
+	fmt.Fprintln(buf, "active:")
+	for p, head := range fc.active {
+		fmt.Fprintf(buf, "  %v: %p", p, head)
+		if head != nil {
+			for cur := head.next; cur != head; cur = cur.next {
+				fmt.Fprintf(buf, " %p", cur)
+			}
+		}
+		fmt.Fprintln(buf, "")
+	}
+	fc.mu.Unlock()
+	return buf.String()
+}
+
+func min(head int, rest ...int) int {
+	for _, r := range rest {
+		if r < head {
+			head = r
+		}
+	}
+	return head
+}
diff --git a/runtime/internal/flow/flowcontrol/flowcontrol_test.go b/runtime/internal/flow/flowcontrol/flowcontrol_test.go
new file mode 100644
index 0000000..7388b8f
--- /dev/null
+++ b/runtime/internal/flow/flowcontrol/flowcontrol_test.go
@@ -0,0 +1,293 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package flowcontrol
+
+import (
+	"bytes"
+	"crypto/rand"
+	"fmt"
+	"io"
+	"net"
+	"sync"
+	"testing"
+	"time"
+
+	"v.io/v23/context"
+	"v.io/v23/verror"
+	"v.io/x/ref/test"
+)
+
+var testdata = make([]byte, 1<<20)
+
+func init() {
+	test.Init()
+	_, err := io.ReadFull(rand.Reader, testdata)
+	if err != nil {
+		panic(err)
+	}
+}
+
+func TestFlowControl(t *testing.T) {
+	const (
+		workers  = 10
+		messages = 10
+	)
+
+	msgs := make(map[int][]byte)
+	fc := New(256, 64)
+
+	ctx, cancel := context.RootContext()
+	defer cancel()
+
+	var wg sync.WaitGroup
+	wg.Add(workers)
+	for i := 0; i < workers; i++ {
+		go func(idx int) {
+			el := fc.NewWorker(0)
+			go el.Release(messages * 5) // Try to make races happen
+			j := 0
+			el.Run(ctx, func(tokens int) (used int, done bool, err error) {
+				msgs[idx] = append(msgs[idx], []byte(fmt.Sprintf("%d-%d,", idx, j))...)
+				j++
+				return 3, j >= messages, nil
+			})
+			wg.Done()
+		}(i)
+	}
+	wg.Wait()
+
+	for i := 0; i < workers; i++ {
+		buf := &bytes.Buffer{}
+		for j := 0; j < messages; j++ {
+			fmt.Fprintf(buf, "%d-%d,", i, j)
+		}
+		if want, got := buf.String(), string(msgs[i]); want != got {
+			t.Errorf("Got %s, want %s for %d", got, want, i)
+		}
+	}
+}
+
+func expect(t *testing.T, work chan interface{}, values ...interface{}) {
+	for i, w := range values {
+		if got := <-work; got != w {
+			t.Errorf("expected %p in pos %d got %p", w, i, got)
+		}
+	}
+}
+
+func TestOrdering(t *testing.T) {
+	const mtu = 10
+
+	ctx, cancel := context.RootContext()
+	defer cancel()
+	fc := New(0, mtu)
+
+	work := make(chan interface{})
+	worker := func(p int) *Worker {
+		w := fc.NewWorker(p)
+		go w.Run(ctx, func(t int) (int, bool, error) {
+			work <- w
+			return t, false, nil
+		})
+		w.Release(mtu)
+		<-work
+		return w
+	}
+
+	w0 := worker(0)
+	w1a := worker(1)
+	w1b := worker(1)
+	w1c := worker(1)
+	w2 := worker(2)
+
+	// Release to all the flows at once and ensure the writes
+	// happen in the correct order.
+	fc.Release([]Release{{w0, 2 * mtu}, {w1a, 2 * mtu}, {w1b, 3 * mtu}, {w1c, 0}, {w2, mtu}})
+	expect(t, work, w0, w0, w1a, w1b, w1a, w1b, w1b, w2)
+}
+
+func TestSharedCounters(t *testing.T) {
+	const (
+		mtu    = 10
+		shared = 2 * mtu
+	)
+
+	ctx, cancel := context.RootContext()
+	defer cancel()
+
+	fc := New(shared, mtu)
+
+	work := make(chan interface{})
+
+	worker := func(p int) *Worker {
+		w := fc.NewWorker(p)
+		go w.Run(ctx, func(t int) (int, bool, error) {
+			work <- w
+			return t, false, nil
+		})
+		return w
+	}
+
+	// w0 should run twice on shared counters.
+	w0 := worker(0)
+	expect(t, work, w0, w0)
+
+	w1 := worker(1)
+	// Now Release to w0 which shouldn't allow it to run since it's just repaying, but
+	// should allow w1 to run on the returned shared counters.
+	w0.Release(2 * mtu)
+	expect(t, work, w1, w1)
+
+	// Releasing again will allow w0 to run.
+	w0.Release(mtu)
+	expect(t, work, w0)
+}
+
+func TestConcurrentRun(t *testing.T) {
+	ctx, cancel := context.RootContext()
+	defer cancel()
+	const mtu = 10
+	fc := New(mtu, mtu)
+
+	ready, wait := make(chan struct{}), make(chan struct{})
+	w := fc.NewWorker(0)
+	go w.Run(ctx, func(t int) (int, bool, error) {
+		close(ready)
+		<-wait
+		return t, true, nil
+	})
+	<-ready
+	if err := w.Run(ctx, nil); verror.ErrorID(err) != ErrConcurrentRun.ID {
+		t.Errorf("expected concurrent run error got: %v", err)
+	}
+	close(wait)
+}
+
+func TestNonFlowControlledRun(t *testing.T) {
+	ctx, cancel := context.RootContext()
+	defer cancel()
+	const mtu = 10
+	fc := New(0, mtu)
+
+	work := make(chan interface{})
+	ready, wait := make(chan struct{}), make(chan struct{})
+	// Start one worker running
+	go fc.Run(ctx, 0, func(t int) (int, bool, error) {
+		close(ready)
+		<-wait
+		return t, true, nil
+	})
+	<-ready
+	// Now queue up sever workers and make sure they execute in order.
+	go fc.Run(ctx, 2, func(t int) (int, bool, error) {
+		work <- "c"
+		return t, true, nil
+	})
+	go fc.Run(ctx, 1, func(t int) (int, bool, error) {
+		work <- "b"
+		return t, true, nil
+	})
+	go fc.Run(ctx, 0, func(t int) (int, bool, error) {
+		work <- "a"
+		return t, true, nil
+	})
+	for fc.numActive() < 4 {
+		time.Sleep(time.Millisecond)
+	}
+	close(wait)
+	expect(t, work, "a", "b", "c")
+}
+
+func newNullConn(mtu int) net.Conn {
+	ln, err := net.Listen("tcp", ":0")
+	if err != nil {
+		panic(err)
+	}
+	addr := ln.Addr()
+
+	go func() {
+		conn, err := ln.Accept()
+		if err != nil {
+			panic(err)
+		}
+		ln.Close()
+		buf := make([]byte, mtu)
+		for {
+			_, err := conn.Read(buf)
+			if err == io.EOF {
+				break
+			}
+			if err != nil {
+				panic(err)
+			}
+		}
+		conn.Close()
+	}()
+
+	conn, err := net.Dial(addr.Network(), addr.String())
+	if err != nil {
+		panic(err)
+	}
+	return conn
+}
+
+func BenchmarkWithFlowControl(b *testing.B) {
+	const (
+		mtu     = 1 << 16
+		shared  = 1 << 20
+		workers = 100
+	)
+	ctx, cancel := context.RootContext()
+	defer cancel()
+	s := newNullConn(mtu)
+
+	for n := 0; n < b.N; n++ {
+		fc := New(shared, mtu)
+		var wg sync.WaitGroup
+		wg.Add(workers)
+		for i := 0; i < workers; i++ {
+			go func(idx int) {
+				w := fc.NewWorker(0)
+				w.Release(len(testdata))
+				t := testdata
+				err := w.Run(ctx, func(tokens int) (used int, done bool, err error) {
+					towrite := min(tokens, len(t))
+					written, err := s.Write(t[:min(tokens, len(t))])
+					t = t[written:]
+					return towrite, len(t) == 0, err
+				})
+				if err != nil {
+					panic(err)
+				}
+				wg.Done()
+			}(i)
+		}
+		wg.Wait()
+	}
+	if err := s.Close(); err != nil {
+		panic(err)
+	}
+}
+
+func BenchmarkWithoutFlowControl(b *testing.B) {
+	const (
+		workers = 100
+		mtu     = 1 << 16
+	)
+	s := newNullConn(mtu)
+	for n := 0; n < b.N; n++ {
+		for cursor := 0; cursor < len(testdata); cursor += mtu {
+			for i := 0; i < workers; i++ {
+				_, err := s.Write(testdata[cursor : cursor+mtu])
+				if err != nil {
+					panic(err)
+				}
+			}
+		}
+	}
+	if err := s.Close(); err != nil {
+		panic(err)
+	}
+}
diff --git a/runtime/internal/flow/model.go b/runtime/internal/flow/model.go
index b43aeed..53de3e1 100644
--- a/runtime/internal/flow/model.go
+++ b/runtime/internal/flow/model.go
@@ -13,24 +13,39 @@
 
 // Manager is the interface for managing the creation of Flows.
 type Manager interface {
-	// Listen creates a Listener that can be used to listen on addresses
-	// and Accept flows initiates to those addresses.
+	// Acceptor creates a Acceptor that can be used to accept new flows from addresses
+	// that the Manager is listening on.
 	//
 	// For example:
-	//   ln, err := sm.NewListener(ctx, blessings)
-	//   ep, err := ln.Listen("tcp", ":0")
+	//   err := m.Listen(ctx, "tcp", ":0")
+	//   a, err := m.Acceptor(ctx, blessings)
 	//   for {
-	//     flow, err := ln.Accept(ctx)
+	//     flow, err := a.Accept(ctx)
 	//     // process flow
 	//   }
-	// can be used to accept Flows initiated by remote processes to the endpoint
-	// returned by ln.Listen.
 	//
-	// blessings are the Blessings presented to the Client during authentication.
-	NewListener(ctx *context.T, blessings security.Blessings) (Listener, error)
+	// can be used to accept Flows initiated by remote processes to the endpoints returned
+	// by Acceptor.Endpoints. All created Acceptors accept Flows from all addresses the
+	// Manager is listening on, but are differentiated by a RoutingID unique to each
+	// Acceptor.
+	//
+	// 'blessings' are the Blessings presented to the Client during authentication.
+	Acceptor(ctx *context.T, blessings security.Blessings) (Acceptor, error)
 
-	// Dial creates a Flow to the provided remote endpoint.
-	Dial(ctx *context.T, remote naming.Endpoint) (Flow, error)
+	// Listen causes the Manager to accept flows from the provided protocol and address.
+	// Listen may be called muliple times.
+	Listen(ctx *context.T, protocol, address string) error
+
+	// Dial creates a Flow to the provided remote endpoint. 'auth' is used to authorize
+	// the remote end.
+	//
+	// To maximize re-use of connections, the Manager will also Listen on Dialed
+	// connections for the lifetime of the connection.
+	//
+	// TODO(suharshs): Revisit passing in an authorizer here. Perhaps restrict server
+	// authorization to a smaller set of policies, or use a different mechanism to
+	// allow the user to specify a policy.
+	Dial(ctx *context.T, remote naming.Endpoint, auth security.Authorizer) (Flow, error)
 
 	// Closed returns a channel that remains open for the lifetime of the Manager
 	// object. Once the channel is closed any operations on the Manager will
@@ -52,26 +67,28 @@
 	RemoteBlessings() security.Blessings
 	// LocalDischarges returns the discharges presented by the local end of the flow during authentication.
 	//
-	// The discharges are organized in a map keyed by the discharge-identifier.
+	// Discharges are organized in a map keyed by the discharge-identifier.
 	LocalDischarges() map[string]security.Discharge
 	// RemoteDischarges returns the discharges presented by the remote end of the flow during authentication.
 	//
-	// The discharges are organized in a map keyed by the discharge-identifier.
+	// Discharges are organized in a map keyed by the discharge-identifier.
 	RemoteDischarges() map[string]security.Discharge
 
 	// Closed returns a channel that remains open until the flow has been closed or
-	// the ctx to the Dail or Accept call used to create the flow has been cancelled.
+	// the ctx to the Dial or Accept call used to create the flow has been cancelled.
 	Closed() <-chan struct{}
 }
 
-// Listener is the interface for accepting Flows created by a remote process.
-type Listener interface {
-	// Listen listens on the protocol and address specified. This may be called
-	// multiple times.
-	Listen(protocol, address string) (naming.Endpoint, error)
+// Acceptor is the interface for accepting Flows created by a remote process.
+type Acceptor interface {
+	// ListeningEndpoints returns the endpoints that the Manager has explicitly
+	// listened on. The Acceptor will accept new flows on these endpoints.
+	// Returned endpoints all have a RoutingID unique to the Acceptor.
+	ListeningEndpoints() []naming.Endpoint
 	// Accept blocks until a new Flow has been initiated by a remote process.
 	Accept(ctx *context.T) (Flow, error)
-	// Closed returns a channel that remains open until the Listener has been closed.
-	// i.e. the context provided to Manager.NewListener() has been cancelled.
+	// Closed returns a channel that remains open until the Acceptor has been closed.
+	// i.e. the context provided to Manager.Acceptor() has been cancelled. Once the
+	// channel is closed, all calls to Accept will result in an error.
 	Closed() <-chan struct{}
 }
diff --git a/services/application/applicationd/perms_test.go b/services/application/applicationd/perms_test.go
index c4446f9..207c0a2 100644
--- a/services/application/applicationd/perms_test.go
+++ b/services/application/applicationd/perms_test.go
@@ -39,8 +39,6 @@
 	publishName := args[0]
 	storedir := args[1]
 
-	v23.GetNamespace(ctx).CacheCtl(naming.DisableCache(true))
-
 	defer fmt.Fprintf(env.Stdout, "%v terminating\n", publishName)
 	defer ctx.VI(1).Infof("%v terminating", publishName)
 
@@ -50,7 +48,7 @@
 	}
 	server, err := xrpc.NewDispatchingServer(ctx, publishName, dispatcher)
 	if err != nil {
-		ctx.Fatalf("Serve(%v) failed: %v", publishName, err)
+		ctx.Fatalf("NewDispatchingServer(%v) failed: %v", publishName, err)
 	}
 	ctx.VI(1).Infof("applicationd name: %v", server.Status().Endpoints[0].Name())
 
@@ -63,17 +61,18 @@
 func TestApplicationUpdatePermissions(t *testing.T) {
 	ctx, shutdown := test.V23Init()
 	defer shutdown()
-	v23.GetNamespace(ctx).CacheCtl(naming.DisableCache(true))
 
-	// By default, all principals in this test will have blessings generated based
-	// on the username/machine running this process. Give them recognizable names
-	// ("root/self" etc.), so the Permissions can be set deterministically.
-	idp := testutil.NewIDProvider("root")
+	// V23Init sets the context up with a self-signed principal, whose
+	// blessing (test-blessing) will act as the root blessing for the test.
+	const rootBlessing = test.TestBlessing
+	idp := testutil.IDProviderFromPrincipal(v23.GetPrincipal(ctx))
+	// Call ourselves test-blessing/self, distinct from test-blessing/other
+	// which we'll give to the 'other' context.
 	if err := idp.Bless(v23.GetPrincipal(ctx), "self"); err != nil {
 		t.Fatal(err)
 	}
 
-	sh, deferFn := servicetest.CreateShellAndMountTable(t, ctx, v23.GetPrincipal(ctx))
+	sh, deferFn := servicetest.CreateShell(t, ctx, nil)
 	defer deferFn()
 
 	// setup mock up directory to put state in
@@ -122,29 +121,29 @@
 	}
 	expected := access.Permissions{
 		"Admin": access.AccessList{
-			In:    []security.BlessingPattern{"root/$", "root/self/$", "root/self/child"},
+			In:    []security.BlessingPattern{test.TestBlessing + "/$", rootBlessing + "/self/$", rootBlessing + "/self/child"},
 			NotIn: []string(nil)},
 		"Read": access.AccessList{
-			In:    []security.BlessingPattern{"root/$", "root/self/$", "root/self/child"},
+			In:    []security.BlessingPattern{rootBlessing + "/$", rootBlessing + "/self/$", rootBlessing + "/self/child"},
 			NotIn: []string(nil)},
 		"Write": access.AccessList{
-			In:    []security.BlessingPattern{"root/$", "root/self/$", "root/self/child"},
+			In:    []security.BlessingPattern{rootBlessing + "/$", rootBlessing + "/self/$", rootBlessing + "/self/child"},
 			NotIn: []string(nil)},
 		"Debug": access.AccessList{
-			In:    []security.BlessingPattern{"root/$", "root/self/$", "root/self/child"},
+			In:    []security.BlessingPattern{rootBlessing + "/$", rootBlessing + "/self/$", rootBlessing + "/self/child"},
 			NotIn: []string(nil)},
 		"Resolve": access.AccessList{
-			In:    []security.BlessingPattern{"root/$", "root/self/$", "root/self/child"},
+			In:    []security.BlessingPattern{rootBlessing + "/$", rootBlessing + "/self/$", rootBlessing + "/self/child"},
 			NotIn: []string(nil)}}
 	if got := perms; !reflect.DeepEqual(expected.Normalize(), got.Normalize()) {
-		t.Errorf("got %#v, exected %#v ", got, expected)
+		t.Errorf("got %#v, expected %#v ", got, expected)
 	}
 
 	ctx.VI(2).Infof("self attempting to give other permission to update application")
 	newPerms := make(access.Permissions)
 	for _, tag := range access.AllTypicalTags() {
-		newPerms.Add("root/self", string(tag))
-		newPerms.Add("root/other", string(tag))
+		newPerms.Add(rootBlessing+"/self", string(tag))
+		newPerms.Add(rootBlessing+"/other", string(tag))
 	}
 	if err := repostub.SetPermissions(ctx, newPerms, ""); err != nil {
 		t.Fatalf("SetPermissions failed: %v", err)
@@ -170,7 +169,7 @@
 		t.Fatalf("GetPermissions 2 should not have failed: %v", err)
 	}
 	perms["Admin"] = access.AccessList{
-		In:    []security.BlessingPattern{"root/other"},
+		In:    []security.BlessingPattern{rootBlessing + "/other"},
 		NotIn: []string{}}
 	if err = repostub.SetPermissions(otherCtx, perms, version); err != nil {
 		t.Fatalf("SetPermissions failed: %v", err)
@@ -186,19 +185,19 @@
 	}
 	expected = access.Permissions{
 		"Admin": access.AccessList{
-			In:    []security.BlessingPattern{"root/other"},
+			In:    []security.BlessingPattern{rootBlessing + "/other"},
 			NotIn: []string{}},
-		"Read": access.AccessList{In: []security.BlessingPattern{"root/other",
-			"root/self"},
+		"Read": access.AccessList{In: []security.BlessingPattern{rootBlessing + "/other",
+			rootBlessing + "/self"},
 			NotIn: []string{}},
-		"Write": access.AccessList{In: []security.BlessingPattern{"root/other",
-			"root/self"},
+		"Write": access.AccessList{In: []security.BlessingPattern{rootBlessing + "/other",
+			rootBlessing + "/self"},
 			NotIn: []string{}},
-		"Debug": access.AccessList{In: []security.BlessingPattern{"root/other",
-			"root/self"},
+		"Debug": access.AccessList{In: []security.BlessingPattern{rootBlessing + "/other",
+			rootBlessing + "/self"},
 			NotIn: []string{}},
-		"Resolve": access.AccessList{In: []security.BlessingPattern{"root/other",
-			"root/self"},
+		"Resolve": access.AccessList{In: []security.BlessingPattern{rootBlessing + "/other",
+			rootBlessing + "/self"},
 			NotIn: []string{}}}
 
 	if got := perms; !reflect.DeepEqual(expected.Normalize(), got.Normalize()) {
diff --git a/services/device/internal/impl/app_service.go b/services/device/internal/impl/app_service.go
index fabd4cd..4712114 100644
--- a/services/device/internal/impl/app_service.go
+++ b/services/device/internal/impl/app_service.go
@@ -127,6 +127,7 @@
 	"path"
 	"path/filepath"
 	"reflect"
+	"regexp"
 	"strconv"
 	"strings"
 	"text/template"
@@ -204,6 +205,8 @@
 	reap *reaper
 	// mtAddress is the address of the local mounttable.
 	mtAddress string
+	// appServiceName is a name by which the appService can be reached
+	appServiceName string
 }
 
 // appService implements the Device manager's Application interface.
@@ -337,6 +340,12 @@
 // title, and thereby being installed in the same app dir.  Do we want to
 // prevent that for the same user or across users?
 
+const (
+	appDirPrefix       = "app-"
+	installationPrefix = "installation-"
+	instancePrefix     = "instance-"
+)
+
 // applicationDirName generates a cryptographic hash of the application title,
 // to be used as a directory name for installations of the application with the
 // given title.
@@ -344,15 +353,15 @@
 	h := md5.New()
 	h.Write([]byte(title))
 	hash := strings.TrimRight(base64.URLEncoding.EncodeToString(h.Sum(nil)), "=")
-	return "app-" + hash
+	return appDirPrefix + hash
 }
 
 func installationDirName(installationID string) string {
-	return "installation-" + installationID
+	return installationPrefix + installationID
 }
 
 func instanceDirName(instanceID string) string {
-	return "instance-" + instanceID
+	return instancePrefix + instanceID
 }
 
 func mkdir(dir string) error {
@@ -837,6 +846,20 @@
 	return suidHelper.getAppCmd(&saArgs)
 }
 
+// instanceNameFromDir returns the instance name, given the instanceDir.
+func instanceNameFromDir(ctx *context.T, instanceDir string) (string, error) {
+	_, _, installation, instance := parseInstanceDir(instanceDir)
+	if installation == "" || instance == "" {
+		return "", fmt.Errorf("Unable to parse instanceDir %v", instanceDir)
+	}
+
+	env, err := loadEnvelopeForInstance(ctx, instanceDir)
+	if err != nil {
+		return "", err
+	}
+	return env.Title + "/" + installation + "/" + instance, nil
+}
+
 func (i *appRunner) startCmd(ctx *context.T, instanceDir string, cmd *exec.Cmd) (int, error) {
 	info, err := loadInstanceInfo(ctx, instanceDir)
 	if err != nil {
@@ -863,6 +886,14 @@
 	cfg.Set(mgmt.ProtocolConfigKey, "tcp")
 	cfg.Set(mgmt.AddressConfigKey, "127.0.0.1:0")
 	cfg.Set(mgmt.ParentBlessingConfigKey, info.DeviceManagerPeerPattern)
+	cfg.Set(mgmt.PublisherBlessingPrefixesKey,
+		v23.GetPrincipal(ctx).BlessingStore().Default().String())
+
+	if instanceName, err := instanceNameFromDir(ctx, instanceDir); err != nil {
+		return 0, err
+	} else {
+		cfg.Set(mgmt.InstanceNameKey, naming.Join(i.appServiceName, instanceName))
+	}
 
 	appPermsDir := filepath.Join(instanceDir, "debugacls", "data")
 	cfg.Set("v23.permissions.file", "runtime:"+appPermsDir)
@@ -990,7 +1021,7 @@
 // device manager then crashes between the reaper marking the app not
 // running and the go routine invoking this function having a chance to
 // complete.
-func (i *appRunner) restartAppIfNecessary(instanceDir string) {
+func (i *appRunner) restartAppIfNecessary(ctx *context.T, instanceDir string) {
 	if err := transitionInstance(instanceDir, device.InstanceStateNotRunning, device.InstanceStateLaunching); err != nil {
 		vlog.Error(err)
 		return
@@ -1007,7 +1038,7 @@
 		return
 	}
 
-	if err := i.run(nil, instanceDir); err != nil {
+	if err := i.run(ctx, instanceDir); err != nil {
 		vlog.Error(err)
 	}
 }
@@ -1040,6 +1071,17 @@
 	return instanceDir, nil
 }
 
+// parseInstanceDir is a partial inverse of instanceDir. It cannot retrieve the app name,
+// as that has been hashed so it returns an appDir instead.
+func parseInstanceDir(dir string) (prefix, appDir, installation, instance string) {
+	dirRE := regexp.MustCompile("(/.*)(/" + appDirPrefix + "[^/]+)/" + installationPrefix + "([^/]+)/" + "instances/" + instancePrefix + "([^/]+)$")
+	matches := dirRE.FindStringSubmatch(dir)
+	if len(matches) < 5 {
+		return "", "", "", ""
+	}
+	return matches[1], matches[2], matches[3], matches[4]
+}
+
 // instanceDir returns the path to the directory containing the app instance
 // referred to by the invoker's suffix, as well as the corresponding not-running
 // instance dir.  Returns an error if the suffix does not name an instance.
@@ -1322,7 +1364,7 @@
 
 func (i *appService) scanEnvelopes(ctx *context.T, tree *treeNode, appDir string) {
 	// Find all envelopes, extract installID.
-	envGlob := []string{i.config.Root, appDir, "installation-*", "*", "envelope"}
+	envGlob := []string{i.config.Root, appDir, installationPrefix + "*", "*", "envelope"}
 	envelopes, err := filepath.Glob(filepath.Join(envGlob...))
 	if err != nil {
 		vlog.Errorf("unexpected error: %v", err)
@@ -1339,7 +1381,7 @@
 			vlog.Errorf("unexpected number of path components: %q (%q)", elems, path)
 			continue
 		}
-		installID := strings.TrimPrefix(elems[1], "installation-")
+		installID := strings.TrimPrefix(elems[1], installationPrefix)
 		tree.find([]string{env.Title, installID}, true)
 	}
 	return
@@ -1357,7 +1399,7 @@
 	// Add the node corresponding to the installation itself.
 	tree.find(i.suffix[:2], true)
 	// Find all instances.
-	infoGlob := []string{installDir, "instances", "instance-*", "info"}
+	infoGlob := []string{installDir, "instances", instancePrefix + "*", "info"}
 	instances, err := filepath.Glob(filepath.Join(infoGlob...))
 	if err != nil {
 		vlog.Errorf("unexpected error: %v", err)
@@ -1374,14 +1416,12 @@
 	if _, err := loadInstanceInfo(ctx, instanceDir); err != nil {
 		return
 	}
-	relpath, _ := filepath.Rel(i.config.Root, instanceDir)
-	elems := strings.Split(relpath, string(filepath.Separator))
-	if len(elems) < 4 {
-		vlog.Errorf("unexpected number of path components: %q (%q)", elems, instanceDir)
+	rootDir, _, installID, instanceID := parseInstanceDir(instanceDir)
+	if installID == "" || instanceID == "" || filepath.Clean(i.config.Root) != filepath.Clean(rootDir) {
+		vlog.Errorf("failed to parse instanceDir %v (got: %v %v %v)", instanceDir, rootDir, installID, instanceID)
 		return
 	}
-	installID := strings.TrimPrefix(elems[1], "installation-")
-	instanceID := strings.TrimPrefix(elems[3], "instance-")
+
 	tree.find([]string{title, installID, instanceID, "logs"}, true)
 	if instanceStateIs(instanceDir, device.InstanceStateRunning) {
 		for _, obj := range []string{"pprof", "stats"} {
@@ -1394,7 +1434,7 @@
 	tree := newTreeNode()
 	switch len(i.suffix) {
 	case 0:
-		i.scanEnvelopes(ctx, tree, "app-*")
+		i.scanEnvelopes(ctx, tree, appDirPrefix+"*")
 	case 1:
 		appDir := applicationDirName(i.suffix[0])
 		i.scanEnvelopes(ctx, tree, appDir)
diff --git a/services/device/internal/impl/applife/app_life_test.go b/services/device/internal/impl/applife/app_life_test.go
index cde8c9a..0e42790 100644
--- a/services/device/internal/impl/applife/app_life_test.go
+++ b/services/device/internal/impl/applife/app_life_test.go
@@ -161,10 +161,32 @@
 	verifyAppPeerBlessings(t, ctx, pubCtx, instanceDebug, envelope)
 
 	// Wait until the app pings us that it's ready.
-	pingCh.VerifyPingArgs(t, utiltest.UserName(t), "flag-val-install", "env-val-envelope")
-
+	pingResult := pingCh.VerifyPingArgs(t, utiltest.UserName(t), "flag-val-install", "env-val-envelope")
 	v1EP1 := utiltest.Resolve(t, ctx, "appV1", 1, true)[0]
 
+	// Check that the instance name handed to the app looks plausible
+	nameRE := regexp.MustCompile(".*/apps/google naps/[^/]+/[^/]+$")
+	if nameRE.FindString(pingResult.InstanceName) == "" {
+		t.Fatalf("Unexpected instance name: %v", pingResult.InstanceName)
+	}
+
+	// There should be at least one publisher blessing prefix, and all prefixes should
+	// end in "/mydevice" because they are just the device manager's blessings
+	prefixes := strings.Split(pingResult.PubBlessingPrefixes, ",")
+	if len(prefixes) == 0 {
+		t.Fatalf("No publisher blessing prefixes found: %v", pingResult)
+	}
+	for _, p := range prefixes {
+		if !strings.HasSuffix(p, "/mydevice") {
+			t.Fatalf("publisher Blessing prefixes don't look right: %v", pingResult.PubBlessingPrefixes)
+		}
+	}
+
+	// We used a signed envelope, so there should have been some publisher blessings
+	if !hasPrefixMatches(pingResult.PubBlessingPrefixes, pingResult.DefaultPeerBlessings) {
+		t.Fatalf("Publisher Blessing Prefixes are not as expected: %v vs %v", pingResult.PubBlessingPrefixes, pingResult.DefaultPeerBlessings)
+	}
+
 	// Stop the app instance.
 	utiltest.KillApp(t, ctx, appID, instance1ID)
 	utiltest.VerifyState(t, ctx, device.InstanceStateNotRunning, appID, instance1ID)
@@ -265,10 +287,21 @@
 	}
 	// Resume the first instance and verify it's running v2 now.
 	utiltest.RunApp(t, ctx, appID, instance1ID)
-	pingCh.VerifyPingArgs(t, utiltest.UserName(t), "flag-val-install", "env-val-envelope")
+	pingResult = pingCh.VerifyPingArgs(t, utiltest.UserName(t), "flag-val-install", "env-val-envelope")
 	utiltest.Resolve(t, ctx, "appV1", 1, false)
 	utiltest.Resolve(t, ctx, "appV2", 1, false)
 
+	// Although v2 does not have a signed envelope, this was an update of v1, which did.
+	// This app's config still includes publisher blessing prefixes, and it should still
+	// have the publisher blessing it acquired earlier.
+	//
+	// TODO: This behavior is non-ideal. A reasonable requirement in future would be that
+	// the publisher blessing string remain unchanged on updates to an installation, just as the
+	// title is not allowed to change.
+	if !hasPrefixMatches(pingResult.PubBlessingPrefixes, pingResult.DefaultPeerBlessings) {
+		t.Fatalf("Publisher Blessing Prefixes are not as expected: %v vs %v", pingResult.PubBlessingPrefixes, pingResult.DefaultPeerBlessings)
+	}
+
 	// Reverting first instance fails since it's still running.
 	utiltest.RevertAppExpectError(t, ctx, appID+"/"+instance1ID, errors.ErrInvalidOperation.ID)
 	// Stop first instance and try again.
@@ -289,7 +322,12 @@
 		t.Fatalf("Instance version expected to be %v, got %v instead", v2, v)
 	}
 	// Wait until the app pings us that it's ready.
-	pingCh.VerifyPingArgs(t, utiltest.UserName(t), "flag-val-install", "env-val-envelope")
+	pingResult = pingCh.VerifyPingArgs(t, utiltest.UserName(t), "flag-val-install", "env-val-envelope")
+	// This app should not have publisher blessings. It was started from an installation
+	// that did not have a signed envelope.
+	if hasPrefixMatches(pingResult.PubBlessingPrefixes, pingResult.DefaultPeerBlessings) {
+		t.Fatalf("Publisher Blessing Prefixes are not as expected: %v vs %v", pingResult.PubBlessingPrefixes, pingResult.DefaultPeerBlessings)
+	}
 
 	utiltest.Resolve(t, ctx, "appV2", 1, true)
 
@@ -561,6 +599,22 @@
 	return pubCtx, nil
 }
 
+// findPrefixMatches takes a set of comma-separated prefixes, and a set of comma-separated
+// strings, and checks if any of the strings match any of the prefixes
+func hasPrefixMatches(prefixList, stringList string) bool {
+	prefixes := strings.Split(prefixList, ",")
+	inStrings := strings.Split(stringList, ",")
+
+	for _, s := range inStrings {
+		for _, p := range prefixes {
+			if strings.HasPrefix(s, p) {
+				return true
+			}
+		}
+	}
+	return false
+}
+
 // verifyAppPeerBlessings checks the instanceDebug string to ensure that the app is running with
 // the expected blessings for peer "..." (i.e. security.AllPrincipals) .
 //
diff --git a/services/device/internal/impl/dispatcher.go b/services/device/internal/impl/dispatcher.go
index 2afb044..f016313 100644
--- a/services/device/internal/impl/dispatcher.go
+++ b/services/device/internal/impl/dispatcher.go
@@ -135,8 +135,9 @@
 		}
 	}
 	reap, err := newReaper(ctx, config.Root, &appRunner{
-		callback:      d.internal.callback,
-		securityAgent: d.internal.securityAgent,
+		callback:       d.internal.callback,
+		securityAgent:  d.internal.securityAgent,
+		appServiceName: naming.Join(d.config.Name, appsSuffix),
 	})
 	if err != nil {
 		return nil, verror.New(errCantCreateAppWatcher, ctx, err)
@@ -324,10 +325,11 @@
 			uat:        d.uat,
 			permsStore: d.permsStore,
 			runner: &appRunner{
-				reap:          d.internal.reap,
-				callback:      d.internal.callback,
-				securityAgent: d.internal.securityAgent,
-				mtAddress:     d.mtAddress,
+				reap:           d.internal.reap,
+				callback:       d.internal.callback,
+				securityAgent:  d.internal.securityAgent,
+				mtAddress:      d.mtAddress,
+				appServiceName: naming.Join(d.config.Name, appsSuffix),
 			},
 		})
 		appSpecificAuthorizer, err := newAppSpecificAuthorizer(auth, d.config, components[1:], d.permsStore)
diff --git a/services/device/internal/impl/instance_reaping.go b/services/device/internal/impl/instance_reaping.go
index 9bfb4e1..7f17aed 100644
--- a/services/device/internal/impl/instance_reaping.go
+++ b/services/device/internal/impl/instance_reaping.go
@@ -46,6 +46,7 @@
 type reaper struct {
 	c          chan pidInstanceDirPair
 	startState *appRunner
+	ctx        *context.T
 }
 
 var stashedPidMap map[string]int
@@ -63,13 +64,14 @@
 	r := &reaper{
 		c:          make(chan pidInstanceDirPair),
 		startState: startState,
+		ctx:        ctx,
 	}
 	r.startState.reap = r
 	go r.processStatusPolling(pidMap)
 
 	// Restart daemon jobs if they're not running (say because the machine crashed.)
 	for _, idir := range restartCandidates {
-		go r.startState.restartAppIfNecessary(idir)
+		go r.startState.restartAppIfNecessary(ctx, idir)
 	}
 	return r, nil
 }
@@ -99,7 +101,7 @@
 				// No such PID.
 				vlog.VI(2).Infof("processStatusPolling discovered pid %d ended", pid)
 				markNotRunning(idir)
-				go r.startState.restartAppIfNecessary(idir)
+				go r.startState.restartAppIfNecessary(r.ctx, idir)
 				delete(trackedPids, idir)
 			case nil, syscall.EPERM:
 				vlog.VI(2).Infof("processStatusPolling saw live pid: %d", pid)
diff --git a/services/device/internal/impl/profile.go b/services/device/internal/impl/profile.go
index ae181d8..74f9ad1 100644
--- a/services/device/internal/impl/profile.go
+++ b/services/device/internal/impl/profile.go
@@ -170,7 +170,13 @@
 			Os:          build.OperatingSystemLinux,
 			Format:      build.FormatElf,
 		},
-		// TODO(caprita): Add other profiles for Mac, Pi, etc.
+		{
+			Label:       "darwin-amd64",
+			Description: "",
+			Arch:        build.ArchitectureAmd64,
+			Os:          build.OperatingSystemDarwin,
+			Format:      build.FormatMach,
+		},
 	}, nil
 
 	// TODO(jsimsa): This function assumes the existence of a profile
diff --git a/services/device/internal/impl/utiltest/app.go b/services/device/internal/impl/utiltest/app.go
index 1f959b8..093796d 100644
--- a/services/device/internal/impl/utiltest/app.go
+++ b/services/device/internal/impl/utiltest/app.go
@@ -11,7 +11,6 @@
 	"io/ioutil"
 	"os"
 	"path/filepath"
-	"reflect"
 	"strings"
 	"testing"
 	"time"
@@ -22,6 +21,8 @@
 	"v.io/v23/rpc"
 	"v.io/v23/security"
 	"v.io/x/lib/vlog"
+	"v.io/x/ref/lib/exec"
+	"v.io/x/ref/lib/mgmt"
 	"v.io/x/ref/lib/signals"
 	"v.io/x/ref/lib/xrpc"
 	"v.io/x/ref/services/device/internal/suid"
@@ -63,8 +64,9 @@
 }
 
 type PingArgs struct {
-	Username, FlagValue, EnvValue string
-	Pid                           int
+	Username, FlagValue, EnvValue,
+	DefaultPeerBlessings, PubBlessingPrefixes, InstanceName string
+	Pid int
 }
 
 // ping makes a RPC from the App back to the invoking device manager
@@ -82,11 +84,20 @@
 	args := &PingArgs{
 		// TODO(rjkroege): Consider validating additional parameters
 		// from helper.
-		Username:  savedArgs.Uname,
-		FlagValue: flagValue,
-		EnvValue:  os.Getenv(TestEnvVarName),
-		Pid:       os.Getpid(),
+		Username:             savedArgs.Uname,
+		FlagValue:            flagValue,
+		EnvValue:             os.Getenv(TestEnvVarName),
+		Pid:                  os.Getpid(),
+		DefaultPeerBlessings: v23.GetPrincipal(ctx).BlessingStore().ForPeer("nonexistent").String(),
 	}
+
+	if handle, err := exec.GetChildHandle(); err != nil {
+		vlog.Fatalf("Couldn't get Child Handle: %v", err)
+	} else {
+		args.PubBlessingPrefixes, _ = handle.Config.Get(mgmt.PublisherBlessingPrefixesKey)
+		args.InstanceName, _ = handle.Config.Get(mgmt.InstanceNameKey)
+	}
+
 	client := v23.GetClient(ctx)
 	if call, err := client.StartCall(ctx, "pingserver", "Ping", []interface{}{args}); err != nil {
 		vlog.Fatalf("StartCall failed: %v", err)
@@ -179,17 +190,12 @@
 	return args
 }
 
-func (p PingServer) VerifyPingArgs(t *testing.T, username, flagValue, envValue string) {
+func (p PingServer) VerifyPingArgs(t *testing.T, username, flagValue, envValue string) PingArgs {
 	args := p.WaitForPingArgs(t)
-	wantArgs := PingArgs{
-		Username:  username,
-		FlagValue: flagValue,
-		EnvValue:  envValue,
-		Pid:       args.Pid, // We are not checking for a value of Pid
+	if args.Username != username || args.FlagValue != flagValue || args.EnvValue != envValue {
+		t.Fatalf(testutil.FormatLogLine(2, "got ping args %q, expected [username = %v, flag value = %v, env value = %v]", args, username, flagValue, envValue))
 	}
-	if !reflect.DeepEqual(args, wantArgs) {
-		t.Fatalf(testutil.FormatLogLine(2, "got ping args %q, expected %q", args, wantArgs))
-	}
+	return args // Useful for tests that want to check other values in the PingArgs result
 }
 
 // HangingApp is the same as App, except that it does not exit properly after
diff --git a/services/device/mgmt_v23_test.go b/services/device/mgmt_v23_test.go
index 61d2ada..bed7179 100644
--- a/services/device/mgmt_v23_test.go
+++ b/services/device/mgmt_v23_test.go
@@ -120,21 +120,20 @@
 		binStagingDir = mkSubdir(i, workDir, "bin")
 		dmInstallDir  = filepath.Join(workDir, "dm")
 
-		// All vanadium command-line utitilities will be run by a
-		// principal that has "root/alice" as its blessing.
+		// Most vanadium command-line utilities will be run by a
+		// principal that has "root/u/alice" as its blessing.
 		// (Where "root" comes from i.Principal().BlessingStore().Default()).
 		// Create those credentials and options to use to setup the
 		// binaries with them.
-		aliceCreds, _ = i.Shell().NewChildCredentials("alice")
+		aliceCreds, _ = i.Shell().NewChildCredentials("u/alice")
 		aliceOpts     = i.Shell().DefaultStartOpts().ExternalProgram().WithCustomCredentials(aliceCreds)
 
 		// Build all the command-line tools and set them up to run as alice.
 		// applicationd/binaryd servers will be run by alice too.
+		// TODO: applicationd/binaryd should run as a separate "service" role, as
+		// alice is just a user.
 		namespaceBin    = i.BuildV23Pkg("v.io/x/ref/cmd/namespace").WithStartOpts(aliceOpts)
-		debugBin        = i.BuildV23Pkg("v.io/x/ref/services/debug/debug").WithStartOpts(aliceOpts)
 		deviceBin       = i.BuildV23Pkg("v.io/x/ref/services/device/device").WithStartOpts(aliceOpts)
-		binaryBin       = i.BuildV23Pkg("v.io/x/ref/services/binary/binary").WithStartOpts(aliceOpts)
-		applicationBin  = i.BuildV23Pkg("v.io/x/ref/services/application/application").WithStartOpts(aliceOpts)
 		binarydBin      = i.BuildV23Pkg("v.io/x/ref/services/binary/binaryd").WithStartOpts(aliceOpts)
 		applicationdBin = i.BuildV23Pkg("v.io/x/ref/services/application/applicationd").WithStartOpts(aliceOpts)
 
@@ -150,6 +149,42 @@
 		mtName = "devices/" + hostname // Name under which the device manager will publish itself.
 	)
 
+	// We also need some tools running with different sets of credentials...
+
+	// Administration tasks will be performed with a blessing that represents a corporate
+	// adminstrator (which is usually a role account)
+	adminCreds, err := i.Shell().NewChildCredentials("r/admin")
+	if err != nil {
+		i.Fatalf("generating admin creds: %v", err)
+	}
+	adminOpts := i.Shell().DefaultStartOpts().ExternalProgram().WithCustomCredentials(adminCreds)
+	adminDeviceBin := deviceBin.WithStartOpts(adminOpts)
+	debugBin := i.BuildV23Pkg("v.io/x/ref/services/debug/debug").WithStartOpts(adminOpts)
+
+	// A special set of credentials will be used to give two blessings to the device manager
+	// when claiming it -- one blessing will be from the corporate administrator role who owns
+	// the machine, and the other will be a manufacturer blessing. (This is a hack until
+	// there's a way to separately supply a manufacturer blessing. Eventually, the claim
+	// would really be done by the administator, and the adminstrator's blessing would get
+	// added to the manufacturer's blessing, which would already be present.)
+	claimCreds, err := i.Shell().AddToChildCredentials(adminCreds, "m/orange/zphone5/ime-i007")
+	if err != nil {
+		i.Fatalf("adding the mfr blessing to admin creds: %v", err)
+	}
+	claimOpts := i.Shell().DefaultStartOpts().ExternalProgram().WithCustomCredentials(claimCreds)
+	claimDeviceBin := deviceBin.WithStartOpts(claimOpts)
+
+	// Another set of credentials be used to represent the application publisher, who
+	// signs and pushes binaries
+	pubCreds, err := i.Shell().NewChildCredentials("a/rovio")
+	if err != nil {
+		i.Fatalf("generating publisher creds: %v", err)
+	}
+	pubOpts := i.Shell().DefaultStartOpts().ExternalProgram().WithCustomCredentials(pubCreds)
+	pubDeviceBin := deviceBin.WithStartOpts(pubOpts)
+	applicationBin := i.BuildV23Pkg("v.io/x/ref/services/application/application").WithStartOpts(pubOpts)
+	binaryBin := i.BuildV23Pkg("v.io/x/ref/services/binary/binary").WithStartOpts(pubOpts)
+
 	if withSuid {
 		// In multiuser mode, deviceUserFlag needs execute access to
 		// tempDir.
@@ -167,7 +202,7 @@
 		"v.io/x/ref/services/device/suidhelper",
 		"v.io/x/ref/services/device/inithelper")
 
-	appDName := "applicationd"
+	appDName := "applications"
 	devicedAppName := filepath.Join(appDName, "deviced", "test")
 
 	deviceScriptArguments := []string{
@@ -218,8 +253,8 @@
 		claimableEP = string(matches[1])
 		break
 	}
-	// Claim the device as "root/alice/myworkstation".
-	deviceBin.Start("claim", claimableEP, "myworkstation")
+	// Claim the device as "root/u/alice/myworkstation".
+	claimDeviceBin.Start("claim", claimableEP, "myworkstation")
 
 	resolve := func(name string) string {
 		resolver := func() (interface{}, error) {
@@ -239,22 +274,25 @@
 
 	// Wait for the device manager to publish its mount table entry.
 	mtEP := resolve(mtName)
+	adminDeviceBin.Run("acl", "set", mtName+"/devmgr/device", "root/u/alice", "Read,Resolve,Write")
 
 	if withSuid {
-		deviceBin.Start("associate", "add", mtName+"/devmgr/device", appUser, "root/alice")
+		adminDeviceBin.Start("associate", "add", mtName+"/devmgr/device", appUser, "root/u/alice")
 
-		aai := deviceBin.Start("associate", "list", mtName+"/devmgr/device")
-		if got, expected := strings.Trim(aai.Output(), "\n "), "root/alice "+appUser; got != expected {
+		aai := adminDeviceBin.Start("associate", "list", mtName+"/devmgr/device")
+		if got, expected := strings.Trim(aai.Output(), "\n "), "root/u/alice "+appUser; got != expected {
 			i.Fatalf("association test, got %v, expected %v", got, expected)
 		}
 	}
 
 	// Verify the device's default blessing is as expected.
+	mfrBlessing := "root/m/orange/zphone5/ime-i007/myworkstation"
+	ownerBlessing := "root/r/admin/myworkstation"
 	inv := debugBin.Start("stats", "read", mtName+"/devmgr/__debug/stats/security/principal/*/blessingstore")
-	inv.ExpectSetEventuallyRE(".*Default Blessings[ ]+root/alice/myworkstation$")
+	inv.ExpectSetEventuallyRE(".*Default Blessings[ ]+" + mfrBlessing + "," + ownerBlessing)
 
 	// Get the device's profile, which should be set to non-empty string
-	inv = deviceBin.Start("describe", mtName+"/devmgr/device")
+	inv = adminDeviceBin.Start("describe", mtName+"/devmgr/device")
 
 	parts := inv.ExpectRE(`{Profiles:map\[(.*):{}\]}`, 1)
 	expectOneMatch := func(parts [][]string) string {
@@ -271,17 +309,19 @@
 
 	// Start a binaryd server that will serve the binary for the test
 	// application to be installed on the device.
-	binarydName := "binaryd"
+	binarydName := "binaries"
 	binarydBin.Start(
 		"--name="+binarydName,
 		"--root-dir="+filepath.Join(workDir, "binstore"),
 		"--v23.tcp.address=127.0.0.1:0",
 		"--http=127.0.0.1:0")
-	sampleAppBinName := binarydName + "/testapp"
-	binaryBin.Run("upload", sampleAppBinName, binarydBin.Path())
-	if got := namespaceBin.Run("glob", sampleAppBinName); len(got) == 0 {
-		i.Fatalf("glob failed for %q", sampleAppBinName)
-	}
+	// Allow publishers to update binaries
+	deviceBin.Run("acl", "set", binarydName, "root/a", "Write")
+
+	// We are also going to use the binaryd binary as our test app binary. Once our test app
+	// binary is published to the binaryd server started above, this (augmented with a
+	// timestamp) is the name the test app binary will have.
+	sampleAppBinName := binarydName + "/binaryd"
 
 	// Start an applicationd server that will serve the application
 	// envelope for the test application to be installed on the device.
@@ -290,7 +330,10 @@
 		"--store="+mkSubdir(i, workDir, "appstore"),
 		"--v23.tcp.address=127.0.0.1:0",
 	)
-	sampleAppName := appDName + "/testapp/v0"
+	// Allow publishers to create and update envelopes
+	deviceBin.Run("acl", "set", appDName, "root/a", "Read,Write,Resolve")
+
+	sampleAppName := appDName + "/testapp/0"
 	appPubName := "testbinaryd"
 	appEnvelopeFilename := filepath.Join(workDir, "app.envelope")
 	appEnvelope := fmt.Sprintf("{\"Title\":\"BINARYD\", \"Args\":[\"--name=%s\", \"--root-dir=./binstore\", \"--v23.tcp.address=127.0.0.1:0\", \"--http=127.0.0.1:0\"], \"Binary\":{\"File\":%q}, \"Env\":[]}", appPubName, sampleAppBinName)
@@ -314,6 +357,12 @@
 		}
 	}
 
+	// Publish the app (This uses the binarydBin binary and the testapp envelope from above)
+	pubDeviceBin.Start("publish", "-from", filepath.Dir(binarydBin.Path()), "-readers", "root/r/admin", filepath.Base(binarydBin.Path())+":testapp").WaitOrDie(os.Stdout, os.Stderr)
+	if got := namespaceBin.Run("glob", sampleAppBinName); len(got) == 0 {
+		i.Fatalf("glob failed for %q", sampleAppBinName)
+	}
+
 	// Install the app on the device.
 	inv = deviceBin.Start("install", mtName+"/devmgr/apps", sampleAppName)
 	installationName := inv.ReadLine()
@@ -353,9 +402,13 @@
 		i.Errorf("app expected to be running as %v but is running as %v", appUser, uname)
 	}
 
-	// Verify the app's default blessing.
+	// Verify the app's blessings. We check the default blessing, as well as the
+	// "..." blessing, which should be the default blessing plus a publisher blessing.
+	userBlessing := "root/u/alice/myapp"
+	pubBlessing := "root/a/rovio/apps/published/binaryd"
+	appBlessing := mfrBlessing + "/a/" + pubBlessing + "," + ownerBlessing + "/a/" + pubBlessing
 	inv = debugBin.Start("stats", "read", instanceName+"/stats/security/principal/*/blessingstore")
-	inv.ExpectSetEventuallyRE(".*Default Blessings[ ]+root/alice/myapp$")
+	inv.ExpectSetEventuallyRE(".*Default Blessings[ ]+"+userBlessing+"$", "[.][.][.][ ]+"+userBlessing+","+appBlessing)
 
 	// Kill and delete the instance.
 	deviceBin.Run("kill", instanceName)
@@ -370,9 +423,15 @@
 		i.Fatalf("output expected for glob %s/logs/..., but got none", instanceName)
 	}
 
+	// TODO: The deviced binary should probably be published by someone other than rovio :-)
+	// Maybe publishing the deviced binary should eventually use "device publish" too?
+	// For now, it uses the "application" and "binary" tools directly to ensure that those work
+
 	// Upload a deviced binary
 	devicedAppBinName := binarydName + "/deviced"
 	binaryBin.Run("upload", devicedAppBinName, i.BuildGoPkg("v.io/x/ref/services/device/deviced").Path())
+	// Allow root/r/admin and its devices to read the binary
+	deviceBin.Run("acl", "set", devicedAppBinName, "root/r/admin", "Read")
 
 	// Upload a device manager envelope.
 	devicedEnvelopeFilename := filepath.Join(workDir, "deviced.envelope")
@@ -380,9 +439,11 @@
 	ioutil.WriteFile(devicedEnvelopeFilename, []byte(devicedEnvelope), 0666)
 	defer os.Remove(devicedEnvelopeFilename)
 	applicationBin.Run("put", devicedAppName, deviceProfile, devicedEnvelopeFilename)
+	// Allow root/r/admin and its devices to read the envelope
+	deviceBin.Run("acl", "set", devicedAppName, "root/r/admin", "Read")
 
 	// Update the device manager.
-	deviceBin.Run("update", mtName+"/devmgr/device")
+	adminDeviceBin.Run("update", mtName+"/devmgr/device")
 	resolveChange := func(name, old string) string {
 		resolver := func() (interface{}, error) {
 			inv := namespaceBin.Start("resolve", name)
@@ -403,7 +464,7 @@
 	}
 
 	// Revert the device manager
-	deviceBin.Run("revert", mtName+"/devmgr/device")
+	adminDeviceBin.Run("revert", mtName+"/devmgr/device")
 	mtEP = resolveChange(mtName, mtEP)
 
 	// Verify that device manager's mounttable is still published under the
diff --git a/services/identity/internal/auditor/sql_database.go b/services/identity/internal/auditor/sql_database.go
index 222c2b3..9dac3a8 100644
--- a/services/identity/internal/auditor/sql_database.go
+++ b/services/identity/internal/auditor/sql_database.go
@@ -74,6 +74,7 @@
 		dst <- databaseEntry{decodeErr: fmt.Errorf("Failed to query for all audits: %v", err)}
 		return
 	}
+	defer rows.Close()
 	for rows.Next() {
 		var dbentry databaseEntry
 		if err = rows.Scan(&dbentry.email, &dbentry.caveats, &dbentry.timestamp, &dbentry.blessings); err != nil {
diff --git a/services/identity/internal/blesser/oauth.go b/services/identity/internal/blesser/oauth.go
index 16c0866..4403a95 100644
--- a/services/identity/internal/blesser/oauth.go
+++ b/services/identity/internal/blesser/oauth.go
@@ -76,16 +76,7 @@
 	var caveat security.Caveat
 	var err error
 	if b.revocationManager != nil {
-		// TODO(ashankar,suharshs): Remove: Added for debugging
-		start := time.Now()
 		caveat, err = b.revocationManager.NewCaveat(self.PublicKey(), b.dischargerLocation)
-		var id string
-		if caveat.ThirdPartyDetails() != nil {
-			id = caveat.ThirdPartyDetails().ID()
-		}
-		if d := time.Since(start); d > time.Second || err != nil {
-			ctx.Infof("NewCaveat took %v and returned error %v (caveat id: %v): (%v <-> %v)", d, err, id, call.RemoteEndpoint(), call.LocalEndpoint())
-		}
 	} else {
 		caveat, err = security.NewExpiryCaveat(time.Now().Add(b.duration))
 	}
diff --git a/services/identity/internal/revocation/revocation_manager.go b/services/identity/internal/revocation/revocation_manager.go
index e9f6649..9a274ee 100644
--- a/services/identity/internal/revocation/revocation_manager.go
+++ b/services/identity/internal/revocation/revocation_manager.go
@@ -62,10 +62,7 @@
 	if err != nil {
 		return empty, err
 	}
-	r.ctx.Infof("revocationDB.InsertCaveat(%s,%v) called", cav.ThirdPartyDetails().ID(), revocation)
 	if err = revocationDB.InsertCaveat(cav.ThirdPartyDetails().ID(), revocation[:]); err != nil {
-		// TODO(suharshs): Remove this log.
-		r.ctx.Infof("revocationDB.InsertCaveat(%s,%v) failed with %v", cav.ThirdPartyDetails().ID(), revocation, err)
 		return empty, err
 	}
 	return cav, nil
@@ -87,7 +84,6 @@
 }
 
 func isRevoked(ctx *context.T, call security.Call, key []byte) error {
-	start := time.Now()
 	revocationLock.RLock()
 	if revocationDB == nil {
 		revocationLock.RUnlock()
@@ -96,11 +92,6 @@
 	revocationLock.RUnlock()
 	revoked, err := revocationDB.IsRevoked(key)
 	if err != nil {
-		// TODO(ashankar): Remove. Added for debugging.
-		ctx.Infof("IsRevoked(%v) returned %v (%v <-> %v)", key, err, call.RemoteEndpoint(), call.LocalEndpoint())
-	}
-	if d := time.Since(start); d > time.Second {
-		ctx.Infof("IsRevoked(%v) took %v (%v <-> %v)", key, d, call.RemoteEndpoint(), call.LocalEndpoint())
 	}
 	if revoked {
 		return fmt.Errorf("revoked")
diff --git a/services/identity/internal/revocation/sql_database.go b/services/identity/internal/revocation/sql_database.go
index 0e88eda..b50beb5 100644
--- a/services/identity/internal/revocation/sql_database.go
+++ b/services/identity/internal/revocation/sql_database.go
@@ -8,8 +8,6 @@
 	"database/sql"
 	"encoding/hex"
 	"fmt"
-	"os"
-	"runtime/pprof"
 	"time"
 )
 
@@ -29,53 +27,22 @@
 }
 
 func (s *sqlDatabase) InsertCaveat(thirdPartyCaveatID string, revocationCaveatID []byte) error {
-	return dumpStackTraceIfTakingTooLong(fmt.Sprintf("InsertCaveat(%v, %v)", thirdPartyCaveatID, revocationCaveatID), func() error {
-		_, err := s.insertCaveatStmt.Exec(thirdPartyCaveatID, hex.EncodeToString(revocationCaveatID))
-		return err
-	})
-}
-
-func dumpStackTraceIfTakingTooLong(tag string, f func() error) error {
-	ch := make(chan error)
-	go dumpStack(ch, tag, time.Now())
-	err := f()
-	ch <- err
-	close(ch)
+	_, err := s.insertCaveatStmt.Exec(thirdPartyCaveatID, hex.EncodeToString(revocationCaveatID))
 	return err
 }
 
-func dumpStack(ch chan error, tag string, start time.Time) {
-	timeout := 5 * time.Minute
-	select {
-	case <-time.After(timeout):
-		fmt.Fprintf(os.Stderr, "%v hasn't completed for %v\n", tag, time.Since(start))
-		fmt.Fprintf(os.Stderr, "Stack trace:\n")
-		pprof.Lookup("goroutine").WriteTo(os.Stderr, 2)
-		fmt.Fprintln(os.Stderr)
-		dumpStack(ch, tag, start)
-	case err := <-ch:
-		fmt.Fprintf(os.Stderr, "%v completed with %v\n", tag, err)
-	}
-}
-
 func (s *sqlDatabase) Revoke(thirdPartyCaveatID string) error {
 	_, err := s.revokeStmt.Exec(time.Now(), thirdPartyCaveatID)
 	return err
 }
 
 func (s *sqlDatabase) IsRevoked(revocationCaveatID []byte) (bool, error) {
-	var revoked bool
-	err := dumpStackTraceIfTakingTooLong(fmt.Sprintf("IsRevoked(%v)", revocationCaveatID), func() error {
-		rows, err := s.isRevokedStmt.Query(hex.EncodeToString(revocationCaveatID))
-		defer rows.Close()
-		if err != nil {
-			revoked = false
-			return err
-		}
-		revoked = rows.Next()
-		return nil
-	})
-	return revoked, err
+	rows, err := s.isRevokedStmt.Query(hex.EncodeToString(revocationCaveatID))
+	defer rows.Close()
+	if err != nil {
+		return false, err
+	}
+	return rows.Next(), err
 }
 
 func (s *sqlDatabase) RevocationTime(thirdPartyCaveatID string) (*time.Time, error) {
diff --git a/services/internal/servicetest/modules.go b/services/internal/servicetest/modules.go
index ee13fa5..56b4264 100644
--- a/services/internal/servicetest/modules.go
+++ b/services/internal/servicetest/modules.go
@@ -109,6 +109,35 @@
 	return sh, fn
 }
 
+// CreateShell builds a new modules shell.
+func CreateShell(t *testing.T, ctx *context.T, p security.Principal) (*modules.Shell, func()) {
+	sh, err := modules.NewShell(ctx, p, testing.Verbose(), t)
+	if err != nil {
+		t.Fatalf("unexpected error: %s", err)
+	}
+	opts := sh.DefaultStartOpts()
+	opts.ExpectTimeout = ExpectTimeout
+	sh.SetDefaultStartOpts(opts)
+	// The shell, will, by default share credentials with its children.
+	sh.ClearVar(ref.EnvCredentials)
+
+	fn := func() {
+		ctx.VI(1).Info("------------ CLEANUP ------------")
+		ctx.VI(1).Info("---------------------------------")
+		ctx.VI(1).Info("--(cleaning up shell)------------")
+		if err := sh.Cleanup(os.Stdout, os.Stderr); err != nil {
+			t.Errorf(testutil.FormatLogLine(2, "sh.Cleanup failed with %v", err))
+		}
+		ctx.VI(1).Info("--(done cleaning up shell)-------")
+	}
+	nsRoots := v23.GetNamespace(ctx).Roots()
+	if len(nsRoots) == 0 {
+		t.Fatalf("shell context has no namespace roots")
+	}
+	sh.SetVar(ref.EnvNamespacePrefix, nsRoots[0])
+	return sh, fn
+}
+
 // RunCommand runs a modules command.
 func RunCommand(t *testing.T, sh *modules.Shell, env []string, prog modules.Program, args ...string) modules.Handle {
 	h, err := sh.StartWithOpts(sh.DefaultStartOpts(), env, prog, args...)
diff --git a/test/modules/shell.go b/test/modules/shell.go
index cb0b564..0f4106a 100644
--- a/test/modules/shell.go
+++ b/test/modules/shell.go
@@ -323,11 +323,15 @@
 // Since the Shell type is intended for tests, it is not required to provide
 // caveats.  In production scenarios though, one must think long and hard
 // before blessing anothing principal without any caveats.
-func (sh *Shell) NewChildCredentials(extension string, caveats ...security.Caveat) (c *CustomCredentials, err error) {
+func (sh *Shell) NewChildCredentials(extension string, caveats ...security.Caveat) (*CustomCredentials, error) {
 	creds, err := sh.NewCustomCredentials()
 	if creds == nil {
 		return nil, err
 	}
+	return sh.AddToChildCredentials(creds, extension, caveats...)
+}
+
+func (sh *Shell) AddToChildCredentials(creds *CustomCredentials, extension string, caveats ...security.Caveat) (*CustomCredentials, error) {
 	parent := sh.principal
 	child := creds.p
 	if len(caveats) == 0 {
@@ -340,10 +344,16 @@
 	if err != nil {
 		return nil, err
 	}
-	if err := child.BlessingStore().SetDefault(blessings); err != nil {
+
+	union, err := security.UnionOfBlessings(child.BlessingStore().Default(), blessings)
+	if err != nil {
 		return nil, err
 	}
-	if _, err := child.BlessingStore().Set(blessings, security.AllPrincipals); err != nil {
+
+	if err := child.BlessingStore().SetDefault(union); err != nil {
+		return nil, err
+	}
+	if _, err := child.BlessingStore().Set(union, security.AllPrincipals); err != nil {
 		return nil, err
 	}
 	if err := child.AddToRoots(blessings); err != nil {
diff --git a/test/testutil/security.go b/test/testutil/security.go
index a59c5f8..2c673dd 100644
--- a/test/testutil/security.go
+++ b/test/testutil/security.go
@@ -78,6 +78,12 @@
 	return &IDProvider{p, b}
 }
 
+// IDProviderFromPrincipal creates and IDProvider for the given principal.  It
+// will bless other principals with extensions of its default blessing.
+func IDProviderFromPrincipal(p security.Principal) *IDProvider {
+	return &IDProvider{p, p.BlessingStore().Default()}
+}
+
 // Bless sets up the provided principal to use blessings from idp as its
 // default. It is shorthand for:
 //    b, _ := idp.NewBlessings(who, extension, caveats...)
@@ -89,16 +95,7 @@
 	if err != nil {
 		return err
 	}
-	if err := who.BlessingStore().SetDefault(b); err != nil {
-		return err
-	}
-	if _, err := who.BlessingStore().Set(b, security.AllPrincipals); err != nil {
-		return err
-	}
-	if err := who.AddToRoots(b); err != nil {
-		return err
-	}
-	return nil
+	return vsecurity.SetDefaultBlessings(who, b)
 }
 
 // NewBlessings returns Blessings that extend the identity provider's blessing