# Brioche: a package manager whose build scripts are TypeScript

> Brioche is a Rust package manager and build tool where a project's build is a TypeScript program rather than a list of shell commands, with caching, lockfiles and OCI image output. It is pre 1.0 and its most interesting feature is also its largest bet, that build logic belongs in a typed language.

**brioche-dev/brioche** — A delicious package manager

- Repository: https://github.com/brioche-dev/brioche
- Website: https://brioche.dev
- Stars: 526 · Forks: 9
- Language: Rust
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/brioche-dev-brioche

## The build script is the program, not the shell

Brioche describes itself as a package manager and build tool for building and running your most complex software projects, and the unusual thing about it is that the build is a program you write in a real language.

The readme's example is a complete Brioche build file.

```ts
import * as std from "std";
import { cargoBuild } from "rust";

export default () => {
  // Build a Rust project
  const server = cargoBuild({
    crate: Brioche.glob("src", "Cargo.*"),
  });

  // Put it in a container image
  return std.ociContainerImage({
    recipe: server,
    entrypoint: ["/bin/server"],
  });
}
```

Read it as a program and the design becomes clear. There are imports, from std and from a language-specific module called rust. There is an exported default function, which is the build. Inside it, cargoBuild takes a crate pattern, and the result is fed into std.ociContainerImage with an entrypoint, so the build's output is a container image.

Compare that with a Makefile or a shell script. A Makefile expresses targets and dependencies; a shell script expresses commands in order, and if you need a conditional you write shell logic that a reader has to interpret. A TypeScript build file expresses the same work with types, so a mistake in the name of a function or an option is a compile error rather than a runtime surprise. The readme's feature list says this plainly: build scripts are written in TypeScript, giving you the flexibility of a familiar full programming language, with great type checking and editor completions.

The project.bri file at the repository root is the build script for the project itself, and the brioche.lock file at the root is its lockfile. So Brioche uses itself.

## Caching and lockfiles, and the S3 escape hatch

The two features that make a package manager a package manager are caching and lockfiles, and Brioche implements both with a specific twist.

On caching, the readme says all build artifacts are saved and re-used between builds, saving time when only part of the project changes. The twist is that you can share the cache between machines using an S3-compatible storage provider. That is a real answer to a problem that most build caches do not solve. A local cache on a developer's laptop does not help CI, and a CI cache is usually scoped to one provider's key value store. An S3 bucket is something almost every organisation already has, and it means a developer and a CI runner can share one cache.

On lockfiles, the readme says all of your dependencies are automatically saved in a lockfile, making builds reliable for everyone on the team. The word automatically is doing work there. For a project with a Rust crate, a Node package, a Python wheel and a Go module in the same tree, the usual situation is four lockfiles, each covering part of the dependency graph, each generated by a different tool, and each potentially resolving versions differently. Brioche's model is one lockfile at the root, and the readme's cross-ecosystem feature, that you can build your project regardless of language and easily mix different languages and ecosystems in one project, is the reason that single lockfile is possible.

The two features work together. The lockfile says what the inputs are, and the cache means that when the inputs have not changed the build does not have to run again. The same cached artifact is reused on a colleague's machine and in CI, which is only true if the inputs are pinned identically, and the lockfile is what pins them.

The repository root confirms the shape of the implementation. There is a Cargo.lock for the Rust workspace, and separately a brioche.lock for the Brioche dependency graph, so the tool's own build is bootstrapped with the same mechanism it offers.

## Installing Brioche, and running it from source

The quick install is a shell one-liner over HTTPS with TLS 1.2 pinned.

```sh
curl --proto '=https' --tlsv1.2 -sSfL 'https://brioche.dev/install.sh' | sh
```

The flags are deliberate rather than decorative. Setting the protocol to HTTPS only stops a downgrade to plain HTTP, and requiring TLS 1.2 as a minimum excludes older stacks, so the fetch cannot be silently intercepted on a network with a hostile proxy. The readme points to the official installation documentation for other options, which is where a source build, a specific version or a platform specific path would be.

Building from source is a standard cargo install.

The readme says to check out the repository and run cargo install --locked --path crates/brioche, which installs Brioche into ~/.cargo/bin, and that you can also run it as a normal Rust project using cargo run.

The workspace layout explains what is actually in the binary. The Cargo.toml lists four workspace members, crates/brioche, crates/brioche-core, crates/brioche-pack and crates/brioche-test-support, with resolver 3 and edition 2024. The rust-version field is 1.97, and the comment next to it says it is to align with the rust-toolchain.toml file, which is in the repository root. So the toolchain is pinned twice, in the manifest and in the file rustup reads, and the comment records why.

The lint configuration is worth a look for anyone judging the project's engineering standards. Clippy is set to warn on all, nursery and pedantic, with a list of individual lints allowed, including cognitive_complexity, future_not_send, missing_errors_doc, missing_panics_doc, struct_excessive_bools, too_many_lines and struct_field_names, and print_stdout and print_stderr are warned so that the library does not write to the terminal directly. The release profile strips debuginfo.

A separate repository, brioche-packages, holds the Brioche packages themselves, which is the catalogue of language support. The readme points there rather than listing what is available, so judging coverage means reading that repository.

## The two work in progress features are the ones to watch

Two of the six features in the readme are marked work in progress, and they are the interesting ones.

