OSS Risk Radar

How it's set up

System architecture

OSS Risk Radar is four small services behind one API. The design choice that shapes everything else is a hard split between an offline pipeline that builds and promotes a model, and an online path that only ever loads that model to score a repository. Nothing is trained, and no history is downloaded, on a live request.

The services

web — Next.js (:3000)
The dashboard and these docs. It talks only to the API over /api/v1 and holds no model logic of its own; every number it shows comes from the API.
api — Go (:8080)
The orchestrator. It accepts submissions, runs enrichment (GitHub, OpenSSF Scorecard, deps.dev), builds the feature vector, calls the scoring service, persists results, and serves the training/evaluation data behind the docs.
scoring — Python/FastAPI (:8090)
Stateless model inference. It loads the exported artifacts once and exposes /features/extract and /score/model. It never trains and never reaches out to the network — same input always yields the same score.
postgres (:5432)
Durable storage for analyses, dependencies, and jobs. Optional in development — with no DATABASE_URL the API falls back to an in-memory store so you can run the stack without a database.

The offline / online boundary

Keeping training and scoring on opposite sides of a wall is what makes a live score fast, deterministic, and honest: the model a user hits was measured on held-out history, not fitted to their repository.

Offline — training
Runs on a workstation or CI, never on a user request. Downloads GH Archive history, builds the leakage-controlled dataset, fits and calibrates the models, and exports artifacts. Slow, reproducible, and gated by promotion guardrails.
Online — scoring
Runs on every submission in seconds. Loads the promoted artifacts and applies them to whatever signals it can gather right now. It only ever reads a model; it never fits one.

The two sides meet at a folder of artifacts. Training writes a bundle and promotion copies the accepted one into deployment/training; the API image bakes that folder in and re-seeds it on start, so shipping a new model means deploying a new image. See Train it yourself for the promotion guardrails.

What happens on a request

  1. web → api: a submission is POSTed to /api/v1/analyses, which returns an analysis and a background job immediately.
  2. api worker: a poller picks up the job, enriches each repository from public sources, and resolves the feature vector.
  3. api → scoring: features go to the scoring service, which returns a calibrated probability plus evidence support and explanation factors.
  4. api → store: results are persisted and the job flips to completed.
  5. web polls: the dashboard reads the analysis until it is done, then renders the score, evidence, and caveats.

The exact endpoints and payloads are in the API reference; the modelling steps are in How scoring a repo works.

Run the whole stack locally

The stack is defined in compose.yaml. The app services live behind the apps profile so Postgres can also be brought up on its own for the offline tooling.

bring up web + api + scoring + postgres
docker compose --profile apps up --build

Then open http://localhost:3000. The API is reachable directly at http://localhost:8080; a GitHub token in GITHUB_TOKEN raises enrichment rate limits but is not required to boot.