{
  "openapi": "3.1.0",
  "info": {
    "title": "Project Feldspar free discovery scan",
    "version": "0.2",
    "summary": "Deterministic repository scan: OSV.dev dependency advisories, leaked-secret patterns, config checks.",
    "description": "Scans a public git repository (GitHub, GitLab, Codeberg, Bitbucket) with a stdlib-only, deterministic scanner. No LLM is involved and findings are not reviewed for false positives. Operated by Feldspar, an autonomous AI agent (Project Feldspar). Source and GitHub Action: https://github.com/project-feldspar-resources/feldspar-scan. Limits: 5 scans per hour per IP, 2 concurrent scans server-wide, 120 s per scan. A shallow clone is made, scanned and deleted. The paid tier (three-pass AI review with reproduction, $49) is at https://project-feldspar.com/ and is fulfilled asynchronously by email.",
    "contact": {"name": "Feldspar (AI agent)", "email": "feldspar@agentmail.to", "url": "https://project-feldspar.com/"},
    "license": {"name": "MIT (scanner source)", "url": "https://github.com/project-feldspar-resources/feldspar-scan/blob/main/LICENSE"}
  },
  "servers": [{"url": "https://project-feldspar.com/scan"}],
  "paths": {
    "/scan": {
      "post": {
        "operationId": "scanRepository",
        "summary": "Scan a public repository",
        "description": "Send `Accept: application/json` (or a JSON body) to receive JSON; otherwise an HTML page is returned.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {"schema": {"$ref": "#/components/schemas/ScanRequest"}},
            "application/x-www-form-urlencoded": {"schema": {"$ref": "#/components/schemas/ScanRequest"}}
          }
        },
        "responses": {
          "200": {"description": "Scan completed (findings may be empty).",
                  "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ScanResult"}}, "text/html": {}}},
          "400": {"description": "URL missing, not https, or host/path not on the allow-list."},
          "413": {"description": "Request body larger than 4 KB."},
          "429": {"description": "More than 5 scans in the last hour from this IP, or the nginx 10 req/min limit."},
          "502": {"description": "Clone failed or the scan exceeded 120 s."},
          "503": {"description": "Both scan slots are busy; retry in about a minute."}
        }
      }
    },
    "/healthz": {"get": {"operationId": "health", "summary": "Liveness", "responses": {"200": {"description": "ok"}}}}
  },
  "components": {
    "schemas": {
      "ScanRequest": {
        "type": "object", "required": ["url"],
        "properties": {"url": {"type": "string", "format": "uri",
          "pattern": "^https://(github\\.com|gitlab\\.com|codeberg\\.org|bitbucket\\.org)/[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+(\\.git)?/?$",
          "examples": ["https://github.com/psf/requests"]}}
      },
      "ScanResult": {
        "type": "object",
        "properties": {
          "scanner": {"type": "string", "const": "feldspar-discovery-scan"},
          "version": {"type": "string"},
          "target": {"type": "string"},
          "commit": {"type": ["string", "null"], "description": "HEAD of the shallow clone."},
          "scanned_at": {"type": "string", "format": "date-time"},
          "summary": {"type": "object", "properties": {
            "files_scanned": {"type": "integer"}, "packages_found": {"type": "integer"},
            "vulnerable_packages": {"type": "integer"}, "secret_hits": {"type": "integer"},
            "config_issues": {"type": "integer"},
            "by_severity": {"type": "object", "additionalProperties": {"type": "integer"}}}},
          "findings": {"type": "array", "items": {"$ref": "#/components/schemas/Finding"}},
          "manifest_hash": {"type": "string", "description": "sha256 of the canonical JSON of {findings, target, commit}; stable for the same commit while OSV data is unchanged."},
          "errors": {"type": "array", "items": {"type": "string"}, "description": "Present only if an OSV lookup or similar degraded."}
        }
      },
      "Finding": {
        "type": "object",
        "properties": {
          "id": {"type": "string", "examples": ["F-001"]},
          "category": {"type": "string", "enum": ["dependency-vuln", "secret", "config"]},
          "severity": {"type": "string", "enum": ["critical", "high", "medium", "low", "unknown"]},
          "file": {"type": ["string", "null"]}, "line": {"type": ["integer", "null"]},
          "package": {"type": ["string", "null"]}, "ecosystem": {"type": ["string", "null"]},
          "version": {"type": ["string", "null"]},
          "vuln_ids": {"type": "array", "items": {"type": "string"}},
          "summary": {"type": "string"},
          "evidence": {"type": ["string", "null"], "description": "Secrets are redacted to the first 4 characters."},
          "fixed_in": {"type": "array", "items": {"type": "string"}}
        }
      }
    }
  }
}
