ossfind
Safety-ranked open-source component discovery for agents across six package ecosystems.
Open source Open in the app JSON README (API)
About
Safety-ranked open-source component discovery for agents across six package ecosystems.
Details
- Kind
- MCP servers
- Topic
- No topic detected
- Publisher
- aniket-kr1030
- Origin
- official
- Category
- ferramentas
- Transport
- local
- Version
- 0.1.12
- Last push
- 2026-09-06T10:29:48Z
- Repository state
- ativo
- Language
- TypeScript
- License
- MIT
- Added
- 2026-09-07 16:01:21
- Updated
- 2026-09-07 16:01:21
- Origin id
io.github.Aniket-kr1030/ossfind
README
# 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`](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
```bash
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)
```bash
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):
```bash
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):
```bash
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:
```bash
# 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
```json
{
"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.**