A verifying package registry

The registry
that says no.

Point your package manager at one URL. Known-vulnerable and malicious packages are refused with an HTTP 403 in the download path itself — no CLI to wrap, no app to install, no cache that can bypass it.

16
package types
1
URL to change
403
in the download path

        
      
Illustrative session. Package names are fictional; the status codes, headers and checks are what Stout actually emits.

One gate for every client your team already uses

  • npm
  • Go
  • Python
  • Docker
  • Maven
  • Rust
  • NuGet
  • Ruby
  • PHP
  • Helm
  • Swift
  • Dart
  • Conan
  • Terraform modules
  • Terraform providers
  • Agent packages

The problem with scanning around the edges

Every other tool needs to be somewhere you forgot to put it.

Scanners ask for a plugin in the IDE, an app on the repo, a wrapper in CI, a flag on the command line. Each is one more surface, and each can be skipped by a laptop, a build script, or a cache that already holds the bad package.

Stout sits in the one place every one of those surfaces pulls from. If the registry will not serve a package, nothing downstream can install it.

How it works

Verify first. Serve second. Never guess.

Stout is a pull-through registry. Every artifact is checked before it is handed to a client, and the decision is made in exactly one place.

  1. 1

    Your client asks

    npm, pip, go, docker or any of the other protocols requests a package from Stout, exactly as it would from the public registry.

    GET /npm/lodash/-/lodash-4.17.4.tgz
  2. 2

    Stout verifies

    An isolated scan lane runs vulnerability, malware, behavioral and AI review checks on the exact bytes. The scanner pods hold no master credentials.

    cve · malware · behavior · llm
  3. 3

    One gate decides

    A single serve decision turns the verdict into an answer. Anything unknown, errored or timed out is held, never published.

    200 serve 403 refuse 503 hold
  4. 4

    Every URL points home

    Download links in the response are rewritten back to Stout. If a link cannot be rewritten, the request errors instead of leaking an upstream URL.

    tarball → registry.stout.io

Holds, not fail-open

A version still being verified holds the connection. If the budget runs out, the client gets a recoverable 503 with Retry-After — never the artifact. Hold times are sized from each client's real timeout, so npm, go and maven ride it out.

An allowlist, not a denylist

Fail-safe is structural. A check that cannot run is never a pass, and an ecosystem Stout can't fully verify is skipped as indeterminate, not green.

No cache to bypass

Tools that hook network fetches lose to a populated local cache. A registry that refuses to serve does not.

Transitive by construction

A malicious dependency five levels down is blocked like a direct one.

Because every download URL Stout emits points back at Stout, each dependency at any depth re-enters the same gate. Protection doesn't depend on someone remembering to scan the lockfile.

Holds while the client resolves through Stout. A lockfile pinned to a direct upstream URL fetches direct, and Stout never sees it.

  • my-app
    • web-framework@4.2
      • http-utils@1.9
        • stream-helpers@0.7
          • color-parse-lite@2.0.1 403
Install halts. The tree never resolves.

Detection

Two kinds of detection. We tell you which is which.

Advisory feeds catch what is already known. Behavioral and AI review catch what a package does, with no feed required.

Feed-dependent

Known bad

Blocks once an advisory or signature has been published, at the same latency as everyone who uses the same feeds.

  • CVE gate — Grype and OSV, deterministic
  • Known malware — OSV MAL-* advisories
  • Antivirus — ClamAV signatures
Feed-independent

Looks at behavior

Fires on what the artifact does, so it can act before any advisory exists.

  • Behavioral malware gates — nine rules, G1 to G9
  • AI review lenses — grounded, fail-closed

Behavioral malware gates

G1Dynamic execution of fetched code
G2curl | sh in install scripts
G3Decoded, executed obfuscated payloads
G4Secret reads next to an outbound send
G5Known exfiltration and C2 hosts
G6Crypto-clipper wallet swaps
G7Reverse shells and connect-back sockets
G8Cloud-credential sweeps
G9Network-fetched payload into eval

AI that has to show its work

A hallucination can't block your build.

The review model only gets to block if it quotes the exact lines of code that are malicious. Stout then re-reads the real file and checks the quote is there. A finding the model can't cite is discarded before it reaches anyone.

The artifact under review is treated as hostile input: the prompt is nonce-fenced against injection from the package itself, and the model never holds a provider key.

// finding: malicious · confidence 0.97
file    postinstall.js:41-43
snippet fetch(u).then(r => r.text()).then(eval)

✓ server re-read postinstall.js
✓ snippet found verbatim at 41-43
→ finding may gate

