SentinelloSentinello

An early-warning system for the dependencies you stopped watching.

In the AI age you ship more projects than you can maintain. Sentinello watches every one and surfaces known CVEs in their Node.js dependencies — so a forgotten project never becomes an incident.

Run it in one command:

docker run -d \
  --name sentinello \
  -p 127.0.0.1:3870:3000 \
  --stop-timeout 60 \
  -v sentinello-data:/app/data \
  -v sentinello-nvm:/home/sentinello/.nvm \
  -v ~/Developer:/roots/personal:ro \
  ghcr.io/walkofcode/sentinello:latest

Runs on linux/amd64 and arm64

No account. No SaaS. No telemetry. One Docker image and one SQLite file — your code and your findings never leave your machine.

Or skip the portal entirely

The same scanners ship as a CLI. No install, no account, no database — it runs, prints an advisory your agent can act on, and exits.

npx sentinello

Piped, stdout carries only the markdown, so the advisory reaches an agent intact:

npx sentinello | claude -p "$(cat -)"

Walks the folder

Point it at a directory and it finds every project underneath, stopping at each project root so a monorepo counts once rather than fifty times. .gitignore and .sentinelloignore are honoured.

Checks three sources

Resolves the exact installed versions from the lockfile and matches them offline against a local advisory cache. No per-project network call, and nothing about your code is uploaded.

Writes an advisory

A dated markdown file with the findings and a remediation prompt attached — triage before touching anything, prefer parent upgrades over overrides, verify fixes in the lockfile.

How it differs from npm audit

It isn't a replacement for npm audit — it runs npm audit, then adds two advisory sources npm audit cannot see, and suppresses anything they duplicate.

npm auditnpx sentinello
ScopeThe one project you are standing in.Every project under a folder, in one pass and one report.
Advisory sourcesYour registry's advisory feed.That, plus OSV and GitLab gemnasium. Duplicates are suppressed, so each extra source only adds net-new findings.
Malicious packagesNot covered.OSV's MAL- records flag packages published with malware — typosquats, install-script payloads — matched against the specific compromised versions.
OutputA table, or JSON you have to interpret.A markdown advisory with a remediation prompt attached, ready to hand to an agent.
MatchingA registry call per project.Offline against a local cache. The first run downloads it and asks first; later runs transfer almost nothing.

The extra sources are not a formality. On Sentinello's own repository, npm audit and OSV each report three findings and agree on all of them — while the single critical finding comes from gemnasium, which neither of the other two carries.

Features

Everything in one self-hosted portal — no external services, no data leaving your network.

Single triage queue

See and triage CVEs across your whole portfolio in one place — instead of npm audit scattered across a dozen checkouts.

Browse by project or library

Drill into any repository for its findings, fix versions, and history — or pivot to a vulnerable package to see every project it affects and mute it everywhere at once.

Findings that agree

When several sources report the same vulnerability it stays one finding — carrying which of them agreed, graded at the worst severity any of them gave it.

Continuous scanning

A background worker rescans on a schedule, so new advisories show up without you remembering to check.

Multiple sources

Beyond npm audit: match against OSV and GitLab gemnasium for broader CVE coverage and known-malicious package detection.

Notifications & webhooks

Get failure and finding alerts via Slack, Telegram, or a plain webhook — scoped per root or project, in the language you choose. JSON or plain-text payloads for an auto-fix agent.

MCP server

Connect Claude Desktop, Cursor, and other MCP clients to query findings, projects, and libraries — and trigger scans — without leaving the chat.

Advisory export

Export findings for a project or library as Markdown, with a customizable remediation prompt for your team or an LLM.

Single image, single file

One Docker image and one SQLite file. No database server, no message queue, no cloud dependency.

Auto-registered roots

Anything mounted under /roots is registered and scanned on boot — the directory name becomes its label.

Per-project Node

Respects each project's .nvmrc, installing and caching the Node version it pins, once.

10 languages

The portal UI, scan reason codes, and statuses are localized to 10 languages.

Screenshots

See it in action — the portal scanning a handful of demo projects. Click any shot to expand it.

How it compares

Sentinello isn’t a heavier Dependency-Track or a cheaper Snyk. It owns a different niche: the long tail of projects nobody wired into a pipeline.

SentinelloDependency-TrackSnykDependabot
Zero-config — point it at a folder~~
No SBOM / CI step required
Scans real resolved lockfiles~
Malicious-package detection
Self-hosted, no SaaS
Single image + SQLite
AI-native (MCP + export)~
Polyglot (Python, Go, …)
Enterprise policy / VEX~~

