5 minutes read
Runtime dependencies
Offing installs as one npm package, and every package it depends on is code its users install and must trust. This page records the audit of that set: why each dependency is there, what removing it would cost, and the checks it passed. It is the written reason the agent brief asks for before a runtime dependency can be kept or added.
The audit ran on 2026-11-06 at commit efc784e5. It changed no behavior and added no network access to any
scan. On 2026-11-09 the lifecycle data and the code that reads and verifies it moved into
Ebb (@velella/ebb), so the three Sigstore packages are now Ebb's dependencies and Offing depends on Ebb.
Result
Six direct dependencies remain. Each one is imported by shipped src/ code, declares no install script, and has
no reviewed lifecycle rule that would make Offing report it as end-of-life, deprecated, or retired. Nothing was
unused, so nothing was removed. Removing any of them would drop a feature users already rely on; the section for
each package says which one.
They resolve to a runtime tree of eleven packages: @modelcontextprotocol/core, Ebb's three Sigstore packages, and
@sigstore/core are the transitive additions. npm audit --omit=dev reports zero advisories, and SECURITY.md requires
that to stay true at any severity and any depth.
| Dependency | Range | Installed | License | What it does for Offing | Install script |
|---|---|---|---|---|---|
@modelcontextprotocol/server |
^2.3.1 |
2.4.0 | Apache-2.0 | Runs the local MCP server and its stdio transport | none |
@velella/ebb |
^0.1.1 |
0.1.1 | MIT (data CC BY 4.0) | The lifecycle rules, purl and VERS matching, and signed data updates | none |
semver |
^7.8.5 |
7.8.5 | ISC | Reads and compares semantic versions and npm ranges | none |
smol-toml |
^1.9.1 |
1.9.1 | BSD-3-Clause | Parses TOML manifests and lockfiles | none |
yaml |
^2.9.1 |
2.9.1 | ISC | Parses YAML manifests with exact line numbers | none |
zod |
^4.6.5 |
4.6.7 | MIT | Validates MCP tool input and the capability manifest | none |
Why each one stays
@modelcontextprotocol/server
The official MCP TypeScript SDK. src/mcp.ts registers the ten tools, resources, and the migration prompt on its
McpServer, and src/mcp-server.ts serves them over stdio for offing mcp. The tests drive the same server
through its InMemoryTransport, which is why the package is also imported by tests/. Writing the JSON-RPC
framing, capability negotiation, and transport by hand would replace maintained protocol code with code no host
has tested, for a server that has to work with Claude, Codex, Copilot, and other MCP hosts.
@velella/ebb
Ebb is the lifecycle data every finding comes from, published as its own package so other tools can use it:
the rules, the purl and VERS matching, the forecast, and the data updates that fetch a newer signed bundle and verify
it with Sigstore before use. Its runtime dependencies are @sigstore/bundle, @sigstore/protobuf-specs, and
@sigstore/verify, which verify a data bundle offline against the trusted root Ebb ships. It is part of the same
organization and reviewed the same way. Without it Offing would have no rules to report.
semver
semver parses and compares versions and evaluates npm ranges in the npm detector, the version schemes, the lockfile
readers, doctor, guard, and the upgrade checks. Range syntax is where a small shortcut turns into wrong findings:
||, x ranges, hyphen ranges, and prerelease precedence all have defined behavior. src/vers/semver.ts already
adds the Composer and Go short forms on top; the comparison below that is the package's job.
smol-toml
smol-toml parses pyproject.toml, poetry.lock, uv.lock, pdm.lock, and other TOML files read by the Python
detector, drift, fix, upgrade, and workspace code. It has no dependencies and no install script. A TOML parser is
not something to hand-roll for a tool whose findings must cite an exact declaration.
yaml
yaml parses workflows, Compose files, Kubernetes manifests, Helm values, serverless templates, and the YAML
manifests behind drift. The detectors need node positions, not only values, so the parser has to keep them:
LineCounter and the document API are why findings can name an exact file and line. It is also used in the
codemods that edit those files without reformatting them.
zod
zod defines the MCP tool input schemas in src/mcp.ts and validates the capability manifest in
src/capabilities/schema.ts. The MCP SDK already depends on it, so it is installed either way; declaring it
directly is required because Offing imports zod/v4 itself. Replacing it would duplicate the schema definitions
and drift from what the SDK validates.
What the audit checked
Need: every dependency in package.json is imported by at least one file under shipped src/, or by a file that
entry points import. knip in npm run check fails on an unused declaration.
Upkeep: each package is published from an actively maintained upstream (modelcontextprotocol/typescript-sdk,
velella-sh/ebb, npm/node-semver, squirrelchat/smol-toml, eemeli/yaml,
colinhacks/zod). Dependabot opens weekly update pull requests with a seven-day
cooldown, and the lockfile pins the resolved version.
Install scripts: none of the six declares preinstall, install, or postinstall at its installed version.
These commands reproduce the audit:
npm ls --omit=dev --all
npm audit --omit=dev
node -e '
const fs = require("node:fs");
for (const name of Object.keys(require("./package.json").dependencies)) {
const { scripts = {} } = JSON.parse(fs.readFileSync(`node_modules/${name}/package.json`, "utf8"));
const install = Object.keys(scripts).filter((key) => /^(pre|post)?install$/.test(key));
console.log(name, install.length ? install.join(",") : "no install scripts");
}'
Lifecycle: offing check pkg:npm/<name>@<version> for each installed version returned no-data on
2026-11-06. That means no reviewed rule covers these packages; it does not mean they are safe, and it is not a
substitute for the advisory check above.
Adding a runtime dependency
A new runtime dependency is a new package every user installs. It needs all of the following in the pull request:
- A reason that names the feature it serves and why the code in this repository or the standard library cannot serve it.
- No
preinstall,install, orpostinstallscript, unless the pull request explains what the script does and why it is required. - A license compatible with Apache-2.0.
npm run dev -- check pkg:npm/<name>@<version>with no end-of-life, deprecated, or retired result. If the check names a replacement, use it or ask in the issue.- No new advisory in the runtime tree (
npm audit --omit=dev) at any severity. - An entry in the table on this page, with the same fields as the existing rows.
Removing a dependency needs the same care in reverse: check that no shipped entry point imports it, that its replacement parses the same inputs, and that the findings and their file and line locations do not change.