Import changes for goma server

  - 021a72b6e00502aaef798ca56a55f8f199d11078 Relax max heap size and file size in nsjail config.
  - fd0ba83ac13ad61f85af192824e0c7caec944a3a auth: resp 504 when auth check failed in retry
  - ab2b317600157529c400db81d685dc1f8a86a381 roll github.com/bazelbuild/remote-apis to latest (a5c5773)
  - 084c463010afd6a4dbd77849204c7a20549f1bc3 remoteexec_proxy: show config on status page
  - d2833f4028bdca68e94ee8b2a8d3d6231b85a73c Add dummy PATH environment.
  - 6cbc346f29dae25e1717203cd2288cebada2b21f gcs: set ChunkSize
  - 0b6b8ad4eb1438cda8983e3f25ae8d39a77148e0 remoteexec: set max call send msg size
  - 255c697d449c4f6b94573adbd10da61ec18d453c roll contrib.go.opencensus.io/exporter/stackdriver from 0...
  - c6b79ee7793551b32c47c418abfb20aabdc797ce Use nsjail config instead of command line arguments.
  - 2a04c3f08e67303c70806b3ad37c0c1f9cd4d62d roll cloud.google.com/go from 0.39.0 to 0.40.0
  - 05f8c5088c311592178db87c40cdd84a8d441dc7 roll google.golang.org/api from 0.5.0 to 0.6.0
  - 26a1729a9a623f70273526efd46851ba0497cad4 remoteexec_proxy: add -exec-config-file
  - ab0ba1d55f0efa0f048d757d62e0503fa9b55bf4 file: report rpc error if resource exhausted
  - 4ef9ec9d0a999a5460d8b904b94f2b41e63b470a remoteexec: don't set credential on insecure connection
  - 383545afc875e3be09be9ed5d46c25b8d8d2fe04 file_server: protect from OOM
  - 85f94766beac9d2d90c46695809ef29fd5ba6334 Allow to have different infra/configs between internal an...
  - 25a2d424cb62237bdcdf65424692493b81c789a2 rpc: need register tag key for log
  - a75bbf4b361177cdc1222927ec2026e79e9f62dd roll google.golang.org/grpc from v1.21.0 to v1.21.1
  - 1b694d68aeaf6f840d61e38d80ae23fb36c34a80 remoteexec: remove too chatty debug log
  - 1c53131edac839119f1ef86d0ed5e19cc8a9dea7 Update nsjail from nsjail-2.4 to nsjail-2.8.
  - 8d692f639aaed837ac64f922f2213fbf840cc9f1 go mod tidy
  - c420ad5b135719736d813fd88de38939efc26774 roll contrib.go.opencensus.io/exporter/stackdriver from v...
  - 3289808e5fe3780d5e195048909491a8200a9135 roll go.opencensus.io from v0.21.0 to v0.22.0
  - 24d9545cee0f3293d2acd9d8c744a21a8e2b5660 exec: set error message when commandspec normalizer fails
  - 5fd89a7f99b51ae4d9a79eaf6700d9d54cf7f09b Allow RuntimeConfig to say having nsjail or not.
  - 09fc66a7f8999c7b6884e83a94dffdc8ef2bd224 Use CIPD to install go and protoc.
  - 260757e7472cffc4c53993a8787ec0a5c16f2605 Recover nsjail docker build.
  - 0e9a5a2a0313e39185327df894c32194f1a7b7c2 exec_server: allow no prebuilt if ATS enabled.
  - abe4ea6697ec0e9eee4d587c180ebee737d47f5d remoteexec: Update logging for upload blobs failure
  - 5124c730ffd6ae455749983455cb6b71c7026a2c frontend: reply 503, then 429 when memory exceeded
  - 74ccce2bad3d60a3538549161bcdcf1d35f7a196 remoteexec: Truncate logged time durations to milliseconds
  - 559d53e907d552314e7bc105856461c4e871909b remoteexec: Log more parts of time taken in exec-server
  - 276dcb48ebecd3e0f253cf6b422ef01e4c6e928c GC: don't run GC concurrently
  - 3ba574567f4cc52f8ca62093c5b3fa2c56b99b0a Allow to chroot or not will be decided by a platform conf...
  - 21d7a02f0a8273d503d8b12e3a3ecc25e09d697c update google.golang.org/grpc

GitOrigin-RevId: 2dd14a0db8aa454342c13eee46ff2971ff20c4f4
Change-Id: If2fc31cfef58ec0eb1909f88b3e609db9fa262df
diff --git a/auth/token.go b/auth/token.go
index 0e49c6a..ff30263 100644
--- a/auth/token.go
+++ b/auth/token.go
@@ -96,11 +96,11 @@
 		defer resp.Body.Close()
 		data, err := ioutil.ReadAll(resp.Body)
 		if err != nil {
-			return status.Errorf(codes.Internal, "read tokeninfo response: %v", err)
+			return status.Errorf(codes.Internal, "read tokeninfo response: code=%d %v", resp.StatusCode, err)
 		}
 		tokenInfo, err = parseResp(data)
 		if err != nil {
-			return status.Errorf(codes.Internal, "parse tokeninfo response: %v", err)
+			return status.Errorf(codes.Internal, "parse tokeninfo response: code=%d %v", resp.StatusCode, err)
 		}
 		return nil
 	})
@@ -112,6 +112,7 @@
 		Email            string `json:"email"`
 		Audience         string `json:"aud"`
 		ExpiresAt        string `json:"exp"`
+		Error            string `json:"error"`
 		ErrorDescription string `json:"error_description"`
 	}
 	d := json.NewDecoder(bytes.NewReader(data))
@@ -119,17 +120,18 @@
 	if err != nil {
 		return nil, err
 	}
-	if js.Email == "" && js.ExpiresAt == "" && js.ErrorDescription == "" {
+	if js.Email == "" && js.ExpiresAt == "" && js.Error == "" && js.ErrorDescription == "" {
 		return nil, status.Errorf(codes.Internal, "unexpected token info: %v", js)
 	}
 	ti := &TokenInfo{
 		Email:    js.Email,
 		Audience: js.Audience,
 	}
-	if js.ErrorDescription != "" {
-		ti.Err = status.Error(codes.PermissionDenied, js.ErrorDescription)
+	if js.Error != "" || js.ErrorDescription != "" {
+		ti.Err = status.Errorf(codes.PermissionDenied, "%s: %q", js.Error, js.ErrorDescription)
 	} else {
-		// when error_description exists, maybe no exp.
+		// parse exp iff no error is set.
+		// when error exists, maybe no exp.
 		ts, err := strconv.ParseInt(js.ExpiresAt, 10, 64)
 		if err != nil {
 			ti.Err = status.Errorf(codes.Internal, "parse exp: %v", err)
diff --git a/buildsetup.sh b/buildsetup.sh
index 1babaa8..8962574 100755
--- a/buildsetup.sh
+++ b/buildsetup.sh
@@ -3,14 +3,14 @@
 
 set -e
 
-sdkdir="$1"
-if [ ! -d "${sdkdir}" ]; then
-  echo "usage: $0 sdkdir" >&2
+script_dir=$(cd $(dirname $0); pwd)
+sdk_dir="$1"
+if [ ! -d "${sdk_dir}" ]; then
+  echo "usage: $0 sdk_dir" >&2
   exit 1
 fi
-mkdir -p "${sdkdir}/go/bin"
 
-# assume host has ca-certificates, cur, git and gcc.
+# assume host has ca-certificates, curl, git and gcc.
 pkg_missing=false
 for pkg in ca-certificates curl git gcc; do
   if ! dpkg-query -s "$pkg" >/dev/null; then
@@ -23,29 +23,15 @@
   exit 1
 fi
 
-# https://repo1.maven.org/maven2/com/google/protobuf/protoc/${protoc}/protoc-${protoc}-linux-x86_64.exe.sha1
-protoc=3.7.0
-protocsha1=b8f4dea2467de954ac0aa399f2d60ea36c73a5ae
+cipd_dir="${sdk_dir}/.cipd_bin"
+cipd_manifest="$script_dir/cipd_manifest.txt"
+mkdir -p "$cipd_dir"
+cipd ensure -log-level warning \
+  -ensure-file "$cipd_manifest"\
+  -root "$cipd_dir"
 
-echo "${protocsha1} ${sdkdir}/go/bin/protoc" > /tmp/protoc.sha1
-if ! sha1sum --check /tmp/protoc.sha1; then
-  curl -o "${sdkdir}/go/bin/protoc" https://repo1.maven.org/maven2/com/google/protobuf/protoc/${protoc}/protoc-${protoc}-linux-x86_64.exe
-  sha1sum --check /tmp/protoc.sha1
-fi
-chmod a+x "${sdkdir}/go/bin/protoc"
-rm -f /tmp/protoc.sha1
+(cd "$sdk_dir"; rm -f go; ln -s .cipd_bin/bin/go)
+(cd "$sdk_dir"; rm -f protoc; ln -s .cipd_bin/protoc)
 
-# TODO: use 'go get golang.org/dl/${go}' ?
-go=go1.12.5
-gosha256=aea86e3c73495f205929cfebba0d63f1382c8ac59be081b6351681415f4063cf
-goarchive="${sdkdir}/${go}.linux-amd64.tar.gz"
-echo "${gosha256} ${goarchive}" > "/tmp/${go}.linux-amd64.tar.gz.sha256"
-if ! sha256sum --check "/tmp/${go}.linux-amd64.tar.gz.sha256"; then
-  curl -o "${goarchive}" "https://storage.googleapis.com/golang/${go}.linux-amd64.tar.gz"
-  sha256sum --check "/tmp/${go}.linux-amd64.tar.gz.sha256"
-  tar -C "${sdkdir}" -xzf "${goarchive}"
-fi
-rm -f "/tmp/${go}.linux-amd64.tar.gz.sha256"
-
-echo "I: protoc-${protoc} and ${go} are installed in ${sdkdir}"
-echo "I: set ${sdkdir}/go/bin in \$PATH"
+echo "I: protoc and go are installed in ${sdk_dir}"
+echo "I: set ${sdk_dir} in \$PATH"
diff --git a/cache/gcs/cache.go b/cache/gcs/cache.go
index 9576bbb..13c6845 100644
--- a/cache/gcs/cache.go
+++ b/cache/gcs/cache.go
@@ -17,15 +17,25 @@
 	"time"
 
 	"cloud.google.com/go/storage"
+	"google.golang.org/api/googleapi"
 
 	"go.chromium.org/goma/server/log"
 	pb "go.chromium.org/goma/server/proto/cache"
 )
 
+// AdmissionController checks incoming request.
+type AdmissionController interface {
+	AdmitPut(*pb.PutReq) error
+}
+
+type nullAdmissionController struct{}
+
+func (nullAdmissionController) AdmitPut(*pb.PutReq) error { return nil }
+
 // Cache represents key-value cache using google cloud storage.
 type Cache struct {
-	bkt *storage.BucketHandle
-
+	bkt                 *storage.BucketHandle
+	AdmissionController AdmissionController
 	// should be accessed via stomic pkg.
 	nhit, nget int64
 }
@@ -33,7 +43,8 @@
 // New creates new cache.
 func New(bkt *storage.BucketHandle) *Cache {
 	return &Cache{
-		bkt: bkt,
+		bkt:                 bkt,
+		AdmissionController: nullAdmissionController{},
 	}
 }
 
@@ -69,9 +80,12 @@
 
 func (c *Cache) Put(ctx context.Context, in *pb.PutReq) (*pb.PutResp, error) {
 	logger := log.FromContext(ctx)
+	if err := c.AdmissionController.AdmitPut(in); err != nil {
+		logger.Warnf("admission error: %v", err)
+		return nil, err
+	}
 	key := in.Kv.Key
 	value := in.Kv.Value
-
 	t := time.Now()
 
 	obj := c.bkt.Object(key)
@@ -93,6 +107,10 @@
 	w := obj.NewWriter(ctx)
 	w.CRC32C = crc32.Checksum(value, crc32cTable)
 	w.SendCRC32C = true
+	w.ChunkSize = len(value)
+	if w.ChunkSize > googleapi.DefaultUploadChunkSize {
+		w.ChunkSize = googleapi.DefaultUploadChunkSize
+	}
 
 	if _, err := w.Write(value); err != nil {
 		w.CloseWithError(err)
diff --git a/cipd_manifest.txt b/cipd_manifest.txt
new file mode 100644
index 0000000..847d7a1
--- /dev/null
+++ b/cipd_manifest.txt
@@ -0,0 +1,21 @@
+# Copyright 2019 Google Inc. All Rights Reserved.
+
+# Pin resolved versions in the repo, to reduce trust in the CIPD backend.
+#
+# To regenerate them (after modifying this file):
+#   cipd ensure-file-resolve -ensure-file cipd_manifest.txt
+$ResolvedVersions cipd_manifest.versions
+
+# Fully supported plaforms.
+$VerifiedPlatform linux-amd64
+
+# You can check available cipd package in:
+# https://chrome-infra-packages.appspot.com/
+
+# go
+infra/go/${platform} version:1.12.5
+
+# protoc
+# If the version you want is missing, please follow the instruction in:
+# https://chromium.googlesource.com/infra/infra/+/refs/heads/master/bootstrap/cipd/doc/infra/tools/protoc/
+infra/tools/protoc/${platform} protobuf_version:v3.7.0
diff --git a/cipd_manifest.versions b/cipd_manifest.versions
new file mode 100644
index 0000000..6dc1428
--- /dev/null
+++ b/cipd_manifest.versions
@@ -0,0 +1,10 @@
+# This file is auto-generated by 'cipd ensure-file-resolve'.
+# Do not modify manually. All changes will be overwritten.
+
+infra/go/linux-amd64
+	version:1.12.5
+	G8oyAmKE671-BJ-Mb1HdgChT7kkcoNwO-w_Z-vVnBGMC
+
+infra/tools/protoc/linux-amd64
+	protobuf_version:v3.7.0
+	pmLXDwrEZ3N2g3deymv-Dm3rhdYjU_UYWDUJ0bB3Kp8C
diff --git a/cmd/auth_server/main.go b/cmd/auth_server/main.go
index 17225d2..7f81f96 100644
--- a/cmd/auth_server/main.go
+++ b/cmd/auth_server/main.go
@@ -58,7 +58,7 @@
 var (
 	configUpdate = stats.Int64("go.chromium.org/goma/server/cmd/auth_server.acl-updates", "acl updates", stats.UnitDimensionless)
 
-	configStatusKey = mustTagNewKey("status")
+	configStatusKey = tag.MustNewKey("status")
 
 	configViews = []*view.View{
 		{
@@ -72,15 +72,6 @@
 	}
 )
 
-func mustTagNewKey(name string) tag.Key {
-	k, err := tag.NewKey(name)
-	if err != nil {
-		logger := log.FromContext(context.Background())
-		logger.Fatal(err)
-	}
-	return k
-}
-
 func recordConfigUpdate(ctx context.Context, err error) {
 	logger := log.FromContext(ctx)
 	status := "success"
diff --git a/cmd/exec_server/main.go b/cmd/exec_server/main.go
index 0feb6b3..68eaece 100644
--- a/cmd/exec_server/main.go
+++ b/cmd/exec_server/main.go
@@ -71,7 +71,7 @@
 var (
 	configUpdate = stats.Int64("go.chromium.org/goma/server/cmd/exec_server.toolchain-config-updates", "toolchain-config updates", stats.UnitDimensionless)
 
-	configStatusKey = mustTagNewKey("status")
+	configStatusKey = tag.MustNewKey("status")
 
 	configViews = []*view.View{
 		{
@@ -85,15 +85,6 @@
 	}
 )
 
-func mustTagNewKey(name string) tag.Key {
-	k, err := tag.NewKey(name)
-	if err != nil {
-		logger := log.FromContext(context.Background())
-		logger.Fatal(err)
-	}
-	return k
-}
-
 func recordConfigUpdate(ctx context.Context, err error) {
 	logger := log.FromContext(ctx)
 	status := "success"
@@ -139,13 +130,10 @@
 	if err != nil {
 		return "", err
 	}
-	n, err := inventory.Configure(ctx, resp)
+	err = inventory.Configure(ctx, resp)
 	if err != nil {
 		return "", err
 	}
-	if n == 0 {
-		return "", fmt.Errorf("no available config in %s (%d)", resp.VersionId, len(resp.Configs))
-	}
 	return resp.VersionId, nil
 }
 
@@ -399,12 +387,12 @@
 				return
 			}
 			resp := configMapToConfigResp(ctx, cm)
-			n, err := inventory.Configure(ctx, resp)
+			err = inventory.Configure(ctx, resp)
 			if err != nil {
 				ready <- err
 				return
 			}
-			logger.Infof("configure %d %s", n, resp.VersionId)
+			logger.Infof("configure %s", resp.VersionId)
 			ready <- nil
 		}()
 		confServer = nullServer{ch: make(chan error)}
diff --git a/cmd/file_server/main.go b/cmd/file_server/main.go
index 6534bdb..3424cce 100644
--- a/cmd/file_server/main.go
+++ b/cmd/file_server/main.go
@@ -13,8 +13,11 @@
 	"flag"
 
 	"cloud.google.com/go/storage"
+	k8sapi "golang.org/x/build/kubernetes/api"
 	"google.golang.org/api/option"
 	"google.golang.org/grpc"
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/status"
 
 	"go.chromium.org/goma/server/cache"
 	"go.chromium.org/goma/server/cache/gcs"
@@ -38,6 +41,23 @@
 	serviceAccountFile = flag.String("service-account-file", "", "service account json file")
 )
 
+type admissionController struct {
+	limit int64
+}
+
+func (a admissionController) AdmitPut(in *cachepb.PutReq) error {
+	if a.limit <= 0 {
+		return nil
+	}
+	rss := server.ResidentMemorySize()
+	s := int64(len(in.Kv.Key) + len(in.Kv.Value))
+	if rss+s <= a.limit {
+		return nil
+	}
+	// TODO: with retryinfo?
+	return status.Errorf(codes.ResourceExhausted, "memory size %d + req:%d > limit %d", rss, s, a.limit)
+}
+
 func main() {
 	flag.Parse()
 
@@ -90,6 +110,19 @@
 		}
 		defer gsclient.Close()
 		c := gcs.New(gsclient.Bucket(*bucket))
+		limit, err := server.MemoryLimit()
+		if err != nil {
+			logger.Errorf("unknown memory limit: %v", err)
+		} else {
+			margin := int64(2 * file.DefaultMaxMsgSize)
+			a := admissionController{
+				limit: limit - margin,
+			}
+			c.AdmissionController = a
+			limitq := k8sapi.NewQuantity(limit, k8sapi.BinarySI)
+			marginq := k8sapi.NewQuantity(margin, k8sapi.BinarySI)
+			logger.Infof("memory check threshold: limit:%s - mergin:%s = %d", limitq, marginq, a.limit)
+		}
 		cclient = cache.LocalClient{CacheServiceServer: c}
 
 	default:
diff --git a/cmd/frontend/main.go b/cmd/frontend/main.go
index e1c5ad1..ce1256a 100644
--- a/cmd/frontend/main.go
+++ b/cmd/frontend/main.go
@@ -69,29 +69,39 @@
 const maxMsgSize = 64 * 1024 * 1024
 
 type memoryCheck struct {
-	Threshold int64
+	hardThreshold int64
+	softThreshold int64
 }
 
+// Admit checks we can accept new request.
+// if memory usage is less than mc.softThreshold, it will accept.
+// Otherwise, it will try to run GC to release memory.
+// if memory usage is [mc.softThreshold, mc.hardThreshold), it returns
+// Unavailable error.
+// if memory usage is more than mc.hardThreshold, it returns ResourceExausted.
 func (mc memoryCheck) Admit(req *http.Request) error {
-	if mc.Threshold <= 0 {
+	if mc.softThreshold <= 0 {
 		return nil
 	}
 	rss := server.ResidentMemorySize()
-	if rss <= mc.Threshold {
+	if rss <= mc.softThreshold {
 		return nil
 	}
 	ctx := req.Context()
 	logger := log.FromContext(ctx)
-	logger.Warnf("memory size %d > threshold:%d", rss, mc.Threshold)
+	logger.Warnf("memory size %d > soft threshold:%d", rss, mc.softThreshold)
 	rss = server.GC(ctx)
-	if rss <= mc.Threshold {
+	if rss <= mc.softThreshold {
 		logger.Infof("GC reduced memory size to %d", rss)
 		return nil
 	}
-	m := fmt.Sprintf("memory size %d > threshold:%d", rss, mc.Threshold)
+	m := fmt.Sprintf("memory size %d > soft threshold:%d: over=%d", rss, mc.softThreshold, rss-mc.softThreshold)
 	healthz.SetUnhealthy(m)
 	logger.Errorf("GC couldn't reduce memory size: %s", m)
-	return status.Errorf(codes.ResourceExhausted, "server resource exhausted")
+	if mc.hardThreshold > 0 && rss > mc.hardThreshold {
+		return status.Errorf(codes.ResourceExhausted, "server resource exhausted")
+	}
+	return status.Errorf(codes.Unavailable, "server unavailable")
 }
 
 func newMainServer(mux *http.ServeMux) server.Server {
@@ -166,9 +176,10 @@
 		if err != nil {
 			logger.Errorf("unknown memory limit: %v", err)
 		} else {
-			memoryChecker.Threshold = limit - q.Value()
+			memoryChecker.hardThreshold = limit - q.Value()
+			memoryChecker.softThreshold = limit - 2*q.Value()
 			limitq := k8sapi.NewQuantity(limit, k8sapi.BinarySI)
-			logger.Infof("memory check threshold: limit:%s - margin:%s = %d", limitq, q, memoryChecker.Threshold)
+			logger.Infof("memory check threshold: limit:%s - margin:%s = hard:%d, soft:%d", limitq, q, memoryChecker.hardThreshold, memoryChecker.softThreshold)
 		}
 	}
 
diff --git a/cmd/remoteexec_proxy/main.go b/cmd/remoteexec_proxy/main.go
index 05e3aff..8bce206 100644
--- a/cmd/remoteexec_proxy/main.go
+++ b/cmd/remoteexec_proxy/main.go
@@ -23,9 +23,9 @@
 	"strings"
 	"time"
 
-	rpb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
-
 	"cloud.google.com/go/storage"
+	rpb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
+	"github.com/golang/protobuf/proto"
 	"go.opencensus.io/plugin/ocgrpc"
 	"go.opencensus.io/trace"
 	"google.golang.org/api/option"
@@ -69,6 +69,8 @@
 
 	fileCacheBucket = flag.String("file-cache-bucket", "", "file cache bucking store bucket")
 
+	execConfigFile = flag.String("exec-config-file", "", "exec inventory config file")
+
 	traceProjectID = flag.String("trace-project-id", "", "project id for cloud tracing")
 	traceFraction  = flag.Float64("trace-sampling-fraction", 1.0, "sampling fraction for stackdriver trace")
 	traceQPS       = flag.Float64("trace-sampling-qps-limit", 1.0, "sampling qps limit for stackdriver trace")
@@ -153,17 +155,6 @@
 	}, nil
 }
 
-func newDigestCache(ctx context.Context) remoteexec.DigestCache {
-	logger := log.FromContext(ctx)
-	addr, err := redis.AddrFromEnv()
-	if err != nil {
-		logger.Warnf("redis disabled for gomafile-digest: %v", err)
-		return digest.NewCache(nil)
-	}
-	logger.Infof("redis enabled for gomafile-digest: %v", addr)
-	return digest.NewCache(redis.NewClient(ctx, addr, "gomafile-digest:"))
-}
-
 type localBackend struct {
 	ExecService execpb.ExecServiceServer
 	FileService filepb.FileServiceServer
@@ -205,6 +196,29 @@
 	return execlogrpc.Handler(execlogService{}, httprpc.Timeout(1*time.Minute), httprpc.WithAuth(b.Auth))
 }
 
+func readConfigResp(fname string) (*cmdpb.ConfigResp, error) {
+	b, err := ioutil.ReadFile(fname)
+	if err != nil {
+		return nil, err
+	}
+	resp := &cmdpb.ConfigResp{}
+	err = proto.UnmarshalText(string(b), resp)
+	if err != nil {
+		return nil, err
+	}
+	// fix target address etc.
+	for _, c := range resp.Configs {
+		if c.Target == nil {
+			c.Target = &cmdpb.Target{}
+		}
+		c.Target.Addr = *remoteexecAddr
+		if c.BuildInfo == nil {
+			c.BuildInfo = &cmdpb.BuildInfo{}
+		}
+	}
+	return resp, nil
+}
+
 func main() {
 	flag.Parse()
 	ctx := context.Background()
@@ -309,14 +323,25 @@
 	}
 	defer reConn.Close()
 
+	var digestCache remoteexec.DigestCache
+	redisAddr, err := redis.AddrFromEnv()
+	if err != nil {
+		logger.Warnf("redis disabled for gomafile-digest: %v", err)
+		digestCache = digest.NewCache(nil)
+	} else {
+		logger.Infof("redis enabled for gomafile-digest: %v", redisAddr)
+		digestCache = digest.NewCache(redis.NewClient(ctx, redisAddr, "gomafile-digest:"))
+	}
+
 	re := &remoteexec.Adapter{
 		InstancePrefix: path.Dir(*remoteInstanceName),
 		ExecTimeout:    15 * time.Minute,
 		Client: remoteexec.Client{
 			ClientConn: reConn,
 		},
-		GomaFile:    fileServiceClient,
-		DigestCache: newDigestCache(ctx),
+		InsecureClient: *insecureRemoteexec,
+		GomaFile:       fileServiceClient,
+		DigestCache:    digestCache,
 		ToolDetails: &rpb.ToolDetails{
 			ToolName:    "remoteexec_proxy",
 			ToolVersion: "0.0.0-experimental",
@@ -324,7 +349,6 @@
 		FileLookupConcurrency: 2,
 	}
 
-	// TODO: non arbitrary toolchain support.
 	configResp := &cmdpb.ConfigResp{
 		VersionId: time.Now().UTC().Format(time.RFC3339),
 		Configs: []*cmdpb.Config{
@@ -348,7 +372,15 @@
 			},
 		},
 	}
-	_, err = re.Inventory.Configure(ctx, configResp)
+	// TODO: document config example?
+	if *execConfigFile != "" {
+		c, err := readConfigResp(*execConfigFile)
+		if err != nil {
+			logger.Fatal(err)
+		}
+		configResp = c
+	}
+	err = re.Inventory.Configure(ctx, configResp)
 	if err != nil {
 		logger.Fatal(err)
 	}
@@ -379,6 +411,11 @@
 <p><b>whitelisted-users:</b> {{.WhitelistedUsers}}</p>
 <p><b>service-account-json:</b> <a href="file://{{.ServiceAccountJSON}}">{{.ServiceAccountJSON}}</a></p>
 <p><b>platform-container-image:</b> {{.PlatformContainerImage}}</p>
+<p><b>redis:</b> {{.RedisAddr}}</p>
+<p><b>file-cache-bucket:</b> {{.FileCacheBucket}}</p>
+
+<p><b>config:</b>
+<pre>{{.Config}}</pre>
 
 <hr>
 <p>
@@ -396,6 +433,9 @@
 			WhitelistedUsers       []string
 			ServiceAccountJSON     string
 			PlatformContainerImage string
+			RedisAddr              string
+			FileCacheBucket        string
+			Config                 *cmdpb.ConfigResp
 		}{
 			Port:                   *port,
 			RemoteexecAddr:         *remoteexecAddr,
@@ -403,6 +443,9 @@
 			WhitelistedUsers:       whitelisted,
 			ServiceAccountJSON:     *serviceAccountJSON,
 			PlatformContainerImage: *platformContainerImage,
+			RedisAddr:              redisAddr,
+			FileCacheBucket:        *fileCacheBucket,
+			Config:                 configResp,
 		})
 		if err != nil {
 			logger := log.FromContext(ctx)
diff --git a/command/configmap.go b/command/configmap.go
index 9289d85..4275fd4 100644
--- a/command/configmap.go
+++ b/command/configmap.go
@@ -433,6 +433,7 @@
 		return nil, err
 	}
 	mergePlatformProperties(platform, rc.Platform)
+	platform.HasNsjail = rc.GetPlatformRuntimeConfig().GetHasNsjail()
 
 	confs, err := loadConfigs(ctx, c.StorageClient, uri, rc, platform)
 	if err != nil {
diff --git a/exec/inventory.go b/exec/inventory.go
index 59247f0..d34d8da 100644
--- a/exec/inventory.go
+++ b/exec/inventory.go
@@ -35,8 +35,8 @@
 		"Toolchain selection",
 		stats.UnitDimensionless)
 
-	selectorKey = mustTagNewKey("selector")
-	resultKey   = mustTagNewKey("result")
+	selectorKey = tag.MustNewKey("selector")
+	resultKey   = tag.MustNewKey("result")
 
 	// DefaultToolchainViews are the default views provided by this package.
 	// You need to register the view for data to actually be collected.
@@ -150,7 +150,7 @@
 }
 
 // Configure sets config in the inventory.
-func (in *Inventory) Configure(ctx context.Context, cfgs *cmdpb.ConfigResp) (int, error) {
+func (in *Inventory) Configure(ctx context.Context, cfgs *cmdpb.ConfigResp) error {
 	ctx, span := trace.StartSpan(ctx, "go.chromium.org/goma/server/exec.Service.Configure")
 	defer span.End()
 	logger := log.FromContext(ctx)
@@ -212,7 +212,10 @@
 	in.addrs = newAddrs
 	in.configs = newConfigs
 	in.platformConfigs = newPlatformConfigs
-	return len(in.configs), nil
+	if len(in.configs) == 0 && len(in.platformConfigs) == 0 {
+		return fmt.Errorf("no available config in %s", cfgs.VersionId)
+	}
+	return nil
 }
 
 func (in *Inventory) VersionID() string {
@@ -496,6 +499,8 @@
 	cmdSel, _, err := fromCommandSpec(req.GetCommandSpec())
 	if err != nil {
 		resp.Error = gomapb.ExecResp_BAD_REQUEST.Enum()
+		resp.ErrorMessage = append(resp.ErrorMessage, fmt.Sprintf("unexpected CommandSpec %s", req.GetCommandSpec()))
+
 		return nil, nil, fmt.Errorf("normalize %v: %v", req.GetCommandSpec(), err)
 	}
 
diff --git a/exec/stats.go b/exec/stats.go
index 58acb7f..05bc333 100644
--- a/exec/stats.go
+++ b/exec/stats.go
@@ -11,7 +11,6 @@
 	"go.opencensus.io/stats/view"
 	"go.opencensus.io/tag"
 
-	"go.chromium.org/goma/server/log"
 	gomapb "go.chromium.org/goma/server/proto/api"
 )
 
@@ -21,7 +20,7 @@
 		"exec request api-error",
 		stats.UnitDimensionless)
 
-	apiErrorKey = mustTagNewKey("api-error")
+	apiErrorKey = tag.MustNewKey("api-error")
 
 	// DefaultViews are the default views provided by this package.
 	// You need to register the view for data to actually be collected.
@@ -46,15 +45,6 @@
 	}
 )
 
-func mustTagNewKey(name string) tag.Key {
-	k, err := tag.NewKey(name)
-	if err != nil {
-		logger := log.FromContext(context.Background())
-		logger.Fatal(err)
-	}
-	return k
-}
-
 func apiErrorValue(resp *gomapb.ExecResp) string {
 	if errVal := resp.GetError(); errVal != gomapb.ExecResp_OK {
 		// marked as BAD_REQUEST for
diff --git a/file/service.go b/file/service.go
index 563166c..b31d52a 100644
--- a/file/service.go
+++ b/file/service.go
@@ -11,6 +11,9 @@
 
 	"github.com/golang/protobuf/proto"
 	"go.opencensus.io/trace"
+	"golang.org/x/sync/errgroup"
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/status"
 
 	"go.chromium.org/goma/server/hash"
 	"go.chromium.org/goma/server/log"
@@ -46,24 +49,30 @@
 		HashKey: make([]string, len(req.GetBlob())),
 	}
 
-	var wg sync.WaitGroup
+	// if it contains one blob only, report error for blob as rpc error.
+	single := len(req.GetBlob()) == 1
+
+	errg, ctx := errgroup.WithContext(ctx)
 
 	for i, blob := range req.GetBlob() {
-		wg.Add(1)
+		i, blob := i, blob
 		// TODO: limit goroutine if cache server is overloaded or many request consume many memory.
-		go func(i int, blob *gomapb.FileBlob) {
-			defer wg.Done()
+		errg.Go(func() error {
 			if !IsValid(blob) {
 				span.Annotatef(nil, "%d: invalid blob", i)
 				logger.Errorf("%d: invalid blob", i)
-				return
+				if single {
+					return status.Error(codes.InvalidArgument, "not valid blob")
+				}
+				return nil
 			}
 			t := time.Now()
 			b, err := proto.Marshal(blob)
 			if err != nil {
+				// blob has been marshalled, so it should never fail.
 				span.Annotatef(nil, "%d: proto.Marshal %v", i, err)
 				logger.Errorf("%d: proto.Marshal: %v", i, err)
-				return
+				return nil
 			}
 			marshalTime := time.Since(t)
 			t = time.Now()
@@ -80,14 +89,25 @@
 			span.Annotatef(nil, "%d hashKey=%s: %v", i, hashKey, err)
 			if err != nil {
 				logger.Errorf("%d: cache.Put %s: %v", i, hashKey, err)
-				return
+				if single || status.Code(err) == codes.ResourceExhausted {
+					// when resource exhausted, fail whole request, not fail of individual blob.
+					return err
+				}
+				// TODO: report individual error.
+				// client will get empty HashKey for failed blobs.
+				return nil
 			}
 			resp.HashKey[i] = hashKey
 			logger.Infof("%d: cache.Put %s: marshal:%s hash:%s put:%s", i, hashKey, marshalTime, hashTime, putTime)
-		}(i, blob)
+			return nil
+		})
 	}
 	logger.Debugf("waiting store %d blobs", len(req.GetBlob()))
-	wg.Wait()
+	err := errg.Wait()
+	if err != nil {
+		logger.Warnf("store %d blobs %s: %v", len(req.GetBlob()), time.Since(start), err)
+		return nil, err
+	}
 	logger.Debugf("store %d blobs %s", len(req.GetBlob()), time.Since(start))
 	return resp, nil
 }
diff --git a/frontend/frontend.go b/frontend/frontend.go
index 2a68eb3..1366124 100644
--- a/frontend/frontend.go
+++ b/frontend/frontend.go
@@ -41,8 +41,8 @@
 	// the value of these tag can be controlled by the client,
 	// so you need to watch out for potentially generating high-cardinality
 	// labels in your metrics backend if you use this tag in views.
-	userAgentCommitHashKey = mustTagNewKey("useragent.hash")
-	userAgentCommitTimeKey = mustTagNewKey("useragent.time")
+	userAgentCommitHashKey = tag.MustNewKey("useragent.hash")
+	userAgentCommitTimeKey = tag.MustNewKey("useragent.time")
 
 	// user-agent format
 	//  compiler-proxy built by <username> at <commitHash>@<commitTime>
@@ -74,15 +74,6 @@
 	}
 }
 
