Merge "discovery: support Lost"
diff --git a/cmd/vdl/builtin_vdlroot.go b/cmd/vdl/builtin_vdlroot.go
new file mode 100644
index 0000000..584141e
--- /dev/null
+++ b/cmd/vdl/builtin_vdlroot.go
@@ -0,0 +1,78 @@
+// 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.
+
+// This file is automatically generated, your changes will be lost.
+// See make_builtin_vdlroot.go.
+
+package main
+
+//go:generate v23 go run make_builtin_vdlroot.go
+
+const (
+	// builtinVdlrootData contains a base64-encoded gzip'd tar file. This file
+	// contains all of the VDL files required to run the VDL tool.
+	builtinVdlrootData = `H4sIAAAJbogA/+xaa3PcNnd+v0q/AqN+iJ2udyXfWjsfOq7tuM44tidS0pl6PBosid2FRRIMQWq16e
+S/9znnAOBF8iWJk47ziuPLkgQOzuU5N4Derivddo1ZpF/z87z4x+e8DnHdv3uX/8c1/f/24b2j+Fue
+H925f+fuP9RnZeJ9V+db3WD5P0pnKtwXci0W6rGrd41db1p1+/DonjrZGPWTrnRuu1I96tqNa/xcPS
+oKxYO8aow3zbnJ5/uY/KM3yq1Uu7Feedc1mVGZy43C7dqdm6YyuVrulFb/efzklm93haFZhc1MhZnt
+Rrcq05VaGrVyXZUrW+GhUS+eP3768vipWtnCzPdpymudnem1UQmmKjcrWxmv2l1tiKuaGKtaW61BpT
+XNSoMZDZqlgRB5P9PP9+spNV7jeZqWG581dknUN8M1ISq4TeTn+7T4YJ5vmy5r1f/u773UpVG48AQc
+7e+9Plu/1u0m3T9xmRq+f1ouTe6VevOWfykFfl465WuTWV0o1+SGBs73975ncTxGyi8a+Ypei6qDtB
+XWn+//ynIJxQ/LZGhMbvJLwsnk3y2YcBA4/US1BhkCA2HuZQ7UpTX7J8+rR83a05M3b/FLsXXrrlW6
+WXclYOL39151bRiUxuDRZNDz6rhtjC6V+g8a0xPy8viGq1vrKl3cZIJhcBobCF4x+EQLf7S6rnaq11
+KLN0FvROZDSsNttS5MYjiojKZN9DVSVbw5ocE0wy3fGQzGivyIPXpI9df9/+9AdX39KVdrS7Ogfz57
+1u+vD+f/o6PD+/cm+f/o3p071/n/r7i+uPxPWE2pH7arct3kffbXFF89x8ald0XXSgnQmAJvzmW6J8
+6JJok6nRmjLdg2hdsiBtKYnztL6bV1qm7cuYWAWzwgGpkra0xd2sK2O8xpt8ZUKrerFRJy1dL4daPL
+ksoSU53bxlWcWeZKPTOVaXQLuqyylWuIYD91OJyitM10UewSB7prXYmlM0yvoGrP/COJOiR/EpaoTc
+Sb0SpIGmVdmEbZ3AYKnYdu+7KItMRa/2FYVeXg1bM6WYtkY83yw5wsRuvAIwQ7dmXKIdb7zviHROzo
+Jmvc6KbdfAUcYQ6NapwwR/Qq14LldVfohuGFa9O29cPFwlTzrT2ztcmtnrtmvaC7xQuj61NvoID8U4
+Y/dqijbEXMnv5YWVKaLk5PSNpPmM1lXqUlg58+gvZtlibfZuEaw4DpbRgR6lnxjFtSJSxp6Ln/JCkP
+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`
+)
diff --git a/cmd/vdl/builtin_vdlroot_support.go b/cmd/vdl/builtin_vdlroot_support.go
new file mode 100644
index 0000000..57e0cfa
--- /dev/null
+++ b/cmd/vdl/builtin_vdlroot_support.go
@@ -0,0 +1,100 @@
+// 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 main
+
+import (
+	"archive/tar"
+	"compress/gzip"
+	"encoding/base64"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strings"
+
+	"v.io/x/ref/lib/vdl/build"
+	"v.io/x/ref/lib/vdl/vdlutil"
+)
+
+func extractTarFile(basePath string, reader *tar.Reader) error {
+	for {
+		header, err := reader.Next()
+		if err == io.EOF {
+			return nil
+		} else if err != nil {
+			return err
+		}
+		// If it's a directory, ignore it. We use MkdirAll on all files anyway.
+		if header.FileInfo().IsDir() {
+			continue
+		}
+		fullPath := filepath.Join(basePath, header.Name)
+		if err := os.MkdirAll(filepath.Dir(fullPath), os.FileMode(0755)); err != nil {
+			return err
+		}
+		f, err := os.OpenFile(fullPath, os.O_CREATE|os.O_WRONLY, os.FileMode(header.Mode))
+		if err != nil {
+			return err
+		}
+		n, err := io.Copy(f, reader)
+		if err != nil {
+			return err
+		}
+		if n != header.Size {
+			return fmt.Errorf("while reading %s from archive, wrote %d bytes but was expecting to write %d", header.Name, header.Size, n)
+		}
+	}
+}
+
+func extractBuiltinVdlroot(destDir string) error {
+	decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(builtinVdlrootData))
+	gzipReader, err := gzip.NewReader(decoder)
+	if err != nil {
+		return err
+	}
+	tarReader := tar.NewReader(gzipReader)
+	return extractTarFile(destDir, tarReader)
+}
+
+// maybeExtractBuiltinVdlroot checks to see if VDLROOT is set or can be
+// determined from V23_ROOT. If not, the builtin root VDL definitions are
+// extracted to a new temporary directory and the VDLROOT environment variable
+// is set. cleanupFunc should be called unconditionally (even if there is
+// a non-empty set of errors returned).
+func maybeExtractBuiltinVdlroot() (func() error, *vdlutil.Errors) {
+	noopCleanup := func() error { return nil }
+	if !flagBuiltinVdlroot {
+		return noopCleanup, vdlutil.NewErrors(-1)
+	}
+	errs := vdlutil.NewErrors(-1)
+	build.VdlRootDir(errs)
+	if errs.IsEmpty() {
+		// No errors? We have a vdlroot already and we don't need to do
+		// anything.
+		return noopCleanup, errs
+	}
+	errs.Reset()
+	// Otherwise, we don't have a VDL rootdir.
+	dir, err := ioutil.TempDir("", "vdlroot-")
+	if err != nil {
+		errs.Errorf("TempDir failed, couldn't create temporary VDLROOT: %v", err)
+		return noopCleanup, errs
+	}
+	removeAllCleanup := func() error {
+		return os.RemoveAll(dir)
+	}
+	// Extract the files to the new VDLROOT
+	if err := extractBuiltinVdlroot(dir); err != nil {
+		errs.Errorf("Could not extract builtin VDL types: %v", err)
+		return removeAllCleanup, errs
+	}
+	if err := os.Setenv("VDLROOT", dir); err != nil {
+		errs.Errorf("Setenv(VDLROOT, %q) failed: %v", dir, err)
+		return removeAllCleanup, errs
+	}
+	vdlutil.Vlog.Printf("set VDLROOT to newly-extracted root at %s pid = %d", dir, os.Getpid())
+	return removeAllCleanup, errs
+}
diff --git a/cmd/vdl/doc.go b/cmd/vdl/doc.go
index 5cc386a..7daef03 100644
--- a/cmd/vdl/doc.go
+++ b/cmd/vdl/doc.go
@@ -26,6 +26,9 @@
    vdl.config  Description of vdl.config files
 
 The vdl flags are:
