Skip to main content
Glama

ossfind — safety-ranked open-source component discovery

Given a query like "http client", ossfind returns open-source components ranked by whether you can actually ship a product on them — a blended, explainable score of fit · license · security · maintenance health · integration effort — served through both a web UI and an MCP tool over one ranking engine.

Its core promise: never recommend ("ship") a component whose safety evidence is unsafe, missing, or ambiguous. The engine fails closed.

New here?GETTING_STARTED.md — install, try the offline demo, go live, and connect it to an AI agent over MCP (Claude Code / Claude Desktop / Cursor config included), in about five minutes.

Command line

npm install -g ossfind

ossfind search "markdown parser"
ossfind search "http client" -e cargo -n 5
ossfind search "web framework" -e pypi --json     # machine-readable
ossfind inspect marked                            # verified exports + import line
 1. marked  SHIP     92/100
    MIT  ·  0 CVEs  ·  OpenSSF 7.4

-e/--ecosystem npm · pypi · github · huggingface · cargo · rubygems · all — -l/--license declares your project's license so incompatible results are ranked AVOID--json for scripting — --no-color (also honours NO_COLOR). Colour is disabled automatically when stdout is not a terminal.

Quick start (from source)

npm install
npm run typecheck && npm test     # 586 tests, fully offline
npm run gates                     # 17 safety gates, each proven able to fail
npm run eval                      # relevance against the labelled query set (live)

Run the web app (offline demo mode, uses frozen fixtures):

OSSFIND_FIXTURES=1 npm run web    # http://127.0.0.1:8787