-func mustTagNewKey(name string) tag.Key {
-	k, err := tag.NewKey(name)
-	if err != nil {
-		logger := log.FromContext(context.Background())
-		logger.Fatal(err)
-	}
-	return k
-}
-
 func parseUserAgent(header string) (commitHash, commitTime string, err error) {
 	m := userAgentRE.FindStringSubmatch(header)
 	if len(m) == 0 {
diff --git a/go.mod b/go.mod
index 46467c1..adebda3 100644
--- a/go.mod
+++ b/go.mod
@@ -3,28 +3,44 @@
 go 1.12
 
 require (
-	cloud.google.com/go v0.39.0
-	contrib.go.opencensus.io/exporter/stackdriver v0.11.0
-	github.com/bazelbuild/remote-apis v0.0.0-20190507145712-5556e9c6153f
+	cloud.google.com/go v0.40.0
+	contrib.go.opencensus.io/exporter/stackdriver v0.12.2
+	github.com/bazelbuild/remote-apis v0.0.0-20190606163526-a5c577357528
 	github.com/fsnotify/fsnotify v1.4.7
+	github.com/gogo/protobuf v1.2.1 // indirect
 	github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef
+	github.com/golang/mock v1.3.1 // indirect
 	github.com/golang/protobuf v1.3.1
 	github.com/gomodule/redigo v2.0.0+incompatible
-	github.com/google/go-cmp v0.2.0
+	github.com/google/btree v1.0.0 // indirect
+	github.com/google/go-cmp v0.3.0
+	github.com/google/pprof v0.0.0-20190515194954-54271f7e092f // indirect
 	github.com/google/uuid v1.1.1
-	github.com/googleapis/gax-go/v2 v2.0.4
+	github.com/googleapis/gax-go/v2 v2.0.5
 	github.com/googleapis/google-cloud-go-testing v0.0.0-20190307174402-f55056552511
 	github.com/grpc-ecosystem/go-grpc-middleware v1.0.0
-	go.opencensus.io v0.21.0
+	github.com/kr/pty v1.1.5 // indirect
+	github.com/pkg/errors v0.8.1 // indirect
+	github.com/sirupsen/logrus v1.4.2 // indirect
+	go.opencensus.io v0.22.0
 	go.uber.org/atomic v1.3.2 // indirect
 	go.uber.org/multierr v1.1.0 // indirect
 	go.uber.org/zap v1.10.0
 	golang.org/x/build v0.0.0-20190314215453-3ce8d48fad73
-	golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53
-	golang.org/x/oauth2 v0.0.0-20190319182350-c85d3e98c914
-	golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6
-	google.golang.org/api v0.5.0
-	google.golang.org/genproto v0.0.0-20190508193815-b515fa19cec8
-	google.golang.org/grpc v1.20.1
-	gopkg.in/yaml.v2 v2.2.2 // indirect
+	golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443 // indirect
+	golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522 // indirect
+	golang.org/x/image v0.0.0-20190618124811-92942e4437e2 // indirect
+	golang.org/x/mobile v0.0.0-20190607214518-6fa95d984e88 // indirect
+	golang.org/x/mod v0.1.0 // indirect
+	golang.org/x/net v0.0.0-20190620200207-3b0461eec859
+	golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
+	golang.org/x/sync v0.0.0-20190423024810-112230192c58
+	golang.org/x/sys v0.0.0-20190620070143-6f217b454f45 // indirect
+	golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect
+	golang.org/x/tools v0.0.0-20190620191750-1fa568393b23 // indirect
+	google.golang.org/api v0.6.0
+	google.golang.org/appengine v1.6.1 // indirect
+	google.golang.org/genproto v0.0.0-20190620144150-6af8c5fc6601
+	google.golang.org/grpc v1.21.1
+	honnef.co/go/tools v0.0.0-20190614002413-cb51c254f01b // indirect
 )
diff --git a/go.sum b/go.sum
index bba7956..76857ab 100644
--- a/go.sum
+++ b/go.sum
@@ -2,66 +2,50 @@
 cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 cloud.google.com/go v0.36.0/go.mod h1:RUoy9p/M4ge0HzT8L+SDZ8jg+Q6fth0CiBuhFJpSV40=
-cloud.google.com/go v0.37.1 h1:2kHhTjz+eKEI7tt3Fqf5j3APCq+z9tuY2CzeCIxTo+A=
-cloud.google.com/go v0.37.1/go.mod h1:SAbnLi6YTSPKSI0dTUEOVLCkyPfKXK8n4ibqiMoj4ok=
-cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU=
-cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
 cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo=
 cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
 cloud.google.com/go v0.39.0 h1:UgQP9na6OTfp4dsAiz/eFpFA1C6tPdH5wiRdi19tuMw=
 cloud.google.com/go v0.39.0/go.mod h1:rVLT6fkc8chs9sfPtFc1SBH6em7n+ZoXaG+87tDISts=
-contrib.go.opencensus.io/exporter/ocagent v0.4.9 h1:8ZbMXpyd04/3LILa/9Tzr8N4HzZNj6Vb2xsaSuR4nQI=
-contrib.go.opencensus.io/exporter/ocagent v0.4.9/go.mod h1:ueLzZcP7LPhPulEBukGn4aLh7Mx9YJwpVJ9nL2FYltw=
-contrib.go.opencensus.io/exporter/ocagent v0.4.12/go.mod h1:450APlNTSR6FrvC3CTRqYosuDstRB9un7SOx2k/9ckA=
-contrib.go.opencensus.io/exporter/stackdriver v0.10.0 h1:izjZKmiNZ7KynTHN6rZajJNpjbnZyZDRlF38q65Zoj8=
-contrib.go.opencensus.io/exporter/stackdriver v0.10.0/go.mod h1:vZAW3NS0lTrrzGcMndYsNJuJKub+Ta3GnUeKOf69oOE=
-contrib.go.opencensus.io/exporter/stackdriver v0.11.0 h1:PV4m31gF3xT3oFDou7SxUVver/jja9sJ20HeTIGR2nM=
-contrib.go.opencensus.io/exporter/stackdriver v0.11.0/go.mod h1:hA7rlmtavV03FGxzWXAPBUnZeZBhWN/QYQAuMtxc9Bk=
-contrib.go.opencensus.io/resource v0.0.0-20190131005048-21591786a5e0 h1:ICrSnXeuT4427bpR8X9I7GxiyT4X5qgLtFT7m1IjK2c=
-contrib.go.opencensus.io/resource v0.0.0-20190131005048-21591786a5e0/go.mod h1:F361eGI91LCmW1I/Saf+rX0+OFcigGlFvXwEGEnkRLA=
+cloud.google.com/go v0.40.0 h1:FjSY7bOj+WzJe6TZRVtXI2b9kAYvtNg4lMbcH2+MUkk=
+cloud.google.com/go v0.40.0/go.mod h1:Tk58MuI9rbLMKlAjeO/bDnteAx7tX2gJIXw4T5Jwlro=
+contrib.go.opencensus.io/exporter/stackdriver v0.12.1 h1:Dll2uFfOVI3fa8UzsHyP6z0M6fEc9ZTAMo+Y3z282Xg=
+contrib.go.opencensus.io/exporter/stackdriver v0.12.1/go.mod h1:iwB6wGarfphGGe/e5CWqyUk/cLzKnWsOKPVW3no6OTw=
+contrib.go.opencensus.io/exporter/stackdriver v0.12.2 h1:jU1p9F07ASK11wYgSTPKtFlTvTtCDj6R1d3nRt0ZHDE=
+contrib.go.opencensus.io/exporter/stackdriver v0.12.2/go.mod h1:iwB6wGarfphGGe/e5CWqyUk/cLzKnWsOKPVW3no6OTw=
+contrib.go.opencensus.io/resource v0.1.1/go.mod h1:F361eGI91LCmW1I/Saf+rX0+OFcigGlFvXwEGEnkRLA=
 dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
 dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
 dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
 dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
 git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
-git.apache.org/thrift.git v0.12.0/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
-github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
-github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
-github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
-github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
-github.com/aws/aws-sdk-go v1.18.6 h1:NuUz/+bi6C5v3BpIXW/VfovfMpvlhl1WUnD0EiDkOwQ=
-github.com/aws/aws-sdk-go v1.18.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
+github.com/aws/aws-sdk-go v1.19.18 h1:Hb3+b9HCqrOrbAtFstUWg7H5TQ+/EcklJtE8VShVs8o=
+github.com/aws/aws-sdk-go v1.19.18/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
 github.com/bazelbuild/remote-apis v0.0.0-20190507145712-5556e9c6153f h1:KLZodm2mQtskynsbHchbt9IIbGrpmBm3/585uCA798M=
 github.com/bazelbuild/remote-apis v0.0.0-20190507145712-5556e9c6153f/go.mod h1:9Y+1FnaNUGVV6wKE0Jdh+mguqDUsyd9uUqokalrC7DQ=
+github.com/bazelbuild/remote-apis v0.0.0-20190606163526-a5c577357528 h1:/+plLAl5S7giewLlFr2GlBm1AwPcirP4QnUssKsA7KQ=
+github.com/bazelbuild/remote-apis v0.0.0-20190606163526-a5c577357528/go.mod h1:9Y+1FnaNUGVV6wKE0Jdh+mguqDUsyd9uUqokalrC7DQ=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
 github.com/census-instrumentation/opencensus-proto v0.2.0 h1:LzQXZOgg4CQfE6bFvXGM30YZL1WW/M337pXml+GrcZ4=
 github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
-github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
-github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
-github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
-github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
 github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
 github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
-github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
-github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
-github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
-github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
 github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
-github.com/gogo/protobuf v1.2.0 h1:xU6/SpYbvkNYiptHJYEDRseDLvYE7wSqhYYNy0QSUzI=
-github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
+github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk=
@@ -70,21 +54,28 @@
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=
 github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s=
+github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
 github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
 github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
 github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
 github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
 github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
 github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57 h1:eqyIo2HjKhKe/mJzTG8n4VqvLXIOEG+SLdDqX7xGtkY=
 github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/pprof v0.0.0-20190515194954-54271f7e092f h1:Jnx61latede7zDD3DiiP4gmNz33uK0U5HDUaF0a/HVQ=
+github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
 github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
 github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/googleapis/gax-go v2.0.0+incompatible h1:j0GKcs05QVmm7yesiZq2+9cxHkNK9YM6zKx4D2qucQU=
@@ -92,64 +83,45 @@
 github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
 github.com/googleapis/gax-go/v2 v2.0.4 h1:hU4mGcQI4DaAYW+IbTun+2qEZVFxK0ySjQLTbS0VQKc=
 github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
+github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
+github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
 github.com/googleapis/google-cloud-go-testing v0.0.0-20190307174402-f55056552511 h1:u4Kw7mJgpryVfx25Qe/+uMHS77lbBrVqRNeCzBLeIHw=
 github.com/googleapis/google-cloud-go-testing v0.0.0-20190307174402-f55056552511/go.mod h1:e4mrQK+Snl9SPRA0LN2wVMV5yAryamv06jcokqRdnFo=
 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
-github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
-github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
 github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 h1:Iju5GlWwrvL6UBg4zJJt3btmonfrMlCDdsejg4CZE7c=
 github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
 github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
-github.com/grpc-ecosystem/grpc-gateway v1.6.2 h1:8KyC64BiO8ndiGHY5DlFWWdangUPC9QHPakFRre/Ud0=
-github.com/grpc-ecosystem/grpc-gateway v1.6.2/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
-github.com/grpc-ecosystem/grpc-gateway v1.8.5 h1:2+KSC78XiO6Qy0hIjfc1OD9H+hsaJdJlb8Kqsd41CTE=
-github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
 github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
+github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
 github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
 github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
 github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
-github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
-github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
-github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
 github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
-github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
-github.com/openzipkin/zipkin-go v0.1.3/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
-github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
-github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
-github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
-github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
-github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
-github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
 github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
-github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
-github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
-github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
 github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
 github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
@@ -174,159 +146,187 @@
 github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
 github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
-github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo=
-github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
 github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
 go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
-go.opencensus.io v0.19.1 h1:gPYKQ/GAQYR2ksU+qXNmq3CrOZWT1kkryvW6O0v1acY=
-go.opencensus.io v0.19.1/go.mod h1:gug0GbSHa8Pafr0d2urOSgoXHZ6x/RUlaiT0d9pqb4A=
-go.opencensus.io v0.20.0 h1:L/ARO58pdktB6dLmYI0zAyW1XnavEmGziFd0MKfxnck=
-go.opencensus.io v0.20.0/go.mod h1:NO/8qkisMZLZ1FCsKNqtJPwc8/TaclWyY0B6wcYNg9M=
-go.opencensus.io v0.20.1 h1:pMEjRZ1M4ebWGikflH7nQpV6+Zr88KBMA2XJD3sbijw=
-go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
-go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
 go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg=
 go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
+go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
+go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
 go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4=
 go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
 go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
-go.uber.org/zap v1.9.1 h1:XCJQEf3W6eZaVwhRBof6ImoYGJSITeKWsyeh3HFu/5o=
-go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
 go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
 go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
 go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
 golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
-golang.org/x/build v0.0.0-20190314133821-5284462c4bec/go.mod h1:atTaCNAy0f16Ah5aV1gMSwgiKVHwu/JncqDpuRr7lS4=
 golang.org/x/build v0.0.0-20190314215453-3ce8d48fad73 h1:ftYCODVYglUsck2VftcuEOPYYTCUPWmP54ql+oMs4yA=
 golang.org/x/build v0.0.0-20190314215453-3ce8d48fad73/go.mod h1:atTaCNAy0f16Ah5aV1gMSwgiKVHwu/JncqDpuRr7lS4=
-golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
+golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
+golang.org/x/image v0.0.0-20190618124811-92942e4437e2/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
 golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20181217174547-8f45f776aaf1/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
+golang.org/x/mobile v0.0.0-20190607214518-6fa95d984e88/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
+golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
+golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53 h1:kcXqo9vE6fsZY5X5Rd7R1l7fTgnWaDCVmln65REefiE=
-golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco=
+golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20190319182350-c85d3e98c914 h1:jIOcLT9BZzyJ9ce+IwwZ+aF9yeCqzrR+NrD68a/SHKw=
-golang.org/x/oauth2 v0.0.0-20190319182350-c85d3e98c914/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20190517181255-950ef44c6e07 h1:XC1K3wNjuz44KaI+cj85C9TW85w/46RH7J+DTXNH5Wk=
+golang.org/x/oauth2 v0.0.0-20190517181255-950ef44c6e07/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
+golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI=
 golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181218192612-074acd46bca6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5 h1:f005F/Jl5JLP036x7QIvUVhNTqxvSYwFIiyOh2q12iU=
+golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190620070143-6f217b454f45 h1:Dl2hc890lrizvUppGbRWhnIh2f8jOTCQpY5IKWRS0oM=
+golang.org/x/sys v0.0.0-20190620070143-6f217b454f45/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20181219222714-6e267b5cc78e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
 golang.org/x/tools v0.0.0-20190305160728-f8c04913dfb7/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190530171427-2b03ca6e44eb/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190620191750-1fa568393b23/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
 google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
-google.golang.org/api v0.0.0-20181220000619-583d854617af/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
 google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
-google.golang.org/api v0.2.0 h1:B5VXkdjt7K2Gm6fGBC9C9a1OAKJDT95cTqwet+2zib0=
-google.golang.org/api v0.2.0/go.mod h1:IfRCZScioGtypHNTlz3gFk67J8uePVW7uDTBzXuIkhU=
-google.golang.org/api v0.3.1 h1:oJra/lMfmtm13/rgY/8i3MzjFWYXvQIAKjQ3HqofMk8=
-google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
-google.golang.org/api v0.3.2 h1:iTp+3yyl/KOtxa/d1/JUE0GGSoR6FuW5udver22iwpw=
-google.golang.org/api v0.3.2/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
 google.golang.org/api v0.4.0 h1:KKgc1aqhV8wDPbDzlDtpvyjZFY3vjz85FP7p4wcQUyI=
 google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
 google.golang.org/api v0.5.0 h1:lj9SyhMzyoa38fgFF0oO2T6pjs5IzkLPKfVtxpyCRMM=
 google.golang.org/api v0.5.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
+google.golang.org/api v0.6.0 h1:2tJEkRfnZL5g1GeBUlITh/rqT5HG3sFcoVCUUxmgJ2g=
+google.golang.org/api v0.6.0/go.mod h1:btoxGiFvQNVUZQ8W08zLtrVS08CNpINPEfxXxgJL1Q4=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.6.0 h1:Tfd7cKwKbFRsI8RMAD3oqqw7JPFRrvFlOsfbgVkjOOw=
+google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
-google.golang.org/genproto v0.0.0-20181219182458-5a97ab628bfb/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
 google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922/go.mod h1:L3J43x8/uS+qIUoksaLKe6OS3nUKxOKuIFz1sl2/jx4=
 google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19 h1:Lj2SnHtxkRGJDqnGaSjo+CCdIieEnwVazbOXILwQemk=
 google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107 h1:xtNn7qFlagY2mQNFHMSRPjT2RkOV4OXM7P5TVy9xATo=
-google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7 h1:ZUjXAXmrAyrmmCPHgCA/vChHcpsX27MZ3yBonD/z1KE=
 google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873 h1:nfPFGzJkUDX6uBmpN/pSw7MbOAWegH5QDQuoXFHedLg=
 google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 google.golang.org/genproto v0.0.0-20190508193815-b515fa19cec8 h1:x913Lq/RebkvUmRSdQ8MNb0GZKn+SR1ESfoetcQSeak=
 google.golang.org/genproto v0.0.0-20190508193815-b515fa19cec8/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69 h1:4rNOqY4ULrKzS6twXa619uQgI7h9PaVd4ZhjFQ7C5zs=
+google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
+google.golang.org/genproto v0.0.0-20190530194941-fb225487d101 h1:wuGevabY6r+ivPNagjUXGGxF+GqgMd+dBhjsxW4q9u4=
+google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
+google.golang.org/genproto v0.0.0-20190620144150-6af8c5fc6601 h1:9VBRTdmgQxbs6HE0sUnMrSWNePppAJU07NYvX5dIB04=
+google.golang.org/genproto v0.0.0-20190620144150-6af8c5fc6601/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
 google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
 google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
 google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
 google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
-google.golang.org/grpc v1.19.1 h1:TrBcJ1yqAl1G++wO39nD/qtgpsW9/1+QGrluyMGEYgM=
-google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
-google.golang.org/grpc v1.20.0 h1:DlsSIrgEBuZAUFJcta2B5i/lzeHHbnfkNFAfFXLVFYQ=
-google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
 google.golang.org/grpc v1.20.1 h1:Hz2g2wirWK7H0qIIhGIqRGTuMwTE8HEKFnDZZ7lm9NU=
 google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
-gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
+google.golang.org/grpc v1.21.0 h1:G+97AoqBnmZIT91cLG/EkCoK9NSelj64P8bOHHNmGn0=
+google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+google.golang.org/grpc v1.21.1 h1:j6XxA85m/6txkUCHvzlV5f+HBNl/1r5cZ2A/3IEFOO8=
+google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
 gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
-gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
-gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
-gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
 gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
 honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20180920025451-e3ad64cb4ed3/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190215041234-466a0476246c/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190614002413-cb51c254f01b/go.mod h1:JlmFZigtG9vBVR3QGIQ9g/Usz4BzH+Xm6Z8iHQWRYUw=
+rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
 sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
 sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
diff --git a/httprpc/server.go b/httprpc/server.go
index 491fbd4..897ee82 100644
--- a/httprpc/server.go
+++ b/httprpc/server.go
@@ -346,6 +346,7 @@
 		// see below if status.Code(err) == codes.DeadlineExceeded case.
 		timeouts := []time.Duration{40 * time.Second, 1 * time.Minute, 3 * time.Minute, 5 * time.Minute}
 		var resp proto.Message
+		authOK := false
 		err = opt.retry.Do(ctx, func() error {
 			pctx := ctx
 			ctx, cancel := context.WithTimeout(ctx, timeouts[0])
@@ -354,11 +355,19 @@
 			if opt.Auth != nil {
 				ctx, err = opt.Auth.Auth(ctx, r)
 				if err != nil {
+					if authOK {
+						// if once auth ok, then it failed because client access token was expired.
+						code := http.StatusGatewayTimeout
+						http.Error(w, fmt.Sprintf("auth token expired %s", RemoteAddr(r)), code)
+						logger.Errorf("auth token expired %s: %d %s", r.URL.Path, code, http.StatusText(code))
+						return err
+					}
 					code := http.StatusUnauthorized
 					http.Error(w, fmt.Sprintf("auth failed %s: %v", RemoteAddr(r), err), code)
 					logger.Errorf("auth error %s: %d %s: %v", r.URL.Path, code, http.StatusText(code), err)
 					return err
 				}
+				authOK = true
 			}
 			resp, err = h(ctx, req)
 			if opt.Auth != nil && status.Code(err) == codes.Unauthenticated {
diff --git a/proto/command/command.pb.go b/proto/command/command.pb.go
index b0f287f..a77e38f 100644
--- a/proto/command/command.pb.go
+++ b/proto/command/command.pb.go
@@ -585,7 +585,9 @@
 type RemoteexecPlatform struct {
 	Properties []*RemoteexecPlatform_Property `protobuf:"bytes,1,rep,name=properties,proto3" json:"properties,omitempty"`
 	// Basename of RBE instance to use. e.g. "default_instance" or "windows".
-	RbeInstanceBasename  string   `protobuf:"bytes,2,opt,name=rbe_instance_basename,json=rbeInstanceBasename,proto3" json:"rbe_instance_basename,omitempty"`
+	RbeInstanceBasename string `protobuf:"bytes,2,opt,name=rbe_instance_basename,json=rbeInstanceBasename,proto3" json:"rbe_instance_basename,omitempty"`
+	// Set true if nsjail is available in the platform image.
+	HasNsjail            bool     `protobuf:"varint,3,opt,name=has_nsjail,json=hasNsjail,proto3" json:"has_nsjail,omitempty"`
 	XXX_NoUnkeyedLiteral struct{} `json:"-"`
 	XXX_unrecognized     []byte   `json:"-"`
 	XXX_sizecache        int32    `json:"-"`
@@ -630,6 +632,13 @@
 	return ""
 }
 
+func (m *RemoteexecPlatform) GetHasNsjail() bool {
+	if m != nil {
+		return m.HasNsjail
+	}
+	return false
+}
+
 type RemoteexecPlatform_Property struct {
 	// The property name.
 	Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
@@ -960,9 +969,11 @@
 }
 
 // PlatformRuntimeConfig is a config to use the runtime.
-// NEXT ID TO USE: 2
+// NEXT ID TO USE: 3
 type PlatformRuntimeConfig struct {
-	Dimensions           []string `protobuf:"bytes,1,rep,name=dimensions,proto3" json:"dimensions,omitempty"`
+	Dimensions []string `protobuf:"bytes,1,rep,name=dimensions,proto3" json:"dimensions,omitempty"`
+	// Set true if nsjail is available in the platform image.
+	HasNsjail            bool     `protobuf:"varint,2,opt,name=has_nsjail,json=hasNsjail,proto3" json:"has_nsjail,omitempty"`
 	XXX_NoUnkeyedLiteral struct{} `json:"-"`
 	XXX_unrecognized     []byte   `json:"-"`
 	XXX_sizecache        int32    `json:"-"`
@@ -1000,6 +1011,13 @@
 	return nil
 }
 
+func (m *PlatformRuntimeConfig) GetHasNsjail() bool {
+	if m != nil {
+		return m.HasNsjail
+	}
+	return false
+}
+
 // ConfigMap is a config map; data source of Config.
 // admin creates/updates the file in <bucket>/<config>.config
 // and ConfigMapBucket will read the info.
@@ -1114,80 +1132,81 @@
 func init() { proto.RegisterFile("command/command.proto", fileDescriptor_ce77433d29a243f3) }
 
 var fileDescriptor_ce77433d29a243f3 = []byte{
-	// 1191 bytes of a gzipped FileDescriptorProto
+	// 1215 bytes of a gzipped FileDescriptorProto
 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0xdb, 0x72, 0x1b, 0x45,
 	0x13, 0xfe, 0x65, 0xeb, 0xb0, 0xdb, 0x8a, 0x13, 0x79, 0x1c, 0x27, 0x8a, 0x7e, 0x93, 0x04, 0x41,
-	0x55, 0x02, 0x01, 0xb9, 0xa2, 0x50, 0xc5, 0xa1, 0xa0, 0x52, 0xb1, 0x1d, 0x2a, 0x76, 0xc0, 0x56,
-	0x8d, 0x4d, 0x02, 0x57, 0x53, 0xa3, 0xdd, 0xb6, 0x34, 0x95, 0x3d, 0x31, 0x33, 0x32, 0x88, 0xe2,
-	0x19, 0x28, 0x9e, 0x80, 0xb7, 0xe0, 0x0d, 0x78, 0x03, 0x9e, 0x85, 0x7b, 0x6a, 0x0e, 0xbb, 0x8a,
-	0x6d, 0x25, 0x17, 0x5c, 0x69, 0xbb, 0xfb, 0x9b, 0xee, 0xe9, 0xfe, 0xba, 0x7b, 0x04, 0x9b, 0x51,
-	0x9e, 0xa6, 0x3c, 0x8b, 0xb7, 0xfd, 0xef, 0xa0, 0x90, 0xb9, 0xce, 0x49, 0xcb, 0x8b, 0xbd, 0x0d,
-	0x5e, 0x88, 0xed, 0x49, 0x9e, 0x72, 0x16, 0x73, 0xcd, 0x9d, 0xb5, 0x77, 0x67, 0x92, 0xe7, 0x93,
-	0x04, 0xb7, 0xad, 0x34, 0x9e, 0x9d, 0x6e, 0x6b, 0x91, 0xa2, 0xd2, 0x3c, 0x2d, 0x1c, 0xa0, 0xff,
-	0x23, 0x04, 0xc7, 0x98, 0x60, 0xa4, 0x73, 0x49, 0x08, 0xd4, 0x33, 0x9e, 0x62, 0xb7, 0x76, 0xb7,
-	0x76, 0x3f, 0xa4, 0xf6, 0x9b, 0x74, 0xa1, 0x75, 0x86, 0x52, 0x89, 0x3c, 0xeb, 0xae, 0x58, 0x75,
-	0x29, 0x92, 0x1b, 0xd0, 0xd4, 0x5c, 0x4e, 0x50, 0x77, 0x57, 0xad, 0xc1, 0x4b, 0xe4, 0x0e, 0xb4,
-	0xc7, 0x22, 0xe3, 0x72, 0xce, 0xa6, 0x5c, 0x4d, 0xbb, 0x75, 0x6b, 0x04, 0xa7, 0x7a, 0xc6, 0xd5,
-	0xb4, 0xff, 0x77, 0x0d, 0x82, 0xaf, 0x45, 0x82, 0xc7, 0x05, 0x46, 0x26, 0x66, 0xc1, 0xf5, 0xb4,
-	0x8c, 0x69, 0xbe, 0x8d, 0x4e, 0x89, 0x5f, 0xd0, 0x06, 0x5c, 0xa5, 0xf6, 0xdb, 0xe8, 0xac, 0x3b,
-	0x17, 0xcb, 0x7e, 0x93, 0xf7, 0x60, 0x4d, 0x28, 0x86, 0x3f, 0x63, 0x34, 0xd3, 0x7c, 0x9c, 0xa0,
-	0x8d, 0x15, 0xd0, 0x2b, 0x42, 0x3d, 0xad, 0x74, 0x26, 0x01, 0x35, 0x4f, 0x13, 0x91, 0xbd, 0xea,
-	0x36, 0x5c, 0x02, 0x5e, 0x24, 0xb7, 0x20, 0x30, 0x6e, 0xd8, 0x2b, 0x9c, 0x77, 0x9b, 0xce, 0x64,
-	0xe4, 0xe7, 0x38, 0x27, 0x0f, 0xa0, 0x3e, 0x4e, 0xf2, 0x71, 0xb7, 0x75, 0xb7, 0x76, 0xbf, 0x3d,
-	0xbc, 0x39, 0x88, 0xf1, 0x4c, 0xe7, 0x79, 0xa2, 0x98, 0xa9, 0xef, 0xc0, 0x5c, 0x7e, 0x27, 0xc9,
-	0xc7, 0xd4, 0x82, 0x0e, 0xea, 0x41, 0xd0, 0x09, 0xfb, 0x5b, 0xd0, 0x3c, 0x71, 0x05, 0x20, 0x50,
-	0xe7, 0x71, 0x2c, 0xcb, 0x94, 0xcc, 0x77, 0xff, 0x9f, 0x1a, 0x84, 0x3b, 0x33, 0x91, 0xc4, 0xfb,
-	0xd9, 0x69, 0x6e, 0xee, 0x14, 0x49, 0xe4, 0x3a, 0x97, 0x65, 0x51, 0xbd, 0x48, 0x7a, 0x10, 0x4c,
-	0x73, 0xa5, 0x2d, 0x0d, 0x2e, 0xd5, 0x4a, 0x26, 0x5b, 0x10, 0xc6, 0x42, 0x5a, 0xaa, 0xe6, 0xbe,
-	0xac, 0x0b, 0x05, 0xf9, 0x0c, 0xc2, 0x8a, 0x5b, 0x9b, 0x69, 0x7b, 0xd8, 0x1b, 0x38, 0xf6, 0x07,
-	0x25, 0xfb, 0x83, 0x93, 0x12, 0x41, 0x17, 0x60, 0xf2, 0x0e, 0x40, 0x94, 0xc6, 0x4c, 0xa1, 0x3c,
-	0x43, 0x69, 0x53, 0x0e, 0x69, 0x18, 0xa5, 0xf1, 0xb1, 0x55, 0x98, 0xb0, 0x26, 0xf7, 0x68, 0xca,
-	0x45, 0xd6, 0x0d, 0x9c, 0xb5, 0x52, 0x1c, 0xd4, 0x83, 0x5a, 0x67, 0xe5, 0xa0, 0x1e, 0x34, 0x3b,
-	0x2d, 0xda, 0x34, 0xad, 0x28, 0x34, 0x0d, 0x67, 0x85, 0xd2, 0x12, 0x79, 0xaa, 0xfa, 0xbf, 0x37,
-	0x60, 0x6d, 0x37, 0x8d, 0xf7, 0x50, 0x45, 0x52, 0x14, 0x26, 0xc3, 0x8f, 0x21, 0x50, 0xbe, 0xe1,
-	0x6c, 0x85, 0xda, 0xc3, 0xf5, 0x41, 0xd9, 0xd1, 0x65, 0x27, 0xd2, 0x0a, 0x42, 0x86, 0xd0, 0x50,
-	0xa8, 0x67, 0x85, 0x2d, 0x54, 0x7b, 0xb8, 0x55, 0x61, 0xcf, 0x79, 0x1d, 0x1c, 0x1b, 0x0c, 0x75,
-	0x50, 0x73, 0x26, 0x92, 0xb9, 0x52, 0xb6, 0x82, 0x6f, 0x3e, 0xb3, 0x6b, 0x30, 0xd4, 0x41, 0xc9,
-	0x73, 0xb8, 0x8a, 0xe9, 0x2c, 0xe1, 0x5a, 0xe4, 0x19, 0xcb, 0x0b, 0xad, 0x6c, 0x4b, 0xb4, 0x87,
-	0xef, 0xbf, 0xe1, 0xf0, 0xd3, 0x12, 0x7c, 0x54, 0x68, 0x45, 0xd7, 0xf0, 0x75, 0xb1, 0xf7, 0x67,
-	0x0d, 0x1a, 0xf6, 0x46, 0xe4, 0x23, 0x08, 0x4c, 0x6d, 0x4f, 0x45, 0x82, 0x97, 0xb2, 0x2d, 0x67,
-	0x80, 0xb6, 0xa2, 0x34, 0x36, 0x02, 0xb9, 0x09, 0xe6, 0x93, 0xc5, 0xa2, 0xec, 0x8b, 0x66, 0x94,
-	0xc6, 0x7b, 0x42, 0x92, 0x7b, 0xd0, 0x30, 0x2e, 0x4c, 0x46, 0xab, 0xcb, 0x7d, 0x38, 0x3b, 0xf9,
-	0x12, 0x42, 0x33, 0x42, 0x4c, 0xcf, 0x0b, 0x37, 0x0e, 0x57, 0x87, 0x77, 0xde, 0x90, 0xc1, 0x88,
-	0xeb, 0xe9, 0xc9, 0xbc, 0x40, 0x1a, 0x14, 0xfe, 0xab, 0xf7, 0x08, 0x1a, 0xb6, 0x28, 0xe4, 0x43,
-	0x58, 0x8f, 0x12, 0x9e, 0x4d, 0x58, 0x86, 0x18, 0x33, 0x3f, 0xe6, 0x35, 0x3b, 0x5d, 0xd7, 0xac,
-	0xe1, 0x10, 0x31, 0x76, 0xed, 0xde, 0x1b, 0xc1, 0xda, 0xb9, 0x62, 0x90, 0xc7, 0xb0, 0x25, 0x51,
-	0x15, 0x18, 0x69, 0x16, 0x25, 0x02, 0x33, 0xcd, 0x44, 0x16, 0x25, 0xb3, 0x18, 0x99, 0x09, 0xa4,
-	0xbc, 0x9f, 0x5b, 0x1e, 0xb3, 0x6b, 0x21, 0xfb, 0x0e, 0x61, 0xee, 0xa4, 0xfa, 0x9f, 0x43, 0x50,
-	0x5e, 0x8e, 0x6c, 0xc2, 0xfa, 0x77, 0x87, 0xcf, 0x0f, 0x8f, 0x5e, 0x1e, 0xb2, 0xd1, 0x93, 0x93,
-	0x67, 0xec, 0xe4, 0x87, 0xd1, 0xd3, 0xce, 0xff, 0x48, 0x08, 0x8d, 0xd1, 0xd1, 0xf1, 0xfe, 0xf7,
-	0x9d, 0x1a, 0x69, 0x43, 0xeb, 0xe5, 0xfe, 0xe1, 0xde, 0xd1, 0xcb, 0x63, 0xdb, 0x88, 0xf5, 0x4e,
-	0xe3, 0xa0, 0x1e, 0x34, 0x3a, 0x4d, 0x6a, 0x2b, 0x6f, 0xa8, 0xa4, 0xed, 0x82, 0x47, 0xaf, 0xf8,
-	0x04, 0x8d, 0xd4, 0xff, 0xab, 0x06, 0x84, 0x62, 0x9a, 0x6b, 0x34, 0x9b, 0x63, 0x94, 0x70, 0x7d,
-	0x9a, 0xcb, 0x94, 0xec, 0x01, 0x14, 0x32, 0x2f, 0x50, 0x6a, 0x81, 0xe6, 0x8e, 0xab, 0xe7, 0xc8,
-	0xbf, 0x7c, 0x60, 0x30, 0x72, 0xe8, 0x39, 0x7d, 0xed, 0x1c, 0x19, 0xc2, 0xa6, 0x1c, 0x23, 0x13,
-	0x99, 0xd2, 0x3c, 0x8b, 0x90, 0x8d, 0xb9, 0x42, 0x3b, 0xcc, 0x8e, 0xcf, 0x0d, 0x39, 0xc6, 0x7d,
-	0x6f, 0xdb, 0xf1, 0xa6, 0xde, 0x27, 0x10, 0x94, 0xbe, 0x96, 0xae, 0xe0, 0xeb, 0xd0, 0x38, 0xe3,
-	0xc9, 0xac, 0xf4, 0xe1, 0x84, 0xfe, 0x1f, 0x2b, 0xd0, 0xdc, 0xcd, 0xb3, 0x53, 0x31, 0x21, 0xf7,
-	0xaa, 0x4d, 0xec, 0x5a, 0xec, 0x5a, 0x75, 0x6d, 0x47, 0x51, 0xb5, 0x9a, 0x1f, 0x02, 0x8c, 0xcd,
-	0x12, 0x62, 0x22, 0x3b, 0xcd, 0xfd, 0x74, 0x90, 0x0a, 0x5c, 0xed, 0x27, 0x1a, 0x8e, 0xab, 0x55,
-	0xf5, 0x15, 0x5c, 0xb5, 0x2d, 0x59, 0xf5, 0x8d, 0xed, 0xaa, 0xf6, 0xf0, 0xc6, 0xf2, 0xae, 0xa2,
-	0x6b, 0xd1, 0xb9, 0x69, 0xff, 0x06, 0x36, 0x64, 0x55, 0x3a, 0x56, 0xf8, 0xda, 0xf9, 0xfd, 0xf4,
-	0xff, 0xb7, 0x94, 0x97, 0x12, 0x79, 0x99, 0xa3, 0xdb, 0x00, 0xb1, 0x48, 0x31, 0x33, 0xef, 0x8f,
-	0x19, 0xd0, 0x55, 0xf3, 0xb2, 0x2c, 0x34, 0x07, 0xf5, 0x60, 0xa5, 0xb3, 0x4a, 0x1b, 0x22, 0xe5,
-	0x13, 0xec, 0xff, 0x0a, 0x41, 0x75, 0xf0, 0x8b, 0x25, 0xe4, 0xf6, 0xaa, 0xe8, 0x6f, 0xa5, 0xf4,
-	0x3f, 0xd2, 0xf3, 0xdb, 0x2a, 0xac, 0xd1, 0x59, 0x66, 0xb6, 0xac, 0x67, 0x69, 0xd9, 0xd9, 0x77,
-	0xe1, 0x8a, 0x59, 0xbb, 0x22, 0x42, 0x66, 0x9f, 0x0c, 0xe7, 0xa2, 0xed, 0x75, 0x4f, 0xe2, 0x58,
-	0x92, 0x17, 0x70, 0xb3, 0x2c, 0x1b, 0x93, 0xce, 0x21, 0x8b, 0xac, 0x47, 0xbf, 0xa1, 0x6e, 0x5f,
-	0xca, 0xe3, 0x5c, 0x5c, 0xba, 0x59, 0x2c, 0x53, 0x9b, 0x3d, 0x5c, 0xd1, 0x11, 0x5c, 0xd8, 0x4c,
-	0x95, 0xa3, 0x0a, 0x42, 0x1e, 0xc0, 0x3a, 0x4f, 0x92, 0xfc, 0x27, 0x8c, 0x59, 0x21, 0xd1, 0xf4,
-	0x87, 0x76, 0xdb, 0x28, 0xa4, 0x1d, 0x6f, 0x18, 0x95, 0x7a, 0xf2, 0x10, 0xae, 0xc7, 0x42, 0x5d,
-	0xc6, 0xd7, 0x2d, 0x7e, 0x63, 0x61, 0x5b, 0x1c, 0xd9, 0x81, 0xd7, 0xd4, 0xcc, 0x5f, 0x44, 0x75,
-	0x1b, 0x17, 0xf6, 0x5d, 0xf5, 0x42, 0x90, 0x05, 0x7a, 0xd7, 0x83, 0x0f, 0xea, 0x41, 0xab, 0x13,
-	0xd0, 0xe5, 0x03, 0xd8, 0xff, 0x14, 0x36, 0x97, 0xd6, 0xe7, 0x42, 0x53, 0xd5, 0x2e, 0x36, 0x55,
-	0xff, 0x31, 0x84, 0x0e, 0xf9, 0x2d, 0x37, 0x4f, 0x4b, 0xe0, 0x49, 0x28, 0xdb, 0x68, 0x31, 0x08,
-	0xe7, 0xcb, 0x5e, 0xe1, 0xfa, 0x2f, 0x00, 0xbc, 0x0e, 0x95, 0x7d, 0x6d, 0xfd, 0x3f, 0x28, 0x26,
-	0x62, 0x4f, 0x78, 0xe8, 0x35, 0xfb, 0x31, 0xf9, 0x00, 0x5a, 0x8e, 0xdd, 0xd2, 0xff, 0x62, 0x98,
-	0xbd, 0x93, 0xd2, 0x3e, 0x6e, 0xda, 0x67, 0xfd, 0xd1, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x39,
-	0xb9, 0x5b, 0xc4, 0x19, 0x0a, 0x00, 0x00,
+	0x55, 0x02, 0x01, 0xb9, 0xa2, 0x70, 0x01, 0x14, 0x54, 0x2a, 0xb6, 0x43, 0xc5, 0x0e, 0xd8, 0xaa,
+	0xb1, 0x49, 0xe0, 0x6a, 0x6a, 0xb4, 0xdb, 0x96, 0x86, 0xec, 0x89, 0x99, 0x91, 0x41, 0x14, 0xcf,
+	0x40, 0xf1, 0x04, 0xbc, 0x05, 0x4f, 0xc2, 0x23, 0xf0, 0x0c, 0xdc, 0x53, 0x73, 0xd8, 0x55, 0x6c,
+	0x2b, 0xb9, 0xe0, 0x4a, 0xd3, 0xdd, 0xdf, 0x74, 0x4f, 0x77, 0x7f, 0xdd, 0x2b, 0xd8, 0x8c, 0xf2,
+	0x34, 0xe5, 0x59, 0xbc, 0xed, 0x7f, 0x07, 0x85, 0xcc, 0x75, 0x4e, 0x5a, 0x5e, 0xec, 0x6d, 0xf0,
+	0x42, 0x6c, 0x4f, 0xf2, 0x94, 0xb3, 0x98, 0x6b, 0xee, 0xac, 0xbd, 0x3b, 0x93, 0x3c, 0x9f, 0x24,
+	0xb8, 0x6d, 0xa5, 0xf1, 0xec, 0x74, 0x5b, 0x8b, 0x14, 0x95, 0xe6, 0x69, 0xe1, 0x00, 0xfd, 0x1f,
+	0x21, 0x38, 0xc6, 0x04, 0x23, 0x9d, 0x4b, 0x42, 0xa0, 0x9e, 0xf1, 0x14, 0xbb, 0xb5, 0xbb, 0xb5,
+	0xfb, 0x21, 0xb5, 0x67, 0xd2, 0x85, 0xd6, 0x19, 0x4a, 0x25, 0xf2, 0xac, 0xbb, 0x62, 0xd5, 0xa5,
+	0x48, 0x6e, 0x40, 0x53, 0x73, 0x39, 0x41, 0xdd, 0x5d, 0xb5, 0x06, 0x2f, 0x91, 0x3b, 0xd0, 0x1e,
+	0x8b, 0x8c, 0xcb, 0x39, 0x9b, 0x72, 0x35, 0xed, 0xd6, 0xad, 0x11, 0x9c, 0xea, 0x19, 0x57, 0xd3,
+	0xfe, 0x5f, 0x35, 0x08, 0xbe, 0x12, 0x09, 0x1e, 0x17, 0x18, 0x99, 0x98, 0x05, 0xd7, 0xd3, 0x32,
+	0xa6, 0x39, 0x1b, 0x9d, 0x12, 0xbf, 0xa0, 0x0d, 0xb8, 0x4a, 0xed, 0xd9, 0xe8, 0xac, 0x3b, 0x17,
+	0xcb, 0x9e, 0xc9, 0x7b, 0xb0, 0x26, 0x14, 0xc3, 0x9f, 0x31, 0x9a, 0x69, 0x3e, 0x4e, 0xd0, 0xc6,
+	0x0a, 0xe8, 0x15, 0xa1, 0x9e, 0x56, 0x3a, 0x93, 0x80, 0x9a, 0xa7, 0x89, 0xc8, 0x5e, 0x75, 0x1b,
+	0x2e, 0x01, 0x2f, 0x92, 0x5b, 0x10, 0x18, 0x37, 0xec, 0x15, 0xce, 0xbb, 0x4d, 0x67, 0x32, 0xf2,
+	0x73, 0x9c, 0x93, 0x07, 0x50, 0x1f, 0x27, 0xf9, 0xb8, 0xdb, 0xba, 0x5b, 0xbb, 0xdf, 0x1e, 0xde,
+	0x1c, 0xc4, 0x78, 0xa6, 0xf3, 0x3c, 0x51, 0xcc, 0xd4, 0x77, 0x60, 0x1e, 0xbf, 0x93, 0xe4, 0x63,
+	0x6a, 0x41, 0x07, 0xf5, 0x20, 0xe8, 0x84, 0xfd, 0x2d, 0x68, 0x9e, 0xb8, 0x02, 0x10, 0xa8, 0xf3,
+	0x38, 0x96, 0x65, 0x4a, 0xe6, 0xdc, 0xff, 0xa7, 0x06, 0xe1, 0xce, 0x4c, 0x24, 0xf1, 0x7e, 0x76,
+	0x9a, 0x9b, 0x37, 0x45, 0x12, 0xb9, 0xce, 0x65, 0x59, 0x54, 0x2f, 0x92, 0x1e, 0x04, 0xd3, 0x5c,
+	0x69, 0xdb, 0x06, 0x97, 0x6a, 0x25, 0x93, 0x2d, 0x08, 0x63, 0x21, 0x6d, 0xab, 0xe6, 0xbe, 0xac,
+	0x0b, 0x05, 0xf9, 0x14, 0xc2, 0xaa, 0xb7, 0x36, 0xd3, 0xf6, 0xb0, 0x37, 0x70, 0xdd, 0x1f, 0x94,
+	0xdd, 0x1f, 0x9c, 0x94, 0x08, 0xba, 0x00, 0x93, 0x77, 0x00, 0xa2, 0x34, 0x66, 0x0a, 0xe5, 0x19,
+	0x4a, 0x9b, 0x72, 0x48, 0xc3, 0x28, 0x8d, 0x8f, 0xad, 0xc2, 0x84, 0x35, 0xb9, 0x47, 0x53, 0x2e,
+	0xb2, 0x6e, 0xe0, 0xac, 0x95, 0xe2, 0xa0, 0x1e, 0xd4, 0x3a, 0x2b, 0x07, 0xf5, 0xa0, 0xd9, 0x69,
+	0xd1, 0xa6, 0xa1, 0xa2, 0xd0, 0x34, 0x9c, 0x15, 0x4a, 0x4b, 0xe4, 0xa9, 0xea, 0xff, 0xde, 0x80,
+	0xb5, 0xdd, 0x34, 0xde, 0x43, 0x15, 0x49, 0x51, 0x98, 0x0c, 0x3f, 0x86, 0x40, 0x79, 0xc2, 0xd9,
+	0x0a, 0xb5, 0x87, 0xeb, 0x83, 0x92, 0xd1, 0x25, 0x13, 0x69, 0x05, 0x21, 0x43, 0x68, 0x28, 0xd4,
+	0xb3, 0xc2, 0x16, 0xaa, 0x3d, 0xdc, 0xaa, 0xb0, 0xe7, 0xbc, 0x0e, 0x8e, 0x0d, 0x86, 0x3a, 0xa8,
+	0xb9, 0x13, 0xc9, 0x5c, 0x29, 0x5b, 0xc1, 0x37, 0xdf, 0xd9, 0x35, 0x18, 0xea, 0xa0, 0xe4, 0x39,
+	0x5c, 0xc5, 0x74, 0x96, 0x70, 0x2d, 0xf2, 0x8c, 0xe5, 0x85, 0x56, 0x96, 0x12, 0xed, 0xe1, 0xfb,
+	0x6f, 0xb8, 0xfc, 0xb4, 0x04, 0x1f, 0x15, 0x5a, 0xd1, 0x35, 0x7c, 0x5d, 0xec, 0xfd, 0x59, 0x83,
+	0x86, 0x7d, 0x11, 0xf9, 0x08, 0x02, 0x53, 0xdb, 0x53, 0x91, 0xe0, 0xa5, 0x6c, 0xcb, 0x19, 0xa0,
+	0xad, 0x28, 0x8d, 0x8d, 0x40, 0x6e, 0x82, 0x39, 0xb2, 0x58, 0x94, 0xbc, 0x68, 0x46, 0x69, 0xbc,
+	0x27, 0x24, 0xb9, 0x07, 0x0d, 0xe3, 0xc2, 0x64, 0xb4, 0xba, 0xdc, 0x87, 0xb3, 0x93, 0x2f, 0x20,
+	0x34, 0x23, 0xc4, 0xf4, 0xbc, 0x70, 0xe3, 0x70, 0x75, 0x78, 0xe7, 0x0d, 0x19, 0x8c, 0xb8, 0x9e,
+	0x9e, 0xcc, 0x0b, 0xa4, 0x41, 0xe1, 0x4f, 0xbd, 0x47, 0xd0, 0xb0, 0x45, 0x21, 0x1f, 0xc2, 0x7a,
+	0x94, 0xf0, 0x6c, 0xc2, 0x32, 0xc4, 0x98, 0xf9, 0x31, 0xaf, 0xd9, 0xe9, 0xba, 0x66, 0x0d, 0x87,
+	0x88, 0xb1, 0xa3, 0x7b, 0x6f, 0x04, 0x6b, 0xe7, 0x8a, 0x41, 0x1e, 0xc3, 0x96, 0x44, 0x55, 0x60,
+	0xa4, 0x59, 0x94, 0x08, 0xcc, 0x34, 0x13, 0x59, 0x94, 0xcc, 0x62, 0x64, 0x26, 0x90, 0xf2, 0x7e,
+	0x6e, 0x79, 0xcc, 0xae, 0x85, 0xec, 0x3b, 0x84, 0x79, 0x93, 0xea, 0x7f, 0x06, 0x41, 0xf9, 0x38,
+	0xb2, 0x09, 0xeb, 0xdf, 0x1e, 0x3e, 0x3f, 0x3c, 0x7a, 0x79, 0xc8, 0x46, 0x4f, 0x4e, 0x9e, 0xb1,
+	0x93, 0xef, 0x47, 0x4f, 0x3b, 0xff, 0x23, 0x21, 0x34, 0x46, 0x47, 0xc7, 0xfb, 0xdf, 0x75, 0x6a,
+	0xa4, 0x0d, 0xad, 0x97, 0xfb, 0x87, 0x7b, 0x47, 0x2f, 0x8f, 0x2d, 0x11, 0xeb, 0x9d, 0xc6, 0x41,
+	0x3d, 0x68, 0x74, 0x9a, 0xd4, 0x56, 0xde, 0xb4, 0x92, 0xb6, 0x0b, 0x1e, 0xbd, 0xe2, 0x13, 0x34,
+	0x52, 0xff, 0xef, 0x1a, 0x10, 0x8a, 0x69, 0xae, 0xd1, 0x6c, 0x8e, 0x51, 0xc2, 0xf5, 0x69, 0x2e,
+	0x53, 0xb2, 0x07, 0x50, 0xc8, 0xbc, 0x40, 0xa9, 0x05, 0x9a, 0x37, 0xae, 0x9e, 0x6b, 0xfe, 0xe5,
+	0x0b, 0x83, 0x91, 0x43, 0xcf, 0xe9, 0x6b, 0xf7, 0xc8, 0x10, 0x36, 0xe5, 0x18, 0x99, 0xc8, 0x94,
+	0xe6, 0x59, 0x84, 0x6c, 0xcc, 0x15, 0xda, 0x61, 0x76, 0xfd, 0xdc, 0x90, 0x63, 0xdc, 0xf7, 0xb6,
+	0x1d, 0x6f, 0x32, 0xf3, 0x37, 0xe5, 0x8a, 0x65, 0xea, 0x07, 0x2e, 0x12, 0xcb, 0xd9, 0x80, 0x86,
+	0x53, 0xae, 0x0e, 0xad, 0xa2, 0xf7, 0x09, 0x04, 0x65, 0xa8, 0xa5, 0x1b, 0xfa, 0x3a, 0x34, 0xce,
+	0x78, 0x32, 0x2b, 0x43, 0x38, 0xa1, 0xff, 0xc7, 0x0a, 0x34, 0x77, 0xf3, 0xec, 0x54, 0x4c, 0xc8,
+	0xbd, 0x6a, 0x51, 0x3b, 0x06, 0x5e, 0xab, 0xb2, 0x72, 0x1d, 0xac, 0x36, 0xf7, 0x43, 0x80, 0xb1,
+	0xd9, 0x51, 0x4c, 0x64, 0xa7, 0xb9, 0x1f, 0x1e, 0x52, 0x81, 0xab, 0xf5, 0x45, 0xc3, 0x71, 0xb5,
+	0xc9, 0xbe, 0x84, 0xab, 0x96, 0xb1, 0x15, 0xad, 0x2c, 0xe9, 0xda, 0xc3, 0x1b, 0xcb, 0x49, 0x47,
+	0xd7, 0xa2, 0x73, 0xcb, 0xe0, 0x6b, 0xd8, 0x90, 0x55, 0x65, 0x59, 0xe1, 0x4b, 0xeb, 0xd7, 0xd7,
+	0xff, 0xdf, 0x52, 0x7d, 0x4a, 0xe4, 0xe5, 0x16, 0xde, 0x06, 0x88, 0x45, 0x8a, 0x99, 0xf9, 0x3c,
+	0x99, 0xf9, 0x5d, 0x35, 0x1f, 0x9e, 0x85, 0xe6, 0xa0, 0x1e, 0xac, 0x74, 0x56, 0x69, 0x43, 0xa4,
+	0x7c, 0x82, 0xfd, 0x5f, 0x21, 0xa8, 0x2e, 0x7e, 0xbe, 0xa4, 0xf7, 0xbd, 0x2a, 0xfa, 0x5b, 0x3b,
+	0xfe, 0x1f, 0xdb, 0xf3, 0xdb, 0x2a, 0xac, 0xd1, 0x59, 0x66, 0x96, 0xb0, 0xef, 0xd2, 0xb2, 0xbb,
+	0xef, 0xc2, 0x15, 0xb3, 0x95, 0x45, 0x84, 0xcc, 0x7e, 0x51, 0x9c, 0x8b, 0xb6, 0xd7, 0x3d, 0x89,
+	0x63, 0x49, 0x5e, 0xc0, 0xcd, 0xb2, 0x6c, 0x4c, 0x3a, 0x87, 0x2c, 0xb2, 0x1e, 0xfd, 0x02, 0xbb,
+	0x7d, 0x29, 0x8f, 0x73, 0x71, 0xe9, 0x66, 0xb1, 0x4c, 0x6d, 0xd6, 0x74, 0xd5, 0x8e, 0xe0, 0xc2,
+	0xe2, 0xaa, 0x1c, 0x55, 0x10, 0xf2, 0x00, 0xd6, 0x79, 0x92, 0xe4, 0x3f, 0x61, 0xcc, 0x0a, 0x89,
+	0x86, 0x1f, 0xda, 0x2d, 0xab, 0x90, 0x76, 0xbc, 0x61, 0x54, 0xea, 0xc9, 0x43, 0xb8, 0x1e, 0x0b,
+	0x75, 0x19, 0x5f, 0xb7, 0xf8, 0x8d, 0x85, 0x6d, 0x71, 0x65, 0x07, 0x5e, 0x53, 0x33, 0xff, 0x10,
+	0xd5, 0x6d, 0x5c, 0x58, 0x87, 0xd5, 0x07, 0x84, 0x2c, 0xd0, 0xbb, 0x1e, 0x7c, 0x50, 0x0f, 0x5a,
+	0x9d, 0x80, 0x2e, 0x9f, 0xcf, 0xfe, 0x0b, 0xd8, 0x5c, 0x5a, 0x9f, 0x0b, 0xa4, 0xaa, 0x5d, 0x24,
+	0xd5, 0x85, 0xe9, 0x5d, 0xb9, 0x30, 0xbd, 0xfd, 0xc7, 0x10, 0x3a, 0x47, 0xdf, 0x70, 0xf3, 0x61,
+	0x0a, 0x7c, 0x8f, 0x4a, 0x96, 0x2d, 0xe6, 0xe4, 0x7c, 0x57, 0x2a, 0x5c, 0xff, 0x05, 0x80, 0xd7,
+	0xa1, 0xb2, 0xdf, 0x6a, 0xff, 0xff, 0x8b, 0x89, 0xd8, 0xf3, 0x21, 0xf4, 0x9a, 0xfd, 0x98, 0x7c,
+	0x00, 0x2d, 0xd7, 0xfc, 0xd2, 0xff, 0x62, 0xd6, 0xbd, 0x93, 0xd2, 0x3e, 0x6e, 0xda, 0x3f, 0x05,
+	0x8f, 0xfe, 0x0d, 0x00, 0x00, 0xff, 0xff, 0xf5, 0x5d, 0x17, 0x06, 0x57, 0x0a, 0x00, 0x00,
 }
diff --git a/proto/command/command.proto b/proto/command/command.proto
index eb849e8..bd2e2cd 100644
--- a/proto/command/command.proto
+++ b/proto/command/command.proto
@@ -165,6 +165,9 @@
 
   // Basename of RBE instance to use. e.g. "default_instance" or "windows".
   string rbe_instance_basename = 2;
+
+  // Set true if nsjail is available in the platform image.
+  bool has_nsjail = 3;
 }
 
 // Config is a command config; mapping from selector.
@@ -246,9 +249,12 @@
 }
 
 // PlatformRuntimeConfig is a config to use the runtime.
-// NEXT ID TO USE: 2
+// NEXT ID TO USE: 3
 message PlatformRuntimeConfig {
   repeated string dimensions = 1;
+
+  // Set true if nsjail is available in the platform image.
+  bool has_nsjail = 2;
 }
 
 // ConfigMap is a config map; data source of Config.
diff --git a/proto/doc.go b/proto/doc.go
index a493db5..394fdf7 100644
--- a/proto/doc.go
+++ b/proto/doc.go
@@ -37,3 +37,5 @@
 //go:generate protoc -I. --go_out=plugins=grpc:. backend/backend.proto
 
 //go:generate protoc -I. --go_out=plugins=grpc:. settings/settings.proto settings/settings_service.proto
+
+//go:generate protoc -I. --go_out=plugins=grpc:. nsjail/config.proto
diff --git a/proto/nsjail/README.md b/proto/nsjail/README.md
new file mode 100644
index 0000000..5cf201e
--- /dev/null
+++ b/proto/nsjail/README.md
@@ -0,0 +1,20 @@
+# nsjail config proto
+
+protobuf schema for [nsjail](http://nsjail.com/)
+([GitHub](https://github.com/google/nsjail)).
+This is used for providing hermetic build environment with
+arbitrary toolchain support.
+
+## How to update the file?
+
+1. git clone
+
+```shell
+$ git clone https://github.com/google/nsjail.git
+```
+
+1. copy config.proto file.
+
+```shell
+$ cp nsjail/config.proto .
+```
diff --git a/proto/nsjail/config.pb.go b/proto/nsjail/config.pb.go
new file mode 100644
index 0000000..b7c0187
--- /dev/null
+++ b/proto/nsjail/config.pb.go
@@ -0,0 +1,1405 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: nsjail/config.proto
+
+package nsjail
+
+import (
+	fmt "fmt"
+	proto "github.com/golang/protobuf/proto"
+	math "math"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+
+type Mode int32
+
+const (
+	Mode_LISTEN Mode = 0
+	Mode_ONCE   Mode = 1
+	Mode_RERUN  Mode = 2
+	Mode_EXECVE Mode = 3
+)
+
+var Mode_name = map[int32]string{
+	0: "LISTEN",
+	1: "ONCE",
+	2: "RERUN",
+	3: "EXECVE",
+}
+
+var Mode_value = map[string]int32{
+	"LISTEN": 0,
+	"ONCE":   1,
+	"RERUN":  2,
+	"EXECVE": 3,
+}
+
+func (x Mode) Enum() *Mode {
+	p := new(Mode)
+	*p = x
+	return p
+}
+
+func (x Mode) String() string {
+	return proto.EnumName(Mode_name, int32(x))
+}
+
+func (x *Mode) UnmarshalJSON(data []byte) error {
+	value, err := proto.UnmarshalJSONEnum(Mode_value, data, "Mode")
+	if err != nil {
+		return err
+	}
+	*x = Mode(value)
+	return nil
+}
+
+func (Mode) EnumDescriptor() ([]byte, []int) {
+	return fileDescriptor_82b7e3129c410694, []int{0}
+}
+
+// Should be self explanatory
+type LogLevel int32
+
+const (
+	LogLevel_DEBUG   LogLevel = 0
+	LogLevel_INFO    LogLevel = 1
+	LogLevel_WARNING LogLevel = 2
+	LogLevel_ERROR   LogLevel = 3
+	LogLevel_FATAL   LogLevel = 4
+)
+
+var LogLevel_name = map[int32]string{
+	0: "DEBUG",
+	1: "INFO",
+	2: "WARNING",
+	3: "ERROR",
+	4: "FATAL",
+}
+
+var LogLevel_value = map[string]int32{
+	"DEBUG":   0,
+	"INFO":    1,
+	"WARNING": 2,
+	"ERROR":   3,
+	"FATAL":   4,
+}
+
+func (x LogLevel) Enum() *LogLevel {
+	p := new(LogLevel)
+	*p = x
+	return p
+}
+
+func (x LogLevel) String() string {
+	return proto.EnumName(LogLevel_name, int32(x))
+}
+
+func (x *LogLevel) UnmarshalJSON(data []byte) error {
+	value, err := proto.UnmarshalJSONEnum(LogLevel_value, data, "LogLevel")
+	if err != nil {
+		return err
+	}
+	*x = LogLevel(value)
+	return nil
+}
+
+func (LogLevel) EnumDescriptor() ([]byte, []int) {
+	return fileDescriptor_82b7e3129c410694, []int{1}
+}
+
+type RLimit int32
+
+const (
+	RLimit_VALUE RLimit = 0
+	RLimit_SOFT  RLimit = 1
+	RLimit_HARD  RLimit = 2
+	RLimit_INF   RLimit = 3
+)
+
+var RLimit_name = map[int32]string{
+	0: "VALUE",
+	1: "SOFT",
+	2: "HARD",
+	3: "INF",
+}
+
+var RLimit_value = map[string]int32{
+	"VALUE": 0,
+	"SOFT":  1,
+	"HARD":  2,
+	"INF":   3,
+}
+
+func (x RLimit) Enum() *RLimit {
+	p := new(RLimit)
+	*p = x
+	return p
+}
+
+func (x RLimit) String() string {
+	return proto.EnumName(RLimit_name, int32(x))
+}
+
+func (x *RLimit) UnmarshalJSON(data []byte) error {
+	value, err := proto.UnmarshalJSONEnum(RLimit_value, data, "RLimit")
+	if err != nil {
+		return err
+	}
+	*x = RLimit(value)
+	return nil
+}
+
+func (RLimit) EnumDescriptor() ([]byte, []int) {
+	return fileDescriptor_82b7e3129c410694, []int{2}
+}
+
+type IdMap struct {
+	// Empty string means "current uid/gid"
+	InsideId  *string `protobuf:"bytes,1,opt,name=inside_id,json=insideId,def=" json:"inside_id,omitempty"`
+	OutsideId *string `protobuf:"bytes,2,opt,name=outside_id,json=outsideId,def=" json:"outside_id,omitempty"`
+	// See 'man user_namespaces' for the meaning of count
+	Count *uint32 `protobuf:"varint,3,opt,name=count,def=1" json:"count,omitempty"`
+	// Does this map use /usr/bin/new[u|g]idmap binary?
+	UseNewidmap          *bool    `protobuf:"varint,4,opt,name=use_newidmap,json=useNewidmap,def=0" json:"use_newidmap,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *IdMap) Reset()         { *m = IdMap{} }
+func (m *IdMap) String() string { return proto.CompactTextString(m) }
+func (*IdMap) ProtoMessage()    {}
+func (*IdMap) Descriptor() ([]byte, []int) {
+	return fileDescriptor_82b7e3129c410694, []int{0}
+}
+
+func (m *IdMap) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_IdMap.Unmarshal(m, b)
+}
+func (m *IdMap) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_IdMap.Marshal(b, m, deterministic)
+}
+func (m *IdMap) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_IdMap.Merge(m, src)
+}
+func (m *IdMap) XXX_Size() int {
+	return xxx_messageInfo_IdMap.Size(m)
+}
+func (m *IdMap) XXX_DiscardUnknown() {
+	xxx_messageInfo_IdMap.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_IdMap proto.InternalMessageInfo
+
+const Default_IdMap_Count uint32 = 1
+const Default_IdMap_UseNewidmap bool = false
+
+func (m *IdMap) GetInsideId() string {
+	if m != nil && m.InsideId != nil {
+		return *m.InsideId
+	}
+	return ""
+}
+
+func (m *IdMap) GetOutsideId() string {
+	if m != nil && m.OutsideId != nil {
+		return *m.OutsideId
+	}
+	return ""
+}
+
+func (m *IdMap) GetCount() uint32 {
+	if m != nil && m.Count != nil {
+		return *m.Count
+	}
+	return Default_IdMap_Count
+}
+
+func (m *IdMap) GetUseNewidmap() bool {
+	if m != nil && m.UseNewidmap != nil {
+		return *m.UseNewidmap
+	}
+	return Default_IdMap_UseNewidmap
+}
+
+type MountPt struct {
+	// Can be skipped for filesystems like 'proc'
+	Src *string `protobuf:"bytes,1,opt,name=src,def=" json:"src,omitempty"`
+	// Should 'src' path be prefixed with this envvar?
+	PrefixSrcEnv *string `protobuf:"bytes,2,opt,name=prefix_src_env,json=prefixSrcEnv,def=" json:"prefix_src_env,omitempty"`
+	// If specified, contains buffer that will be written to the dst file
+	SrcContent []byte `protobuf:"bytes,3,opt,name=src_content,json=srcContent,def=" json:"src_content,omitempty"`
+	// Mount point inside jail
+	Dst *string `protobuf:"bytes,4,req,name=dst,def=" json:"dst,omitempty"`
+	// Should 'dst' path be prefixed with this envvar?
+	PrefixDstEnv *string `protobuf:"bytes,5,opt,name=prefix_dst_env,json=prefixDstEnv,def=" json:"prefix_dst_env,omitempty"`
+	// Can be empty for mount --bind mounts
+	Fstype *string `protobuf:"bytes,6,opt,name=fstype,def=" json:"fstype,omitempty"`
+	// E.g. size=5000000 for 'tmpfs'
+	Options *string `protobuf:"bytes,7,opt,name=options,def=" json:"options,omitempty"`
+	// Is it a 'mount --bind src dst' type of mount?
+	IsBind *bool `protobuf:"varint,8,opt,name=is_bind,json=isBind,def=0" json:"is_bind,omitempty"`
+	// Is it a R/W mount?
+	Rw *bool `protobuf:"varint,9,opt,name=rw,def=0" json:"rw,omitempty"`
+	// Is it a directory? If not specified an internal
+	//heuristics will be used to determine that
+	IsDir *bool `protobuf:"varint,10,opt,name=is_dir,json=isDir" json:"is_dir,omitempty"`
+	// Should the sandboxing fail if we cannot mount this resource?
+	Mandatory *bool `protobuf:"varint,11,opt,name=mandatory,def=1" json:"mandatory,omitempty"`
+	// Is it a symlink (instead of real mount point)?
+	IsSymlink *bool `protobuf:"varint,12,opt,name=is_symlink,json=isSymlink,def=0" json:"is_symlink,omitempty"`
+	// Is it a nosuid mount
+	Nosuid *bool `protobuf:"varint,13,opt,name=nosuid,def=0" json:"nosuid,omitempty"`
+	// Is it a nodev mount
+	Nodev *bool `protobuf:"varint,14,opt,name=nodev,def=0" json:"nodev,omitempty"`
+	// Is it a noexec mount
+	Noexec               *bool    `protobuf:"varint,15,opt,name=noexec,def=0" json:"noexec,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *MountPt) Reset()         { *m = MountPt{} }
+func (m *MountPt) String() string { return proto.CompactTextString(m) }
+func (*MountPt) ProtoMessage()    {}
+func (*MountPt) Descriptor() ([]byte, []int) {
+	return fileDescriptor_82b7e3129c410694, []int{1}
+}
+
+func (m *MountPt) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_MountPt.Unmarshal(m, b)
+}
+func (m *MountPt) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_MountPt.Marshal(b, m, deterministic)
+}
+func (m *MountPt) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MountPt.Merge(m, src)
+}
+func (m *MountPt) XXX_Size() int {
+	return xxx_messageInfo_MountPt.Size(m)
+}
+func (m *MountPt) XXX_DiscardUnknown() {
+	xxx_messageInfo_MountPt.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MountPt proto.InternalMessageInfo
+
+const Default_MountPt_IsBind bool = false
+const Default_MountPt_Rw bool = false
+const Default_MountPt_Mandatory bool = true
+const Default_MountPt_IsSymlink bool = false
+const Default_MountPt_Nosuid bool = false
+const Default_MountPt_Nodev bool = false
+const Default_MountPt_Noexec bool = false
+
+func (m *MountPt) GetSrc() string {
+	if m != nil && m.Src != nil {
+		return *m.Src
+	}
+	return ""
+}
+
+func (m *MountPt) GetPrefixSrcEnv() string {
+	if m != nil && m.PrefixSrcEnv != nil {
+		return *m.PrefixSrcEnv
+	}
+	return ""
+}
+
+func (m *MountPt) GetSrcContent() []byte {
+	if m != nil {
+		return m.SrcContent
+	}
+	return nil
+}
+
+func (m *MountPt) GetDst() string {
+	if m != nil && m.Dst != nil {
+		return *m.Dst
+	}
+	return ""
+}
+
+func (m *MountPt) GetPrefixDstEnv() string {
+	if m != nil && m.PrefixDstEnv != nil {
+		return *m.PrefixDstEnv
+	}
+	return ""
+}
+
+func (m *MountPt) GetFstype() string {
+	if m != nil && m.Fstype != nil {
+		return *m.Fstype
+	}
+	return ""
+}
+
+func (m *MountPt) GetOptions() string {
+	if m != nil && m.Options != nil {
+		return *m.Options
+	}
+	return ""
+}
+
+func (m *MountPt) GetIsBind() bool {
+	if m != nil && m.IsBind != nil {
+		return *m.IsBind
+	}
+	return Default_MountPt_IsBind
+}
+
+func (m *MountPt) GetRw() bool {
+	if m != nil && m.Rw != nil {
+		return *m.Rw
+	}
+	return Default_MountPt_Rw
+}
+
+func (m *MountPt) GetIsDir() bool {
+	if m != nil && m.IsDir != nil {
+		return *m.IsDir
+	}
+	return false
+}
+
+func (m *MountPt) GetMandatory() bool {
+	if m != nil && m.Mandatory != nil {
+		return *m.Mandatory
+	}
+	return Default_MountPt_Mandatory
+}
+
+func (m *MountPt) GetIsSymlink() bool {
+	if m != nil && m.IsSymlink != nil {
+		return *m.IsSymlink
+	}
+	return Default_MountPt_IsSymlink
+}
+
+func (m *MountPt) GetNosuid() bool {
+	if m != nil && m.Nosuid != nil {
+		return *m.Nosuid
+	}
+	return Default_MountPt_Nosuid
+}
+
+func (m *MountPt) GetNodev() bool {
+	if m != nil && m.Nodev != nil {
+		return *m.Nodev
+	}
+	return Default_MountPt_Nodev
+}
+
+func (m *MountPt) GetNoexec() bool {
+	if m != nil && m.Noexec != nil {
+		return *m.Noexec
+	}
+	return Default_MountPt_Noexec
+}
+
+type Exe struct {
+	// Will be used both as execv's path and as argv[0]
+	Path *string `protobuf:"bytes,1,req,name=path" json:"path,omitempty"`
+	// This will be argv[1] and so on..
+	Arg []string `protobuf:"bytes,2,rep,name=arg" json:"arg,omitempty"`
+	// Override argv[0]
+	Arg0 *string `protobuf:"bytes,3,opt,name=arg0" json:"arg0,omitempty"`
+	// Should execveat() be used to execute a file-descriptor instead?
+	ExecFd               *bool    `protobuf:"varint,4,opt,name=exec_fd,json=execFd,def=0" json:"exec_fd,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *Exe) Reset()         { *m = Exe{} }
+func (m *Exe) String() string { return proto.CompactTextString(m) }
+func (*Exe) ProtoMessage()    {}
+func (*Exe) Descriptor() ([]byte, []int) {
+	return fileDescriptor_82b7e3129c410694, []int{2}
+}
+
+func (m *Exe) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_Exe.Unmarshal(m, b)
+}
+func (m *Exe) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_Exe.Marshal(b, m, deterministic)
+}
+func (m *Exe) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Exe.Merge(m, src)
+}
+func (m *Exe) XXX_Size() int {
+	return xxx_messageInfo_Exe.Size(m)
+}
+func (m *Exe) XXX_DiscardUnknown() {
+	xxx_messageInfo_Exe.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Exe proto.InternalMessageInfo
+
+const Default_Exe_ExecFd bool = false
+
+func (m *Exe) GetPath() string {
+	if m != nil && m.Path != nil {
+		return *m.Path
+	}
+	return ""
+}
+
+func (m *Exe) GetArg() []string {
+	if m != nil {
+		return m.Arg
+	}
+	return nil
+}
+
+func (m *Exe) GetArg0() string {
+	if m != nil && m.Arg0 != nil {
+		return *m.Arg0
+	}
+	return ""
+}
+
+func (m *Exe) GetExecFd() bool {
+	if m != nil && m.ExecFd != nil {
+		return *m.ExecFd
+	}
+	return Default_Exe_ExecFd
+}
+
+type NsJailConfig struct {
+	// Optional name and description for this config
+	Name        *string  `protobuf:"bytes,1,opt,name=name,def=" json:"name,omitempty"`
+	Description []string `protobuf:"bytes,2,rep,name=description" json:"description,omitempty"`
+	// Execution mode: see 'msg Mode' description for more
+	Mode *Mode `protobuf:"varint,3,opt,name=mode,enum=nsjail.Mode,def=1" json:"mode,omitempty"`
+	// Equivalent to a bind mount with dst='/'. DEPRECATED: Use bind mounts.
+	ChrootDir *string `protobuf:"bytes,4,opt,name=chroot_dir,json=chrootDir" json:"chroot_dir,omitempty"` // Deprecated: Do not use.
+	// Applies both to the chroot_dir and to /proc mounts. DEPRECATED: Use bind mounts
+	IsRootRw *bool `protobuf:"varint,5,opt,name=is_root_rw,json=isRootRw,def=0" json:"is_root_rw,omitempty"` // Deprecated: Do not use.
+	// Hostname inside jail
+	Hostname *string `protobuf:"bytes,8,opt,name=hostname,def=NSJAIL" json:"hostname,omitempty"`
+	// Initial current working directory for the binary
+	Cwd *string `protobuf:"bytes,9,opt,name=cwd,def=/" json:"cwd,omitempty"`
+	// TCP port to listen to. Valid with mode=LISTEN only
+	Port *uint32 `protobuf:"varint,10,opt,name=port,def=0" json:"port,omitempty"`
+	// Host to bind to for mode=LISTEN. Must be in IPv6 format
+	Bindhost *string `protobuf:"bytes,11,opt,name=bindhost,def=::" json:"bindhost,omitempty"`
+	// For mode=LISTEN, maximum number of connections from a single IP
+	MaxConnsPerIp *uint32 `protobuf:"varint,12,opt,name=max_conns_per_ip,json=maxConnsPerIp,def=0" json:"max_conns_per_ip,omitempty"`
+	// Wall-time time limit for commands
+	TimeLimit *uint32 `protobuf:"varint,13,opt,name=time_limit,json=timeLimit,def=600" json:"time_limit,omitempty"`
+	// Should nsjail go into background?
+	Daemon *bool `protobuf:"varint,14,opt,name=daemon,def=0" json:"daemon,omitempty"`
+	// Maximum number of CPUs to use: 0 - no limit
+	MaxCpus *uint32 `protobuf:"varint,15,opt,name=max_cpus,json=maxCpus,def=0" json:"max_cpus,omitempty"`
+	// FD to log to.
+	LogFd *int32 `protobuf:"varint,16,opt,name=log_fd,json=logFd" json:"log_fd,omitempty"`
+	// File to save lofs to
+	LogFile *string `protobuf:"bytes,17,opt,name=log_file,json=logFile" json:"log_file,omitempty"`
+	// Minimum log level displayed.
+	//See 'msg LogLevel' description for more
+	LogLevel *LogLevel `protobuf:"varint,18,opt,name=log_level,json=logLevel,enum=nsjail.LogLevel" json:"log_level,omitempty"`
+	// Should the current environment variables be kept
+	//when executing the binary
+	KeepEnv *bool `protobuf:"varint,19,opt,name=keep_env,json=keepEnv,def=0" json:"keep_env,omitempty"`
+	// EnvVars to be set before executing binaries. If the envvar doesn't contain '='
+	//(e.g. just the 'DISPLAY' string), the current envvar value will be used
+	Envar []string `protobuf:"bytes,20,rep,name=envar" json:"envar,omitempty"`
+	// Should capabilities be preserved or dropped
+	KeepCaps *bool `protobuf:"varint,21,opt,name=keep_caps,json=keepCaps,def=0" json:"keep_caps,omitempty"`
+	// Which capabilities should be preserved if keep_caps == false.
+	//Format: "CAP_SYS_PTRACE"
+	Cap []string `protobuf:"bytes,22,rep,name=cap" json:"cap,omitempty"`
+	// Should nsjail close FD=0,1,2 before executing the process
+	Silent *bool `protobuf:"varint,23,opt,name=silent,def=0" json:"silent,omitempty"`
+	// Should the child process have control over terminal?
+	//Can be useful to allow /bin/sh to provide
+	//job control / signals. Dangerous, can be used to put
+	//characters into the controlling terminal back
+	SkipSetsid *bool `protobuf:"varint,24,opt,name=skip_setsid,json=skipSetsid,def=0" json:"skip_setsid,omitempty"`
+	// Redirect sdterr of the process to /dev/null instead of the socket or original TTY
+	StderrToNull *bool `protobuf:"varint,25,opt,name=stderr_to_null,json=stderrToNull,def=0" json:"stderr_to_null,omitempty"`
+	// Which FDs should be passed to the newly executed process
+	//By default only FD=0,1,2 are passed
+	PassFd []int32 `protobuf:"varint,26,rep,name=pass_fd,json=passFd" json:"pass_fd,omitempty"`
+	// Setting it to true will allow to have set-uid binaries
+	//inside the jail
+	DisableNoNewPrivs *bool `protobuf:"varint,27,opt,name=disable_no_new_privs,json=disableNoNewPrivs,def=0" json:"disable_no_new_privs,omitempty"`
+	// Various rlimits, the rlimit_as/rlimit_core/... are used only if
+	//rlimit_as_type/rlimit_core_type/... are set to RLimit::VALUE
+	RlimitAs         *uint64 `protobuf:"varint,28,opt,name=rlimit_as,json=rlimitAs,def=512" json:"rlimit_as,omitempty"`
+	RlimitAsType     *RLimit `protobuf:"varint,29,opt,name=rlimit_as_type,json=rlimitAsType,enum=nsjail.RLimit,def=0" json:"rlimit_as_type,omitempty"`
+	RlimitCore       *uint64 `protobuf:"varint,30,opt,name=rlimit_core,json=rlimitCore,def=0" json:"rlimit_core,omitempty"`
+	RlimitCoreType   *RLimit `protobuf:"varint,31,opt,name=rlimit_core_type,json=rlimitCoreType,enum=nsjail.RLimit,def=0" json:"rlimit_core_type,omitempty"`
+	RlimitCpu        *uint64 `protobuf:"varint,32,opt,name=rlimit_cpu,json=rlimitCpu,def=600" json:"rlimit_cpu,omitempty"`
+	RlimitCpuType    *RLimit `protobuf:"varint,33,opt,name=rlimit_cpu_type,json=rlimitCpuType,enum=nsjail.RLimit,def=0" json:"rlimit_cpu_type,omitempty"`
+	RlimitFsize      *uint64 `protobuf:"varint,34,opt,name=rlimit_fsize,json=rlimitFsize,def=1" json:"rlimit_fsize,omitempty"`
+	RlimitFsizeType  *RLimit `protobuf:"varint,35,opt,name=rlimit_fsize_type,json=rlimitFsizeType,enum=nsjail.RLimit,def=0" json:"rlimit_fsize_type,omitempty"`
+	RlimitNofile     *uint64 `protobuf:"varint,36,opt,name=rlimit_nofile,json=rlimitNofile,def=32" json:"rlimit_nofile,omitempty"`
+	RlimitNofileType *RLimit `protobuf:"varint,37,opt,name=rlimit_nofile_type,json=rlimitNofileType,enum=nsjail.RLimit,def=0" json:"rlimit_nofile_type,omitempty"`
+	// RLIMIT_NPROC is system-wide - tricky to use; use the soft limit value by
+	// default here
+	RlimitNproc     *uint64 `protobuf:"varint,38,opt,name=rlimit_nproc,json=rlimitNproc,def=1024" json:"rlimit_nproc,omitempty"`
+	RlimitNprocType *RLimit `protobuf:"varint,39,opt,name=rlimit_nproc_type,json=rlimitNprocType,enum=nsjail.RLimit,def=1" json:"rlimit_nproc_type,omitempty"`
+	// In MiB, use the soft limit value by default
+	RlimitStack     *uint64 `protobuf:"varint,40,opt,name=rlimit_stack,json=rlimitStack,def=1048576" json:"rlimit_stack,omitempty"`
+	RlimitStackType *RLimit `protobuf:"varint,41,opt,name=rlimit_stack_type,json=rlimitStackType,enum=nsjail.RLimit,def=1" json:"rlimit_stack_type,omitempty"`
+	// See 'man personality' for more
+	PersonaAddrCompatLayout *bool `protobuf:"varint,42,opt,name=persona_addr_compat_layout,json=personaAddrCompatLayout,def=0" json:"persona_addr_compat_layout,omitempty"`
+	PersonaMmapPageZero     *bool `protobuf:"varint,43,opt,name=persona_mmap_page_zero,json=personaMmapPageZero,def=0" json:"persona_mmap_page_zero,omitempty"`
+	PersonaReadImpliesExec  *bool `protobuf:"varint,44,opt,name=persona_read_implies_exec,json=personaReadImpliesExec,def=0" json:"persona_read_implies_exec,omitempty"`
+	PersonaAddrLimit_3Gb    *bool `protobuf:"varint,45,opt,name=persona_addr_limit_3gb,json=personaAddrLimit3gb,def=0" json:"persona_addr_limit_3gb,omitempty"`
+	PersonaAddrNoRandomize  *bool `protobuf:"varint,46,opt,name=persona_addr_no_randomize,json=personaAddrNoRandomize,def=0" json:"persona_addr_no_randomize,omitempty"`
+	// Which name-spaces should be used?
+	CloneNewnet  *bool `protobuf:"varint,47,opt,name=clone_newnet,json=cloneNewnet,def=1" json:"clone_newnet,omitempty"`
+	CloneNewuser *bool `protobuf:"varint,48,opt,name=clone_newuser,json=cloneNewuser,def=1" json:"clone_newuser,omitempty"`
+	CloneNewns   *bool `protobuf:"varint,49,opt,name=clone_newns,json=cloneNewns,def=1" json:"clone_newns,omitempty"`
+	CloneNewpid  *bool `protobuf:"varint,50,opt,name=clone_newpid,json=cloneNewpid,def=1" json:"clone_newpid,omitempty"`
+	CloneNewipc  *bool `protobuf:"varint,51,opt,name=clone_newipc,json=cloneNewipc,def=1" json:"clone_newipc,omitempty"`
+	CloneNewuts  *bool `protobuf:"varint,52,opt,name=clone_newuts,json=cloneNewuts,def=1" json:"clone_newuts,omitempty"`
+	// Disable for kernel versions < 4.6 as it's not supported there
+	CloneNewcgroup *bool `protobuf:"varint,53,opt,name=clone_newcgroup,json=cloneNewcgroup,def=1" json:"clone_newcgroup,omitempty"`
+	// Mappings for UIDs and GIDs. See the description for 'msg IdMap'
+	//for more
+	Uidmap []*IdMap `protobuf:"bytes,54,rep,name=uidmap" json:"uidmap,omitempty"`
+	Gidmap []*IdMap `protobuf:"bytes,55,rep,name=gidmap" json:"gidmap,omitempty"`
+	// Should /proc be mounted (R/O)? This can also be added in the 'mount'
+	//section below
+	MountProc *bool `protobuf:"varint,56,opt,name=mount_proc,json=mountProc,def=0" json:"mount_proc,omitempty"`
+	// Mount points inside the jail. See the description for 'msg MountPt'
+	//for more
+	Mount []*MountPt `protobuf:"bytes,57,rep,name=mount" json:"mount,omitempty"`
+	// Kafel seccomp-bpf policy file or a string:
+	//Homepage of the project: https://github.com/google/kafel
+	SeccompPolicyFile *string  `protobuf:"bytes,58,opt,name=seccomp_policy_file,json=seccompPolicyFile" json:"seccomp_policy_file,omitempty"`
+	SeccompString     []string `protobuf:"bytes,59,rep,name=seccomp_string,json=seccompString" json:"seccomp_string,omitempty"`
+	// Setting it to true makes audit write seccomp logs to dmesg
+	SeccompLog *bool `protobuf:"varint,60,opt,name=seccomp_log,json=seccompLog,def=0" json:"seccomp_log,omitempty"`
+	// If > 0, maximum cumulative size of RAM used inside any jail
+	CgroupMemMax *uint64 `protobuf:"varint,61,opt,name=cgroup_mem_max,json=cgroupMemMax,def=0" json:"cgroup_mem_max,omitempty"`
+	// Mount point for cgroups-memory in your system
+	CgroupMemMount *string `protobuf:"bytes,62,opt,name=cgroup_mem_mount,json=cgroupMemMount,def=/sys/fs/cgroup/memory" json:"cgroup_mem_mount,omitempty"`
+	// Writeable directory (for the nsjail user) under cgroup_mem_mount
+	CgroupMemParent *string `protobuf:"bytes,63,opt,name=cgroup_mem_parent,json=cgroupMemParent,def=NSJAIL" json:"cgroup_mem_parent,omitempty"`
+	// If > 0, maximum number of PIDs (threads/processes) inside jail
+	CgroupPidsMax *uint64 `protobuf:"varint,64,opt,name=cgroup_pids_max,json=cgroupPidsMax,def=0" json:"cgroup_pids_max,omitempty"`
+	// Mount point for cgroups-pids in your system
+	CgroupPidsMount *string `protobuf:"bytes,65,opt,name=cgroup_pids_mount,json=cgroupPidsMount,def=/sys/fs/cgroup/pids" json:"cgroup_pids_mount,omitempty"`
+	// Writeable directory (for the nsjail user) under cgroup_pids_mount
+	CgroupPidsParent *string `protobuf:"bytes,66,opt,name=cgroup_pids_parent,json=cgroupPidsParent,def=NSJAIL" json:"cgroup_pids_parent,omitempty"`
+	// If > 0, Class identifier of network packets inside jail
+	CgroupNetClsClassid *uint32 `protobuf:"varint,67,opt,name=cgroup_net_cls_classid,json=cgroupNetClsClassid,def=0" json:"cgroup_net_cls_classid,omitempty"`
+	// Mount point for cgroups-net-cls in your system
+	CgroupNetClsMount *string `protobuf:"bytes,68,opt,name=cgroup_net_cls_mount,json=cgroupNetClsMount,def=/sys/fs/cgroup/net_cls" json:"cgroup_net_cls_mount,omitempty"`
+	// Writeable directory (for the nsjail user) under cgroup_net_mount
+	CgroupNetClsParent *string `protobuf:"bytes,69,opt,name=cgroup_net_cls_parent,json=cgroupNetClsParent,def=NSJAIL" json:"cgroup_net_cls_parent,omitempty"`
+	// If > 0 number of milliseconds of CPU that jail processes can use per each second
+	CgroupCpuMsPerSec *uint32 `protobuf:"varint,70,opt,name=cgroup_cpu_ms_per_sec,json=cgroupCpuMsPerSec,def=0" json:"cgroup_cpu_ms_per_sec,omitempty"`
+	// Mount point for cgroups-cpu in your system
+	CgroupCpuMount *string `protobuf:"bytes,71,opt,name=cgroup_cpu_mount,json=cgroupCpuMount,def=/sys/fs/cgroup/cpu" json:"cgroup_cpu_mount,omitempty"`
+	// Writeable directory (for the nsjail user) under cgroup_cpu_mount
+	CgroupCpuParent *string `protobuf:"bytes,72,opt,name=cgroup_cpu_parent,json=cgroupCpuParent,def=NSJAIL" json:"cgroup_cpu_parent,omitempty"`
+	// Should the 'lo' interface be brought up (active) inside this jail?
+	IfaceNoLo *bool `protobuf:"varint,73,opt,name=iface_no_lo,json=ifaceNoLo,def=0" json:"iface_no_lo,omitempty"`
+	// Put this interface inside the jail
+	IfaceOwn []string `protobuf:"bytes,74,rep,name=iface_own,json=ifaceOwn" json:"iface_own,omitempty"`
+	// Parameters for the cloned MACVLAN interface inside jail
+	MacvlanIface *string `protobuf:"bytes,75,opt,name=macvlan_iface,json=macvlanIface" json:"macvlan_iface,omitempty"`
+	MacvlanVsIp  *string `protobuf:"bytes,76,opt,name=macvlan_vs_ip,json=macvlanVsIp,def=192.168.0.2" json:"macvlan_vs_ip,omitempty"`
+	MacvlanVsNm  *string `protobuf:"bytes,77,opt,name=macvlan_vs_nm,json=macvlanVsNm,def=255.255.255.0" json:"macvlan_vs_nm,omitempty"`
+	MacvlanVsGw  *string `protobuf:"bytes,78,opt,name=macvlan_vs_gw,json=macvlanVsGw,def=192.168.0.1" json:"macvlan_vs_gw,omitempty"`
+	MacvlanVsMa  *string `protobuf:"bytes,79,opt,name=macvlan_vs_ma,json=macvlanVsMa,def=" json:"macvlan_vs_ma,omitempty"`
+	// Binary path (with arguments) to be executed. If not specified here, it
+	//can be specified with cmd-line as "-- /path/to/command arg1 arg2"
+	ExecBin              *Exe     `protobuf:"bytes,80,opt,name=exec_bin,json=execBin" json:"exec_bin,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *NsJailConfig) Reset()         { *m = NsJailConfig{} }
+func (m *NsJailConfig) String() string { return proto.CompactTextString(m) }
+func (*NsJailConfig) ProtoMessage()    {}
+func (*NsJailConfig) Descriptor() ([]byte, []int) {
+	return fileDescriptor_82b7e3129c410694, []int{3}
+}
+
+func (m *NsJailConfig) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_NsJailConfig.Unmarshal(m, b)
+}
+func (m *NsJailConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_NsJailConfig.Marshal(b, m, deterministic)
+}
+func (m *NsJailConfig) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_NsJailConfig.Merge(m, src)
+}
+func (m *NsJailConfig) XXX_Size() int {
+	return xxx_messageInfo_NsJailConfig.Size(m)
+}
+func (m *NsJailConfig) XXX_DiscardUnknown() {
+	xxx_messageInfo_NsJailConfig.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_NsJailConfig proto.InternalMessageInfo
+
+const Default_NsJailConfig_Mode Mode = Mode_ONCE
+const Default_NsJailConfig_IsRootRw bool = false
+const Default_NsJailConfig_Hostname string = "NSJAIL"
+const Default_NsJailConfig_Cwd string = "/"
+const Default_NsJailConfig_Port uint32 = 0
+const Default_NsJailConfig_Bindhost string = "::"
+const Default_NsJailConfig_MaxConnsPerIp uint32 = 0
+const Default_NsJailConfig_TimeLimit uint32 = 600
+const Default_NsJailConfig_Daemon bool = false
+const Default_NsJailConfig_MaxCpus uint32 = 0
+const Default_NsJailConfig_KeepEnv bool = false
+const Default_NsJailConfig_KeepCaps bool = false
+const Default_NsJailConfig_Silent bool = false
+const Default_NsJailConfig_SkipSetsid bool = false
+const Default_NsJailConfig_StderrToNull bool = false
+const Default_NsJailConfig_DisableNoNewPrivs bool = false
+const Default_NsJailConfig_RlimitAs uint64 = 512
+const Default_NsJailConfig_RlimitAsType RLimit = RLimit_VALUE
+const Default_NsJailConfig_RlimitCore uint64 = 0
+const Default_NsJailConfig_RlimitCoreType RLimit = RLimit_VALUE
+const Default_NsJailConfig_RlimitCpu uint64 = 600
+const Default_NsJailConfig_RlimitCpuType RLimit = RLimit_VALUE
+const Default_NsJailConfig_RlimitFsize uint64 = 1
+const Default_NsJailConfig_RlimitFsizeType RLimit = RLimit_VALUE
+const Default_NsJailConfig_RlimitNofile uint64 = 32
+const Default_NsJailConfig_RlimitNofileType RLimit = RLimit_VALUE
+const Default_NsJailConfig_RlimitNproc uint64 = 1024
+const Default_NsJailConfig_RlimitNprocType RLimit = RLimit_SOFT
+const Default_NsJailConfig_RlimitStack uint64 = 1048576
+const Default_NsJailConfig_RlimitStackType RLimit = RLimit_SOFT
+const Default_NsJailConfig_PersonaAddrCompatLayout bool = false
+const Default_NsJailConfig_PersonaMmapPageZero bool = false
+const Default_NsJailConfig_PersonaReadImpliesExec bool = false
+const Default_NsJailConfig_PersonaAddrLimit_3Gb bool = false
+const Default_NsJailConfig_PersonaAddrNoRandomize bool = false
+const Default_NsJailConfig_CloneNewnet bool = true
+const Default_NsJailConfig_CloneNewuser bool = true
+const Default_NsJailConfig_CloneNewns bool = true
+const Default_NsJailConfig_CloneNewpid bool = true
+const Default_NsJailConfig_CloneNewipc bool = true
+const Default_NsJailConfig_CloneNewuts bool = true
+const Default_NsJailConfig_CloneNewcgroup bool = true
+const Default_NsJailConfig_MountProc bool = false
+const Default_NsJailConfig_SeccompLog bool = false
+const Default_NsJailConfig_CgroupMemMax uint64 = 0
+const Default_NsJailConfig_CgroupMemMount string = "/sys/fs/cgroup/memory"
+const Default_NsJailConfig_CgroupMemParent string = "NSJAIL"
+const Default_NsJailConfig_CgroupPidsMax uint64 = 0
+const Default_NsJailConfig_CgroupPidsMount string = "/sys/fs/cgroup/pids"
+const Default_NsJailConfig_CgroupPidsParent string = "NSJAIL"
+const Default_NsJailConfig_CgroupNetClsClassid uint32 = 0
+const Default_NsJailConfig_CgroupNetClsMount string = "/sys/fs/cgroup/net_cls"
+const Default_NsJailConfig_CgroupNetClsParent string = "NSJAIL"
+const Default_NsJailConfig_CgroupCpuMsPerSec uint32 = 0
+const Default_NsJailConfig_CgroupCpuMount string = "/sys/fs/cgroup/cpu"
+const Default_NsJailConfig_CgroupCpuParent string = "NSJAIL"
+const Default_NsJailConfig_IfaceNoLo bool = false
+const Default_NsJailConfig_MacvlanVsIp string = "192.168.0.2"
+const Default_NsJailConfig_MacvlanVsNm string = "255.255.255.0"
+const Default_NsJailConfig_MacvlanVsGw string = "192.168.0.1"
+
+func (m *NsJailConfig) GetName() string {
+	if m != nil && m.Name != nil {
+		return *m.Name
+	}
+	return ""
+}
+
+func (m *NsJailConfig) GetDescription() []string {
+	if m != nil {
+		return m.Description
+	}
+	return nil
+}
+
+func (m *NsJailConfig) GetMode() Mode {
+	if m != nil && m.Mode != nil {
+		return *m.Mode
+	}
+	return Default_NsJailConfig_Mode
+}
+
+// Deprecated: Do not use.
+func (m *NsJailConfig) GetChrootDir() string {
+	if m != nil && m.ChrootDir != nil {
+		return *m.ChrootDir
+	}
+	return ""
+}
+
+// Deprecated: Do not use.
+func (m *NsJailConfig) GetIsRootRw() bool {
+	if m != nil && m.IsRootRw != nil {
+		return *m.IsRootRw
+	}
+	return Default_NsJailConfig_IsRootRw
+}
+
+func (m *NsJailConfig) GetHostname() string {
+	if m != nil && m.Hostname != nil {
+		return *m.Hostname
+	}
+	return Default_NsJailConfig_Hostname
+}
+
+func (m *NsJailConfig) GetCwd() string {
+	if m != nil && m.Cwd != nil {
+		return *m.Cwd
+	}
+	return Default_NsJailConfig_Cwd
+}
+
+func (m *NsJailConfig) GetPort() uint32 {
+	if m != nil && m.Port != nil {
+		return *m.Port
+	}
+	return Default_NsJailConfig_Port
+}
+
+func (m *NsJailConfig) GetBindhost() string {
+	if m != nil && m.Bindhost != nil {
+		return *m.Bindhost
+	}
+	return Default_NsJailConfig_Bindhost
+}
+
+func (m *NsJailConfig) GetMaxConnsPerIp() uint32 {
+	if m != nil && m.MaxConnsPerIp != nil {
+		return *m.MaxConnsPerIp
+	}
+	return Default_NsJailConfig_MaxConnsPerIp
+}
+
+func (m *NsJailConfig) GetTimeLimit() uint32 {
+	if m != nil && m.TimeLimit != nil {
+		return *m.TimeLimit
+	}
+	return Default_NsJailConfig_TimeLimit
+}
+
+func (m *NsJailConfig) GetDaemon() bool {
+	if m != nil && m.Daemon != nil {
+		return *m.Daemon
+	}
+	return Default_NsJailConfig_Daemon
+}
+
+func (m *NsJailConfig) GetMaxCpus() uint32 {
+	if m != nil && m.MaxCpus != nil {
+		return *m.MaxCpus
+	}
+	return Default_NsJailConfig_MaxCpus
+}
+
+func (m *NsJailConfig) GetLogFd() int32 {
+	if m != nil && m.LogFd != nil {
+		return *m.LogFd
+	}
+	return 0
+}
+
+func (m *NsJailConfig) GetLogFile() string {
+	if m != nil && m.LogFile != nil {
+		return *m.LogFile
+	}
+	return ""
+}
+
+func (m *NsJailConfig) GetLogLevel() LogLevel {
+	if m != nil && m.LogLevel != nil {
+		return *m.LogLevel
+	}
+	return LogLevel_DEBUG
+}
+
+func (m *NsJailConfig) GetKeepEnv() bool {
+	if m != nil && m.KeepEnv != nil {
+		return *m.KeepEnv
+	}
+	return Default_NsJailConfig_KeepEnv
+}
+
+func (m *NsJailConfig) GetEnvar() []string {
+	if m != nil {
+		return m.Envar
+	}
+	return nil
+}
+
+func (m *NsJailConfig) GetKeepCaps() bool {
+	if m != nil && m.KeepCaps != nil {
+		return *m.KeepCaps
+	}
+	return Default_NsJailConfig_KeepCaps
+}
+
+func (m *NsJailConfig) GetCap() []string {
+	if m != nil {
+		return m.Cap
+	}
+	return nil
+}
+
+func (m *NsJailConfig) GetSilent() bool {
+	if m != nil && m.Silent != nil {
+		return *m.Silent
+	}
+	return Default_NsJailConfig_Silent
+}
+
+func (m *NsJailConfig) GetSkipSetsid() bool {
+	if m != nil && m.SkipSetsid != nil {
+		return *m.SkipSetsid
+	}
+	return Default_NsJailConfig_SkipSetsid
+}
+
+func (m *NsJailConfig) GetStderrToNull() bool {
+	if m != nil && m.StderrToNull != nil {
+		return *m.StderrToNull
+	}
+	return Default_NsJailConfig_StderrToNull
+}
+
+func (m *NsJailConfig) GetPassFd() []int32 {
+	if m != nil {
+		return m.PassFd
+	}
+	return nil
+}
+
+func (m *NsJailConfig) GetDisableNoNewPrivs() bool {
+	if m != nil && m.DisableNoNewPrivs != nil {
+		return *m.DisableNoNewPrivs
+	}
+	return Default_NsJailConfig_DisableNoNewPrivs
+}
+
+func (m *NsJailConfig) GetRlimitAs() uint64 {
+	if m != nil && m.RlimitAs != nil {
+		return *m.RlimitAs
+	}
+	return Default_NsJailConfig_RlimitAs
+}
+
+func (m *NsJailConfig) GetRlimitAsType() RLimit {
+	if m != nil && m.RlimitAsType != nil {
+		return *m.RlimitAsType
+	}
+	return Default_NsJailConfig_RlimitAsType
+}
+
+func (m *NsJailConfig) GetRlimitCore() uint64 {
+	if m != nil && m.RlimitCore != nil {
+		return *m.RlimitCore
+	}
+	return Default_NsJailConfig_RlimitCore
+}
+
+func (m *NsJailConfig) GetRlimitCoreType() RLimit {
+	if m != nil && m.RlimitCoreType != nil {
+		return *m.RlimitCoreType
+	}
+	return Default_NsJailConfig_RlimitCoreType
+}
+
+func (m *NsJailConfig) GetRlimitCpu() uint64 {
+	if m != nil && m.RlimitCpu != nil {
+		return *m.RlimitCpu
+	}
+	return Default_NsJailConfig_RlimitCpu
+}
+
+func (m *NsJailConfig) GetRlimitCpuType() RLimit {
+	if m != nil && m.RlimitCpuType != nil {
+		return *m.RlimitCpuType
+	}
+	return Default_NsJailConfig_RlimitCpuType
+}
+
+func (m *NsJailConfig) GetRlimitFsize() uint64 {
+	if m != nil && m.RlimitFsize != nil {
+		return *m.RlimitFsize
+	}
+	return Default_NsJailConfig_RlimitFsize
+}
+
+func (m *NsJailConfig) GetRlimitFsizeType() RLimit {
+	if m != nil && m.RlimitFsizeType != nil {
+		return *m.RlimitFsizeType
+	}
+	return Default_NsJailConfig_RlimitFsizeType
+}
+
+func (m *NsJailConfig) GetRlimitNofile() uint64 {
+	if m != nil && m.RlimitNofile != nil {
+		return *m.RlimitNofile
+	}
+	return Default_NsJailConfig_RlimitNofile
+}
+
+func (m *NsJailConfig) GetRlimitNofileType() RLimit {
+	if m != nil && m.RlimitNofileType != nil {
+		return *m.RlimitNofileType
+	}
+	return Default_NsJailConfig_RlimitNofileType
+}
+
+func (m *NsJailConfig) GetRlimitNproc() uint64 {
+	if m != nil && m.RlimitNproc != nil {
+		return *m.RlimitNproc
+	}
+	return Default_NsJailConfig_RlimitNproc
+}
+
+func (m *NsJailConfig) GetRlimitNprocType() RLimit {
+	if m != nil && m.RlimitNprocType != nil {
+		return *m.RlimitNprocType
+	}
+	return Default_NsJailConfig_RlimitNprocType
+}
+
+func (m *NsJailConfig) GetRlimitStack() uint64 {
+	if m != nil && m.RlimitStack != nil {
+		return *m.RlimitStack
+	}
+	return Default_NsJailConfig_RlimitStack
+}
+
+func (m *NsJailConfig) GetRlimitStackType() RLimit {
+	if m != nil && m.RlimitStackType != nil {
+		return *m.RlimitStackType
+	}
+	return Default_NsJailConfig_RlimitStackType
+}
+
+func (m *NsJailConfig) GetPersonaAddrCompatLayout() bool {
+	if m != nil && m.PersonaAddrCompatLayout != nil {
+		return *m.PersonaAddrCompatLayout
+	}
+	return Default_NsJailConfig_PersonaAddrCompatLayout
+}
+
+func (m *NsJailConfig) GetPersonaMmapPageZero() bool {
+	if m != nil && m.PersonaMmapPageZero != nil {
+		return *m.PersonaMmapPageZero
+	}
+	return Default_NsJailConfig_PersonaMmapPageZero
+}
+
+func (m *NsJailConfig) GetPersonaReadImpliesExec() bool {
+	if m != nil && m.PersonaReadImpliesExec != nil {
+		return *m.PersonaReadImpliesExec
+	}
+	return Default_NsJailConfig_PersonaReadImpliesExec
+}
+
+func (m *NsJailConfig) GetPersonaAddrLimit_3Gb() bool {
+	if m != nil && m.PersonaAddrLimit_3Gb != nil {
+		return *m.PersonaAddrLimit_3Gb
+	}
+	return Default_NsJailConfig_PersonaAddrLimit_3Gb
+}
+
+func (m *NsJailConfig) GetPersonaAddrNoRandomize() bool {
+	if m != nil && m.PersonaAddrNoRandomize != nil {
+		return *m.PersonaAddrNoRandomize
+	}
+	return Default_NsJailConfig_PersonaAddrNoRandomize
+}
+
+func (m *NsJailConfig) GetCloneNewnet() bool {
+	if m != nil && m.CloneNewnet != nil {
+		return *m.CloneNewnet
+	}
+	return Default_NsJailConfig_CloneNewnet
+}
+
+func (m *NsJailConfig) GetCloneNewuser() bool {
+	if m != nil && m.CloneNewuser != nil {
+		return *m.CloneNewuser
+	}
+	return Default_NsJailConfig_CloneNewuser
+}
+
+func (m *NsJailConfig) GetCloneNewns() bool {
+	if m != nil && m.CloneNewns != nil {
+		return *m.CloneNewns
+	}
+	return Default_NsJailConfig_CloneNewns
+}
+
+func (m *NsJailConfig) GetCloneNewpid() bool {
+	if m != nil && m.CloneNewpid != nil {
+		return *m.CloneNewpid
+	}
+	return Default_NsJailConfig_CloneNewpid
+}
+
+func (m *NsJailConfig) GetCloneNewipc() bool {
+	if m != nil && m.CloneNewipc != nil {
+		return *m.CloneNewipc
+	}
+	return Default_NsJailConfig_CloneNewipc
+}
+
+func (m *NsJailConfig) GetCloneNewuts() bool {
+	if m != nil && m.CloneNewuts != nil {
+		return *m.CloneNewuts
+	}
+	return Default_NsJailConfig_CloneNewuts
+}
+
+func (m *NsJailConfig) GetCloneNewcgroup() bool {
+	if m != nil && m.CloneNewcgroup != nil {
+		return *m.CloneNewcgroup
+	}
+	return Default_NsJailConfig_CloneNewcgroup
+}
+
+func (m *NsJailConfig) GetUidmap() []*IdMap {
+	if m != nil {
+		return m.Uidmap
+	}
+	return nil
+}
+
+func (m *NsJailConfig) GetGidmap() []*IdMap {
+	if m != nil {
+		return m.Gidmap
+	}
+	return nil
+}
+
+func (m *NsJailConfig) GetMountProc() bool {
+	if m != nil && m.MountProc != nil {
+		return *m.MountProc
+	}
+	return Default_NsJailConfig_MountProc
+}
+
+func (m *NsJailConfig) GetMount() []*MountPt {
+	if m != nil {
+		return m.Mount
+	}
+	return nil
+}
+
+func (m *NsJailConfig) GetSeccompPolicyFile() string {
+	if m != nil && m.SeccompPolicyFile != nil {
+		return *m.SeccompPolicyFile
+	}
+	return ""
+}
+
+func (m *NsJailConfig) GetSeccompString() []string {
+	if m != nil {
+		return m.SeccompString
+	}
+	return nil
+}
+
+func (m *NsJailConfig) GetSeccompLog() bool {
+	if m != nil && m.SeccompLog != nil {
+		return *m.SeccompLog
+	}
+	return Default_NsJailConfig_SeccompLog
+}
+
+func (m *NsJailConfig) GetCgroupMemMax() uint64 {
+	if m != nil && m.CgroupMemMax != nil {
+		return *m.CgroupMemMax
+	}
+	return Default_NsJailConfig_CgroupMemMax
+}
+
+func (m *NsJailConfig) GetCgroupMemMount() string {
+	if m != nil && m.CgroupMemMount != nil {
+		return *m.CgroupMemMount
+	}
+	return Default_NsJailConfig_CgroupMemMount
+}
+
+func (m *NsJailConfig) GetCgroupMemParent() string {
+	if m != nil && m.CgroupMemParent != nil {
+		return *m.CgroupMemParent
+	}
+	return Default_NsJailConfig_CgroupMemParent
+}
+
+func (m *NsJailConfig) GetCgroupPidsMax() uint64 {
+	if m != nil && m.CgroupPidsMax != nil {
+		return *m.CgroupPidsMax
+	}
+	return Default_NsJailConfig_CgroupPidsMax
+}
+
+func (m *NsJailConfig) GetCgroupPidsMount() string {
+	if m != nil && m.CgroupPidsMount != nil {
+		return *m.CgroupPidsMount
+	}
+	return Default_NsJailConfig_CgroupPidsMount
+}
+
+func (m *NsJailConfig) GetCgroupPidsParent() string {
+	if m != nil && m.CgroupPidsParent != nil {
+		return *m.CgroupPidsParent
+	}
+	return Default_NsJailConfig_CgroupPidsParent
+}
+
+func (m *NsJailConfig) GetCgroupNetClsClassid() uint32 {
+	if m != nil && m.CgroupNetClsClassid != nil {
+		return *m.CgroupNetClsClassid
+	}
+	return Default_NsJailConfig_CgroupNetClsClassid
+}
+
+func (m *NsJailConfig) GetCgroupNetClsMount() string {
+	if m != nil && m.CgroupNetClsMount != nil {
+		return *m.CgroupNetClsMount
+	}
+	return Default_NsJailConfig_CgroupNetClsMount
+}
+
+func (m *NsJailConfig) GetCgroupNetClsParent() string {
+	if m != nil && m.CgroupNetClsParent != nil {
+		return *m.CgroupNetClsParent
+	}
+	return Default_NsJailConfig_CgroupNetClsParent
+}
+
+func (m *NsJailConfig) GetCgroupCpuMsPerSec() uint32 {
+	if m != nil && m.CgroupCpuMsPerSec != nil {
+		return *m.CgroupCpuMsPerSec
+	}
+	return Default_NsJailConfig_CgroupCpuMsPerSec
+}
+
+func (m *NsJailConfig) GetCgroupCpuMount() string {
+	if m != nil && m.CgroupCpuMount != nil {
+		return *m.CgroupCpuMount
+	}
+	return Default_NsJailConfig_CgroupCpuMount
+}
+
+func (m *NsJailConfig) GetCgroupCpuParent() string {
+	if m != nil && m.CgroupCpuParent != nil {
+		return *m.CgroupCpuParent
+	}
+	return Default_NsJailConfig_CgroupCpuParent
+}
+
+func (m *NsJailConfig) GetIfaceNoLo() bool {
+	if m != nil && m.IfaceNoLo != nil {
+		return *m.IfaceNoLo
+	}
+	return Default_NsJailConfig_IfaceNoLo
+}
+
+func (m *NsJailConfig) GetIfaceOwn() []string {
+	if m != nil {
+		return m.IfaceOwn
+	}
+	return nil
+}
+
+func (m *NsJailConfig) GetMacvlanIface() string {
+	if m != nil && m.MacvlanIface != nil {
+		return *m.MacvlanIface
+	}
+	return ""
+}
+
+func (m *NsJailConfig) GetMacvlanVsIp() string {
+	if m != nil && m.MacvlanVsIp != nil {
+		return *m.MacvlanVsIp
+	}
+	return Default_NsJailConfig_MacvlanVsIp
+}
+
+func (m *NsJailConfig) GetMacvlanVsNm() string {
+	if m != nil && m.MacvlanVsNm != nil {
+		return *m.MacvlanVsNm
+	}
+	return Default_NsJailConfig_MacvlanVsNm
+}
+
+func (m *NsJailConfig) GetMacvlanVsGw() string {
+	if m != nil && m.MacvlanVsGw != nil {
+		return *m.MacvlanVsGw
+	}
+	return Default_NsJailConfig_MacvlanVsGw
+}
+
+func (m *NsJailConfig) GetMacvlanVsMa() string {
+	if m != nil && m.MacvlanVsMa != nil {
+		return *m.MacvlanVsMa
+	}
+	return ""
+}
+
+func (m *NsJailConfig) GetExecBin() *Exe {
+	if m != nil {
+		return m.ExecBin
+	}
+	return nil
+}
+
+func init() {
+	proto.RegisterEnum("nsjail.Mode", Mode_name, Mode_value)
+	proto.RegisterEnum("nsjail.LogLevel", LogLevel_name, LogLevel_value)
+	proto.RegisterEnum("nsjail.RLimit", RLimit_name, RLimit_value)
+	proto.RegisterType((*IdMap)(nil), "nsjail.IdMap")
+	proto.RegisterType((*MountPt)(nil), "nsjail.MountPt")
+	proto.RegisterType((*Exe)(nil), "nsjail.Exe")
+	proto.RegisterType((*NsJailConfig)(nil), "nsjail.NsJailConfig")
+}
+
+func init() { proto.RegisterFile("nsjail/config.proto", fileDescriptor_82b7e3129c410694) }
+
+var fileDescriptor_82b7e3129c410694 = []byte{
+	// 2041 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x57, 0x6d, 0x73, 0xd3, 0x48,
+	0x12, 0x5e, 0xc7, 0xef, 0xed, 0x97, 0x38, 0x13, 0x02, 0x03, 0x2c, 0xac, 0xc9, 0x12, 0x30, 0xd9,
+	0x23, 0x71, 0x1c, 0xc8, 0x82, 0x77, 0xf7, 0x96, 0xc4, 0x38, 0xac, 0x39, 0xc7, 0x49, 0x29, 0xc0,
+	0x5d, 0xdd, 0x17, 0x95, 0x90, 0x26, 0x66, 0x0e, 0x49, 0xa3, 0xd2, 0x48, 0x71, 0xb2, 0x3f, 0xe1,
+	0x3e, 0xdd, 0x1f, 0xbb, 0xff, 0x74, 0x35, 0x3d, 0x92, 0x23, 0xa7, 0x52, 0xdc, 0x07, 0x57, 0x79,
+	0xba, 0x9f, 0xa7, 0x9f, 0x9e, 0x99, 0x9e, 0x99, 0x16, 0xac, 0xfa, 0xf2, 0x5f, 0x16, 0x77, 0xb7,
+	0x6d, 0xe1, 0x9f, 0xf1, 0xe9, 0x56, 0x10, 0x8a, 0x48, 0x90, 0x92, 0x36, 0xae, 0xff, 0x3b, 0x07,
+	0xc5, 0x91, 0x73, 0x64, 0x05, 0xe4, 0x01, 0x54, 0xb9, 0x2f, 0xb9, 0xc3, 0x4c, 0xee, 0xd0, 0x5c,
+	0x3b, 0xd7, 0xa9, 0xf6, 0xbf, 0x33, 0x2a, 0xda, 0x34, 0x72, 0xc8, 0x0f, 0x00, 0x22, 0x8e, 0x52,
+	0xff, 0x52, 0xe2, 0xaf, 0x26, 0xb6, 0x91, 0x43, 0xee, 0x40, 0xd1, 0x16, 0xb1, 0x1f, 0xd1, 0x7c,
+	0x3b, 0xd7, 0x69, 0xf4, 0x73, 0x3b, 0x86, 0x1e, 0x93, 0x0e, 0xd4, 0x63, 0xc9, 0x4c, 0x9f, 0xcd,
+	0xb8, 0xe3, 0x59, 0x01, 0x2d, 0xb4, 0x73, 0x9d, 0x4a, 0xbf, 0x78, 0x66, 0xb9, 0x92, 0x19, 0xb5,
+	0x58, 0xb2, 0x49, 0xe2, 0x59, 0xff, 0x6f, 0x1e, 0xca, 0x47, 0x8a, 0x73, 0x12, 0x11, 0x02, 0x79,
+	0x19, 0xda, 0xf3, 0x44, 0xd4, 0x80, 0x3c, 0x81, 0x66, 0x10, 0xb2, 0x33, 0x7e, 0x61, 0xca, 0xd0,
+	0x36, 0x99, 0x7f, 0x3e, 0xcf, 0xa3, 0xae, 0xed, 0xa7, 0xa1, 0x3d, 0xf4, 0xcf, 0xc9, 0x23, 0xa8,
+	0x29, 0x80, 0x2d, 0xfc, 0x88, 0x25, 0x09, 0xd5, 0xfb, 0xdf, 0x19, 0x20, 0x43, 0x7b, 0xa0, 0x6d,
+	0x2a, 0xbc, 0x23, 0x23, 0x5a, 0x68, 0x2f, 0xe9, 0xf0, 0x8e, 0x8c, 0x32, 0xe1, 0x1d, 0x19, 0x61,
+	0xf8, 0xe2, 0x62, 0xf8, 0xb7, 0x32, 0x52, 0xe1, 0x29, 0x94, 0xce, 0x64, 0x74, 0x19, 0x30, 0x5a,
+	0x4a, 0xfc, 0xc9, 0x98, 0xdc, 0x83, 0xb2, 0x08, 0x22, 0x2e, 0x7c, 0x49, 0xcb, 0x89, 0x2b, 0x35,
+	0x90, 0x87, 0x50, 0xe6, 0xd2, 0xfc, 0xcc, 0x7d, 0x87, 0x56, 0xb2, 0x2b, 0x50, 0xe2, 0xf2, 0x80,
+	0xfb, 0x0e, 0x59, 0x83, 0xa5, 0x70, 0x46, 0xab, 0x59, 0xd7, 0x52, 0x38, 0x23, 0x6b, 0x50, 0xe2,
+	0xd2, 0x74, 0x78, 0x48, 0x41, 0xb9, 0x8c, 0x22, 0x97, 0x6f, 0x79, 0x48, 0xd6, 0xa1, 0xea, 0x59,
+	0xbe, 0x63, 0x45, 0x22, 0xbc, 0xa4, 0x35, 0x24, 0x15, 0xa2, 0x30, 0x66, 0xc6, 0x95, 0x99, 0x3c,
+	0x06, 0xe0, 0xd2, 0x94, 0x97, 0x9e, 0xcb, 0xfd, 0xaf, 0xb4, 0x9e, 0x8d, 0x5c, 0xe5, 0xf2, 0x54,
+	0xdb, 0xc9, 0x03, 0x28, 0xf9, 0x42, 0xc6, 0xdc, 0xa1, 0x8d, 0x85, 0xb4, 0xb4, 0x91, 0xdc, 0x87,
+	0xa2, 0x2f, 0x1c, 0x76, 0x4e, 0x9b, 0x59, 0xaf, 0xb6, 0x69, 0x2e, 0xbb, 0x60, 0x36, 0x5d, 0xbe,
+	0xc6, 0x55, 0xc6, 0x75, 0x13, 0xf2, 0xc3, 0x0b, 0x46, 0x08, 0x14, 0x02, 0x2b, 0xfa, 0x42, 0x73,
+	0x6a, 0xb1, 0x0d, 0xfc, 0x4f, 0x5a, 0x90, 0xb7, 0xc2, 0x29, 0x5d, 0x6a, 0xe7, 0x3b, 0x55, 0x43,
+	0xfd, 0x55, 0x28, 0x2b, 0x9c, 0x76, 0x71, 0xb7, 0xaa, 0x06, 0xfe, 0x57, 0x6b, 0xa6, 0x02, 0x99,
+	0x67, 0xce, 0x62, 0xd5, 0x94, 0x94, 0xf5, 0xd0, 0x59, 0xff, 0xcf, 0x7d, 0xa8, 0x4f, 0xe4, 0x7b,
+	0x8b, 0xbb, 0x03, 0x2c, 0x6e, 0x72, 0x0b, 0x0a, 0xbe, 0xe5, 0xb1, 0x79, 0xd9, 0xe0, 0x88, 0xb4,
+	0xa1, 0xe6, 0x30, 0x69, 0x87, 0x1c, 0xb7, 0x22, 0x11, 0xcd, 0x9a, 0xc8, 0x13, 0x28, 0x78, 0xc2,
+	0x61, 0x28, 0xde, 0xec, 0xd5, 0xb7, 0xf4, 0xe9, 0xd8, 0x3a, 0x12, 0x0e, 0xeb, 0x17, 0x8e, 0x27,
+	0x83, 0xa1, 0x81, 0x7e, 0xf2, 0x08, 0xc0, 0xfe, 0x12, 0x0a, 0x11, 0xe1, 0x8e, 0xa8, 0x9c, 0xaa,
+	0x07, 0x4b, 0x34, 0x67, 0x54, 0xb5, 0x55, 0xed, 0xcc, 0x53, 0x5c, 0x75, 0xc4, 0x84, 0x33, 0xac,
+	0xa0, 0x34, 0x6d, 0x44, 0x56, 0xb8, 0x34, 0x84, 0x88, 0x8c, 0x19, 0x59, 0x87, 0xca, 0x17, 0x21,
+	0x23, 0xcc, 0xb7, 0x82, 0xf9, 0x96, 0x26, 0xa7, 0xef, 0xf7, 0x47, 0x63, 0x63, 0x6e, 0x27, 0xab,
+	0x90, 0xb7, 0x67, 0x0e, 0x56, 0x45, 0xb5, 0x9f, 0xdb, 0x36, 0xd4, 0x88, 0xac, 0x41, 0x21, 0x10,
+	0x61, 0x84, 0x05, 0xd1, 0xe8, 0xe7, 0xba, 0x06, 0x0e, 0xc9, 0x43, 0xa8, 0xa8, 0xea, 0x52, 0x5c,
+	0xac, 0x88, 0x6a, 0x7f, 0xa9, 0xdf, 0x37, 0xe6, 0x36, 0xb2, 0x09, 0x2d, 0xcf, 0xba, 0x50, 0xa7,
+	0xc2, 0x97, 0x66, 0xc0, 0x42, 0x93, 0x07, 0x58, 0x14, 0x18, 0xa2, 0xe1, 0x59, 0x17, 0x03, 0xe5,
+	0x39, 0x61, 0xe1, 0x28, 0x20, 0xeb, 0x00, 0x11, 0xf7, 0x98, 0xe9, 0x72, 0x8f, 0x47, 0x58, 0x18,
+	0x8d, 0x7e, 0x7e, 0xaf, 0xdb, 0x35, 0xaa, 0xca, 0x3c, 0x56, 0x56, 0xb5, 0xf9, 0x8e, 0xc5, 0x3c,
+	0xe1, 0x2f, 0x96, 0x46, 0x62, 0x24, 0xdf, 0x43, 0x05, 0xe5, 0x82, 0x58, 0x62, 0x75, 0xa0, 0x4c,
+	0x59, 0xc9, 0x04, 0xb1, 0x54, 0x65, 0xed, 0x8a, 0xa9, 0xda, 0xd8, 0x56, 0x3b, 0xd7, 0x29, 0x1a,
+	0x45, 0x57, 0x4c, 0x0f, 0x1d, 0x72, 0x17, 0x2a, 0x68, 0xe6, 0x2e, 0xa3, 0x2b, 0x58, 0x08, 0x65,
+	0xe5, 0xe0, 0x2e, 0x23, 0xcf, 0xa1, 0xaa, 0x5c, 0x2e, 0x3b, 0x67, 0x2e, 0x25, 0xb8, 0x4f, 0xad,
+	0x74, 0x9f, 0xc6, 0x62, 0x3a, 0x56, 0x76, 0x43, 0xb1, 0xf1, 0x1f, 0x69, 0x43, 0xe5, 0x2b, 0x63,
+	0x01, 0x1e, 0xe3, 0xd5, 0x6c, 0x7e, 0x65, 0x65, 0x56, 0xc7, 0xf8, 0x16, 0x14, 0x99, 0x7f, 0x6e,
+	0x85, 0xf4, 0x16, 0xd6, 0x83, 0x1e, 0xa8, 0x83, 0x85, 0x3c, 0xdb, 0x0a, 0x24, 0x5d, 0xcb, 0x12,
+	0x31, 0xde, 0xc0, 0x0a, 0xa4, 0x2a, 0x5e, 0xdb, 0x0a, 0xe8, 0x6d, 0x5d, 0xbc, 0x36, 0x5e, 0x9e,
+	0x25, 0xc9, 0x5d, 0x75, 0xd9, 0xdc, 0x59, 0x58, 0x0b, 0x6d, 0x24, 0x4f, 0xa0, 0x26, 0xbf, 0xf2,
+	0xc0, 0x94, 0x4c, 0xdd, 0x96, 0x94, 0x66, 0x31, 0xa0, 0x3c, 0xa7, 0xe8, 0x20, 0x3f, 0x41, 0x53,
+	0x46, 0x0e, 0x0b, 0x43, 0x33, 0x12, 0xa6, 0x1f, 0xbb, 0x2e, 0xbd, 0x9b, 0x85, 0xd6, 0xb5, 0xf3,
+	0x83, 0x98, 0xc4, 0xae, 0x4b, 0xee, 0x40, 0x39, 0xb0, 0xa4, 0x54, 0x6b, 0x78, 0xaf, 0x9d, 0xef,
+	0x14, 0x8d, 0x92, 0x1a, 0x1e, 0x3a, 0x64, 0x0f, 0x6e, 0x39, 0x5c, 0x5a, 0x9f, 0x5d, 0x66, 0xfa,
+	0x42, 0xdd, 0xbb, 0x66, 0x10, 0xf2, 0x73, 0x49, 0xef, 0x67, 0x63, 0xad, 0x24, 0x90, 0x89, 0x98,
+	0xb0, 0xd9, 0x89, 0xf2, 0x93, 0x36, 0x54, 0x43, 0xdc, 0x70, 0xd3, 0x92, 0xf4, 0xfb, 0x76, 0xae,
+	0x53, 0xe8, 0xe7, 0x5f, 0xee, 0xf4, 0x8c, 0x8a, 0xb6, 0xee, 0x4b, 0xf2, 0x0b, 0x34, 0xe7, 0x08,
+	0x13, 0x6f, 0xc0, 0x07, 0xb8, 0x11, 0xcd, 0x74, 0x23, 0x0c, 0x2c, 0x8d, 0x7e, 0xf1, 0xd3, 0xfe,
+	0xf8, 0xe3, 0xd0, 0xa8, 0xa7, 0xc4, 0x0f, 0xea, 0x72, 0x5c, 0x87, 0x5a, 0x42, 0xb6, 0x45, 0xc8,
+	0xe8, 0x43, 0x14, 0xc8, 0x75, 0x0d, 0xd0, 0xd6, 0x81, 0x08, 0x19, 0xf9, 0x1d, 0x5a, 0x19, 0x8c,
+	0x96, 0xf8, 0xe1, 0x5b, 0x12, 0xcd, 0x2b, 0x72, 0x22, 0x02, 0x69, 0x80, 0x20, 0xa6, 0x6d, 0x3d,
+	0x09, 0x2c, 0xdc, 0x04, 0x18, 0xc4, 0xe4, 0x37, 0x58, 0xbe, 0xc2, 0x68, 0x8d, 0x47, 0xdf, 0xd2,
+	0x68, 0xcc, 0xa9, 0x28, 0xf1, 0x18, 0x92, 0x79, 0x99, 0x67, 0x92, 0xff, 0xc9, 0xe8, 0xba, 0x9e,
+	0xc8, 0x8e, 0x91, 0x4c, 0xef, 0x50, 0x59, 0xc9, 0x3e, 0xac, 0x64, 0x51, 0x5a, 0xe6, 0xc7, 0x6f,
+	0xc9, 0x2c, 0x67, 0xe8, 0x28, 0xf4, 0x14, 0x12, 0x65, 0xd3, 0x17, 0x78, 0x22, 0x1e, 0xa3, 0xd2,
+	0xd2, 0x6e, 0x2f, 0x5d, 0xd9, 0x09, 0xda, 0xc9, 0x00, 0xc8, 0x02, 0x50, 0x8b, 0x6d, 0x7c, 0x4b,
+	0xac, 0x95, 0x0d, 0x90, 0xa8, 0xa5, 0xd3, 0xf2, 0x83, 0x50, 0xd8, 0xf4, 0x09, 0x8a, 0x15, 0x76,
+	0xba, 0xbd, 0x17, 0xe9, 0xcc, 0x26, 0xca, 0x41, 0xde, 0xcc, 0x67, 0x86, 0x40, 0x2d, 0xf6, 0xf4,
+	0x46, 0xb1, 0xc2, 0xe9, 0xf1, 0xe1, 0x87, 0x74, 0x62, 0xc8, 0x46, 0xa9, 0xcd, 0xb9, 0x94, 0x8c,
+	0x2c, 0xfb, 0x2b, 0xed, 0xa0, 0x54, 0x79, 0xa7, 0xfb, 0xe2, 0xd5, 0xcb, 0x9f, 0xf7, 0x52, 0xb5,
+	0x53, 0xe5, 0xcb, 0xa8, 0x21, 0x56, 0xab, 0x3d, 0xfb, 0xff, 0x6a, 0xc8, 0x46, 0xb5, 0x03, 0xb8,
+	0x17, 0xb0, 0x50, 0x0a, 0xdf, 0x32, 0x2d, 0xc7, 0x09, 0x4d, 0x5b, 0x78, 0x81, 0x15, 0x99, 0xae,
+	0x75, 0x29, 0xe2, 0x88, 0x6e, 0x66, 0x0f, 0xc5, 0x9d, 0x04, 0xb8, 0xef, 0x38, 0xe1, 0x00, 0x61,
+	0x63, 0x44, 0x91, 0x3e, 0xdc, 0x4e, 0x63, 0x78, 0x9e, 0x15, 0x98, 0x81, 0x35, 0x65, 0xe6, 0x9f,
+	0x2c, 0x14, 0xf4, 0xa7, 0x2c, 0x7f, 0x35, 0x01, 0x1d, 0x79, 0x56, 0x70, 0x62, 0x4d, 0xd9, 0x3f,
+	0x59, 0x28, 0xc8, 0x1b, 0xb8, 0x9b, 0x72, 0x43, 0x66, 0x39, 0x26, 0xf7, 0x02, 0x97, 0x33, 0x69,
+	0xe2, 0xbb, 0xf9, 0x97, 0x2c, 0x3d, 0xd5, 0x30, 0x98, 0xe5, 0x8c, 0x34, 0x6a, 0x78, 0xc1, 0xec,
+	0xac, 0x3a, 0xce, 0x40, 0xaf, 0xc7, 0xee, 0xf4, 0x33, 0x7d, 0x7e, 0x93, 0xba, 0xca, 0x1e, 0x57,
+	0x64, 0x77, 0xfa, 0x39, 0xab, 0x8e, 0x5c, 0x5f, 0x98, 0xa1, 0xe5, 0x3b, 0xc2, 0x53, 0xa5, 0xbb,
+	0x75, 0x93, 0xba, 0xa2, 0x4f, 0x84, 0x91, 0x82, 0x54, 0x61, 0xd8, 0xae, 0xf0, 0xb1, 0x83, 0xf3,
+	0x59, 0x44, 0xb7, 0x33, 0xdd, 0x46, 0x0d, 0x3d, 0x13, 0x74, 0x90, 0x67, 0xd0, 0x98, 0x03, 0x63,
+	0xc9, 0x42, 0xda, 0xcd, 0x20, 0xeb, 0x29, 0x52, 0x79, 0xc8, 0x06, 0xd4, 0xae, 0x62, 0x4a, 0xba,
+	0x93, 0x01, 0xc2, 0x3c, 0xa4, 0x5c, 0x90, 0x0e, 0xb8, 0x43, 0x7b, 0x37, 0x49, 0x07, 0xdc, 0x59,
+	0x00, 0xf2, 0xc0, 0xa6, 0xbb, 0x37, 0x01, 0x79, 0x60, 0x2f, 0x00, 0xe3, 0x48, 0xd2, 0x17, 0x37,
+	0x01, 0xe3, 0x48, 0x92, 0xe7, 0xb0, 0x3c, 0x07, 0xda, 0xd3, 0x50, 0xc4, 0x01, 0x7d, 0x99, 0xc1,
+	0x36, 0x53, 0xac, 0xf6, 0x91, 0x0d, 0x28, 0xc5, 0xba, 0xbd, 0xdd, 0x6b, 0xe7, 0x3b, 0xb5, 0x5e,
+	0x23, 0xad, 0x4d, 0x6c, 0xae, 0x8d, 0xc4, 0xa9, 0x60, 0x53, 0x0d, 0xfb, 0xf9, 0x46, 0x98, 0x76,
+	0xaa, 0xce, 0xcd, 0x53, 0x7d, 0xb0, 0x89, 0x27, 0xf1, 0xd5, 0x42, 0xe7, 0x86, 0x8e, 0x13, 0x75,
+	0x10, 0x37, 0xa0, 0x88, 0x03, 0xfa, 0x1a, 0x63, 0x2d, 0x5f, 0x75, 0x2d, 0xd8, 0x42, 0x1b, 0xda,
+	0x4b, 0xb6, 0x60, 0x55, 0x32, 0x5b, 0x55, 0xbd, 0x19, 0x08, 0x97, 0xdb, 0x97, 0xfa, 0x79, 0xed,
+	0xe3, 0xf3, 0xba, 0x92, 0xb8, 0x4e, 0xd0, 0x83, 0x0f, 0xed, 0x06, 0x34, 0x53, 0xbc, 0x8c, 0x42,
+	0xee, 0x4f, 0xe9, 0x2f, 0xf8, 0xd0, 0x35, 0x12, 0xeb, 0x29, 0x1a, 0xf1, 0x4d, 0x4b, 0x60, 0xae,
+	0x98, 0xd2, 0x5f, 0x17, 0xdf, 0x34, 0xed, 0x19, 0x8b, 0x29, 0x79, 0x0a, 0x4d, 0xbd, 0x46, 0xa6,
+	0xc7, 0x3c, 0xd3, 0xb3, 0x2e, 0xe8, 0x6f, 0xe9, 0xcd, 0x5f, 0xd7, 0x8e, 0x23, 0xe6, 0x1d, 0x59,
+	0x17, 0xea, 0xee, 0xcf, 0x02, 0x71, 0x66, 0x7f, 0xc5, 0x3e, 0x66, 0x6d, 0x5b, 0x5e, 0xca, 0xed,
+	0x33, 0xb9, 0xad, 0xfd, 0xdb, 0x1e, 0xf3, 0x44, 0x78, 0x69, 0x34, 0xaf, 0xe8, 0x38, 0xd1, 0x1e,
+	0xac, 0x64, 0x02, 0x04, 0x56, 0xa8, 0xde, 0xe3, 0xdf, 0x17, 0x3a, 0xab, 0xe5, 0x39, 0xe5, 0x04,
+	0xdd, 0xe4, 0x19, 0x24, 0x26, 0x33, 0xe0, 0x8e, 0xc4, 0xf4, 0xde, 0xa4, 0xe9, 0x35, 0xb4, 0xe7,
+	0x84, 0x3b, 0x52, 0xe7, 0xb7, 0xb2, 0x00, 0xc5, 0x04, 0xf7, 0x31, 0xfc, 0xea, 0xb5, 0x04, 0x15,
+	0x20, 0xd5, 0x42, 0x3a, 0xe6, 0xf7, 0x02, 0x48, 0x36, 0x40, 0x92, 0xe0, 0xc1, 0x42, 0x82, 0xad,
+	0x2b, 0x52, 0x92, 0xe1, 0x1e, 0xdc, 0x4e, 0x58, 0x3e, 0x8b, 0x4c, 0xdb, 0x95, 0xa6, 0xed, 0x5a,
+	0x52, 0xb5, 0x11, 0x83, 0xb4, 0xab, 0x5a, 0xd5, 0x80, 0x09, 0x8b, 0x06, 0xae, 0x1c, 0x68, 0x2f,
+	0x79, 0x07, 0xb7, 0xae, 0xf1, 0x74, 0xc6, 0x6f, 0x51, 0xef, 0xf6, 0xb5, 0x8c, 0x13, 0x8c, 0xb1,
+	0x92, 0x0d, 0xa5, 0xd3, 0x7e, 0x0d, 0x6b, 0xd7, 0x02, 0x25, 0x99, 0x0f, 0x17, 0x32, 0x27, 0x59,
+	0x66, 0x92, 0xfb, 0xee, 0x9c, 0xaa, 0x5e, 0x5a, 0x4f, 0xb7, 0x9d, 0x92, 0xd9, 0xf4, 0x30, 0x4d,
+	0x3d, 0xd1, 0x1b, 0x04, 0xf1, 0x91, 0x6a, 0x3d, 0x4f, 0x99, 0x4d, 0x7e, 0x9d, 0xd7, 0x01, 0x92,
+	0x30, 0xe9, 0x77, 0x28, 0x45, 0xae, 0x25, 0x6d, 0x07, 0x71, 0x5a, 0x04, 0x2a, 0xc0, 0xb5, 0x22,
+	0x50, 0xec, 0x24, 0xd3, 0x3f, 0x6e, 0x2a, 0x82, 0x41, 0x10, 0x27, 0x69, 0x6e, 0x40, 0x8d, 0x9f,
+	0x59, 0x36, 0xb6, 0x4b, 0xae, 0xa0, 0xa3, 0xc5, 0x2f, 0x25, 0xe5, 0x99, 0x88, 0xb1, 0x20, 0xf7,
+	0x41, 0x0f, 0x4c, 0x31, 0xf3, 0xe9, 0x7b, 0x3c, 0x13, 0x15, 0x34, 0x1c, 0xcf, 0x7c, 0xf2, 0x23,
+	0x34, 0x3c, 0xcb, 0x3e, 0x77, 0x2d, 0xdf, 0x44, 0x1b, 0xfd, 0x1b, 0x9e, 0xaf, 0x7a, 0x62, 0x1c,
+	0x29, 0x1b, 0xd9, 0xbe, 0x02, 0x9d, 0x4b, 0xd5, 0x7f, 0x8f, 0x31, 0xb1, 0xda, 0xce, 0xeb, 0xde,
+	0xd6, 0xce, 0xde, 0xab, 0xad, 0xee, 0x56, 0xcf, 0xa8, 0x25, 0x88, 0x4f, 0x72, 0x14, 0x90, 0x9d,
+	0x05, 0x82, 0xef, 0xd1, 0x23, 0x24, 0x34, 0x7a, 0x2f, 0x5f, 0x6e, 0xa5, 0xbf, 0x6e, 0x86, 0x32,
+	0xf1, 0xae, 0x69, 0x4c, 0x67, 0x74, 0x72, 0x5d, 0x63, 0x27, 0x43, 0x78, 0x37, 0x23, 0x8f, 0x17,
+	0x08, 0x9e, 0x45, 0x8f, 0x93, 0x8f, 0xa7, 0x2b, 0xd4, 0x91, 0x45, 0x9e, 0x40, 0x05, 0x3f, 0xc5,
+	0x3e, 0x73, 0x9f, 0x9e, 0xb4, 0x73, 0x9d, 0x5a, 0xaf, 0x96, 0xde, 0x37, 0xc3, 0x0b, 0x66, 0xe0,
+	0x77, 0xda, 0x01, 0xf7, 0x37, 0x77, 0xa1, 0xa0, 0xbe, 0x9a, 0x08, 0x40, 0x69, 0x3c, 0x3a, 0xfd,
+	0x30, 0x9c, 0xb4, 0xbe, 0x23, 0x15, 0xc0, 0x6f, 0xa8, 0x56, 0x8e, 0x54, 0xa1, 0x68, 0x0c, 0x8d,
+	0x8f, 0x93, 0xd6, 0x92, 0x02, 0x0c, 0xff, 0x31, 0x1c, 0x7c, 0x1a, 0xb6, 0xf2, 0x9b, 0x07, 0x50,
+	0x49, 0x5b, 0x78, 0x05, 0x79, 0x3b, 0x3c, 0xf8, 0xf8, 0x4e, 0xf3, 0x46, 0x93, 0xc3, 0xe3, 0x56,
+	0x8e, 0xd4, 0xa0, 0xfc, 0xf7, 0x7d, 0x63, 0x32, 0x9a, 0xbc, 0x6b, 0x2d, 0x29, 0xc4, 0xd0, 0x30,
+	0x8e, 0x8d, 0x56, 0x5e, 0xfd, 0x3d, 0xdc, 0xff, 0xb0, 0x3f, 0x6e, 0x15, 0x36, 0xbb, 0x50, 0xd2,
+	0x7d, 0x80, 0x32, 0x62, 0x93, 0xa3, 0x23, 0xa8, 0xa6, 0xa0, 0x95, 0x53, 0xff, 0xfe, 0xd8, 0x37,
+	0xde, 0xb6, 0x96, 0x48, 0x19, 0xf2, 0xa3, 0xc9, 0x61, 0x2b, 0xff, 0xbf, 0x00, 0x00, 0x00, 0xff,
+	0xff, 0x27, 0xf6, 0x8f, 0xb6, 0x19, 0x11, 0x00, 0x00,
+}
diff --git a/proto/nsjail/config.proto b/proto/nsjail/config.proto
new file mode 100644
index 0000000..4e02537
--- /dev/null
+++ b/proto/nsjail/config.proto
@@ -0,0 +1,243 @@
+syntax = "proto2";
+
+package nsjail;
+
+enum Mode {
+    LISTEN = 0; /* Listening on a TCP port */
+    ONCE = 1;   /* Running the command once only */
+    RERUN = 2;  /* Re-executing the command (forever) */
+    EXECVE = 3; /* Executing command w/o the supervisor */
+}
+/* Should be self explanatory */
+enum LogLevel {
+    DEBUG = 0;   /* Equivalent to the '-v' cmd-line option */
+    INFO = 1;    /* Default level */
+    WARNING = 2; /* Equivalent to the '-q' cmd-line option */
+    ERROR = 3;
+    FATAL = 4;
+}
+message IdMap {
+    /* Empty string means "current uid/gid" */
+    optional string inside_id = 1 [default = ""];
+    optional string outside_id = 2 [default = ""];
+    /* See 'man user_namespaces' for the meaning of count */
+    optional uint32 count = 3 [default = 1];
+    /* Does this map use /usr/bin/new[u|g]idmap binary? */
+    optional bool use_newidmap = 4 [default = false];
+}
+message MountPt {
+    /* Can be skipped for filesystems like 'proc' */
+    optional string src = 1 [default = ""];
+    /* Should 'src' path be prefixed with this envvar? */
+    optional string prefix_src_env = 2 [default = ""];
+    /* If specified, contains buffer that will be written to the dst file */
+    optional bytes src_content = 3 [default = ""];
+    /* Mount point inside jail */
+    required string dst = 4 [default = ""];
+    /* Should 'dst' path be prefixed with this envvar? */
+    optional string prefix_dst_env = 5 [default = ""];
+    /* Can be empty for mount --bind mounts */
+    optional string fstype = 6 [default = ""];
+    /* E.g. size=5000000 for 'tmpfs' */
+    optional string options = 7 [default = ""];
+    /* Is it a 'mount --bind src dst' type of mount? */
+    optional bool is_bind = 8 [default = false];
+    /* Is it a R/W mount? */
+    optional bool rw = 9 [default = false];
+    /* Is it a directory? If not specified an internal
+       heuristics will be used to determine that */
+    optional bool is_dir = 10;
+    /* Should the sandboxing fail if we cannot mount this resource? */
+    optional bool mandatory = 11 [default = true];
+    /* Is it a symlink (instead of real mount point)? */
+    optional bool is_symlink = 12 [default = false];
+    /* Is it a nosuid mount */
+    optional bool nosuid = 13 [default = false];
+    /* Is it a nodev mount */
+    optional bool nodev = 14 [default = false];
+    /* Is it a noexec mount */
+    optional bool noexec = 15 [default = false];
+}
+enum RLimit {
+    VALUE = 0; /* Use the provided value */
+    SOFT = 1;  /* Use the current soft rlimit */
+    HARD = 2;  /* Use the current hard rlimit */
+    INF = 3;   /* Use RLIM64_INFINITY */
+}
+message Exe {
+    /* Will be used both as execv's path and as argv[0] */
+    required string path = 1;
+    /* This will be argv[1] and so on.. */
+    repeated string arg = 2;
+    /* Override argv[0] */
+    optional string arg0 = 3;
+    /* Should execveat() be used to execute a file-descriptor instead? */
+    optional bool exec_fd = 4 [default = false];
+}
+message NsJailConfig {
+    /* Optional name and description for this config */
+    optional string name = 1 [default = ""];
+    repeated string description = 2;
+
+    /* Execution mode: see 'msg Mode' description for more */
+    optional Mode mode = 3 [default = ONCE];
+    /* Equivalent to a bind mount with dst='/'. DEPRECATED: Use bind mounts. */
+    optional string chroot_dir = 4 [deprecated = true];
+    /* Applies both to the chroot_dir and to /proc mounts. DEPRECATED: Use bind mounts */
+    optional bool is_root_rw = 5 [default = false, deprecated = true];
+    /* Hostname inside jail */
+    optional string hostname = 8 [default = "NSJAIL"];
+    /* Initial current working directory for the binary */
+    optional string cwd = 9 [default = "/"];
+
+    /* TCP port to listen to. Valid with mode=LISTEN only */
+    optional uint32 port = 10 [default = 0];
+    /* Host to bind to for mode=LISTEN. Must be in IPv6 format */
+    optional string bindhost = 11 [default = "::"];
+    /* For mode=LISTEN, maximum number of connections from a single IP */
+    optional uint32 max_conns_per_ip = 12 [default = 0];
+
+    /* Wall-time time limit for commands */
+    optional uint32 time_limit = 13 [default = 600];
+    /* Should nsjail go into background? */
+    optional bool daemon = 14 [default = false];
+    /* Maximum number of CPUs to use: 0 - no limit */
+    optional uint32 max_cpus = 15 [default = 0];
+
+    /* FD to log to. */
+    optional int32 log_fd = 16;
+    /* File to save lofs to */
+    optional string log_file = 17;
+    /* Minimum log level displayed.
+       See 'msg LogLevel' description for more */
+    optional LogLevel log_level = 18;
+
+    /* Should the current environment variables be kept
+       when executing the binary */
+    optional bool keep_env = 19 [default = false];
+    /* EnvVars to be set before executing binaries. If the envvar doesn't contain '='
+       (e.g. just the 'DISPLAY' string), the current envvar value will be used */
+    repeated string envar = 20;
+
+    /* Should capabilities be preserved or dropped */
+    optional bool keep_caps = 21 [default = false];
+    /* Which capabilities should be preserved if keep_caps == false.
+       Format: "CAP_SYS_PTRACE" */
+    repeated string cap = 22;
+    /* Should nsjail close FD=0,1,2 before executing the process */
+    optional bool silent = 23 [default = false];
+    /* Should the child process have control over terminal?
+       Can be useful to allow /bin/sh to provide
+       job control / signals. Dangerous, can be used to put
+       characters into the controlling terminal back */
+    optional bool skip_setsid = 24 [default = false];
+    /* Redirect sdterr of the process to /dev/null instead of the socket or original TTY */
+    optional bool stderr_to_null = 25 [default = false];
+    /* Which FDs should be passed to the newly executed process
+       By default only FD=0,1,2 are passed */
+    repeated int32 pass_fd = 26;
+    /* Setting it to true will allow to have set-uid binaries
+       inside the jail */
+    optional bool disable_no_new_privs = 27 [default = false];
+
+    /* Various rlimits, the rlimit_as/rlimit_core/... are used only if
+       rlimit_as_type/rlimit_core_type/... are set to RLimit::VALUE */
+    optional uint64 rlimit_as = 28 [default = 512]; /* In MiB */
+    optional RLimit rlimit_as_type = 29 [default = VALUE];
+    optional uint64 rlimit_core = 30 [default = 0]; /* In MiB */
+    optional RLimit rlimit_core_type = 31 [default = VALUE];
+    optional uint64 rlimit_cpu = 32 [default = 600]; /* In seconds */
+    optional RLimit rlimit_cpu_type = 33 [default = VALUE];
+    optional uint64 rlimit_fsize = 34 [default = 1]; /* In MiB */
+    optional RLimit rlimit_fsize_type = 35 [default = VALUE];
+    optional uint64 rlimit_nofile = 36 [default = 32];
+    optional RLimit rlimit_nofile_type = 37 [default = VALUE];
+    /* RLIMIT_NPROC is system-wide - tricky to use; use the soft limit value by
+     * default here */
+    optional uint64 rlimit_nproc = 38 [default = 1024];
+    optional RLimit rlimit_nproc_type = 39 [default = SOFT];
+    /* In MiB, use the soft limit value by default */
+    optional uint64 rlimit_stack = 40 [default = 1048576];
+    optional RLimit rlimit_stack_type = 41 [default = SOFT];
+
+    /* See 'man personality' for more */
+    optional bool persona_addr_compat_layout = 42 [default = false];
+    optional bool persona_mmap_page_zero = 43 [default = false];
+    optional bool persona_read_implies_exec = 44 [default = false];
+    optional bool persona_addr_limit_3gb = 45 [default = false];
+    optional bool persona_addr_no_randomize = 46 [default = false];
+
+    /* Which name-spaces should be used? */
+    optional bool clone_newnet = 47 [default = true];
+    optional bool clone_newuser = 48 [default = true];
+    optional bool clone_newns = 49 [default = true];
+    optional bool clone_newpid = 50 [default = true];
+    optional bool clone_newipc = 51 [default = true];
+    optional bool clone_newuts = 52 [default = true];
+    /* Disable for kernel versions < 4.6 as it's not supported there */
+    optional bool clone_newcgroup = 53 [default = true];
+
+    /* Mappings for UIDs and GIDs. See the description for 'msg IdMap'
+       for more */
+    repeated IdMap uidmap = 54;
+    repeated IdMap gidmap = 55;
+
+    /* Should /proc be mounted (R/O)? This can also be added in the 'mount'
+       section below */
+    optional bool mount_proc = 56 [default = false];
+    /* Mount points inside the jail. See the description for 'msg MountPt'
+       for more */
+    repeated MountPt mount = 57;
+
+    /* Kafel seccomp-bpf policy file or a string:
+       Homepage of the project: https://github.com/google/kafel */
+    optional string seccomp_policy_file = 58;
+    repeated string seccomp_string = 59;
+    /* Setting it to true makes audit write seccomp logs to dmesg */
+    optional bool seccomp_log = 60 [default = false];
+
+    /* If > 0, maximum cumulative size of RAM used inside any jail */
+    optional uint64 cgroup_mem_max = 61 [default = 0]; /* In MiB */
+    /* Mount point for cgroups-memory in your system */
+    optional string cgroup_mem_mount = 62 [default = "/sys/fs/cgroup/memory"];
+    /* Writeable directory (for the nsjail user) under cgroup_mem_mount */
+    optional string cgroup_mem_parent = 63 [default = "NSJAIL"];
+
+    /* If > 0, maximum number of PIDs (threads/processes) inside jail */
+    optional uint64 cgroup_pids_max = 64 [default = 0];
+    /* Mount point for cgroups-pids in your system */
+    optional string cgroup_pids_mount = 65 [default = "/sys/fs/cgroup/pids"];
+    /* Writeable directory (for the nsjail user) under cgroup_pids_mount */
+    optional string cgroup_pids_parent = 66 [default = "NSJAIL"];
+
+    /* If > 0, Class identifier of network packets inside jail */
+    optional uint32 cgroup_net_cls_classid = 67 [default = 0];
+    /* Mount point for cgroups-net-cls in your system */
+    optional string cgroup_net_cls_mount = 68 [default = "/sys/fs/cgroup/net_cls"];
+    /* Writeable directory (for the nsjail user) under cgroup_net_mount */
+    optional string cgroup_net_cls_parent = 69 [default = "NSJAIL"];
+
+    /* If > 0 number of milliseconds of CPU that jail processes can use per each second */
+    optional uint32 cgroup_cpu_ms_per_sec = 70 [default = 0];
+    /* Mount point for cgroups-cpu in your system */
+    optional string cgroup_cpu_mount = 71 [default = "/sys/fs/cgroup/cpu"];
+    /* Writeable directory (for the nsjail user) under cgroup_cpu_mount */
+    optional string cgroup_cpu_parent = 72 [default = "NSJAIL"];
+
+    /* Should the 'lo' interface be brought up (active) inside this jail? */
+    optional bool iface_no_lo = 73 [default = false];
+
+    /* Put this interface inside the jail */
+    repeated string iface_own = 74;
+
+    /* Parameters for the cloned MACVLAN interface inside jail */
+    optional string macvlan_iface = 75; /* Interface to be cloned, eg 'eth0' */
+    optional string macvlan_vs_ip = 76 [default = "192.168.0.2"];
+    optional string macvlan_vs_nm = 77 [default = "255.255.255.0"];
+    optional string macvlan_vs_gw = 78 [default = "192.168.0.1"];
+    optional string macvlan_vs_ma = 79 [default = ""];
+
+    /* Binary path (with arguments) to be executed. If not specified here, it
+       can be specified with cmd-line as "-- /path/to/command arg1 arg2" */
+    optional Exe exec_bin = 80;
+}
diff --git a/remoteexec/adapter.go b/remoteexec/adapter.go
index 5be173e..489b9ba 100644
--- a/remoteexec/adapter.go
+++ b/remoteexec/adapter.go
@@ -17,7 +17,6 @@
 	"time"
 
 	rpb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
-	"go.chromium.org/goma/server/server"
 
 	"github.com/golang/protobuf/proto"
 	"github.com/golang/protobuf/ptypes"
@@ -33,6 +32,7 @@
 	fpb "go.chromium.org/goma/server/proto/file"
 	"go.chromium.org/goma/server/remoteexec/cas"
 	"go.chromium.org/goma/server/remoteexec/digest"
+	"go.chromium.org/goma/server/server"
 )
 
 // DigetCache caches digest for goma file hash.
@@ -51,7 +51,8 @@
 	ExecTimeout time.Duration
 
 	// Client is remoteexec API client.
-	Client Client
+	Client         Client
+	InsecureClient bool
 
 	// GomaFile handles output files from remoteexec's cas to goma's FileBlob.
 	GomaFile fpb.FileServiceClient
@@ -141,6 +142,10 @@
 }
 
 func (f *Adapter) client(ctx context.Context) Client {
+	// grpc rejects call on insecure connection if credential is set.
+	if f.InsecureClient {
+		return f.Client
+	}
 	user, _ := enduser.FromContext(ctx)
 	client := f.Client
 	token := user.Token()
@@ -149,6 +154,13 @@
 	}
 	client.CallOptions = append(client.CallOptions,
 		grpc.PerRPCCredentials(oauth.NewOauthAccess(token)))
+
+	maxBytes := int64(cas.DefaultBatchLimit)
+	if s := f.capabilities.GetCacheCapabilities().GetMaxBatchTotalSizeBytes(); s > maxBytes {
+		maxBytes = s
+	}
+	// TODO: set MaxCallRecvMsgSize if it uses BatchReadBlobs
+	client.CallOptions = append(client.CallOptions, grpc.MaxCallSendMsgSize(int(maxBytes)))
 	return client
 }
 
@@ -246,20 +258,6 @@
 	f.ensureCapabilities(ctx)
 
 	r := f.newRequest(ctx, req)
-	t := time.Now()
-
-	resp = r.getInventoryData(ctx)
-	if resp != nil {
-		logger.Infof("fail fast in inventory lookup: %s", time.Since(t))
-		return resp, nil
-	}
-
-	resp = r.newInputTree(ctx)
-	if resp != nil {
-		logger.Infof("fail fast in input tree: %s", time.Since(t))
-		return resp, nil
-	}
-	r.setupNewAction(ctx)
 
 	// Use this to collect all timestamps and then print on one line, regardless of where
 	// this function returns.
@@ -268,25 +266,45 @@
 		logger.Infof("%s", strings.Join(timestamps, ", "))
 	}()
 	addTimestamp := func(desc string, duration time.Duration) {
-		timestamps = append(timestamps, fmt.Sprintf("%s: %s", desc, duration))
+		timestamps = append(timestamps, fmt.Sprintf("%s: %s", desc, duration.Truncate(time.Millisecond)))
 	}
 
+	t := time.Now()
+	resp = r.getInventoryData(ctx)
+	addTimestamp("inventory", time.Since(t))
+	if resp != nil {
+		logger.Infof("fail fast in inventory lookup: %s", time.Since(t))
+		return resp, nil
+	}
+
+	t = time.Now()
+	resp = r.newInputTree(ctx)
+	addTimestamp("input tree", time.Since(t))
+	if resp != nil {
+		logger.Infof("fail fast in input tree: %s", time.Since(t))
+		return resp, nil
+	}
+
+	t = time.Now()
+	r.setupNewAction(ctx)
 	addTimestamp("setup", time.Since(t))
 
 	eresp := &rpb.ExecuteResponse{}
 	var cached bool
+	t = time.Now()
 	eresp.Result, cached = r.checkCache(ctx)
+	addTimestamp("check cache", time.Since(t))
 	if !cached {
 		t = time.Now()
 		blobs := r.missingBlobs(ctx)
 		addTimestamp("check missing", time.Since(t))
 		t = time.Now()
 		resp := r.uploadBlobs(ctx, blobs)
+		addTimestamp("upload blobs", time.Since(t))
 		if resp != nil {
-			addTimestamp("fail fast for missing input", time.Since(t))
+			logger.Infof("fail fast for uploading missing blobs: %v", resp)
 			return resp, nil
 		}
-		addTimestamp("upload blobs", time.Since(t))
 
 		t = time.Now()
 		var err error
diff --git a/remoteexec/cas/cas.go b/remoteexec/cas/cas.go
index 408a89b..6e76443 100644
--- a/remoteexec/cas/cas.go
+++ b/remoteexec/cas/cas.go
@@ -10,20 +10,23 @@
 	"sort"
 	"time"
 
+	rpb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
 	"go.opencensus.io/trace"
+	bpb "google.golang.org/genproto/googleapis/bytestream"
 	"google.golang.org/grpc"
 	"google.golang.org/grpc/codes"
 
-	rpb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
-
-	bpb "google.golang.org/genproto/googleapis/bytestream"
-
 	"go.chromium.org/goma/server/log"
 	"go.chromium.org/goma/server/remoteexec/datasource"
 	"go.chromium.org/goma/server/remoteexec/digest"
 	"go.chromium.org/goma/server/rpc"
 )
 
+const (
+	// DefaultBatchLimit is bytes limit for cas BatchUploadBlobs.
+	DefaultBatchLimit = 4 * 1024 * 1024
+)
+
 // Client is a client of cas service.
 type Client interface {
 	CAS() rpb.ContentAddressableStorageClient
@@ -112,8 +115,7 @@
 	// TODO: better packing
 	var i int
 
-	const defaultBatchLimit = 4 * 1024 * 1024
-	batchLimit := int64(defaultBatchLimit)
+	batchLimit := int64(DefaultBatchLimit)
 	if c.CacheCapabilities != nil && c.CacheCapabilities.MaxBatchTotalSizeBytes > 0 {
 		batchLimit = c.CacheCapabilities.MaxBatchTotalSizeBytes
 	}
diff --git a/remoteexec/exec.go b/remoteexec/exec.go
index 83a0557..0fc0164 100644
--- a/remoteexec/exec.go
+++ b/remoteexec/exec.go
@@ -196,8 +196,8 @@
 	for _, prop := range cmdConfig.GetRemoteexecPlatform().GetProperties() {
 		r.addPlatformProperty(ctx, prop.Name, prop.Value)
 	}
-	// TODO: set allow chroot if RemoteexecPlatform allows.
-	logger.Infof("platform: %s", r.platform)
+	r.allowChroot = cmdConfig.GetRemoteexecPlatform().GetHasNsjail()
+	logger.Infof("platform: %s, allowChroot=%t", r.platform, r.allowChroot)
 	return nil
 }
 
@@ -350,8 +350,6 @@
 				})
 				return
 			}