Dependency-Track only sees the projects someone instrumented with an SBOM pipeline. Sentinello finds the ones you forgot. They’re stronger on enterprise policy — if you already run them on a mature pipeline, keep them. Sentinello is for the rest of your portfolio that nobody is watching.

Why we built this

In the AI age, you ship more than you can maintain.

A solo dev now spins up, delivers, and moves on from a dozen projects a year — the marketing site, the client dashboard, the side project that quietly went to production. Keeping them safe used to mean SSHing into each checkout to run npm audit by hand, or finding out about a Next.js CVE from a headline days after it dropped. Nobody keeps that up across a dozen repos, so it doesn't happen at all.

A single forgotten dependency with a critical remote-code-execution flaw is all it takes. The simplest site you stopped watching becomes the way in.

“Why not just use Snyk or Dependabot?” Those live inside the CI pipeline you wired up — and the long tail never got one. Sentinello is the early-warning system for everything else: point it at a folder and it watches every project you forgot, surfacing each new CVE in one queue before it becomes an incident.

How it works

Three steps. No agents to install in your projects, no accounts to create.

Point it at your code

Mount your repositories under /roots, or add them from Settings → Roots. Every directory is auto-registered and discovered on startup.

It scans continuously

A background worker checks your dependencies against known CVEs on a schedule, installing the Node version each project pins when it needs to.

Triage in one queue

Every finding across every project lands in a single queue you can filter by severity — with optional alerts to Slack, Telegram, or a webhook.

Who it's for

Sentinello is for everyone who has more in production than they have eyes on — the solo developer, the small team, the agency juggling client work.

  • You ship side projects and client sites that still need to stay safe long after launch.
  • You want a portfolio-wide view without wiring CI into every repository.
  • You'd rather self-host than hand your code inventory to a SaaS.

If you're a large org with Snyk or Dependabot already wired into a mature pipeline, keep them — Sentinello isn't trying to replace enterprise SCA. It's here for the rest of your portfolio that nobody is watching. It's open source and MIT-licensed, so you can read exactly what it does.

Release notes

Sentinello ships regularly — here's what each release delivered.

A dashboard that let one source speak for the whole project

v3.5.0 · Aug 20, 2026
  • The dashboard’s <strong>State</strong> column reported one source and threw the rest away. Each scan writes one row per source and they finish milliseconds apart, so the column showed whichever finished last — in practice always OSV. Every project read “OSV database not downloaded yet” while npm audit had scanned them fine and found real vulnerabilities. State now carries one badge per source that could not answer, each naming the source, and shows nothing at all when every enabled source is happy. The scan history gained a <strong>Source</strong> column for the same reason: a sweep wrote three identical-looking rows with no way to tell them apart.
  • Settings → Sources claimed a cache was up to date while it was being rebuilt. The status the portal reads was only written when a sync finished, so for the whole of a multi-minute rebuild it kept showing the count from before it — while every scan correctly refused the half-deleted cache. It also never carried the normalizer version, which the scanner requires, so a version bump produced the same false claim with no rebuild at all. The row now reads <strong>Rebuilding…</strong> with the previous count dimmed, or <strong>Rebuild pending</strong> when a cache is unusable and nothing is currently fixing it.
  • A cache that finished downloading left every project stuck on the verdict it got while the cache was missing. Nothing re-scanned, so <code>osv_db_not_seeded</code> sat there until the next scheduled sweep, or until you noticed and pressed Scan. Sentinello now queues one full re-scan the moment a cache becomes usable again. An incremental update queues nothing. Upgrading counts: if this release lands on an instance whose projects are still holding that verdict from before the cache finished, the worker notices the disagreement on its first boot and clears it for you — no scan to remember to run.

An advisory that flagged the version which fixed it

