pithy add auth --ejectThis is the only path that writes handler source into your repo, and it is one-way.
Why it exists
What a capability is is what you are taking a copy of, pithy add --eject is the command, and versions and upgrades is what you give up by doing it.
The kit keeps your repo thin: a capability’s logic lives in its package and upgrades with minor releases. pithy add scaffolds only config — the registration, the bindings, the options — and never writes handler source.
Sometimes you need to fork that logic. Bend a route, change a flow, do something the config options do not expose.
This is the escape hatch, and it is always available. That availability is part of the argument for the fat-package model: you are never locked in.
What it does
Copies the source. The capability’s entire source tree is copied into a directory in your project, structure and relative imports preserved. Routes, middleware, schemas, migrations — all local and editable.
Repoints the wiring. The config import moves from the package to your copy. So does every re-export in your Worker’s entry that points into the package — so a Durable Object class Cloudflare runs is the one you edit rather than the packaged one.
Each file names the fork the way it reaches it, so the config line and the entry line read differently and point at one directory. The registration, the bindings and the config options are unchanged.
Promotes dependencies. The capability’s runtime dependencies — transitive through the package until now — are added to your own manifest, so the local copy builds standalone.
Afterwards your project imports nothing from that package. The ejected code still resolves the core contract seam and third-party libraries.
The trade
The capability is now yours. No part of it upgrades.
Security fixes and improvements in that package no longer reach it. Maintaining the fork is your responsibility.
pithy upgrade detects an ejected capability by its local import and skips it, forever after — reported as skipped rather than silently ignored.
There is no un-eject. Re-attaching a fork to the package is not supported, by design.
Try these first
Genuinely — most reasons to eject have a smaller answer.
A config option you did not know about. The manifest’s options are listed in the capability’s reference page, and pithy add --set lists valid keys on a typo.
A composed plugin. Auth’s ecosystem is open: organizations, passkeys, second factors and API keys all compose through the capability’s own config, and their tables are created by migrate.
Middleware of your own, stacked on top. A gate you add in your app capability runs before the capability’s routes, which covers a surprising amount of I need this route to do one more thing.
Your own route beside theirs. Nothing stops you writing a route that does what you want and leaving theirs alone.
Ask for the option. The kit’s own dashboard filed ninety-three gaps while being built on it. A missing option is a reasonable thing to want.
The guardrails
Idempotent. Install, add and eject in one command — and because add is idempotent, running it against an already-added capability simply ejects it.
No clobber. If a local copy already exists, eject refuses rather than overwriting your edits.
capabilities/audit already exists.
Edit the local copy, or re-run with --force to overwrite it (discards your changes).--force re-copies from the package, discarding local changes — and it removes the old copy first, so stale files do not linger.
Agent-drivable. Non-interactive, with a payload naming the copied path and the promoted dependencies.
What it looks like
$ pithy add audit --eject
Wired audit into board.
Ejected audit into apps/board/capabilities/audit/. It's yours now — @pithy-sh/audit no longer upgrades it.
Promoted 4 dependencies. @pithy-sh/audit is safe to remove.
Done.That third line is worth acting on. The package is installed and unimported.
Removing an ejected capability
pithy remove deletes the local source rather than uninstalling a package — through a gate that refuses a symlink at any segment of the path and refuses a path resolving outside the project.
If you eject, eject deliberately
Write down why, somewhere your future self will find it. The most expensive version of this is a fork nobody remembers making, discovered when somebody asks why a security fix did not land.