veyron/lib/modules: Use the agent for managing credentials in modules.Shell.

Change-Id: I17737af8d4fe2281d2f0e5a267cbff268758f788
diff --git a/lib/filelocker/locker_test.go b/lib/filelocker/locker_test.go
index 4e96ef9..b58bfe2 100644
--- a/lib/filelocker/locker_test.go
+++ b/lib/filelocker/locker_test.go
@@ -63,7 +63,7 @@
 	filepath := newFile()
 	defer os.Remove(filepath)
 
-	sh, err := modules.NewShell(nil)
+	sh, err := modules.NewShell(nil, nil)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -136,7 +136,7 @@
 	filepath := newFile()
 	defer os.Remove(filepath)
 
-	sh, err := modules.NewShell(nil)
+	sh, err := modules.NewShell(nil, nil)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
diff --git a/lib/modules/core/core_test.go b/lib/modules/core/core_test.go
index b314ad3..64eeabb 100644
--- a/lib/modules/core/core_test.go
+++ b/lib/modules/core/core_test.go
@@ -18,6 +18,7 @@
 	"v.io/core/veyron/lib/modules/core"
 	"v.io/core/veyron/lib/testutil"
 	_ "v.io/core/veyron/profiles"
+	"v.io/core/veyron2/rt"
 )
 
 func TestCommands(t *testing.T) {
@@ -37,7 +38,11 @@
 // TODO(cnicolaou): add test for proxyd
 
 func newShell(t *testing.T) (*modules.Shell, func()) {
-	sh, err := modules.NewShell(nil)
+	runtime, err := rt.New()
+	if err != nil {
+		panic(err)
+	}
+	sh, err := modules.NewShell(runtime.NewContext(), nil)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -48,6 +53,7 @@
 		} else {
 			sh.Cleanup(nil, nil)
 		}
+		runtime.Cleanup()
 	}
 }
 
@@ -193,7 +199,6 @@
 	}
 	cltSession := expect.NewSession(t, clt.Stdout(), time.Minute)
 	cltSession.Expect("test: a message")
-	srv.Shutdown(nil, nil)
 }
 
 func TestExec(t *testing.T) {
diff --git a/lib/modules/examples_test.go b/lib/modules/examples_test.go
index 2154214..ee78224 100644
--- a/lib/modules/examples_test.go
+++ b/lib/modules/examples_test.go
@@ -28,7 +28,7 @@
 		return
 	}
 	// Parent process.
-	sh, _ := modules.NewShell(nil)
+	sh, _ := modules.NewShell(runtime.NewContext(), nil)
 	defer sh.Cleanup(nil, nil)
 	h, _ := sh.Start("echo", nil, "a", "b")
 	h.Shutdown(os.Stdout, os.Stderr)
@@ -41,7 +41,7 @@
 func ExampleDispatchAndExit() {
 	// DispatchAndExit will call os.Exit(0) when executed within the child.
 	modules.DispatchAndExit()
-	sh, _ := modules.NewShell(nil)
+	sh, _ := modules.NewShell(runtime.NewContext(), nil)
 	defer sh.Cleanup(nil, nil)
 	h, _ := sh.Start("echo", nil, "c", "d")
 	h.Shutdown(os.Stdout, os.Stderr)
diff --git a/lib/modules/exec.go b/lib/modules/exec.go
index 351af5f..b7e77c0 100644
--- a/lib/modules/exec.go
+++ b/lib/modules/exec.go
@@ -5,11 +5,13 @@
 	"io"
 	"os"
 	"os/exec"
+	"strconv"
 	"strings"
 	"sync"
 	"time"
 
 	vexec "v.io/core/veyron/lib/exec"
+	"v.io/core/veyron2/mgmt"
 	"v.io/core/veyron2/vlog"
 )
 
@@ -108,7 +110,7 @@
 	return newargs, append(cleaned, eh.entryPoint)
 }
 
