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 big picture

React SPA (Vite + TS + Tailwind)
  │  supabase-js: GitHub login + read data (RLS-protected)
  │  fetch() with Supabase JWT → /api/* for privileged work

Go API
  • verify the Supabase user JWT
  • mint GitHub App installation tokens (RS256)
  • fetch .github/workflows/* via the GitHub API (no clone)
  • scan in memory with scanner.ScanBytes
  • persist scans + findings (pgx)

Postgres (history/trends) + Auth (GitHub login)

Key architectural decisions

One scan engine, two callers

All detection lives in a single Go scanner package. The CLI reads from disk; the web API reads from the GitHub API. They both end up calling the same ScanBytes function:
// CLI path
findings, _ := scanner.ScanFile(path)   // ScanFile = os.ReadFile + ScanBytes

// API path
content := githubClient.GetWorkflowBytes(...)
findings, _ := scanner.ScanBytes(filename, content)
This is why the CLI and the dashboard always produce identical findings for the same workflow YAML and the same ruleset.

Two independent auth concerns

Don’t conflate identity with repo access. They use different GitHub flows and different credential types.

Supabase Auth (GitHub provider)

Establishes who the user is. The SPA gets a JWT; the API verifies it (HS256) to extract the user ID for downstream queries.

GitHub App installation

Grants repo read access. The API mints installation tokens (RS256 app JWT) and links each installation to a user via /api/github/callback.

Read/write split around RLS

The React client reads Postgres directly through supabase-js. Every table has row-level security scoped to auth.uid() = user_id, so users can only see their own data even though they’re hitting Postgres with their JWT. The Go API only handles privileged work — minting GitHub installation tokens, fetching/scanning, and writing scans + findings as the service role (which bypasses RLS). The findings table mirrors the scanner’s Finding struct 1:1.

Scan orchestration is client-driven

There is no server-side batch scan. POST /api/scan does one repo (fetch workflows → ScanBytes → persist). The SPA loops it across repos with bounded concurrency so each request stays within serverless time limits.

No git clone in the web app

The API pulls only the workflow YAML via the GitHub Git Trees/Blobs API and scans the bytes in memory, so each per-repo scan is sub-second and fits a serverless time budget. Org-wide scans are orchestrated client-side, one short request per repo, with live progress. The CLI’s -g owner/repo mode does shallow-clone — it’s a one-shot local invocation, not a multi-tenant service.