{
  "$schema": "https://scholar-sidekick.com/.well-known/sources.schema.json",
  "service": "scholar-sidekick",
  "service_url": "https://scholar-sidekick.com",
  "version": "2026-05-04",
  "transform_version": "2026-05-04",
  "openapi": "https://scholar-sidekick.com/openapi/openapi.yml",
  "documentation": "https://scholar-sidekick.com/docs",
  "principles": "https://scholar-sidekick.com/engineering-principles",
  "verification_url": "https://scholar-sidekick.com/verification",
  "description": "Authoritative metadata sources used by Scholar Sidekick to resolve scholarly identifiers. Each identifier type lists its resolver chain in fallback order. All outbound fetches are HTTPS-only, host-allowlisted, time-bounded, and have bounded retries.",
  "resolvers": {
    "doi": {
      "chain": ["crossref", "datacite", "doi.org"],
      "primary_host": "api.crossref.org",
      "fallbacks": ["api.datacite.org", "doi.org"],
      "notes": "Crossref is consulted first; DataCite is consulted on miss for non-Crossref DOIs (datasets, theses, software). doi.org content negotiation is the final fallback for citation-style metadata."
    },
    "pmid": {
      "chain": ["pubmed"],
      "primary_host": "eutils.ncbi.nlm.nih.gov",
      "fallbacks": [],
      "notes": "NCBI E-utilities (esummary/efetch) for PubMed identifiers."
    },
    "pmcid": {
      "chain": ["pmcid-converter", "pubmed"],
      "primary_host": "eutils.ncbi.nlm.nih.gov",
      "fallbacks": [],
      "notes": "PMCID is normalised to a PMID via NCBI's ID converter, then resolved through PubMed."
    },
    "isbn": {
      "chain": ["openlibrary", "googlebooks"],
      "primary_host": "openlibrary.org",
      "fallbacks": ["www.googleapis.com"],
      "notes": "Open Library is consulted first; Google Books is consulted on miss."
    },
    "issn": {
      "chain": ["issn"],
      "primary_host": "portal.issn.org",
      "fallbacks": [],
      "notes": "ISSN portal lookup for journal-level metadata."
    },
    "eissn": {
      "chain": ["issn"],
      "primary_host": "portal.issn.org",
      "fallbacks": [],
      "notes": "Same as ISSN; eISSN values are normalised to canonical form before lookup."
    },
    "arxiv": {
      "chain": ["arxiv"],
      "primary_host": "export.arxiv.org",
      "fallbacks": [],
      "notes": "arXiv export API for preprint metadata."
    },
    "ads": {
      "chain": ["nasa-ads"],
      "primary_host": "api.adsabs.harvard.edu",
      "fallbacks": [],
      "notes": "NASA Astrophysics Data System API for bibcode resolution."
    },
    "iris": {
      "chain": ["who-iris"],
      "primary_host": "iris.who.int",
      "fallbacks": [],
      "notes": "WHO Institutional Repository for Information Sharing — resolves IRIS URLs to publication metadata."
    },
    "scholarly_url": {
      "chain": ["url-meta"],
      "primary_host": "varies (allowlisted publisher hosts only)",
      "fallbacks": [],
      "notes": "URL metadata extraction for scholarly publisher pages. Restricted to the allowlisted set in src/lib/http/allowlist.ts; arbitrary URLs are rejected."
    }
  },
  "guarantees": {
    "deterministic": "Identical inputs (identifier, style, format) at a given transform_version produce identical output bytes.",
    "provenance_headers": [
      "x-request-id",
      "x-scholar-cache",
      "x-scholar-formatter",
      "x-scholar-style-used",
      "x-scholar-transform-version",
      "x-csl-warning",
      "x-csl-alias",
      "x-csl-dependent",
      "x-csl-fetch-style-id"
    ],
    "network_safety": "All outbound fetches are HTTPS-only, host-allowlisted, AbortController-bounded with timeouts, and use bounded retries (no infinite retry loops). Response size is capped at 2 MB.",
    "ssrf_protection": "Arbitrary user-supplied URLs are not fetched; only identifier resolution against the allowlisted source hosts above is permitted."
  },
  "policy": {
    "cache_strategy": "two-tier (in-memory LRU + persistent KV); not-found entries cached to avoid repeated upstream lookups",
    "rate_limiting": "sliding-window per plan tier (anonymous/free/pro/ultra/mega); IETF + legacy X-RateLimit-* headers",
    "error_envelope": "{ \"ok\": false, \"code\": \"<ERROR_CODE>\", \"error\": \"<message>\" }"
  },
  "contact": {
    "email": "support@scholar-sidekick.com",
    "issues": "https://scholar-sidekick.com"
  }
}