-func (eh *execHandle) start(sh *Shell, env []string, args ...string) (Handle, error) {
+func (eh *execHandle) start(sh *Shell, agentfd *os.File, env []string, args ...string) (Handle, error) {
 	eh.mu.Lock()
 	defer eh.mu.Unlock()
 	eh.sh = sh
@@ -132,8 +134,20 @@
 	if err != nil {
 		return nil, err
 	}
+	config := vexec.NewConfig()
+	serialized, err := sh.config.Serialize()
+	if err != nil {
+		return nil, err
+	}
+	config.MergeFrom(serialized)
+	if agentfd != nil {
+		childfd := len(cmd.ExtraFiles) + vexec.FileOffset
+		config.Set(mgmt.SecurityAgentFDConfigKey, strconv.Itoa(childfd))
+		cmd.ExtraFiles = append(cmd.ExtraFiles, agentfd)
+		defer agentfd.Close()
+	}
 
-	handle := vexec.NewParentHandle(cmd, vexec.ConfigOpt{Config: sh.config})
+	handle := vexec.NewParentHandle(cmd, vexec.ConfigOpt{Config: config})
 	eh.stdout = stdout
 	eh.stderr = stderr
 	eh.stdin = stdin
diff --git a/lib/modules/func.go b/lib/modules/func.go
index 4103fae..e7372e6 100644
--- a/lib/modules/func.go
+++ b/lib/modules/func.go
@@ -55,9 +55,13 @@
 	return args, env
 }
 
-func (fh *functionHandle) start(sh *Shell, env []string, args ...string) (Handle, error) {
+func (fh *functionHandle) start(sh *Shell, agent *os.File, env []string, args ...string) (Handle, error) {
 	fh.mu.Lock()
 	defer fh.mu.Unlock()
+	// In process commands need their own reference to a principal.
+	if agent != nil {
+		agent.Close()
+	}
 	fh.sh = sh
 	for _, p := range []*pipe{&fh.stdin, &fh.stdout} {
 		var err error
diff --git a/lib/modules/modules_internal_test.go b/lib/modules/modules_internal_test.go
index b8229e3..3c26937 100644
--- a/lib/modules/modules_internal_test.go
+++ b/lib/modules/modules_internal_test.go
@@ -6,6 +6,10 @@
 	"path/filepath"
 	"runtime"
 	"testing"
+
+	_ "v.io/core/veyron/profiles"
+
+	"v.io/core/veyron2/rt"
 )
 
 func Echo(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
@@ -26,7 +30,12 @@
 }
 
 func TestState(t *testing.T) {
-	sh, err := NewShell(nil)
+	r, err := rt.New()
+	if err != nil {
+		panic(err)
+	}
+	defer r.Cleanup()
+	sh, err := NewShell(r.NewContext(), nil)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
diff --git a/lib/modules/modules_test.go b/lib/modules/modules_test.go
index 6915d68..1f4e311 100644
--- a/lib/modules/modules_test.go
+++ b/lib/modules/modules_test.go
@@ -3,7 +3,6 @@
 import (
 	"bufio"
 	"bytes"
-	"errors"
 	"fmt"
 	"io"
 	"os"
@@ -18,19 +17,22 @@
 	"v.io/core/veyron/lib/flags/consts"
 	"v.io/core/veyron/lib/modules"
 	"v.io/core/veyron/lib/testutil"
-	tsecurity "v.io/core/veyron/lib/testutil/security"
+	"v.io/core/veyron/lib/testutil/security"
 	_ "v.io/core/veyron/profiles"
-	vsecurity "v.io/core/veyron/security"
 
-	"v.io/core/veyron2/security"
+	"v.io/core/veyron2"
+	"v.io/core/veyron2/rt"
 )
 
 const credentialsEnvPrefix = "\"" + consts.VeyronCredentials + "="
 
+var runtime veyron2.Runtime
+
 func init() {
 	testutil.Init()
 	modules.RegisterChild("envtest", "envtest: <variables to print>...", PrintFromEnv)
 	modules.RegisterChild("printenv", "printenv", PrintEnv)
+	modules.RegisterChild("printblessing", "printblessing", PrintBlessing)
 	modules.RegisterChild("echos", "[args]*", Echo)
 	modules.RegisterChild("errortestChild", "", ErrorMain)
 	modules.RegisterChild("ignores_stdin", "", ignoresStdin)
@@ -38,6 +40,11 @@
 	modules.RegisterFunction("envtestf", "envtest: <variables to print>...", PrintFromEnv)
 	modules.RegisterFunction("echof", "[args]*", Echo)
 	modules.RegisterFunction("errortestFunc", "", ErrorMain)
+	var err error
+	runtime, err = rt.New()
+	if err != nil {
+		panic(err)
+	}
 }
 
 func ignoresStdin(io.Reader, io.Writer, io.Writer, map[string]string, ...string) error {
@@ -53,6 +60,12 @@
 	return nil
 }
 
+func PrintBlessing(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+	blessing := veyron2.GetPrincipal(runtime.NewContext()).BlessingStore().Default()
+	fmt.Fprintf(stdout, "%s", blessing)
+	return nil
+}
+
 func PrintFromEnv(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
 	for _, a := range args[1:] {
 		if v := env[a]; len(v) > 0 {
@@ -132,8 +145,21 @@
 	}
 }
 
+func getBlessing(t *testing.T, sh *modules.Shell, env ...string) string {
+	h, err := sh.Start("printblessing", env)
+	if err != nil {
+		t.Fatalf("unexpected error: %s", err)
+	}
+	scanner := bufio.NewScanner(h.Stdout())
+	if !waitForInput(scanner) {
+		t.Errorf("timeout")
+		return ""
+	}
+	return scanner.Text()
+}
+
 func TestChild(t *testing.T) {
-	sh, err := modules.NewShell(nil)
+	sh, err := modules.NewShell(runtime.NewContext(), nil)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -143,8 +169,62 @@
 	testCommand(t, sh, "envtest", key, val)
 }
 
+func TestAgent(t *testing.T) {
+	sh, err := modules.NewShell(runtime.NewContext(), nil)
+	if err != nil {
+		t.Fatalf("unexpected error: %s", err)
+	}
+	defer sh.Cleanup(os.Stdout, os.Stderr)
+	a := getBlessing(t, sh)
+	b := getBlessing(t, sh)
+	if a != b {
+		t.Errorf("Expected same blessing for children, got %s and %s", a, b)
+	}
+	sh2, err := modules.NewShell(runtime.NewContext(), nil)
+	if err != nil {
+		t.Fatalf("unexpected error: %s", err)
+	}
+	defer sh2.Cleanup(os.Stdout, os.Stderr)
+	c := getBlessing(t, sh2)
+	if a == c {
+		t.Errorf("Expected different blessing for each shell, got %s and %s", a, c)
+	}
+}
+
+func TestCustomPrincipal(t *testing.T) {
+	p := security.NewPrincipal("myshell")
+	cleanDebug := p.BlessingStore().DebugString()
+	sh, err := modules.NewShell(runtime.NewContext(), p)
+	if err != nil {
+		t.Fatalf("unexpected error: %s", err)
+	}
+	defer sh.Cleanup(os.Stdout, os.Stderr)
+	blessing := getBlessing(t, sh)
+	if blessing != "myshell/child" {
+		t.Errorf("Bad blessing. Expected myshell/child, go %q", blessing)
+	}
+	newDebug := p.BlessingStore().DebugString()
+	if cleanDebug != newDebug {
+		t.Errorf("Shell modified custom principal. Was:\n%q\nNow:\n%q", cleanDebug, newDebug)
+	}
+}
+
+func TestNoAgent(t *testing.T) {
+	creds, _ := security.NewCredentials("noagent")
+	defer os.RemoveAll(creds)
+	sh, err := modules.NewShell(nil, nil)
+	if err != nil {
+		t.Fatalf("unexpected error: %s", err)
+	}
+	defer sh.Cleanup(os.Stdout, os.Stderr)
+	blessing := getBlessing(t, sh, fmt.Sprintf("VEYRON_CREDENTIALS=%s", creds))
+	if blessing != "noagent" {
+		t.Errorf("Bad blessing. Expected noagent, go %q", blessing)
+	}
+}
+
 func TestChildNoRegistration(t *testing.T) {
-	sh, err := modules.NewShell(nil)
+	sh, err := modules.NewShell(runtime.NewContext(), nil)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -159,7 +239,7 @@
 }
 
 func TestFunction(t *testing.T) {
-	sh, err := modules.NewShell(nil)
+	sh, err := modules.NewShell(runtime.NewContext(), nil)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -170,7 +250,7 @@
 }
 
 func TestErrorChild(t *testing.T) {
-	sh, err := modules.NewShell(nil)
+	sh, err := modules.NewShell(runtime.NewContext(), nil)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -210,7 +290,7 @@
 }
 
 func TestShutdownSubprocess(t *testing.T) {
-	sh, err := modules.NewShell(nil)
+	sh, err := modules.NewShell(runtime.NewContext(), nil)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -222,7 +302,7 @@
 // forever if a child does not die upon closing stdin; but instead times out and
 // returns an appropriate error.
 func TestShutdownSubprocessIgnoresStdin(t *testing.T) {
-	sh, err := modules.NewShell(nil)
+	sh, err := modules.NewShell(runtime.NewContext(), nil)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -247,7 +327,7 @@
 // implementation inappropriately sets stdout to the file that is to be closed
 // in Wait.
 func TestStdoutRace(t *testing.T) {
-	sh, err := modules.NewShell(nil)
+	sh, err := modules.NewShell(runtime.NewContext(), nil)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -279,7 +359,7 @@
 }
 
 func TestShutdownFunction(t *testing.T) {
-	sh, err := modules.NewShell(nil)
+	sh, err := modules.NewShell(runtime.NewContext(), nil)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -288,7 +368,7 @@
 }
 
 func TestErrorFunc(t *testing.T) {
-	sh, err := modules.NewShell(nil)
+	sh, err := modules.NewShell(runtime.NewContext(), nil)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -312,7 +392,7 @@
 }
 
 func TestEnvelope(t *testing.T) {
-	sh, err := modules.NewShell(nil)
+	sh, err := modules.NewShell(runtime.NewContext(), nil)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -347,146 +427,24 @@
 		}
 	}
 
-	foundVeyronCredentials := false
 	for _, want := range shEnv {
-		if strings.HasPrefix(want, credentialsEnvPrefix) {
-			foundVeyronCredentials = true
-			continue
-		}
 		if !find(want, childEnv) {
 			t.Errorf("failed to find %s in %#v", want, childEnv)
 		}
 	}
-	if !foundVeyronCredentials {
-		t.Errorf("%v environment variable not set in command envelope", consts.VeyronCredentials)
-	}
 
-	foundVeyronCredentials = false
 	for _, want := range childEnv {
 		if want == "\""+exec.VersionVariable+"=\"" {
 			continue
 		}
-		if strings.HasPrefix(want, credentialsEnvPrefix) {
-			foundVeyronCredentials = true
-			continue
-		}
 		if !find(want, shEnv) {
 			t.Errorf("failed to find %s in %#v", want, shEnv)
 		}
 	}
-	if !foundVeyronCredentials {
-		t.Errorf("%v environment variable not set for command", consts.VeyronCredentials)
-	}
-}
-
-func TestEnvCredentials(t *testing.T) {
-	startChildAndGetCredentials := func(sh *modules.Shell, env []string) string {
-		h, err := sh.Start("printenv", env)
-		if err != nil {
-			t.Fatalf("unexpected error: %s", err)
-		}
-		defer h.Shutdown(os.Stderr, os.Stderr)
-		scanner := bufio.NewScanner(h.Stdout())
-		for scanner.Scan() {
-			o := scanner.Text()
-			if strings.HasPrefix(o, credentialsEnvPrefix) {
-				return strings.TrimSuffix(strings.TrimPrefix(o, credentialsEnvPrefix), "\"")
-			}
-		}
-		return ""
-	}
-	validateCredentials := func(dir string, wantBlessing string) error {
-		if len(dir) == 0 {
-			return errors.New("credentials directory not found")
-		}
-		p, err := vsecurity.LoadPersistentPrincipal(dir, nil)
-		if err != nil {
-			return err
-		}
-		def := p.BlessingStore().Default()
-		if blessingsSharedWithAll := p.BlessingStore().ForPeer("random_peer"); !reflect.DeepEqual(blessingsSharedWithAll, def) {
-			return fmt.Errorf("Blessing shared with all peers: %v is different from default Blessings: %v", blessingsSharedWithAll, def)
-		}
-		if got := def.ForContext(security.NewContext(&security.ContextParams{LocalPrincipal: p})); len(got) != 1 || got[0] != wantBlessing {
-			return fmt.Errorf("got blessings: %v, want exactly one blessing: %v", got, wantBlessing)
-		}
-		return nil
-	}
-
-	// Test child credentials when runtime is not initialized and VeyronCredentials
-	// is not set.
-	sh, err := modules.NewShell(nil)
-	if err != nil {
-		t.Fatalf("unexpected error: %s", err)
-	}
-	if err := validateCredentials(startChildAndGetCredentials(sh, nil), "test-shell/child"); err != nil {
-		t.Fatal(err)
-	}
-
-	// Test that no two children have the same credentials directory.
-	if cred1, cred2 := startChildAndGetCredentials(sh, nil), startChildAndGetCredentials(sh, nil); cred1 == cred2 {
-		t.Fatalf("The same credentials directory %v was set for two children", cred1)
-	}
-	sh.Cleanup(nil, nil)
-
-	// Test child credentials when VeyronCredentials are set.
-	old := os.Getenv(consts.VeyronCredentials)
-	defer os.Setenv(consts.VeyronCredentials, old)
-	dir, _ := tsecurity.NewCredentials("os")
-	defer os.RemoveAll(dir)
-	if err := os.Setenv(consts.VeyronCredentials, dir); err != nil {
-		t.Fatal(err)
-	}
-	sh, err = modules.NewShell(nil)
-	if err != nil {
-		t.Fatalf("unexpected error: %s", err)
-	}
-	if err := validateCredentials(startChildAndGetCredentials(sh, nil), "os/child"); err != nil {
-		t.Fatal(err)
-	}
-	sh.Cleanup(nil, nil)
-
-	// Test that os VeyronCredentials are not deleted by the Cleanup.
-	if finfo, err := os.Stat(dir); err != nil || !finfo.IsDir() {
-		t.Fatalf("%q is not a directory", dir)
-	}
-
-	// Test that VeyronCredentials specified on the shell override the OS ones.
-	dir, _ = tsecurity.NewCredentials("shell")
-	defer os.RemoveAll(dir)
-	sh, err = modules.NewShell(nil)
-	if err != nil {
-		t.Fatalf("unexpected error: %s", err)
-	}
-	sh.SetVar(consts.VeyronCredentials, dir)
-	if err := validateCredentials(startChildAndGetCredentials(sh, nil), "shell/child"); err != nil {
-		t.Fatal(err)
-	}
-	sh.ClearVar(consts.VeyronCredentials)
-	if credentials := startChildAndGetCredentials(sh, nil); len(credentials) != 0 {
-		t.Fatalf("found credentials %v set for child even when shell credentials were cleared", credentials)
-	}
-	anotherDir, _ := tsecurity.NewCredentials("anotherShell")
-	defer os.RemoveAll(anotherDir)
-	sh.SetVar(consts.VeyronCredentials, anotherDir)
-	if err := validateCredentials(startChildAndGetCredentials(sh, nil), "anotherShell/child"); err != nil {
-		t.Fatal(err)
-	}
-	sh.Cleanup(nil, nil)
-
-	// Test that VeyronCredentials specified as a parameter overrides the OS and
-	// shell ones.
-	dir, _ = tsecurity.NewCredentials("param")
-	defer os.RemoveAll(dir)
-	env := []string{consts.VeyronCredentials + "=" + dir}
-	if err := validateCredentials(startChildAndGetCredentials(sh, env), "param"); err != nil {
-		t.Fatal(err)
-	}
-
 }
 
 func TestEnvMerge(t *testing.T) {
-	sh, err := modules.NewShell(nil)
+	sh, err := modules.NewShell(runtime.NewContext(), nil)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
diff --git a/lib/modules/shell.go b/lib/modules/shell.go
index 0c6a5eb..ecaefde 100644
--- a/lib/modules/shell.go
+++ b/lib/modules/shell.go
@@ -29,20 +29,10 @@
 // In particular stdin, stdout and stderr are provided as parameters, as is
 // a map representation of the shell's environment.
 //
-// Every Shell created by NewShell is initialized with its VeyronCredentials
-// environment variable set. The variable is set to the os's VeyronCredentials
-// if that is set, otherwise to a freshly created credentials directory. The
-// shell's VeyronCredentials can be set and cleared using the SetVar and
-// ClearVar methods respectively.
-//
-// By default, the VeyronCredentials for each command Start-ed by the shell are
-// set to a freshly created credentials directory that is blessed by the shell's
-// credentials (i.e., if shell's credentials have not been cleared). Thus, each
-// child of the shell gets its own credentials directory with a blessing from the
-// shell of the form
-//   <shell's default blessing>/child
-// These default credentials provided by the shell to each command can be
-// overridden by specifying VeyronCredentials in the environment provided as a
+// By default, every Shell created by NewShell starts a security agent
+// to manage principals for child processes.  These default
+// credentials can be overridden by passing a nil context to NewShell
+// then specifying VeyronCredentials in the environment provided as a
 // parameter to the Start method.
 package modules
 
@@ -53,13 +43,17 @@
 	"math/rand"
 	"os"
 	"sync"
+	"syscall"
 	"time"
 
 	"v.io/core/veyron2/security"
 
 	"v.io/core/veyron/lib/exec"
 	"v.io/core/veyron/lib/flags/consts"
-	vsecurity "v.io/core/veyron/security"
+	"v.io/core/veyron/security/agent"
+	"v.io/core/veyron/security/agent/keymgr"
+	"v.io/core/veyron2"
+	"v.io/core/veyron2/context"
 )
 
 const (
@@ -72,38 +66,55 @@
 	mu      sync.Mutex
 	env     map[string]string
 	handles map[Handle]struct{}
-	// tmpCredDirs are the temporary directories created by this
-	// shell. These must be removed when the shell is cleaned up.
-	tempCredDirs              []string
+	// tmpCredDir is the temporary directory created by this
+	// shell. This must be removed when the shell is cleaned up.
+	tempCredDir               string
 	startTimeout, waitTimeout time.Duration
 	config                    exec.Config
 	principal                 security.Principal
 	blessing                  security.Blessings
+	agent                     *keymgr.Agent
+	ctx                       *context.T
+	cancelCtx                 func()
 }
 
-// NewShell creates a new instance of Shell. It will also add a blessing
-// to the supplied principal and ensure that any child processes are
-// created with principals that are in turn blessed with it. A typical
-// use case is to pass in the runtime's principal and hence allow the
-// process hosting the shell to interact with its children and vice
-// versa. If a nil principal is passed in then NewShell will create a new
-// principal that shares a blessing with all of its children, in this mode,
-// any child processes can interact with each other but not with the parent.
-func NewShell(p security.Principal) (*Shell, error) {
+// NewShell creates a new instance of Shell.
+// If ctx is non-nil, the shell will manage Principals for child processes.
+// By default it adds a blessing to ctx's principal to ensure isolation. Any child processes
+// are created with principals derived from this new blessing.
+// However, if p is non-nil the shell will skip the new blessing and child processes
+// will have their principals derived from p's default blessing(s).
+func NewShell(ctx *context.T, p security.Principal) (*Shell, error) {
 	sh := &Shell{
 		env:          make(map[string]string),
 		handles:      make(map[Handle]struct{}),
 		startTimeout: time.Minute,
 		waitTimeout:  10 * time.Second,
 		config:       exec.NewConfig(),
-		principal:    p,
 	}
-	if p == nil {
-		if err := sh.initShellCredentials(); err != nil {
-			return nil, err
-		}
+	if ctx == nil {
 		return sh, nil
 	}
+	var err error
+	ctx, sh.cancelCtx = context.WithCancel(ctx)
+	if ctx, _, err = veyron2.SetNewStreamManager(ctx); err != nil {
+		return nil, err
+	}
+	sh.ctx = ctx
+
+	if sh.tempCredDir, err = ioutil.TempDir("", "shell_credentials"); err != nil {
+		return nil, err
+	}
+	if sh.agent, err = keymgr.NewLocalAgent(ctx, sh.tempCredDir, nil); err != nil {
+		return nil, err
+	}
+	if p != nil {
+		sh.principal = p
+		sh.blessing = p.BlessingStore().Default()
+		return sh, nil
+	}
+	p = veyron2.GetPrincipal(ctx)
+	sh.principal = p
 	gen := rand.New(rand.NewSource(time.Now().UnixNano()))
 	// Use a unique blessing tree per shell.
 	blessingName := fmt.Sprintf("%s-%d", shellBlessingExtension, gen.Int63())
@@ -124,60 +135,42 @@
 	return sh, nil
 }
 
-func (sh *Shell) initShellCredentials() error {
-	sh.mu.Lock()
-	defer sh.mu.Unlock()
-	if dir := os.Getenv(consts.VeyronCredentials); len(dir) != 0 {
-		sh.env[consts.VeyronCredentials] = dir
-		return nil
+func (sh *Shell) getChildCredentials() (*os.File, error) {
+	if sh.ctx == nil {
+		return nil, nil
 	}
-
-	dir, err := ioutil.TempDir("", "shell_credentials")
-	if err != nil {
-		return err
-	}
-	sh.env[consts.VeyronCredentials] = dir
-	sh.tempCredDirs = append(sh.tempCredDirs, dir)
-	return nil
-}
-
-func (sh *Shell) getChildCredentials(shellCredDir string) (string, error) {
 	root := sh.principal
 	rootBlessing := sh.blessing
-	if root == nil {
-		r, err := principalFromDir(shellCredDir)
-		if err != nil {
-			return "", err
-		}
-		root = r
-		rootBlessing = root.BlessingStore().Default()
-	}
-
-	dir, err := ioutil.TempDir("", "shell_child_credentials")
+	_, conn, err := sh.agent.NewPrincipal(sh.ctx, true)
 	if err != nil {
-		return "", err
+		return nil, err
 	}
-	sh.tempCredDirs = append(sh.tempCredDirs, dir)
-	// Create a principal and default blessing for the child that is
-	// derived from the blessing created for this shell. This can
-	// be used by the parent to invoke RPCs on any children and for the
-	// children to invoke RPCs on each other.
-	p, err := vsecurity.CreatePersistentPrincipal(dir, nil)
+	ctx, cancel := context.WithCancel(sh.ctx)
+	if ctx, _, err = veyron2.SetNewStreamManager(ctx); err != nil {
+		return nil, err
+	}
+	defer cancel()
+	fd, err := syscall.Dup(int(conn.Fd()))
 	if err != nil {
-		return "", err
+		return nil, err
+	}
+	syscall.CloseOnExec(fd)
+	p, err := agent.NewAgentPrincipal(ctx, fd)
+	if err != nil {
+		return nil, err
 	}
 	blessingForChild, err := root.Bless(p.PublicKey(), rootBlessing, childBlessingExtension, security.UnconstrainedUse())
 	if err != nil {
-		return "", err
+		return nil, err
 	}
 	if err := p.BlessingStore().SetDefault(blessingForChild); err != nil {
-		return "", err
+		return nil, err
 	}
 	if _, err := p.BlessingStore().Set(blessingForChild, security.AllPrincipals); err != nil {
-		return "", err
+		return nil, err
 	}
 	if err := p.AddToRoots(blessingForChild); err != nil {
-		return "", err
+		return nil, err
 	}
 
 	if sh.blessing != nil {
@@ -185,7 +178,7 @@
 		// by the parent, should the child choose to invoke RPCs on the parent.
 		blessingFromChild, err := root.Bless(p.PublicKey(), root.BlessingStore().Default(), childBlessingExtension, security.UnconstrainedUse())
 		if err != nil {
-			return "", err
+			return nil, err
 		}
 		// We store this blessing as the one to use with a pattern that matches
 		// the root's name.
@@ -194,14 +187,14 @@
 		ctx := security.NewContext(&security.ContextParams{LocalPrincipal: root})
 		rootName := root.BlessingStore().Default().ForContext(ctx)[0]
 		if _, err := p.BlessingStore().Set(blessingFromChild, security.BlessingPattern(rootName)); err != nil {
-			return "", err
+			return nil, err
 		}
 
 		if err := p.AddToRoots(blessingFromChild); err != nil {
-			return "", err
+			return nil, err
 		}
 	}
-	return dir, nil
+	return conn, nil
 }
 
 type Main func(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error
@@ -244,12 +237,16 @@
 	if err != nil {
 		return nil, err
 	}
+	p, err := sh.getChildCredentials()
+	if err != nil {
+		return nil, err
+	}
 	cmd := registry.getCommand(name)
 	if cmd == nil {
 		return nil, fmt.Errorf("%s: not registered", name)
 	}
 	expanded := append([]string{name}, sh.expand(args...)...)
-	h, err := cmd.factory().start(sh, cenv, expanded...)
+	h, err := cmd.factory().start(sh, p, cenv, expanded...)
 	if err != nil {
 		// If the error is a timeout, then h can be used to recover
 		// any output from the process.
@@ -399,18 +396,14 @@
 		}
 	}
 
-	// TODO(ataly, ashankar, caprita): The following code may lead to
-	// removing the credential directories for child processes that are
-	// still alive (this can happen e.g. if the Wait on the child timed
-	// out). While we can hope that some error will reach the caller of
-	// Cleanup (because we harvest the error codes from Shutdown), it may
-	// lead to failures that are harder to debug because the original issue
-	// will get masked by the credentials going away. One possibility is
-	// to only remove the credentials dir when Shutdown returns no timeout
-	// error.
-	for _, dir := range sh.tempCredDirs {
-		os.RemoveAll(dir)
+	if sh.cancelCtx != nil {
+		// Note(ribrdb, caprita): This will shutdown the agents.  If there
+		// were errors shutting down it is possible there could be child
+		// processes still running, and stopping the agent may cause
+		// additional failures.
+		sh.cancelCtx()
 	}
+	os.RemoveAll(sh.tempCredDir)
 	return err
 }
 
@@ -425,15 +418,7 @@
 	// want the child to directly use the directory specified
 	// by the shell's VeyronCredentials.
 	delete(m1, consts.VeyronCredentials)
-
-	// Set the VeyronCredentials environment variable for the child
-	// if it is not already set and the shell's VeyronCredentials are set.
-	if len(evmap[consts.VeyronCredentials]) == 0 && (len(sh.env[consts.VeyronCredentials]) != 0 || sh.principal != nil) {
-		var err error
-		if evmap[consts.VeyronCredentials], err = sh.getChildCredentials(sh.env[consts.VeyronCredentials]); err != nil {
-			return nil, err
-		}
-	}
+	delete(m1, agent.FdVarName)
 
 	m2 := mergeMaps(m1, evmap)
 	r := []string{}
@@ -477,5 +462,5 @@
 // commands.
 type command interface {
 	envelope(sh *Shell, env []string, args ...string) ([]string, []string)
-	start(sh *Shell, env []string, args ...string) (Handle, error)
+	start(sh *Shell, agent *os.File, env []string, args ...string) (Handle, error)
 }
diff --git a/lib/signals/signals_test.go b/lib/signals/signals_test.go
index b5ea0bc..21d3430 100644
--- a/lib/signals/signals_test.go
+++ b/lib/signals/signals_test.go
@@ -20,10 +20,8 @@
 	"v.io/core/veyron2/services/mgmt/appcycle"
 
 	"v.io/core/veyron/lib/expect"
-	"v.io/core/veyron/lib/flags/consts"
 	"v.io/core/veyron/lib/modules"
 	"v.io/core/veyron/lib/testutil"
-	"v.io/core/veyron/lib/testutil/security"
 	"v.io/core/veyron/profiles"
 	vflag "v.io/core/veyron/security/flag"
 	"v.io/core/veyron/services/mgmt/device"
@@ -125,8 +123,8 @@
 	}
 }
 
-func newShell(t *testing.T, command string) (*modules.Shell, modules.Handle, *expect.Session) {
-	sh, err := modules.NewShell(nil)
+func newShell(t *testing.T, r veyron2.Runtime, command string) (*modules.Shell, modules.Handle, *expect.Session) {
+	sh, err := modules.NewShell(r.NewContext(), nil)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
@@ -143,7 +141,12 @@
 // TestCleanShutdownSignal verifies that sending a signal to a child that
 // handles it by default causes the child to shut down cleanly.
 func TestCleanShutdownSignal(t *testing.T) {
-	sh, h, s := newShell(t, "handleDefaults")
+	runtime, err := rt.New()
+	if err != nil {
+		panic(err)
+	}
+	defer runtime.Cleanup()
+	sh, h, s := newShell(t, runtime, "handleDefaults")
 	defer sh.Cleanup(os.Stderr, os.Stderr)
 	s.Expect("ready")
 	checkSignalIsDefault(t, syscall.SIGINT)
@@ -156,7 +159,12 @@
 // TestCleanShutdownStop verifies that sending a stop comamnd to a child that
 // handles stop commands by default causes the child to shut down cleanly.
 func TestCleanShutdownStop(t *testing.T) {
-	sh, h, s := newShell(t, "handleDefaults")
+	runtime, err := rt.New()
+	if err != nil {
+		panic(err)
+	}
+	defer runtime.Cleanup()
+	sh, h, s := newShell(t, runtime, "handleDefaults")
 	defer sh.Cleanup(os.Stderr, os.Stderr)
 	s.Expect("ready")
 	fmt.Fprintf(h.Stdin(), "stop\n")
@@ -170,7 +178,12 @@
 // that handles stop command as part of a custom set of signals handled, causes
 // the child to shut down cleanly.
 func TestCleanShutdownStopCustom(t *testing.T) {
-	sh, h, s := newShell(t, "handleCustomWithStop")
+	runtime, err := rt.New()
+	if err != nil {
+		panic(err)
+	}
+	defer runtime.Cleanup()
+	sh, h, s := newShell(t, runtime, "handleCustomWithStop")
 	defer sh.Cleanup(os.Stderr, os.Stderr)
 	s.Expect("ready")
 	fmt.Fprintf(h.Stdin(), "stop\n")
@@ -191,7 +204,12 @@
 // TestStopNoHandler verifies that sending a stop command to a child that does
 // not handle stop commands causes the child to exit immediately.
 func TestStopNoHandler(t *testing.T) {
-	sh, h, s := newShell(t, "handleCustom")
+	runtime, err := rt.New()
+	if err != nil {
+		panic(err)
+	}
+	defer runtime.Cleanup()
+	sh, h, s := newShell(t, runtime, "handleCustom")
 	defer sh.Cleanup(os.Stderr, os.Stderr)
 	s.Expect("ready")
 	fmt.Fprintf(h.Stdin(), "stop\n")
@@ -202,7 +220,12 @@
 // that handles these signals by default causes the child to exit immediately
 // upon receiving the second signal.
 func TestDoubleSignal(t *testing.T) {
-	sh, h, s := newShell(t, "handleDefaults")
+	runtime, err := rt.New()
+	if err != nil {
+		panic(err)
+	}
+	defer runtime.Cleanup()
+	sh, h, s := newShell(t, runtime, "handleDefaults")
 	defer sh.Cleanup(os.Stderr, os.Stderr)
 	s.Expect("ready")
 	checkSignalIsDefault(t, syscall.SIGTERM)
@@ -217,7 +240,12 @@
 // to a child that handles these by default causes the child to exit immediately
 // upon receiving the stop command.
 func TestSignalAndStop(t *testing.T) {
-	sh, h, s := newShell(t, "handleDefaults")
+	runtime, err := rt.New()
+	if err != nil {
+		panic(err)
+	}
+	defer runtime.Cleanup()
+	sh, h, s := newShell(t, runtime, "handleDefaults")
 	defer sh.Cleanup(os.Stderr, os.Stderr)
 	s.Expect("ready")
 	checkSignalIsDefault(t, syscall.SIGTERM)
@@ -231,7 +259,12 @@
 // that handles stop commands by default causes the child to exit immediately
 // upon receiving the second stop command.
 func TestDoubleStop(t *testing.T) {
-	sh, h, s := newShell(t, "handleDefaults")
+	runtime, err := rt.New()
+	if err != nil {
+		panic(err)
+	}
+	defer runtime.Cleanup()
+	sh, h, s := newShell(t, runtime, "handleDefaults")
 	defer sh.Cleanup(os.Stderr, os.Stderr)
 	s.Expect("ready")
 	fmt.Fprintf(h.Stdin(), "stop\n")
@@ -243,7 +276,12 @@
 // TestSendUnhandledSignal verifies that sending a signal that the child does
 // not handle causes the child to exit as per the signal being sent.
 func TestSendUnhandledSignal(t *testing.T) {
-	sh, h, s := newShell(t, "handleDefaults")
+	runtime, err := rt.New()
+	if err != nil {
+		panic(err)
+	}
+	defer runtime.Cleanup()
+	sh, h, s := newShell(t, runtime, "handleDefaults")
 	defer sh.Cleanup(os.Stderr, os.Stderr)
 	s.Expect("ready")
 	checkSignalIsNotDefault(t, syscall.SIGABRT)
@@ -256,7 +294,12 @@
 // process to exit (ensures that there is no dependency in ShutdownOnSignals
 // on having a goroutine read from the returned channel).
 func TestDoubleSignalIgnoreChan(t *testing.T) {
-	sh, h, s := newShell(t, "handleDefaultsIgnoreChan")
+	runtime, err := rt.New()
+	if err != nil {
+		panic(err)
+	}
+	defer runtime.Cleanup()
+	sh, h, s := newShell(t, runtime, "handleDefaultsIgnoreChan")
 	defer sh.Cleanup(os.Stderr, os.Stderr)
 	s.Expect("ready")
 	// Even if we ignore the channel that ShutdownOnSignals returns,
@@ -271,7 +314,12 @@
 // TestHandlerCustomSignal verifies that sending a non-default signal to a
 // server that listens for that signal causes the server to shut down cleanly.
 func TestHandlerCustomSignal(t *testing.T) {
-	sh, h, s := newShell(t, "handleCustom")
+	runtime, err := rt.New()
+	if err != nil {
+		panic(err)
+	}
+	defer runtime.Cleanup()
+	sh, h, s := newShell(t, runtime, "handleCustom")
 	defer sh.Cleanup(os.Stderr, os.Stderr)
 	s.Expect("ready")
 	checkSignalIsNotDefault(t, syscall.SIGABRT)
@@ -285,8 +333,13 @@
 // to a server that listens for that signal causes the server to shut down
 // cleanly, even when a STOP signal is also among the handled signals.
 func TestHandlerCustomSignalWithStop(t *testing.T) {
+	runtime, err := rt.New()
+	if err != nil {
+		panic(err)
+	}
+	defer runtime.Cleanup()
 	for _, signal := range []syscall.Signal{syscall.SIGABRT, syscall.SIGHUP} {
-		sh, h, s := newShell(t, "handleCustomWithStop")
+		sh, h, s := newShell(t, runtime, "handleCustomWithStop")
 		s.Expect("ready")
 		checkSignalIsNotDefault(t, signal)
 		syscall.Kill(h.Pid(), signal)
@@ -349,20 +402,14 @@
 	defer runtime.Cleanup()
 	ctx := runtime.NewContext()
 
-	sh, err := modules.NewShell(nil)
+	sh, err := modules.NewShell(runtime.NewContext(), nil)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
 	defer sh.Cleanup(os.Stderr, os.Stderr)
 
-	// Set the child process up with a blessing from the parent so that
-	// the default authorization works for RPCs between the two.
-	principal := veyron2.GetPrincipal(ctx)
-	childcreds, _ := security.ForkCredentials(principal, "child")
-	defer os.RemoveAll(childcreds)
 	configServer, configServiceName, ch := createConfigServer(t, ctx)
 	defer configServer.Stop()
-	sh.SetVar(consts.VeyronCredentials, childcreds)
 	sh.SetConfigKey(mgmt.ParentNameConfigKey, configServiceName)
 	sh.SetConfigKey(mgmt.ProtocolConfigKey, "tcp")
 	sh.SetConfigKey(mgmt.AddressConfigKey, "127.0.0.1:0")
diff --git a/lib/testutil/integration/util.go b/lib/testutil/integration/util.go
index 0255ab3..531cec3 100644
--- a/lib/testutil/integration/util.go
+++ b/lib/testutil/integration/util.go
@@ -20,6 +20,8 @@
 	"v.io/core/veyron/lib/modules"
 	"v.io/core/veyron/lib/modules/core"
 	tsecurity "v.io/core/veyron/lib/testutil/security"
+	"v.io/core/veyron2/options"
+	"v.io/core/veyron2/rt"
 	"v.io/core/veyron2/security"
 )
 
@@ -364,7 +366,11 @@
 func NewTestEnvironment(t *testing.T) TestEnvironment {
 	t.Log("creating root principal")
 	principal := tsecurity.NewPrincipal("root")
-	shell, err := modules.NewShell(principal)
+	runtime, err := rt.New(options.RuntimePrincipal{principal})
+	if err != nil {
+		t.Fatalf("rt.New() failed: %v", err)
+	}
+	shell, err := modules.NewShell(runtime.NewContext(), principal)
 	if err != nil {
 		t.Fatalf("NewShell() failed: %v", err)
 	}