Skip to content

feat: run on Node or Bun — Bun is no longer an install prerequisite - #9

Merged
MyAlterLego merged 1 commit into
mainfrom
feat/node-or-bun
Jul 25, 2026
Merged

MyAlterLego merged 1 commit into
mainfrom
feat/node-or-bun

Conversation

@MyAlterLego

Copy link
Copy Markdown
Contributor

The question was whether Bun could be shipped with the skill. It can — and it shouldn't, because it isn't necessary.

The toolkit's entire Bun dependency was four lines of dead code:

const f = Bun.file(path);                              // created
text = require("node:fs").readFileSync(path, "utf8");  // actually read via Node
void f;                                                // explicitly discarded

That was the only Bun.* call in Tools/ or the CLI. What remained binding the project to Bun was cosmetic: the shebangs, and the CLI hardcoding spawnSync("bun", …).

Why not vendor Bun

cost
size ~34MB compressed per platform × 5 platforms in git, forever, including history
GitHub warns >50MB, hard-blocks >100MB per file — needs Git LFS
supply chain an unsigned runtime vendored inside a security-analysis toolkit — the disqualifying one

Download-on-first-use is smaller but breaks the promise the toolkit makes repeatedly: no API keys, no network calls, no telemetry, fully offline.

What this does instead

  • Removes the dead Bun.file block — zero Bun APIs remain.
  • stpa dispatches to process.execPath, so the child runtime always matches the parent. No PATH probing, no ambiguity about which runtime produced an analysis. Node <22.18 gets --experimental-strip-types automatically.
  • stpa becomes CommonJS with a node shebang — it's the extensionless entry point, and Node only auto-detects ESM in extensionless files from 22.7 onward, so CJS is unambiguous on every Node ≥12. bun stpa still works and is still first-class.

Also fixes the new gates on the worked example

Examples/ledgerline is a design-document analysis, and two gates from #6 misbehaved on it:

  • ControlInventory hard-failed for want of candidates.json. A Modality-B analysis has no code scan, so there is nothing to cross-check — passing is correct, not lenient. It still hard-fails a codebase analysis with no scan, which is the case where every absence claim is unfalsifiable. Verified both ways.
  • The example now declares trustRoot on all six process-model variables, so it demonstrates the field instead of failing the gate that requires it.

Verification

With Bun genuinely removed from PATH, on Node v22.23:

./stpa --help via shebang        PASS
fixture grid                     PASS
worked example                   COVERAGE 100.0% (32/32), open 0
scope / controls / evidence / plan   all exit 0
full run chain                   exit 3 (NOT PEER-REVIEWED — correct for the example; main was also non-zero)
live 74-finding analysis         scope + controls + evidence + peer review all green

All five CI steps replicated locally under Node before pushing.

One honest limit: I have no genuine Bun in this environment, so I have not exercised the real Bun path — CI runs Bun and covers it there. I'm not claiming otherwise.

Asked whether Bun could be shipped with the skill. It can (a ~34MB binary per
platform, five platforms, Git LFS, and an unsigned runtime vendored inside a
SECURITY toolkit), but it should not be, and it turns out not to be necessary:

the toolkit's entire Bun dependency was four lines of dead code.

    const f = Bun.file(path);                                 // created
    text = require("node:fs").readFileSync(path, "utf8");     // actually read via Node
    void f;                                                   // explicitly discarded

That was the ONLY Bun.* call anywhere in Tools/ or the CLI. What remained binding
the project to Bun was cosmetic: `#!/usr/bin/env bun` shebangs and the CLI
hardcoding spawnSync("bun", ...).

  - Removed the dead Bun.file block. Zero Bun APIs now remain.
  - `stpa` dispatches to process.execPath — whichever runtime is executing it — so
    the child always matches the parent and there is no PATH probing and no
    ambiguity about which runtime produced an analysis. Node <22.18 gets
    --experimental-strip-types automatically; Bun gets no flag.
  - `stpa` is now CommonJS with a node shebang. It is the extensionless entry
    point, and Node only auto-detects ESM in extensionless files from 22.7 onward,
    so CJS is unambiguous on every Node >=12. `bun stpa` still works.

Also fixes two ways the new gates misbehaved on the shipped worked example, which
is a design-document analysis:

  - ControlInventory hard-failed for want of candidates.json. A Modality-B analysis
    has no code scan, so there is nothing to cross-check and passing is correct
    rather than lenient. It still HARD-FAILS a codebase analysis with no scan,
    which is the case where every absence claim is unfalsifiable (verified both
    ways).
  - Examples/ledgerline now declares trustRoot on all six process-model variables
    (five attacker-input, one database), so the worked example demonstrates the
    field rather than failing the gate that requires it.

Gate run-artifacts (review-scorecard.json, evidence-gate.json,
control-inventory.json) are gitignored; they regenerate and were never tracked.

VERIFIED with Bun genuinely absent from PATH, on Node v22.23: ./stpa --help via the
shebang, fixture grid, the worked example at 100%, every gate individually, the full
run chain, and the live 74-finding analysis with all four gates green. The five CI
steps were replicated locally under node before pushing. CI itself runs Bun, so the
Bun path is covered there for real — I have no genuine Bun here and am not claiming
to have exercised it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants