Docs

OSS Protector developer docs

Public REST API, maintainer-authenticated endpoints, rate limits, pagination, repository configuration, and authentication.

What this is

OSS Protector exposes a small public REST API (used by other tools and automations to read the public account directory) and a set of authenticated maintainer endpoints (used by the dashboard at /dashboard). All responses are JSON.

The shape stabilises through schema_version; breaking changes bump the date.

Base URL

https://oss-protector.raedbahri90.workers.dev

Schema version

Every public response includes "schema_version" (an ISO date). When we bump this, the response shape changed. Pin consumers to a date and re-test before upgrading.

Current: 2026-05-30

Public API

Three public read endpoints. All are throttled per client IP — see Rate limits.

GET/api/accounts
Filterable list of accounts currently published for review.
/api/accounts?status=review&reason=external_blocklist&min_score=70&limit=10&offset=0
QueryValuesMeaning
qstringSearch login or evidence summary.
statusall, watch, review, high_risk, blockFilter by published review status. 400 on unknown values.
reasonall, fake_bounty, ai_slop, spam_pr, duplicate_pr, low_quality_ai, credential_phishing, malicious_code, impersonation, maintainer_report, honeypot_match, external_blocklistFilter by stored abuse reason. 400 on unknown values.
min_scoreinteger 0–100Only return accounts at or above this score (0–100).
limitinteger 1–500Page size. Default 50. Larger values rejected with 400.
offsetinteger 0–50000Offset for pagination. Use with limit. Larger values rejected with 400.
GET/api/protectors
Filterable list of maintainers who submitted review signals.
/api/protectors?min_reports=1&min_score=10&limit=10
QueryValuesMeaning
qstringSearch maintainer login.
min_scoreinteger 0–100Only return maintainers at or above this validated score.
min_reportsinteger 0–500Only return maintainers with at least this many review signals.
limitinteger 1–500Page size. Default 50.
offsetinteger 0–50000Offset for pagination.
GET/api/openrouter/free-models
OpenRouter model IDs the platform key cycles through. For transparency — maintainers can see exactly which models are used for analysis when they don't bring their own key.
/api/openrouter/free-models

Pagination

All filterable endpoints (/api/accounts, /api/protectors) accept the same pagination params and return a page_info block.

limit + offset

ParamDefaultBoundsBehavior
limit501–500Out-of-bounds → 400.
offset00–50000Out-of-bounds → 400.

page_info

{
  "accounts": [...],
  "count": 50,
  "page_info": {
    "limit": 50,
    "offset": 0,
    "total": 149,
    "hasMore": true
  },
  "filters": { ... },
  "schema_version": "2026-05-30"
}

count is this page's row count; total is the full matched set. To iterate, increment offset by limit until hasMore is false.

Maintainer API

Authenticated endpoints used by the dashboard. They require an active Better Auth session cookie — sign in at /login. Not rate-limited by the public 60 req/min bucket. All return 401 when called without a session.

GET / POST/api/user/preferences
Read or update your notification kinds + BYOK OpenRouter key. POST accepts { notificationKinds?: string[]; openrouterApiKey?: string | null }. Pass `null` to clear the key.
POST/api/openrouter/test
Validate a BYOK OpenRouter key against /api/v1/key. Body: { apiKey: string }. No model credit consumed.
POST / DELETE/api/maintainer/repo-decision
Block or allow a specific account on a repo you maintain. Body: { repositoryId, targetLogin, decision: 'block' | 'allow', note? }. DELETE clears the override.
GET/api/maintainer/repo-decisions
List every repo-local override across the repos you maintain.
GET / POST / DELETE/api/maintainer/repo-policy?repositoryId=…
Dashboard-saved repository policy (enabled, analyzePrivateRepositories, minimumLikelyAbuseConfidence, trustedAuthors, ignoredPaths). The committed .github/oss-protector.json takes precedence per-field.

Rate limits & errors

400 on invalid filter

Invalid filter values (unknown status/ reason, out-of-range limit/offset/min_score) return HTTP 400 with a structured body so you can correct the request without scraping the message:

{
  "error": "Invalid limit \"1000\". Allowed: 1–500.",
  "field": "limit",
  "value": "1000",
  "allowed": ["1–500"]
}

Bot commands

Bot-driven endpoints — invoked by mentioning @oss-protector in a PR or issue comment.

Report commands

Anyone can file a report by mentioning the bot. Submitted / needs-review reports are tracked but only validated or corroborated reports affect shared scores.

@oss-protector review this user
@oss-protector flag this user reason: fake bounty
@oss-protector recommend block reason: malicious code

Maintainer corrections

Repo owners and members (author_association of OWNER, MEMBER, or COLLABORATOR) can correct the system from any PR comment. Each correction is applied silently and recorded as an in-app notification — no reply is posted to the PR.

@oss-protector dismiss     # false positive: dismiss all open reports
@oss-protector confirm     # validate the latest open report
@oss-protector allow       # allowlist the PR author (sticky)
@oss-protector reset       # clear a prior allowlist; recompute from current signals

Configuration

Repository policy

Each repo can tune the analyzer with .github/oss-protector.json:

{
  "enabled": true,
  "analyzePrivateRepositories": false,
  "minimumLikelyAbuseConfidence": 80,
  "trustedAuthors": ["dependabot[bot]", "renovate[bot]"],
  "ignoredPaths": ["docs/", "examples/"]
}

Dashboard editor

The same fields can be edited from the Repo policy tab in the dashboard. When both exist, the committed file wins per-field; the dashboard value fills in any field the file doesn't set.

Authentication

OSS Protector uses Better Auth for end-user sign-in and an installation-token model (@octokit/auth-app) for webhook actions.

GitHub OAuth

The default sign-in path. Better Auth handles the OAuth round trip — clicking Continue with GitHub on /login POSTs to /api/auth/sign-in/social, which returns a github.com authorize URL the browser follows. Callback URL: /api/auth/callback/github.

Email OTP

Optional email-only sign-in. Requires RESEND_API_KEY to be set on the Worker. Without it, the send is rejected with a clear error in non-localhost environments (codes log to the server console in local dev).

Found something missing? File an issue on GitHub or open a PR against src/routes/docs.tsx.