lacs-project / lacs-project/sysknife
npm audit makes `frontend` fail whenever registry.npmjs.org does
- Dominant language
- Rust
- Stars
- 12
- Forks
- 19
- Avg merge
- 18h 57m
- Merged PRs (30d)
- 116
Description
> **Line references corrected 2026-09-07 at `adab560`.** The three line numbers
> in this issue were wrong when it was filed, and the `grep -n` output pasted
> under "Getting started" reproduces on no commit this repository has. My error,
> not a change under you. The step is at **197-198**, the surrounding numbers are
> in the corrected block below, and the substance of the issue is unchanged:
> `frontend` is still required, the step is still enforced, and it still gates
> every pull request on `registry.npmjs.org`.
`frontend` is a required status check on `main`, and its fourth step is:
```yaml
- name: Audit production dependencies
run: npm audit --omit=dev --audit-level=high
```
That command POSTs to `https://registry.npmjs.org/-/npm/v1/security/advisories/bulk`.
When the endpoint is unavailable, npm exits 1, and the check fails exactly the way
it fails for a genuine high-severity advisory.
This morning that took every open pull request down for 45 minutes:
| run | PR | started (UTC) | error |
|---|---|---|---|
| [33859810636](https://github.com/lacs-project/sysknife/actions/runs/33859810636) | #365 | 09:44 | `npm warn audit 503 Service Unavailable - POST .../advisories/bulk` |
| [33862001249](https://github.com/lacs-project/sysknife/actions/runs/33862001249) | #366 | 10:11 | `npm warn audit network timeout at: .../advisories/bulk` |
| [33863121379](https://github.com/lacs-project/sysknife/actions/runs/33863121379) | #366 | 10:25 | `503 Service Unavailable` |
Each attempt ran for five minutes before npm gave up (10:25:37 → 10:30:37 on the
last one), so a bad morning at the registry costs runner minutes on top of the
blocked merges. Re-running the failed job cleared all three.
Two things make this worse than an ordinary flake.
The audit covers `apps/sysknife-shell`, the paused Tauri GUI. `CONTRIBUTING.md`
puts GUI work out of scope, so #366, which changes one line of `CONTRIBUTING.md`,
waited twice on that directory's dependency advisories.
And `gh pr checks` prints `frontend fail` either way. A contributor reading that
cannot separate an outage from a vulnerability without opening the log.
Three shapes, and I hold none of them strongly:
- Move the audit into its own scheduled workflow against `main`, daily, and drop
it from the pull-request path. Advisories appear on the registry's clock. A
PR-time audit answers a question the pull request did not ask.
- Keep it on pull requests and stop it blocking: `continue-on-error: true`, so
the result is reported and not enforced.
- Leave the PR-shaped half to `dependency-review`, which already runs here and
already catches a dependency a pull request introduces.
Whoever picks this up: the step is `.github/workflows/ci.yml:197-198`. Branch
protection on `main` lists `frontend` among its required contexts, so deleting
the step is enough; deleting the job would need the protection setting changed
in the same breath or every pull request sits on a check that never reports.
## Getting started
One workflow file. No VM, no daemon, no LLM provider, no credentials.
```
$ grep -n 'npm audit' .github/workflows/ci.yml
198: run: npm audit --omit=dev --audit-level=high
$ awk 'NR>=170 && NR<=230 && /- name:/ {print NR": "$0}' .github/workflows/ci.yml
177: - name: Lint GitHub issue templates
186: - name: Checkout
189: - name: Set up Node
194: - name: Install dependencies
197: - name: Audit production dependencies
200: - name: Type check
203: - name: Test
216: - name: Checkout
219: - name: Install Tauri system dependencies
229: - name: Set up Rust
```
That window is wider than the `frontend` job, so it picks up the tail of
`docs-and-hygiene` at 177 and the head of the next job at 216. Steps 186 through
203 are the ones this issue is about.
The job runs with `working-directory: apps/sysknife-shell`, which is the paused
desktop app, so this step gates every pull request in the repository on the
availability of `registry.npmjs.org` for a component nobody is developing.
Confirm the branch-protection half before you touch anything, because it decides
which of the three options in the body is safe:
```
$ gh api repos/lacs-project/sysknife/branches/main/protection --jq '.required_status_checks.contexts'
```
`frontend` is in that list. Deleting the step leaves the job reporting. Deleting
the job leaves every pull request waiting on a context that never arrives.
## Tests first
The awkward part is that the thing you are fixing is a network outage, and you
cannot make `registry.npmjs.org` fail on demand.
**Break what the guard protects.** Whichever option you take, assert on the
workflow text rather than on a run. A guard under `tests/release/` that parses
the `frontend` job and asserts the audit step carries `continue-on-error: true`,
or that no `npm audit` invocation is enforced on the pull-request path, is
checkable offline and cannot pass by accident.
**Break the guard's own input.** This is the failure mode #346, #362 and #368 all
share, so it is worth care here. Assert on a count, not only on a match. If your
guard greps the job for a token, it passes just as happily when the job is renamed
out from under it and it reads zero steps. Give it a fixture whose `frontend` job
has the step enforced, and assert it goes red on that fixture. A guard that has
never been seen to fail is not a guard.
`tests/release/postgres-contract-guard.test.sh` is the closest existing model for
parsing a job out of `ci.yml`, and #362 is the open issue about the ways that
particular pattern can fool you.
## Difficulty
`easy`. The change is a few lines of YAML. Choosing among the three options is the
judgement, and the body already lays them out.
Contributor guide
Research direction
Read the frontend job in .github/workflows/ci.yml around steps 186-203, then confirm required contexts with the branch-protection API command in the issue. Review tests/release/postgres-contract-guard.test.sh and issue #362 before choosing among the listed workflow approaches. Done means the pull-request path no longer blocks on an npm registry outage, with an offline guard that verifies the intended workflow text and fails on an enforced-audit fixture.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- github-actions, javascript
- Domain
- ci-cd, devops, security
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 68/100