puck

Native tooling for PHP and Laravel, starting with the install. Composer-compatible composer.json / lock, a shared store, one static binary. Bigger layers come next.

curl -fsSL https://puck.jimmyverburgt.com/install | bash
Watercolor hill with a stack of packages, one lit warm, beside a lamp. A river runs down from the hill and off the bottom of the page.

Live feel

Watch the wait.

Real-ish timings from Linux CI on laravel-app with require-dev. puck first, then Composer. The pause is the point.

Replay derived from CI cold install timings (run 34124132162). Stylized pacing for the demo, not a wall-clock replay.

Linux CI

The numbers.

Same run for Composer and puck (34124132162). Lead with warm-wipe. Cold is network-heavy. Scripts and flags live in benches/.

laravel-skeleton --no-dev

Linux CI. Warm-wipe is the honest CI number.

  • warm wipe
    composer1662 ms
    puck327 ms
  • warm keep
    composer908 ms
    puck24 ms
  • cold
    composer4094 ms
    puck2029 ms

laravel-app with require-dev

Warm-wipe time is dominated by the optimized classmap dump.

  • warm wipe
    composer2814 ms
    puck504 ms
  • warm keep
    composer1380 ms
    puck25 ms
  • cold
    composer4422 ms
    puck1778 ms

Warm-wipe: optimized classmap dump scales with require-dev. Cold: downloads still dominate;overlap_ms is summed download+extract work minus wall clock (mostly HTTP concurrency; skeleton cold was 17611 ms).

How it works

One shelf. Every project.

Packages settle into a shared store under ~/.puck/store. Linking them into vendor/ is nearly free. Two apps can share the same bytes on disk.

~/.puck/storecontent-addressed
  • laravel/frameworksha...
  • nesbot/carbonsha...
  • symfony/http-foundationsha...
  • illuminate/supportsha...
app-api/vendor
  • laravel/framework → store
  • nesbot/carbon → store
  • symfony/http-foundation → store
app-web/vendor
  • laravel/framework → store
  • nesbot/carbon → store
  • pestphp/pest → store
  1. Land onceEach archive extracts into a content-addressed store. Same bytes never unpack twice.
  2. Link cheapProjects hardlink or reflink from the store into vendor/. That directory looks normal to PHP, IDEs, and Docker — but those files share inodes with ~/.puck/store, so do not hand-edit vendor/.
  3. Stay nativeInstall is a Rust binary. Composer stays installed so you can switch back any time.

Before you switch

Run doctor first.

Keep Composer installed. puck reads and writes the same files, and you can switch back at any time.

$ puck doctor
warning: script `Composer\Config::disableProcessTimeout` is a Class::method callback puck skips or runs differently
info: PHP /usr/bin/php (8.4.23) (from PATH)
info: store ~/.puck/store — warm (index non-empty)
info: optimize-autoloader=true
ready to switch
AreaStatus
composer.json / lockSupported
Composer + path reposSupported
install / require / remove / updateSupported
Pest + phpstan Tier 1 adaptersSupported
vcs / artifact / package reposNot in 0.1
WindowsNot in 0.1
Broader plugin hostNot in 0.1
Replace Composer entirelyNot planned

Install

One binary. Same files.

macOS and Linux. Installs into ~/.puck/bin. Upgrade later with puck upgrade. Manifest truth stays on GitHub Releases.

curl -fsSL https://puck.jimmyverburgt.com/install | bash

Current release v0.1.1· checksums and minisign on the release page ·install docs