> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sesameterminal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Runbook: dev tools and lint gates

# Runbook: dev tools and lint gates

> Owner: DevOps. The technical writer owns `docs/runbooks/`, but DevOps maintains this
> file together with `Taskfile.yml`, `lefthook.yml` and `scripts/`.

This runbook lists the pinned tools, what each lint gate checks, and how to fix the usual
failures. Setup is in [dev-setup.md](dev-setup.md).

## Tool versions

`task tools` installs these into `go env GOPATH`\bin. Rerun it after a version bump in
`Taskfile.yml`, which is the only place the versions are set.

| Tool | Version | Used by |
| - | - | - |
| Go toolchain | go1.26.6 minimum (`GO_TOOLCHAIN`) | every `go` command run through Task |
| golangci-lint | v2.14.0 | `lint:go`, `fmt` |
| gofumpt | v0.12.0 | `lint:go`, pre-commit |
| lefthook | v2.1.8 | git hooks |
| task | v3.53.1 | everything |
| govulncheck | v1.8.0 | `lint:vuln` |
| gitleaks | v8.30.1 | pre-commit, `lint:secrets`, `secrets:scan` |
| osv-scanner | v2.5.1 | `lint:osv` |

**Go toolchain.** Task sets `GOTOOLCHAIN=go1.26.6+auto`. On first use the `go` command
downloads Go 1.26.6 and verifies its checksum. Go 1.26.0 to 1.26.5 have standard-library
vulnerabilities that govulncheck reports as called by the backend. A `GOTOOLCHAIN` already
set in your shell takes precedence over the Taskfile value. Plain `go build` outside Task
uses your installed Go until `backend/go.mod` pins the toolchain.

## Lint gates

`task lint` runs these in order, and **MUST** pass before a PR and before every merge.
`lint:vuln` and `lint:osv` query vuln.go.dev and osv.dev, so they need network access.

| Task | Checks | Fix |
| - | - | - |
| `lint:gen` | Generated API code matches `api/openapi.yaml`. | `task gen`, then commit the result. Never edit `gen/` by hand. |
| `lint:scope` | No fund-moving code in `backend/`, `frontend/src/` or `api/` (security F9). | Remove the code; see [Scope check](#scope-check). |
| `lint:go` | gofumpt, golangci-lint (`backend/.golangci.yml`, which bans `http.DefaultClient`, `http.DefaultTransport` and `InsecureSkipVerify`), `go vet` for Windows and Linux. | `task fmt` for formatting; fix the rest by hand. |
| `lint:web` | Biome and `tsc --noEmit`. | `task fmt` for formatting; fix the rest by hand. |
| `lint:vuln` | govulncheck: known Go vulnerabilities in code the backend actually calls. | See [govulncheck](#govulncheck). |
| `lint:osv` | osv-scanner: known vulnerabilities in `frontend/` and `tools/vectors/` npm lockfiles. | See [osv-scanner](#osv-scanner). |
| `lint:secrets` | gitleaks over the branch's commits not on `main` (`main..HEAD`), with `.gitleaks.toml`. | See [gitleaks](#gitleaks). |

Other checks:

| Where | Checks |
| - | - |
| pre-commit hook | No `.env` files; gofumpt and Biome on staged files; gitleaks on the staged diff; the scope check on staged files. |
| pre-push hook | `task lint`. |
| `task secrets:scan` | gitleaks over the full git history. |

Enable the hooks once per clone with `lefthook install`. `lefthook run <hook>` also
rewrites the installed hooks when `lefthook.yml` changed; add `--no-auto-install` to
avoid that.

## Fixing common failures

### Scope check

`lint:scope` prints `path:line: fund-moving pattern "<regex>" (scope-patterns.txt:N)`.
The app holds trade-only credentials: it places and cancels orders and reads account data.
Deposits, withdrawals, bridge, transfers, approvals, permits and relayer operations
(redeem, split, merge) are out of scope, so the fix is to remove the code.

A false positive has two fixes:

* **Tighten the pattern** in `scripts/scope-patterns.txt`, and add the line to the
  allowed list in `scripts/scopecheck/main_test.go`. The test runs first in
  `lint:scope`, so a pattern change that stops catching a known case fails the lint.
* **Mark the line** with a `scope:allow <reason>` comment, for example where the config
  loader refuses a relayer variable. A marker needs security review.

Generated code, `testdata/` and files with a Go `Code generated ... DO NOT EDIT.` header
are not scanned.

### govulncheck

It fails only when the backend calls a vulnerable function. Each finding names the fixed
version:

* **Standard library** (`Found in: net/http@go1.26.x`): raise `GO_TOOLCHAIN` in
  `Taskfile.yml` (or the `toolchain` line in `backend/go.mod` once it exists) to the fixed
  release.
* **A module:** `go get <module>@<fixed>` in `backend/`, then `go mod tidy`. This is a
  `go.mod` change, so it goes through the backend owner.

Findings in modules the backend requires but doesn't call are printed as a count and don't
fail the lint. `govulncheck -show verbose ./...` in `backend/` lists them.

### osv-scanner

The frontend lockfile has no ignores; fix a finding by upgrading the package (a
`package.json` change goes through its owner). `tools/vectors` findings that can't be
fixed are ignored in `scripts/osv-scanner-vectors.toml`, each with a reason and an
`ignoreUntil` date. When the date passes, the lint fails again: recheck the finding, then
fix it or extend the date with a new reason.

### gitleaks

A pre-commit failure prints the file, line and rule, with the secret redacted:

* **A real secret:** unstage it, move the value to `.env`, and rotate it if it was ever
  committed or pushed.
* **A test value:** add an allowlist to `.gitleaks.toml` that names the exact value and the
  exact file, with a comment explaining why the value is harmless. The existing entries
  cover the Hardhat account #0 key and the fake L2 secrets in the golden signing vectors.

`gitleaks: command not found` means `task tools` has not been run.

## npm

* Install dependencies with `task setup`, which runs `npm ci` from the lockfiles. Don't use
  `npm install` except to add or upgrade a package, and commit the lockfile with it.
* `frontend/.npmrc` sets `ignore-scripts=true`: no dependency runs install scripts. The
  native binaries (rolldown, Biome, lightningcss, Tailwind oxide) come as platform
  `optionalDependencies` and need no script. `npm run <script>` still works. npm reads the
  project `.npmrc` only from the package's own folder, so a repo-root `.npmrc` would have no
  effect.
* `save-exact=true` makes `npm install <pkg>` record an exact version.
* If a future dependency needs its install script, run it explicitly with
  `npm rebuild <pkg> --ignore-scripts=false`, and note why in the PR.

## Dev servers

* `task dev` runs the backend and Vite. Open [http://localhost:5173](http://localhost:5173): Vite listens on
  localhost only.
* `task dev:fake` does the same with `SESAME_FAKE_VENUE=1`. The backend then serves a fake
  venue, so the UI works without reaching Polymarket.
* On Windows, if the backend exits by itself (for example on a config error), Task stops
  `npm run dev` but the Vite `node` process can keep running, and the next `task dev` fails
  with port 5173 in use. Stop it with
  `Get-NetTCPConnection -LocalPort 5173 -State Listen | % { Stop-Process -Id $_.OwningProcess }`.
  Ctrl+C in the terminal stops both.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.