v3.4.0 · Aug 18, 2026
  • gemnasium writes a small number of advisories with a space between the comparator and its version — <code>&lt; 0.5.2</code> rather than <code>&lt;0.5.2</code> — and the parser read the pair as two separate tokens. The orphaned <code>&lt;</code> took an empty bound, and the version left standing alone was cached as an exact pin. So the <code>fresh</code> advisory reported version 0.5.2 as vulnerable when 0.5.2 is the release that fixed it, and reported it with no fix available, because a pinned version carries no upgrade target at all. 19 advisories are written that way and every one of them parsed wrong: 15 lost their range entirely, and 7 pinned a version the record itself lists as the fix — <code>pg</code> pinned eleven.
  • Sentinello stopped interpreting npm’s range syntax itself and now hands every range to npm’s own implementation first, which retires a whole family of misreadings at once. <code>&lt;=3.3</code> means “through the end of the 3.3 line” to npm — read literally it stopped at 3.3.0 and missed 3.3.1, which is a real <code>converse.js</code> advisory — and <code>=103</code> means the whole 103 line rather than a single point, which <code>binaryen</code> states eight times over. Caret, tilde, <code>1.x</code> and hyphen ranges are now read too, where before a record written in any of them was dropped without a word. Checked across all 4,696 distinct version ranges gemnasium publishes for npm: interpreting them ourselves disagreed with npm on 9, delegating disagrees on none. Only npm is affected — Python’s <code>==1.0</code> is an exact pin rather than a wildcard, so applying npm’s rule there would invent findings.
  • Three things npm’s reading gets right for installing a package and wrong for an advisory are held back. A range naming no version at all — <code>*</code>, <code>x</code>, an empty field — means “any version” when npm installs something and “nobody filled this in” in an advisory, so it is refused rather than turned into a finding against every release ever published. A stray trailing <code>||</code> can no longer widen the range it trails into everything. And <code>&gt;0</code> keeps meaning every version, because <code>pandora-doomsday</code> states its malicious package that way and npm’s reading would clear all of 0.x. Separately, an advisory whose stated fix sits at or below the start of its own affected range no longer has that fix substituted in — it produced an interval matching nothing, which is a finding that silently stops reporting — and the rule for discarding such an interval is now shared with the OSV source rather than written once per source, which is how the two came to disagree in the first place.
  • The tests that kept missing all of this now generate their inputs instead of listing them. Every distinct version range gemnasium publishes for npm is checked against npm’s own implementation on every build, alongside the whole cross-product of the range grammar and every ordering of OSV’s range events. Run against the previous release that sweep fails on 28 ranges, while the ten hand-written examples it replaces all passed. A spelling upstream invents from here on fails the build when it is first imported, rather than when someone notices the finding it produced.
  • Every line of the code behind these findings is now exercised by the test suite — statements, branches, functions and lines all at 100%, with no exemptions anywhere in the repository. That is a direct answer to how the bugs in the last few releases got out: each one was a branch nothing ran, reporting success while doing nothing. Closing the final 109 of them found several that had been filed as unreachable safety checks and were simply untested, plus a coverage rule that had been enforcing nothing for months because the file it named had moved. That rule and the 500 lines of per-file exceptions around it are replaced by a single one, so untested code now fails the build instead of lowering an average.
  • The final defensive gaps are closed too: an unreadable OSV upper bound now leaves the valid interval open instead of making it impossible to match, fallback bounds are checked after they are attached, and an explicit npm prerelease boundary such as <code>&lt;1.2.3-0</code> keeps its exact meaning.

Advisories that claimed every version was vulnerable

v3.3.2 · Aug 17, 2026
  • An advisory whose affected range has no end matches every version forever — a finding no upgrade can clear, and indistinguishable on the page from a genuine unpatched vulnerability. Two <code>xlsx</code> advisories arrived that way and reported a fully patched 0.20.3 as high severity with no fix available, across 11 projects at once, while <code>npm audit</code> reported the same two correctly as fixed in 0.19.3 and 0.20.2. The cause is upstream and deliberate: GitHub will not name a fixed version the registry does not serve under that package name — SheetJS publishes 0.19.3 and later only from its own CDN — so it states the range as “everything from 0” and records the real boundary in a separate field Sentinello never read. It reads it now. 15 npm advisories were affected, <code>babel-traverse</code> and <code>sandbox</code> among them. The 480 that are genuinely unfixed still say so, and a record that already states its own boundary is never overridden
  • A gemnasium advisory that left its range open while listing the versions that fix it is now bounded by the highest of them. A range with no end claims every future release is vulnerable too, which no record naming a fix can mean
  • gemnasium writes Python version ranges as PEP 440 intersections — <code>&gt;=5.0,&lt;5.8</code> — and the parser split tokens on spaces alone, so the whole thing became one: an unreadable lower bound and no upper bound at all. Ranges in that state match nothing, and matching nothing means reporting nothing. 2,830 of 7,159 cached PyPI records were affected. This was one of the three reasons Python, Go and Rust were withdrawn in 3.3.0 — the other two are still open, so they stay withdrawn

Same release as 3.3.0, with a Docker image that builds