Cross-compilation is described as work in progress: easily build your project from any platform, to any platform. Dev environments are also work in progress: set up a suite of tools to help onboard new team members quickly.

Both of these are the point of a build system in a sense that caching is not. Caching makes the builds you already have faster. Cross-compilation and dev environments change what builds you can do at all. A team that needs to produce a Linux binary on a Mac, or a container image for an architecture the developer does not have, is currently doing that with a separate tool or a separate project. A dev environment specification, where the tools a new joiner needs are declared rather than written into a wiki, is the same problem from the other side.

Their absence is also the clearest signal about the state of the project. The version numbers tell the story: v0.1.8 from 2026-05-30, v0.1.7 from 2026-03-29 and v0.1.6 from 2025-11-02. This is a 0.1 series with regular releases and no promise of interface stability yet. The last push was on 2026-10-18, so the work is ongoing, and the changelog at the repository root records what changed between releases.

So the honest reading is that Brioche today is a package manager with a lockfile and a shared cache, written in a language that makes the build script checkable, and that the two capabilities which would make it a build tool rather than a package manager are still being written. Anyone adopting it now is adopting the first half and betting on the second.

The description of the project is a single line, a delicious package manager, and the tone of the whole readme matches it. That matters less than it sounds, because the tool asks you to move your build logic into a language it controls, and the tone is part of the pitch.

## Where Brioche is the wrong tool

There are four cases where this is the wrong choice, and they are about maturity and coverage rather than design.

The first is a project that needs to work today with the ecosystem it already has. Brioche is at 0.1.8, the package catalogue lives in a separate repository, and the readme does not enumerate what is in it. If your project needs a specific language's package manager, and that language's package manager already works, then Brioche is asking you to trade a working tool for a new one whose coverage you cannot read from the documentation. The first thing to check is the packages repository for your own toolchain.

The second is a team that will not accept an unstable interface. A 0.1 series means the build file format and the API surface can change. If your build scripts are long lived, which they are in a large monorepo, that is a real cost, and it is the kind of cost that a stability promise would remove and this project does not yet make.

The third is a single language project with a good existing toolchain. A Rust project with Cargo, a Node project with pnpm, a Python project with uv, each of these has a lockfile, a cache and a way to make a container image. Brioche's advantage is the cross-ecosystem one, and if you have one ecosystem you are paying for a generalisation you do not need. That said, the readme is explicit that the project is for your most complex software projects, which is a reasonable place to draw the line.

The fourth is a build that must run in a restricted environment. The install path pipes a script from the network into a shell, and the source build is a cargo install of a Rust project with a workspace of four crates. Neither is impossible in a locked down environment, but both need work to fit into a policy that does not allow fetching a script and executing it.

On the alternative, the difference in approach against the established build tools is worth stating precisely. Nix describes a build as a function of a dependency graph, which gives reproducibility and therefore reproducibility on other machines, at the cost of a steep learning curve and a package ecosystem you have to learn separately. Brioche makes the build a program in a language you already know, which keeps the learning curve low and makes the build script reviewable like any other code, and the cost is that the dependency resolution is its own rather than inherited from each ecosystem's own manager. That is the trade: Nix gives you a uniform language for the whole system and asks you to learn it, Brioche lets you stay in TypeScript and gives you a uniform lockfile and cache.

The other comparison is a plain script plus a per-ecosystem manager, which is what most teams have today. That is cheaper to adopt and it is the thing Brioche is proposing to replace. Its weakness is the fragmentation the readme's opening line points at: each tool covers only its own part of the build.

## Conclusion

Brioche suits a team that builds polyglot projects, wants one lockfile and one cache across every language in a repository, and is willing to adopt a tool at version 0.1, and it does not suit a project that needs a stable ecosystem of existing packages today, since the package catalogue lives in a separate repository and the readme does not enumerate what is available. Before you adopt it, read that packages repository and check the coverage for your own toolchain, and note that the licence is MIT.

## FAQ

### What is Brioche?

Brioche is a package manager and build tool written in Rust, where a project's build is a TypeScript program rather than a shell script or a Makefile. It provides caching of build artifacts, a lockfile for dependencies and output as an OCI container image.

### How do I install Brioche?

Run the install command in the readme, which fetches the install script over HTTPS with TLS 1.2 and pipes it to sh. To build from source, check out the repository and run cargo install --locked --path crates/brioche, which installs it into ~/.cargo/bin.

### Can Brioche share its cache between machines?

Yes. The readme says you can share the cache between machines using an S3-compatible storage provider, so a developer machine and a CI runner can use the same cached artifacts.

### Which languages does Brioche support?

The readme does not enumerate the catalogue. Brioche packages live in a separate repository, brioche-packages, and the example in the readme shows a Rust module with cargoBuild alongside the standard library, with cross-ecosystem building described as a feature.

### What licence is Brioche released under?

Brioche is released under the MIT licence, with the full text in LICENSE.md in the repository root.

## Sources

- [brioche-dev/brioche on GitHub](https://github.com/brioche-dev/brioche)
- [License: MIT](https://github.com/brioche-dev/brioche/blob/main/LICENSE)
- [Project website](https://brioche.dev)
- [README](https://github.com/brioche-dev/brioche/blob/main/README.md)
- [Releases](https://github.com/brioche-dev/brioche/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/brioche-dev-brioche