-			logger.Debugf("input %s hash=%s digest=%s", input.GetFilename(), input.GetHashKey(), data.Digest())
-
 			file := merkletree.Entry{
 				Name: fname,
 				Data: inputDigestData{
@@ -438,8 +436,7 @@
 		return nil
 	}
 
-	symAbsOk := r.f.capabilities.GetCacheCapabilities().GetSymlinkAbsolutePathStrategy() ==
-		rpb.CacheCapabilities_ALLOWED
+	symAbsOk := r.f.capabilities.GetCacheCapabilities().GetSymlinkAbsolutePathStrategy() == rpb.SymlinkAbsolutePathStrategy_ALLOWED
 
 	for _, f := range r.cmdFiles {
 		if _, found := toolchainInputs[f.Path]; found {
@@ -601,60 +598,6 @@
 fi
 exec "$@"
 `
-
-	chrootRunWrapperScript = `#!/bin/bash
-set -e
-
-if [[ "$WORK_DIR" == "" ]]; then
-  echo "ERROR: WORK_DIR is not set" >&2
-  exit 1
-fi
-
-rundir="$(pwd)"
-chroot_workdir="/tmp/goma_chroot"
-
-#
-# mount directories under $chroot_workdir and execute.
-#
-run_dirs=($(ls -1 "$rundir"))
-sys_dirs=(dev proc)
-
-# RBE server generates __action_home__XXXXXXXXXX directory in $rundir
-# (note: XXXXXXXXXX is a random).  Let's skip it because we do not use that.
-# mount directories in the request.
-for d in "${run_dirs[@]}"; do
-  if [[ "$d" == __action_home__* ]]; then
-    continue
-  fi
-  mkdir -p "$chroot_workdir/$d"
-  mount --bind "$rundir/$d" "$chroot_workdir/$d"
-done
-
-# mount directories not included in the request.
-for d in "${sys_dirs[@]}"; do
-  # avoid to mount system directories if that exist in the user's request.
-  if [[ -d "$rundir/$d" ]]; then
-    continue
-  fi
-  # workaround to make them read only mount with util-linux < 2.27.
-  mkdir -p "$chroot_workdir/$d"
-  mount --bind "/$d" "$chroot_workdir/$d"
-  mount "$chroot_workdir/$d" -o remount,ro,bind
-done
-
-# currently running with root. run the command with nobody:nogroup with chroot.
-# We use nsjail to chdir without running bash script inside chroot, and
-# libc inside chroot can be different from libc outside.
-# TODO: give nsjail rule with file.
-nsjail \
-  --user nobody:nobody \
-  --group nogroup:nogroup \
-  --chroot "$chroot_workdir" \
-  --cwd "$WORK_DIR" \
-  --rw \
-  --quiet \
-  -- "$@"
-`
 )
 
 // TODO: put wrapper script in platform container?
@@ -672,22 +615,22 @@
 	envs := []string{fmt.Sprintf("WORK_DIR=%s", wd)}
 
 	// The developer of this program can make multiple wrapper scripts
-	// to be used by adding wrapperDesc instances to `wrappers`.
+	// to be used by adding fileDesc instances to `files`.
 	// However, only the first one is called in the command line.
-	// The other scripts should be called from the first wrapper script.
-	type wrapperDesc struct {
-		name string
-		data digest.Data
+	// The other scripts should be called from the first wrapper script
+	// if needed.
+	type fileDesc struct {
+		name         string
+		data         digest.Data
+		isExecutable bool
 	}
-	var wrappers []wrapperDesc
+	var files []fileDesc
 
 	args := buildArgs(ctx, cmdConfig, argv0, r.gomaReq)
 
 	pathType := cmdConfig.GetCmdDescriptor().GetSetup().GetPathType()
 	switch pathType {
 	case cmdpb.CmdDescriptor_POSIX:
-		var d digest.Data
-		// TODO: use chroot for all ATS cases.
 		if r.needChroot {
 			logger.Infof("run with chroot")
 			envs = append(envs, r.gomaReq.Env...)
@@ -695,8 +638,20 @@
 			r.addPlatformProperty(ctx, "dockerPrivileged", "true")
 			// needed for chroot command and mount command.
 			r.addPlatformProperty(ctx, "dockerRunAsRoot", "true")
-			d = digest.Bytes("chroot-run-wrapper-script", []byte(chrootRunWrapperScript))
+			nsjailCfg := nsjailConfig(cwd)
+			files = []fileDesc{
+				{
+					name:         "run.sh",
+					data:         digest.Bytes("nsjail-run-wrapper-script", []byte(nsjailRunWrapperScript)),
+					isExecutable: true,
+				},
+				{
+					name: "nsjail.cfg",
+					data: digest.Bytes("nsjail-config-file", []byte(nsjailCfg)),
+				},
+			}
 		} else {
+			var d digest.Data
 			err = cwdAgnosticReq(ctx, cmdConfig, r.filepath, r.gomaReq.Arg, r.gomaReq.Env)
 			if err != nil {
 				logger.Infof("non cwd agnostic: %v", err)
@@ -717,23 +672,24 @@
 					envs = append(envs, e)
 				}
 			}
+			files = []fileDesc{
+				{
+					name:         "run.sh",
+					data:         d,
+					isExecutable: true,
+				},
+			}
 		}
-		wrappers = []wrapperDesc{
-			{
-				name: "run.sh",
-				data: d,
-			},
-		}
-
 	case cmdpb.CmdDescriptor_WINDOWS:
 		wn, data, err := wrapperForWindows(ctx)
 		if err != nil {
 			return err
 		}
-		wrappers = []wrapperDesc{
+		files = []fileDesc{
 			{
-				name: wn,
-				data: data,
+				name:         wn,
+				data:         data,
+				isExecutable: true,
 			},
 		}
 
@@ -744,17 +700,17 @@
 	// Only the first one is called in the command line via storing
 	// `wrapperPath` in `r.args` later.
 	wrapperPath := ""
-	for i, w := range wrappers {
+	for i, w := range files {
 		wp, err := rootRel(r.filepath, w.name, r.gomaReq.GetCwd(), r.tree.RootDir())
 		if err != nil {
 			return err
 		}
 
-		logger.Infof("wrapper (%d) %s => %v", i, wp, w.data.Digest())
+		logger.Infof("file (%d) %s => %v", i, wp, w.data.Digest())
 		r.tree.Set(merkletree.Entry{
 			Name:         wp,
 			Data:         w.data,
-			IsExecutable: true,
+			IsExecutable: w.isExecutable,
 		})
 		if wrapperPath == "" {
 			wrapperPath = wp
diff --git a/remoteexec/fake_cluster_for_test.go b/remoteexec/fake_cluster_for_test.go
index 613d6b7..8c48e27 100644
--- a/remoteexec/fake_cluster_for_test.go
+++ b/remoteexec/fake_cluster_for_test.go
@@ -291,7 +291,7 @@
 			RemoteexecPlatform: tc.RemoteexecPlatform,
 		})
 	}
-	_, err := f.adapter.Inventory.Configure(ctx, config)
+	err := f.adapter.Inventory.Configure(ctx, config)
 	return err
 }
 
@@ -314,7 +314,7 @@
 			Dimensions: dimensions,
 		},
 	}
-	_, err := f.adapter.Inventory.Configure(ctx, config)
+	err := f.adapter.Inventory.Configure(ctx, config)
 	return err
 }
 
diff --git a/remoteexec/fake_rbe_for_test.go b/remoteexec/fake_rbe_for_test.go
index 4450a7c..ce7b0d4 100644
--- a/remoteexec/fake_rbe_for_test.go
+++ b/remoteexec/fake_rbe_for_test.go
@@ -143,7 +143,7 @@
 	// high_api_version:<major:2 >
 	return &rpb.ServerCapabilities{
 		CacheCapabilities: &rpb.CacheCapabilities{
-			DigestFunction: []rpb.DigestFunction{
+			DigestFunction: []rpb.DigestFunction_Value{
 				rpb.DigestFunction_SHA256,
 			},
 			ActionCacheUpdateCapabilities: &rpb.ActionCacheUpdateCapabilities{
@@ -151,7 +151,7 @@
 			},
 			// CachePriorityCapabilities:
 			MaxBatchTotalSizeBytes:      4 * 1024 * 1024,
-			SymlinkAbsolutePathStrategy: rpb.CacheCapabilities_DISALLOWED,
+			SymlinkAbsolutePathStrategy: rpb.SymlinkAbsolutePathStrategy_DISALLOWED,
 		},
 		ExecutionCapabilities: &rpb.ExecutionCapabilities{
 			DigestFunction: rpb.DigestFunction_SHA256,
diff --git a/remoteexec/gomaoutput.go b/remoteexec/gomaoutput.go
index 58d34ea..ad0b9f4 100644
--- a/remoteexec/gomaoutput.go
+++ b/remoteexec/gomaoutput.go
@@ -180,6 +180,7 @@
 			return err
 		})
 		if err != nil {
+			logger.Warnf("store blob failed offset=%d for %v: %v", offset, output, err)
 			return nil, err
 		}
 		for _, hashKey := range resp.HashKey {
diff --git a/remoteexec/nsjail.go b/remoteexec/nsjail.go
new file mode 100644
index 0000000..455c97e
--- /dev/null
+++ b/remoteexec/nsjail.go
@@ -0,0 +1,113 @@
+// Copyright 2019 The Goma 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 remoteexec
+
+import (
+	"github.com/golang/protobuf/proto"
+
+	nsjailpb "go.chromium.org/goma/server/proto/nsjail"
+)
+
+const (
+	nsjailRunWrapperScript = `#!/bin/bash
+set -e
+
+if [[ "$WORK_DIR" == "" ]]; then
+  echo "ERROR: WORK_DIR is not set" >&2
+  exit 1
+fi
+
+rundir="$(pwd)"
+chroot_workdir="/tmp/goma_chroot"
+
+#
+# mount directories under $chroot_workdir and execute.
+#
+run_dirs=($(ls -1 "$rundir"))
+sys_dirs=(dev proc)
+
+# RBE server generates __action_home__XXXXXXXXXX directory in $rundir
+# (note: XXXXXXXXXX is a random).  Let's skip it because we do not use that.
+# mount directories in the request.
+for d in "${run_dirs[@]}"; do
+  if [[ "$d" == __action_home__* ]]; then
+    continue
+  fi
+  mkdir -p "$chroot_workdir/$d"
+  mount --bind "$rundir/$d" "$chroot_workdir/$d"
+done
+
+# mount directories not included in the request.
+for d in "${sys_dirs[@]}"; do
+  # avoid to mount system directories if that exist in the user's request.
+  if [[ -d "$rundir/$d" ]]; then
+    continue
+  fi
+  # directory will be mounted by nsjail later.
+  mkdir -p "$chroot_workdir/$d"
+done
+# needed to make nsjail bind /dev/urandom.
+touch "$chroot_workdir/dev/urandom"
+
+# currently running with root. run the command with nobody:nogroup with chroot.
+# We use nsjail to chdir without running bash script inside chroot, and
+# libc inside chroot can be different from libc outside.
+nsjail --quiet --config "$WORK_DIR/nsjail.cfg" -- "$@"
+`
+)
+
+// nsjailConfig returns nsjail configuration.
+// When you modify followings, please make sure it matches
+// nsjailRunWrapperScript above.
+func nsjailConfig(cwd string) []byte {
+	chrootWorkdir := "/tmp/goma_chroot"
+	cfg := &nsjailpb.NsJailConfig{
+		Uidmap: []*nsjailpb.IdMap{
+			{
+				InsideId:  proto.String("nobody"),
+				OutsideId: proto.String("nobody"),
+			},
+		},
+		Gidmap: []*nsjailpb.IdMap{
+			{
+				InsideId:  proto.String("nogroup"),
+				OutsideId: proto.String("nogroup"),
+			},
+		},
+		Mount: []*nsjailpb.MountPt{
+			{
+				Src:    proto.String(chrootWorkdir),
+				Dst:    proto.String("/"),
+				IsBind: proto.Bool(true),
+				Rw:     proto.Bool(true),
+				IsDir:  proto.Bool(true),
+			},
+			{
+				Src:    proto.String("/dev/urandom"),
+				Dst:    proto.String("/dev/urandom"),
+				IsBind: proto.Bool(true),
+			},
+		},
+		Cwd: proto.String(cwd),
+		// TODO: use log file and print to server log.
+		LogLevel:  nsjailpb.LogLevel_WARNING.Enum(),
+		MountProc: proto.Bool(true),
+		Envar: []string{
+			// HACK: ChromeOS clang wrapper needs this.
+			// https://chromium.googlesource.com/chromiumos/overlays/chromiumos-overlay/+/f15eab2d792acfe1ee5eca4d95792c081558cf84/sys-devel/gcc/files/sysroot_wrapper.body#69
+			// TODO: set better path upon needs.
+			// e.g. auto generate from where executables and symlink
+			// to executable exists, or choose directories with
+			// executables from PATH sent by clients.
+			"PATH=",
+		},
+		RlimitAsType:    nsjailpb.RLimit_INF.Enum(),
+		RlimitFsizeType: nsjailpb.RLimit_INF.Enum(),
+		// TODO: relax RLimit from the default.
+		// Default size might be too strict, and not suitable for
+		// compiling.
+	}
+	return []byte(proto.MarshalTextString(cfg))
+}
diff --git a/rpc/id.go b/rpc/id.go
index 4be6b2b..ae78460 100644
--- a/rpc/id.go
+++ b/rpc/id.go
@@ -15,17 +15,11 @@
 )
 
 var (
-	requestID = mustTagNewKey("go.chromium.org/goma/server/rpc.request_id")
+	requestID = tag.MustNewKey("go.chromium.org/goma/server/rpc.request_id")
 )
 
-func mustTagNewKey(name string) tag.Key {
-	k, err := tag.NewKey(name)
-	if err != nil {
-		logger := log.FromContext(context.Background())
-		logger.Fatal(err)
-	}
-	log.RegisterTagKey(k)
-	return k
+func init() {
+	log.RegisterTagKey(requestID)
 }
 
 // RequestID returns string identifier of this request.
diff --git a/server/process_collector.go b/server/process_collector.go
index c64b88d..6d2d6ef 100644
--- a/server/process_collector.go
+++ b/server/process_collector.go
@@ -69,6 +69,8 @@
 	lastResidentMemorySize int64 // atomic.
 
 	samplingInterval = time.Second
+
+	gcSema = make(chan bool, 1)
 )
 
 // ResidentMemorySize reports latest measured resident memory size in bytes.
@@ -81,7 +83,13 @@
 	logger := log.FromContext(ctx)
 	rss := ResidentMemorySize()
 	logger.Infof("GC start: rss=%d", rss)
-	runtime.GC()
+	select {
+	case gcSema <- true:
+		runtime.GC()
+		<-gcSema
+	default:
+		logger.Infof("GC already running")
+	}
 	procStats(ctx)
 	rss = ResidentMemorySize()
 	logger.Infof("GC end: rss=%d", rss)