v3.3.1 · Aug 15, 2026
  • 3.3.0 published to npm and GitHub, but its container image failed to build, so at release time there was no 3.3.0 on GHCR or Docker Hub. The Dockerfile lists each workspace package it installs, and one of them — the version-comparison package — had never been listed. Nothing in the image imported it until this release, so the omission had never once mattered. A corrected 3.3.0 image has since been published, built from 3.3.0’s own application code, so <code>docker pull sentinello:3.3.0</code> works again. 3.3.1 carries the same fix in the source tree. If you use the CLI, 3.3.0 was already correct.

Only the ecosystems that actually work — and notifications you can trust

v3.3.0 · Aug 15, 2026
  • A notification could arrive empty. One operator received a Telegram message reading “Sentinello found vulnerabilities in woc-ide” with nothing under it. Dispatch is scoped to the project but ran once per scanner, so npm audit’s pass was handed two pending OSV events, matched neither, and rendered the headline over an empty list anyway. It then marked both events delivered — so the two findings it never named were recorded as notified, and nothing revisits a delivered event. An event is now dispatched only if it can be described, and one that cannot stays pending and is reconsidered on the next scan
  • A finding is reported at the worst grade any source gave it, and that escalation was reaching the dashboard, project totals and the CLI’s <code>--fail-on</code> gate — but not notification thresholds. Notification ran before corroboration, so the event was stamped with the surviving source’s own grade and never rewritten. On one instance 135 open findings carried an event severity below the finding’s real one, <strong>41 of them recording a critical as low, high or moderate</strong>. A target filtered to critical and high would never have been paged for any of them, permanently
  • Scan schedules stopped at midnight instead of wrapping. “Every 3 hours from 07:00” ran at 07, 10, 13, 16, 19 and 22 and then not again until 07:00 — six scans a day rather than eight, with a nine-hour blind window every night — while Settings went on reporting the interval you picked. “Every 6 hours from 20:00” managed one scan a day. The slots now wrap
  • <strong>Python, Go and Rust have been withdrawn.</strong> This is not caution about rough edges: their failures reported as clean. Fix derivation and version ordering are semver-only, so an OSV Django advisory recommended “upgrade to 3.2.23” against an installed 4.2; OSV’s PyPI package names are not PEP 503-canonicalized, so they never joined the resolver’s; and gemnasium’s range parser cannot read PEP 440 comma intersections. A source that answers “no vulnerabilities” for the wrong reason is worse than one that is not offered, so they are gone from the product surface entirely — no switch, no discovery, no download. <strong>Findings you already collected under them stay visible and mutable; nothing is deleted.</strong> The npm ecosystem is now labelled <strong>Node.js</strong>, which names the package ecosystem actually scanned rather than a language
  • <strong>Settings → Sources</strong> has been rebuilt around that. Every source used to restate its own explanation beside its own switch, and each cache-backed one carried a five-row status panel with its own full-size “Refresh now” button — even though that button enqueues one shared signal per source however many times it appears. The switches now say only whether a source is on; what each one adds, what it downloads and from where, and when it runs moved into a single reference table beneath them, and sync state collapsed to one line
  • Packages that npm’s own lockfile says are reachable from production were being demoted to dev-only. A lockfile omitting <code>dev: true</code> is npm asserting the package IS reachable from production — a stronger statement than the root manifest can make — but the resolver overrode it whenever the name also appeared under <code>devDependencies</code>, which is exactly when npm was right. Measured across 130 real projects: 142 packages demoted in 97 of them — lodash, semver, postcss, tailwindcss, @babel/runtime — hiding seven open findings from the production-only filter on one instance
  • Any package pinned to a version like <code>0.0.0-20180523222229-09b5706aa936</code> matched <strong>no advisory at all</strong>, not even an open-ended one, and the scan reported ok with zero findings. An advisory bound of <code>introduced: 0</code> was compared as the release 0.0.0, and under semver a prerelease sorts below its release. 330 of the 19,085 comparable ranges in a real cache were stored as intervals nothing could satisfy
  • An interrupted OSV sync could erase an advisory permanently. The incremental path deleted an advisory’s rows and then fetched the replacement inside a try/catch, so any timeout, 5xx or shutdown removed it outright — then advanced the cursor anyway, putting the id permanently behind it. The loss was silent, survived until the next full re-seed, and happened on a sync that reported success. The <code>npx sentinello</code> cache eroded the same way on every flaky run. Both now fetch first and replace second
  • The CLI’s <code>--dep-type dev</code> meant “reachable from dev at all” while the portal means “reachable <em>only</em> from dev”, so a package reachable from both appeared in one view and not the other — 177 open findings differ between the two readings on one instance. The CLI now uses the portal’s rule, and it honours OSV’s <code>withdrawn</code> field, which it was structurally incapable of reading before: 585 rows of a real npm cache carry one, and every one of them was being reported as a live finding
  • A gemnasium advisory that says a vulnerability begins <em>after</em> a version — <code>&gt;1.2.8</code> rather than <code>&gt;=1.2.8</code> — was being read as though the boundary version itself were affected. The 2021 hijack of the <code>rc</code> package is written exactly that way, and 1.2.8 is its last clean release: the very version the advisory’s own remediation note tells you to stay on. Every project with <code>rc</code> installed was shown a critical malware finding, with no fix available, against a version that was never compromised. Bounds are now kept exactly as the advisory states them, and false criticals of this shape disappear
  • The same rounding ran in the other direction and was hiding real findings. An advisory bounded by <code>&lt;=2.0.0</code> was stored as “below 2.0.0”, so 2.0.0 — the version it is most explicit about — went unreported, and an advisory naming exactly one affected version collapsed into an empty range and was discarded whole. Expect a small number of new findings that were always present and simply invisible
  • Advisory ranges written with syntax Sentinello does not implement — <code>^1.0.0</code>, <code>~1.0.0</code> — used to be stored as an exact-version pin on the literal text, which could never match anything for as long as it stayed cached: a live-looking advisory that was incapable of firing. Those records are now refused rather than kept in a form that cannot work, and advisories whose upper bound carries no clean fix version finally get an upgrade suggestion
  • The helper that masks a webhook URL or bot token before it reaches a log line was printing short ones in full. It rejected values of six characters or fewer but then kept an eight-character head and a four-character tail, and nothing checked that those two halves did not meet — so every secret between 7 and 12 characters came back complete. It now hides at least eight characters or redacts the value entirely

