| name | interlinked-supply-chain |
| description | Respond to blocked package installs and manage the Interlinked supply-chain allowlist. Load this when `npm/pnpm/yarn/bun/pip/pipx/poetry/uv/cargo/gem/bundle/go/composer/mvn/dotnet` install is BLOCKED with `[interlinked:supply-chain]`, when adding a dependency to package.json / requirements.txt / pyproject.toml / Cargo.toml / go.mod is refused, when a version is rejected as "not an exact pin", when `npm ci` fails a lockfile-snapshot check, or when you need to approve a package (`interlinked allowlist add`), snapshot a lockfile, or verify deps. Package installs are default-deny; a package needs both allowlist membership AND an exact version pin. |
interlinked-supply-chain — package installs are default-deny
In an Interlinked-guarded repo, every package install is blocked before it runs unless the
exact package is pre-approved on .interlinked/package-allowlist.json. The stance: any new
dependency is potentially malicious (a response to the npm/PyPI malware surge). You cannot
install your way out, and you cannot silently self-approve. When blocked, the correct move is
almost always to surface the need to the human, not to force-approve.
Two independent requirements for an install to pass: (a) the name is allowlisted AND (b)
the version is an exact pin (pkg@1.2.3). An approved name at a floating version (lodash,
lodash@^4, lodash@latest) is still blocked — a range can resolve to a newer, compromised
release.
Load this when
- A package install was blocked with
[interlinked:supply-chain].
- Editing a manifest (
package.json, requirements.txt, pyproject.toml, Cargo.toml,
go.mod, Gemfile, composer.json, *.csproj, …) to add a dep was refused.
npm ci / bare npm install / pip install -r fails a snapshot check.
- You need to approve, snapshot, list, or verify allowlisted packages.
What gets blocked
| Action | Result | Why |
|---|
npm install left-pad (name not allowlisted) | block | not approved |
npm install lodash / lodash@^4 / lodash@latest | block | not an exact pin (range/tag) |
npm install lodash@4.17.21 (allowlisted + pinned) | allow | — |
npm install --registry https://evil.tld pkg / --index-url … | block | custom registry bypasses signing |
npm install git+https://… / tarball URL / file: spec | block | never auto-approved (bypasses registry) |
npm ci / bare npm install / pip install -r req.txt with no matching snapshot | block | lockfile not snapshot-approved |
Edit package.json to add "evil": "^1" | block | manifest-edit gate (a second, separate gate) |
npm uninstall x / version bump of an existing dep | allow | nothing new enters |
npm install ./local-dir (local path) | allow | in-workspace, version-controlled |
| install verb while the daemon is down | block (fail-closed) | a dead guard is a security failure |
Example block text:
[interlinked:supply-chain] npm add: 'left-pad' is not in the npm allowlist.
Run `interlinked allowlist add npm left-pad` after reviewing the package.
When an install is blocked: what to do
First, question the dependency — the cheapest approved package is the one you don't add.
Confirm it's actually needed and not trivially replaceable (stdlib/native, or a dep already
present); many "blocked install" situations are really "don't add this" (e.g. left-pad → native
padStart). If it is warranted:
- Read the reason — it names the rule and the fix.
- Unpinned version? Add the exact version:
npm install lodash@4.17.21 (only if lodash
is already allowlisted).
- Unapproved package? Stop and surface to the human. State the package, version, and
why you need it, and propose:
interlinked allowlist add <ecosystem> <package> --by <human> --reason "…".
Do not --force. Do not switch to editing the manifest (that's the second gate,
also blocked).
- Snapshot mismatch (a sync like
npm ci)? The manifest/lockfile changed vs the approved
snapshot — a human re-runs interlinked allowlist snapshot --by <human>.
- Custom registry? Drop the
--registry/--index-url override; use the default registry.
- Daemon down (
[harness-offline])? interlinked harness start, then retry.
Getting a package approved (human sign-off operations)
interlinked allowlist add <ecosystem> <package> --by <name> [--reason <t>] [--version-range <r>] [--force]
--by is required. Ecosystems: npm, pypi, cargo, rubygems, go, composer, maven, gradle, nuget.
add runs three admission screens (cheapest first) and refuses without --force if any fires:
- Typosquat (npm only, offline) — Levenshtein ≤2 to a popular package name.
- License (network) — the version's declared SPDX license vs the committed
license_allowlist (recorded on the entry, re-checked later at manifest-edit time).
- OSV advisories (network,
api.osv.dev) — open vulnerabilities against the version.
--force = "I reviewed this and accept the risk"; it records the finding as a note instead of
refusing. An agent should not --force on its own initiative — approving a bad package is
the worst failure mode (install then proceeds silently). Surface the screen output to the human.
Screens 2–3 fail open with a loud note: when offline — a clean add with "screen skipped"
notes did not pass those screens.
Whole-lockfile approval (unblocks npm ci / bare install / pip install -r):
interlinked allowlist snapshot --by <name> [--lockfile <name>]
Re-snapshot whenever the manifest/lockfile changes — the gate matches on exact hash.
Command surface
| Command | Purpose |
|---|
interlinked allowlist add <eco> <pkg> --by <name> [--reason] [--version-range] [--force] | Approve one package (3 screens). |
interlinked allowlist remove <eco> <pkg> | Delete an entry. |
interlinked allowlist list [--ecosystem <e>] [--json] | Show approved packages + snapshots. |
interlinked allowlist snapshot --by <name> [--lockfile <n>] | Hash + store manifest/lockfile state. |
interlinked allowlist verify | Diff every committed manifest's deps vs the allowlist; exits 1 on any unapproved (CI-gateable). |
Allowlist file format — .interlinked/package-allowlist.json (committed, PR-reviewed)
{
"version": 1,
"packages": { "npm": { "lodash": { "approved_by": "qcody", "reason": "utility",
"version_range": "^4.0.0", "license": "MIT" } }, "pypi": {}, … },
"lockfile_snapshots": { "package-lock.json": { "sha256": "…", "approved_by": "qcody" } },
"license_allowlist": ["MIT", "Apache-2.0", …]
}
Malformed JSON loads as an empty allowlist (fail-safe: never blocks bootstrap on syntax;
verify surfaces the parse error separately). A per-package version_range additionally
constrains which requested version passes. license_allowlist is optional — when absent
(as in a minimal file), admission falls back to a permissive built-in default seed.
Gotchas
- Fail-closed when the daemon is down. The cold paths block installs (the coarse
.mjs
path blocks all install verbs and tells you to interlinked harness start).
- Exact-pin is separate from membership. An allowlisted name is still blocked at a floating
version. Pin grammar is per-ecosystem (npm/cargo/go need full
major.minor.patch; PyPI
==24.2; RubyGems '7.1'; Maven/Gradle reject -SNAPSHOT).
- Manifest-edit is a distinct gate. Get the package approved first, then edit the
manifest, then install at the pin. Flipping a dep to a git/path/URL source counts as new →
blocked. A plain version bump of an existing dep is allowed.
interlinked audit is NOT this. It's the tamper-evident guard-decision log (see
interlinked-observability). The dependency-vuln (SCA) lane runs in the default
interlinked verify (the dep-audit tool, npm audit-based). Membership auditing =
interlinked allowlist verify.
- A few harness dev-tools are pre-allowed for membership (npm
vitest/@vitest/coverage-v8/
@vitest/coverage-istanbul; pypi pytest/pytest-cov/coverage/radon) so the coverage gate
isn't a catch-22 — they still need an exact pin. Everything else is default-deny.
--ignore-scripts warn still fires on allowlisted installs (defense-in-depth).
- Env bypass
INTERLINKED_DISABLE_PACKAGE_GUARD=1 is logged and defeats the whole layer for
that command — for documented bootstrap flows only, not for getting unstuck.
Related skills
- interlinked-harness — the general PreToolUse guard this gate is part of.
- interlinked-observability —
interlinked audit (the guard-log integrity check).