Metadata-Version: 2.5
Name: whoholdsup
Version: 0.1.0
Summary: Who holds up your app? Reads your lockfile and shows how many dependencies are kept alive by one person, untouched for years, or recently changed hands. npm, yarn, pnpm, uv, poetry, pip.
Project-URL: Homepage, https://github.com/ArkaKhorchidian/whoholdsup
Project-URL: Issues, https://github.com/ArkaKhorchidian/whoholdsup/issues
Author: Arka Khorchidian
License-Expression: MIT
License-File: LICENSE
Keywords: bus-factor,dependencies,lockfile,maintainers,npm,open-source,pypi,sbom,security,supply-chain
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Security
Classifier: Topic :: Software Development
Classifier: Topic :: Utilities
Requires-Python: >=3.9
Requires-Dist: rich>=13
Requires-Dist: tomli>=1.1; python_version < '3.11'
Provides-Extra: test
Requires-Dist: pytest>=7; extra == 'test'
Description-Content-Type: text/markdown

# whoholdsup

**Who holds up your app?** Point it at your lockfile and it shows how many of your dependencies are kept alive by a single person, which haven't shipped in years, and which recently changed hands.

```bash
uvx whoholdsup          # in any project with a lockfile
```

<p align="center"><img src="https://raw.githubusercontent.com/ArkaKhorchidian/whoholdsup/main/docs/demo.gif" alt="whoholdsup demo" width="760"></p>

<sub>Real output for a typical Next.js + Express app (the lockfile is in <a href="docs/example">docs/example</a>), November 2026.</sub>

In 2024, a backdoor came within weeks of shipping in the stable releases of major Linux distros through `xz`, a compression library maintained by one exhausted volunteer who handed releases to a helpful newcomer. Your app has hundreds of packages just like it. whoholdsup tells you which ones.

## What it checks

| | What it means | Where it comes from |
|---|---|---|
| **One maintainer** | Exactly one person can publish this package. If they burn out, get hacked or hand it over, there's nobody else. | npm's maintainer list; PyPI's owners and maintainers |
| **No release in 2+ years** | Might be finished. Might be abandoned. Either way, nobody's shipping fixes. | Release dates on the registry |
| **Changed hands this year** | Everyone who published a release in the last 12 months is someone who had never published it before. Usually a normal handover. It's also exactly what the xz takeover looked like. | npm's per-release publisher history |
| **Deprecated** | The authors told you to stop using it. | npm deprecation notices; PyPI "Inactive" status |
| **Not on the public registry** | Private, or removed (which sometimes means malware was taken down). | 404 from the registry |

It also works out **which single-maintainer packages are load-bearing**: how many other packages in your tree depend on each one, directly or indirectly. And it shows **the people holding it up**: who is the sole maintainer of the most packages you use.

Organisation and release-bot accounts (`types` for DefinitelyTyped, PyPI organisations, GitHub Actions trusted publishing) aren't counted as one person.

## Supported lockfiles

| Ecosystem | Files |
|---|---|
| npm | `package-lock.json` (v1–v3), `npm-shrinkwrap.json`, `yarn.lock` (classic and Berry), `pnpm-lock.yaml` (v5–v9) |
| Python | `uv.lock`, `poetry.lock`, `Pipfile.lock`, pinned `requirements*.txt` |

Direct dependencies are marked using your `package.json` or `pyproject.toml`.

## Options

```
whoholdsup                       # current folder
whoholdsup path/to/project       # another folder
whoholdsup uv.lock yarn.lock     # specific lockfiles
whoholdsup --direct              # only packages you depend on directly
whoholdsup --top 25              # longer tables
whoholdsup --json                # everything, machine-readable (for CI)
whoholdsup --no-cache            # ignore the one-day lookup cache
```

## How it works

whoholdsup reads your lockfile locally, then asks the public registries about each package: `registry.npmjs.org` for npm packages, and PyPI's JSON API, XML-RPC `package_roles` and project pages for Python. Lookups run 16 at a time and are cached for a day, so a second run is instant. Nothing about your project is uploaded anywhere: the registries only see package names, the same as when you install them.

**Honest limits.** "One maintainer" means one account with publish rights. That's the risk that matters for supply-chain attacks, but some single accounts are backed by a company, and some multi-maintainer packages are really run by one tired person. The publisher-change signal is npm-only, because PyPI doesn't publish who uploaded each release.

## Install

```bash
uvx whoholdsup          # run without installing
pipx install whoholdsup # or install it
```

Requires Python 3.9+. The only dependency is `rich` (plus `tomli` on Python < 3.11).

## License

MIT
