This is an automated email from the ASF dual-hosted git repository.

juergbi pushed a commit to branch jbilleter/http-cache
in repository https://gitbox.apache.org/repos/asf/buildstream.git

commit 6a6298c10ce29e98b48562aa93cc32d8c68fd96e
Author: Jürg Billeter <[email protected]>
AuthorDate: Fri Aug 7 14:50:08 2026 +0200

    _protos: Update local_cas.proto from buildbox
---
 .../_protos/build/buildgrid/local_cas.proto        | 236 ++++++++++++++++++---
 .../_protos/build/buildgrid/local_cas_pb2.py       | 102 +++++----
 .../_protos/build/buildgrid/local_cas_pb2.pyi      | 146 ++++++++++---
 .../_protos/build/buildgrid/local_cas_pb2_grpc.py  | 168 ++++++++++++---
 4 files changed, 526 insertions(+), 126 deletions(-)

diff --git a/src/buildstream/_protos/build/buildgrid/local_cas.proto 
b/src/buildstream/_protos/build/buildgrid/local_cas.proto
index 0dc0cbc36..cca5e21cb 100644
--- a/src/buildstream/_protos/build/buildgrid/local_cas.proto
+++ b/src/buildstream/_protos/build/buildgrid/local_cas.proto
@@ -100,6 +100,21 @@ service LocalContentAddressableStorage {
   // cleanup is complete.
   rpc StageTree(stream StageTreeRequest) returns (stream StageTreeResponse) {}
 
+  // Export files to a user specified location on the local filesystem.
+  //
+  // The way in which files are populated is implementation defined,
+  // however, the server will guarantee that mutations to these files
+  // will not corrupt the local cache.
+  //
+  // This does not create any directories. The client needs to ensure that
+  // the parent directory of each output file exists and is writable.
+  //
+  // Missing blobs are fetched, if a CAS remote is configured.
+  //
+  // Errors:
+  // * `FAILED_PRECONDITION`: The destination directory does not exist.
+  rpc ExportFiles(ExportFilesRequest) returns (ExportFilesResponse) {}
+
   // Capture a directory tree from the local filesystem.
   //
   // This imports the specified path from the local filesystem into CAS.
@@ -138,6 +153,17 @@ service LocalContentAddressableStorage {
   // workflows where an upload step adds unnecessary latency.
   rpc CaptureFiles(CaptureFilesRequest) returns (CaptureFilesResponse) {}
 
+  // Hash files from the local filesystem.
+  //
+  // This asks the local CAS server to hash the file on behalf of the
+  // client and populate the inode cache for efficent subqeuent hashing
+  // of the same file.
+  //
+  // Unlike CaptureFiles which always store the file content either to
+  // local storage or remote CAS or both. HashFiles only return the hash
+  // and populate the inode cache.
+  rpc HashFiles(HashFilesRequest) returns (HashFilesResponse) {}
+
   // Configure remote CAS endpoint.
   //
   // This returns a string that can be used as instance_name to access the
@@ -153,15 +179,20 @@ service LocalContentAddressableStorage {
   // specified endpoints in further requests.
   rpc GetInstanceNameForRemotes(GetInstanceNameForRemotesRequest) returns 
(GetInstanceNameForRemotesResponse) {}
 
-  // Configure sandboxed clients.
-  //
-  // This returns a string that can be used as instance_name to access
-  // this service from clients running in the specified filesystem/mount
-  // namespace or chroot environment
-  rpc GetInstanceNameForNamespace(GetInstanceNameForNamespaceRequest) returns 
(GetInstanceNameForNamespaceResponse) {}
-
   // Query total space used by the local cache.
   rpc GetLocalDiskUsage(GetLocalDiskUsageRequest) returns 
(GetLocalDiskUsageResponse) {}
+
+
+  // Create a nested server restricted to a directory tree in the local 
filesystem.
+  //
+  // The nested server is started when the server receives the initial request
+  // and it is ready to be used on the initial (non-error) response from the
+  // server.
+  //
+  // The nested server will shut down when the server either receives an
+  // additional request (with all fields unset) or when the stream is closed.
+  // The server will send an additional response after cleanup is complete.
+  rpc NestedServer(stream NestedServerRequest) returns (stream 
NestedServerResponse) {}
 }
 
 // A request message for
@@ -306,6 +337,26 @@ message UploadTreeRequest {
 message UploadTreeResponse {
 }
 
+message RemoteApisSocketConfig {
+  // The instance of the execution system clients of the additional server
+  // socket will operate against.
+  string instance_name = 1;
+
+  // The path, relative to the staging directory, where a server socket should
+  // be created for access to the Remote Execution API.
+  string path = 2;
+
+  // If true, allow clients of the additional server socket to update the
+  // action cache of the specified instance.
+  bool action_cache_update_enabled = 3;
+
+  // A map from cloned instance name to base instance name.
+  // The nested server will clone each base instance and expose it
+  // under the corresponding cloned name (the key).
+  // Cannot be used together with instance_name.
+  map<string, string> instance_map = 4;
+}
+
 // A request message for
 // 
[LocalContentAddressableStorage.StageTree][build.buildgrid.v2.LocalContentAddressableStorage.StageTree].
 message StageTreeRequest {
@@ -344,9 +395,9 @@ message StageTreeRequest {
   // for access without risking corruption of files in the local cache.
   Credentials access_credentials = 4;
 
-  // The path, relative to the staging directory, where a server socket should
-  // be created for access to the Remote Execution API.
-  string remote_apis_socket_path = 7;
+  // Optional configuration of an additional server socket to be created for
+  // access to the Remote Execution API.
+  RemoteApisSocketConfig remote_apis_socket = 8;
 
   // The commands to run against the tree before it is cleaned.
   // Each command must be an absolute path to an executable which can be run as
@@ -382,6 +433,39 @@ message StageTreeResponse {
   string path = 1;
 }
 
+// A request message for
+// 
[LocalContentAddressableStorage.ExportFiles][build.buildgrid.v2.LocalContentAddressableStorage.ExportFiles].
+message ExportFilesRequest {
+  // The instance of the execution system to operate against. A server may
+  // support multiple instances of the execution system (with their own 
workers,
+  // storage, caches, etc.). The server MAY require use of this field to select
+  // between them in an implementation-defined fashion, otherwise it can be
+  // omitted.
+  string instance_name = 1;
+
+  // Output paths of individual files will be resolved relative to this path.
+  string path = 2;
+
+  // The individual files to export.
+  repeated build.bazel.remote.execution.v2.OutputFile output_files = 3;
+}
+
+// A response message for
+// 
[LocalContentAddressableStorage.ExportFiles][build.buildgrid.v2.LocalContentAddressableStorage.ExportFiles].
+message ExportFilesResponse {
+  // A response corresponding to a single blob that the client tried to export.
+  message Response {
+    // The path to which this response corresponds.
+    string path = 1;
+
+    // The result of attempting to export the file.
+    google.rpc.Status status = 2;
+  }
+
+  // The responses to the requests.
+  repeated Response responses = 1;
+}
+
 // A request message for
 // 
[LocalContentAddressableStorage.CaptureTree][build.buildgrid.v2.LocalContentAddressableStorage.CaptureTree].
 message CaptureTreeRequest {
@@ -432,6 +516,12 @@ message CaptureTreeRequest {
   // For example, a mask of `0222` will result in contents being captured as 
read-only.
   // This is only effective when the unix_mode property is captured.
   google.protobuf.UInt32Value unix_mode_mask = 9;
+
+  // If true, temporarily grant read permissions to files lacking read access
+  // during capture, then restore original permissions afterwards.
+  // The files/directories are expected to be owned by the same user running
+  // the capture.
+  bool allow_chmod_to_read = 10;
 }
 
 // A response message for
@@ -454,6 +544,66 @@ message CaptureTreeResponse {
     // [Tree][build.bazel.remote.execution.v2.Directory] proto containing the
     // directory's contents, if successful.
     build.bazel.remote.execution.v2.Digest root_directory_digest = 4;
+
+    // The accumulated total size of all blobs in the tree
+    int64 total_size_bytes = 5;
+  }
+
+  // The responses to the requests.
+  repeated Response responses = 1;
+}
+
+// A request message for
+// 
[LocalContentAddressableStorage.HashFiles][build.buildgrid.v2.LocalContentAddressableStorage.HashFiles].
+message HashFilesRequest {
+  // The instance of the execution system to operate against. A server may
+  // support multiple instances of the execution system (with their own 
workers,
+  // storage, caches, etc.). The server MAY require use of this field to select
+  // between them in an implementation-defined fashion, otherwise it can be
+  // omitted.
+  string instance_name = 1;
+
+  // The optional root path to restrict hash to a subtree.
+  // If specified, `path` will be resolved inside this root.
+  // No files outside the root will be hashed
+  string root = 3;
+
+  // The path(s) in the local filesystem to capture.
+  repeated string path = 2;
+
+  // The properties of path(s) in the local filesystem to capture.
+  repeated string node_properties = 4;
+
+  // The mask to apply to the files being captured.
+  // For example, a mask of `0222` will result in contents being captured as 
read-only.
+  // This is only effective when the unix_mode property is captured.
+  google.protobuf.UInt32Value unix_mode_mask = 5;
+
+  // If true, temporarily grant read permissions to files lacking read access
+  // during capture, then restore original permissions afterwards.
+  // The files/directories are expected to be owned by the same user running
+  // the capture.
+  bool allow_chmod_to_read = 6;
+}
+
+// A response message for
+// 
[LocalContentAddressableStorage.HashFiles][build.buildgrid.v2.LocalContentAddressableStorage.HashFiles].
+message HashFilesResponse {
+  // A response corresponding to a single blob that the client tried to upload.
+  message Response {
+    // The path to which this response corresponds.
+    string path = 1;
+
+    // The digest of the hashed file's content, if successful.
+    build.bazel.remote.execution.v2.Digest digest = 2;
+
+    // The result of attempting to hash the file and populate the inode cache.
+    google.rpc.Status status = 3;
+
+    // True if the hashed file was executable, false otherwise.
+    bool is_executable = 4;
+
+    build.bazel.remote.execution.v2.NodeProperties node_properties = 5;
   }
 
   // The responses to the requests.
@@ -500,6 +650,12 @@ message CaptureFilesRequest {
   // For example, a mask of `0222` will result in contents being captured as 
read-only.
   // This is only effective when the unix_mode property is captured.
   google.protobuf.UInt32Value unix_mode_mask = 8;
+
+  // If true, temporarily grant read permissions to files lacking read access
+  // during capture, then restore original permissions afterwards.
+  // The files/directories are expected to be owned by the same user running
+  // the capture.
+  bool allow_chmod_to_read = 9;
 }
 
 // A response message for
@@ -612,6 +768,10 @@ message Remote {
 
   // If set, don't upload any blobs, action results or assets to the remote.
   bool read_only = 12;
+
+  // Protocol to use for communication with the server, acceptable values
+  // are 'grpc' and 'http' (read-only). It defaults to 'grpc'.
+  string protocol = 13;
 }
 
 // A request message for
@@ -637,27 +797,6 @@ message GetInstanceNameForRemotesResponse {
 }
 
 
-// A request message for
-// 
[LocalContentAddressableStorage.GetInstanceNameForRemote][build.buildgrid.v2.LocalContentAddressableStorage.GetInstanceNameForNamespace].
-message GetInstanceNameForNamespaceRequest {
-  // The instance of the execution system to operate against. A server may
-  // support multiple instances of the execution system (with their own 
workers,
-  // storage, caches, etc.). The server MAY require use of this field to select
-  // between them in an implementation-defined fashion, otherwise it can be
-  // omitted.
-  string instance_name = 1;
-
-  // The root path of the mount namespace to restrict capture and staging.
-  // All paths in requests to the new instance will be resolved inside this 
root.
-  string root = 2;
-}
-
-// A response message for
-// 
[LocalContentAddressableStorage.GetInstanceNameForRemote][build.buildgrid.v2.LocalContentAddressableStorage.GetInstanceNameForNamespace].
-message GetInstanceNameForNamespaceResponse {
-  string instance_name = 1;
-}
-
 // A request message for
 // 
[LocalContentAddressableStorage.GetLocalDiskUsage][build.buildgrid.v2.LocalContentAddressableStorage.GetLocalDiskUsage].
 message GetLocalDiskUsageRequest {
@@ -672,3 +811,38 @@ message GetLocalDiskUsageResponse {
   // Disk quota for the local cache, in bytes. A value of 0 means no quota is 
set.
   int64 quota_bytes = 2;
 }
+
+
+// A request message for
+// 
[LocalContentAddressableStorage.NestedServer][build.buildgrid.v2.LocalContentAddressableStorage.NestedServer].
+message NestedServerRequest {
+  // The instance of the execution system to operate against. A server may
+  // support multiple instances of the execution system (with their own 
workers,
+  // storage, caches, etc.). The server MAY require use of this field to select
+  // between them in an implementation-defined fashion, otherwise it can be
+  // omitted.
+  string instance_name = 1;
+
+  // The path in the local filesystem that is used as filesystem root in
+  // requests sent to the nested server.
+  string path = 2;
+
+  message Credentials {
+    int64 uid = 1;
+    int64 gid = 2;
+  }
+
+  // The UNIX credentials of the processes that will access the nested server.
+  // This will be used to ensure that the socket has the right access
+  // permission.
+  Credentials access_credentials = 3;
+
+  // Configuration of the additional server socket to be created for access to
+  // the Remote Execution API.
+  RemoteApisSocketConfig remote_apis_socket = 4;
+}
+
+// A response message for
+// 
[LocalContentAddressableStorage.NestedServer][build.buildgrid.v2.LocalContentAddressableStorage.NestedServer].
+message NestedServerResponse {
+}
diff --git a/src/buildstream/_protos/build/buildgrid/local_cas_pb2.py 
b/src/buildstream/_protos/build/buildgrid/local_cas_pb2.py
index 30bebeae3..98cf4fbed 100644
--- a/src/buildstream/_protos/build/buildgrid/local_cas_pb2.py
+++ b/src/buildstream/_protos/build/buildgrid/local_cas_pb2.py
@@ -28,13 +28,15 @@ from google.protobuf import duration_pb2 as 
google_dot_protobuf_dot_duration__pb
 from google.protobuf import wrappers_pb2 as 
google_dot_protobuf_dot_wrappers__pb2
 
 
-DESCRIPTOR = 
_descriptor_pool.Default().AddSerializedFile(b'\n\x1f\x62uild/buildgrid/local_cas.proto\x12\x0f\x62uild.buildgrid\x1a\x36\x62uild/bazel/remote/execution/v2/remote_execution.proto\x1a\x17google/rpc/status.proto\x1a\x1egoogle/protobuf/duration.proto\x1a\x1egoogle/protobuf/wrappers.proto\"5\n\x1cGetLocalServerDetailsRequest\x12\x15\n\rinstance_name\x18\x01
 \x01(\t\"Q\n\x12LocalServerDetails\x12\x10\n\x08hostname\x18\x01 
\x01(\t\x12\x13\n\x0bprocess_uid\x18\x02 \x01(\x03\x12\x14 [...]
+DESCRIPTOR = 
_descriptor_pool.Default().AddSerializedFile(b'\n\x1f\x62uild/buildgrid/local_cas.proto\x12\x0f\x62uild.buildgrid\x1a\x36\x62uild/bazel/remote/execution/v2/remote_execution.proto\x1a\x17google/rpc/status.proto\x1a\x1egoogle/protobuf/duration.proto\x1a\x1egoogle/protobuf/wrappers.proto\"5\n\x1cGetLocalServerDetailsRequest\x12\x15\n\rinstance_name\x18\x01
 \x01(\t\"Q\n\x12LocalServerDetails\x12\x10\n\x08hostname\x18\x01 
\x01(\t\x12\x13\n\x0bprocess_uid\x18\x02 \x01(\x03\x12\x14 [...]
 
 _globals = globals()
 _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
 _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 
'build.buildgrid.local_cas_pb2', _globals)
 if not _descriptor._USE_C_DESCRIPTORS:
   DESCRIPTOR._loaded_options = None
+  _globals['_REMOTEAPISSOCKETCONFIG_INSTANCEMAPENTRY']._loaded_options = None
+  _globals['_REMOTEAPISSOCKETCONFIG_INSTANCEMAPENTRY']._serialized_options = 
b'8\001'
   _globals['_GETLOCALSERVERDETAILSREQUEST']._serialized_start=197
   _globals['_GETLOCALSERVERDETAILSREQUEST']._serialized_end=250
   _globals['_LOCALSERVERDETAILS']._serialized_start=252
@@ -59,44 +61,62 @@ if not _descriptor._USE_C_DESCRIPTORS:
   _globals['_UPLOADTREEREQUEST']._serialized_end=1237
   _globals['_UPLOADTREERESPONSE']._serialized_start=1239
   _globals['_UPLOADTREERESPONSE']._serialized_end=1259
-  _globals['_STAGETREEREQUEST']._serialized_start=1262
-  _globals['_STAGETREEREQUEST']._serialized_end=1683
-  _globals['_STAGETREEREQUEST_CREDENTIALS']._serialized_start=1588
-  _globals['_STAGETREEREQUEST_CREDENTIALS']._serialized_end=1627
-  _globals['_STAGETREEREQUEST_STAGINGMODE']._serialized_start=1629
-  _globals['_STAGETREEREQUEST_STAGINGMODE']._serialized_end=1683
-  _globals['_STAGETREERESPONSE']._serialized_start=1685
-  _globals['_STAGETREERESPONSE']._serialized_end=1718
-  _globals['_CAPTURETREEREQUEST']._serialized_start=1721
-  _globals['_CAPTURETREEREQUEST']._serialized_end=2037
-  _globals['_CAPTURETREERESPONSE']._serialized_start=2040
-  _globals['_CAPTURETREERESPONSE']._serialized_end=2324
-  _globals['_CAPTURETREERESPONSE_RESPONSE']._serialized_start=2130
-  _globals['_CAPTURETREERESPONSE_RESPONSE']._serialized_end=2324
-  _globals['_CAPTUREFILESREQUEST']._serialized_start=2327
-  _globals['_CAPTUREFILESREQUEST']._serialized_end=2547
-  _globals['_CAPTUREFILESRESPONSE']._serialized_start=2550
-  _globals['_CAPTUREFILESRESPONSE']._serialized_end=2862
-  _globals['_CAPTUREFILESRESPONSE_RESPONSE']._serialized_start=2642
-  _globals['_CAPTUREFILESRESPONSE_RESPONSE']._serialized_end=2862
-  _globals['_GETINSTANCENAMEFORREMOTEREQUEST']._serialized_start=2865
-  _globals['_GETINSTANCENAMEFORREMOTEREQUEST']._serialized_end=2996
-  _globals['_GETINSTANCENAMEFORREMOTERESPONSE']._serialized_start=2998
-  _globals['_GETINSTANCENAMEFORREMOTERESPONSE']._serialized_end=3055
-  _globals['_REMOTE']._serialized_start=3058
-  _globals['_REMOTE']._serialized_end=3447
-  _globals['_GETINSTANCENAMEFORREMOTESREQUEST']._serialized_start=3450
-  _globals['_GETINSTANCENAMEFORREMOTESREQUEST']._serialized_end=3707
-  _globals['_GETINSTANCENAMEFORREMOTESRESPONSE']._serialized_start=3709
-  _globals['_GETINSTANCENAMEFORREMOTESRESPONSE']._serialized_end=3767
-  _globals['_GETINSTANCENAMEFORNAMESPACEREQUEST']._serialized_start=3769
-  _globals['_GETINSTANCENAMEFORNAMESPACEREQUEST']._serialized_end=3842
-  _globals['_GETINSTANCENAMEFORNAMESPACERESPONSE']._serialized_start=3844
-  _globals['_GETINSTANCENAMEFORNAMESPACERESPONSE']._serialized_end=3904
-  _globals['_GETLOCALDISKUSAGEREQUEST']._serialized_start=3906
-  _globals['_GETLOCALDISKUSAGEREQUEST']._serialized_end=3932
-  _globals['_GETLOCALDISKUSAGERESPONSE']._serialized_start=3934
-  _globals['_GETLOCALDISKUSAGERESPONSE']._serialized_end=4002
-  _globals['_LOCALCONTENTADDRESSABLESTORAGE']._serialized_start=4005
-  _globals['_LOCALCONTENTADDRESSABLESTORAGE']._serialized_end=5341
+  _globals['_REMOTEAPISSOCKETCONFIG']._serialized_start=1262
+  _globals['_REMOTEAPISSOCKETCONFIG']._serialized_end=1492
+  _globals['_REMOTEAPISSOCKETCONFIG_INSTANCEMAPENTRY']._serialized_start=1442
+  _globals['_REMOTEAPISSOCKETCONFIG_INSTANCEMAPENTRY']._serialized_end=1492
+  _globals['_STAGETREEREQUEST']._serialized_start=1495
+  _globals['_STAGETREEREQUEST']._serialized_end=1952
+  _globals['_STAGETREEREQUEST_CREDENTIALS']._serialized_start=1857
+  _globals['_STAGETREEREQUEST_CREDENTIALS']._serialized_end=1896
+  _globals['_STAGETREEREQUEST_STAGINGMODE']._serialized_start=1898
+  _globals['_STAGETREEREQUEST_STAGINGMODE']._serialized_end=1952
+  _globals['_STAGETREERESPONSE']._serialized_start=1954
+  _globals['_STAGETREERESPONSE']._serialized_end=1987
+  _globals['_EXPORTFILESREQUEST']._serialized_start=1989
+  _globals['_EXPORTFILESREQUEST']._serialized_end=2113
+  _globals['_EXPORTFILESRESPONSE']._serialized_start=2116
+  _globals['_EXPORTFILESRESPONSE']._serialized_end=2265
+  _globals['_EXPORTFILESRESPONSE_RESPONSE']._serialized_start=2205
+  _globals['_EXPORTFILESRESPONSE_RESPONSE']._serialized_end=2265
+  _globals['_CAPTURETREEREQUEST']._serialized_start=2268
+  _globals['_CAPTURETREEREQUEST']._serialized_end=2613
+  _globals['_CAPTURETREERESPONSE']._serialized_start=2616
+  _globals['_CAPTURETREERESPONSE']._serialized_end=2926
+  _globals['_CAPTURETREERESPONSE_RESPONSE']._serialized_start=2706
+  _globals['_CAPTURETREERESPONSE_RESPONSE']._serialized_end=2926
+  _globals['_HASHFILESREQUEST']._serialized_start=2929
+  _globals['_HASHFILESREQUEST']._serialized_end=3106
+  _globals['_HASHFILESRESPONSE']._serialized_start=3109
+  _globals['_HASHFILESRESPONSE']._serialized_end=3409
+  _globals['_HASHFILESRESPONSE_RESPONSE']._serialized_start=3195
+  _globals['_HASHFILESRESPONSE_RESPONSE']._serialized_end=3409
+  _globals['_CAPTUREFILESREQUEST']._serialized_start=3412
+  _globals['_CAPTUREFILESREQUEST']._serialized_end=3661
+  _globals['_CAPTUREFILESRESPONSE']._serialized_start=3664
+  _globals['_CAPTUREFILESRESPONSE']._serialized_end=3976
+  _globals['_CAPTUREFILESRESPONSE_RESPONSE']._serialized_start=3756
+  _globals['_CAPTUREFILESRESPONSE_RESPONSE']._serialized_end=3976
+  _globals['_GETINSTANCENAMEFORREMOTEREQUEST']._serialized_start=3979
+  _globals['_GETINSTANCENAMEFORREMOTEREQUEST']._serialized_end=4110
+  _globals['_GETINSTANCENAMEFORREMOTERESPONSE']._serialized_start=4112
+  _globals['_GETINSTANCENAMEFORREMOTERESPONSE']._serialized_end=4169
+  _globals['_REMOTE']._serialized_start=4172
+  _globals['_REMOTE']._serialized_end=4579
+  _globals['_GETINSTANCENAMEFORREMOTESREQUEST']._serialized_start=4582
+  _globals['_GETINSTANCENAMEFORREMOTESREQUEST']._serialized_end=4839
+  _globals['_GETINSTANCENAMEFORREMOTESRESPONSE']._serialized_start=4841
+  _globals['_GETINSTANCENAMEFORREMOTESRESPONSE']._serialized_end=4899
+  _globals['_GETLOCALDISKUSAGEREQUEST']._serialized_start=4901
+  _globals['_GETLOCALDISKUSAGEREQUEST']._serialized_end=4927
+  _globals['_GETLOCALDISKUSAGERESPONSE']._serialized_start=4929
+  _globals['_GETLOCALDISKUSAGERESPONSE']._serialized_end=4997
+  _globals['_NESTEDSERVERREQUEST']._serialized_start=5000
+  _globals['_NESTEDSERVERREQUEST']._serialized_end=5246
+  _globals['_NESTEDSERVERREQUEST_CREDENTIALS']._serialized_start=1857
+  _globals['_NESTEDSERVERREQUEST_CREDENTIALS']._serialized_end=1896
+  _globals['_NESTEDSERVERRESPONSE']._serialized_start=5248
+  _globals['_NESTEDSERVERRESPONSE']._serialized_end=5270
+  _globals['_LOCALCONTENTADDRESSABLESTORAGE']._serialized_start=5273
+  _globals['_LOCALCONTENTADDRESSABLESTORAGE']._serialized_end=6745
 # @@protoc_insertion_point(module_scope)
diff --git a/src/buildstream/_protos/build/buildgrid/local_cas_pb2.pyi 
b/src/buildstream/_protos/build/buildgrid/local_cas_pb2.pyi
index 86a3bf22a..d4b7b8845 100644
--- a/src/buildstream/_protos/build/buildgrid/local_cas_pb2.pyi
+++ b/src/buildstream/_protos/build/buildgrid/local_cas_pb2.pyi
@@ -94,8 +94,27 @@ class UploadTreeResponse(_message.Message):
     __slots__ = ()
     def __init__(self) -> None: ...
 
+class RemoteApisSocketConfig(_message.Message):
+    __slots__ = ("instance_name", "path", "action_cache_update_enabled", 
"instance_map")
+    class InstanceMapEntry(_message.Message):
+        __slots__ = ("key", "value")
+        KEY_FIELD_NUMBER: _ClassVar[int]
+        VALUE_FIELD_NUMBER: _ClassVar[int]
+        key: str
+        value: str
+        def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = 
...) -> None: ...
+    INSTANCE_NAME_FIELD_NUMBER: _ClassVar[int]
+    PATH_FIELD_NUMBER: _ClassVar[int]
+    ACTION_CACHE_UPDATE_ENABLED_FIELD_NUMBER: _ClassVar[int]
+    INSTANCE_MAP_FIELD_NUMBER: _ClassVar[int]
+    instance_name: str
+    path: str
+    action_cache_update_enabled: bool
+    instance_map: _containers.ScalarMap[str, str]
+    def __init__(self, instance_name: _Optional[str] = ..., path: 
_Optional[str] = ..., action_cache_update_enabled: bool = ..., instance_map: 
_Optional[_Mapping[str, str]] = ...) -> None: ...
+
 class StageTreeRequest(_message.Message):
-    __slots__ = ("instance_name", "root_digest", "path", "access_credentials", 
"remote_apis_socket_path", "pre_unstage_commands", "staging_mode")
+    __slots__ = ("instance_name", "root_digest", "path", "access_credentials", 
"remote_apis_socket", "pre_unstage_commands", "staging_mode")
     class StagingMode(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
         __slots__ = ()
         DEFAULT: _ClassVar[StageTreeRequest.StagingMode]
@@ -115,17 +134,17 @@ class StageTreeRequest(_message.Message):
     ROOT_DIGEST_FIELD_NUMBER: _ClassVar[int]
     PATH_FIELD_NUMBER: _ClassVar[int]
     ACCESS_CREDENTIALS_FIELD_NUMBER: _ClassVar[int]
-    REMOTE_APIS_SOCKET_PATH_FIELD_NUMBER: _ClassVar[int]
+    REMOTE_APIS_SOCKET_FIELD_NUMBER: _ClassVar[int]
     PRE_UNSTAGE_COMMANDS_FIELD_NUMBER: _ClassVar[int]
     STAGING_MODE_FIELD_NUMBER: _ClassVar[int]
     instance_name: str
     root_digest: _remote_execution_pb2.Digest
     path: str
     access_credentials: StageTreeRequest.Credentials
-    remote_apis_socket_path: str
+    remote_apis_socket: RemoteApisSocketConfig
     pre_unstage_commands: _containers.RepeatedScalarFieldContainer[str]
     staging_mode: StageTreeRequest.StagingMode
-    def __init__(self, instance_name: _Optional[str] = ..., root_digest: 
_Optional[_Union[_remote_execution_pb2.Digest, _Mapping]] = ..., path: 
_Optional[str] = ..., access_credentials: 
_Optional[_Union[StageTreeRequest.Credentials, _Mapping]] = ..., 
remote_apis_socket_path: _Optional[str] = ..., pre_unstage_commands: 
_Optional[_Iterable[str]] = ..., staging_mode: 
_Optional[_Union[StageTreeRequest.StagingMode, str]] = ...) -> None: ...
+    def __init__(self, instance_name: _Optional[str] = ..., root_digest: 
_Optional[_Union[_remote_execution_pb2.Digest, _Mapping]] = ..., path: 
_Optional[str] = ..., access_credentials: 
_Optional[_Union[StageTreeRequest.Credentials, _Mapping]] = ..., 
remote_apis_socket: _Optional[_Union[RemoteApisSocketConfig, _Mapping]] = ..., 
pre_unstage_commands: _Optional[_Iterable[str]] = ..., staging_mode: 
_Optional[_Union[StageTreeRequest.StagingMode, str]] = ...) -> None: ...
 
 class StageTreeResponse(_message.Message):
     __slots__ = ("path",)
@@ -133,8 +152,31 @@ class StageTreeResponse(_message.Message):
     path: str
     def __init__(self, path: _Optional[str] = ...) -> None: ...
 
+class ExportFilesRequest(_message.Message):
+    __slots__ = ("instance_name", "path", "output_files")
+    INSTANCE_NAME_FIELD_NUMBER: _ClassVar[int]
+    PATH_FIELD_NUMBER: _ClassVar[int]
+    OUTPUT_FILES_FIELD_NUMBER: _ClassVar[int]
+    instance_name: str
+    path: str
+    output_files: 
_containers.RepeatedCompositeFieldContainer[_remote_execution_pb2.OutputFile]
+    def __init__(self, instance_name: _Optional[str] = ..., path: 
_Optional[str] = ..., output_files: 
_Optional[_Iterable[_Union[_remote_execution_pb2.OutputFile, _Mapping]]] = ...) 
-> None: ...
+
+class ExportFilesResponse(_message.Message):
+    __slots__ = ("responses",)
+    class Response(_message.Message):
+        __slots__ = ("path", "status")
+        PATH_FIELD_NUMBER: _ClassVar[int]
+        STATUS_FIELD_NUMBER: _ClassVar[int]
+        path: str
+        status: _status_pb2.Status
+        def __init__(self, path: _Optional[str] = ..., status: 
_Optional[_Union[_status_pb2.Status, _Mapping]] = ...) -> None: ...
+    RESPONSES_FIELD_NUMBER: _ClassVar[int]
+    responses: 
_containers.RepeatedCompositeFieldContainer[ExportFilesResponse.Response]
+    def __init__(self, responses: 
_Optional[_Iterable[_Union[ExportFilesResponse.Response, _Mapping]]] = ...) -> 
None: ...
+
 class CaptureTreeRequest(_message.Message):
-    __slots__ = ("instance_name", "root", "path", "bypass_local_cache", 
"node_properties", "move_files", "output_directory_format", "skip_upload", 
"unix_mode_mask")
+    __slots__ = ("instance_name", "root", "path", "bypass_local_cache", 
"node_properties", "move_files", "output_directory_format", "skip_upload", 
"unix_mode_mask", "allow_chmod_to_read")
     INSTANCE_NAME_FIELD_NUMBER: _ClassVar[int]
     ROOT_FIELD_NUMBER: _ClassVar[int]
     PATH_FIELD_NUMBER: _ClassVar[int]
@@ -144,6 +186,7 @@ class CaptureTreeRequest(_message.Message):
     OUTPUT_DIRECTORY_FORMAT_FIELD_NUMBER: _ClassVar[int]
     SKIP_UPLOAD_FIELD_NUMBER: _ClassVar[int]
     UNIX_MODE_MASK_FIELD_NUMBER: _ClassVar[int]
+    ALLOW_CHMOD_TO_READ_FIELD_NUMBER: _ClassVar[int]
     instance_name: str
     root: str
     path: _containers.RepeatedScalarFieldContainer[str]
@@ -153,27 +196,65 @@ class CaptureTreeRequest(_message.Message):
     output_directory_format: 
_remote_execution_pb2.Command.OutputDirectoryFormat
     skip_upload: bool
     unix_mode_mask: _wrappers_pb2.UInt32Value
-    def __init__(self, instance_name: _Optional[str] = ..., root: 
_Optional[str] = ..., path: _Optional[_Iterable[str]] = ..., 
bypass_local_cache: bool = ..., node_properties: _Optional[_Iterable[str]] = 
..., move_files: bool = ..., output_directory_format: 
_Optional[_Union[_remote_execution_pb2.Command.OutputDirectoryFormat, str]] = 
..., skip_upload: bool = ..., unix_mode_mask: 
_Optional[_Union[_wrappers_pb2.UInt32Value, _Mapping]] = ...) -> None: ...
+    allow_chmod_to_read: bool
+    def __init__(self, instance_name: _Optional[str] = ..., root: 
_Optional[str] = ..., path: _Optional[_Iterable[str]] = ..., 
bypass_local_cache: bool = ..., node_properties: _Optional[_Iterable[str]] = 
..., move_files: bool = ..., output_directory_format: 
_Optional[_Union[_remote_execution_pb2.Command.OutputDirectoryFormat, str]] = 
..., skip_upload: bool = ..., unix_mode_mask: 
_Optional[_Union[_wrappers_pb2.UInt32Value, _Mapping]] = ..., 
allow_chmod_to_read: bool = ...) -> None: ...
 
 class CaptureTreeResponse(_message.Message):
     __slots__ = ("responses",)
     class Response(_message.Message):
-        __slots__ = ("path", "tree_digest", "status", "root_directory_digest")
+        __slots__ = ("path", "tree_digest", "status", "root_directory_digest", 
"total_size_bytes")
         PATH_FIELD_NUMBER: _ClassVar[int]
         TREE_DIGEST_FIELD_NUMBER: _ClassVar[int]
         STATUS_FIELD_NUMBER: _ClassVar[int]
         ROOT_DIRECTORY_DIGEST_FIELD_NUMBER: _ClassVar[int]
+        TOTAL_SIZE_BYTES_FIELD_NUMBER: _ClassVar[int]
         path: str
         tree_digest: _remote_execution_pb2.Digest
         status: _status_pb2.Status
         root_directory_digest: _remote_execution_pb2.Digest
-        def __init__(self, path: _Optional[str] = ..., tree_digest: 
_Optional[_Union[_remote_execution_pb2.Digest, _Mapping]] = ..., status: 
_Optional[_Union[_status_pb2.Status, _Mapping]] = ..., root_directory_digest: 
_Optional[_Union[_remote_execution_pb2.Digest, _Mapping]] = ...) -> None: ...
+        total_size_bytes: int
+        def __init__(self, path: _Optional[str] = ..., tree_digest: 
_Optional[_Union[_remote_execution_pb2.Digest, _Mapping]] = ..., status: 
_Optional[_Union[_status_pb2.Status, _Mapping]] = ..., root_directory_digest: 
_Optional[_Union[_remote_execution_pb2.Digest, _Mapping]] = ..., 
total_size_bytes: _Optional[int] = ...) -> None: ...
     RESPONSES_FIELD_NUMBER: _ClassVar[int]
     responses: 
_containers.RepeatedCompositeFieldContainer[CaptureTreeResponse.Response]
     def __init__(self, responses: 
_Optional[_Iterable[_Union[CaptureTreeResponse.Response, _Mapping]]] = ...) -> 
None: ...
 
+class HashFilesRequest(_message.Message):
+    __slots__ = ("instance_name", "root", "path", "node_properties", 
"unix_mode_mask", "allow_chmod_to_read")
+    INSTANCE_NAME_FIELD_NUMBER: _ClassVar[int]
+    ROOT_FIELD_NUMBER: _ClassVar[int]
+    PATH_FIELD_NUMBER: _ClassVar[int]
+    NODE_PROPERTIES_FIELD_NUMBER: _ClassVar[int]
+    UNIX_MODE_MASK_FIELD_NUMBER: _ClassVar[int]
+    ALLOW_CHMOD_TO_READ_FIELD_NUMBER: _ClassVar[int]
+    instance_name: str
+    root: str
+    path: _containers.RepeatedScalarFieldContainer[str]
+    node_properties: _containers.RepeatedScalarFieldContainer[str]
+    unix_mode_mask: _wrappers_pb2.UInt32Value
+    allow_chmod_to_read: bool
+    def __init__(self, instance_name: _Optional[str] = ..., root: 
_Optional[str] = ..., path: _Optional[_Iterable[str]] = ..., node_properties: 
_Optional[_Iterable[str]] = ..., unix_mode_mask: 
_Optional[_Union[_wrappers_pb2.UInt32Value, _Mapping]] = ..., 
allow_chmod_to_read: bool = ...) -> None: ...
+
+class HashFilesResponse(_message.Message):
+    __slots__ = ("responses",)
+    class Response(_message.Message):
+        __slots__ = ("path", "digest", "status", "is_executable", 
"node_properties")
+        PATH_FIELD_NUMBER: _ClassVar[int]
+        DIGEST_FIELD_NUMBER: _ClassVar[int]
+        STATUS_FIELD_NUMBER: _ClassVar[int]
+        IS_EXECUTABLE_FIELD_NUMBER: _ClassVar[int]
+        NODE_PROPERTIES_FIELD_NUMBER: _ClassVar[int]
+        path: str
+        digest: _remote_execution_pb2.Digest
+        status: _status_pb2.Status
+        is_executable: bool
+        node_properties: _remote_execution_pb2.NodeProperties
+        def __init__(self, path: _Optional[str] = ..., digest: 
_Optional[_Union[_remote_execution_pb2.Digest, _Mapping]] = ..., status: 
_Optional[_Union[_status_pb2.Status, _Mapping]] = ..., is_executable: bool = 
..., node_properties: _Optional[_Union[_remote_execution_pb2.NodeProperties, 
_Mapping]] = ...) -> None: ...
+    RESPONSES_FIELD_NUMBER: _ClassVar[int]
+    responses: 
_containers.RepeatedCompositeFieldContainer[HashFilesResponse.Response]
+    def __init__(self, responses: 
_Optional[_Iterable[_Union[HashFilesResponse.Response, _Mapping]]] = ...) -> 
None: ...
+
 class CaptureFilesRequest(_message.Message):
-    __slots__ = ("instance_name", "root", "path", "bypass_local_cache", 
"node_properties", "move_files", "skip_upload", "unix_mode_mask")
+    __slots__ = ("instance_name", "root", "path", "bypass_local_cache", 
"node_properties", "move_files", "skip_upload", "unix_mode_mask", 
"allow_chmod_to_read")
     INSTANCE_NAME_FIELD_NUMBER: _ClassVar[int]
     ROOT_FIELD_NUMBER: _ClassVar[int]
     PATH_FIELD_NUMBER: _ClassVar[int]
@@ -182,6 +263,7 @@ class CaptureFilesRequest(_message.Message):
     MOVE_FILES_FIELD_NUMBER: _ClassVar[int]
     SKIP_UPLOAD_FIELD_NUMBER: _ClassVar[int]
     UNIX_MODE_MASK_FIELD_NUMBER: _ClassVar[int]
+    ALLOW_CHMOD_TO_READ_FIELD_NUMBER: _ClassVar[int]
     instance_name: str
     root: str
     path: _containers.RepeatedScalarFieldContainer[str]
@@ -190,7 +272,8 @@ class CaptureFilesRequest(_message.Message):
     move_files: bool
     skip_upload: bool
     unix_mode_mask: _wrappers_pb2.UInt32Value
-    def __init__(self, instance_name: _Optional[str] = ..., root: 
_Optional[str] = ..., path: _Optional[_Iterable[str]] = ..., 
bypass_local_cache: bool = ..., node_properties: _Optional[_Iterable[str]] = 
..., move_files: bool = ..., skip_upload: bool = ..., unix_mode_mask: 
_Optional[_Union[_wrappers_pb2.UInt32Value, _Mapping]] = ...) -> None: ...
+    allow_chmod_to_read: bool
+    def __init__(self, instance_name: _Optional[str] = ..., root: 
_Optional[str] = ..., path: _Optional[_Iterable[str]] = ..., 
bypass_local_cache: bool = ..., node_properties: _Optional[_Iterable[str]] = 
..., move_files: bool = ..., skip_upload: bool = ..., unix_mode_mask: 
_Optional[_Union[_wrappers_pb2.UInt32Value, _Mapping]] = ..., 
allow_chmod_to_read: bool = ...) -> None: ...
 
 class CaptureFilesResponse(_message.Message):
     __slots__ = ("responses",)
@@ -232,7 +315,7 @@ class GetInstanceNameForRemoteResponse(_message.Message):
     def __init__(self, instance_name: _Optional[str] = ...) -> None: ...
 
 class Remote(_message.Message):
-    __slots__ = ("url", "instance_name", "server_cert", "client_key", 
"client_cert", "access_token_path", "access_token_reload_interval", 
"keepalive_time", "retry_limit", "retry_delay", "request_timeout", "read_only")
+    __slots__ = ("url", "instance_name", "server_cert", "client_key", 
"client_cert", "access_token_path", "access_token_reload_interval", 
"keepalive_time", "retry_limit", "retry_delay", "request_timeout", "read_only", 
"protocol")
     URL_FIELD_NUMBER: _ClassVar[int]
     INSTANCE_NAME_FIELD_NUMBER: _ClassVar[int]
     SERVER_CERT_FIELD_NUMBER: _ClassVar[int]
@@ -245,6 +328,7 @@ class Remote(_message.Message):
     RETRY_DELAY_FIELD_NUMBER: _ClassVar[int]
     REQUEST_TIMEOUT_FIELD_NUMBER: _ClassVar[int]
     READ_ONLY_FIELD_NUMBER: _ClassVar[int]
+    PROTOCOL_FIELD_NUMBER: _ClassVar[int]
     url: str
     instance_name: str
     server_cert: bytes
@@ -257,7 +341,8 @@ class Remote(_message.Message):
     retry_delay: _duration_pb2.Duration
     request_timeout: _duration_pb2.Duration
     read_only: bool
-    def __init__(self, url: _Optional[str] = ..., instance_name: 
_Optional[str] = ..., server_cert: _Optional[bytes] = ..., client_key: 
_Optional[bytes] = ..., client_cert: _Optional[bytes] = ..., access_token_path: 
_Optional[str] = ..., access_token_reload_interval: 
_Optional[_Union[_duration_pb2.Duration, _Mapping]] = ..., keepalive_time: 
_Optional[_Union[_duration_pb2.Duration, _Mapping]] = ..., retry_limit: 
_Optional[int] = ..., retry_delay: _Optional[_Union[_duration_pb2.Duration, _ 
[...]
+    protocol: str
+    def __init__(self, url: _Optional[str] = ..., instance_name: 
_Optional[str] = ..., server_cert: _Optional[bytes] = ..., client_key: 
_Optional[bytes] = ..., client_cert: _Optional[bytes] = ..., access_token_path: 
_Optional[str] = ..., access_token_reload_interval: 
_Optional[_Union[_duration_pb2.Duration, _Mapping]] = ..., keepalive_time: 
_Optional[_Union[_duration_pb2.Duration, _Mapping]] = ..., retry_limit: 
_Optional[int] = ..., retry_delay: _Optional[_Union[_duration_pb2.Duration, _ 
[...]
 
 class GetInstanceNameForRemotesRequest(_message.Message):
     __slots__ = ("instance_name", "content_addressable_storage", 
"remote_asset", "action_cache", "execution")
@@ -279,20 +364,6 @@ class GetInstanceNameForRemotesResponse(_message.Message):
     instance_name: str
     def __init__(self, instance_name: _Optional[str] = ...) -> None: ...
 
-class GetInstanceNameForNamespaceRequest(_message.Message):
-    __slots__ = ("instance_name", "root")
-    INSTANCE_NAME_FIELD_NUMBER: _ClassVar[int]
-    ROOT_FIELD_NUMBER: _ClassVar[int]
-    instance_name: str
-    root: str
-    def __init__(self, instance_name: _Optional[str] = ..., root: 
_Optional[str] = ...) -> None: ...
-
-class GetInstanceNameForNamespaceResponse(_message.Message):
-    __slots__ = ("instance_name",)
-    INSTANCE_NAME_FIELD_NUMBER: _ClassVar[int]
-    instance_name: str
-    def __init__(self, instance_name: _Optional[str] = ...) -> None: ...
-
 class GetLocalDiskUsageRequest(_message.Message):
     __slots__ = ()
     def __init__(self) -> None: ...
@@ -304,3 +375,26 @@ class GetLocalDiskUsageResponse(_message.Message):
     size_bytes: int
     quota_bytes: int
     def __init__(self, size_bytes: _Optional[int] = ..., quota_bytes: 
_Optional[int] = ...) -> None: ...
+
+class NestedServerRequest(_message.Message):
+    __slots__ = ("instance_name", "path", "access_credentials", 
"remote_apis_socket")
+    class Credentials(_message.Message):
+        __slots__ = ("uid", "gid")
+        UID_FIELD_NUMBER: _ClassVar[int]
+        GID_FIELD_NUMBER: _ClassVar[int]
+        uid: int
+        gid: int
+        def __init__(self, uid: _Optional[int] = ..., gid: _Optional[int] = 
...) -> None: ...
+    INSTANCE_NAME_FIELD_NUMBER: _ClassVar[int]
+    PATH_FIELD_NUMBER: _ClassVar[int]
+    ACCESS_CREDENTIALS_FIELD_NUMBER: _ClassVar[int]
+    REMOTE_APIS_SOCKET_FIELD_NUMBER: _ClassVar[int]
+    instance_name: str
+    path: str
+    access_credentials: NestedServerRequest.Credentials
+    remote_apis_socket: RemoteApisSocketConfig
+    def __init__(self, instance_name: _Optional[str] = ..., path: 
_Optional[str] = ..., access_credentials: 
_Optional[_Union[NestedServerRequest.Credentials, _Mapping]] = ..., 
remote_apis_socket: _Optional[_Union[RemoteApisSocketConfig, _Mapping]] = ...) 
-> None: ...
+
+class NestedServerResponse(_message.Message):
+    __slots__ = ()
+    def __init__(self) -> None: ...
diff --git a/src/buildstream/_protos/build/buildgrid/local_cas_pb2_grpc.py 
b/src/buildstream/_protos/build/buildgrid/local_cas_pb2_grpc.py
index 6c0a69e7e..a66005009 100644
--- a/src/buildstream/_protos/build/buildgrid/local_cas_pb2_grpc.py
+++ b/src/buildstream/_protos/build/buildgrid/local_cas_pb2_grpc.py
@@ -64,6 +64,11 @@ class LocalContentAddressableStorageStub(object):
                 
request_serializer=build_dot_buildgrid_dot_local__cas__pb2.StageTreeRequest.SerializeToString,
                 
response_deserializer=build_dot_buildgrid_dot_local__cas__pb2.StageTreeResponse.FromString,
                 _registered_method=True)
+        self.ExportFiles = channel.unary_unary(
+                '/build.buildgrid.LocalContentAddressableStorage/ExportFiles',
+                
request_serializer=build_dot_buildgrid_dot_local__cas__pb2.ExportFilesRequest.SerializeToString,
+                
response_deserializer=build_dot_buildgrid_dot_local__cas__pb2.ExportFilesResponse.FromString,
+                _registered_method=True)
         self.CaptureTree = channel.unary_unary(
                 '/build.buildgrid.LocalContentAddressableStorage/CaptureTree',
                 
request_serializer=build_dot_buildgrid_dot_local__cas__pb2.CaptureTreeRequest.SerializeToString,
@@ -74,6 +79,11 @@ class LocalContentAddressableStorageStub(object):
                 
request_serializer=build_dot_buildgrid_dot_local__cas__pb2.CaptureFilesRequest.SerializeToString,
                 
response_deserializer=build_dot_buildgrid_dot_local__cas__pb2.CaptureFilesResponse.FromString,
                 _registered_method=True)
+        self.HashFiles = channel.unary_unary(
+                '/build.buildgrid.LocalContentAddressableStorage/HashFiles',
+                
request_serializer=build_dot_buildgrid_dot_local__cas__pb2.HashFilesRequest.SerializeToString,
+                
response_deserializer=build_dot_buildgrid_dot_local__cas__pb2.HashFilesResponse.FromString,
+                _registered_method=True)
         self.GetInstanceNameForRemote = channel.unary_unary(
                 
'/build.buildgrid.LocalContentAddressableStorage/GetInstanceNameForRemote',
                 
request_serializer=build_dot_buildgrid_dot_local__cas__pb2.GetInstanceNameForRemoteRequest.SerializeToString,
@@ -84,16 +94,16 @@ class LocalContentAddressableStorageStub(object):
                 
request_serializer=build_dot_buildgrid_dot_local__cas__pb2.GetInstanceNameForRemotesRequest.SerializeToString,
                 
response_deserializer=build_dot_buildgrid_dot_local__cas__pb2.GetInstanceNameForRemotesResponse.FromString,
                 _registered_method=True)
-        self.GetInstanceNameForNamespace = channel.unary_unary(
-                
'/build.buildgrid.LocalContentAddressableStorage/GetInstanceNameForNamespace',
-                
request_serializer=build_dot_buildgrid_dot_local__cas__pb2.GetInstanceNameForNamespaceRequest.SerializeToString,
-                
response_deserializer=build_dot_buildgrid_dot_local__cas__pb2.GetInstanceNameForNamespaceResponse.FromString,
-                _registered_method=True)
         self.GetLocalDiskUsage = channel.unary_unary(
                 
'/build.buildgrid.LocalContentAddressableStorage/GetLocalDiskUsage',
                 
request_serializer=build_dot_buildgrid_dot_local__cas__pb2.GetLocalDiskUsageRequest.SerializeToString,
                 
response_deserializer=build_dot_buildgrid_dot_local__cas__pb2.GetLocalDiskUsageResponse.FromString,
                 _registered_method=True)
+        self.NestedServer = channel.stream_stream(
+                '/build.buildgrid.LocalContentAddressableStorage/NestedServer',
+                
request_serializer=build_dot_buildgrid_dot_local__cas__pb2.NestedServerRequest.SerializeToString,
+                
response_deserializer=build_dot_buildgrid_dot_local__cas__pb2.NestedServerResponse.FromString,
+                _registered_method=True)
 
 
 class LocalContentAddressableStorageServicer(object):
@@ -201,6 +211,25 @@ class LocalContentAddressableStorageServicer(object):
         context.set_details('Method not implemented!')
         raise NotImplementedError('Method not implemented!')
 
+    def ExportFiles(self, request, context):
+        """Export files to a user specified location on the local filesystem.
+
+        The way in which files are populated is implementation defined,
+        however, the server will guarantee that mutations to these files
+        will not corrupt the local cache.
+
+        This does not create any directories. The client needs to ensure that
+        the parent directory of each output file exists and is writable.
+
+        Missing blobs are fetched, if a CAS remote is configured.
+
+        Errors:
+        * `FAILED_PRECONDITION`: The destination directory does not exist.
+        """
+        context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+        context.set_details('Method not implemented!')
+        raise NotImplementedError('Method not implemented!')
+
     def CaptureTree(self, request, context):
         """Capture a directory tree from the local filesystem.
 
@@ -247,6 +276,21 @@ class LocalContentAddressableStorageServicer(object):
         context.set_details('Method not implemented!')
         raise NotImplementedError('Method not implemented!')
 
+    def HashFiles(self, request, context):
+        """Hash files from the local filesystem.
+
+        This asks the local CAS server to hash the file on behalf of the
+        client and populate the inode cache for efficent subqeuent hashing
+        of the same file.
+
+        Unlike CaptureFiles which always store the file content either to
+        local storage or remote CAS or both. HashFiles only return the hash
+        and populate the inode cache.
+        """
+        context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+        context.set_details('Method not implemented!')
+        raise NotImplementedError('Method not implemented!')
+
     def GetInstanceNameForRemote(self, request, context):
         """Configure remote CAS endpoint.
 
@@ -270,19 +314,23 @@ class LocalContentAddressableStorageServicer(object):
         context.set_details('Method not implemented!')
         raise NotImplementedError('Method not implemented!')
 
-    def GetInstanceNameForNamespace(self, request, context):
-        """Configure sandboxed clients.
-
-        This returns a string that can be used as instance_name to access
-        this service from clients running in the specified filesystem/mount
-        namespace or chroot environment
+    def GetLocalDiskUsage(self, request, context):
+        """Query total space used by the local cache.
         """
         context.set_code(grpc.StatusCode.UNIMPLEMENTED)
         context.set_details('Method not implemented!')
         raise NotImplementedError('Method not implemented!')
 
-    def GetLocalDiskUsage(self, request, context):
-        """Query total space used by the local cache.
+    def NestedServer(self, request_iterator, context):
+        """Create a nested server restricted to a directory tree in the local 
filesystem.
+
+        The nested server is started when the server receives the initial 
request
+        and it is ready to be used on the initial (non-error) response from the
+        server.
+
+        The nested server will shut down when the server either receives an
+        additional request (with all fields unset) or when the stream is 
closed.
+        The server will send an additional response after cleanup is complete.
         """
         context.set_code(grpc.StatusCode.UNIMPLEMENTED)
         context.set_details('Method not implemented!')
@@ -321,6 +369,11 @@ def 
add_LocalContentAddressableStorageServicer_to_server(servicer, server):
                     
request_deserializer=build_dot_buildgrid_dot_local__cas__pb2.StageTreeRequest.FromString,
                     
response_serializer=build_dot_buildgrid_dot_local__cas__pb2.StageTreeResponse.SerializeToString,
             ),
+            'ExportFiles': grpc.unary_unary_rpc_method_handler(
+                    servicer.ExportFiles,
+                    
request_deserializer=build_dot_buildgrid_dot_local__cas__pb2.ExportFilesRequest.FromString,
+                    
response_serializer=build_dot_buildgrid_dot_local__cas__pb2.ExportFilesResponse.SerializeToString,
+            ),
             'CaptureTree': grpc.unary_unary_rpc_method_handler(
                     servicer.CaptureTree,
                     
request_deserializer=build_dot_buildgrid_dot_local__cas__pb2.CaptureTreeRequest.FromString,
@@ -331,6 +384,11 @@ def 
add_LocalContentAddressableStorageServicer_to_server(servicer, server):
                     
request_deserializer=build_dot_buildgrid_dot_local__cas__pb2.CaptureFilesRequest.FromString,
                     
response_serializer=build_dot_buildgrid_dot_local__cas__pb2.CaptureFilesResponse.SerializeToString,
             ),
+            'HashFiles': grpc.unary_unary_rpc_method_handler(
+                    servicer.HashFiles,
+                    
request_deserializer=build_dot_buildgrid_dot_local__cas__pb2.HashFilesRequest.FromString,
+                    
response_serializer=build_dot_buildgrid_dot_local__cas__pb2.HashFilesResponse.SerializeToString,
+            ),
             'GetInstanceNameForRemote': grpc.unary_unary_rpc_method_handler(
                     servicer.GetInstanceNameForRemote,
                     
request_deserializer=build_dot_buildgrid_dot_local__cas__pb2.GetInstanceNameForRemoteRequest.FromString,
@@ -341,16 +399,16 @@ def 
add_LocalContentAddressableStorageServicer_to_server(servicer, server):
                     
request_deserializer=build_dot_buildgrid_dot_local__cas__pb2.GetInstanceNameForRemotesRequest.FromString,
                     
response_serializer=build_dot_buildgrid_dot_local__cas__pb2.GetInstanceNameForRemotesResponse.SerializeToString,
             ),
-            'GetInstanceNameForNamespace': grpc.unary_unary_rpc_method_handler(
-                    servicer.GetInstanceNameForNamespace,
-                    
request_deserializer=build_dot_buildgrid_dot_local__cas__pb2.GetInstanceNameForNamespaceRequest.FromString,
-                    
response_serializer=build_dot_buildgrid_dot_local__cas__pb2.GetInstanceNameForNamespaceResponse.SerializeToString,
-            ),
             'GetLocalDiskUsage': grpc.unary_unary_rpc_method_handler(
                     servicer.GetLocalDiskUsage,
                     
request_deserializer=build_dot_buildgrid_dot_local__cas__pb2.GetLocalDiskUsageRequest.FromString,
                     
response_serializer=build_dot_buildgrid_dot_local__cas__pb2.GetLocalDiskUsageResponse.SerializeToString,
             ),
+            'NestedServer': grpc.stream_stream_rpc_method_handler(
+                    servicer.NestedServer,
+                    
request_deserializer=build_dot_buildgrid_dot_local__cas__pb2.NestedServerRequest.FromString,
+                    
response_serializer=build_dot_buildgrid_dot_local__cas__pb2.NestedServerResponse.SerializeToString,
+            ),
     }
     generic_handler = grpc.method_handlers_generic_handler(
             'build.buildgrid.LocalContentAddressableStorage', 
rpc_method_handlers)
@@ -524,6 +582,33 @@ class LocalContentAddressableStorage(object):
             metadata,
             _registered_method=True)
 
+    @staticmethod
+    def ExportFiles(request,
+            target,
+            options=(),
+            channel_credentials=None,
+            call_credentials=None,
+            insecure=False,
+            compression=None,
+            wait_for_ready=None,
+            timeout=None,
+            metadata=None):
+        return grpc.experimental.unary_unary(
+            request,
+            target,
+            '/build.buildgrid.LocalContentAddressableStorage/ExportFiles',
+            
build_dot_buildgrid_dot_local__cas__pb2.ExportFilesRequest.SerializeToString,
+            
build_dot_buildgrid_dot_local__cas__pb2.ExportFilesResponse.FromString,
+            options,
+            channel_credentials,
+            insecure,
+            call_credentials,
+            compression,
+            wait_for_ready,
+            timeout,
+            metadata,
+            _registered_method=True)
+
     @staticmethod
     def CaptureTree(request,
             target,
@@ -578,6 +663,33 @@ class LocalContentAddressableStorage(object):
             metadata,
             _registered_method=True)
 
+    @staticmethod
+    def HashFiles(request,
+            target,
+            options=(),
+            channel_credentials=None,
+            call_credentials=None,
+            insecure=False,
+            compression=None,
+            wait_for_ready=None,
+            timeout=None,
+            metadata=None):
+        return grpc.experimental.unary_unary(
+            request,
+            target,
+            '/build.buildgrid.LocalContentAddressableStorage/HashFiles',
+            
build_dot_buildgrid_dot_local__cas__pb2.HashFilesRequest.SerializeToString,
+            
build_dot_buildgrid_dot_local__cas__pb2.HashFilesResponse.FromString,
+            options,
+            channel_credentials,
+            insecure,
+            call_credentials,
+            compression,
+            wait_for_ready,
+            timeout,
+            metadata,
+            _registered_method=True)
+
     @staticmethod
     def GetInstanceNameForRemote(request,
             target,
@@ -633,7 +745,7 @@ class LocalContentAddressableStorage(object):
             _registered_method=True)
 
     @staticmethod
-    def GetInstanceNameForNamespace(request,
+    def GetLocalDiskUsage(request,
             target,
             options=(),
             channel_credentials=None,
@@ -646,9 +758,9 @@ class LocalContentAddressableStorage(object):
         return grpc.experimental.unary_unary(
             request,
             target,
-            
'/build.buildgrid.LocalContentAddressableStorage/GetInstanceNameForNamespace',
-            
build_dot_buildgrid_dot_local__cas__pb2.GetInstanceNameForNamespaceRequest.SerializeToString,
-            
build_dot_buildgrid_dot_local__cas__pb2.GetInstanceNameForNamespaceResponse.FromString,
+            
'/build.buildgrid.LocalContentAddressableStorage/GetLocalDiskUsage',
+            
build_dot_buildgrid_dot_local__cas__pb2.GetLocalDiskUsageRequest.SerializeToString,
+            
build_dot_buildgrid_dot_local__cas__pb2.GetLocalDiskUsageResponse.FromString,
             options,
             channel_credentials,
             insecure,
@@ -660,7 +772,7 @@ class LocalContentAddressableStorage(object):
             _registered_method=True)
 
     @staticmethod
-    def GetLocalDiskUsage(request,
+    def NestedServer(request_iterator,
             target,
             options=(),
             channel_credentials=None,
@@ -670,12 +782,12 @@ class LocalContentAddressableStorage(object):
             wait_for_ready=None,
             timeout=None,
             metadata=None):
-        return grpc.experimental.unary_unary(
-            request,
+        return grpc.experimental.stream_stream(
+            request_iterator,
             target,
-            
'/build.buildgrid.LocalContentAddressableStorage/GetLocalDiskUsage',
-            
build_dot_buildgrid_dot_local__cas__pb2.GetLocalDiskUsageRequest.SerializeToString,
-            
build_dot_buildgrid_dot_local__cas__pb2.GetLocalDiskUsageResponse.FromString,
+            '/build.buildgrid.LocalContentAddressableStorage/NestedServer',
+            
build_dot_buildgrid_dot_local__cas__pb2.NestedServerRequest.SerializeToString,
+            
build_dot_buildgrid_dot_local__cas__pb2.NestedServerResponse.FromString,
             options,
             channel_credentials,
             insecure,

Reply via email to