TinyTree thesis

Hello TinyTree

By Son Luong

As I started sharing TinyTree with folks, I started getting a lot of questions about this project:

What is TinyTree? What is a neo-forge? What is a monorepo? Why SCM and not VCS?

Let me walk you through the problem I am trying to solve and the way I plan to solve it.

First-class monorepos

Up until now, monorepo tooling in the wild has still been lacking. It remains a $BIG_TECH-exclusive technology. If you talk to anybody who worked there, they will tell you how superior that setup is. The more avid users will also tell you why it is better.

It is because dedicated teams work full time to keep a single repository healthy.

They build custom version-control tooling when users feel blocked. They build highly scalable servers to unblock automated commits. They build custom code review tools and countless integrations with internal services to provide additional context to authors and reviewers. They build custom virtual file systems for source code with first-class build tool and IDE integration.

Most importantly, they have the $BIG_TECH money to keep funding those teams and keep multiplying engineering productivity.

Monorepos make it easier for these DevX teams to function because they have a single place to pour their resources into, instead of tens of thousands of Git repos scattered across different namespaces. In turn, these DevX teams make it easier for their fellow engineers to do their jobs.

I want to bring that ecosystem to the masses. Not just by building cool technology, but also by building an ecosystem around that technology, starting with a functional commercial business dedicated to promoting monorepo technologies. That is TinyTree in a nutshell.

The how

So what is special about a monorepo designed for large source trees with many boundaries?

Narrow clones

The first thing is that you will not need most of that code on a day-to-day basis. You will only need the pieces that you care about. It is extremely inconvenient to clone the entire Linux.git repository just to work on one file. Because of that, a TinyTree monorepo will never start as a full clone.

Instead, we use narrow clones. Inside a narrow clone, the working copy of the repo is defined by a sparse checkout, and the downloaded change history is shallow. You get to craft your own sparse checkout profile, also known as a view, and we take care of the rest.

These views are defined in config files and tracked inside the monorepo itself. The config files are written in Starlark and can be generated using our tt view create CLI subcommand. Users without a default view configured will be able to discover views their teammates have already crafted for them.

A narrow checkout should make those boundaries obvious:

$ tt view client
# switched to view "client"
# checked out:
#   client/**
#   shared/api/**
#   shared/proto/public/**
# hidden:
#   server/**
#   infra/**
# history downloaded:
#   184 commits

$ tt log --oneline
# f3c2a18 client: import gh repos
# 9bd71e4 client: add onboarding view
# c8aa421 shared: publish public API types

$ tt view server
# switched to view "server"
# checked out:
#   server/**
#   shared/api/**
#   shared/proto/public/**
# hidden:
#   client/**
#   web/**
# history downloaded:
#   231 commits

$ tt log --oneline
# 73a1b5c server: import auth service
# 11d2f09 server: wire request router
# c8aa421 shared: publish public API types

By being narrow by default, we enable many more use cases that are typically not possible with a full-clone setup. More things can be added to the monorepo simply as new directories.

For example, instead of using tags for versioned releases, users can do something like tt cp src release/1.0.1 to create a new release using a copy of the current source tree at HEAD. Releases are never included in the working copy of your view unless you explicitly configure them to be included. As we gradually move our backend off Git into TinyTree’s own data model, tt cp can add additional metadata to track the copy relationship so that something like tt log -- release/1.0.1 will also give you the history from src at the cutoff point.

Access control

So how are you keeping secrets in this repo?

The secret is just a config file under .tt, but that file is only included in an admin-only view. That means only users with admin roles can check out the file, inspect its history, and modify it.

This is where .tt gets special system .scl files. Unlike tree-local SRC, .tt/*.scl is system configuration: user roles, privileged views, and other repo-level control-plane state.

role(
    name = "admin",
    users = [
        "son",
    ],
    groups = [
        "platform-admins",
    ],
)

view(
    name = "admin-only",
    include = [
        ".tt/access.scl",
        ".tt/secrets/**",
    ],
    roles = [
        "admin",
    ],
)

For everybody else, those files simply do not exist in their view. They cannot check them out by accident, browse their history, or send a change that modifies them. The boundary is declared as source, reviewed as source, and enforced through the view they are allowed to work in.

SRC Automation

Another interesting thing that can become source is repository settings. Instead of using a web UI to control tree import, export, and automation, those settings can become infrastructure as code living in fine-grained SRC files throughout the monorepo.

Think of SRC files like Bazel’s BUILD files. A client/SRC file describes the client tree by default, is reviewed with the code, and can be owned by the same CODEOWNERS entry. The repo admin should not be the bottleneck for a team changing its own import, export, or automation config.

Bazel mostly reads source files to produce artifacts. It keeps the source tree in read-only mode and makes it hard for users to accidentally mutate the source tree. SRC is the opposite side of that model. It is designed to mutate, update, create, and delete source trees. Inside the monorepo, SRC automation is the missing half that complements hermetic build tools like Bazel and Buck2.

The config model should stay narrow. A SRC file should describe concrete tree relationships, not arbitrary source generation logic. The target is implicit from the directory that owns the SRC file; an explicit target can exist for exceptions, but it should not be in the common path.

But we are not just rethinking package managers here. The primitive is more powerful than that. For example, with something like this:

owner(teams = ["//.tt/teams:source_infra"])

import_git(
    name = "client",
    remote = "https://github.com/foo/oss_client",
    branch = "main",
    patches = ["patches/internal_build_targets.patch"],
)

export_git(
    name = "oss-client",
    remote = "https://github.com/foo/oss_client",
    branch = "main",
    sanitizer = "go/cmd/src_sanitizer",
)

you should effectively be able to create a two-way source mirror between a tree in your monorepo and your open source counterpart.

Unlike YAML, Starlark is a powerful and expressive language that helps us encapsulate complex automation logic in simple, readable configuration. Users will be able to import and export trees from and to different remote sources. They will also be able to validate and transform changes as they enter and leave the monorepo.

Our goal is to help you manage forked and patched source code better. In the age of agentic coding, we expect there will be a lot of it: dependencies that need just a little nudge to work for your use case. We are here to help you tend those gardens of grafted forks.

But we are not stopping at just trees. We intend to use the same configuration pattern for fundamental SCM primitives such as code review rules and pre/post-receive hooks, which will then enable CI/CD integrations.

Closing note

The obvious question is whether we can do all of this while still using Git underneath.

The answer is yes, for a while. That compatibility is the whole point of the first phase. But if we try to expose all of these ideas through raw Git, the UX gets confusing very quickly. Git’s packfile storage model and transfer protocol are also not efficient for sparse checkouts over very large trees, and they are not a secure foundation for maintaining separate views with strict ACL enforcement. If a user can fetch the object graph behind hidden paths, the view boundary is only a convention.

So the plan is staged.

First, TinyTree should help teams onboard easily by importing existing code into TinyTree and exporting selected trees back out to normal Git repositories elsewhere.

Then we introduce a new tt client, still wrapped around Git, with a simpler UX for working in views and gradually adopting new TinyTree features.

Eventually, TinyTree monorepos move onto a new data model designed for views from the ground up. That is where the more interesting features unlock: efficient sparse history, stricter access boundaries, and source automation that treats the tree as the product surface, not just a folder Git happens to store.

Stay tuned for more.

Son Luong

Join waitlist

Follow TinyTree as it grows.

Early design notes, prototype updates, and beta access as the import/view/export model takes shape.

No launch spam. Just the useful milestones.