Files
coder/scripts/scopesdocgen
Nick Vigilante 4a184ca54a feat: add generated API key scopes reference (DOCS-135) (#29307)
## Summary

Coder validates every requested API key scope against a curated public
catalog in `coderd/rbac`, but the docs never listed it. Readers had to
infer the requestable set from the `codersdk.APIKeyScope` enum, which is
a superset that includes internal scopes a token cannot carry.

This adds `scripts/scopesdocgen`, which renders
`docs/reference/api-key-scopes.md` from `rbac.ExternalScopeNames` and
the policy definitions behind it:

- built-in scopes (`coder:all`, `coder:application_connect`)
- composite `coder:*` scopes, each with the permissions it expands to
- low-level `resource:action` scopes, with the action descriptions the
policy declares
- the deprecated `all` / `application_connect` aliases and their
canonical spellings

The generator is wired into `make gen`, so CI fails when the scope
catalog and the page drift apart.

## Also in this change

- `coderd/apikey.go`: the unrequestable-scope `400` detail pointed at
`admin/users/sessions-tokens#api-key-scopes`, which only ever held a
partial list. It now points at the generated reference.
- `docs/admin/users/sessions-tokens.md`: the partial enumeration and the
"read the schema, but not all of it applies" caveat are replaced with a
link to the reference. The how-to content stays.

## Verification

- `go test ./scripts/scopesdocgen` covers the sentence formatting, the
scope partitioning, and a guard that every name in
`rbac.ExternalScopeNames()` appears on the rendered page.
- `go test ./coderd -run
'TestTokenScoped|TestCreateTokenScopes|TestExternalScopesAreStorable|TestTokenLegacySingularScopeCompat'`
passes, confirming the page's claims about the default scope and the
rejection of internal scopes.
- `make lint/emdash`, `make lint/docs-html`, `make lint/check-scopes`,
`pnpm run check-docs`, and Vale on the changed pages are clean. Full
`make pre-commit` passed.

<details>
<summary>Context and scope decisions</summary>

Part of the DOCS Automated reference documentation project, which tracks
generating reference pages from product source of truth instead of
maintaining them by hand. DOCS-135 is the first item: it was filed as
"wire up the existing generator," but `scripts/apikeyscopesgen` emits Go
(`codersdk/apikey_scopes_gen.go`), not documentation, so there was no
generator output to surface and this adds one.

Decisions made while implementing:

- **Placement.** `docs/reference/api-key-scopes.md`, as a direct child
of Reference rather than inside `docs/reference/api/`, because that
directory is owned by the swagger generator.
- **Page shape.** Follows the `scripts/docgenenv` convention for
generated reference pages: the generated marker and page metadata in
front matter, the generated-content banner in the body, and sections
starting at level two because the front-matter title renders as the page
heading.
- **Public scopes only.** The page lists what `rbac.IsExternalScope`
accepts. Internal `resource:action` pairs are deliberately excluded,
which is the distinction the old schema link failed to make.
- **Acronym handling.** Policy action descriptions are lowercase source
strings (`"ssh into a given workspace"`). The generator restores the
casing of a small set of leading acronyms rather than editing the RBAC
package, so the change stays inside the docs pipeline.

Sibling backlog items turned out to be already delivered and are being
rescoped separately: `metricsdocgen` and `auditdocgen` already write
into their docs pages, and `scripts/configdocgen` already generates the
configuration reference.

</details>

---

Generated with Coder Agents on behalf of @nickvigilante. Linear:
[DOCS-135](https://linear.app/codercom/issue/DOCS-135/wave-1-wire-up-api-key-scopes-reference-to-docs)


## Product UI impact, please read

This PR corrects three RBAC action description strings in
`coderd/rbac/policy/policy.go`:

| Action | Before | After |
|---|---|---|
| `workspace:start` | allows starting a workspace | start a workspace |
| `workspace:stop` | allows stopping a workspace | stop a workspace |
| `api_key:update` | update an api key, eg expires | update an api key,
for example its expiry |

These were internal-facing until this PR, which is what made them worth
fixing: the new reference page renders them verbatim to operators, and
"eg expires" is not decodable by a reader without codebase context.

**They are not docs-only.** `make gen` propagates them to
`coderd/rbac/object_gen.go` and
`site/src/api/rbacresourcesGenerated.ts`, and
`CreateEditRolePageView.tsx` renders the description next to each action
in the custom roles editor. So this changes admin UI copy, and
`CreateEditRolePageView.stories.tsx` means Pixel may now report a
genuine snapshot diff on that component rather than the pre-existing
failure noted below.

The new wording matches the convention the rest of the table already
follows (`ssh into a given workspace`, `create an AI Gateway key`), so
this makes the UI more internally consistent, not less. Flagging it
explicitly because a docs PR changing product copy deserves an owner's
eyes rather than a silent merge. Happy to split it out if RBAC or
frontend would rather own it, with the caveat that the reference page
would then publish `eg expires` in the meantime.

<details>
<summary>CI note: Storybook / Pixel failure</summary>

The `Storybook` check fails at its `Snapshot` step, and `Pixel / Review`
reports `build failed` with no snapshots processed.

**Updated:** this note originally said the branch touched no files under
`site/`. That is no longer true. Correcting the RBAC policy description
strings (CRF-24) required `make gen`, which regenerated
`site/src/api/rbacresourcesGenerated.ts` alongside
`coderd/rbac/object_gen.go`. That file is a generated string table with
no rendered component behind it, so it still cannot account for a
Storybook snapshot or Pixel build failure, but the original
justification no longer holds and is replaced by this one. The failure
predates that change: it was present when the branch genuinely had no
`site/` diff. Every other check passes.

</details>
2026-10-01 16:09:01 -04:00
..