# aube: a Rust package manager with a lifecycle-script jail

> Aube is a Node.js package manager written in Rust that reads your existing lockfile in place, installs automatically before scripts run, and ships a lifecycle-script jail behind one config line. The speed claims are the headline, but the security defaults are the more interesting bet.

**jdx/aube** — A fast Node.js package manager. Repeat test commands run up to 31x faster than pnpm and up to 5x faster than Bun.

- Repository: https://github.com/jdx/aube
- Website: https://aube.jdx.dev
- Stars: 2,016 · Forks: 64
- Language: Rust
- License: MIT
- Published: 2026-08-08 · Updated: 2026-08-18 · Language: en
- Canonical page: https://hysenlabs.com/projects/jdx-aube

## The problem aube is aimed at: stale node_modules and unapproved install scripts

Two failures motivate the project. The first is the stale install. You pull a branch, a dependency changed, and the script you ran still executes against the old node_modules tree. Aube's answer is to make installation implicit: `aubr test`, `aube test`, and `aube exec vitest` check whether node_modules is fresh for the current package.json and lockfile, install first if it is not, and skip the work when nothing changed. The tagline in the README is blunt about the target: "Never forget to install."

The second failure is the install script itself. Lifecycle scripts in npm packages run arbitrary code at install time, and a transitive dependency you never chose can execute on your machine. Aube ships what the README calls "the only lifecycle-script jail" among Node.js package managers. Out of the box, exotic transitive dependencies are blocked, lifecycle scripts wait for approval, trust downgrades fail at resolve, and brand-new releases sit in a 24 hour cooling window. Setting `paranoid: true` adds the build jail and converts the soft gates into hard failures.

That combination defines the audience. This is for engineers who run other people's code on their own machines and CI runners and want the default posture to be restrictive rather than permissive. It is also for teams that cannot force a migration, because aube reads and writes existing lockfiles in place.

## How aube decides whether to install before running your script

The mechanism is a freshness check rather than a daemon. When you invoke `aubr test`, aube compares the current package.json and lockfile against the state of node_modules. Fresh means it goes straight to the script. Stale or missing means it resolves and installs first, then runs the script. The README frames `aube install` as the exception: use it when the install itself is the task, such as first local setup without running a script, lockfile updates, Docker layers, production-only installs, or CI flows.

Dependency files are shared through a global content-addressable store, so two checkouts of the same project point at the same package files instead of each holding a full copy. That is the same broad design pnpm popularised, and the README lists "less disk use" as a stated benefit rather than a side effect.

The resolver is not the only thing doing work at run time. Aube switches Node.js versions itself: if a project pins Node through `devEngines.runtime` in package.json, `.nvmrc`, or `.node-version`, every script and binary run through aube gets that version. When the pinned version is missing, aube delegates the install to mise if mise is present (one shared Node store on disk) and downloads from nodejs.org otherwise. Shell activation extends the same routing to plain `node`, `npm`, `npx`, `pnpm`, `pnpx`, `yarn`, and `yarnpkg` commands, while package-manager shims route back to aube so the existing project lockfile kind stays authoritative.

The workspace is a Rust workspace with crates split under `crates/`, and Cargo.toml defines separate profiles for Node-API addons (`panic = "unwind"`) and C ABI calls, both because a panic that aborts would take down the JavaScript host process. That is a build detail, not a user-facing feature, but it explains why the binary ships in several flavours.

## Installing aube and running your first script

The README recommends mise as the primary install path. This puts the binary on your global tool set:

```bash
mise use -g aube
```

After that, confirm the binary resolves on your PATH:

```bash
aube --version
```

If you prefer npm, aube is published under the `@endevco/aube` scope. The package uses an install script to fetch native binaries, so the README includes the flag that keeps that working even when your npm config disables scripts:

```bash
npm install -g --ignore-scripts=false @endevco/aube
```

Homebrew users install from the jdx tap:

```bash
brew install jdx/tap/aube
```

Now open an existing Node.js project and run the test script through the `aubr` shim, which is shorthand for `aube run`:

```bash
aubr test
```

What you should see: aube checks whether node_modules is fresh for the current package.json and lockfile, installs if it is not, and then runs the test script. If the project already has a supported lockfile (`pnpm-lock.yaml`, `package-lock.json`, `npm-shrinkwrap.json`, `yarn.lock`, or `bun.lock`), aube reads it and writes updates back to the same file, so the rest of the team does not have to switch. With no lockfile present, aube creates `aube-lock.yaml`.

Day-to-day commands follow the familiar shape. `aube add react` and `aube add -D vitest` change dependencies, `aube remove react` drops one, and `aube update` moves dependencies within their package.json ranges. `aubx cowsay hi` runs a local binary if one is installed or fetches the tool into a throwaway environment. For CI, `aube ci` removes node_modules, verifies the lockfile is fresh for the current package.json, and installs, treating the lockfile as the source of truth.

## Where aube's defaults become friction

The security posture is the selling point and the main source of friction. Lifecycle scripts waiting for approval means a package that needs a postinstall step to fetch a prebuilt binary, compile a native addon, or generate code will not complete that step until you approve it. The README describes the gates and the `paranoid: true` escalation, but it does not enumerate which packages trip the exotic-transitive-dependency rule or how the approval flow surfaces in a non-interactive CI run. If your build depends on a chain of postinstall scripts, budget time for the first run to be a negotiation rather than a single command.