Findings now show which sources agree — and retracted advisories stop being reported

v3.2.0 · Aug 15, 2026
  • When more than one advisory database reports the same vulnerability, Sentinello has always kept a single finding — reporting one flaw three times because three databases know about it is noise. What it used to do was discard everything about the sources it collapsed, so a vulnerability confirmed independently by npm audit, OSV and GitLab gemnasium looked exactly like one that a single database had ever heard of. On a real instance that is two thirds of all findings. Each finding now carries the other sources that reported it, and their badges appear alongside the surviving one
  • A finding is reported at the WORST severity any source assigned it. The databases genuinely disagree — gemnasium computes severity from the CVSS vector while npm audit takes GitHub’s bucket — and for a scanner the cautious reading is the one worth acting on. This is not cosmetic: an escalated finding moves between severity buckets on the dashboard, in project totals, in the CLI’s <code>--fail-on</code> gate and in notification thresholds. Expect some counts to shift on the first scan after upgrading; nothing new was detected, the same findings are being graded more carefully
  • Where the sources disagree, the finding shows a control next to its severity that opens what each one actually said — its own advisory id and its own grade. It appears only when there is a disagreement to explain, so a finding everyone grades alike stays uncluttered
  • OSV records a withdrawal in a dedicated field and GitHub removes withdrawn advisories before <code>npm audit</code> ever sees them, but GitLab gemnasium has no such field in its schema. It retracts an advisory by rewriting the record in place — the title becomes “False Positive”, “Withdrawn Advisory: …” or “Duplicate Advisory: …” — while leaving the versions it used to name exactly where they were. Sentinello was reading those versions and reporting findings GitLab had explicitly taken back: 383 records across JavaScript, Python, Go and Rust, including one that reported <code>express</code> under the title “False Positive”. All of them are now dropped, which also removes a whole class of duplicate findings, since 278 of the 383 are advisories withdrawn for being duplicates of another one
  • The check matches the retraction markers exactly rather than searching for the words anywhere, so a genuine advisory that happens to be *about* a false positive still reports — Cosign’s CVE-2026-39395, titled “Cosign’s verify-blob-attestation reports false positive when payload parsing fails”, is unaffected
  • The gemnasium cache rebuilds itself on its first sync after this upgrade, so the retracted advisories disappear then. Nothing to do — it happens on the daily schedule, or immediately from Settings → Sources → Refresh

Advisory version ranges are right in both directions

