Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.pipefort.com/llms.txt

Use this file to discover all available pages before exploring further.

The Pipefort API exposes eleven endpoints under /api/*. All non-health endpoints require a Supabase JWT in the Authorization header:
Authorization: Bearer <supabase-access-token>
The token is the access token the SPA gets from supabase-js after a successful GitHub login. The API verifies it (HS256, SUPABASE_JWT_SECRET) and extracts user_id for downstream queries.

GET /api/health

Unauthenticated health check. Returns { "status": "ok" }.

GET /api/repos

Refreshes the user’s repositories from GitHub (across all their installations) and persists them. Returns the stored rows. Response
{
  "connected": true,
  "repositories": [
    {
      "id": "uuid",
      "full_name": "owner/repo",
      "private": false,
      "html_url": "https://github.com/owner/repo"
    }
  ]
}
If the user hasn’t connected any GitHub App installations yet, returns { "connected": false, "repositories": [] }.

POST /api/scan

Scans a single repository. The client loops this across repos client-side for org-wide scans, keeping each request short. Request
{
  "repo_id": "uuid-from-GET-/api/repos",
  "ruleset": "all"        // or "owasp"
}
ruleset defaults to "all" if omitted. Response
{
  "scan_id": "uuid",
  "repo_id": "uuid",
  "counts": {
    "HIGH": 2,
    "MEDIUM": 1,
    "LOW": 0,
    "INFO": 0
  },
  "findings": [
    {
      "file": ".github/workflows/release.yml",
      "line": 12,
      "column": 5,
      "severity": "HIGH",
      "category": "CICD-SEC-4",
      "rule_id": "cicd-sec-4-ppe-shell-injection",
      "title": "Poisoned Pipeline Execution (Shell Injection)",
      "description": "...",
      "recommendation": "..."
    }
  ]
}
The findings array uses the same shape as the CLI’s JSON output — they share scanner.Finding. The rule_id field is the stable per-check identifier the Rule settings page toggles on; category is the coarser OWASP / best-practice group several rules can share. The findings returned and the severity counts both reflect any rule preferences the user has set — filtering happens server-side before persistence, so findings and counts always agree.

GET /api/github/callback

Links a freshly-created GitHub App installation to the signed-in user. GitHub redirects here with ?installation_id=...; the SPA forwards it with the user’s bearer token so the API can associate the two. Query params
ParamRequiredDescription
installation_idyesThe numeric installation ID GitHub provides on the redirect.
Response
{
  "installation_id": 12345,
  "account": "octocat",
  "account_type": "User"
}

Rule settings

The seven endpoints below back the Rule settings page (global toggles) and the Rule overrides card on each repository detail page (per-repo overrides). Reads return sparse rows — rules with no entry are treated as default-enabled per the catalog.

GET /api/rules

Returns the canonical rule catalog (static — same data the SPA renders).
{
  "rules": [
    {
      "id": "best-prac-2-missing-timeout",
      "category": "BEST-PRAC-2",
      "title": "Job timeout not configured",
      "default_severity": "LOW",
      "surface": "workflow",
      "description": "...",
      "doc_url": "/rules/best-prac-2"
    }
  ]
}
surface is "workflow" or "repo-settings".

GET /api/rule-settings

Returns the user’s sparse global preferences. Missing rule IDs are default-enabled.
{
  "settings": [
    { "rule_id": "best-prac-2-missing-timeout", "enabled": false, "updated_at": "..." }
  ]
}

PUT /api/rule-settings/{rule_id}

Upserts a global preference. Returns 204 No Content. Body:
{ "enabled": false }
Validates rule_id against the catalog; unknown slugs return 400.

DELETE /api/rule-settings/{rule_id}

Clears the global row, reverting the rule to default-enabled. Idempotent (204 even if no row existed).

GET /api/repositories/{repo_id}/rule-overrides

Returns the sparse list of override rows for one repo. The user must own the repo (verified via the same path POST /api/scan uses) — 404 otherwise.
{
  "overrides": [
    { "rule_id": "best-prac-2-missing-timeout", "enabled": true, "updated_at": "..." }
  ]
}

PUT /api/repositories/{repo_id}/rule-overrides/{rule_id}

Upserts an override for (user, repo, rule). Body { "enabled": bool }. Returns 204.

DELETE /api/repositories/{repo_id}/rule-overrides/{rule_id}

Clears the override, reverting the rule to inherit from the global setting. Idempotent.

Reading data directly (no API call)

Reading scans, findings, repositories, installations, and rule_settings does not go through the Go API — the SPA queries Postgres directly via supabase-js. Row-level security (auth.uid() = user_id) scopes every result to the signed-in user. The API only handles writes (persisting scan results and rule preferences), GitHub-side reads (which need an installation token), and the installation link callback.