The 24 hour cooling window on brand-new releases is the same trade in a different place. A security patch published an hour ago is not installable at default settings. Teams that track upstream advisories closely will feel that delay; teams that would rather not be the first person to run a compromised release will consider it the point.

There is also a structural cost to the lockfile compatibility story. Reading and writing five lockfile formats in place is what makes adoption incremental, but it also means aube's resolver has to agree with pnpm, npm, Yarn, and Bun about how a dependency graph is represented. The README gives no statement about what happens when the two disagree, and it does not document rollback. If a lockfile rewrite goes wrong, the documented recovery path is your version control, not an aube command.

Finally, the performance numbers in the README are the project's own benchmarks, linked from the repository. Warm installs are described as about 6x faster than pnpm and about 2x faster than Bun, and repeat test commands as up to 21x faster than pnpm and up to 1.9x faster than Bun. Those are claims to verify against your own project, not measurements you can transfer.

## aube against pnpm: same store idea, different defaults

The closest comparison is pnpm, and the difference is not the content-addressable store. Both share package files across projects through a global store. The difference is what happens at install time by default. pnpm runs lifecycle scripts unless you configure otherwise; aube holds them for approval and blocks exotic transitive dependencies at the resolver. pnpm's lockfile is `pnpm-lock.yaml`; aube will read and write that same file, which means you can evaluate aube inside a pnpm repository without asking anyone to change their tooling.

That in-place lockfile support is what makes the comparison practical rather than theoretical. You can run `aubr test` in a pnpm checkout, watch aube resolve against the existing lockfile, and decide afterwards whether the security gates are worth the friction. The reverse migration is the risk: once aube has rewritten `pnpm-lock.yaml`, a teammate on pnpm reads whatever aube wrote, and the README does not describe a compatibility guarantee for that direction.

Bun is the other reference point in the README's benchmarks, and it differs in kind rather than degree: Bun is a JavaScript runtime that also manages packages, so adopting it means adopting the runtime. Aube is a package manager that manages Node versions for you and delegates to mise or nodejs.org when a pinned version is missing. If you want the runtime and the package manager from one vendor, aube is not that.

## Maintenance, licensing, and what a version bump costs you

Aube is MIT licensed. The Cargo.toml sets `license = "MIT"` at the workspace level, and the repository carries a LICENSE file and a licenses/ directory. MIT is permissive: you can use, modify, and redistribute the code, including in commercial settings, provided the copyright notice and permission notice travel with it. That is a statement about the licence text, not legal advice for your situation.

The repository is not archived, and the last push was on 2026-08-25. The release cadence around that date was tight: v2.0.1 on 2026-08-23, v2.1.0 later the same day, and v2.2.0 on 2026-08-25. The v2.0.1 notes mention aube's own global home, a leaner resolver, and lower install memory; v2.1.0 covers faster script runs and echoed commands; v2.2.0 adds a bundled compatibility catalog and an embeddable node-gyp bootstrap.

Those notes matter for upgrade cost. A leaner resolver and a new global home in a minor-to-major transition are the kind of changes that can invalidate a store layout or a cache path. The workspace version in Cargo.toml is 2.2.16, ahead of the tagged releases listed, which suggests the crate and the release tags are not published in lockstep. If you pin aube in CI, pin the release tag, not the workspace version. The MSRV is 1.91 and the Cargo.toml comment says it is held at the floor mise can build with, so anyone building from source needs that toolchain or newer.

## Conclusion

Adopt aube if you want the security defaults (blocked exotic transitive deps, lifecycle scripts waiting for approval, a 24h cooling window on new releases) and you are willing to keep your current lockfile format so teammates can stay on pnpm or npm. Do not adopt it if your CI or release flow depends on a package manager whose install and rollback semantics you have already audited, because the README does not document rollback behaviour. Before switching anything, run aube ci on a throwaway clone and diff the resulting node_modules against what your current manager produces, then read the security page for what paranoid: true actually turns from soft gate into hard fail.

## FAQ

### How do I install aube?

The README recommends mise: run `mise use -g aube`, then check `aube --version`. It is also on npm as `@endevco/aube` (installed with `--ignore-scripts=false` so the native binary fetch runs) and on Homebrew via `brew install jdx/tap/aube`.

### Does aube work with my existing pnpm or npm lockfile?

Yes. The README states that aube reads and writes `pnpm-lock.yaml`, `package-lock.json`, `npm-shrinkwrap.json`, `yarn.lock`, and `bun.lock` in place, so you can try it locally without forcing the rest of the team to switch. With no lockfile present, it creates `aube-lock.yaml`.

### Why does aube not run my package's install script?

Lifecycle scripts wait for approval by default, which is one of the security defaults the README describes. Setting `paranoid: true` adds the build jail and turns the soft gates into hard fails. The README does not document how the approval flow behaves in a non-interactive CI run.

## Sources

- [Official documentation](https://aube.jdx.dev)
- [Official README](https://github.com/jdx/aube#readme)
- [Project repository](https://github.com/jdx/aube)
- [Release notes](https://github.com/jdx/aube/releases)

---

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