v3.1.1 · Aug 14, 2026
  • Some GitLab gemnasium advisories ship no machine-readable version range at all — 698 of the 10,777 JavaScript ones. Sentinello had been filling that gap by assuming every version below the first listed fix was affected, but that list is unordered and holds one fix per release branch, so the guess regularly landed on the wrong branch. protobufjs 7.6.5 was reported as a critical remote code execution even though that branch was patched in 7.5.5, and three separate advisories each claimed every version of vite below 8.0.5 was vulnerable. Sentinello no longer invents a range. It recovers the real one from the same advisory as published under its other identifier, from the advisory’s own description, or — only if you have OSV switched on — from the OSV copy already on your machine, and it discards the record rather than guessing when none of those can answer. Expect some criticals to disappear
  • OSV describes an advisory fixed on several release branches as a separate entry per branch, and Sentinello was keeping the first and discarding the rest — 1,927 vulnerable version ranges for JavaScript alone, every one of them a real vulnerability it could no longer see. The minimatch advisory covers eight branches and only one survived, so an installed minimatch 3.0.4 or 9.0.0 went unreported; next and ua-parser-js lost branches the same way. Every branch is kept now. Expect some new findings to appear — those vulnerabilities were always present, they were simply invisible
  • Both advisory caches rebuild themselves the first time they sync after this upgrade, because the ranges they hold were produced by the old code. There is nothing to do — it happens on the daily schedule, or immediately from Settings → Sources → Refresh if you would rather not wait

Muted findings get out of the way — and the database stops growing forever

v3.1.0 · Aug 13, 2026
  • A muted finding is a decision you already made, so it now leaves the project page entirely instead of sitting there greyed out. That is not just tidier: every number on the page — the heading count, both tab badges, the pagination, the per-library totals, the export button — is counted from the same rows, so the page finally agrees with the dashboard, the MCP tools and the advisory export, all of which were already leaving muted findings out. A “Show muted” toggle brings them back whenever you want them, including on a project whose findings are *all* muted
  • Typing in a dialog no longer loses focus after a single character. That bug made the mute dialog’s Reason field — required, and the only thing that makes a mute auditable months later — effectively impossible to fill in. The same dialog also stopped inheriting the alignment of whatever table row it was opened from, which is why muting a finding gave you a right-aligned dialog while muting a project did not
  • Scan history no longer grows forever. Nothing had ever deleted a scan row by age, so a project that stayed on disk accumulated one row per source per sweep indefinitely — a real instance reached 2.2 GB in under three months. Settings → Advanced now carries a retention window, 90 days by default, and the worker prunes past it hourly while always keeping the 100 most recent scans of every project. Findings, mutes and notification history are never touched; only the scan log is. At 90 days an instance upgrading into this deletes nothing on its first pass — trimming starts only once history is genuinely older than the window, or once you lower it yourself
  • The bulk of that growth was `npm audit`’s raw output, stored in full on every successful scan and read by nothing at all — 98.7% of that instance’s database. Scans now record a short summary instead, taking a row from roughly 79 KB to about 100 bytes, with a hard ceiling so no scanner can do this again
  • Over MCP, `get_dashboard_summary` now states that a project you have muted leaves its totals while `list_projects` still returns it. The two count different populations on purpose, and an agent comparing them was reading that as a bug. `list_scans` also stopped returning each scan’s raw scanner output, which could reach around 16 MB in a single response

The gemnasium download works again — and the CLI gives the terminal back

v3.0.1 · Aug 4, 2026
  • GitLab gemnasium failed to download on 3.0.0 with `HTTP 406`, for everyone. Node’s built-in fetch attaches a `Sec-Fetch-Mode: cors` header that a program is not permitted to remove, and GitLab refuses any repository archive request carrying it — so this was never about your network, your IP, or how many times you retried. The download uses a plain HTTPS request now, and succeeds
  • The archive is fetched by commit id rather than by branch name, so everyone updating from the same upstream commit shares one cached copy instead of each asking GitLab to build a fresh 60 MB archive. A first download that took nearly seven minutes now finishes in seconds
  • The CLI used to finish its entire run — advisory written, summary printed — and then never return the terminal. The connection carrying the download was left open behind it, which kept the process alive; it is now closed as soon as the archive has been read
  • A feed that refuses a download no longer stalls for three minutes before saying so. It reports in seconds and, in a terminal, offers to try again — retrying only the source that actually failed
  • `--fail-on` is honest in both directions. It refuses a run whose advisory source could not be consulted, rather than reporting a clean scan it never performed; and it no longer fails a run over a source you switched off yourself with `SENTINELLO_OSV_FEED_URL=off` or `SENTINELLO_GEMNASIUM_FEED_URL=off` and never downloaded