Setup

Change the URL. Done.

No new CLI for developers to learn and no pipeline step to maintain. Change the registry URL once, in the config file each tool already reads.

  • Pull-through for 16 package types, never plan-gated
  • Org-scoped URLs for private policy and packages
  • Works the same on laptops, CI runners and build farms
.npmrc

      
    

Evidence, not just a verdict

Every refusal comes with a receipt.

When Stout says no, you can see exactly why, and decide whether to override it.

Findings with proof

Each finding records the file, the line range and the verbatim snippet behind it.

SBOMs in two formats

CycloneDX and SPDX for every package Stout verifies.

Ed25519 signatures

Verified artifacts are signed, and the public key is served from the registry. Signatures are local, not transparency-logged.

Tamper-evident audit log

Audit events are anchored on a schedule so changes to history can be detected.

Exceptions you approve

Refusals and the approved exceptions to them are recorded side by side.

Community reviews

Reputation-weighted reviews from people sit next to the automated verdict.

Org policy

Choose per ecosystem and per check whether a rule enforces, observes or stays off.

Isolated scanning

Pods that touch hostile packages hold no database password, storage master key or signing key.

In development

Make every pull programmable.

The next layer lets platform teams attach their own checks and transformations to package pulls. A pull waits until required work succeeds, and a failure serves zero bytes and explains itself.

  • Durable runs with retries and restart recovery
  • Original bytes keep their digest; derived outputs get their own address
  • Run evidence and failed-run retries in the admin app
# illustrative policy
policy: require-internal-scan
match:   { ecosystem: npm, scope: "@acme/*" }
require:
  - check: license-allowlist
  - check: internal-sast
on_failure: refuse   # 0 bytes served
timeout: 90s

Honest status

We label every capability. Even the ones we haven't turned on.

Security tools lose trust by overclaiming. Ours are tagged by what runs in production, what is built but off, and what isn't done yet.

CapabilityStatusWhat that means
Pull-through proxy, 16 typesLiveNever plan-gated. The free path.
Verify-before-serve gateLiveOne serve decision for every protocol.
CVE and known-malware blockingLiveDeterministic. Depends on published advisories.
AI review lensesLiveFail-closed. Blocks only on verified, quoted evidence.
SBOM (CycloneDX + SPDX)LiveGenerated for verified packages.
Behavioral gates G1 to G9, ClamAVAdvisoryRun and record findings. Promoted to blocking per ecosystem.
Static analysis (SAST)AdvisoryDeliberately non-blocking to avoid false positives.
Docker image enforcementAdvisoryPer-layer verdicts aren't finished yet.
Transparency-logged signingNot yetSignatures are local Ed25519. No Rekor entries.
SSO (OIDC) and SCIM 2.0DarkBuilt, switched off until enabled per organization.
Source buildsDarkBuilt, not offered today.

We don't publish a false-positive rate yet because we haven't measured one. When we do, it will be here.

Questions

What engineers ask first.

Is the AI review going to block good packages?

A finding can only block if the model quotes the offending code and Stout's server confirms that exact snippet exists in the file. Hallucinated findings cannot produce a real quote, so they are dropped. We are deliberately not claiming a measured false-positive rate until we have one.

Will it slow down my installs?

Warm pulls of already-verified packages are served directly. A package seen for the first time is verified before it is served, so that first pull can be slow by design. Stout holds the connection rather than serving unchecked bytes.

What if Stout can't finish checking a package?

It never publishes it. After the hold budget, your client gets a recoverable 503 with a Retry-After header. Unknown, errored and timed-out all resolve to hold.

Do I need to change my CI or install a plugin?

No. You change the registry URL in the config your tools already read. There is no wrapper, GitHub app or IDE extension.

Can it catch a malicious package before an advisory exists?

The behavioral gates and AI review inspect what a package does, so they don't wait for a feed. Whether those layers block or only advise depends on the ecosystem, which is why the status table above matters.

Which ecosystems are covered?

Sixteen package types: npm, Go, Python, Docker, Maven, Rust, NuGet, Ruby, PHP, Helm, Swift, Dart, Conan, Terraform modules, Terraform providers and agent packages. Docker is advisory today, and a few ecosystems lack OSV coverage, so those checks report indeterminate rather than clean.

Is it open source?

Stout is Apache 2.0 licensed. A self-hosted distribution is in development and hasn't been released yet.

Change one URL.
Stop trusting the internet.

The pull-through proxy for all 16 package types is never behind a plan. Point one client at Stout and watch what it refuses.