about summary refs log tree commit diff
path: root/tvix/castore/protos
diff options
context:
space:
mode:
Diffstat (limited to 'tvix/castore/protos')
-rw-r--r--tvix/castore/protos/castore.go212
-rw-r--r--tvix/castore/protos/castore.pb.go580
-rw-r--r--tvix/castore/protos/castore_test.go298
-rw-r--r--tvix/castore/protos/default.nix33
-rw-r--r--tvix/castore/protos/go.mod22
-rw-r--r--tvix/castore/protos/go.sum99
-rw-r--r--tvix/castore/protos/rename_node.go38
-rw-r--r--tvix/castore/protos/rpc_blobstore.pb.go414
-rw-r--r--tvix/castore/protos/rpc_blobstore_grpc.pb.go274
-rw-r--r--tvix/castore/protos/rpc_directory.pb.go273
-rw-r--r--tvix/castore/protos/rpc_directory_grpc.pb.go238
11 files changed, 33 insertions, 2448 deletions
diff --git a/tvix/castore/protos/castore.go b/tvix/castore/protos/castore.go
deleted file mode 100644
index c9e3757885..0000000000
--- a/tvix/castore/protos/castore.go
+++ /dev/null
@@ -1,212 +0,0 @@
-package castorev1
-
-import (
-	"bytes"
-	"encoding/base64"
-	"fmt"
-
-	"google.golang.org/protobuf/proto"
-	"lukechampine.com/blake3"
-)
-
-// The size of a directory is calculated by summing up the numbers of
-// `directories`, `files` and `symlinks`, and for each directory, its size
-// field.
-func (d *Directory) Size() uint32 {
-	var size uint32
-	size = uint32(len(d.Files) + len(d.Symlinks))
-	for _, d := range d.Directories {
-		size += 1 + d.Size
-	}
-	return size
-}
-
-func (d *Directory) Digest() ([]byte, error) {
-	b, err := proto.MarshalOptions{
-		Deterministic: true,
-	}.Marshal(d)
-
-	if err != nil {
-		return nil, fmt.Errorf("error while marshalling directory: %w", err)
-	}
-
-	h := blake3.New(32, nil)
-
-	_, err = h.Write(b)
-	if err != nil {
-		return nil, fmt.Errorf("error writing to hasher: %w", err)
-	}
-
-	return h.Sum(nil), nil
-}
-
-// isValidName checks a name for validity.
-// We disallow slashes, null bytes, '.', '..' and the empty string.
-// Depending on the context, a *Node message with an empty string as name is
-// allowed, but they don't occur inside a Directory message.
-func isValidName(n []byte) bool {
-	if len(n) == 0 || bytes.Equal(n, []byte("..")) || bytes.Equal(n, []byte{'.'}) || bytes.Contains(n, []byte{'\x00'}) || bytes.Contains(n, []byte{'/'}) {
-		return false
-	}
-	return true
-}
-
-// Validate ensures a DirectoryNode has a valid name and correct digest len.
-func (n *DirectoryNode) Validate() error {
-	if len(n.Digest) != 32 {
-		return fmt.Errorf("invalid digest length for %s, expected %d, got %d", n.Name, 32, len(n.Digest))
-	}
-
-	if !isValidName(n.Name) {
-		return fmt.Errorf("invalid node name: %s", n.Name)
-	}
-
-	return nil
-}
-
-// Validate ensures a FileNode has a valid name and correct digest len.
-func (n *FileNode) Validate() error {
-	if len(n.Digest) != 32 {
-		return fmt.Errorf("invalid digest length for %s, expected %d, got %d", n.Name, 32, len(n.Digest))
-	}
-
-	if !isValidName(n.Name) {
-		return fmt.Errorf("invalid node name: %s", n.Name)
-	}
-
-	return nil
-}
-
-// Validate ensures a SymlinkNode has a valid name and target.
-func (n *SymlinkNode) Validate() error {
-	if len(n.Target) == 0 || bytes.Contains(n.Target, []byte{0}) {
-		return fmt.Errorf("invalid symlink target: %s", n.Target)
-	}
-
-	if !isValidName(n.Name) {
-		return fmt.Errorf("invalid node name: %s", n.Name)
-	}
-
-	return nil
-}
-
-// Validate ensures a node is valid, by dispatching to the per-type validation functions.
-func (n *Node) Validate() error {
-	if node := n.GetDirectory(); node != nil {
-		if err := node.Validate(); err != nil {
-			return fmt.Errorf("SymlinkNode failed validation: %w", err)
-		}
-	} else if node := n.GetFile(); node != nil {
-		if err := node.Validate(); err != nil {
-			return fmt.Errorf("FileNode failed validation: %w", err)
-		}
-	} else if node := n.GetSymlink(); node != nil {
-		if err := node.Validate(); err != nil {
-			return fmt.Errorf("SymlinkNode failed validation: %w", err)
-		}
-
-	} else {
-		// this would only happen if we introduced a new type
-		return fmt.Errorf("no specific node found")
-	}
-
-	return nil
-}
-
-// Validate thecks the Directory message for invalid data, such as:
-// - violations of name restrictions
-// - invalid digest lengths
-// - not properly sorted lists
-// - duplicate names in the three lists
-func (d *Directory) Validate() error {
-	// seenNames contains all seen names so far.
-	// We populate this to ensure node names are unique across all three lists.
-	seenNames := make(map[string]interface{})
-
-	// We also track the last seen name in each of the three lists,
-	// to ensure nodes are sorted by their names.
-	var lastDirectoryName, lastFileName, lastSymlinkName []byte
-
-	// helper function to only insert in sorted order.
-	// used with the three lists above.
-	// Note this consumes a *pointer to* a string,  as it mutates it.
-	insertIfGt := func(lastName *[]byte, name []byte) error {
-		// update if it's greater than the previous name
-		if bytes.Compare(name, *lastName) == 1 {
-			*lastName = name
-			return nil
-		} else {
-			return fmt.Errorf("%v is not in sorted order", name)
-		}
-	}
-
-	// insertOnce inserts into seenNames if the key doesn't exist yet.
-	insertOnce := func(name []byte) error {
-		encoded := base64.StdEncoding.EncodeToString(name)
-		if _, found := seenNames[encoded]; found {
-			return fmt.Errorf("duplicate name: %v", string(name))
-		}
-		seenNames[encoded] = nil
-		return nil
-	}
-
-	// Loop over all Directories, Files and Symlinks individually,
-	// check them for validity, then check for sorting in the current list, and
-	// uniqueness across all three lists.
-	for _, directoryNode := range d.Directories {
-		directoryName := directoryNode.GetName()
-
-		if err := directoryNode.Validate(); err != nil {
-			return fmt.Errorf("DirectoryNode %s failed validation: %w", directoryName, err)
-		}
-
-		// ensure names are sorted
-		if err := insertIfGt(&lastDirectoryName, directoryName); err != nil {
-			return err
-		}
-
-		// add to seenNames
-		if err := insertOnce(directoryName); err != nil {
-			return err
-		}
-
-	}
-
-	for _, fileNode := range d.Files {
-		fileName := fileNode.GetName()
-
-		if err := fileNode.Validate(); err != nil {
-			return fmt.Errorf("FileNode %s failed validation: %w", fileName, err)
-		}
-
-		// ensure names are sorted
-		if err := insertIfGt(&lastFileName, fileName); err != nil {
-			return err
-		}
-
-		// add to seenNames
-		if err := insertOnce(fileName); err != nil {
-			return err
-		}
-	}
-
-	for _, symlinkNode := range d.Symlinks {
-		symlinkName := symlinkNode.GetName()
-
-		if err := symlinkNode.Validate(); err != nil {
-			return fmt.Errorf("SymlinkNode %s failed validation: %w", symlinkName, err)
-		}
-
-		// ensure names are sorted
-		if err := insertIfGt(&lastSymlinkName, symlinkName); err != nil {
-			return err
-		}
-
-		// add to seenNames
-		if err := insertOnce(symlinkName); err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
diff --git a/tvix/castore/protos/castore.pb.go b/tvix/castore/protos/castore.pb.go
deleted file mode 100644
index 5323d6c923..0000000000
--- a/tvix/castore/protos/castore.pb.go
+++ /dev/null
@@ -1,580 +0,0 @@
-// SPDX-FileCopyrightText: edef <edef@unfathomable.blue>
-// SPDX-License-Identifier: OSL-3.0 OR MIT OR Apache-2.0
-
-// Code generated by protoc-gen-go. DO NOT EDIT.
-// versions:
-// 	protoc-gen-go v1.31.0
-// 	protoc        (unknown)
-// source: tvix/castore/protos/castore.proto
-
-package castorev1
-
-import (
-	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
-	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
-	reflect "reflect"
-	sync "sync"
-)
-
-const (
-	// Verify that this generated code is sufficiently up-to-date.
-	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
-	// Verify that runtime/protoimpl is sufficiently up-to-date.
-	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
-)
-
-// A Directory can contain Directory, File or Symlink nodes.
-// Each of these nodes have a name attribute, which is the basename in that directory
-// and node type specific attributes.
-// The name attribute:
-//   - MUST not contain slashes or null bytes
-//   - MUST not be '.' or '..'
-//   - MUST be unique across all three lists
-//
-// Elements in each list need to be lexicographically ordered by the name
-// attribute.
-type Directory struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	Directories []*DirectoryNode `protobuf:"bytes,1,rep,name=directories,proto3" json:"directories,omitempty"`
-	Files       []*FileNode      `protobuf:"bytes,2,rep,name=files,proto3" json:"files,omitempty"`
-	Symlinks    []*SymlinkNode   `protobuf:"bytes,3,rep,name=symlinks,proto3" json:"symlinks,omitempty"`
-}
-
-func (x *Directory) Reset() {
-	*x = Directory{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_tvix_castore_protos_castore_proto_msgTypes[0]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *Directory) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*Directory) ProtoMessage() {}
-
-func (x *Directory) ProtoReflect() protoreflect.Message {
-	mi := &file_tvix_castore_protos_castore_proto_msgTypes[0]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use Directory.ProtoReflect.Descriptor instead.
-func (*Directory) Descriptor() ([]byte, []int) {
-	return file_tvix_castore_protos_castore_proto_rawDescGZIP(), []int{0}
-}
-
-func (x *Directory) GetDirectories() []*DirectoryNode {
-	if x != nil {
-		return x.Directories
-	}
-	return nil
-}
-
-func (x *Directory) GetFiles() []*FileNode {
-	if x != nil {
-		return x.Files
-	}
-	return nil
-}
-
-func (x *Directory) GetSymlinks() []*SymlinkNode {
-	if x != nil {
-		return x.Symlinks
-	}
-	return nil
-}
-
-// A DirectoryNode represents a directory in a Directory.
-type DirectoryNode struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	// The (base)name of the directory
-	Name []byte `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
-	// The blake3 hash of a Directory message, serialized in protobuf canonical form.
-	Digest []byte `protobuf:"bytes,2,opt,name=digest,proto3" json:"digest,omitempty"`
-	// Number of child elements in the Directory referred to by `digest`.
-	// Calculated by summing up the numbers of `directories`, `files` and
-	// `symlinks`, and for each directory, its size field. Used for inode
-	// number calculation.
-	// This field is precisely as verifiable as any other Merkle tree edge.
-	// Resolve `digest`, and you can compute it incrementally. Resolve the
-	// entire tree, and you can fully compute it from scratch.
-	// A credulous implementation won't reject an excessive size, but this is
-	// harmless: you'll have some ordinals without nodes. Undersizing is
-	// obvious and easy to reject: you won't have an ordinal for some nodes.
-	Size uint32 `protobuf:"varint,3,opt,name=size,proto3" json:"size,omitempty"`
-}
-
-func (x *DirectoryNode) Reset() {
-	*x = DirectoryNode{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_tvix_castore_protos_castore_proto_msgTypes[1]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *DirectoryNode) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*DirectoryNode) ProtoMessage() {}
-
-func (x *DirectoryNode) ProtoReflect() protoreflect.Message {
-	mi := &file_tvix_castore_protos_castore_proto_msgTypes[1]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use DirectoryNode.ProtoReflect.Descriptor instead.
-func (*DirectoryNode) Descriptor() ([]byte, []int) {
-	return file_tvix_castore_protos_castore_proto_rawDescGZIP(), []int{1}
-}
-
-func (x *DirectoryNode) GetName() []byte {
-	if x != nil {
-		return x.Name
-	}
-	return nil
-}
-
-func (x *DirectoryNode) GetDigest() []byte {
-	if x != nil {
-		return x.Digest
-	}
-	return nil
-}
-
-func (x *DirectoryNode) GetSize() uint32 {
-	if x != nil {
-		return x.Size
-	}
-	return 0
-}
-
-// A FileNode represents a regular or executable file in a Directory.
-type FileNode struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	// The (base)name of the file
-	Name []byte `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
-	// The blake3 digest of the file contents
-	Digest []byte `protobuf:"bytes,2,opt,name=digest,proto3" json:"digest,omitempty"`
-	// The file content size
-	Size uint32 `protobuf:"varint,3,opt,name=size,proto3" json:"size,omitempty"`
-	// Whether the file is executable
-	Executable bool `protobuf:"varint,4,opt,name=executable,proto3" json:"executable,omitempty"`
-}
-
-func (x *FileNode) Reset() {
-	*x = FileNode{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_tvix_castore_protos_castore_proto_msgTypes[2]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *FileNode) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*FileNode) ProtoMessage() {}
-
-func (x *FileNode) ProtoReflect() protoreflect.Message {
-	mi := &file_tvix_castore_protos_castore_proto_msgTypes[2]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use FileNode.ProtoReflect.Descriptor instead.
-func (*FileNode) Descriptor() ([]byte, []int) {
-	return file_tvix_castore_protos_castore_proto_rawDescGZIP(), []int{2}
-}
-
-func (x *FileNode) GetName() []byte {
-	if x != nil {
-		return x.Name
-	}
-	return nil
-}
-
-func (x *FileNode) GetDigest() []byte {
-	if x != nil {
-		return x.Digest
-	}
-	return nil
-}
-
-func (x *FileNode) GetSize() uint32 {
-	if x != nil {
-		return x.Size
-	}
-	return 0
-}
-
-func (x *FileNode) GetExecutable() bool {
-	if x != nil {
-		return x.Executable
-	}
-	return false
-}
-
-// A SymlinkNode represents a symbolic link in a Directory.
-type SymlinkNode struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	// The (base)name of the symlink
-	Name []byte `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
-	// The target of the symlink.
-	Target []byte `protobuf:"bytes,2,opt,name=target,proto3" json:"target,omitempty"`
-}
-
-func (x *SymlinkNode) Reset() {
-	*x = SymlinkNode{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_tvix_castore_protos_castore_proto_msgTypes[3]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *SymlinkNode) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*SymlinkNode) ProtoMessage() {}
-
-func (x *SymlinkNode) ProtoReflect() protoreflect.Message {
-	mi := &file_tvix_castore_protos_castore_proto_msgTypes[3]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use SymlinkNode.ProtoReflect.Descriptor instead.
-func (*SymlinkNode) Descriptor() ([]byte, []int) {
-	return file_tvix_castore_protos_castore_proto_rawDescGZIP(), []int{3}
-}
-
-func (x *SymlinkNode) GetName() []byte {
-	if x != nil {
-		return x.Name
-	}
-	return nil
-}
-
-func (x *SymlinkNode) GetTarget() []byte {
-	if x != nil {
-		return x.Target
-	}
-	return nil
-}
-
-// A Node is either a DirectoryNode, FileNode or SymlinkNode.
-type Node struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	// Types that are assignable to Node:
-	//
-	//	*Node_Directory
-	//	*Node_File
-	//	*Node_Symlink
-	Node isNode_Node `protobuf_oneof:"node"`
-}
-
-func (x *Node) Reset() {
-	*x = Node{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_tvix_castore_protos_castore_proto_msgTypes[4]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *Node) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*Node) ProtoMessage() {}
-
-func (x *Node) ProtoReflect() protoreflect.Message {
-	mi := &file_tvix_castore_protos_castore_proto_msgTypes[4]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use Node.ProtoReflect.Descriptor instead.
-func (*Node) Descriptor() ([]byte, []int) {
-	return file_tvix_castore_protos_castore_proto_rawDescGZIP(), []int{4}
-}
-
-func (m *Node) GetNode() isNode_Node {
-	if m != nil {
-		return m.Node
-	}
-	return nil
-}
-
-func (x *Node) GetDirectory() *DirectoryNode {
-	if x, ok := x.GetNode().(*Node_Directory); ok {
-		return x.Directory
-	}
-	return nil
-}
-
-func (x *Node) GetFile() *FileNode {
-	if x, ok := x.GetNode().(*Node_File); ok {
-		return x.File
-	}
-	return nil
-}
-
-func (x *Node) GetSymlink() *SymlinkNode {
-	if x, ok := x.GetNode().(*Node_Symlink); ok {
-		return x.Symlink
-	}
-	return nil
-}
-
-type isNode_Node interface {
-	isNode_Node()
-}
-
-type Node_Directory struct {
-	Directory *DirectoryNode `protobuf:"bytes,1,opt,name=directory,proto3,oneof"`
-}
-
-type Node_File struct {
-	File *FileNode `protobuf:"bytes,2,opt,name=file,proto3,oneof"`
-}
-
-type Node_Symlink struct {
-	Symlink *SymlinkNode `protobuf:"bytes,3,opt,name=symlink,proto3,oneof"`
-}
-
-func (*Node_Directory) isNode_Node() {}
-
-func (*Node_File) isNode_Node() {}
-
-func (*Node_Symlink) isNode_Node() {}
-
-var File_tvix_castore_protos_castore_proto protoreflect.FileDescriptor
-
-var file_tvix_castore_protos_castore_proto_rawDesc = []byte{
-	0x0a, 0x21, 0x74, 0x76, 0x69, 0x78, 0x2f, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x70,
-	0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72,
-	0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72,
-	0x65, 0x2e, 0x76, 0x31, 0x22, 0xb8, 0x01, 0x0a, 0x09, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f,
-	0x72, 0x79, 0x12, 0x40, 0x0a, 0x0b, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x69, 0x65,
-	0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x63,
-	0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74,
-	0x6f, 0x72, 0x79, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x0b, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f,
-	0x72, 0x69, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20,
-	0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x63, 0x61, 0x73, 0x74, 0x6f,
-	0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05,
-	0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x08, 0x73, 0x79, 0x6d, 0x6c, 0x69, 0x6e, 0x6b,
-	0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x63,
-	0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x79, 0x6d, 0x6c, 0x69, 0x6e,
-	0x6b, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x08, 0x73, 0x79, 0x6d, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x22,
-	0x4f, 0x0a, 0x0d, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x4e, 0x6f, 0x64, 0x65,
-	0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04,
-	0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x02,
-	0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04,
-	0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65,
-	0x22, 0x6a, 0x0a, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04,
-	0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
-	0x12, 0x16, 0x0a, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c,
-	0x52, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65,
-	0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x1e, 0x0a, 0x0a,
-	0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08,
-	0x52, 0x0a, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x39, 0x0a, 0x0b,
-	0x53, 0x79, 0x6d, 0x6c, 0x69, 0x6e, 0x6b, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e,
-	0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
-	0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52,
-	0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0xb9, 0x01, 0x0a, 0x04, 0x4e, 0x6f, 0x64, 0x65,
-	0x12, 0x3e, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20,
-	0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x63, 0x61, 0x73, 0x74, 0x6f,
-	0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x4e,
-	0x6f, 0x64, 0x65, 0x48, 0x00, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79,
-	0x12, 0x2f, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19,
-	0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31,
-	0x2e, 0x46, 0x69, 0x6c, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x48, 0x00, 0x52, 0x04, 0x66, 0x69, 0x6c,
-	0x65, 0x12, 0x38, 0x0a, 0x07, 0x73, 0x79, 0x6d, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x03, 0x20, 0x01,
-	0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72,
-	0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x79, 0x6d, 0x6c, 0x69, 0x6e, 0x6b, 0x4e, 0x6f, 0x64, 0x65,
-	0x48, 0x00, 0x52, 0x07, 0x73, 0x79, 0x6d, 0x6c, 0x69, 0x6e, 0x6b, 0x42, 0x06, 0x0a, 0x04, 0x6e,
-	0x6f, 0x64, 0x65, 0x42, 0x2c, 0x5a, 0x2a, 0x63, 0x6f, 0x64, 0x65, 0x2e, 0x74, 0x76, 0x6c, 0x2e,
-	0x66, 0x79, 0x69, 0x2f, 0x74, 0x76, 0x69, 0x78, 0x2f, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65,
-	0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x3b, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x76,
-	0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
-}
-
-var (
-	file_tvix_castore_protos_castore_proto_rawDescOnce sync.Once
-	file_tvix_castore_protos_castore_proto_rawDescData = file_tvix_castore_protos_castore_proto_rawDesc
-)
-
-func file_tvix_castore_protos_castore_proto_rawDescGZIP() []byte {
-	file_tvix_castore_protos_castore_proto_rawDescOnce.Do(func() {
-		file_tvix_castore_protos_castore_proto_rawDescData = protoimpl.X.CompressGZIP(file_tvix_castore_protos_castore_proto_rawDescData)
-	})
-	return file_tvix_castore_protos_castore_proto_rawDescData
-}
-
-var file_tvix_castore_protos_castore_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
-var file_tvix_castore_protos_castore_proto_goTypes = []interface{}{
-	(*Directory)(nil),     // 0: tvix.castore.v1.Directory
-	(*DirectoryNode)(nil), // 1: tvix.castore.v1.DirectoryNode
-	(*FileNode)(nil),      // 2: tvix.castore.v1.FileNode
-	(*SymlinkNode)(nil),   // 3: tvix.castore.v1.SymlinkNode
-	(*Node)(nil),          // 4: tvix.castore.v1.Node
-}
-var file_tvix_castore_protos_castore_proto_depIdxs = []int32{
-	1, // 0: tvix.castore.v1.Directory.directories:type_name -> tvix.castore.v1.DirectoryNode
-	2, // 1: tvix.castore.v1.Directory.files:type_name -> tvix.castore.v1.FileNode
-	3, // 2: tvix.castore.v1.Directory.symlinks:type_name -> tvix.castore.v1.SymlinkNode
-	1, // 3: tvix.castore.v1.Node.directory:type_name -> tvix.castore.v1.DirectoryNode
-	2, // 4: tvix.castore.v1.Node.file:type_name -> tvix.castore.v1.FileNode
-	3, // 5: tvix.castore.v1.Node.symlink:type_name -> tvix.castore.v1.SymlinkNode
-	6, // [6:6] is the sub-list for method output_type
-	6, // [6:6] is the sub-list for method input_type
-	6, // [6:6] is the sub-list for extension type_name
-	6, // [6:6] is the sub-list for extension extendee
-	0, // [0:6] is the sub-list for field type_name
-}
-
-func init() { file_tvix_castore_protos_castore_proto_init() }
-func file_tvix_castore_protos_castore_proto_init() {
-	if File_tvix_castore_protos_castore_proto != nil {
-		return
-	}
-	if !protoimpl.UnsafeEnabled {
-		file_tvix_castore_protos_castore_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*Directory); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_tvix_castore_protos_castore_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*DirectoryNode); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_tvix_castore_protos_castore_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*FileNode); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_tvix_castore_protos_castore_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*SymlinkNode); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_tvix_castore_protos_castore_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*Node); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-	}
-	file_tvix_castore_protos_castore_proto_msgTypes[4].OneofWrappers = []interface{}{
-		(*Node_Directory)(nil),
-		(*Node_File)(nil),
-		(*Node_Symlink)(nil),
-	}
-	type x struct{}
-	out := protoimpl.TypeBuilder{
-		File: protoimpl.DescBuilder{
-			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
-			RawDescriptor: file_tvix_castore_protos_castore_proto_rawDesc,
-			NumEnums:      0,
-			NumMessages:   5,
-			NumExtensions: 0,
-			NumServices:   0,
-		},
-		GoTypes:           file_tvix_castore_protos_castore_proto_goTypes,
-		DependencyIndexes: file_tvix_castore_protos_castore_proto_depIdxs,
-		MessageInfos:      file_tvix_castore_protos_castore_proto_msgTypes,
-	}.Build()
-	File_tvix_castore_protos_castore_proto = out.File
-	file_tvix_castore_protos_castore_proto_rawDesc = nil
-	file_tvix_castore_protos_castore_proto_goTypes = nil
-	file_tvix_castore_protos_castore_proto_depIdxs = nil
-}
diff --git a/tvix/castore/protos/castore_test.go b/tvix/castore/protos/castore_test.go
deleted file mode 100644
index fda87a6cfb..0000000000
--- a/tvix/castore/protos/castore_test.go
+++ /dev/null
@@ -1,298 +0,0 @@
-package castorev1_test
-
-import (
-	"testing"
-
-	castorev1pb "code.tvl.fyi/tvix/castore/protos"
-	"github.com/stretchr/testify/assert"
-)
-
-var (
-	dummyDigest = []byte{
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-		0x00, 0x00, 0x00, 0x00,
-	}
-)
-
-func TestDirectorySize(t *testing.T) {
-	t.Run("empty", func(t *testing.T) {
-		d := castorev1pb.Directory{
-			Directories: []*castorev1pb.DirectoryNode{},
-			Files:       []*castorev1pb.FileNode{},
-			Symlinks:    []*castorev1pb.SymlinkNode{},
-		}
-
-		assert.Equal(t, uint32(0), d.Size())
-	})
-
-	t.Run("containing single empty directory", func(t *testing.T) {
-		d := castorev1pb.Directory{
-			Directories: []*castorev1pb.DirectoryNode{{
-				Name:   []byte([]byte("foo")),
-				Digest: dummyDigest,
-				Size:   0,
-			}},
-			Files:    []*castorev1pb.FileNode{},
-			Symlinks: []*castorev1pb.SymlinkNode{},
-		}
-
-		assert.Equal(t, uint32(1), d.Size())
-	})
-
-	t.Run("containing single non-empty directory", func(t *testing.T) {
-		d := castorev1pb.Directory{
-			Directories: []*castorev1pb.DirectoryNode{{
-				Name:   []byte("foo"),
-				Digest: dummyDigest,
-				Size:   4,
-			}},
-			Files:    []*castorev1pb.FileNode{},
-			Symlinks: []*castorev1pb.SymlinkNode{},
-		}
-
-		assert.Equal(t, uint32(5), d.Size())
-	})
-
-	t.Run("containing single file", func(t *testing.T) {
-		d := castorev1pb.Directory{
-			Directories: []*castorev1pb.DirectoryNode{},
-			Files: []*castorev1pb.FileNode{{
-				Name:       []byte("foo"),
-				Digest:     dummyDigest,
-				Size:       42,
-				Executable: false,
-			}},
-			Symlinks: []*castorev1pb.SymlinkNode{},
-		}
-
-		assert.Equal(t, uint32(1), d.Size())
-	})
-
-	t.Run("containing single symlink", func(t *testing.T) {
-		d := castorev1pb.Directory{
-			Directories: []*castorev1pb.DirectoryNode{},
-			Files:       []*castorev1pb.FileNode{},
-			Symlinks: []*castorev1pb.SymlinkNode{{
-				Name:   []byte("foo"),
-				Target: []byte("bar"),
-			}},
-		}
-
-		assert.Equal(t, uint32(1), d.Size())
-	})
-
-}
-func TestDirectoryDigest(t *testing.T) {
-	d := castorev1pb.Directory{
-		Directories: []*castorev1pb.DirectoryNode{},
-		Files:       []*castorev1pb.FileNode{},
-		Symlinks:    []*castorev1pb.SymlinkNode{},
-	}
-
-	dgst, err := d.Digest()
-	assert.NoError(t, err, "calling Digest() on a directory shouldn't error")
-	assert.Equal(t, []byte{
-		0xaf, 0x13, 0x49, 0xb9, 0xf5, 0xf9, 0xa1, 0xa6, 0xa0, 0x40, 0x4d, 0xea, 0x36, 0xdc,
-		0xc9, 0x49, 0x9b, 0xcb, 0x25, 0xc9, 0xad, 0xc1, 0x12, 0xb7, 0xcc, 0x9a, 0x93, 0xca,
-		0xe4, 0x1f, 0x32, 0x62,
-	}, dgst)
-}
-
-func TestDirectoryValidate(t *testing.T) {
-	t.Run("empty", func(t *testing.T) {
-		d := castorev1pb.Directory{
-			Directories: []*castorev1pb.DirectoryNode{},
-			Files:       []*castorev1pb.FileNode{},
-			Symlinks:    []*castorev1pb.SymlinkNode{},
-		}
-
-		assert.NoError(t, d.Validate())
-	})
-
-	t.Run("invalid names", func(t *testing.T) {
-		{
-			d := castorev1pb.Directory{
-				Directories: []*castorev1pb.DirectoryNode{{
-					Name:   []byte{},
-					Digest: dummyDigest,
-					Size:   42,
-				}},
-				Files:    []*castorev1pb.FileNode{},
-				Symlinks: []*castorev1pb.SymlinkNode{},
-			}
-
-			assert.ErrorContains(t, d.Validate(), "invalid node name")
-		}
-		{
-			d := castorev1pb.Directory{
-				Directories: []*castorev1pb.DirectoryNode{{
-					Name:   []byte("."),
-					Digest: dummyDigest,
-					Size:   42,
-				}},
-				Files:    []*castorev1pb.FileNode{},
-				Symlinks: []*castorev1pb.SymlinkNode{},
-			}
-
-			assert.ErrorContains(t, d.Validate(), "invalid node name")
-		}
-		{
-			d := castorev1pb.Directory{
-				Directories: []*castorev1pb.DirectoryNode{},
-				Files: []*castorev1pb.FileNode{{
-					Name:       []byte(".."),
-					Digest:     dummyDigest,
-					Size:       42,
-					Executable: false,
-				}},
-				Symlinks: []*castorev1pb.SymlinkNode{},
-			}
-
-			assert.ErrorContains(t, d.Validate(), "invalid node name")
-		}
-		{
-			d := castorev1pb.Directory{
-				Directories: []*castorev1pb.DirectoryNode{},
-				Files:       []*castorev1pb.FileNode{},
-				Symlinks: []*castorev1pb.SymlinkNode{{
-					Name:   []byte("\x00"),
-					Target: []byte("foo"),
-				}},
-			}
-
-			assert.ErrorContains(t, d.Validate(), "invalid node name")
-		}
-		{
-			d := castorev1pb.Directory{
-				Directories: []*castorev1pb.DirectoryNode{},
-				Files:       []*castorev1pb.FileNode{},
-				Symlinks: []*castorev1pb.SymlinkNode{{
-					Name:   []byte("foo/bar"),
-					Target: []byte("foo"),
-				}},
-			}
-
-			assert.ErrorContains(t, d.Validate(), "invalid node name")
-		}
-	})
-
-	t.Run("invalid digest", func(t *testing.T) {
-		d := castorev1pb.Directory{
-			Directories: []*castorev1pb.DirectoryNode{{
-				Name:   []byte("foo"),
-				Digest: nil,
-				Size:   42,
-			}},
-			Files:    []*castorev1pb.FileNode{},
-			Symlinks: []*castorev1pb.SymlinkNode{},
-		}
-
-		assert.ErrorContains(t, d.Validate(), "invalid digest length")
-	})
-
-	t.Run("invalid symlink targets", func(t *testing.T) {
-		{
-			d := castorev1pb.Directory{
-				Directories: []*castorev1pb.DirectoryNode{},
-				Files:       []*castorev1pb.FileNode{},
-				Symlinks: []*castorev1pb.SymlinkNode{{
-					Name:   []byte("foo"),
-					Target: []byte{},
-				}},
-			}
-
-			assert.ErrorContains(t, d.Validate(), "invalid symlink target")
-		}
-		{
-			d := castorev1pb.Directory{
-				Directories: []*castorev1pb.DirectoryNode{},
-				Files:       []*castorev1pb.FileNode{},
-				Symlinks: []*castorev1pb.SymlinkNode{{
-					Name:   []byte("foo"),
-					Target: []byte{0x66, 0x6f, 0x6f, 0},
-				}},
-			}
-
-			assert.ErrorContains(t, d.Validate(), "invalid symlink target")
-		}
-	})
-
-	t.Run("sorting", func(t *testing.T) {
-		// "b" comes before "a", bad.
-		{
-			d := castorev1pb.Directory{
-				Directories: []*castorev1pb.DirectoryNode{{
-					Name:   []byte("b"),
-					Digest: dummyDigest,
-					Size:   42,
-				}, {
-					Name:   []byte("a"),
-					Digest: dummyDigest,
-					Size:   42,
-				}},
-				Files:    []*castorev1pb.FileNode{},
-				Symlinks: []*castorev1pb.SymlinkNode{},
-			}
-			assert.ErrorContains(t, d.Validate(), "is not in sorted order")
-		}
-
-		// "a" exists twice, bad.
-		{
-			d := castorev1pb.Directory{
-				Directories: []*castorev1pb.DirectoryNode{{
-					Name:   []byte("a"),
-					Digest: dummyDigest,
-					Size:   42,
-				}},
-				Files: []*castorev1pb.FileNode{{
-					Name:       []byte("a"),
-					Digest:     dummyDigest,
-					Size:       42,
-					Executable: false,
-				}},
-				Symlinks: []*castorev1pb.SymlinkNode{},
-			}
-			assert.ErrorContains(t, d.Validate(), "duplicate name")
-		}
-
-		// "a" comes before "b", all good.
-		{
-			d := castorev1pb.Directory{
-				Directories: []*castorev1pb.DirectoryNode{{
-					Name:   []byte("a"),
-					Digest: dummyDigest,
-					Size:   42,
-				}, {
-					Name:   []byte("b"),
-					Digest: dummyDigest,
-					Size:   42,
-				}},
-				Files:    []*castorev1pb.FileNode{},
-				Symlinks: []*castorev1pb.SymlinkNode{},
-			}
-			assert.NoError(t, d.Validate(), "shouldn't error")
-		}
-
-		// [b, c] and [a] are both properly sorted.
-		{
-			d := castorev1pb.Directory{
-				Directories: []*castorev1pb.DirectoryNode{{
-					Name:   []byte("b"),
-					Digest: dummyDigest,
-					Size:   42,
-				}, {
-					Name:   []byte("c"),
-					Digest: dummyDigest,
-					Size:   42,
-				}},
-				Files: []*castorev1pb.FileNode{},
-				Symlinks: []*castorev1pb.SymlinkNode{{
-					Name:   []byte("a"),
-					Target: []byte("foo"),
-				}},
-			}
-			assert.NoError(t, d.Validate(), "shouldn't error")
-		}
-	})
-}
diff --git a/tvix/castore/protos/default.nix b/tvix/castore/protos/default.nix
new file mode 100644
index 0000000000..e2e2f910ee
--- /dev/null
+++ b/tvix/castore/protos/default.nix
@@ -0,0 +1,33 @@
+{ depot, pkgs, ... }: {
+  # Produces the golang bindings.
+  go-bindings = pkgs.stdenv.mkDerivation {
+    name = "go-bindings";
+
+    src = depot.nix.sparseTree {
+      name = "castore-protos";
+      root = depot.path.origSrc;
+      paths = [
+        ./castore.proto
+        ./rpc_blobstore.proto
+        ./rpc_directory.proto
+        ../../../buf.yaml
+        ../../../buf.gen.yaml
+      ];
+    };
+
+    nativeBuildInputs = [
+      pkgs.buf
+      pkgs.protoc-gen-go
+      pkgs.protoc-gen-go-grpc
+    ];
+
+    buildPhase = ''
+      export HOME=$TMPDIR
+      buf lint
+      buf generate
+
+      mkdir -p $out
+      cp tvix/castore/protos/*.pb.go $out/
+    '';
+  };
+}
diff --git a/tvix/castore/protos/go.mod b/tvix/castore/protos/go.mod
deleted file mode 100644
index 9048aa205c..0000000000
--- a/tvix/castore/protos/go.mod
+++ /dev/null
@@ -1,22 +0,0 @@
-module code.tvl.fyi/tvix/castore/protos
-
-go 1.19
-
-require (
-	github.com/stretchr/testify v1.8.1
-	google.golang.org/grpc v1.51.0
-	google.golang.org/protobuf v1.31.0
-	lukechampine.com/blake3 v1.1.7
-)
-
-require (
-	github.com/davecgh/go-spew v1.1.1 // indirect
-	github.com/golang/protobuf v1.5.2 // indirect
-	github.com/klauspost/cpuid/v2 v2.0.9 // indirect
-	github.com/pmezard/go-difflib v1.0.0 // indirect
-	golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
-	golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
-	golang.org/x/text v0.4.0 // indirect
-	google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect
-	gopkg.in/yaml.v3 v3.0.1 // indirect
-)
diff --git a/tvix/castore/protos/go.sum b/tvix/castore/protos/go.sum
deleted file mode 100644
index 535b8e32f0..0000000000
--- a/tvix/castore/protos/go.sum
+++ /dev/null
@@ -1,99 +0,0 @@
-cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
-github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-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/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
-github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
-github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
-github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
-github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
-github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
-github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
-github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
-github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
-github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
-github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
-github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
-github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
-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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
-github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
-github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
-github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
-github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/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-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-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-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-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
-golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
-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-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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
-golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
-golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-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-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
-google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
-google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
-google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
-google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
-google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
-google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
-google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U=
-google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
-google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
-google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
-google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
-google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
-google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
-google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
-google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
-google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
-gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0=
-lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA=
diff --git a/tvix/castore/protos/rename_node.go b/tvix/castore/protos/rename_node.go
deleted file mode 100644
index 80537b16d3..0000000000
--- a/tvix/castore/protos/rename_node.go
+++ /dev/null
@@ -1,38 +0,0 @@
-package castorev1
-
-// RenamedNode returns a node with a new name.
-func RenamedNode(node *Node, name string) *Node {
-	if directoryNode := node.GetDirectory(); directoryNode != nil {
-		return &Node{
-			Node: &Node_Directory{
-				Directory: &DirectoryNode{
-					Name:   []byte(name),
-					Digest: directoryNode.GetDigest(),
-					Size:   directoryNode.GetSize(),
-				},
-			},
-		}
-	} else if fileNode := node.GetFile(); fileNode != nil {
-		return &Node{
-			Node: &Node_File{
-				File: &FileNode{
-					Name:       []byte(name),
-					Digest:     fileNode.GetDigest(),
-					Size:       fileNode.GetSize(),
-					Executable: fileNode.GetExecutable(),
-				},
-			},
-		}
-	} else if symlinkNode := node.GetSymlink(); symlinkNode != nil {
-		return &Node{
-			Node: &Node_Symlink{
-				Symlink: &SymlinkNode{
-					Name:   []byte(name),
-					Target: symlinkNode.GetTarget(),
-				},
-			},
-		}
-	} else {
-		panic("unreachable")
-	}
-}
diff --git a/tvix/castore/protos/rpc_blobstore.pb.go b/tvix/castore/protos/rpc_blobstore.pb.go
deleted file mode 100644
index 1afc826744..0000000000
--- a/tvix/castore/protos/rpc_blobstore.pb.go
+++ /dev/null
@@ -1,414 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright © 2022 The Tvix Authors
-
-// Code generated by protoc-gen-go. DO NOT EDIT.
-// versions:
-// 	protoc-gen-go v1.31.0
-// 	protoc        (unknown)
-// source: tvix/castore/protos/rpc_blobstore.proto
-
-package castorev1
-
-import (
-	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
-	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
-	reflect "reflect"
-	sync "sync"
-)
-
-const (
-	// Verify that this generated code is sufficiently up-to-date.
-	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
-	// Verify that runtime/protoimpl is sufficiently up-to-date.
-	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
-)
-
-type StatBlobRequest struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	// The blake3 digest of the blob requested
-	Digest []byte `protobuf:"bytes,1,opt,name=digest,proto3" json:"digest,omitempty"`
-}
-
-func (x *StatBlobRequest) Reset() {
-	*x = StatBlobRequest{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_tvix_castore_protos_rpc_blobstore_proto_msgTypes[0]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *StatBlobRequest) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*StatBlobRequest) ProtoMessage() {}
-
-func (x *StatBlobRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_tvix_castore_protos_rpc_blobstore_proto_msgTypes[0]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use StatBlobRequest.ProtoReflect.Descriptor instead.
-func (*StatBlobRequest) Descriptor() ([]byte, []int) {
-	return file_tvix_castore_protos_rpc_blobstore_proto_rawDescGZIP(), []int{0}
-}
-
-func (x *StatBlobRequest) GetDigest() []byte {
-	if x != nil {
-		return x.Digest
-	}
-	return nil
-}
-
-type BlobMeta struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-}
-
-func (x *BlobMeta) Reset() {
-	*x = BlobMeta{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_tvix_castore_protos_rpc_blobstore_proto_msgTypes[1]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *BlobMeta) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*BlobMeta) ProtoMessage() {}
-
-func (x *BlobMeta) ProtoReflect() protoreflect.Message {
-	mi := &file_tvix_castore_protos_rpc_blobstore_proto_msgTypes[1]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use BlobMeta.ProtoReflect.Descriptor instead.
-func (*BlobMeta) Descriptor() ([]byte, []int) {
-	return file_tvix_castore_protos_rpc_blobstore_proto_rawDescGZIP(), []int{1}
-}
-
-type ReadBlobRequest struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	// The blake3 digest of the blob or chunk requested
-	Digest []byte `protobuf:"bytes,1,opt,name=digest,proto3" json:"digest,omitempty"`
-}
-
-func (x *ReadBlobRequest) Reset() {
-	*x = ReadBlobRequest{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_tvix_castore_protos_rpc_blobstore_proto_msgTypes[2]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *ReadBlobRequest) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*ReadBlobRequest) ProtoMessage() {}
-
-func (x *ReadBlobRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_tvix_castore_protos_rpc_blobstore_proto_msgTypes[2]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use ReadBlobRequest.ProtoReflect.Descriptor instead.
-func (*ReadBlobRequest) Descriptor() ([]byte, []int) {
-	return file_tvix_castore_protos_rpc_blobstore_proto_rawDescGZIP(), []int{2}
-}
-
-func (x *ReadBlobRequest) GetDigest() []byte {
-	if x != nil {
-		return x.Digest
-	}
-	return nil
-}
-
-// This represents some bytes of a blob.
-// Blobs are sent in smaller chunks to keep message sizes manageable.
-type BlobChunk struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"`
-}
-
-func (x *BlobChunk) Reset() {
-	*x = BlobChunk{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_tvix_castore_protos_rpc_blobstore_proto_msgTypes[3]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *BlobChunk) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*BlobChunk) ProtoMessage() {}
-
-func (x *BlobChunk) ProtoReflect() protoreflect.Message {
-	mi := &file_tvix_castore_protos_rpc_blobstore_proto_msgTypes[3]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use BlobChunk.ProtoReflect.Descriptor instead.
-func (*BlobChunk) Descriptor() ([]byte, []int) {
-	return file_tvix_castore_protos_rpc_blobstore_proto_rawDescGZIP(), []int{3}
-}
-
-func (x *BlobChunk) GetData() []byte {
-	if x != nil {
-		return x.Data
-	}
-	return nil
-}
-
-type PutBlobResponse struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	// The blake3 digest of the data that was sent.
-	Digest []byte `protobuf:"bytes,1,opt,name=digest,proto3" json:"digest,omitempty"`
-}
-
-func (x *PutBlobResponse) Reset() {
-	*x = PutBlobResponse{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_tvix_castore_protos_rpc_blobstore_proto_msgTypes[4]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *PutBlobResponse) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*PutBlobResponse) ProtoMessage() {}
-
-func (x *PutBlobResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_tvix_castore_protos_rpc_blobstore_proto_msgTypes[4]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use PutBlobResponse.ProtoReflect.Descriptor instead.
-func (*PutBlobResponse) Descriptor() ([]byte, []int) {
-	return file_tvix_castore_protos_rpc_blobstore_proto_rawDescGZIP(), []int{4}
-}
-
-func (x *PutBlobResponse) GetDigest() []byte {
-	if x != nil {
-		return x.Digest
-	}
-	return nil
-}
-
-var File_tvix_castore_protos_rpc_blobstore_proto protoreflect.FileDescriptor
-
-var file_tvix_castore_protos_rpc_blobstore_proto_rawDesc = []byte{
-	0x0a, 0x27, 0x74, 0x76, 0x69, 0x78, 0x2f, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x70,
-	0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x72, 0x70, 0x63, 0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x74,
-	0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x74, 0x76, 0x69, 0x78, 0x2e,
-	0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x22, 0x29, 0x0a, 0x0f, 0x53, 0x74,
-	0x61, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a,
-	0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x64,
-	0x69, 0x67, 0x65, 0x73, 0x74, 0x22, 0x0a, 0x0a, 0x08, 0x42, 0x6c, 0x6f, 0x62, 0x4d, 0x65, 0x74,
-	0x61, 0x22, 0x29, 0x0a, 0x0f, 0x52, 0x65, 0x61, 0x64, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71,
-	0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x01,
-	0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x22, 0x1f, 0x0a, 0x09,
-	0x42, 0x6c, 0x6f, 0x62, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74,
-	0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x29, 0x0a,
-	0x0f, 0x50, 0x75, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
-	0x12, 0x16, 0x0a, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c,
-	0x52, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x32, 0xe1, 0x01, 0x0a, 0x0b, 0x42, 0x6c, 0x6f,
-	0x62, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x43, 0x0a, 0x04, 0x53, 0x74, 0x61, 0x74,
-	0x12, 0x20, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e,
-	0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65,
-	0x73, 0x74, 0x1a, 0x19, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72,
-	0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x46, 0x0a,
-	0x04, 0x52, 0x65, 0x61, 0x64, 0x12, 0x20, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x63, 0x61, 0x73,
-	0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x42, 0x6c, 0x6f, 0x62,
-	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x63,
-	0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x43, 0x68,
-	0x75, 0x6e, 0x6b, 0x30, 0x01, 0x12, 0x45, 0x0a, 0x03, 0x50, 0x75, 0x74, 0x12, 0x1a, 0x2e, 0x74,
-	0x76, 0x69, 0x78, 0x2e, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42,
-	0x6c, 0x6f, 0x62, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x1a, 0x20, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e,
-	0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x74, 0x42, 0x6c,
-	0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, 0x42, 0x2c, 0x5a, 0x2a,
-	0x63, 0x6f, 0x64, 0x65, 0x2e, 0x74, 0x76, 0x6c, 0x2e, 0x66, 0x79, 0x69, 0x2f, 0x74, 0x76, 0x69,
-	0x78, 0x2f, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
-	0x3b, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
-	0x6f, 0x33,
-}
-
-var (
-	file_tvix_castore_protos_rpc_blobstore_proto_rawDescOnce sync.Once
-	file_tvix_castore_protos_rpc_blobstore_proto_rawDescData = file_tvix_castore_protos_rpc_blobstore_proto_rawDesc
-)
-
-func file_tvix_castore_protos_rpc_blobstore_proto_rawDescGZIP() []byte {
-	file_tvix_castore_protos_rpc_blobstore_proto_rawDescOnce.Do(func() {
-		file_tvix_castore_protos_rpc_blobstore_proto_rawDescData = protoimpl.X.CompressGZIP(file_tvix_castore_protos_rpc_blobstore_proto_rawDescData)
-	})
-	return file_tvix_castore_protos_rpc_blobstore_proto_rawDescData
-}
-
-var file_tvix_castore_protos_rpc_blobstore_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
-var file_tvix_castore_protos_rpc_blobstore_proto_goTypes = []interface{}{
-	(*StatBlobRequest)(nil), // 0: tvix.castore.v1.StatBlobRequest
-	(*BlobMeta)(nil),        // 1: tvix.castore.v1.BlobMeta
-	(*ReadBlobRequest)(nil), // 2: tvix.castore.v1.ReadBlobRequest
-	(*BlobChunk)(nil),       // 3: tvix.castore.v1.BlobChunk
-	(*PutBlobResponse)(nil), // 4: tvix.castore.v1.PutBlobResponse
-}
-var file_tvix_castore_protos_rpc_blobstore_proto_depIdxs = []int32{
-	0, // 0: tvix.castore.v1.BlobService.Stat:input_type -> tvix.castore.v1.StatBlobRequest
-	2, // 1: tvix.castore.v1.BlobService.Read:input_type -> tvix.castore.v1.ReadBlobRequest
-	3, // 2: tvix.castore.v1.BlobService.Put:input_type -> tvix.castore.v1.BlobChunk
-	1, // 3: tvix.castore.v1.BlobService.Stat:output_type -> tvix.castore.v1.BlobMeta
-	3, // 4: tvix.castore.v1.BlobService.Read:output_type -> tvix.castore.v1.BlobChunk
-	4, // 5: tvix.castore.v1.BlobService.Put:output_type -> tvix.castore.v1.PutBlobResponse
-	3, // [3:6] is the sub-list for method output_type
-	0, // [0:3] is the sub-list for method input_type
-	0, // [0:0] is the sub-list for extension type_name
-	0, // [0:0] is the sub-list for extension extendee
-	0, // [0:0] is the sub-list for field type_name
-}
-
-func init() { file_tvix_castore_protos_rpc_blobstore_proto_init() }
-func file_tvix_castore_protos_rpc_blobstore_proto_init() {
-	if File_tvix_castore_protos_rpc_blobstore_proto != nil {
-		return
-	}
-	if !protoimpl.UnsafeEnabled {
-		file_tvix_castore_protos_rpc_blobstore_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*StatBlobRequest); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_tvix_castore_protos_rpc_blobstore_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*BlobMeta); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_tvix_castore_protos_rpc_blobstore_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*ReadBlobRequest); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_tvix_castore_protos_rpc_blobstore_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*BlobChunk); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_tvix_castore_protos_rpc_blobstore_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*PutBlobResponse); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-	}
-	type x struct{}
-	out := protoimpl.TypeBuilder{
-		File: protoimpl.DescBuilder{
-			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
-			RawDescriptor: file_tvix_castore_protos_rpc_blobstore_proto_rawDesc,
-			NumEnums:      0,
-			NumMessages:   5,
-			NumExtensions: 0,
-			NumServices:   1,
-		},
-		GoTypes:           file_tvix_castore_protos_rpc_blobstore_proto_goTypes,
-		DependencyIndexes: file_tvix_castore_protos_rpc_blobstore_proto_depIdxs,
-		MessageInfos:      file_tvix_castore_protos_rpc_blobstore_proto_msgTypes,
-	}.Build()
-	File_tvix_castore_protos_rpc_blobstore_proto = out.File
-	file_tvix_castore_protos_rpc_blobstore_proto_rawDesc = nil
-	file_tvix_castore_protos_rpc_blobstore_proto_goTypes = nil
-	file_tvix_castore_protos_rpc_blobstore_proto_depIdxs = nil
-}
diff --git a/tvix/castore/protos/rpc_blobstore_grpc.pb.go b/tvix/castore/protos/rpc_blobstore_grpc.pb.go
deleted file mode 100644
index 0876bcc4e9..0000000000
--- a/tvix/castore/protos/rpc_blobstore_grpc.pb.go
+++ /dev/null
@@ -1,274 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright © 2022 The Tvix Authors
-
-// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
-// versions:
-// - protoc-gen-go-grpc v1.3.0
-// - protoc             (unknown)
-// source: tvix/castore/protos/rpc_blobstore.proto
-
-package castorev1
-
-import (
-	context "context"
-	grpc "google.golang.org/grpc"
-	codes "google.golang.org/grpc/codes"
-	status "google.golang.org/grpc/status"
-)
-
-// This is a compile-time assertion to ensure that this generated file
-// is compatible with the grpc package it is being compiled against.
-// Requires gRPC-Go v1.32.0 or later.
-const _ = grpc.SupportPackageIsVersion7
-
-const (
-	BlobService_Stat_FullMethodName = "/tvix.castore.v1.BlobService/Stat"
-	BlobService_Read_FullMethodName = "/tvix.castore.v1.BlobService/Read"
-	BlobService_Put_FullMethodName  = "/tvix.castore.v1.BlobService/Put"
-)
-
-// BlobServiceClient is the client API for BlobService service.
-//
-// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
-type BlobServiceClient interface {
-	// In the future, Stat will expose more metadata about a given blob,
-	// such as more granular chunking, baos.
-	// For now, it's only used to check for the existence of a blob, as asking
-	// this for a non-existing Blob will return a Status::not_found gRPC error.
-	Stat(ctx context.Context, in *StatBlobRequest, opts ...grpc.CallOption) (*BlobMeta, error)
-	// Read returns a stream of BlobChunk, which is just a stream of bytes with
-	// the digest specified in ReadBlobRequest.
-	//
-	// The server may decide on whatever chunking it may seem fit as a size for
-	// the individual BlobChunk sent in the response stream.
-	Read(ctx context.Context, in *ReadBlobRequest, opts ...grpc.CallOption) (BlobService_ReadClient, error)
-	// Put uploads a Blob, by reading a stream of bytes.
-	//
-	// The way the data is chunked up in individual BlobChunk messages sent in
-	// the stream has no effect on how the server ends up chunking blobs up.
-	Put(ctx context.Context, opts ...grpc.CallOption) (BlobService_PutClient, error)
-}
-
-type blobServiceClient struct {
-	cc grpc.ClientConnInterface
-}
-
-func NewBlobServiceClient(cc grpc.ClientConnInterface) BlobServiceClient {
-	return &blobServiceClient{cc}
-}
-
-func (c *blobServiceClient) Stat(ctx context.Context, in *StatBlobRequest, opts ...grpc.CallOption) (*BlobMeta, error) {
-	out := new(BlobMeta)
-	err := c.cc.Invoke(ctx, BlobService_Stat_FullMethodName, in, out, opts...)
-	if err != nil {
-		return nil, err
-	}
-	return out, nil
-}
-
-func (c *blobServiceClient) Read(ctx context.Context, in *ReadBlobRequest, opts ...grpc.CallOption) (BlobService_ReadClient, error) {
-	stream, err := c.cc.NewStream(ctx, &BlobService_ServiceDesc.Streams[0], BlobService_Read_FullMethodName, opts...)
-	if err != nil {
-		return nil, err
-	}
-	x := &blobServiceReadClient{stream}
-	if err := x.ClientStream.SendMsg(in); err != nil {
-		return nil, err
-	}
-	if err := x.ClientStream.CloseSend(); err != nil {
-		return nil, err
-	}
-	return x, nil
-}
-
-type BlobService_ReadClient interface {
-	Recv() (*BlobChunk, error)
-	grpc.ClientStream
-}
-
-type blobServiceReadClient struct {
-	grpc.ClientStream
-}
-
-func (x *blobServiceReadClient) Recv() (*BlobChunk, error) {
-	m := new(BlobChunk)
-	if err := x.ClientStream.RecvMsg(m); err != nil {
-		return nil, err
-	}
-	return m, nil
-}
-
-func (c *blobServiceClient) Put(ctx context.Context, opts ...grpc.CallOption) (BlobService_PutClient, error) {
-	stream, err := c.cc.NewStream(ctx, &BlobService_ServiceDesc.Streams[1], BlobService_Put_FullMethodName, opts...)
-	if err != nil {
-		return nil, err
-	}
-	x := &blobServicePutClient{stream}
-	return x, nil
-}
-
-type BlobService_PutClient interface {
-	Send(*BlobChunk) error
-	CloseAndRecv() (*PutBlobResponse, error)
-	grpc.ClientStream
-}
-
-type blobServicePutClient struct {
-	grpc.ClientStream
-}
-
-func (x *blobServicePutClient) Send(m *BlobChunk) error {
-	return x.ClientStream.SendMsg(m)
-}
-
-func (x *blobServicePutClient) CloseAndRecv() (*PutBlobResponse, error) {
-	if err := x.ClientStream.CloseSend(); err != nil {
-		return nil, err
-	}
-	m := new(PutBlobResponse)
-	if err := x.ClientStream.RecvMsg(m); err != nil {
-		return nil, err
-	}
-	return m, nil
-}
-
-// BlobServiceServer is the server API for BlobService service.
-// All implementations must embed UnimplementedBlobServiceServer
-// for forward compatibility
-type BlobServiceServer interface {
-	// In the future, Stat will expose more metadata about a given blob,
-	// such as more granular chunking, baos.
-	// For now, it's only used to check for the existence of a blob, as asking
-	// this for a non-existing Blob will return a Status::not_found gRPC error.
-	Stat(context.Context, *StatBlobRequest) (*BlobMeta, error)
-	// Read returns a stream of BlobChunk, which is just a stream of bytes with
-	// the digest specified in ReadBlobRequest.
-	//
-	// The server may decide on whatever chunking it may seem fit as a size for
-	// the individual BlobChunk sent in the response stream.
-	Read(*ReadBlobRequest, BlobService_ReadServer) error
-	// Put uploads a Blob, by reading a stream of bytes.
-	//
-	// The way the data is chunked up in individual BlobChunk messages sent in
-	// the stream has no effect on how the server ends up chunking blobs up.
-	Put(BlobService_PutServer) error
-	mustEmbedUnimplementedBlobServiceServer()
-}
-
-// UnimplementedBlobServiceServer must be embedded to have forward compatible implementations.
-type UnimplementedBlobServiceServer struct {
-}
-
-func (UnimplementedBlobServiceServer) Stat(context.Context, *StatBlobRequest) (*BlobMeta, error) {
-	return nil, status.Errorf(codes.Unimplemented, "method Stat not implemented")
-}
-func (UnimplementedBlobServiceServer) Read(*ReadBlobRequest, BlobService_ReadServer) error {
-	return status.Errorf(codes.Unimplemented, "method Read not implemented")
-}
-func (UnimplementedBlobServiceServer) Put(BlobService_PutServer) error {
-	return status.Errorf(codes.Unimplemented, "method Put not implemented")
-}
-func (UnimplementedBlobServiceServer) mustEmbedUnimplementedBlobServiceServer() {}
-
-// UnsafeBlobServiceServer may be embedded to opt out of forward compatibility for this service.
-// Use of this interface is not recommended, as added methods to BlobServiceServer will
-// result in compilation errors.
-type UnsafeBlobServiceServer interface {
-	mustEmbedUnimplementedBlobServiceServer()
-}
-
-func RegisterBlobServiceServer(s grpc.ServiceRegistrar, srv BlobServiceServer) {
-	s.RegisterService(&BlobService_ServiceDesc, srv)
-}
-
-func _BlobService_Stat_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
-	in := new(StatBlobRequest)
-	if err := dec(in); err != nil {
-		return nil, err
-	}
-	if interceptor == nil {
-		return srv.(BlobServiceServer).Stat(ctx, in)
-	}
-	info := &grpc.UnaryServerInfo{
-		Server:     srv,
-		FullMethod: BlobService_Stat_FullMethodName,
-	}
-	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
-		return srv.(BlobServiceServer).Stat(ctx, req.(*StatBlobRequest))
-	}
-	return interceptor(ctx, in, info, handler)
-}
-
-func _BlobService_Read_Handler(srv interface{}, stream grpc.ServerStream) error {
-	m := new(ReadBlobRequest)
-	if err := stream.RecvMsg(m); err != nil {
-		return err
-	}
-	return srv.(BlobServiceServer).Read(m, &blobServiceReadServer{stream})
-}
-
-type BlobService_ReadServer interface {
-	Send(*BlobChunk) error
-	grpc.ServerStream
-}
-
-type blobServiceReadServer struct {
-	grpc.ServerStream
-}
-
-func (x *blobServiceReadServer) Send(m *BlobChunk) error {
-	return x.ServerStream.SendMsg(m)
-}
-
-func _BlobService_Put_Handler(srv interface{}, stream grpc.ServerStream) error {
-	return srv.(BlobServiceServer).Put(&blobServicePutServer{stream})
-}
-
-type BlobService_PutServer interface {
-	SendAndClose(*PutBlobResponse) error
-	Recv() (*BlobChunk, error)
-	grpc.ServerStream
-}
-
-type blobServicePutServer struct {
-	grpc.ServerStream
-}
-
-func (x *blobServicePutServer) SendAndClose(m *PutBlobResponse) error {
-	return x.ServerStream.SendMsg(m)
-}
-
-func (x *blobServicePutServer) Recv() (*BlobChunk, error) {
-	m := new(BlobChunk)
-	if err := x.ServerStream.RecvMsg(m); err != nil {
-		return nil, err
-	}
-	return m, nil
-}
-
-// BlobService_ServiceDesc is the grpc.ServiceDesc for BlobService service.
-// It's only intended for direct use with grpc.RegisterService,
-// and not to be introspected or modified (even as a copy)
-var BlobService_ServiceDesc = grpc.ServiceDesc{
-	ServiceName: "tvix.castore.v1.BlobService",
-	HandlerType: (*BlobServiceServer)(nil),
-	Methods: []grpc.MethodDesc{
-		{
-			MethodName: "Stat",
-			Handler:    _BlobService_Stat_Handler,
-		},
-	},
-	Streams: []grpc.StreamDesc{
-		{
-			StreamName:    "Read",
-			Handler:       _BlobService_Read_Handler,
-			ServerStreams: true,
-		},
-		{
-			StreamName:    "Put",
-			Handler:       _BlobService_Put_Handler,
-			ClientStreams: true,
-		},
-	},
-	Metadata: "tvix/castore/protos/rpc_blobstore.proto",
-}
diff --git a/tvix/castore/protos/rpc_directory.pb.go b/tvix/castore/protos/rpc_directory.pb.go
deleted file mode 100644
index f658c6b60c..0000000000
--- a/tvix/castore/protos/rpc_directory.pb.go
+++ /dev/null
@@ -1,273 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright © 2022 The Tvix Authors
-
-// Code generated by protoc-gen-go. DO NOT EDIT.
-// versions:
-// 	protoc-gen-go v1.31.0
-// 	protoc        (unknown)
-// source: tvix/castore/protos/rpc_directory.proto
-
-package castorev1
-
-import (
-	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
-	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
-	reflect "reflect"
-	sync "sync"
-)
-
-const (
-	// Verify that this generated code is sufficiently up-to-date.
-	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
-	// Verify that runtime/protoimpl is sufficiently up-to-date.
-	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
-)
-
-type GetDirectoryRequest struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	// Types that are assignable to ByWhat:
-	//
-	//	*GetDirectoryRequest_Digest
-	ByWhat isGetDirectoryRequest_ByWhat `protobuf_oneof:"by_what"`
-	// If set to true, recursively resolve all child Directory messages.
-	// Directory messages SHOULD be streamed in a recursive breadth-first walk,
-	// but other orders are also fine, as long as Directory messages are only
-	// sent after they are referred to from previously sent Directory messages.
-	Recursive bool `protobuf:"varint,2,opt,name=recursive,proto3" json:"recursive,omitempty"`
-}
-
-func (x *GetDirectoryRequest) Reset() {
-	*x = GetDirectoryRequest{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_tvix_castore_protos_rpc_directory_proto_msgTypes[0]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *GetDirectoryRequest) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*GetDirectoryRequest) ProtoMessage() {}
-
-func (x *GetDirectoryRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_tvix_castore_protos_rpc_directory_proto_msgTypes[0]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use GetDirectoryRequest.ProtoReflect.Descriptor instead.
-func (*GetDirectoryRequest) Descriptor() ([]byte, []int) {
-	return file_tvix_castore_protos_rpc_directory_proto_rawDescGZIP(), []int{0}
-}
-
-func (m *GetDirectoryRequest) GetByWhat() isGetDirectoryRequest_ByWhat {
-	if m != nil {
-		return m.ByWhat
-	}
-	return nil
-}
-
-func (x *GetDirectoryRequest) GetDigest() []byte {
-	if x, ok := x.GetByWhat().(*GetDirectoryRequest_Digest); ok {
-		return x.Digest
-	}
-	return nil
-}
-
-func (x *GetDirectoryRequest) GetRecursive() bool {
-	if x != nil {
-		return x.Recursive
-	}
-	return false
-}
-
-type isGetDirectoryRequest_ByWhat interface {
-	isGetDirectoryRequest_ByWhat()
-}
-
-type GetDirectoryRequest_Digest struct {
-	// The blake3 hash of the (root) Directory message, serialized in
-	// protobuf canonical form.
-	// Keep in mind this can be a subtree of another root.
-	Digest []byte `protobuf:"bytes,1,opt,name=digest,proto3,oneof"`
-}
-
-func (*GetDirectoryRequest_Digest) isGetDirectoryRequest_ByWhat() {}
-
-type PutDirectoryResponse struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	RootDigest []byte `protobuf:"bytes,1,opt,name=root_digest,json=rootDigest,proto3" json:"root_digest,omitempty"`
-}
-
-func (x *PutDirectoryResponse) Reset() {
-	*x = PutDirectoryResponse{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_tvix_castore_protos_rpc_directory_proto_msgTypes[1]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *PutDirectoryResponse) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*PutDirectoryResponse) ProtoMessage() {}
-
-func (x *PutDirectoryResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_tvix_castore_protos_rpc_directory_proto_msgTypes[1]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use PutDirectoryResponse.ProtoReflect.Descriptor instead.
-func (*PutDirectoryResponse) Descriptor() ([]byte, []int) {
-	return file_tvix_castore_protos_rpc_directory_proto_rawDescGZIP(), []int{1}
-}
-
-func (x *PutDirectoryResponse) GetRootDigest() []byte {
-	if x != nil {
-		return x.RootDigest
-	}
-	return nil
-}
-
-var File_tvix_castore_protos_rpc_directory_proto protoreflect.FileDescriptor
-
-var file_tvix_castore_protos_rpc_directory_proto_rawDesc = []byte{
-	0x0a, 0x27, 0x74, 0x76, 0x69, 0x78, 0x2f, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x70,
-	0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x72, 0x70, 0x63, 0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74,
-	0x6f, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x74, 0x76, 0x69, 0x78, 0x2e,
-	0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x21, 0x74, 0x76, 0x69, 0x78,
-	0x2f, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f,
-	0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x58, 0x0a,
-	0x13, 0x47, 0x65, 0x74, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71,
-	0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x01,
-	0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x1c,
-	0x0a, 0x09, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
-	0x08, 0x52, 0x09, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x42, 0x09, 0x0a, 0x07,
-	0x62, 0x79, 0x5f, 0x77, 0x68, 0x61, 0x74, 0x22, 0x37, 0x0a, 0x14, 0x50, 0x75, 0x74, 0x44, 0x69,
-	0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
-	0x1f, 0x0a, 0x0b, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x01,
-	0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x72, 0x6f, 0x6f, 0x74, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74,
-	0x32, 0xa9, 0x01, 0x0a, 0x10, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65,
-	0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x49, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x24, 0x2e, 0x74,
-	0x76, 0x69, 0x78, 0x2e, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47,
-	0x65, 0x74, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65,
-	0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72,
-	0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x30, 0x01,
-	0x12, 0x4a, 0x0a, 0x03, 0x50, 0x75, 0x74, 0x12, 0x1a, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x63,
-	0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74,
-	0x6f, 0x72, 0x79, 0x1a, 0x25, 0x2e, 0x74, 0x76, 0x69, 0x78, 0x2e, 0x63, 0x61, 0x73, 0x74, 0x6f,
-	0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x74, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f,
-	0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, 0x42, 0x2c, 0x5a, 0x2a,
-	0x63, 0x6f, 0x64, 0x65, 0x2e, 0x74, 0x76, 0x6c, 0x2e, 0x66, 0x79, 0x69, 0x2f, 0x74, 0x76, 0x69,
-	0x78, 0x2f, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
-	0x3b, 0x63, 0x61, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
-	0x6f, 0x33,
-}
-
-var (
-	file_tvix_castore_protos_rpc_directory_proto_rawDescOnce sync.Once
-	file_tvix_castore_protos_rpc_directory_proto_rawDescData = file_tvix_castore_protos_rpc_directory_proto_rawDesc
-)
-
-func file_tvix_castore_protos_rpc_directory_proto_rawDescGZIP() []byte {
-	file_tvix_castore_protos_rpc_directory_proto_rawDescOnce.Do(func() {
-		file_tvix_castore_protos_rpc_directory_proto_rawDescData = protoimpl.X.CompressGZIP(file_tvix_castore_protos_rpc_directory_proto_rawDescData)
-	})
-	return file_tvix_castore_protos_rpc_directory_proto_rawDescData
-}
-
-var file_tvix_castore_protos_rpc_directory_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
-var file_tvix_castore_protos_rpc_directory_proto_goTypes = []interface{}{
-	(*GetDirectoryRequest)(nil),  // 0: tvix.castore.v1.GetDirectoryRequest
-	(*PutDirectoryResponse)(nil), // 1: tvix.castore.v1.PutDirectoryResponse
-	(*Directory)(nil),            // 2: tvix.castore.v1.Directory
-}
-var file_tvix_castore_protos_rpc_directory_proto_depIdxs = []int32{
-	0, // 0: tvix.castore.v1.DirectoryService.Get:input_type -> tvix.castore.v1.GetDirectoryRequest
-	2, // 1: tvix.castore.v1.DirectoryService.Put:input_type -> tvix.castore.v1.Directory
-	2, // 2: tvix.castore.v1.DirectoryService.Get:output_type -> tvix.castore.v1.Directory
-	1, // 3: tvix.castore.v1.DirectoryService.Put:output_type -> tvix.castore.v1.PutDirectoryResponse
-	2, // [2:4] is the sub-list for method output_type
-	0, // [0:2] is the sub-list for method input_type
-	0, // [0:0] is the sub-list for extension type_name
-	0, // [0:0] is the sub-list for extension extendee
-	0, // [0:0] is the sub-list for field type_name
-}
-
-func init() { file_tvix_castore_protos_rpc_directory_proto_init() }
-func file_tvix_castore_protos_rpc_directory_proto_init() {
-	if File_tvix_castore_protos_rpc_directory_proto != nil {
-		return
-	}
-	file_tvix_castore_protos_castore_proto_init()
-	if !protoimpl.UnsafeEnabled {
-		file_tvix_castore_protos_rpc_directory_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*GetDirectoryRequest); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_tvix_castore_protos_rpc_directory_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*PutDirectoryResponse); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-	}
-	file_tvix_castore_protos_rpc_directory_proto_msgTypes[0].OneofWrappers = []interface{}{
-		(*GetDirectoryRequest_Digest)(nil),
-	}
-	type x struct{}
-	out := protoimpl.TypeBuilder{
-		File: protoimpl.DescBuilder{
-			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
-			RawDescriptor: file_tvix_castore_protos_rpc_directory_proto_rawDesc,
-			NumEnums:      0,
-			NumMessages:   2,
-			NumExtensions: 0,
-			NumServices:   1,
-		},
-		GoTypes:           file_tvix_castore_protos_rpc_directory_proto_goTypes,
-		DependencyIndexes: file_tvix_castore_protos_rpc_directory_proto_depIdxs,
-		MessageInfos:      file_tvix_castore_protos_rpc_directory_proto_msgTypes,
-	}.Build()
-	File_tvix_castore_protos_rpc_directory_proto = out.File
-	file_tvix_castore_protos_rpc_directory_proto_rawDesc = nil
-	file_tvix_castore_protos_rpc_directory_proto_goTypes = nil
-	file_tvix_castore_protos_rpc_directory_proto_depIdxs = nil
-}
diff --git a/tvix/castore/protos/rpc_directory_grpc.pb.go b/tvix/castore/protos/rpc_directory_grpc.pb.go
deleted file mode 100644
index f19e457d86..0000000000
--- a/tvix/castore/protos/rpc_directory_grpc.pb.go
+++ /dev/null
@@ -1,238 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright © 2022 The Tvix Authors
-
-// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
-// versions:
-// - protoc-gen-go-grpc v1.3.0
-// - protoc             (unknown)
-// source: tvix/castore/protos/rpc_directory.proto
-
-package castorev1
-
-import (
-	context "context"
-	grpc "google.golang.org/grpc"
-	codes "google.golang.org/grpc/codes"
-	status "google.golang.org/grpc/status"
-)
-
-// This is a compile-time assertion to ensure that this generated file
-// is compatible with the grpc package it is being compiled against.
-// Requires gRPC-Go v1.32.0 or later.
-const _ = grpc.SupportPackageIsVersion7
-
-const (
-	DirectoryService_Get_FullMethodName = "/tvix.castore.v1.DirectoryService/Get"
-	DirectoryService_Put_FullMethodName = "/tvix.castore.v1.DirectoryService/Put"
-)
-
-// DirectoryServiceClient is the client API for DirectoryService service.
-//
-// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
-type DirectoryServiceClient interface {
-	// Get retrieves a stream of Directory messages, by using the lookup
-	// parameters in GetDirectoryRequest.
-	// Keep in mind multiple DirectoryNodes in different parts of the graph might
-	// have the same digest if they have the same underlying contents,
-	// so sending subsequent ones can be omitted.
-	Get(ctx context.Context, in *GetDirectoryRequest, opts ...grpc.CallOption) (DirectoryService_GetClient, error)
-	// Put uploads a graph of Directory messages.
-	// Individual Directory messages need to be send in an order walking up
-	// from the leaves to the root - a Directory message can only refer to
-	// Directory messages previously sent in the same stream.
-	// Keep in mind multiple DirectoryNodes in different parts of the graph might
-	// have the same digest if they have the same underlying contents,
-	// so sending subsequent ones can be omitted.
-	// We might add a separate method, allowing to send partial graphs at a later
-	// time, if requiring to send the full graph turns out to be a problem.
-	Put(ctx context.Context, opts ...grpc.CallOption) (DirectoryService_PutClient, error)
-}
-
-type directoryServiceClient struct {
-	cc grpc.ClientConnInterface
-}
-
-func NewDirectoryServiceClient(cc grpc.ClientConnInterface) DirectoryServiceClient {
-	return &directoryServiceClient{cc}
-}
-
-func (c *directoryServiceClient) Get(ctx context.Context, in *GetDirectoryRequest, opts ...grpc.CallOption) (DirectoryService_GetClient, error) {
-	stream, err := c.cc.NewStream(ctx, &DirectoryService_ServiceDesc.Streams[0], DirectoryService_Get_FullMethodName, opts...)
-	if err != nil {
-		return nil, err
-	}
-	x := &directoryServiceGetClient{stream}
-	if err := x.ClientStream.SendMsg(in); err != nil {
-		return nil, err
-	}
-	if err := x.ClientStream.CloseSend(); err != nil {
-		return nil, err
-	}
-	return x, nil
-}
-
-type DirectoryService_GetClient interface {
-	Recv() (*Directory, error)
-	grpc.ClientStream
-}
-
-type directoryServiceGetClient struct {
-	grpc.ClientStream
-}
-
-func (x *directoryServiceGetClient) Recv() (*Directory, error) {
-	m := new(Directory)
-	if err := x.ClientStream.RecvMsg(m); err != nil {
-		return nil, err
-	}
-	return m, nil
-}
-
-func (c *directoryServiceClient) Put(ctx context.Context, opts ...grpc.CallOption) (DirectoryService_PutClient, error) {
-	stream, err := c.cc.NewStream(ctx, &DirectoryService_ServiceDesc.Streams[1], DirectoryService_Put_FullMethodName, opts...)
-	if err != nil {
-		return nil, err
-	}
-	x := &directoryServicePutClient{stream}
-	return x, nil
-}
-
-type DirectoryService_PutClient interface {
-	Send(*Directory) error
-	CloseAndRecv() (*PutDirectoryResponse, error)
-	grpc.ClientStream
-}
-
-type directoryServicePutClient struct {
-	grpc.ClientStream
-}
-
-func (x *directoryServicePutClient) Send(m *Directory) error {
-	return x.ClientStream.SendMsg(m)
-}
-
-func (x *directoryServicePutClient) CloseAndRecv() (*PutDirectoryResponse, error) {
-	if err := x.ClientStream.CloseSend(); err != nil {
-		return nil, err
-	}
-	m := new(PutDirectoryResponse)
-	if err := x.ClientStream.RecvMsg(m); err != nil {
-		return nil, err
-	}
-	return m, nil
-}
-
-// DirectoryServiceServer is the server API for DirectoryService service.
-// All implementations must embed UnimplementedDirectoryServiceServer
-// for forward compatibility
-type DirectoryServiceServer interface {
-	// Get retrieves a stream of Directory messages, by using the lookup
-	// parameters in GetDirectoryRequest.
-	// Keep in mind multiple DirectoryNodes in different parts of the graph might
-	// have the same digest if they have the same underlying contents,
-	// so sending subsequent ones can be omitted.
-	Get(*GetDirectoryRequest, DirectoryService_GetServer) error
-	// Put uploads a graph of Directory messages.
-	// Individual Directory messages need to be send in an order walking up
-	// from the leaves to the root - a Directory message can only refer to
-	// Directory messages previously sent in the same stream.
-	// Keep in mind multiple DirectoryNodes in different parts of the graph might
-	// have the same digest if they have the same underlying contents,
-	// so sending subsequent ones can be omitted.
-	// We might add a separate method, allowing to send partial graphs at a later
-	// time, if requiring to send the full graph turns out to be a problem.
-	Put(DirectoryService_PutServer) error
-	mustEmbedUnimplementedDirectoryServiceServer()
-}
-
-// UnimplementedDirectoryServiceServer must be embedded to have forward compatible implementations.
-type UnimplementedDirectoryServiceServer struct {
-}
-
-func (UnimplementedDirectoryServiceServer) Get(*GetDirectoryRequest, DirectoryService_GetServer) error {
-	return status.Errorf(codes.Unimplemented, "method Get not implemented")
-}
-func (UnimplementedDirectoryServiceServer) Put(DirectoryService_PutServer) error {
-	return status.Errorf(codes.Unimplemented, "method Put not implemented")
-}
-func (UnimplementedDirectoryServiceServer) mustEmbedUnimplementedDirectoryServiceServer() {}
-
-// UnsafeDirectoryServiceServer may be embedded to opt out of forward compatibility for this service.
-// Use of this interface is not recommended, as added methods to DirectoryServiceServer will
-// result in compilation errors.
-type UnsafeDirectoryServiceServer interface {
-	mustEmbedUnimplementedDirectoryServiceServer()
-}
-
-func RegisterDirectoryServiceServer(s grpc.ServiceRegistrar, srv DirectoryServiceServer) {
-	s.RegisterService(&DirectoryService_ServiceDesc, srv)
-}
-
-func _DirectoryService_Get_Handler(srv interface{}, stream grpc.ServerStream) error {
-	m := new(GetDirectoryRequest)
-	if err := stream.RecvMsg(m); err != nil {
-		return err
-	}
-	return srv.(DirectoryServiceServer).Get(m, &directoryServiceGetServer{stream})
-}
-
-type DirectoryService_GetServer interface {
-	Send(*Directory) error
-	grpc.ServerStream
-}
-
-type directoryServiceGetServer struct {
-	grpc.ServerStream
-}
-
-func (x *directoryServiceGetServer) Send(m *Directory) error {
-	return x.ServerStream.SendMsg(m)
-}
-
-func _DirectoryService_Put_Handler(srv interface{}, stream grpc.ServerStream) error {
-	return srv.(DirectoryServiceServer).Put(&directoryServicePutServer{stream})
-}
-
-type DirectoryService_PutServer interface {
-	SendAndClose(*PutDirectoryResponse) error
-	Recv() (*Directory, error)
-	grpc.ServerStream
-}
-
-type directoryServicePutServer struct {
-	grpc.ServerStream
-}
-
-func (x *directoryServicePutServer) SendAndClose(m *PutDirectoryResponse) error {
-	return x.ServerStream.SendMsg(m)
-}
-
-func (x *directoryServicePutServer) Recv() (*Directory, error) {
-	m := new(Directory)
-	if err := x.ServerStream.RecvMsg(m); err != nil {
-		return nil, err
-	}
-	return m, nil
-}
-
-// DirectoryService_ServiceDesc is the grpc.ServiceDesc for DirectoryService service.
-// It's only intended for direct use with grpc.RegisterService,
-// and not to be introspected or modified (even as a copy)
-var DirectoryService_ServiceDesc = grpc.ServiceDesc{
-	ServiceName: "tvix.castore.v1.DirectoryService",
-	HandlerType: (*DirectoryServiceServer)(nil),
-	Methods:     []grpc.MethodDesc{},
-	Streams: []grpc.StreamDesc{
-		{
-			StreamName:    "Get",
-			Handler:       _DirectoryService_Get_Handler,
-			ServerStreams: true,
-		},
-		{
-			StreamName:    "Put",
-			Handler:       _DirectoryService_Put_Handler,
-			ClientStreams: true,
-		},
-	},
-	Metadata: "tvix/castore/protos/rpc_directory.proto",
-}