Sentinello now runs without a portal at all

v3.0.0 · Aug 3, 2026
  • The scanners ship as a CLI on npm. `npx sentinello` walks a folder, finds every project underneath, checks them against npm audit, OSV and GitLab gemnasium, and writes a markdown advisory with a remediation prompt attached — no install, no account, no database, and nothing about your code leaves the machine
  • Piped, the advisory is the only thing on stdout, so `npx sentinello | claude -p "$(cat -)"` hands an agent a complete work list without anything corrupting the document
  • A first run no longer loses the gemnasium source to a refused download. GitLab declines its archive for a minute or two at a time, and the old retry gave up after thirteen seconds; the CLI now waits it out, says why it is waiting, and takes `--feed-wait` if the default of three minutes is wrong for you
  • Both download estimates were measured rather than guessed: the OSV npm export is quoted at 204 MB rather than 196, and the gemnasium archive at 52 MB rather than 80. The consent prompt marks an estimate with a tilde so it is never mistaken for a size the server reported
  • A value that looks like a flag is now rejected instead of taken literally — `--out --` used to write an advisory to a file named `--` inside your project and report success
  • The What’s new panel no longer runs off the bottom of the window when a release has a lot to say

The advisory document actually arrives — and counts what you mean

v2.6.0 · Jul 29, 2026
  • get_project_advisory now returns the advisory document itself. Connected clients previously received only its metadata — a filename and a count — and never the document, despite the tool describing it as a complete work list
  • The advisory export now holds one entry per distinct advisory with its sources merged, instead of one per scanner row: a vulnerability that npm audit and OSV both report is a single work item carrying both advisory IDs, not two near-identical ones. This applies to the portal’s Download .md as well, and the count now matches the dashboard
  • A project too large to fit in one MCP response is now paginated — the document states that it is incomplete and gives the exact follow-up call to fetch the rest, instead of being silently cut off where an agent would read the remainder as clean
  • Every input on every MCP tool now carries a description, and a new list_mutes tool exposes the mute IDs that unmute needs — previously obtainable only by creating the mute in the same session
  • Fixed a gap in the severity counts: a finding whose severity was not one of the five known values was counted as a finding but placed in no severity bucket, so a project whose only finding had one appeared completely clean

The advisory export, straight over MCP

v2.5.0 · Jul 28, 2026
  • Connected MCP clients can pull a project’s full Markdown advisory with the new get_project_advisory tool — the same document as the portal’s Download .md, without copying it out of the browser
  • Muted findings are now excluded from the project advisory export, so an agent is never handed work whose risk you have already accepted
  • Note: because the advisory contains your export prompt, an MCP client can now read whatever you have written in Settings → Export

Unclipped popups, stricter export prompt

v2.4.3 · Jul 26, 2026
  • Dropdowns, the dependency-path popover, and the advisory export menu no longer get clipped by the table or dialog they sit in — they render above the page and flip above the trigger when there is no room below
  • The default advisory-export prompt now asks the agent to plan before editing anything, group findings that share a single fix, and spell out the code impact of each version bump — and it targets zero findings while ruling out the shortcuts to a fake zero: muting, widening ranges, or narrowing the scan, with anything genuinely unfixable listed in a dated residual table

Branch in its own column

v2.4.2 · Jul 25, 2026
  • The git branch a project was scanned on now has a column of its own in the project list — plain text, no icon — instead of sitting under the project name

Clean shutdowns

v2.4.1 · Jul 25, 2026
  • Restarting the container no longer kills a scan that is midway through writing, and the worker now starts immediately instead of retrying for ~30 seconds first
  • Set stop_grace_period: 60s (or --stop-timeout 60) in your compose file to give it room — the README and Docker docs now cover this

Polyglot scanning — Python, Go, and Rust join npm

v2.4.0 · Jul 25, 2026
  • Sentinello now scans Python, Go, and Rust projects alongside npm — lockfiles are resolved entirely offline, and every project reports its scan coverage (full, partial, or unauditable) so gaps are visible instead of silent
  • GitLab’s gemnasium database joins npm audit and OSV as an offline advisory source, deduplicated against the others by CVE/GHSA alias; Settings → Sources is now a Languages × Sources matrix with per-cell notification scope, and npm audit itself can be turned off as long as one source stays active
  • Findings now record the git branch they came from, shown in the project list, the project header, and every notification
  • Project rows carry their own actions — scan now, copy or download the advisory, mute or unmute, and edit tags — so a triage pass no longer needs a trip into each project
  • The projects dashboard went from ~3.3s to ~0.03s, and navigation now shows loading states instead of appearing frozen
  • Security: 25 dependency advisories cleared, including libvips CVEs that were live in the portal’s image optimizer and nine Next.js advisories affecting the shipped portal
  • The default advisory-export prompt now covers minimum release age, lockfile verification, and stale overrides