+ -builtin_vdlroot=false
+   If V23_ROOT and VDLROOT are not set, use built-in VDL definitions for core
+   types
  -exts=.vdl
    Comma-separated list of valid VDL file name extensions.
  -ignore_unknown=false
diff --git a/cmd/vdl/main.go b/cmd/vdl/main.go
index 2810b55..0749c07 100644
--- a/cmd/vdl/main.go
+++ b/cmd/vdl/main.go
@@ -16,6 +16,7 @@
 	"path/filepath"
 	"strings"
 
+	"v.io/jiri/collect"
 	"v.io/v23/vdlroot/vdltool"
 	"v.io/x/lib/cmdline"
 	"v.io/x/lib/textutil"
@@ -32,7 +33,26 @@
 }
 
 func main() {
-	cmdline.Main(cmdVDL)
+  env := cmdline.EnvFromOS()
+  err := runMain(env)
+  os.Exit(cmdline.ExitCode(err, env.Stderr))
+}
+
+func runMain(env *cmdline.Env) (e error) {
+	args := os.Args[1:]
+	runner, args, err := cmdline.Parse(cmdVDL, env, args)
+	if err != nil {
+		return err
+	}
+	cleanup, errs := maybeExtractBuiltinVdlroot()
+	defer collect.Error(cleanup, &err)
+	if err := checkErrors(errs); err != nil {
+		return err
+	}
+	if err := runner.Run(env, args); err != nil {
+		return err
+	}
+	return nil
 }
 
 func checkErrors(errs *vdlutil.Errors) error {
@@ -322,11 +342,12 @@
 
 var (
 	// Common flags for the tool itself, applicable to all commands.
-	flagVerbose       bool
-	flagMaxErrors     int
-	flagExts          string
-	flagVDLConfig     string
-	flagIgnoreUnknown bool
+	flagVerbose        bool
+	flagMaxErrors      int
+	flagExts           string
+	flagVDLConfig      string
+	flagIgnoreUnknown  bool
+	flagBuiltinVdlroot bool
 
 	// Options for each command.
 	optCompileStatus bool
@@ -375,6 +396,7 @@
 	cmdVDL.Flags.StringVar(&flagExts, "exts", ".vdl", "Comma-separated list of valid VDL file name extensions.")
 	cmdVDL.Flags.StringVar(&flagVDLConfig, "vdl.config", "vdl.config", "Basename of the optional per-package config file.")
 	cmdVDL.Flags.BoolVar(&flagIgnoreUnknown, "ignore_unknown", false, "Ignore unknown packages provided on the command line.")
+	cmdVDL.Flags.BoolVar(&flagBuiltinVdlroot, "builtin_vdlroot", false, "If V23_ROOT and VDLROOT are not set, use built-in VDL definitions for core types")
 
 	// Options for compile.
 	cmdCompile.Flags.BoolVar(&optCompileStatus, "status", true, "Show package names while we compile")
diff --git a/cmd/vdl/make_builtin_vdlroot.go b/cmd/vdl/make_builtin_vdlroot.go
new file mode 100644
index 0000000..1377fd2
--- /dev/null
+++ b/cmd/vdl/make_builtin_vdlroot.go
@@ -0,0 +1,171 @@
+// 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.
+
+// Command make_builtin_vdlroot runs at v23 go generate time. It emits a Go
+// source file called builtin_vdlroot.go containing a gzip'd version of all
+// the core VDL types required by the VDL tool. This allows the VDL tool to
+// run 'standalone' (i.e. without access to the Vanadium source code).
+//
+// +build ignored
+
+package main
+
+import (
+	"archive/tar"
+	"compress/gzip"
+	"encoding/base64"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"log"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+const (
+	outputPreamble = `// 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.
+
+// This file is automatically generated, your changes will be lost.
+// See make_builtin_vdlroot.go.
+
+package main
+
+//go:generate v23 go run make_builtin_vdlroot.go
+
+const (
+	// builtinVdlrootData contains a base64-encoded gzip'd tar file. This file
+	// contains all of the VDL files required to run the VDL tool.
+	builtinVdlrootData = ` + "`"
+	outputFooter = "`" + `
+)
+`
+)
+
+// writeBytes writes the given bytes to the given writer, returning an error if
+// the underlying Write fails or if the number of bytes written is not equal to
+// len(content).
+func writeBytes(out io.Writer, content []byte) error {
+	n, err := out.Write(content)
+	if err == nil && n != len(content) {
+		err = fmt.Errorf("wrote an unexpected number of bytes, wanted %d, wrote %d", len(content), n)
+	}
+	return err
+}
+
+// writeString writes the given string to the given writer, returning an error
+// if the underlying Write fails or if the number of bytes written is not equal
+// to len(content).
+func writeString(out io.Writer, content string) error {
+	return writeBytes(out, []byte(content))
+}
+
+type wrapWriter struct {
+	writer       io.Writer
+	totalWritten int
+}
+
+// Writes b to the underlying writer but inserts a \n every 78 characters. The
+// returned bytes-written count includes the \n characters.
+func (w *wrapWriter) Write(b []byte) (int, error) {
+  const max = 78
+  n := 0
+  for len(b) > 0 {
+    newline := true
+    chunk := max - w.totalWritten%max
+    if len(b) < chunk {
+      chunk = len(b)
+      newline = false
+    }
+    if err := writeBytes(w.writer, b[:chunk]); err != nil {
+      return n, err
+    }
+    n += chunk
+    w.totalWritten += chunk
+    b = b[chunk:]
+    if newline {
+      if err := writeString(w.writer, "\n"); err != nil {
+        return n, err
+      }
+      n++
+    }
+  }
+  return n, nil
+}
+
+// writeVdlrootData creates a gzip'd tar file containing all of the VDL files
+// in vdlroot. The data is encoded as base64. Does not close out.
+func writeVdlrootData(out io.Writer) error {
+	v23root := os.Getenv("V23_ROOT")
+	if v23root == "" {
+		return fmt.Errorf("V23_ROOT is not set")
+	}
+	vdlroot := filepath.Join(v23root, "release", "go", "src", "v.io", "v23", "vdlroot")
+	wrapWriter := &wrapWriter{
+		writer:       out,
+		totalWritten: 0,
+	}
+	base64writer := base64.NewEncoder(base64.StdEncoding, wrapWriter)
+	gzipWriter := gzip.NewWriter(base64writer)
+	tarWriter := tar.NewWriter(gzipWriter)
+	walkFn := func(path string, info os.FileInfo, err error) error {
+		if strings.HasSuffix(path, ".vdl") {
+			content, err := ioutil.ReadFile(path)
+			if err != nil {
+				return err
+			}
+			relPath, err := filepath.Rel(vdlroot, path)
+			if err != nil {
+				return err
+			}
+			header := tar.Header{
+				Mode:    int64(0644),
+				Name:    relPath,
+				Size:    int64(len(content)),
+			}
+			if err := tarWriter.WriteHeader(&header); err != nil {
+				return err
+			}
+			return writeBytes(tarWriter, content)
+		}
+		return nil
+	}
+	if err := filepath.Walk(vdlroot, walkFn); err != nil {
+		log.Printf("Walk() failed: %v", err)
+	}
+	if err := tarWriter.Close(); err != nil {
+		log.Printf("Close() of tar file failed: %v", err)
+		return err
+	}
+	if err := gzipWriter.Close(); err != nil {
+		log.Printf("Close() of gzip file failed: %v", err)
+		return err
+	}
+	if err := base64writer.Close(); err != nil {
+		log.Printf("Close() of base64 file failed: %v", err)
+		return err
+	}
+	return nil
+}
+
+func main() {
+	filename := "builtin_vdlroot.go"
+	f, err := os.Create(filename)
+	if err != nil {
+		log.Printf("Create(%q) failed: %v", filename, err)
+		os.Exit(1)
+	}
+	defer f.Close()
+	if err := writeString(f, outputPreamble); err != nil {
+		os.Exit(1)
+	}
+	if err := writeVdlrootData(f); err != nil {
+		os.Exit(1)
+	}
+	if err := writeString(f, outputFooter); err != nil {
+		os.Exit(1)
+	}
+}
diff --git a/cmd/vdl/vdl_test.go b/cmd/vdl/vdl_test.go
index 7a127cd..99f5c5a 100644
--- a/cmd/vdl/vdl_test.go
+++ b/cmd/vdl/vdl_test.go
@@ -8,10 +8,12 @@
 	"bytes"
 	"fmt"
 	"io/ioutil"
+	"os"
 	"path/filepath"
 	"strings"
 	"testing"
 
+	"v.io/x/lib/envvar"
 	"v.io/x/ref/test/v23tests"
 )
 
@@ -22,18 +24,7 @@
 
 //go:generate v23 test generate
 
-// Compares generated VDL files against the copy in the repo.
-func TestVDLGenerator(t *testing.T) {
-	testEnv := v23tests.New(t)
-	defer testEnv.Cleanup()
-	vdlBin := testEnv.BuildGoPkg("v.io/x/ref/cmd/vdl")
-
-	// Use vdl to generate Go code from input, into a temporary directory.
-	outDir := testEnv.NewTempDir("")
-	// TODO(toddw): test the generated java and javascript files too.
-	outOpt := fmt.Sprintf("--go-out-dir=%s", outDir)
-	vdlBin.Run("generate", "--lang=go", outOpt, testDir)
-	// Check that each *.vdl.go file in the testDir matches the generated output.
+func verifyOutput(t *testing.T, outDir string) {
 	entries, err := ioutil.ReadDir(testDir)
 	if err != nil {
 		t.Fatalf("ReadDir(%v) failed: %v", testDir, err)
@@ -62,3 +53,34 @@
 		t.Fatalf("testDir %s has no golden files *.vdl.go", testDir)
 	}
 }
+
+// Compares generated VDL files against the copy in the repo.
+func TestVDLGenerator(t *testing.T) {
+	testEnv := v23tests.New(t)
+	defer testEnv.Cleanup()
+	vdlBin := testEnv.BuildGoPkg("v.io/x/ref/cmd/vdl")
+
+	// Use vdl to generate Go code from input, into a temporary directory.
+	outDir := testEnv.NewTempDir("")
+	// TODO(toddw): test the generated java and javascript files too.
+	outOpt := fmt.Sprintf("--go-out-dir=%s", outDir)
+	vdlBin.Run("generate", "--lang=go", outOpt, testDir)
+	// Check that each *.vdl.go file in the testDir matches the generated output.
+	verifyOutput(t, outDir)
+}
+
+// Asserts that the VDL command can run to completion without VDLROOT or
+// V23_ROOT being set.
+func TestVDLGeneratorWithNoVDLRoot(t *testing.T) {
+	testEnv := v23tests.New(t)
+	defer testEnv.Cleanup()
+	vdlBin := testEnv.BuildGoPkg("v.io/x/ref/cmd/vdl")
+
+	outDir := testEnv.NewTempDir("")
+	outOpt := fmt.Sprintf("--go-out-dir=%s", outDir)
+	env := envvar.SliceToMap(os.Environ())
+	env["V23_ROOT"] = ""
+	env["VDLROOT"] = ""
+	vdlBin.WithEnv(envvar.MapToSlice(env)...).Run("-v", "--builtin_vdlroot", "generate", "--lang=go", outOpt, testDir)
+	verifyOutput(t, outDir)
+}
diff --git a/lib/vdl/build/build.go b/lib/vdl/build/build.go
index 8ce8ee7..0d6bea5 100644
--- a/lib/vdl/build/build.go
+++ b/lib/vdl/build/build.go
@@ -270,13 +270,20 @@
 // neither VDLROOT nor V23_ROOT is specified.
 func SrcDirs(errs *vdlutil.Errors) []string {
 	var srcDirs []string
-	if root := vdlRootDir(errs); root != "" {
+	if root := VdlRootDir(errs); root != "" {
 		srcDirs = append(srcDirs, root)
 	}
 	return append(srcDirs, vdlPathSrcDirs(errs)...)
 }
 
-func vdlRootDir(errs *vdlutil.Errors) string {
+// VdlRootDir returns the VDL root directory, based on the VDLPATH, VDLROOT and
+// V23_ROOT environment variables.
+//
+// VDLROOT is a single directory specifying the location of the standard vdl
+// packages.  It has the same requirements as VDLPATH components.  If VDLROOT is
+// empty, we use V23_ROOT to construct the VDLROOT.  An error is reported if
+// neither VDLROOT nor V23_ROOT is specified.
+func VdlRootDir(errs *vdlutil.Errors) string {
 	vdlroot := os.Getenv("VDLROOT")
 	if vdlroot == "" {
 		// Try to construct VDLROOT out of V23_ROOT.
diff --git a/runtime/internal/flow/conn/conn.go b/runtime/internal/flow/conn/conn.go
index a7991c7..ecbd8a9 100644
--- a/runtime/internal/flow/conn/conn.go
+++ b/runtime/internal/flow/conn/conn.go
@@ -346,3 +346,8 @@
 	c.lastUsedTime = time.Now()
 	c.mu.Unlock()
 }
+
+func (c *Conn) IsEncapsulated() bool {
+	_, ok := c.mp.rw.(*flw)
+	return ok
+}
diff --git a/runtime/internal/flow/errors.vdl b/runtime/internal/flow/errors.vdl
new file mode 100644
index 0000000..8019811
--- /dev/null
+++ b/runtime/internal/flow/errors.vdl
@@ -0,0 +1,15 @@
+// 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 flow
+
+// These messages are constructed so as to avoid embedding a component/method name
+// and are thus more suitable for inclusion in other verrors.
+// This practice of omitting {1}{2} should be used throughout the flow implementations
+// since all of their errors are intended to be used as arguments to higher level errors.
+// TODO(suharshs,toddw): Allow skipping of {1}{2} in vdl generated errors.
+error (
+  WrongObjectInContext(typ string) {"en":
+  "context passed to method of {typ} object, but that object is not attached to the context."}
+)
diff --git a/runtime/internal/flow/errors.vdl.go b/runtime/internal/flow/errors.vdl.go
new file mode 100644
index 0000000..3f4d3b2
--- /dev/null
+++ b/runtime/internal/flow/errors.vdl.go
@@ -0,0 +1,28 @@
+// 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.
+
+// This file was auto-generated by the vanadium vdl tool.
+// Source: errors.vdl
+
+package flow
+
+import (
+	// VDL system imports
+	"v.io/v23/context"
+	"v.io/v23/i18n"
+	"v.io/v23/verror"
+)
+
+var (
+	ErrWrongObjectInContext = verror.Register("v.io/x/ref/runtime/internal/flow.WrongObjectInContext", verror.NoRetry, "{1:}{2:} context passed to method of {3} object, but that object is not attached to the context.")
+)
+
+func init() {
+	i18n.Cat().SetWithBase(i18n.LangID("en"), i18n.MsgID(ErrWrongObjectInContext.ID), "{1:}{2:} context passed to method of {3} object, but that object is not attached to the context.")
+}
+
+// NewErrWrongObjectInContext returns an error with the ErrWrongObjectInContext ID.
+func NewErrWrongObjectInContext(ctx *context.T, typ string) error {
+	return verror.New(ErrWrongObjectInContext, ctx, typ)
+}
diff --git a/runtime/internal/flow/manager/conncache.go b/runtime/internal/flow/manager/conncache.go
index 92f8348..b3975e2 100644
--- a/runtime/internal/flow/manager/conncache.go
+++ b/runtime/internal/flow/manager/conncache.go
@@ -151,6 +151,10 @@
 			delete(c.ridCache, e.rid)
 			continue
 		}
+		if e.conn.IsEncapsulated() {
+			// Killing a proxied connection doesn't save us any FD resources, just memory.
+			continue
+		}
 		pq = append(pq, e)
 	}
 	sort.Sort(pq)
diff --git a/runtime/internal/flow/manager/manager.go b/runtime/internal/flow/manager/manager.go
index 2490352..60fa43e 100644
--- a/runtime/internal/flow/manager/manager.go
+++ b/runtime/internal/flow/manager/manager.go
@@ -20,13 +20,17 @@
 	"v.io/v23/security"
 	"v.io/v23/verror"
 
+	iflow "v.io/x/ref/runtime/internal/flow"
 	"v.io/x/ref/runtime/internal/flow/conn"
 	"v.io/x/ref/runtime/internal/lib/upcqueue"
 	inaming "v.io/x/ref/runtime/internal/naming"
 	"v.io/x/ref/runtime/internal/rpc/version"
 )
 
-const reconnectDelay = 50 * time.Millisecond
+const (
+	reconnectDelay    = 50 * time.Millisecond
+	reapCacheInterval = 5 * time.Minute
+)
 
 type manager struct {
 	rid    naming.RoutingID
@@ -52,19 +56,27 @@
 		listeners:      []flow.Listener{},
 	}
 	go func() {
-		select {
-		case <-ctx.Done():
-			m.mu.Lock()
-			listeners := m.listeners
-			m.listeners = nil
-			m.mu.Unlock()
-			for _, ln := range listeners {
-				ln.Close()
+		ticker := time.NewTicker(reapCacheInterval)
+		for {
+			select {
+			case <-ctx.Done():
+				m.mu.Lock()
+				listeners := m.listeners
+				m.listeners = nil
+				m.mu.Unlock()
+				for _, ln := range listeners {
+					ln.Close()
+				}
+				m.cache.Close(ctx)
+				m.q.Close()
+				m.wg.Wait()
+				ticker.Stop()
+				close(m.closed)
+				return
+			case <-ticker.C:
+				// Periodically kill closed connections.
+				m.cache.KillConnections(ctx, 0)
 			}
-			m.cache.Close(ctx)
-			m.q.Close()
-			m.wg.Wait()
-			close(m.closed)
 		}
 	}()
 	return m
@@ -76,6 +88,9 @@
 // The flow.Manager associated with ctx must be the receiver of the method,
 // otherwise an error is returned.
 func (m *manager) Listen(ctx *context.T, protocol, address string) error {
+	if err := m.validateContext(ctx); err != nil {
+		return err
+	}
 	if protocol == inaming.Network {
 		return m.proxyListen(ctx, address)
 	}
@@ -298,7 +313,9 @@
 // The flow.Manager associated with ctx must be the receiver of the method,
 // otherwise an error is returned.
 func (m *manager) Accept(ctx *context.T) (flow.Flow, error) {
-	// TODO(suharshs): Ensure that m is attached to ctx.
+	if err := m.validateContext(ctx); err != nil {
+		return nil, err
+	}
 	item, err := m.q.Get(ctx.Done())
 	switch {
 	case err == upcqueue.ErrQueueIsClosed:
@@ -319,6 +336,9 @@
 // The flow.Manager associated with ctx must be the receiver of the method,
 // otherwise an error is returned.
 func (m *manager) Dial(ctx *context.T, remote naming.Endpoint, fn flow.BlessingsForPeer) (flow.Flow, error) {
+	if err := m.validateContext(ctx); err != nil {
+		return nil, err
+	}
 	var fh conn.FlowHandler
 	if m.rid != naming.NullRoutingID {
 		fh = &flowHandler{q: m.q}
@@ -436,6 +456,13 @@
 	return m.closed
 }
 
+func (m *manager) validateContext(ctx *context.T) error {
+	if v23.ExperimentalGetFlowManager(ctx) != m {
+		return flow.NewErrBadArg(ctx, iflow.NewErrWrongObjectInContext(ctx, "manager"))
+	}
+	return nil
+}
+
 func dial(ctx *context.T, p flow.Protocol, protocol, address string) (flow.Conn, error) {
 	if p != nil {
 		var timeout time.Duration
diff --git a/runtime/internal/flow/manager/manager_test.go b/runtime/internal/flow/manager/manager_test.go
index cce80f9..02a96d3 100644
--- a/runtime/internal/flow/manager/manager_test.go
+++ b/runtime/internal/flow/manager/manager_test.go
@@ -15,7 +15,7 @@
 	"v.io/v23/flow"
 	"v.io/v23/naming"
 
-	_ "v.io/x/ref/runtime/factories/fake"
+	"v.io/x/ref/runtime/factories/fake"
 	"v.io/x/ref/runtime/internal/flow/conn"
 	"v.io/x/ref/runtime/internal/flow/flowtest"
 	"v.io/x/ref/test"
@@ -33,12 +33,14 @@
 	ctx, shutdown := v23.Init()
 
 	am := New(ctx, naming.FixedRoutingID(0x5555))
-	if err := am.Listen(ctx, "tcp", "127.0.0.1:0"); err != nil {
+	actx := fake.SetFlowManager(ctx, am)
+	if err := am.Listen(actx, "tcp", "127.0.0.1:0"); err != nil {
 		t.Fatal(err)
 	}
 	dm := New(ctx, naming.FixedRoutingID(0x1111))
+	dctx := fake.SetFlowManager(ctx, dm)
 
-	testFlows(t, ctx, dm, am, flowtest.BlessingsForPeer)
+	testFlows(t, dctx, actx, flowtest.BlessingsForPeer)
 
 	shutdown()
 	<-am.Closed()
@@ -50,24 +52,26 @@
 	ctx, shutdown := v23.Init()
 
 	am := New(ctx, naming.FixedRoutingID(0x5555))
-	if err := am.Listen(ctx, "tcp", "127.0.0.1:0"); err != nil {
+	actx := fake.SetFlowManager(ctx, am)
+	if err := am.Listen(actx, "tcp", "127.0.0.1:0"); err != nil {
 		t.Fatal(err)
 	}
 
 	dm := New(ctx, naming.FixedRoutingID(0x1111))
+	dctx := fake.SetFlowManager(ctx, dm)
 	// At first the cache should be empty.
 	if got, want := len(dm.(*manager).cache.addrCache), 0; got != want {
 		t.Fatalf("got cache size %v, want %v", got, want)
 	}
 	// After dialing a connection the cache should hold one connection.
-	testFlows(t, ctx, dm, am, flowtest.BlessingsForPeer)
+	testFlows(t, dctx, actx, flowtest.BlessingsForPeer)
 	if got, want := len(dm.(*manager).cache.addrCache), 1; got != want {
 		t.Fatalf("got cache size %v, want %v", got, want)
 	}
 	old := dm.(*manager).cache.ridCache[am.RoutingID()]
 	// After dialing another connection the cache should still hold one connection
 	// because the connections should be reused.
-	testFlows(t, ctx, dm, am, flowtest.BlessingsForPeer)
+	testFlows(t, dctx, actx, flowtest.BlessingsForPeer)
 	if got, want := len(dm.(*manager).cache.addrCache), 1; got != want {
 		t.Errorf("got cache size %v, want %v", got, want)
 	}
@@ -85,14 +89,16 @@
 	ctx, shutdown := v23.Init()
 
 	am := New(ctx, naming.FixedRoutingID(0x5555))
-	if err := am.Listen(ctx, "tcp", "127.0.0.1:0"); err != nil {
+	actx := fake.SetFlowManager(ctx, am)
+	if err := am.Listen(actx, "tcp", "127.0.0.1:0"); err != nil {
 		t.Fatal(err)
 	}
 
 	dm := New(ctx, naming.FixedRoutingID(0x1111))
-	testFlows(t, ctx, dm, am, flowtest.BlessingsForPeer)
+	dctx := fake.SetFlowManager(ctx, dm)
+	testFlows(t, dctx, actx, flowtest.BlessingsForPeer)
 	// Now am should be able to make a flow to dm even though dm is not listening.
-	testFlows(t, ctx, am, dm, flowtest.BlessingsForPeer)
+	testFlows(t, actx, dctx, flowtest.BlessingsForPeer)
 
 	shutdown()
 	<-am.Closed()
@@ -104,17 +110,20 @@
 	ctx, shutdown := v23.Init()
 
 	am := New(ctx, naming.FixedRoutingID(0x5555))
-	if err := am.Listen(ctx, "tcp", "127.0.0.1:0"); err != nil {
+	actx := fake.SetFlowManager(ctx, am)
+	if err := am.Listen(actx, "tcp", "127.0.0.1:0"); err != nil {
 		t.Fatal(err)
 	}
 	nulldm := New(ctx, naming.NullRoutingID)
-	_, af := testFlows(t, ctx, nulldm, am, flowtest.BlessingsForPeer)
+	nctx := fake.SetFlowManager(ctx, nulldm)
+	_, af := testFlows(t, nctx, actx, flowtest.BlessingsForPeer)
 	// Ensure that the remote blessings of the underlying conn of the accepted flow are zero.
 	if rBlessings := af.Conn().(*conn.Conn).RemoteBlessings(); !rBlessings.IsZero() {
 		t.Errorf("got %v, want zero-value blessings", rBlessings)
 	}
 	dm := New(ctx, naming.FixedRoutingID(0x1111))
-	_, af = testFlows(t, ctx, dm, am, flowtest.BlessingsForPeer)
+	dctx := fake.SetFlowManager(ctx, dm)
+	_, af = testFlows(t, dctx, actx, flowtest.BlessingsForPeer)
 	// Ensure that the remote blessings of the underlying conn of the accepted flow are
 	// non-zero if we did specify a RoutingID.
 	if rBlessings := af.Conn().(*conn.Conn).RemoteBlessings(); rBlessings.IsZero() {
@@ -127,20 +136,17 @@
 	<-nulldm.Closed()
 }
 
-func testFlows(t *testing.T, ctx *context.T, dm, am flow.Manager, bFn flow.BlessingsForPeer) (df, af flow.Flow) {
-	eps := am.ListeningEndpoints()
-	if len(eps) == 0 {
-		t.Fatalf("no endpoints listened on")
-	}
-	ep := eps[0]
+func testFlows(t *testing.T, dctx, actx *context.T, bFn flow.BlessingsForPeer) (df, af flow.Flow) {
+	am := v23.ExperimentalGetFlowManager(actx)
+	ep := am.ListeningEndpoints()[0]
 	var err error
-	df, err = dm.Dial(ctx, ep, bFn)
+	df, err = v23.ExperimentalGetFlowManager(dctx).Dial(dctx, ep, bFn)
 	if err != nil {
 		t.Fatal(err)
 	}
 	want := "do you read me?"
 	writeLine(df, want)
-	af, err = am.Accept(ctx)
+	af, err = am.Accept(actx)
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/services/agent/vbecome/vbecome.go b/services/agent/vbecome/vbecome.go
index 15abcc0..1b91eb2 100644
--- a/services/agent/vbecome/vbecome.go
+++ b/services/agent/vbecome/vbecome.go
@@ -109,6 +109,11 @@
 		}
 	}
 
+	// Clear out the environment variable before starting the child.
+	if err = ref.EnvClearCredentials(); err != nil {
+		return err
+	}
+
 	// Start an agent server.
 	i := ipc.NewIPC()
 	if err := server.ServeAgent(i, principal); err != nil {