By default, the web server binds exclusively to loopback (127.0.0.1).

  • HOST — bind host (default 127.0.0.1). Non-loopback hosts (e.g. HOST=0.0.0.0) require OSSFIND_WEB_TOKEN to be set; starting wide-open without a token is refused.

  • PORT — server port (default 8787).

  • OSSFIND_WEB_TOKEN — optional Bearer token requiring Authorization: Bearer <token> on /api/* endpoints.

Run the MCP server (for AI agents):

OSSFIND_FIXTURES=1 npm run mcp    # stdio MCP server exposing `search_components`

Drop OSSFIND_FIXTURES=1 to hit live suppliers (npm registry, ecosyste.ms, deps.dev, OSV).

Ecosystems (npm · PyPI · crates.io · RubyGems · GitHub · Hugging Face)

ossfind searches npm (default), PyPI, crates.io (Rust), RubyGems, GitHub repositories, Hugging Face models, or all six at once (ecosystem: "all") — one query, results from every ecosystem merged and safety-ranked together, so you don't have to guess where the answer lives (e.g. "video generation" → PyPI's decord, a GitHub AI-model repo, and a Hugging Face model in the same result set). Pick the ecosystem with the web/MCP selector, the ecosystem MCP tool argument, or &ecosystem=all on /api/search.

Discovery is federated: a FederatedDiscoverer composes multiple source adapters per query (parallel, per-source error isolation + timeouts, results merged and deduped by id). Enrichment routes each candidate by its own id prefix (npm:/pypi:/cargo:/rubygems:/github:/huggingface:), so a mixed batch is enriched correctly per-source. The safety-ranking layer is the same for every source — ossfind owns the ranking, not the corpus. GitHub and Hugging Face are what surface AI-model repos/models (diffusers, CogVideo, …) that aren't on any package registry.

  • npm needs no key — discovery uses the npm registry search API, with query expansion (progressively shorter slices of the query, unioned) to recover the recall a conjunctive text match loses. Optionally federate it with a local semantic index to bridge vocabulary the registry cannot — marked says parser when you asked for a renderer:

    INDEX_MAX=8000 INDEX_DB_PATH=.cache/index/npm.db npm run index:build npm

    The same optional index federates crates.io, RubyGems and PyPI. When an index has not been built, that ecosystem's search behaves exactly as before.

    It does not help everywhere, and the eval says where. Measured on the labelled set: npm and crates.io improve substantially (crates.io MRR 0.000 → 0.675, since crates.io's own search ranks by name similarity and never returns serde for "serialization"). RubyGems is neutral on MRR and slightly positive on recall. Rebuilding PyPI's index concentrated on the top 8,000 packages measured slightly worse (0.611 → 0.597) than the broader 25,000-package index, so the wider corpus stays — a hypothesis the harness rejected.

    A RubyGems index originally measured much worse (MRR 0.500 → 0.250) by pushing rails out of the enrichment shortlist. That was a shortlisting defect, not an index one, and is fixed: a complete lexical match now counts as relevance evidence, so an adopted package whose description contains every query word earns a slot regardless of its embedding score. Measured on the labelled set, adding the index moved MRR 0.561 → 0.636, hit@3 60.5% → 67.4% and noise@3 2.6% → 0.0%, with no per-query regressions — and made marked the top result for "markdown to html renderer", which no lexical probe can reach. Note that npm run eval therefore depends on a locally built index; without one the numbers are the registry-only ones.

  • GitHub uses the repo search API. Set an optional GITHUB_TOKEN in .env.local for higher rate limits.

  • Hugging Face needs no key — discovery uses the public models search API.

  • crates.io (Rust) and RubyGems need no key — discovery uses their public search APIs, with licence/vulnerability/health enrichment from ecosyste.ms, OSV and deps.dev like any package ecosystem. crates.io ranks by name similarity, so "serialization" never returns serde from the registry alone; federate a local index (INDEX_MAX=6000 INDEX_DB_PATH=.cache/index/cargo.db npm run index:build cargo) to fix that.

  • Licence expressions: an SPDX expression whose operands are all permissive resolves to a permissive licence — Rust's near-universal MIT OR Apache-2.0 is a real choice, not an audit item. Any copyleft operand keeps the conservative treatment G4 requires: GPL-3.0 OR MIT never ships into a permissive project, and a WITH exception or unrecognized operand is left for manual audit.

  • Health evidence is attributed only when the repository claim is corroborated. A package's repository URL is self-declared, and typosquats name the real project's repo to inherit its OpenSSF score — five PyPI packages claiming github.com/psf/requests were reported SHIP 92/100 on the real project's 8.1. The claim is now checked against the package name and fails closed (G17).

  • GitHub and Hugging Face components fail-closed to at most "caution" (never "ship") — a raw repo's or model's dependency CVEs can't be verified the way a published package's can; Hugging Face also has no OpenSSF-style health score, so it relies on the existing missing-scorecard cap. License (SPDX) is still enriched and gated for both.

  • PyPI discovery uses a self-hosted local index by default (no key, no third-party service). Build/refresh it once:

    INDEX_MAX=50000 npm run index:build          # top-N PyPI packages by downloads → .cache/index/pypi.db

    The index is node:sqlite FTS5 (BM25) over name/description/keywords, semantically reranked by the embedding model. Select the discovery source with OSSFIND_PYPI_DISCOVERY=index|libraries|auto (default auto: local index if built, else libraries.io).

  • libraries.io is the fallback for PyPI (used when no local index exists). It needs a free key in a gitignored .env.local (LIBRARY_IO_API_KEY=…, LIBRARIES_IO_API_KEY also accepted), loaded via node --env-file=.env.local …. Without index or key, PyPI discovery degrades to empty (never crashes).

Live mode & caching

Live mode stores successful supplier responses on disk to reduce repeat requests and avoid supplier rate limits. Fixture mode remains local and does not use this cache.

  • OSSFIND_CACHE_DIR — cache directory (default .cache/http/).

  • OSSFIND_CACHE_TTL — cache lifetime in seconds for discovery, license, and health data (default 3600).

  • OSSFIND_SECURITY_TTL — cache lifetime in seconds for OSV vulnerability data (default 300).

  • OSSFIND_CONCURRENCY — maximum concurrent upstream enrichment requests (default 4).

  • OSSFIND_NO_CACHE=1 — disable the live-response cache.

Security responses may be up to OSSFIND_SECURITY_TTL seconds stale; tune this value down when stricter vulnerability-data freshness is required.

Supplier APIs are free but rate-limited; review each supplier's terms before commercial use.

Telemetry & Usage Metrics

ossfind includes an in-memory, privacy-preserving usage collector that tracks aggregate operational health and supplier rate limits.

Local Inspection (Read-Only)

You can inspect usage metrics at any time without sending data anywhere:

  • MCP Tool: Call usage_stats to receive the metrics snapshot and a formatted summary of top suppliers, cache hit rates, rate-limit headroom, and latency percentiles (p50/p95).

  • Web API: Send GET /api/usage to retrieve the JSON snapshot. When OSSFIND_WEB_TOKEN is set, /api/usage requires the same Authorization: Bearer <token> header as /api/search.

What Is Collected

  • Aggregate Supplier Counters: Total requests, cache hits, cache misses, HTTP status class counts (2xx, 4xx, 5xx), 429 counts, error counts, and latest rate-limit headroom (remaining, limit, reset, retryAfter) per approved supplier host.

  • Search Operations: Total searches served, ecosystem distribution (npm, pypi, github, huggingface), verdict distribution (ship, caution, avoid), result count summary (min, max, mean), error counts, and latency percentiles (p50, p95).

  • Anonymous Install ID: A random UUID v4 generated once and stored locally in .cache/telemetry/install-id.

  • Metadata: Tool version (0.1.0) and ISO 8601 timestamp.

What Is Explicitly NOT Collected

  • NO raw query strings or search phrases.

  • NO package names, repository names, or model identifiers.

  • NO file paths, local paths, or directory names.

  • NO auth tokens, API keys, credentials, or environment secrets.

  • NO full URLs, request payloads, or response bodies.

  • NO IP addresses, hostnames, usernames, MAC addresses, or hardware fingerprints.

Opt-In Remote Telemetry (Client-Side)

Remote telemetry is off by default. Absolutely no network calls are made unless both switches are explicitly set:

# Enable remote telemetry by setting BOTH switches:
export OSSFIND_TELEMETRY=1
export OSSFIND_TELEMETRY_URL="https://your-telemetry-collector.example.com/v1/metrics"
  • Two-switch requirement: If either OSSFIND_TELEMETRY=1 or OSSFIND_TELEMETRY_URL is omitted, telemetry is completely inert.

  • HTTPS required: Ingestion URLs must use https://; unencrypted http:// URLs are rejected.

  • Batched & Non-blocking: Telemetry flushes asynchronously in the background and never blocks search or user requests.

  • Fail-open & silent: Any network failure, DNS error, timeout, or HTTP error is swallowed silently. It will never break, slow, or alter search results.

  • Inert in fixture/test mode: Telemetry never executes when OSSFIND_FIXTURES=1 or during automated test runs.

  • To disable: Unset OSSFIND_TELEMETRY (or set OSSFIND_TELEMETRY=0) or unset OSSFIND_TELEMETRY_URL.

Telemetry Payload Shape

{
  "installId": "c3e98db2-5b94-4f27-9c98-1092e4ab78ef",
  "version": "0.1.0",
  "timestamp": "2026-08-30T06:30:00.000Z",
  "snapshot": {
    "suppliers": {
      "registry.npmjs.org": {
        "requests": 14,
        "cacheHits": 12,
        "cacheMisses": 2,
        "statusClasses": { "1xx": 0, "2xx": 2, "3xx": 0, "4xx": 0, "5xx": 0 },
        "rateLimited429": 0,
        "errors": 0,
        "rateLimit": { "remaining": 980, "limit": 1000 }
      },
      "api.github.com": {
        "requests": 4,
        "cacheHits": 3,
        "cacheMisses": 1,
        "statusClasses": { "1xx": 0, "2xx": 1, "3xx": 0, "4xx": 0, "5xx": 0 },
        "rateLimited429": 0,
        "errors": 0,
        "rateLimit": { "remaining": 58, "limit": 60, "reset": 1725000000 }
      }
    },
    "operations": {
      "searchesServed": 3,
      "ecosystems": { "npm": 2, "pypi": 1, "github": 0, "huggingface": 0 },
      "verdicts": { "ship": 2, "caution": 1, "avoid": 0 },
      "results": { "count": 3, "total": 24, "min": 5, "max": 10, "mean": 8.0 },
      "errors": 0,
      "latency": { "count": 3, "p50": 18, "p95": 42, "reservoirSize": 3 }
    }
  }
}

How it works

discover → enrich → fit → rank, wired in src/pipeline/orchestrator.ts:

Stage

Module

Source

Discover

src/adapters/discovery.ts

npm registry search

Enrich

src/adapters/enrichment.ts

ecosyste.ms (license/repo), deps.dev + OpenSSF Scorecard (health), OSV (vulns)

Fit

src/fit/embeddings.ts · src/fit/tfidf.ts

local embedding model (live) / deterministic TF-IDF (tests), both with a keyword+coverage guard

Rank

src/ranking/rank.ts

pure, deterministic, explainable blend + verdict

The HTTP layer is injectable (src/http/client.ts), so every test replays frozen fixtures in fixtures/raw/no test touches the network.

The safety model (the moat)

rank() is pure and deterministic. It computes five 0–1 sub-scores, an overall 0–100, and a verdict (ship / caution / avoid) with hard, fail-closed rules that override any high score:

  • A vulnerability that affects the selected version (version-aware, prerelease-aware) forces avoid if critical — regardless of a future fixedIn.

  • GPL/AGPL into a permissive project — across SPDX expression forms (-or-later, +, AND/OR, case) — can never ship.

  • Unknown / unverified license, failed OSV retrieval, or unparseable severity → capped at caution; the engine will not claim "no vulnerabilities" when OSV data is missing.

Every result carries a non-empty, human-readable reasons[] explaining the drivers.

Quality gates (npm run gates)

Thirteen executable gates, each mapped to the defect/decision that spawned it (see PIPELINE_LOG.md), each proven to reject a known-bad input (not just accept a good one):

G1 contract · G2 determinism · G3 critical-CVSS fact (v3.0/v3.1/v4) · G4 license SPDX fact · G5 offline · G6 version-relevance fact · G7 evidence completeness · G8 federation provenance · G9 Python project-context honesty · G10 scaffold snippet integrity · G11 Python stub structural honesty · G12 recipe resolution honesty · G13 adoption cannot override safety · G14 cache preserves response bodies · G15 suggested ESM import matches declared exports · G16 recall survives discovery · G17 health evidence belongs to the package.

Every gate after G7 exists because a real bug got past a green test suite — found by an independent adversarial audit, by building a real project against the published package (G14, G15), or by the relevance eval (G16).

Audit trail

Every component has been independently audited — each audit by an agent that did not write the code — and every one found real bugs the green test suite had missed. All are fixed and gated.

  • AUDIT_REPORT.md — the safety layer: 5 blockers (all fixed).

  • REAUDIT_REPORT.md — independent re-audit confirming all 5 closed.

  • CACHE_AUDIT.md — the live cache: key-collision + stale safety signal (both fixed).

  • FEDERATION_AUDIT.md — federation/GitHub/all-ecosystem: 2 structural holes (→ G8).

  • AUDIT_AGENT_LAYER_A.md — npm/PyPI manifest, compat, MCP, ZIP/range: false "compatible" blocker (→ G9).

  • AUDIT_AGENT_LAYER_B.md — scaffold + stub parser: code injection into generated snippets and fabricated exports from docstrings (→ G10, G11).

  • AUDIT_AGENT_LAYER_C.md — recipes + typeshed: fail-open on supplier errors, false "ready" (→ G12).

Status & limitations (MVP)

  • Live mode works against real suppliers, with a persistent disk cache (category-aware TTL: security/OSV data defaults to 300s, everything else 3600s) and a concurrency cap. Fixture mode stays offline and deterministic for tests/demos.

  • Fit is semantic in live mode — a local embedding model (Xenova/all-MiniLM-L6-v2 via @huggingface/transformers, mean-pooled, cached per package under .cache/embeddings/) ranks by meaning. Fixture/test mode uses deterministic TF-IDF so tests stay offline and exact. Force either with OSSFIND_FIT=embeddings|tfidf; live falls back to TF-IDF if the model can't load.

  • Ecosystems: npm, PyPI, crates.io, RubyGems, GitHub and Hugging Face, via a federated discovery layer. Discovery composes existing search sources (registry APIs, self-hosted indexes, libraries.io, GitHub) — ossfind owns the safety ranking, not the corpus. Go and Maven remain unbuilt: Go has no free search API, and Maven's groupId:artifactId collides with the ecosystem:name id convention.

  • Relevance is measured, not asserted. npm run eval scores a labelled set of 43 queries (MRR, hit@k, recall, noise@3) and diffs against a saved baseline. Registry search matches conjunctively, so a natural-language query excludes terse-description packages — discovery probes with progressively shorter slices of the query and unions the results, and fit is scored before the budgeted enrichment step so a wide pool costs no more than a narrow one. The harness has already rejected one plausible change that measured worse, and caught a defect where a rate-limited search was reported as "no results".

  • Verified signatures cover class members, so a default-exported class reports its methods and constructor rather than only its name.

  • GitHub repo components fail-closed to "caution" (dependency-CVE data isn't available for a raw repo).

  • The self-hosted PyPI index is optional (one source in the federation): FTS5/BM25 recall + semantic rerank, plus a stored-vector hybrid recall (searchHybrid). It does not scale to the full ~928k corpus by API crawl — use ecosyste.ms bulk dumps + an ANN index for that (deliberately not built).

  • Bundled fixtures cover ~15 npm + 12 PyPI packages for offline tests/demo; live mode enriches any package.

  • License output is guidance, not legal advice.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Aniket-kr1030/ossfind'

If you have feedback or need assistance with the MCP directory API, please join our Discord server