Simpler MCP setup — no environment variables

v2.3.0 · Jun 9, 2026
  • Set up MCP entirely in Settings → MCP: generate a token to turn the /api/mcp endpoint on, clear it to turn it off — the SENTINELLO_MCP_ENABLED and SENTINELLO_MCP_API_TOKEN environment variables are gone (an existing env token is imported once on upgrade)
  • Ready-to-paste connection snippets for Claude Code, Codex, Cursor, and Claude Desktop, pre-filled with your token
  • When SENTINELLO_PORTAL_BASE_URL is set in the environment it’s shown read-only in Settings → Advanced, since it stays authoritative and is re-applied on every boot

Fewer false alarms and self-cleaning findings

v2.2.0 · Jun 9, 2026
  • Malware advisories now match the exact compromised version — a clean or already-remediated version of a once-compromised package is no longer flagged
  • Duplicate findings now resolve themselves on the next scan, so old or stranded entries clear out automatically
  • Production and development labels are now computed one consistent way across every source (npm and OSV)

A cleaner project header and consistent filters

v2.1.0 · Jun 6, 2026
  • Streamlined the project header — rename inline beside the title, with mute and tags as one-tap icons
  • Filter findings by source (npm / OSV) from a new dropdown beside the dependency-type filter
  • Unified, consistent dropdowns across the app, with type-to-search on long lists like time zones

Clearer upgrade guidance

v2.0.1 · Jun 4, 2026
  • Expanded upgrade steps for the 2.0 breaking changes
  • README notes the localhost-only port binding

Multi-source scanning and a hardened, secure-by-default install

v2.0.0 · Jun 4, 2026
  • OSV as an opt-in second source (Settings → Sources, off by default) with malicious-package detection, matched against the public OSV database in a local cache
  • Findings now merge across sources — one row per vulnerability, every source tagged, the best available fix, and the union of dependency paths, with a source filter and a dependency-path popover
  • Security hardening: the MCP endpoint is off by default and requires a token, webhook delivery is guarded against SSRF, an optional portal login gate, and the container runs as an unprivileged user
  • Settings is now a top-level section with a sidebar and a Profile page

MCP integration & what’s-new

v1.4.0 · May 29, 2026
  • MCP server at /api/mcp for Claude Desktop, Cursor, and other clients
  • New Settings → MCP section with server URL and token management
  • What’s-new pill plus a Release notes history

Footer version fix

v1.3.1 · May 28, 2026
  • The running version renders cleanly in the footer

Notification improvements

v1.3.0 · May 28, 2026
  • Filter notifications by environment
  • Simpler notification-target edit form
  • Duplicate an existing notification target

Projects and Libraries pages

v1.2.0 · May 24, 2026
  • The home view is split into dedicated Projects and Libraries pages

Live schedule reload

v1.1.2 · May 24, 2026
  • The worker reloads the scan schedule the moment you save changes in the portal

Safer deletes & a clearer update banner

v1.1.0 · May 23, 2026
  • Confirmation prompts before deleting roots and notification targets
  • Update notice moved to a dismissible top banner
  • Worker prunes stale roots when a host mount disappears

Scanner accuracy fixes

v1.0.1 · May 23, 2026
  • Drop audit findings whose installed version isn’t actually in the vulnerable range
  • Allow deleting a notification target that has delivery history

Initial open-source release

v1.0.0 · May 23, 2026
  • The first public release of Sentinello

Roadmap

Sentinello watches your dependencies across languages today. Here's where it's headed — and what you can ask for.

Smarter prioritization

Planned

Rank findings by exploitability and whether the vulnerable code is actually reachable — triage what matters first.

More integrations

Planned

More notification channels and ways to plug Sentinello into the tools your team already uses.

Static analysis (SAST)

Planned

Catch risky patterns in your own source code, not just known CVEs in your dependencies.

Secret & license scanning

Planned

Flag committed secrets and license issues across the same portfolio, in the same queue.

Request an integration or source

Tell us what you'd integrate or scan next — open an issue on GitHub and help shape the roadmap.